---
title: Mission Control — the table, the views and the board (part 2)
---

# Mission Control — the table, the views and the board (part 2)

## What it is

This is part 2 of the [Mission Control](mission-control.md) page. It covers the board itself: the table, the views, and the controls around them.

## Where to find it

Inside **Mission Control**, on any board — the view switcher and the filter toolbar sit at the top of it.

## How it behaves

### Main Table (Phase 2)

The board route (`/board/:id`) renders `BoardView`, which loads everything in one call — `GET /boards/:id/full` → `{ board, groups, columns, items, values }` — and draws a custom grid (no heavy table lib): a column header (with a **Column Center** `+` to add any of the essential column types), then each group (colored strip, collapse) as item rows, an "+ Add item" row, and a group footer of per-column summaries. Each cell is driven by a **frontend column registry** (`features/board/columns/`) that maps a column `type` to a renderer/editor — the status cell is a colored pill with a label picker + Edit Labels; numbers/text/date/checkbox/timeline/people/dropdown have inline editors.

The backend's core primitive is the **column-type registry** (`src/lib/column-types/` in `amc-back`): one handler per type owning `defaultSettings` / `validate` / `serialize` / `toText` / `summarize`. `columns` rows store `type` + `settings` (JSONB); `column_values` rows store `value` (JSONB) + a denormalized `text`. Writing a cell (`PUT /items/:id/columns/:colId`) validates the value through the registry (HTTP 400 on a bad value), so a status value must match one of its column's labels. New backend modules — `groups`, `columns` (the Column Center), `items` + column-values — are account-scoped through the board's workspace via a shared `ownedBoard` helper.

### Board filter toolbar (post-Phase-4 fast-follow)

A pixel-near rebuild of monday's Main-Table toolbar, replacing the plain Phase-3c controls with monday's real filter surfaces (`features/board/BoardControls.tsx` + `features/board/toolbar/`). The row reads as one control set — **New task · Search · Person · Filter · Sort · Hide** — over the shared `ToolbarButton` shape (`px-2.5 py-1 text-sm`, 1px border, foreground text idle → accent when active). All logic is a pure module (`features/board/boardQuickFilter.ts` + codec), compiled to a predicate in `BoardView` and applied client-side over the loaded board data (no backend).

- **Board search** — collapsed it's a plain toolbar button (search glyph + "Search"); clicking **expands it in place** into a ~240px "Search this board" input (accent border) that owns the query. Escape clears + collapses, an empty blur collapses, a non-empty value keeps it open.
- **Person filter** — a toolbar button (accent-tint fill when active) opening a **searchable** list of teammates; multi-selecting members filters items by the people assigned to them. It originally rendered every member as a bare grid of avatars with no search box, which stopped being usable somewhere around a dozen people; it now uses the shared teammate picker, so it is searchable and keyboard-operable like every other place you pick a person.
- **Quick filters panel** — the Filter split-button (funnel + "Filter", showing **"Filter / {n}"** once selections exist; accent-tint fill when the panel is open or `n > 0`) opens a wide panel of **per-column facet columns**, each a scrollable list of multi-select chips. Chips are **OR within a column, AND across columns**. The panel **stays open across chip clicks** (only a click-away closes it); a header **"Showing X of Y tasks"** count and a **Clear all** reset are always present.
- **Save as new view** — both the Person popover and the Quick filters panel offer **"Save as new view"** (enabled only when a real handler exists and something is filtered). It persists the **current filter set as a new saved view tab** (a `views` row) via the existing view system. Edits to filters while viewing an already-saved view are **session-only** — they don't mutate the saved view until you save again as a new one. The save is guarded against **double-submit**.

Pure filter/codec logic is unit-tested (`tests/unit/plugins/mission-control/board-quick-filter.test.ts`); the toolbar components have component coverage (`search-board-button`, `person-filter-button`, `quick-filters-panel` under `tests/unit/plugins/mission-control/`).

### Finding a teammate — one search, everywhere

Every place Mission Control asks you to pick a person — assigning an item, `@`-mentioning someone
in an update, adding someone in Permissions, choosing an approver, and the toolbar's Person filter
— now answers a query the same way.

They did not used to. Each of those five was written separately and they disagreed about what a
search even searches: assigning matched a name, email or role; `@`-mentions matched the name only;
Permissions matched the name only; the approver field matched name or email; and the Person filter
had no search box at all. The visible consequence was that typing a teammate's **email** found them
when you were assigning work and found nothing when you were mentioning them, and typing a **role**
worked on exactly one screen.

All five now share one rule (`matchTeammates` in `src/shared/teammate.ts`): it searches display
name, handle, email and role, and ranks exact matches above ones that start with what you typed,
above ones that merely contain it. Every picker has a search box and responds to ↑ / ↓ / Enter /
Escape. A picker whose member list failed to load now says so, instead of showing "No matching
people" as though your search simply missed.

The same rule backs Team Chat's `@`-mention typeahead and a main-app picker
(`components/ui/TeammatePicker`), so a feature that needs "let the user pick a teammate" has one
to reach for rather than writing a sixth. Accent handling is passed in per surface rather than
baked in, because the main app and the plugin fold accented characters differently on purpose.

The promises this locks in — and the one thing deliberately left out of it, the fact that Mission
Control and Team Chat still identify the same human by two unreconciled ids — are written up in
`.claude/memory/contracts/teammate-primitive-contract.md`.

### Roadmap view (post-Phase-3 addition)

The **Roadmap tracker** view (`RoadmapView.tsx`) is a matrix of **time-bucket columns** (e.g. quarters/months) crossed with **status rows**. Each item lands in the cell where its date column's period meets its status value, giving a bird's-eye view of items across time horizons. Listed as `{ type: 'roadmap', label: 'Roadmap tracker' }` in `getAddableViews()` (`features/board/views/ViewSwitcher.tsx`). Like the other alternate views, it is a config-only view over the loaded board data — no backend change required.

### Location column + Map view (Ollert Port WI-6)

The **Location** column type (`'location'` in `PmColumnType`) stores a geographic position as `{ lat, lng, label }` (the `LocationCellValue` interface in `columnValue.ts`). Geocoding is powered by **Nominatim/OpenStreetMap** (free, no API key; `User-Agent: Omniscio/1.0`). The cell (`LocationCell.tsx`) shows a MapPin icon + truncated place name; clicking opens a **search combobox** (`LocationForm.tsx`) with debounced type-ahead, ARIA combobox pattern (arrow keys, Enter, Escape), and a "Remove location" action when a value is set. Listed in the Column Center's "Super useful" section.

The **Map** view (`MapView.tsx`, `{ type: 'map', label: 'Map' }` in `ADDABLE_VIEWS`) plots items with location values as **circle markers** on an interactive Leaflet.js map with OpenStreetMap tiles. Finds the board's first location column and builds a `LocatedItem[]` from items+values. Markers use the board's accent color, show item title + place label as tooltips (XSS-safe via `textContent`), and clicking a marker opens the item detail. The viewport auto-fits to all markers on load (with signature-based dedup to avoid re-fitting on unrelated data changes). Two empty states: "no location column" (add one first) and "no items with locations" (overlay on the map). An SR-only item list provides keyboard/screen-reader access. CSP: `connect-src` includes `nominatim.openstreetmap.org`; `img-src` already covers `https:` for OSM tiles.

Feature catalog: `col-location` (column node) and `view-map` (view node, `requires` edge to `col-location` in the feature graph). PM feature catalog: `view-map` in the `operations` preset with `dependencies: ['col-location']`.

### Workload view

The **Workload** view (`WorkloadView.tsx`, `{ type: 'workload' }` in `getAddableViews()`) shows per-person capacity utilization across a board. It groups items by a **People column** (auto-detected or configurable via `groupByColumnId`), sums each person's **effort** from an optional numeric effort column (`effortColumnId`; defaults to item count when no effort column is set), and renders a horizontal bar per person against a configurable **capacity** (`capacityHours`, default 40h/week). Over-capacity bars turn red (`--status-error-rgb`) with a translucent overflow overlay. A tick mark on each bar shows where the capacity threshold sits relative to the widest bar. The `WorkloadSummary` header (sticky) shows the capacity input and which column drives effort. `PersonAvatar` renders initials in accent-colored circles. Unassigned items (no people-column value) are grouped in a collapsible section at the bottom. Each person row is expandable to list individual items (clicking an item opens its detail via `onOpenItem`). Empty states: "no people column" (add one first) and "no items" (generic). Read-only mode hides the capacity input.

### Number formatting, Time Tracking, and Checklist columns

Three column additions to the **Column Center** catalog. **(1) Number formatting** — the existing `numbers` column gained per-column format settings: plain (locale-aware decimals), currency (configurable symbol + before/after position), or percent; configured via a `NumberFormatPopover` on hover. The pure formatting function `formatNumber()` lives in `columnValue.ts` (used by both the cell and `mc-render-helpers.ts`). **(2) Time Tracking** (`'time_tracking'` in `PmColumnType`) — reads elapsed time from the `time_entries` table via `session-bridge-time.ts` (the cell imports the bridge directly, not via `CellProps`). Shows compact duration (e.g. `2h 15m`) with a green dot when a timer is running; the running row ticks via a 1s `setInterval` (no grid-wide re-render). Start/stop toggle with a pending latch to prevent double-fire. **(3) Checklist** (`'checklist'` in `PmColumnType`) stores `{ items: [{text, done}] }` as JSON. The grid shows a progress pill (mini bar + `done/total`); clicking opens an inline popover editor with checkable items, text editing, remove buttons, and an Enter-to-add input. Auto-commits on every change. Both new column types have a `checklistSummary` (aggregate completion %) in the frontend registry. All three render in `mc-render-helpers.ts` for HTML snapshots. Frontend-only; backend column-type registry handlers are a separate follow-up.

### Kanban card actions + "⋯" menu (post-Phase-4 fast-follow)

Hovering a **Kanban** card reveals monday's two actions in the top-right: a **pencil** (opens the item card) and a **⋯** overflow menu (`features/board/views/KanbanCardMenu.tsx`, on the shared `Popover`). The menu mirrors monday's real order: **Open task · Move to ▸ · Duplicate ▸ · Copy name · Copy task link · Add subitem · Customize cards ▸ · Archive · Delete** (Delete confirms first). "Move to" and "Duplicate" open **right-side flyout submenus** (`features/board/CardMenuFlyout.tsx` — `SubmenuRow`/`FlyoutItem`, portaled out of the menu so they aren't clipped, flipping left near the screen edge): **Move to → Move to group** (lists the board's other groups → `patchItem({groupId})`) or **Move to board** (lists other boards from `/nav/tree` → a new `POST /items/:id/move-to-board`, which reassigns the board + a target group and drops the board-scoped column values); **Duplicate → without / with updates** (the existing `POST /items/:id/duplicate`, with `?includeUpdates=true` also cloning the conversation thread). **Copy name** / **Copy task link** write to the clipboard (the link is an `omniscio://mission-control/board/<id>?item=<id>` deep link; `BoardView` opens an item from a `?item=` param on load).

**Subitems** are real item rows tagged with `parent_item_id` (no migration — the column already existed). `GET /boards/:id/full` now returns top-level rows in `items` and child rows separately in `subitems`, so every other view is unchanged; the Kanban card renders its subitems as **nested name-only mini-cards** with a footer **count badge** and an inline **"+ Add sub-task"** composer (`POST /items/:id/subitems`), which the menu's **Add subitem** row opens and focuses. Backend behavior is covered by `amc-back`'s item-card-actions bun test (a package not tracked in this repo); the subitem grouping helper is unit-tested (`groupSubitemsByParent` in `tests/unit/plugins/mission-control/kanban-data.test.ts`).

### Kanban card coloring by field value

The Kanban toolbar exposes a **Color by** dropdown (`KanbanToolbar.tsx`) listing all Status and Priority columns on the board (columns with per-label colors). Selecting a column applies its label color as a **4px left-border stripe** on each card; selecting "None" removes coloring. The selection persists per-view in `ViewConfig.colorByColumnId`. New kanban views default to the first Status column. Color resolution (`KanbanView.tsx`'s `cardColorMap` memo) maps each item's value to its label's hex color via `statusLabelsOf` + `asStatusValue`; a deleted or invalid column gracefully falls back to no coloring. The drag overlay card also shows the stripe. Dropdown columns are excluded (their labels carry no color). Unit tests: tests/unit/plugins/mission-control/kanban-card-coloring.test.ts (no longer present — the `colorByColumnId`/`colorHex` wiring this section describes has no covering test in the current tree).

### Kanban swimlanes (second grouping axis)

The Kanban view gained a **swimlane** feature — a second row-level grouping axis perpendicular to the status columns. A **"Swimlane by"** dropdown in the Kanban toolbar (`SwimlanePicker` → the `Select` component, persists as `ViewConfig.swimlaneColumnId`) lets users group items horizontally by any **status, priority, people, dropdown, or checkbox** column; "None" disables swimlanes. When active, items stack into collapsible **`KanbanSwimlaneRow`** sections — each a filtered `KanbanBoard` with its own DnD context (namespaced via `id` prop, no cross-swimlane drag in V1). Items with no value land in a "**No value**" lane at the bottom; multi-assignee items (people column) appear in each assignee's lane. Subitems follow their parent's lane. The swimlane column picker excludes the current `groupByColumnId` to prevent the useless same-column-both-axes case. Data helpers: `computeSwimlanes()` + `SWIMLANE_GROUPABLE_TYPES` in `kanbanData.ts`; unit-tested in `tests/unit/plugins/mission-control/kanbanData.test.ts`.

### Grouped search (Phase 4a, extended)

Search Everything originally returned **items only**, grouped by board. A parity fast-follow **broadened it to four result types — items, boards, dashboards, and people — grouped by type**. **Private boards you're not a member of are excluded** (both the board results themselves and any items on them; dashboards and people are workspace/account-scoped and unaffected), consistent with the private-board enforcement above.

### Board customization features (desktop-only)

Six board-level customization features shipped as part of the Ollert Port work items. All are desktop-only — their IPC channels are not exposed as CLI routes and are blocked from the mobile/web WS bridge.

- **Celebrations** (`pm:celebrations:get/set`) — confetti animation on status completion
- **Accent color** (`pm:accent-color:get/set`) — per-board accent color override
- **Voting** (`pm:vote:toggle`, `pm:vote:data`) — item-level voting/polling
- **Board background** (`pm:board-background:set`) — custom board background image/color
- **Card aging** (`pm:card-aging:get/set`) — visual aging indicator on stale cards
- **Stickers** (`pm:item-stickers:list/add/update/remove`, `pm:item-stickers:changed` push) — decorative item stickers

**IPC:** 14 channels total (see `src/shared/ipc-channels/pm.ts`), all desktop-only. CLI-parity: classified as `feature-scoped-surface` exemptions (board-level visual settings with no external integration need).

### Board favorites (parity fast-follow)

The **star** in the board-info popover is now wired to **real favorites** — starring a board persists it as a favorite (it was previously a dead control).

### Stub cleanup (no dead buttons)

Every affordance that did nothing was either wired up or removed, so a control the user can see is a control that works. **Wired:** the view menu's **Share** item now opens the board share modal (access is per-board, not per-view, so it's labelled "Share board"); the board nav-row's **Add to favorites** now toggles the shared `FavoritesProvider`; and **User management** gained a real **Filter** menu (`features/users/UserFilterMenu.tsx`) — client-side status + role facets over the already-loaded member list, where an empty facet means "no constraint" and the two facets AND together. **Removed** (each needs a subsystem nobody has built): _Save as template_ (board nav + workspace ⋯), My Work's _Customize_, the Gantt toolbar's _Baseline_/filter/⋯ affordances, the dashboard widget's _Dock this widget_, the workspace _Feedback_ button (the plugin has no host bridge to Omniscio's intake), and the board-info popover's fake "🔔 Everything" notifications row. The **Apps** widget left the Add-widget menu, but `'apps'` stays in the persisted `WidgetType` union and `WidgetCard` still renders a placeholder body for it, so a dashboard that already saved one keeps working — it just can't be added again.

### Sub-pages

- [mc-dashboards.md](mc-dashboards.md) — Phase 4d workspace-scoped dashboards (grid, widgets, filters, export)
- [mc-automations.md](mc-automations.md) — Phase 5C board automation engine, PM event channel bridge, Phase 5H workflow engine
- [mc-templates.md](mc-templates.md) — Phase 5E project templates (save, category picker, deploy preview, editor)
- [mc-chat-bot.md](mc-chat-bot.md) — AI chat bot (board-aware NL queries, tool-calling, streaming)
- [pm-agent-trust.md](pm-agent-trust.md) — G13 trust levels and approval gates for AI agents on PM boards
- [pm-composability.md](pm-composability.md) — G3 feature interaction validation engine
- [pm-cross-board-intelligence.md](pm-cross-board-intelligence.md) — G2 cross-board reasoning infrastructure
- [pm-data-density.md](pm-data-density.md) — earn-the-right-to-speak gatekeeper (usage density tracking)
- [pm-discovery-callouts.md](pm-discovery-callouts.md) — proactive inbox suggestions (cross-tool nudges)
- [pm-email-integration.md](pm-email-integration.md) — link emails to PM items (Gmail, Agent Email, Supermail)
- [pm-feature-profiles.md](pm-feature-profiles.md) — G12 per-board progressive disclosure engine
- [pm-goals.md](pm-goals.md) — hierarchical goal tracking (auto/manual progress, board linking)
- [pm-my-work.md](pm-my-work.md) — cross-board personal task view (My Work)
- [pm-notification-preferences.md](pm-notification-preferences.md) — per-board notification controls
- [pm-onboarding-features.md](pm-onboarding-features.md) — import + post-import onboarding flows
- [pm-onboarding-help.md](pm-onboarding-help.md) — preset-scoped checklist + contextual tips
- [pm-portfolios.md](pm-portfolios.md) — cross-board aggregation (status summaries across boards)
- [pm-predictive-analytics.md](pm-predictive-analytics.md) — G9 velocity, forecasts, risk heatmaps
- [pm-sprints.md](pm-sprints.md) — time-boxed iteration management (velocity, retrospectives)
- [pm-team-chat-integration.md](pm-team-chat-integration.md) — PM + Team Chat bridge (mentions, task creation, status updates)

## Related

- [Mission Control](mission-control.md) — the overview page this continues.
- [Mission Control part 3](mission-control-part-3.md) — workspaces, sharing, people and permissions.
- [mc-dashboards.md](mc-dashboards.md) — pulling several boards into one view.

