Subchapter 1.113
references/dashboard-page/DRAFT_TEMPLATE.mdMarkdown10 KBView on GitHub
Start here for any Dashboard Page request, before writing a shell, provider, or router. Page templates ship inside the installed @wix/patterns. This file covers finding and placing their files in a Wix CLI app; the package’s chooses the template and explains its wiring.
Page Templates.mdGo through WIX_PATTERNS_DOCS.md § Prerequisites first for <pkgRoot>; every path below is relative to it.
One call lists every page template with its files, plus the guide that chooses between them:
node <pkgRoot>/bin/patterns-lookup.cjs --templatesAdd known symbols to the same call (--templates Table useTableCollection). Without the script, probe the Templates entries in <pkgRoot>/dist/docs/index.json once.
No Templates entries means the install predates them — upgrade @wix/patterns. Do not rebuild a page from memory or from an older copy of this skill instead: the templates exist so the shell, provider nesting and router wiring come from the package.
Read <pkgRoot>/dist/docs/Page Templates.md — choose the editable collection template by default, then account for the three read-only cases below, settings, and where the rows come from (your own fetch, or a CMS collection).Read the chosen template’s page (dist/docs/<entry file>) — its files, and the wiring to keep. Most of that wiring passes tsc and wix build when wrong and fails only in the browser, which is why Step 5’s Preview is not optional for a routed page.Editing existing records is the default. A site owner who opens a record from a dashboard table expects to fix what they see; a read-only page they never asked for sends them to a follow-up prompt. So every collection row opens an editable EntityPage with a working form and save operation, for CMS and SDK data alike. “Table”, “list”, “show” or “view” alone do not mean read-only. Apply this choice to each collection when a page contains several.
Use the Collection and Read-Only Detail Template instead in exactly three cases:
An explicit report-only or export-only request needs no detail page at all.
An existing read-only page in the project shows how to call the data, not what to build; start from the template.
Add create, delete or settings only when the workflow needs them; otherwise remove the template’s create action and /new route. For a read-only CMS page, keep the schema-driven collection and adapt its detail route to the read-only layout rather than exposing the entity form.
Scaffold once — every template is one extension, however many routes it has:
wix generate --params '{"extensionType":"DASHBOARD_PAGE","title":"<title>","route":"<route>"}'route takes no leading slash — support-tickets, not /support-tickets. See Dashboard Page → Scaffold.
Then copy every file --templates lists for it into the generated folder (src/extensions/dashboard/pages/<feature>/). They import one another by relative path, so keep their names relative to each other; one file on its own does not compile.
The template’s page.tsx is the entry file, and the CLI generated none by that name: put its contents into the component file the builder’s component path points at (e.g. employee-shifts.tsx), and add no separate page.tsx, which nothing loads. Leave the builder file and the src/extensions.ts registration as the CLI wrote them.
Rename Items / Item to your feature and entity, in file names and identifiers alike.
Route paths stay page-relative (/, /:id, /new) — never prefix them with <route> (DASHBOARD_PAGE.md).
Theme the entry file. In it, replace the template’s WixDesignSystemProvider and its @wix/design-system/styles.global.css import with the app’s BusinessManagerTheme — written once per app (BUSINESS_MANAGER_THEME.md § 2), kept in the entry file so it sits above WixPatternsProvider. Directly inside it, wrap the app component in BusinessManagerPatternsTheme (BUSINESS_MANAGER_PATTERNS.md). Business Manager passes none of the redesign through the extension’s iframe, so the bare provider renders the pre-redesign look while tsc, wix build and wix preview all pass.
If the page needs tabs: Start from a Patterns template. When tabs navigate between different sections, use WDS Tabs, reuse the template’s provider and router, and keep Patterns components for managing each section’s entities. Adapt the routes and navigation together so opening an entity and returning selects the correct section. When tabs only filter the same entity list, pass Patterns TabsFilter to the Table’s tabs prop.
Each template’s data file (items-api.ts, settings-api.ts) is an in-memory stand-in with the signatures the pages call. Replace the bodies, keep the signatures:
collection.ts takes the full scoped id, <app-namespace>/<idSuffix> for a collection your extension ships (DATA_COLLECTION.md). The collection does not exist until the app is released (LIFECYCLE.md).@wix/patterns-cms pins one exact @wix/patterns version, and the template’s page says why a second copy breaks the page silently.
Already in package.json (some app templates ship both): don’t install it again — go straight to the check. Not there: install it, then align @wix/patterns to its pin:
npm install @wix/patterns-cms
npm install @wix/patterns@$(node -p "require('@wix/patterns-cms/package.json').dependencies['@wix/patterns']")Either way, confirm a single copy:
npm dedupe
find node_modules -path '*@wix/patterns/package.json' -not -path '*/dist/*'More than one copy breaks shared context despite passing tsc and build. Align the versions with the second install command above, then check again.
On the hand-wired path the data file turns one search term into a filter over several fields — an OR. This is where search ships broken: it renders, it reaches the query, and still returns every row. (The CMS template’s source does this itself.)
There is no shared free-text operator; the shape differs per endpoint. Read its Supported Filters page (QUERY_AND_PAGING.md), then $or one clause per identity field it lists. Never route the term to a single field by its shape — a measured run shipped this, and one branch is always dead:
query = term.includes('@')
? query.startsWith('loginEmail', term)
: query.startsWith('contact.firstName', term); // a surname matches nothing, everstartsWith is prefix-only too — “Smith” never finds “John Smith” — so prefer the containment operator when the endpoint declares one, and say which you used in noResultsState.
Whatever the shape, prove it narrows. Run one term you expect to hit a known row and one you expect to hit nothing, and check the row count changes for both.
SummaryBar unless the request asked for one — a named total, count or “how many / how much” figure. The templates ship without one on purpose (SKILL.md § Step 2).useSelector (TABLE_STATE.md).