# `PhoenixKitProjects.ScheduleLayout`
[🔗](https://github.com/BeamLabEU/phoenix_kit_projects/blob/v0.19.1/lib/phoenix_kit_projects/schedule_layout.ex#L1)

The shared durations→dates schedule walk behind the project show page's
Timeline (gantt) and Calendar tabs.

Flattens a project's assignment tree (sub-project descendants included) and
lays every item out sequentially from the project's schedule anchor via
`PhoenixLiveGantt.Layout.sequential/2` — each task starts where the previous
one ends, honoring the task's effective weekday/weekend rule through
`Project.eta_from/3`. Both tabs render from this one walk so they can never
disagree about which dates a task occupies.

The walk is hour-precise (`NaiveDateTime` spans in UTC, matching the
project's stored `started_at`) and zoom/display-independent; consumers decide
how to render the spans (bars on a date axis, all-day calendar chips, …).

# `item`

```elixir
@type item() :: %{
  uuid: String.t(),
  assignment: PhoenixKitProjects.Schemas.Assignment.t(),
  project: PhoenixKitProjects.Schemas.Project.t(),
  parent_uuid: String.t() | nil,
  position: integer() | nil
}
```

One flattened schedule item: the assignment, its owning project (a
sub-project descendant belongs to the CHILD project), and its
linking-assignment parent (`nil` at top level).

# `span`

```elixir
@type span() :: %{start: NaiveDateTime.t(), end: NaiveDateTime.t()}
```

# `assignment_hours`

```elixir
@spec assignment_hours(
  PhoenixKitProjects.Schemas.Assignment.t(),
  PhoenixKitProjects.Schemas.Project.t()
) :: number()
```

The estimated hours for an assignment: its own duration override if set,
otherwise the underlying task's duration (nil-safe). Weekends are honored
per `task_counts_weekends?/2`.

# `task_counts_weekends?`

```elixir
@spec task_counts_weekends?(
  PhoenixKitProjects.Schemas.Assignment.t(),
  PhoenixKitProjects.Schemas.Project.t()
) :: boolean()
```

Whether an assignment counts weekends — its own override, falling back to
the project's setting.

# `tree`

```elixir
@spec tree(PhoenixKitProjects.Schemas.Project.t()) ::
  {[item()], %{required(String.t()) =&gt; span()}}
```

Flattens `project`'s assignment tree and computes each item's scheduled
span. Returns `{items, layout}`: `items` in flattened tree order (each
sub-project parent immediately followed by its descendants), `layout` a
map of item uuid → `%{start: NaiveDateTime, end: NaiveDateTime}`.

Tasks are laid out in the order the user gave them (drag `position`) — a
manual order that violates a dependency is rendered honestly by the
consumers, not reordered here. A sub-project's span covers its children's
walk, so top-level spans stay correct even when a consumer only renders
the top level.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
