From 36756c15e93fc195d7d8027317def0f16e03adc0 Mon Sep 17 00:00:00 2001 From: os-zhuang Date: Sat, 20 Jun 2026 14:23:26 +0800 Subject: [PATCH] feat(app-showcase): My Work home, Approvals review queue, New Project wizard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three more composed enterprise surfaces (part 2 of the set begun in #2059), each exercising SDUI capabilities the showcase hadn't surfaced as real pages. My Work (showcase_my_work, type:home) — a role-aware workspace that composes LIVE data rather than one-component-per-type: • KPI hero row of live `object-metric` tiles (Open Tasks / In Review / At-Risk Projects) — async aggregates with per-tile filters; • a personal work queue: `object-grid` filtered to the signed-in user via the `{current_user_id}` token (records I own); • a role-gated card (`properties.visible` CEL) only the admin sees — demonstrating per-user differentiated rendering. Approvals (showcase_review_queue, type:list) — the human side of the ~20 approval/review flows: an interface page over tasks `in_review` (the work awaiting a decision), the source object's action surfaced as a toolbar button ("Mark done" = approve & complete), drawer to inspect before deciding. New Project Wizard (showcase_new_project_wizard) — `object-form` with `formType:'wizard'` + step indicator: Basics → Status & Health → Budget, the first page to actually walk a stepped create flow. All three wired into nav + objectstack.config. Browser-verified (:5181): My Work KPIs resolve (8 / 2 / 1) and the owner-filtered queue + role card render; Approvals shows the 2 in-review tasks with the approve button; the wizard navigates Basics → Status (Step 2 of 3). typecheck + 20 tests pass. Co-Authored-By: Claude Opus 4.8 --- examples/app-showcase/objectstack.config.ts | 4 +- examples/app-showcase/src/apps/index.ts | 3 + examples/app-showcase/src/pages/index.ts | 3 + .../app-showcase/src/pages/my-work.page.ts | 62 +++++++++++++++++++ .../src/pages/new-project-wizard.page.ts | 43 +++++++++++++ .../src/pages/review-queue.page.ts | 38 ++++++++++++ 6 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 examples/app-showcase/src/pages/my-work.page.ts create mode 100644 examples/app-showcase/src/pages/new-project-wizard.page.ts create mode 100644 examples/app-showcase/src/pages/review-queue.page.ts diff --git a/examples/app-showcase/objectstack.config.ts b/examples/app-showcase/objectstack.config.ts index 34e95424f7..feb2ec7333 100644 --- a/examples/app-showcase/objectstack.config.ts +++ b/examples/app-showcase/objectstack.config.ts @@ -18,7 +18,7 @@ import { ChartGalleryDashboard, OpsDashboard } from './src/dashboards/index.js'; import { ShowcaseTaskDataset, ShowcaseProjectDataset } from './src/datasets/index.js'; import { allReports } from './src/reports/index.js'; import { allActions } from './src/actions/index.js'; -import { ComponentGalleryPage, ProjectWorkspacePage, ProjectDetailPage, TaskWorkbenchPage, TaskTriagePage, TaskBoardPage, TaskCalendarPage, TaskGalleryPage, TaskSchedulePage, TaskTimelinePage, TaskMapPage, TaskAllViewsPage, ActiveProjectsPage, TaskDetailPage, AccountDetailPage } from './src/pages/index.js'; +import { ComponentGalleryPage, ProjectWorkspacePage, ProjectDetailPage, TaskWorkbenchPage, TaskTriagePage, TaskBoardPage, TaskCalendarPage, TaskGalleryPage, TaskSchedulePage, TaskTimelinePage, TaskMapPage, TaskAllViewsPage, ActiveProjectsPage, TaskDetailPage, AccountDetailPage, ReviewQueuePage, NewProjectWizardPage, MyWorkPage } from './src/pages/index.js'; import { allFlows } from './src/flows/index.js'; import { allWebhooks } from './src/webhooks/index.js'; import { allHooks } from './src/hooks/index.js'; @@ -143,7 +143,7 @@ export default defineStack({ apps: [ShowcaseApp], portals: allPortals, views: [TaskViews, ProjectViews], - pages: [ComponentGalleryPage, ProjectWorkspacePage, ProjectDetailPage, TaskWorkbenchPage, TaskTriagePage, TaskBoardPage, TaskCalendarPage, TaskGalleryPage, TaskSchedulePage, TaskTimelinePage, TaskMapPage, TaskAllViewsPage, ActiveProjectsPage, TaskDetailPage, AccountDetailPage], + pages: [ComponentGalleryPage, ProjectWorkspacePage, ProjectDetailPage, TaskWorkbenchPage, TaskTriagePage, TaskBoardPage, TaskCalendarPage, TaskGalleryPage, TaskSchedulePage, TaskTimelinePage, TaskMapPage, TaskAllViewsPage, ActiveProjectsPage, TaskDetailPage, AccountDetailPage, ReviewQueuePage, NewProjectWizardPage, MyWorkPage], dashboards: [ChartGalleryDashboard, OpsDashboard], books: allBooks, datasets: [ShowcaseTaskDataset, ShowcaseProjectDataset], diff --git a/examples/app-showcase/src/apps/index.ts b/examples/app-showcase/src/apps/index.ts index 7bb0c4dc24..e1452773b6 100644 --- a/examples/app-showcase/src/apps/index.ts +++ b/examples/app-showcase/src/apps/index.ts @@ -50,6 +50,9 @@ export const ShowcaseApp = App.create({ label: 'Pages', icon: 'layout', children: [ + { id: 'nav_my_work', type: 'page', pageName: 'showcase_my_work', label: 'My Work', icon: 'home' }, + { id: 'nav_review_queue', type: 'page', pageName: 'showcase_review_queue', label: 'Approvals', icon: 'check-check' }, + { id: 'nav_new_project_wizard', type: 'page', pageName: 'showcase_new_project_wizard', label: 'New Project (Wizard)', icon: 'wand-2' }, { id: 'nav_gallery', type: 'page', pageName: 'showcase_component_gallery', label: 'Component Gallery', icon: 'layout-template' }, { id: 'nav_project_workspace', type: 'page', pageName: 'showcase_project_workspace', label: 'New Project + Tasks', icon: 'folder-plus' }, // ADR-0047 interface mode: same object as nav_tasks, curated surface. diff --git a/examples/app-showcase/src/pages/index.ts b/examples/app-showcase/src/pages/index.ts index acad0f337b..dd1c96def1 100644 --- a/examples/app-showcase/src/pages/index.ts +++ b/examples/app-showcase/src/pages/index.ts @@ -9,6 +9,9 @@ export { TaskTriagePage } from './task-triage.page.js'; export { ActiveProjectsPage } from './active-projects.page.js'; export { TaskDetailPage } from './task-detail.page.js'; export { AccountDetailPage } from './account-detail.page.js'; +export { ReviewQueuePage } from './review-queue.page.js'; +export { NewProjectWizardPage } from './new-project-wizard.page.js'; +export { MyWorkPage } from './my-work.page.js'; export { TaskBoardPage, TaskCalendarPage, diff --git a/examples/app-showcase/src/pages/my-work.page.ts b/examples/app-showcase/src/pages/my-work.page.ts new file mode 100644 index 0000000000..29c8240353 --- /dev/null +++ b/examples/app-showcase/src/pages/my-work.page.ts @@ -0,0 +1,62 @@ +// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. + +import type { Page } from '@objectstack/spec/ui'; + +/** + * My Work — a role-aware workspace home that *composes* live data the way a + * real landing page does, instead of listing one component per type: + * • a KPI hero row of live `object-metric` tiles (team throughput); + * • a personal work queue — `object-grid` filtered to the signed-in user + * via the `{current_user_id}` token (records I own); + * • a role-gated card (`visible` expression) that only the admin sees — + * demonstrating per-user differentiated rendering. + */ +export const MyWorkPage: Page = { + name: 'showcase_my_work', + label: 'My Work', + type: 'home', + template: 'header-sidebar-main', + isDefault: false, + kind: 'full', + regions: [ + { + name: 'header', + width: 'full', + components: [ + { type: 'page:header', properties: { title: 'My Work', subtitle: 'Your queue, the team’s throughput, and what needs attention.' } }, + ], + }, + { + name: 'main', + width: 'large', + components: [ + // KPI hero row — live aggregates over the team's work. + { type: 'object-metric', properties: { objectName: 'showcase_task', label: 'Open Tasks', icon: 'list-checks', aggregate: { field: 'id', function: 'count' }, filter: { status: { $ne: 'done' } } } }, + { type: 'object-metric', properties: { objectName: 'showcase_task', label: 'In Review', icon: 'eye', aggregate: { field: 'id', function: 'count' }, filter: { status: 'in_review' } } }, + { type: 'object-metric', properties: { objectName: 'showcase_project', label: 'At-Risk Projects', icon: 'alert-triangle', aggregate: { field: 'id', function: 'count' }, filter: { health: 'red' } } }, + { type: 'element:divider', properties: {} }, + // Personal work queue — records owned by the signed-in user. + { + type: 'object-grid', + properties: { + objectName: 'showcase_task', + columns: ['title', 'project', 'status', 'priority', 'due_date'], + filters: [['owner_id', '=', '{current_user_id}']], + }, + }, + ], + }, + { + name: 'sidebar', + width: 'small', + components: [ + // Role-gated: only the admin sees this card — differentiated rendering. + { + type: 'page:card', + properties: { title: 'Leadership View', visible: "current_user.email == 'admin@objectos.ai'" }, + }, + { type: 'element:text', properties: { content: 'Tip: open Delivery Operations for the org-wide dashboard.' } }, + ], + }, + ], +}; diff --git a/examples/app-showcase/src/pages/new-project-wizard.page.ts b/examples/app-showcase/src/pages/new-project-wizard.page.ts new file mode 100644 index 0000000000..548254bc0b --- /dev/null +++ b/examples/app-showcase/src/pages/new-project-wizard.page.ts @@ -0,0 +1,43 @@ +// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. + +import type { Page } from '@objectstack/spec/ui'; + +/** + * New Project Wizard — a multi-step (wizard) form surface. The showcase + * defines wizard/tabbed/split form view *types* but had no page that actually + * walks a user through a stepped create flow. This renders `object-form` with + * `formType: 'wizard'` directly: Basics → Status → Budget, with a step + * indicator, over showcase_project. + */ +export const NewProjectWizardPage: Page = { + name: 'showcase_new_project_wizard', + label: 'New Project (Wizard)', + type: 'app', + kind: 'full', + template: 'default', + isDefault: false, + regions: [ + { + name: 'main', + width: 'large', + components: [ + { + type: 'object-form', + properties: { + objectName: 'showcase_project', + mode: 'create', + formType: 'wizard', + showStepIndicator: true, + title: 'Create a Project', + description: 'A three-step wizard — basics, status, then budget & schedule.', + sections: [ + { label: 'Basics', description: 'Name the project and bind its account.', fields: ['name', 'account', 'owner'] }, + { label: 'Status & Health', description: 'Where does it stand today?', fields: ['status', 'health'] }, + { label: 'Budget & Schedule', description: 'Money and dates.', fields: ['budget', 'spent', 'start_date', 'end_date'] }, + ], + }, + }, + ], + }, + ], +}; diff --git a/examples/app-showcase/src/pages/review-queue.page.ts b/examples/app-showcase/src/pages/review-queue.page.ts new file mode 100644 index 0000000000..6d2f2cf03f --- /dev/null +++ b/examples/app-showcase/src/pages/review-queue.page.ts @@ -0,0 +1,38 @@ +// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. + +import type { Page } from '@objectstack/spec/ui'; + +/** + * Approvals · Review Queue — the human side of the approval / review flows. + * The showcase has ~20 approval & review flows but no UI where a reviewer + * sees what is waiting on them; this is that surface. + * + * An interface (list) page over tasks currently `in_review` — the work + * awaiting a decision — with the source object's actions surfaced as toolbar + * buttons (Mark done = approve & complete) and a drawer to inspect each item. + * Tabs let the reviewer pivot to urgent or blocked work. + */ +export const ReviewQueuePage: Page = { + name: 'showcase_review_queue', + label: 'Approvals', + type: 'list', + object: 'showcase_task', + kind: 'full', + template: 'default', + isDefault: false, + regions: [], + interfaceConfig: { + source: 'showcase_task', + columns: ['title', 'project', 'assignee', 'priority', 'due_date'], + // Always-on base filter: only items awaiting review reach this queue. + filterBy: [{ field: 'status', operator: 'equals', value: 'in_review' }], + sort: [{ field: 'due_date', order: 'asc' }], + appearance: { showDescription: true, allowedVisualizations: ['grid'] }, + userActions: { sort: true, search: true, filter: false, rowHeight: false, addRecordForm: false }, + // Object actions as toolbar buttons — "approve & complete" the reviewed item. + buttons: ['showcase_mark_done'], + // Click a row → drawer with the full record to review before deciding. + recordAction: 'drawer', + showRecordCount: true, + }, +};