@tailor-platform/app-shell 1.15.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/dist/app-shell-core.css +1 -1
  2. package/dist/app-shell.d.ts +208 -114
  3. package/dist/app-shell.js +3449 -3446
  4. package/dist/{appshell-DLVBFo5J.js → appshell-B0wh5Lq0.js} +625 -512
  5. package/dist/style-entry.d.ts +116 -96
  6. package/dist/testing.d.ts +116 -96
  7. package/dist/testing.js +1 -1
  8. package/dist/vite-plugin.d.ts +116 -96
  9. package/package.json +8 -8
  10. package/skills/app-shell-patterns/SKILL.md +101 -14
  11. package/skills/app-shell-patterns/references/api/create-ai-gateway-client.md +115 -0
  12. package/skills/app-shell-patterns/references/api/create-typed-paths.md +117 -0
  13. package/skills/app-shell-patterns/references/api/define-i18n-labels.md +143 -0
  14. package/skills/app-shell-patterns/references/api/define-module.md +247 -0
  15. package/skills/app-shell-patterns/references/api/define-resource.md +307 -0
  16. package/skills/app-shell-patterns/references/api/guards/hidden.md +202 -0
  17. package/skills/app-shell-patterns/references/api/guards/overview.md +354 -0
  18. package/skills/app-shell-patterns/references/api/guards/pass.md +129 -0
  19. package/skills/app-shell-patterns/references/api/guards/redirect-to.md +307 -0
  20. package/skills/app-shell-patterns/references/api/router/use-location.md +240 -0
  21. package/skills/app-shell-patterns/references/api/router/use-navigate.md +138 -0
  22. package/skills/app-shell-patterns/references/api/router/use-params.md +218 -0
  23. package/skills/app-shell-patterns/references/api/use-ai-chat.md +110 -0
  24. package/skills/app-shell-patterns/references/api/use-app-shell-config.md +126 -0
  25. package/skills/app-shell-patterns/references/api/use-app-shell-data.md +146 -0
  26. package/skills/app-shell-patterns/references/api/use-app-shell.md +143 -0
  27. package/skills/app-shell-patterns/references/api/use-auth.md +145 -0
  28. package/skills/app-shell-patterns/references/api/use-override-breadcrumb.md +74 -0
  29. package/skills/app-shell-patterns/references/api/use-page-meta.md +96 -0
  30. package/skills/app-shell-patterns/references/api/use-register-command-palette-actions.md +90 -0
  31. package/skills/app-shell-patterns/references/api/use-resolved-locale.md +67 -0
  32. package/skills/app-shell-patterns/references/api/use-route-error.md +214 -0
  33. package/skills/app-shell-patterns/references/api/use-theme.md +108 -0
  34. package/skills/app-shell-patterns/references/api/use-time-zone.md +80 -0
  35. package/skills/app-shell-patterns/references/api/use-toast.md +107 -0
  36. package/skills/app-shell-patterns/references/components/action-panel.md +168 -0
  37. package/skills/app-shell-patterns/references/components/activity-card.md +142 -0
  38. package/skills/app-shell-patterns/references/components/ai-chat.md +315 -0
  39. package/skills/app-shell-patterns/references/components/alert.md +202 -0
  40. package/skills/app-shell-patterns/references/components/app-shell.md +530 -0
  41. package/skills/app-shell-patterns/references/components/appearance-switcher.md +87 -0
  42. package/skills/app-shell-patterns/references/components/attachment.md +138 -0
  43. package/skills/app-shell-patterns/references/components/autocomplete.md +278 -0
  44. package/skills/app-shell-patterns/references/components/avatar.md +80 -0
  45. package/skills/app-shell-patterns/references/components/badge.md +309 -0
  46. package/skills/app-shell-patterns/references/components/button.md +164 -0
  47. package/skills/app-shell-patterns/references/components/card.md +109 -0
  48. package/skills/app-shell-patterns/references/components/checkbox.md +169 -0
  49. package/skills/app-shell-patterns/references/components/combobox.md +318 -0
  50. package/skills/app-shell-patterns/references/components/command-palette.md +413 -0
  51. package/skills/app-shell-patterns/references/components/csv-importer.md +214 -0
  52. package/skills/app-shell-patterns/references/components/data-table.md +953 -0
  53. package/skills/app-shell-patterns/references/components/date-picker.md +248 -0
  54. package/skills/app-shell-patterns/references/components/default-header.md +75 -0
  55. package/skills/app-shell-patterns/references/components/default-sidebar.md +136 -0
  56. package/skills/app-shell-patterns/references/components/description-card.md +569 -0
  57. package/skills/app-shell-patterns/references/components/dialog.md +164 -0
  58. package/skills/app-shell-patterns/references/components/document-progress-card.md +154 -0
  59. package/skills/app-shell-patterns/references/components/form.md +378 -0
  60. package/skills/app-shell-patterns/references/components/global-header-layout.md +132 -0
  61. package/skills/app-shell-patterns/references/components/grid.md +184 -0
  62. package/skills/app-shell-patterns/references/components/input.md +83 -0
  63. package/skills/app-shell-patterns/references/components/layout.md +539 -0
  64. package/skills/app-shell-patterns/references/components/menu.md +218 -0
  65. package/skills/app-shell-patterns/references/components/metric-card.md +94 -0
  66. package/skills/app-shell-patterns/references/components/select.md +283 -0
  67. package/skills/app-shell-patterns/references/components/sheet.md +237 -0
  68. package/skills/app-shell-patterns/references/components/sidebar-group.md +466 -0
  69. package/skills/app-shell-patterns/references/components/sidebar-item.md +458 -0
  70. package/skills/app-shell-patterns/references/components/sidebar-layout.md +258 -0
  71. package/skills/app-shell-patterns/references/components/spinner.md +81 -0
  72. package/skills/app-shell-patterns/references/components/table.md +188 -0
  73. package/skills/app-shell-patterns/references/components/tabs.md +158 -0
  74. package/skills/app-shell-patterns/references/components/textarea.md +122 -0
  75. package/skills/app-shell-patterns/references/components/timeline.md +317 -0
  76. package/skills/app-shell-patterns/references/components/toolbar.md +138 -0
  77. package/skills/app-shell-patterns/references/components/tooltip.md +144 -0
  78. package/skills/app-shell-patterns/references/components/with-guard.md +445 -0
  79. package/skills/app-shell-patterns/references/concepts/authentication.md +301 -0
  80. package/skills/app-shell-patterns/references/concepts/custom-components.md +59 -0
  81. package/skills/app-shell-patterns/references/concepts/file-based-routing.md +350 -0
  82. package/skills/app-shell-patterns/references/{fundamental → concepts}/graphql.md +11 -4
  83. package/skills/app-shell-patterns/references/concepts/modules-and-resources.md +229 -0
  84. package/skills/app-shell-patterns/references/concepts/routing-navigation.md +387 -0
  85. package/skills/app-shell-patterns/references/concepts/sidebar-navigation.md +388 -0
  86. package/skills/app-shell-patterns/references/{fundamental/design-system.md → concepts/styling-theming.md} +258 -110
  87. package/skills/app-shell-patterns/references/migrations.md +34 -0
  88. package/skills/app-shell-patterns/references/pages/{detail.md → document-detail.md} +4 -27
  89. package/skills/app-shell-patterns/references/patterns/form-composer.md +43 -45
  90. package/skills/app-shell-patterns/references/patterns/form-modal.md +39 -100
  91. package/skills/app-shell-patterns/references/patterns/form-sectioned.md +12 -38
  92. package/skills/app-shell-patterns/references/patterns/form-single-page.md +11 -32
  93. package/skills/app-shell-patterns/references/patterns/form-wizard.md +12 -53
  94. package/skills/app-shell-patterns/references/patterns/interaction-confirm.md +11 -27
  95. package/skills/app-shell-patterns/references/patterns/interaction-multi-select.md +13 -27
  96. package/skills/app-shell-patterns/references/patterns/interaction-toast.md +10 -34
  97. package/skills/app-shell-patterns/references/patterns/list-dense-scan.md +11 -86
  98. package/skills/app-shell-patterns/references/fundamental/components.md +0 -1009
@@ -0,0 +1,214 @@
1
+ ---
2
+ title: useRouteError
3
+ description: Hook to access error details in custom error boundaries
4
+ ---
5
+
6
+ <!-- GENERATED by docs-kit from docs-src/hooks/use-route-error.docs.outline.md — do not edit by hand. -->
7
+
8
+ # useRouteError
9
+
10
+ React Router hook to access error details within a custom error boundary component. Re-exported from `react-router` for convenience.
11
+
12
+ ## Signature
13
+
14
+ ```typescript
15
+ const useRouteError: () => unknown;
16
+ ```
17
+
18
+ ## Return Value
19
+
20
+ | Type | Description |
21
+ | --------- | ---------------------------------------------------------------------------- |
22
+ | `unknown` | The error that was thrown (typically `Error` object or route error response) |
23
+
24
+ ## Usage
25
+
26
+ ### Basic Error Boundary
27
+
28
+ ```typescript
29
+ import { useRouteError } from "@tailor-platform/app-shell";
30
+
31
+ function ErrorBoundary() {
32
+ const error = useRouteError();
33
+
34
+ const message = error instanceof Error
35
+ ? error.message
36
+ : "An unexpected error occurred";
37
+
38
+ return (
39
+ <div>
40
+ <h1>Something went wrong</h1>
41
+ <p>{message}</p>
42
+ <button onClick={() => window.location.reload()}>
43
+ Try Again
44
+ </button>
45
+ </div>
46
+ );
47
+ }
48
+ ```
49
+
50
+ ### Use in Module Definition
51
+
52
+ ```typescript
53
+ import { defineModule } from "@tailor-platform/app-shell";
54
+
55
+ const myModule = defineModule({
56
+ path: "dashboard",
57
+ component: DashboardPage,
58
+ errorBoundary: <ErrorBoundary />,
59
+ resources: [...]
60
+ });
61
+ ```
62
+
63
+ ### Use in Resource Definition
64
+
65
+ ```typescript
66
+ const productsModule = defineModule({
67
+ path: "products",
68
+ component: ProductsPage,
69
+ resources: [
70
+ defineResource({
71
+ path: ":id",
72
+ component: ProductDetail,
73
+ errorBoundary: <ErrorBoundary />,
74
+ }),
75
+ ],
76
+ });
77
+ ```
78
+
79
+ ### Advanced Error Handling
80
+
81
+ ```typescript
82
+ import { isRouteErrorResponse, useRouteError } from "@tailor-platform/app-shell";
83
+
84
+ function ErrorBoundary() {
85
+ const error = useRouteError();
86
+
87
+ // Handle route error responses (404, 500, etc.)
88
+ if (isRouteErrorResponse(error)) {
89
+ return (
90
+ <div>
91
+ <h1>{error.status} {error.statusText}</h1>
92
+ <p>{error.data}</p>
93
+ </div>
94
+ );
95
+ }
96
+
97
+ // Handle JavaScript errors
98
+ if (error instanceof Error) {
99
+ return (
100
+ <div>
101
+ <h1>Error</h1>
102
+ <p>{error.message}</p>
103
+ <pre>{error.stack}</pre>
104
+ </div>
105
+ );
106
+ }
107
+
108
+ // Handle unknown errors
109
+ return (
110
+ <div>
111
+ <h1>Unknown Error</h1>
112
+ <p>Something went wrong</p>
113
+ </div>
114
+ );
115
+ }
116
+ ```
117
+
118
+ ### With Error Logging
119
+
120
+ ```typescript
121
+ function ErrorBoundary() {
122
+ const error = useRouteError();
123
+
124
+ useEffect(() => {
125
+ // Log to error tracking service
126
+ console.error("Route error:", error);
127
+ // analytics.trackError(error);
128
+ }, [error]);
129
+
130
+ return (
131
+ <div>
132
+ <h1>Error</h1>
133
+ <p>We've been notified and will fix this soon.</p>
134
+ </div>
135
+ );
136
+ }
137
+ ```
138
+
139
+ ### Custom Error Pages
140
+
141
+ ```typescript
142
+ function ErrorBoundary() {
143
+ const error = useRouteError();
144
+
145
+ if (isRouteErrorResponse(error)) {
146
+ if (error.status === 404) {
147
+ return <NotFoundPage />;
148
+ }
149
+
150
+ if (error.status === 403) {
151
+ return <AccessDeniedPage />;
152
+ }
153
+
154
+ if (error.status === 500) {
155
+ return <ServerErrorPage />;
156
+ }
157
+ }
158
+
159
+ return <GenericErrorPage error={error} />;
160
+ }
161
+ ```
162
+
163
+ ## Error Boundary Scope
164
+
165
+ Error boundaries can be defined at different levels:
166
+
167
+ ```typescript
168
+ // Module-level - catches errors in all resources
169
+ defineModule({
170
+ path: "products",
171
+ errorBoundary: <ModuleErrorBoundary />,
172
+ resources: [...]
173
+ })
174
+
175
+ // Resource-level - catches errors in specific resource
176
+ defineResource({
177
+ path: ":id",
178
+ errorBoundary: <ResourceErrorBoundary />,
179
+ component: ProductDetail
180
+ })
181
+
182
+ // Global - catches all unhandled errors
183
+ <AppShell
184
+ modules={modules}
185
+ errorBoundary={<GlobalErrorBoundary />}
186
+ />
187
+ ```
188
+
189
+ ## Important Notes
190
+
191
+ ### Always Import from AppShell
192
+
193
+ ```typescript
194
+ // ✅ Correct
195
+ import { useRouteError } from "@tailor-platform/app-shell";
196
+
197
+ // ❌ Wrong - won't work with AppShell's router
198
+ import { useRouteError } from "react-router";
199
+ ```
200
+
201
+ ### Error Boundaries Don't Catch
202
+
203
+ Error boundaries do **not** catch:
204
+
205
+ - Errors in event handlers (use try/catch)
206
+ - Async errors (use try/catch with async/await)
207
+ - Server-side rendering errors
208
+ - Errors thrown in the error boundary itself
209
+
210
+ ## Related
211
+
212
+ - [defineModule](./define-module.md) - Module-level error boundaries
213
+ - [defineResource](./define-resource.md) - Resource-level error boundaries
214
+ - [AppShell Component](../components/app-shell.md) - Global error boundary
@@ -0,0 +1,108 @@
1
+ ---
2
+ title: useTheme
3
+ description: Hook for accessing and controlling theme (light/dark mode)
4
+ ---
5
+
6
+ <!-- GENERATED by docs-kit from docs-src/hooks/use-theme.docs.outline.md — do not edit by hand. -->
7
+
8
+ # useTheme
9
+
10
+ React hook to access and control the current theme (light/dark mode).
11
+
12
+ ## Signature
13
+
14
+ ```typescript
15
+ import { type ColorTheme, type ResolvedColorTheme } from "@tailor-platform/app-shell";
16
+
17
+ const useTheme: () => {
18
+ theme: ColorTheme;
19
+ resolvedTheme: ResolvedColorTheme;
20
+ setTheme: (theme: ColorTheme) => void;
21
+ };
22
+ ```
23
+
24
+ ## Return Value
25
+
26
+ ### `theme`
27
+
28
+ - **Type:** `ColorTheme` (`"light" | "dark" | "system"`)
29
+ - **Description:** Current user preference. `"system"` means the OS setting is followed.
30
+
31
+ ### `resolvedTheme`
32
+
33
+ - **Type:** `ResolvedColorTheme` (`"light" | "dark"`)
34
+ - **Description:** Concrete color mode after resolving `"system"`. Always `"light"` or `"dark"`.
35
+
36
+ ### `setTheme()`
37
+
38
+ - **Type:** `(theme: ColorTheme) => void`
39
+ - **Description:** Set the color theme. Persisted to `localStorage` under the key `appshell-ui-theme`.
40
+
41
+ ## Usage
42
+
43
+ ### Display Current Theme
44
+
45
+ ```typescript
46
+ import { useTheme } from "@tailor-platform/app-shell";
47
+
48
+ function ThemeDisplay() {
49
+ const { theme } = useTheme();
50
+
51
+ return <div>Current theme: {theme}</div>;
52
+ }
53
+ ```
54
+
55
+ ### Theme Toggle
56
+
57
+ ```typescript
58
+ function ThemeToggle() {
59
+ const { theme, setTheme } = useTheme();
60
+
61
+ const toggleTheme = () => {
62
+ setTheme(theme === "dark" ? "light" : "dark");
63
+ };
64
+
65
+ return <button onClick={toggleTheme}>Toggle Theme</button>;
66
+ }
67
+ ```
68
+
69
+ ### Theme Selector
70
+
71
+ ```typescript
72
+ function ThemeSelector() {
73
+ const { theme, setTheme } = useTheme();
74
+
75
+ return (
76
+ <select value={theme} onChange={(e) => setTheme(e.target.value)}>
77
+ <option value="light">Light</option>
78
+ <option value="dark">Dark</option>
79
+ <option value="system">System</option>
80
+ </select>
81
+ );
82
+ }
83
+ ```
84
+
85
+ ### Conditional Rendering
86
+
87
+ Use `resolvedTheme` when you need a concrete `"light"` or `"dark"` value — it already accounts for `"system"`:
88
+
89
+ ```typescript
90
+ function Logo() {
91
+ const { resolvedTheme } = useTheme();
92
+
93
+ return (
94
+ <img
95
+ src={resolvedTheme === "dark" ? "/logo-dark.png" : "/logo-light.png"}
96
+ alt="Logo"
97
+ />
98
+ );
99
+ }
100
+ ```
101
+
102
+ ## Theme Persistence
103
+
104
+ Theme preference is automatically saved to localStorage and restored on page load.
105
+
106
+ ## Related
107
+
108
+ - [Styling & Theming](../concepts/styling-theming.md) - Theme customization
@@ -0,0 +1,80 @@
1
+ ---
2
+ title: useTimeZone
3
+ description: Hook to access the configured IANA timezone from AppShell context
4
+ ---
5
+
6
+ <!-- GENERATED by docs-kit from docs-src/hooks/use-time-zone.docs.outline.md — do not edit by hand. -->
7
+
8
+ # useTimeZone
9
+
10
+ React hook that returns a `TimeZone` object for the timezone configured on `AppShell`. Falls back to the user's local timezone when no timezone is configured.
11
+
12
+ Date/time components (`DateField`, `DatePicker`, `Calendar`) consume this hook automatically — use it when you need the same timezone in custom components.
13
+
14
+ ## Signature
15
+
16
+ ```typescript
17
+ const useTimeZone: () => TimeZone;
18
+ ```
19
+
20
+ ## Return Value
21
+
22
+ A `TimeZone` object with the following properties:
23
+
24
+ ### `value`
25
+
26
+ - **Type:** `string`
27
+ - **Description:** Raw IANA timezone identifier (e.g. `"America/Los_Angeles"`). Returns the value of AppShell's `timeZone` prop when configured, or the result of `getLocalTimeZone()` (the user's local timezone) when not.
28
+
29
+ ### `today()`
30
+
31
+ - **Type:** `() => CalendarDate`
32
+ - **Description:** Returns today's date in this timezone.
33
+
34
+ ### `now()`
35
+
36
+ - **Type:** `() => ZonedDateTime`
37
+ - **Description:** Returns the current instant in this timezone.
38
+
39
+ ## Usage
40
+
41
+ ```typescript
42
+ import { useTimeZone } from "@tailor-platform/app-shell";
43
+
44
+ function TodayDisplay() {
45
+ const timeZone = useTimeZone();
46
+ const todayDate = timeZone.today();
47
+
48
+ return <span>Today: {todayDate.toString()}</span>;
49
+ }
50
+ ```
51
+
52
+ ### Current timestamp
53
+
54
+ ```typescript
55
+ import { useTimeZone } from "@tailor-platform/app-shell";
56
+
57
+ function CurrentTimestamp() {
58
+ const timeZone = useTimeZone();
59
+
60
+ return <span>{timeZone.now().toString()}</span>;
61
+ }
62
+ ```
63
+
64
+ ### Accessing the raw IANA string
65
+
66
+ ```typescript
67
+ import { useTimeZone } from "@tailor-platform/app-shell";
68
+
69
+ function TimeZoneLabel() {
70
+ const { value } = useTimeZone();
71
+
72
+ return <span>Timezone: {value}</span>;
73
+ }
74
+ ```
75
+
76
+ ## Related
77
+
78
+ - [AppShell](../components/app-shell.md) — configure timezone via the `timeZone` prop
79
+ - [useResolvedLocale](./use-resolved-locale.md) — access the locale for formatting
80
+ - [DatePicker](../components/date-picker.md) — date input components that consume this timezone automatically
@@ -0,0 +1,107 @@
1
+ ---
2
+ title: useToast
3
+ description: Hook for displaying toast notifications
4
+ ---
5
+
6
+ <!-- GENERATED by docs-kit from docs-src/hooks/use-toast.docs.outline.md — do not edit by hand. -->
7
+
8
+ # useToast
9
+
10
+ React hook to display toast notifications for user feedback.
11
+
12
+ ## Signature
13
+
14
+ ```typescript
15
+ const useToast: () => {
16
+ toast: (props: ToastProps) => void;
17
+ };
18
+ ```
19
+
20
+ ## Parameters
21
+
22
+ ### ToastProps
23
+
24
+ ```typescript
25
+ {
26
+ title?: string;
27
+ description?: string;
28
+ variant?: "default" | "success" | "error" | "warning";
29
+ duration?: number; // milliseconds
30
+ }
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ ### Basic Toast
36
+
37
+ ```typescript
38
+ import { useToast } from "@tailor-platform/app-shell";
39
+
40
+ function SaveButton() {
41
+ const { toast } = useToast();
42
+
43
+ const handleSave = () => {
44
+ // Save logic...
45
+ toast({
46
+ title: "Saved!",
47
+ description: "Your changes have been saved.",
48
+ });
49
+ };
50
+
51
+ return <button onClick={handleSave}>Save</button>;
52
+ }
53
+ ```
54
+
55
+ ### Success Toast
56
+
57
+ ```typescript
58
+ function CreateButton() {
59
+ const { toast } = useToast();
60
+
61
+ const handleCreate = async () => {
62
+ await createItem();
63
+ toast({
64
+ title: "Created",
65
+ description: "Item created successfully.",
66
+ variant: "success",
67
+ });
68
+ };
69
+
70
+ return <button onClick={handleCreate}>Create</button>;
71
+ }
72
+ ```
73
+
74
+ ### Error Toast
75
+
76
+ ```typescript
77
+ function DeleteButton() {
78
+ const { toast } = useToast();
79
+
80
+ const handleDelete = async () => {
81
+ try {
82
+ await deleteItem();
83
+ } catch (error) {
84
+ toast({
85
+ title: "Error",
86
+ description: error.message,
87
+ variant: "error",
88
+ });
89
+ }
90
+ };
91
+
92
+ return <button onClick={handleDelete}>Delete</button>;
93
+ }
94
+ ```
95
+
96
+ ### Custom Duration
97
+
98
+ ```typescript
99
+ toast({
100
+ title: "Processing...",
101
+ duration: 5000, // 5 seconds
102
+ });
103
+ ```
104
+
105
+ ## Related
106
+
107
+ - [SidebarLayout](../components/sidebar-layout.md) - Toast provider included
@@ -0,0 +1,168 @@
1
+ ---
2
+ title: ActionPanel
3
+ description: Display a title and a vertical list of actions with per-row loading and disabled states
4
+ ---
5
+
6
+ <!-- GENERATED by docs-kit from docs-src/components/action-panel.docs.outline.md — do not edit by hand. -->
7
+
8
+ # ActionPanel
9
+
10
+ `ActionPanel` is a presentational card component for rendering a vertical list of actions (icon + label). It is designed for ERP detail pages and side panels where available actions are often backend-driven.
11
+
12
+ ## Import
13
+
14
+ ```tsx
15
+ import { ActionPanel } from "@tailor-platform/app-shell";
16
+ ```
17
+
18
+ ## Basic Usage
19
+
20
+ ```tsx
21
+ <ActionPanel
22
+ title="Actions"
23
+ actions={[
24
+ {
25
+ key: "create-invoice",
26
+ label: "Create new sales invoice",
27
+ icon: <ReceiptIcon />,
28
+ onClick: () => openCreateInvoiceModal(),
29
+ },
30
+ {
31
+ key: "view-docs",
32
+ label: "View documentation",
33
+ icon: <ExternalLinkIcon />,
34
+ onClick: () => window.open("/docs", "_blank", "noopener,noreferrer"),
35
+ },
36
+ ]}
37
+ />
38
+ ```
39
+
40
+ ## Props
41
+
42
+ _Generated from the type surface — first-party props only._
43
+
44
+ ### ActionPanelProps
45
+
46
+ | Prop | Type | Default | Description |
47
+ | ------------ | -------------- | ------- | --------------------------------------------- |
48
+ | `actions` | `ActionItem[]` | - | List of actions to render |
49
+ | `className?` | `string` | - | Additional CSS classes for the card container |
50
+ | `title` | `string` | - | Card title |
51
+
52
+ ## Exports
53
+
54
+ - `ActionPanel` — function
55
+ - `ActionPanelProps` — interface
56
+
57
+ ### ActionItem
58
+
59
+ | Property | Type | Default | Description |
60
+ | ---------- | ----------------------------- | ------------ | --------------------------------------------------------------------- |
61
+ | `key` | `string` | **Required** | Unique key for stable rendering |
62
+ | `label` | `string` | **Required** | Action label shown to users |
63
+ | `icon` | `ReactNode` | **Required** | Icon rendered in a fixed 16px slot |
64
+ | `onClick` | `() => void \| Promise<void>` | - | Click handler for the action |
65
+ | `disabled` | `boolean` | `false` | Disables interaction and applies disabled styling |
66
+ | `loading` | `boolean` | `false` | Shows spinner, disables interaction, sets busy UI |
67
+ | `variant` | `"default" \| "destructive"` | `"default"` | Visual variant; use `"destructive"` for dangerous actions like delete |
68
+
69
+ ## Behavior
70
+
71
+ - Rows are rendered as `<button type="button">` elements.
72
+ - `disabled` or `loading` makes the row non-interactive.
73
+ - When `loading` is `true`, the icon slot renders a spinner without shifting layout.
74
+ - Empty actions show a built-in fallback message: `No actions available`.
75
+
76
+ ## With Loading State
77
+
78
+ Use parent state to control which row is loading:
79
+
80
+ ```tsx
81
+ const [loadingKey, setLoadingKey] = useState<string | null>(null);
82
+
83
+ const handleConfirm = async () => {
84
+ setLoadingKey("confirm");
85
+ try {
86
+ await confirmOrder(orderId);
87
+ } finally {
88
+ setLoadingKey(null);
89
+ }
90
+ };
91
+
92
+ <ActionPanel
93
+ title="Actions"
94
+ actions={[
95
+ {
96
+ key: "confirm",
97
+ label: "Confirm order",
98
+ icon: <CheckIcon />,
99
+ onClick: handleConfirm,
100
+ loading: loadingKey === "confirm",
101
+ },
102
+ ]}
103
+ />;
104
+ ```
105
+
106
+ ## CommandPalette Integration
107
+
108
+ `ActionPanel` automatically registers its enabled actions to the CommandPalette. Actions that are not `disabled`, not `loading`, and have an `onClick` handler are registered under the panel's `title` as the group name. Registration is cleaned up when the component unmounts.
109
+
110
+ This means users can discover and trigger any visible ActionPanel action via `Cmd+K` / `Ctrl+K` without any additional setup.
111
+
112
+ ```tsx
113
+ // Actions in this panel will automatically appear in the CommandPalette
114
+ <ActionPanel
115
+ title="Order Actions"
116
+ actions={[
117
+ { key: "confirm", label: "Confirm order", icon: <CheckIcon />, onClick: handleConfirm },
118
+ { key: "cancel", label: "Cancel order", icon: <XIcon />, onClick: handleCancel },
119
+ ]}
120
+ />
121
+ ```
122
+
123
+ > **Note:** `ActionPanel` must be rendered inside `AppShell` (which provides the required `CommandPaletteProvider`). Rendering it outside that tree will throw.
124
+
125
+ ## Backend-Driven Pattern
126
+
127
+ In ERP apps, the backend often decides which actions are available for a document. A common setup:
128
+
129
+ 1. Backend returns document data and available action keys.
130
+ 2. Frontend maps each key to a registry entry (`label`, `icon`, `execute`).
131
+ 3. Page builds `ActionItem[]` and wires `onClick`.
132
+ 4. Page controls `loading` per key and passes it to `ActionPanel`.
133
+
134
+ This keeps `ActionPanel` simple and reusable across entity types.
135
+
136
+ ## Accessibility
137
+
138
+ - Action list uses `role="list"` and each row wrapper uses `role="listitem"`.
139
+ - Buttons are keyboard accessible and include focus-visible states.
140
+ - Loading rows set `aria-busy="true"`.
141
+ - Disabled rows set `aria-disabled="true"` and native `disabled`.
142
+
143
+ ## Styling
144
+
145
+ The component is styled with the library's internal `astw:`-prefixed utilities:
146
+
147
+ - Full width card
148
+ - Compact spacing for dense action lists
149
+ - Fixed icon slot for stable alignment
150
+ - Inline spinner for loading state
151
+
152
+ Use `className` on `ActionPanel` for card-level properties it does not set itself. Write **plain** utilities — see [Styling AppShell components](../concepts/styling-theming.md#styling-appshell-components).
153
+
154
+ ## Examples in This Repo
155
+
156
+ - `examples/app-module` includes an **Action Panel Demo** resource.
157
+ - The **2 Columns** layout page also includes an ActionPanel with loading behavior.
158
+
159
+ ## Related Components
160
+
161
+ - [Layout](./layout.md) - Compose page-level column layouts
162
+ - [DescriptionCard](./description-card.md) - Display structured record fields
163
+ - [Badge](./badge.md) - Show statuses and semantic labels
164
+
165
+ ## Related Concepts
166
+
167
+ - [Modules and Resources](../concepts/modules-and-resources.md) - Organize pages and routes
168
+ - [Routing and Navigation](../concepts/routing-navigation.md) - Navigation patterns