Skip to content

Commit af0df56

Browse files
os-zhuangclaude
andauthored
feat(app-showcase): demonstrate the two list-view navigation modes (ADR-0053) (#2047)
Migrate the showcase off the deprecated in-view `tabs` mechanism and add a clean example of each of the two remaining, orthogonal list-nav modes. Views mode (Salesforce-style saved views) — task.view.ts Status presets (In Progress / Urgent / Done) are now *named views* with a base `ListView.filter`, surfaced in the object list's view switcher, instead of a default-view `list.tabs` row. Filters mode (Airtable-style user filters) — interface pages - Task Workbench (existing) demonstrates `userFilters.element: 'dropdown'`. - Task Triage (new, task-triage.page.ts) demonstrates the counterpart `userFilters.element: 'tabs'`: a row of pure filter presets ({ name, label, filter }) over a locked grid visualization. Registered in pages/index.ts, the explicit `pages: [...]` array in objectstack.config.ts, and as a `Task Triage (Tabs)` nav entry. Verified in the browser: the triage tabs filter (All 10 → In Progress 2 → Done 2 → All 10) and the object switcher shows the named views — with no redundant second tab row in either surface. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent c651f38 commit af0df56

5 files changed

Lines changed: 92 additions & 14 deletions

File tree

examples/app-showcase/objectstack.config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { ChartGalleryDashboard } from './src/dashboards/index.js';
1818
import { ShowcaseTaskDataset, ShowcaseProjectDataset } from './src/datasets/index.js';
1919
import { allReports } from './src/reports/index.js';
2020
import { allActions } from './src/actions/index.js';
21-
import { ComponentGalleryPage, ProjectWorkspacePage, ProjectDetailPage, TaskWorkbenchPage } from './src/pages/index.js';
21+
import { ComponentGalleryPage, ProjectWorkspacePage, ProjectDetailPage, TaskWorkbenchPage, TaskTriagePage } from './src/pages/index.js';
2222
import { allFlows } from './src/flows/index.js';
2323
import { allWebhooks } from './src/webhooks/index.js';
2424
import { allHooks } from './src/hooks/index.js';
@@ -143,7 +143,7 @@ export default defineStack({
143143
apps: [ShowcaseApp],
144144
portals: allPortals,
145145
views: [TaskViews, ProjectViews],
146-
pages: [ComponentGalleryPage, ProjectWorkspacePage, ProjectDetailPage, TaskWorkbenchPage],
146+
pages: [ComponentGalleryPage, ProjectWorkspacePage, ProjectDetailPage, TaskWorkbenchPage, TaskTriagePage],
147147
dashboards: [ChartGalleryDashboard],
148148
books: allBooks,
149149
datasets: [ShowcaseTaskDataset, ShowcaseProjectDataset],

examples/app-showcase/src/apps/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export const ShowcaseApp = App.create({
5353
{ id: 'nav_project_workspace', type: 'page', pageName: 'showcase_project_workspace', label: 'New Project + Tasks', icon: 'folder-plus' },
5454
// ADR-0047 interface mode: same object as nav_tasks, curated surface.
5555
{ id: 'nav_task_workbench', type: 'page', pageName: 'showcase_task_workbench', label: 'Task Workbench', icon: 'sliders-horizontal' },
56+
{ id: 'nav_task_triage', type: 'page', pageName: 'showcase_task_triage', label: 'Task Triage (Tabs)', icon: 'layout-list' },
5657
],
5758
},
5859
],

examples/app-showcase/src/pages/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { Page } from '@objectstack/spec/ui';
55
export { ProjectWorkspacePage } from './project-workspace.page.js';
66
export { ProjectDetailPage } from './project-detail.page.js';
77
export { TaskWorkbenchPage } from './task-workbench.page.js';
8+
export { TaskTriagePage } from './task-triage.page.js';
89

910
/**
1011
* Component Gallery — a custom page that places a spread of standard page
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
import type { Page } from '@objectstack/spec/ui';
4+
5+
/**
6+
* Task Triage — interface page demonstrating the **tabs** user-filter element
7+
* (Airtable "User filters" → Elements: Tabs). Counterpart to Task Workbench,
8+
* which demonstrates **dropdowns**: same source view, but end users switch a
9+
* single preset tab instead of combining per-field dropdowns.
10+
*
11+
* Tabs and dropdowns are the two mutually-exclusive Elements of one
12+
* `userFilters` config (ADR-0053). A tab is a pure filter preset
13+
* (`{ name, label, filter }`) — it never switches the view form; that is the
14+
* separate "Visualizations" axis (locked to grid here).
15+
*/
16+
export const TaskTriagePage: Page = {
17+
name: 'showcase_task_triage',
18+
label: 'Task Triage',
19+
type: 'list',
20+
object: 'showcase_task',
21+
kind: 'full',
22+
template: 'default',
23+
isDefault: false,
24+
regions: [],
25+
interfaceConfig: {
26+
source: 'showcase_task',
27+
// Inherit columns/filter/sort from the object's default view (ADR-0047).
28+
sourceView: 'default',
29+
30+
// End-user filter element: a row of preset tabs. `showAllRecords` adds the
31+
// leading unfiltered "All" tab automatically.
32+
userFilters: {
33+
element: 'tabs',
34+
showAllRecords: true,
35+
tabs: [
36+
{ name: 'in_progress', label: 'In Progress', filter: [{ field: 'status', operator: 'equals', value: 'in_progress' }] },
37+
{ name: 'urgent', label: 'Urgent', icon: 'flame', filter: [{ field: 'priority', operator: 'equals', value: 'urgent' }] },
38+
{ name: 'in_review', label: 'In Review', filter: [{ field: 'status', operator: 'equals', value: 'in_review' }] },
39+
{ name: 'done', label: 'Done', filter: [{ field: 'status', operator: 'equals', value: 'done' }] },
40+
],
41+
},
42+
43+
// Locked visualization (single-entry whitelist renders no form switcher).
44+
appearance: {
45+
showDescription: true,
46+
allowedVisualizations: ['grid'],
47+
},
48+
49+
userActions: {
50+
sort: true,
51+
search: true,
52+
filter: false,
53+
rowHeight: false,
54+
addRecordForm: false,
55+
},
56+
57+
showRecordCount: true,
58+
},
59+
};

examples/app-showcase/src/views/task.view.ts

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,11 @@ export const TaskViews = defineView({
2626
{ field: 'progress' },
2727
],
2828

29-
// ADR-0047 — in-view filter tabs (ViewTab presets). Each tab applies
30-
// its own filter rules on top of the view's base criteria; the first
31-
// tab is the unfiltered default. Filter tabs and dropdown filters are
32-
// MUTUALLY EXCLUSIVE on the toolbar (Airtable's Elements choice):
33-
// this view demonstrates tabs; the `tabular` view below demonstrates
34-
// dropdowns. Configuring both renders tabs only.
35-
tabs: [
36-
{ name: 'all_tasks', label: 'All', isDefault: true },
37-
{ name: 'in_progress', label: 'In Progress', filter: [{ field: 'status', operator: 'equals', value: 'in_progress' }] },
38-
{ name: 'urgent', label: 'Urgent', icon: 'flame', filter: [{ field: 'priority', operator: 'equals', value: 'urgent' }] },
39-
{ name: 'done', label: 'Done', filter: [{ field: 'status', operator: 'equals', value: 'done' }] },
40-
],
29+
// ADR-0053 — on the object's DEFAULT list (views mode) status presets are
30+
// *named views* in the switcher (Salesforce/Airtable "saved views"), not an
31+
// in-view tab row. See the `in_progress` / `urgent` / `done` listViews below.
32+
// The end-user filter ELEMENTS (Airtable "User filters": tabs / dropdowns)
33+
// belong to interface pages (filters mode) — see the *.page.ts examples.
4134

4235
// ADR-0047 — runtime visualization whitelist (Airtable "Appearance →
4336
// Visualizations"). Rendered as a compact dropdown in the toolbar's
@@ -49,6 +42,30 @@ export const TaskViews = defineView({
4942
},
5043

5144
listViews: {
45+
// ── Status presets (ADR-0053) — saved views with a base ListView.filter,
46+
// shown as switcher entries on the object's default list (views mode). ──
47+
in_progress: {
48+
label: 'In Progress',
49+
type: 'grid',
50+
data,
51+
columns: [{ field: 'title' }, { field: 'project' }, { field: 'assignee' }, { field: 'status' }, { field: 'priority' }, { field: 'due_date' }],
52+
filter: [{ field: 'status', operator: 'equals', value: 'in_progress' }],
53+
},
54+
urgent: {
55+
label: 'Urgent',
56+
type: 'grid',
57+
data,
58+
columns: [{ field: 'title' }, { field: 'project' }, { field: 'assignee' }, { field: 'status' }, { field: 'priority' }, { field: 'due_date' }],
59+
filter: [{ field: 'priority', operator: 'equals', value: 'urgent' }],
60+
},
61+
done: {
62+
label: 'Done',
63+
type: 'grid',
64+
data,
65+
columns: [{ field: 'title' }, { field: 'project' }, { field: 'assignee' }, { field: 'status' }, { field: 'due_date' }],
66+
filter: [{ field: 'status', operator: 'equals', value: 'done' }],
67+
},
68+
5269
// 0 ── Tabular ───────────────────────────────────────────────────────
5370
// ADR-0021 Phase 2: replaces the former `showcase_task_list` report
5471
// (a flat record list — a ListView concern, not analytics).

0 commit comments

Comments
 (0)