Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion examples/app-showcase/src/coverage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,12 @@ export const KIND_COVERAGE: Record<MetadataType, KindCoverage> = {
'revenue-pulse also demonstrates dashboard-level filters (framework#2501): dateRange + globalFilters driving two objects via per-widget filterBindings.',
},
app: { status: 'demonstrated', files: ['src/ui/apps/index.ts'] },
action: { status: 'demonstrated', files: ['src/ui/actions/index.ts'] },
action: {
status: 'demonstrated',
files: ['src/ui/actions/index.ts'],
notes:
'Every ActionType (script/url/flow/modal/api/form). `ActionParamGalleryAction` additionally exercises the ADR-0059 param-dialog widgets: one inline param per non-trivial type (richtext/color/date/select/number/autonumber) plus image/file uploads with multiple/accept/maxSize and the upload guard.',
},
report: { status: 'demonstrated', files: ['src/ui/reports/index.ts'] },
dataset: { status: 'demonstrated', files: ['src/ui/datasets/index.ts'] },

Expand Down
12 changes: 12 additions & 0 deletions examples/app-showcase/src/data/seed/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,18 @@ const contacts = defineSeed(Contact, {
{ name: '张伟', email: 'zhangwei@huaning.example', phone: '+86 21 5550 1111', company: '华宁科技', title: 'Engineering Manager', account: '华宁科技', stage: 'qualified' },
{ name: '王芳', email: 'wangfang@huaning.example', phone: '+86 21 5550 2222', company: '华宁科技', title: 'Procurement Director', account: '华宁科技', stage: 'working' },
{ name: '李雷', email: 'lilei@huaning.example', phone: '+86 21 5550 3333', company: '华宁科技', title: 'IT Specialist', account: '华宁科技', stage: 'new' },
// Bulk prospects under ONE account (Northwind) so its Account → Contacts
// related list exceeds a page and demonstrates server-side related-list
// pagination ($top/$skip, objectui#2711) on a fresh boot — every other
// account keeps a handful for a clean people picker.
...Array.from({ length: 24 }, (_, i) => ({
name: `Prospect ${String(i + 1).padStart(2, '0')}`,
email: `prospect${i + 1}@northwind.example`,
company: 'Northwind',
title: 'Sales Prospect',
account: 'Northwind',
stage: 'new',
})),
],
});

Expand Down
56 changes: 56 additions & 0 deletions examples/app-showcase/src/ui/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { defineAction } from '@objectstack/spec/ui';

const task = 'showcase_task';
const invoice = 'showcase_invoice';
const fieldZoo = 'showcase_field_zoo';

/**
* Action matrix — covers every `ActionType` (script / url / flow / modal /
Expand Down Expand Up @@ -172,6 +173,60 @@ export const SubmitForSignoffAction = defineAction({
refreshAfter: true,
});

/**
* script — the **action-param widget gallery** (ADR-0059). One inline param of
* every non-trivial widget type, so the `ActionParamDialog` renders each real
* field widget (not a text box): richtext editor, color picker, date picker,
* select, number, the AutoNumber widget for an `autonumber` param, and — the
* ⚠️ ones — `image`/`file` uploads through the ambient UploadProvider with
* `multiple` / `accept` / `maxSize` honored, and the **upload guard** (Confirm
* stays disabled while a file is still uploading). Lives on Field Zoo, the
* "one specimen of everything" object, next to its every-field-type record.
*
* The body just echoes the received keys — the point is the dialog, not a side
* effect — so it needs no capabilities.
*/
export const ActionParamGalleryAction = defineAction({
name: 'showcase_action_param_gallery',
label: 'Action Param Gallery',
icon: 'sparkles',
objectName: fieldZoo,
type: 'script',
params: [
{ name: 'p_text', type: 'text', label: 'Title', required: true, placeholder: 'A short title' },
{ name: 'p_richtext', type: 'richtext', label: 'Rich note', helpText: 'Renders the rich-text editor, not a plain textarea.' },
{
name: 'p_priority', type: 'select', label: 'Priority', defaultValue: 'normal',
options: [
{ label: 'Low', value: 'low' },
{ label: 'Normal', value: 'normal' },
{ label: 'High', value: 'high' },
],
},
{ name: 'p_date', type: 'date', label: 'Effective date' },
{ name: 'p_color', type: 'color', label: 'Accent color', defaultValue: '#7C3AED' },
// Spec `autonumber` param → the AutoNumber widget (read-only, auto-assigned).
{ name: 'p_reference', type: 'autonumber', label: 'Reference #' },
// ⚠️ image/file uploads: real widget + upload guard + multiple/accept/maxSize.
{ name: 'p_cover', type: 'image', label: 'Cover image', accept: ['image/*'], maxSize: 5 * 1024 * 1024 },
{
name: 'p_attachments', type: 'file', label: 'Attachments', multiple: true,
accept: ['application/pdf', 'image/*'], maxSize: 10 * 1024 * 1024,
helpText: 'Confirm stays disabled while a file is still uploading (ADR-0059 upload guard).',
},
],
body: {
language: 'js',
// No side effect — the value of this action is the dialog's widgets. Echo
// the keys the dialog collected so the result dialog shows something.
source: 'return { ok: true, received: Object.keys(input || {}) };',
capabilities: [],
},
successMessage: 'Params received — every widget type rendered through the shared field-widget map.',
locations: ['record_header', 'list_item'],
refreshAfter: false,
});

export const allActions = [
MarkDoneAction,
OpenDocsAction,
Expand All @@ -181,4 +236,5 @@ export const allActions = [
LogTimeAction,
NewTaskAction,
SubmitForSignoffAction,
ActionParamGalleryAction,
];
Loading