The first page every user sees, and the only one they cannot switch off. Two routes, and the distinction matters:
| Route | What it is |
|---|---|
/dashboard | My Work — the personal list: tasks, bugs, stories and form tickets assigned to you, across every workspace. |
/dashboard/overview | The widget dashboard — a configurable grid. |
They swapped places. My Work used to live at /tickets, under a nav item
called "Tickets", on a page titled "My Work", next to a different nav item
also called "My Work" pointing at a thinner version of the same list. It took
over /dashboard because "what is on my plate?" is what people open the app to
find out. /tickets and /my-work are both redirects now; ticket detail
pages at /tickets/{id} are unmoved.
The widget grid#
/dashboard/overview renders from two config files, and neither holds data:
frontend/src/config/dashboardWidgets.ts— 84 widget definitions, each with an id, a category, a default size, and the app it belongs to.frontend/src/config/widgetRegistry.tsx— maps a widget id to the component that renders it. The split exists so the definition list can be filtered by app access without importing every widget's code.
A widget whose app the workspace has switched off is not rendered, and not
offered in the picker: getAccessibleWidgets filters the definitions against
the resolved app access. Adding a widget means adding to both files.
Presets#
frontend/src/config/dashboardPresets.ts defines seven starting layouts —
developer, manager, product, hr, support, sales, admin. A preset
is the initial grid for someone who has never arranged one; after that their
layout is their own and the preset is not consulted again.
Presets are chosen during onboarding from the role the user picks. Getting this wrong is recoverable — the picker is on the page — but it decides what the product looks like on day one, which is most of what a first impression is.
Adding a widget#
- Add the definition to
DASHBOARD_WIDGETSindashboardWidgets.ts, with theappIdit belongs to so access filtering works. - Add the component to
widgetRegistry.tsxunder the same id. - If it needs props from the page rather than fetching its own data, add its
id to
HOST_PROP_WIDGETS. - Add it to whichever presets should start with it.
Common pitfalls#
/dashboardis not the widget grid. A link meaning "the dashboard" in the widget sense must say/dashboard/overview.- A widget with no
appIdis visible to everyone regardless of workspace app access, because "belongs to no app" reads as "not access-controlled". HOST_PROP_WIDGETSis opt-in. A widget not in that set is expected to fetch its own data; put a prop-driven one outside it and it renders empty.