# The Linear surface, composed from the widget catalogue. Every number is a
# query over the parsed vault; the five Linear CATEGORIES (backlog / unstarted
# / started / completed / canceled) appear here as where-clauses — which is
# all they ever were. Run `dj dashboard --dashboard dashboard.yaml` to serve
# it live; forms write back through the referee.

dashboard:
  title: "{{NAME}}"
  subtitle: Issues, projects and docs — Linear's defaults as a format you own.

# How much the surface moves: full (view transitions + entrance + FLIP list/
# board reflow, on the Astryx motion tokens) · reduced · off. Full is the
# default; a reader's own reduced-motion preference always wins over it.
motion: full

# Linear's indigo, because the eye expects it. Override or delete freely.
theme:
  light: { color-accent: "#5E6AD2" }
  dark:  { color-accent: "#7B87E0" }

indicators:
  - id: triage
    label: Triage
    select: rows
    from: issues
    where: "status == Triage"
    metric: count
    caption: waiting at the gate — accept, decline, or mark duplicate

  - id: urgent
    label: Urgent
    select: rows
    from: issues
    where: "status in [Todo, 'In Progress', 'In Review'] && priority == Urgent"
    metric: count
    caption: urgent issues in flight

  - id: started
    label: In flight
    select: rows
    from: issues
    where: "status in ['In Progress', 'In Review']"
    metric: count
    caption: the started category — actively being worked

  - id: in-review
    label: In review
    select: rows
    from: issues
    where: "status == 'In Review'"
    metric: count
    caption: waiting on a reviewer

  # Linear's accept guard ("set a priority before accepting"), as a number:
  # issues that LEFT triage carrying no priority.
  - id: needs-priority
    label: Needs priority
    select: rows
    from: issues
    where: "status in [Backlog, Todo, 'In Progress', 'In Review'] && !priority"
    metric: count
    caption: left triage without a priority — Linear's accept dialog would have asked

views:
  - id: inbox
    label: Triage
    widgets:
      - { widget: note, text: "The quarantine zone: issues from outside the team's flow wait here. Accept by moving to Backlog or Todo (and set a priority on the way — the Needs-priority tile is the guard); decline to Canceled; consolidate to Duplicate." }
      - { widget: rows, label: Inbox, from: issues,
          where: "status == Triage",
          search: { by: [ Assignee, Labels, document ] } }

  - id: active
    label: Active
    widgets:
      - widget: board
        label: Board
        from: issues
        where: "status in [Todo, 'In Progress', 'In Review']"
        lanes: { by: Status }
        search: { by: [ Assignee, Priority, Labels, document ] }
        filter: { by: document, label: Team, as: dropdown, multi: true }
      - { widget: chart, label: Active by assignee, from: issues,
          where: "status in [Todo, 'In Progress', 'In Review']",
          x: { by: Assignee }, series: [ { mark: bar, label: issues } ] }

  - id: backlog
    label: Backlog
    widgets:
      - { widget: rows, label: Backlog, from: issues,
          where: "status == Backlog",
          search: { by: [ Priority, Labels, Assignee, document ] } }

  - id: all
    label: All issues
    widgets:
      - widget: board
        label: Everything
        from: issues
        lanes: { by: Status }
        search: { by: [ Assignee, Priority, Labels, document ] }
        filter: { by: document, label: Team, as: dropdown, multi: true }
      - { widget: chart, label: By status, from: issues,
          x: { by: Status }, series: [ { mark: bar, label: issues } ] }
      - { widget: chart, label: By priority, from: issues,
          where: "priority", x: { by: Priority }, series: [ { mark: bar, label: issues } ] }

  - id: projects
    label: Projects
    widgets:
      - { widget: note, text: "Projects are documents: dates and health in frontmatter, milestones as records, the gantt drawn from both. The fill is shipped milestones." }
      - { widget: timeline, label: Roadmap, select: concepts,
          where: "type == project",
          bind: { start: start, end: end,
                  progress: { from: milestones, done: "state == shipped" } } }
      - { widget: concepts, label: All projects, where: "type == project" }

  - id: docs
    label: Docs
    widgets:
      - { widget: concepts, label: Documents, where: "type == doc" }

  # named views from vault/views/ — where the CLI looks without a flag
  # (`--views <dir>` moves it; the default is <vault>/views). E38: the issue
  # and milestone RECORD pages (any
  # row's ID cell opens them; they are not sidebar items — a record view opens
  # per record), and the Blockers dependency picture.
  - issue
  - milestone
  - blockers

# The sidebar — Linear's left rail, declared. A `view:` item opens a composed
# view; a `select:` item is a generated page (rows with facets, or a concept
# listing). Without this section the menu is empty: views exist to be LISTED.
# Icons come from the engine catalogue (`dj catalog` → Nav icons) — generic
# glyphs the format assigns meaning to. A parent with `children:` expands like
# Linear's team row (and may carry its own view: to land somewhere when
# clicked); an icon-less child indents behind a rail, Linear's own spelling
# for "Upcoming" under Cycles.
navigation:
  - group: Workspace
    items:
      - { label: Triage, view: inbox, icon: triage }
      - { label: Active, view: active, icon: pulse }
      - { label: Backlog, view: backlog, icon: archive }
      - { label: All issues, view: all, icon: layers }
      - { label: Blockers, view: blockers, icon: graph }
      # the cross-cutting page Linear calls My Issues — every issue, faceted
      # by assignee and status, searchable by everything the format declares
      - { label: Issues, icon: list, select: rows, from: issues,
          search: { by: [ Assignee, Priority, Labels, Status, document ] } }
      # Linear's "My issues", as a filter value rather than engine machinery:
      # type @me into an Assignee filter and the view scopes to whoever the
      # host says is reading. A host that names no reader matches nothing and
      # the token says so — a filter that lies is worse than one that refuses.
      - { label: My issues, icon: person, select: rows, from: issues,
          search: { by: [ Assignee, Priority, Status, document ] } }
  - group: Projects
    items:
      - label: Projects
        icon: box
        view: projects
        children:
          - { label: Roadmap, view: projects, icon: map }
          - label: All projects
            select: concepts
            where: "type == project"
            filter: { by: status, label: Status }
  - group: Knowledge
    items:
      - { label: Docs, view: docs, icon: book }
      - { label: Teams, icon: team, select: concepts, where: "type == team" }
