@open-mercato/core 0.6.7-develop.6785.1.1dd7cfac55 → 0.6.7-develop.6795.1.8a3f27921c

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 (196) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/generated/entities.ids.generated.js +1 -0
  3. package/dist/generated/entities.ids.generated.js.map +2 -2
  4. package/dist/modules/auth/backend/settings/page.js +14 -9
  5. package/dist/modules/auth/backend/settings/page.js.map +2 -2
  6. package/dist/modules/customers/cli.js +17 -7
  7. package/dist/modules/customers/cli.js.map +2 -2
  8. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +229 -216
  9. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
  10. package/dist/modules/customers/search.js +50 -29
  11. package/dist/modules/customers/search.js.map +2 -2
  12. package/dist/modules/data_sync/api/mappings/[id]/route.js +22 -9
  13. package/dist/modules/data_sync/api/mappings/[id]/route.js.map +2 -2
  14. package/dist/modules/data_sync/api/mappings/route.js +13 -4
  15. package/dist/modules/data_sync/api/mappings/route.js.map +2 -2
  16. package/dist/modules/data_sync/api/options.js +7 -2
  17. package/dist/modules/data_sync/api/options.js.map +2 -2
  18. package/dist/modules/data_sync/api/run.js +7 -2
  19. package/dist/modules/data_sync/api/run.js.map +2 -2
  20. package/dist/modules/data_sync/api/runs/[id]/cancel.js +8 -3
  21. package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
  22. package/dist/modules/data_sync/api/runs/[id]/retry.js +7 -2
  23. package/dist/modules/data_sync/api/runs/[id]/retry.js.map +2 -2
  24. package/dist/modules/data_sync/api/runs/[id]/route.js +8 -3
  25. package/dist/modules/data_sync/api/runs/[id]/route.js.map +2 -2
  26. package/dist/modules/data_sync/api/runs.js +7 -2
  27. package/dist/modules/data_sync/api/runs.js.map +2 -2
  28. package/dist/modules/data_sync/api/schedules/[id]/route.js +19 -6
  29. package/dist/modules/data_sync/api/schedules/[id]/route.js.map +2 -2
  30. package/dist/modules/data_sync/api/schedules/route.js +13 -4
  31. package/dist/modules/data_sync/api/schedules/route.js.map +2 -2
  32. package/dist/modules/data_sync/api/validate.js +9 -4
  33. package/dist/modules/data_sync/api/validate.js.map +2 -2
  34. package/dist/modules/design_system/acl.js +9 -0
  35. package/dist/modules/design_system/acl.js.map +7 -0
  36. package/dist/modules/design_system/backend/design-system/page.js +11 -0
  37. package/dist/modules/design_system/backend/design-system/page.js.map +7 -0
  38. package/dist/modules/design_system/backend/design-system/page.meta.js +15 -0
  39. package/dist/modules/design_system/backend/design-system/page.meta.js.map +7 -0
  40. package/dist/modules/design_system/gallery/components/CodeSnippet.js +52 -0
  41. package/dist/modules/design_system/gallery/components/CodeSnippet.js.map +7 -0
  42. package/dist/modules/design_system/gallery/components/EntryCard.js +63 -0
  43. package/dist/modules/design_system/gallery/components/EntryCard.js.map +7 -0
  44. package/dist/modules/design_system/gallery/components/GalleryShell.js +163 -0
  45. package/dist/modules/design_system/gallery/components/GalleryShell.js.map +7 -0
  46. package/dist/modules/design_system/gallery/components/VariantPreview.js +9 -0
  47. package/dist/modules/design_system/gallery/components/VariantPreview.js.map +7 -0
  48. package/dist/modules/design_system/gallery/entries/banners.js +186 -0
  49. package/dist/modules/design_system/gallery/entries/banners.js.map +7 -0
  50. package/dist/modules/design_system/gallery/entries/buttons.js +361 -0
  51. package/dist/modules/design_system/gallery/entries/buttons.js.map +7 -0
  52. package/dist/modules/design_system/gallery/entries/charts.js +457 -0
  53. package/dist/modules/design_system/gallery/entries/charts.js.map +7 -0
  54. package/dist/modules/design_system/gallery/entries/dates.js +306 -0
  55. package/dist/modules/design_system/gallery/entries/dates.js.map +7 -0
  56. package/dist/modules/design_system/gallery/entries/detail.js +358 -0
  57. package/dist/modules/design_system/gallery/entries/detail.js.map +7 -0
  58. package/dist/modules/design_system/gallery/entries/display.js +785 -0
  59. package/dist/modules/design_system/gallery/entries/display.js.map +7 -0
  60. package/dist/modules/design_system/gallery/entries/feedback.js +658 -0
  61. package/dist/modules/design_system/gallery/entries/feedback.js.map +7 -0
  62. package/dist/modules/design_system/gallery/entries/filters.js +505 -0
  63. package/dist/modules/design_system/gallery/entries/filters.js.map +7 -0
  64. package/dist/modules/design_system/gallery/entries/foundations.js +453 -0
  65. package/dist/modules/design_system/gallery/entries/foundations.js.map +7 -0
  66. package/dist/modules/design_system/gallery/entries/icons.js +140 -0
  67. package/dist/modules/design_system/gallery/entries/icons.js.map +7 -0
  68. package/dist/modules/design_system/gallery/entries/inputs.js +1217 -0
  69. package/dist/modules/design_system/gallery/entries/inputs.js.map +7 -0
  70. package/dist/modules/design_system/gallery/entries/messages.js +334 -0
  71. package/dist/modules/design_system/gallery/entries/messages.js.map +7 -0
  72. package/dist/modules/design_system/gallery/entries/navigation.js +475 -0
  73. package/dist/modules/design_system/gallery/entries/navigation.js.map +7 -0
  74. package/dist/modules/design_system/gallery/entries/notifications.js +257 -0
  75. package/dist/modules/design_system/gallery/entries/notifications.js.map +7 -0
  76. package/dist/modules/design_system/gallery/entries/overlays.js +576 -0
  77. package/dist/modules/design_system/gallery/entries/overlays.js.map +7 -0
  78. package/dist/modules/design_system/gallery/entries/scaffolding.js +378 -0
  79. package/dist/modules/design_system/gallery/entries/scaffolding.js.map +7 -0
  80. package/dist/modules/design_system/gallery/entries/schedule.js +297 -0
  81. package/dist/modules/design_system/gallery/entries/schedule.js.map +7 -0
  82. package/dist/modules/design_system/gallery/registry.js +139 -0
  83. package/dist/modules/design_system/gallery/registry.js.map +7 -0
  84. package/dist/modules/design_system/gallery/types.js +1 -0
  85. package/dist/modules/design_system/gallery/types.js.map +7 -0
  86. package/dist/modules/design_system/index.js +9 -0
  87. package/dist/modules/design_system/index.js.map +7 -0
  88. package/dist/modules/design_system/setup.js +14 -0
  89. package/dist/modules/design_system/setup.js.map +7 -0
  90. package/dist/modules/inbox_ops/search.js +15 -4
  91. package/dist/modules/inbox_ops/search.js.map +2 -2
  92. package/dist/modules/integrations/api/[id]/credentials/route.js +11 -5
  93. package/dist/modules/integrations/api/[id]/credentials/route.js.map +2 -2
  94. package/dist/modules/integrations/api/[id]/health/route.js +6 -3
  95. package/dist/modules/integrations/api/[id]/health/route.js.map +2 -2
  96. package/dist/modules/integrations/api/[id]/route.js +6 -3
  97. package/dist/modules/integrations/api/[id]/route.js.map +2 -2
  98. package/dist/modules/integrations/api/[id]/state/route.js +6 -3
  99. package/dist/modules/integrations/api/[id]/state/route.js.map +2 -2
  100. package/dist/modules/integrations/api/[id]/version/route.js +6 -3
  101. package/dist/modules/integrations/api/[id]/version/route.js.map +2 -2
  102. package/dist/modules/integrations/api/logs/route.js +6 -3
  103. package/dist/modules/integrations/api/logs/route.js.map +2 -2
  104. package/dist/modules/integrations/api/route.js +6 -3
  105. package/dist/modules/integrations/api/route.js.map +2 -2
  106. package/dist/modules/integrations/api/umes-read.js +2 -2
  107. package/dist/modules/integrations/api/umes-read.js.map +2 -2
  108. package/dist/modules/integrations/lib/organization-scope.js +2 -9
  109. package/dist/modules/integrations/lib/organization-scope.js.map +2 -2
  110. package/dist/modules/messages/search.js +10 -5
  111. package/dist/modules/messages/search.js.map +2 -2
  112. package/dist/modules/query_index/data/entities.js +2 -1
  113. package/dist/modules/query_index/data/entities.js.map +2 -2
  114. package/dist/modules/query_index/lib/engine.js +32 -50
  115. package/dist/modules/query_index/lib/engine.js.map +2 -2
  116. package/dist/modules/query_index/migrations/Migration20260731105052_query_index.js +17 -0
  117. package/dist/modules/query_index/migrations/Migration20260731105052_query_index.js.map +7 -0
  118. package/generated/entities.ids.generated.ts +1 -0
  119. package/package.json +16 -16
  120. package/src/modules/auth/backend/settings/page.tsx +17 -8
  121. package/src/modules/customers/cli.ts +17 -7
  122. package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +24 -13
  123. package/src/modules/customers/i18n/de.json +17 -0
  124. package/src/modules/customers/i18n/en.json +17 -0
  125. package/src/modules/customers/i18n/es.json +17 -0
  126. package/src/modules/customers/i18n/pl.json +17 -0
  127. package/src/modules/customers/search.ts +59 -27
  128. package/src/modules/data_sync/AGENTS.md +1 -0
  129. package/src/modules/data_sync/api/mappings/[id]/route.ts +22 -9
  130. package/src/modules/data_sync/api/mappings/route.ts +13 -4
  131. package/src/modules/data_sync/api/options.ts +7 -2
  132. package/src/modules/data_sync/api/run.ts +7 -2
  133. package/src/modules/data_sync/api/runs/[id]/cancel.ts +8 -3
  134. package/src/modules/data_sync/api/runs/[id]/retry.ts +7 -2
  135. package/src/modules/data_sync/api/runs/[id]/route.ts +8 -3
  136. package/src/modules/data_sync/api/runs.ts +7 -2
  137. package/src/modules/data_sync/api/schedules/[id]/route.ts +19 -6
  138. package/src/modules/data_sync/api/schedules/route.ts +13 -4
  139. package/src/modules/data_sync/api/validate.ts +9 -4
  140. package/src/modules/design_system/AGENTS.md +34 -0
  141. package/src/modules/design_system/acl.ts +5 -0
  142. package/src/modules/design_system/backend/design-system/page.meta.ts +11 -0
  143. package/src/modules/design_system/backend/design-system/page.tsx +14 -0
  144. package/src/modules/design_system/gallery/components/CodeSnippet.tsx +55 -0
  145. package/src/modules/design_system/gallery/components/EntryCard.tsx +95 -0
  146. package/src/modules/design_system/gallery/components/GalleryShell.tsx +206 -0
  147. package/src/modules/design_system/gallery/components/VariantPreview.tsx +15 -0
  148. package/src/modules/design_system/gallery/entries/banners.tsx +226 -0
  149. package/src/modules/design_system/gallery/entries/buttons.tsx +422 -0
  150. package/src/modules/design_system/gallery/entries/charts.tsx +518 -0
  151. package/src/modules/design_system/gallery/entries/dates.tsx +325 -0
  152. package/src/modules/design_system/gallery/entries/detail.tsx +403 -0
  153. package/src/modules/design_system/gallery/entries/display.tsx +893 -0
  154. package/src/modules/design_system/gallery/entries/feedback.tsx +746 -0
  155. package/src/modules/design_system/gallery/entries/filters.tsx +570 -0
  156. package/src/modules/design_system/gallery/entries/foundations.tsx +529 -0
  157. package/src/modules/design_system/gallery/entries/icons.tsx +168 -0
  158. package/src/modules/design_system/gallery/entries/inputs.tsx +1516 -0
  159. package/src/modules/design_system/gallery/entries/messages.tsx +339 -0
  160. package/src/modules/design_system/gallery/entries/navigation.tsx +573 -0
  161. package/src/modules/design_system/gallery/entries/notifications.tsx +285 -0
  162. package/src/modules/design_system/gallery/entries/overlays.tsx +677 -0
  163. package/src/modules/design_system/gallery/entries/scaffolding.tsx +430 -0
  164. package/src/modules/design_system/gallery/entries/schedule.tsx +307 -0
  165. package/src/modules/design_system/gallery/registry.ts +152 -0
  166. package/src/modules/design_system/gallery/types.ts +32 -0
  167. package/src/modules/design_system/i18n/.hardcoded-allowlist.json +71 -0
  168. package/src/modules/design_system/i18n/de.json +46 -0
  169. package/src/modules/design_system/i18n/en.json +46 -0
  170. package/src/modules/design_system/i18n/es.json +46 -0
  171. package/src/modules/design_system/i18n/pl.json +46 -0
  172. package/src/modules/design_system/index.ts +7 -0
  173. package/src/modules/design_system/setup.ts +12 -0
  174. package/src/modules/inbox_ops/i18n/de.json +3 -0
  175. package/src/modules/inbox_ops/i18n/en.json +3 -0
  176. package/src/modules/inbox_ops/i18n/es.json +3 -0
  177. package/src/modules/inbox_ops/i18n/pl.json +3 -0
  178. package/src/modules/inbox_ops/search.ts +20 -4
  179. package/src/modules/integrations/api/[id]/credentials/route.ts +11 -5
  180. package/src/modules/integrations/api/[id]/health/route.ts +6 -3
  181. package/src/modules/integrations/api/[id]/route.ts +6 -3
  182. package/src/modules/integrations/api/[id]/state/route.ts +6 -3
  183. package/src/modules/integrations/api/[id]/version/route.ts +6 -3
  184. package/src/modules/integrations/api/logs/route.ts +6 -3
  185. package/src/modules/integrations/api/route.ts +6 -3
  186. package/src/modules/integrations/api/umes-read.ts +2 -2
  187. package/src/modules/integrations/lib/organization-scope.ts +5 -25
  188. package/src/modules/messages/i18n/de.json +2 -0
  189. package/src/modules/messages/i18n/en.json +2 -0
  190. package/src/modules/messages/i18n/es.json +2 -0
  191. package/src/modules/messages/i18n/pl.json +2 -0
  192. package/src/modules/messages/search.ts +11 -5
  193. package/src/modules/query_index/data/entities.ts +7 -0
  194. package/src/modules/query_index/lib/engine.ts +40 -63
  195. package/src/modules/query_index/migrations/.snapshot-open-mercato.json +12 -0
  196. package/src/modules/query_index/migrations/Migration20260731105052_query_index.ts +35 -0
@@ -0,0 +1,430 @@
1
+ import * as React from 'react'
2
+ import { Archive, Copy, Download, Lock, Plus, Trash2, UserRound } from 'lucide-react'
3
+ import { Badge } from '@open-mercato/ui/primitives/badge'
4
+ import { Button } from '@open-mercato/ui/primitives/button'
5
+ import { Page, PageBody, PageHeader } from '@open-mercato/ui/backend/Page'
6
+ import { CollapsibleSection, SectionHeader } from '@open-mercato/ui/backend/SectionHeader'
7
+ import {
8
+ SectionNav,
9
+ SectionPage,
10
+ type SectionNavGroup,
11
+ } from '@open-mercato/ui/backend/section-page'
12
+ import {
13
+ ActionsDropdown,
14
+ FormFooter,
15
+ FormHeader,
16
+ type ActionItem,
17
+ } from '@open-mercato/ui/backend/forms'
18
+ import type { GalleryEntry } from '../types'
19
+
20
+ // Component titles and variant names are proper nouns from the codebase and
21
+ // are deliberately not translated. `code` MUST contain the entry's importPath
22
+ // (enforced by the registry-integrity test) and is always reviewed alongside
23
+ // its sibling `render`.
24
+
25
+ // The demos use `#`-anchors as hrefs so clicking inside the gallery never
26
+ // navigates away, and every callback is a no-op.
27
+
28
+ const demoSections: SectionNavGroup[] = [
29
+ {
30
+ id: 'account',
31
+ label: 'Account',
32
+ items: [
33
+ { id: 'profile', label: 'Profile', href: '#profile', icon: <UserRound className="size-4" /> },
34
+ { id: 'security', label: 'Security', href: '#security', icon: <Lock className="size-4" /> },
35
+ ],
36
+ },
37
+ ]
38
+
39
+ // SectionPage stretches to the viewport by design — the miniature demo clips
40
+ // it inside a fixed-height frame so the two-item nav plus content stay small.
41
+ function SectionPageMiniDemo() {
42
+ return (
43
+ <div className="h-80 w-full overflow-hidden rounded-lg border bg-background">
44
+ <SectionPage title="Settings" sections={demoSections} activePath="#profile">
45
+ <div className="space-y-2">
46
+ <h2 className="text-sm font-semibold">Profile</h2>
47
+ <p className="text-sm text-muted-foreground">Section content renders here.</p>
48
+ </div>
49
+ </SectionPage>
50
+ </div>
51
+ )
52
+ }
53
+
54
+ // SectionNav is controlled — the demo owns the collapsed state.
55
+ function SectionNavDemo() {
56
+ const [collapsed, setCollapsed] = React.useState(false)
57
+ return (
58
+ <div className="w-64 rounded-lg border bg-background px-3 py-4">
59
+ <SectionNav
60
+ title="Settings"
61
+ sections={demoSections}
62
+ activePath="#profile"
63
+ collapsed={collapsed}
64
+ onToggleCollapse={() => setCollapsed((value) => !value)}
65
+ />
66
+ </div>
67
+ )
68
+ }
69
+
70
+ const demoActions: ActionItem[] = [
71
+ { id: 'duplicate', label: 'Duplicate record', icon: Copy, onSelect: () => {} },
72
+ { id: 'export', label: 'Export as CSV', icon: Download, onSelect: () => {} },
73
+ { id: 'delete', label: 'Delete record', icon: Trash2, onSelect: () => {} },
74
+ ]
75
+
76
+ const pageEntry: GalleryEntry = {
77
+ id: 'page',
78
+ title: 'Page',
79
+ importPath: '@open-mercato/ui/backend/Page',
80
+ variants: [
81
+ {
82
+ id: 'basic',
83
+ title: 'Page + PageHeader + PageBody',
84
+ render: () => (
85
+ <div className="w-full">
86
+ <Page>
87
+ <PageHeader title="Currencies" description="Exchange rates refresh nightly." />
88
+ <PageBody>
89
+ <div className="rounded-lg border border-dashed p-6 text-sm text-muted-foreground">
90
+ Page content
91
+ </div>
92
+ </PageBody>
93
+ </Page>
94
+ </div>
95
+ ),
96
+ code: `import { Page, PageBody, PageHeader } from '@open-mercato/ui/backend/Page'
97
+
98
+ <Page>
99
+ <PageHeader title={t('currencies.list.title')} description={t('currencies.list.description')} />
100
+ <PageBody>{/* DataTable, sections… */}</PageBody>
101
+ </Page>`,
102
+ },
103
+ {
104
+ id: 'with-actions',
105
+ title: 'Header actions',
106
+ render: () => (
107
+ <div className="w-full">
108
+ <Page>
109
+ <PageHeader
110
+ title="Currencies"
111
+ description="Exchange rates refresh nightly."
112
+ actions={
113
+ <>
114
+ <Button variant="outline" size="sm">
115
+ Import
116
+ </Button>
117
+ <Button size="sm">
118
+ <Plus className="size-4" /> Add currency
119
+ </Button>
120
+ </>
121
+ }
122
+ />
123
+ <PageBody>
124
+ <div className="rounded-lg border border-dashed p-6 text-sm text-muted-foreground">
125
+ Page content
126
+ </div>
127
+ </PageBody>
128
+ </Page>
129
+ </div>
130
+ ),
131
+ code: `import { Plus } from 'lucide-react'
132
+ import { Page, PageBody, PageHeader } from '@open-mercato/ui/backend/Page'
133
+ import { Button } from '@open-mercato/ui/primitives/button'
134
+
135
+ <Page>
136
+ <PageHeader
137
+ title={t('currencies.list.title')}
138
+ actions={
139
+ <>
140
+ <Button variant="outline" size="sm">Import</Button>
141
+ <Button size="sm"><Plus className="size-4" /> Add currency</Button>
142
+ </>
143
+ }
144
+ />
145
+ <PageBody>{/* … */}</PageBody>
146
+ </Page>`,
147
+ },
148
+ ],
149
+ }
150
+
151
+ const sectionHeaderEntry: GalleryEntry = {
152
+ id: 'section-header',
153
+ title: 'SectionHeader',
154
+ importPath: '@open-mercato/ui/backend/SectionHeader',
155
+ variants: [
156
+ {
157
+ id: 'count-and-action',
158
+ title: 'With count and action',
159
+ render: () => (
160
+ <div className="w-full max-w-md">
161
+ <SectionHeader
162
+ title="Addresses"
163
+ count={3}
164
+ action={
165
+ <Button variant="ghost" size="sm">
166
+ <Plus className="size-4" /> Add
167
+ </Button>
168
+ }
169
+ />
170
+ </div>
171
+ ),
172
+ code: `import { Plus } from 'lucide-react'
173
+ import { SectionHeader } from '@open-mercato/ui/backend/SectionHeader'
174
+ import { Button } from '@open-mercato/ui/primitives/button'
175
+
176
+ <SectionHeader
177
+ title={t('customers.people.detail.addresses.title')}
178
+ count={addresses.length}
179
+ action={<Button variant="ghost" size="sm"><Plus className="size-4" /> {t('common.add')}</Button>}
180
+ />`,
181
+ },
182
+ {
183
+ id: 'collapsible',
184
+ title: 'CollapsibleSection',
185
+ render: () => (
186
+ <div className="w-full max-w-md">
187
+ <CollapsibleSection title="Billing details" count={2}>
188
+ <div className="rounded-lg border border-dashed p-4 text-sm text-muted-foreground">
189
+ Section content
190
+ </div>
191
+ </CollapsibleSection>
192
+ </div>
193
+ ),
194
+ code: `import { CollapsibleSection } from '@open-mercato/ui/backend/SectionHeader'
195
+
196
+ <CollapsibleSection title={t('billing.details.title')} count={2}>
197
+ {/* section content */}
198
+ </CollapsibleSection>`,
199
+ },
200
+ ],
201
+ }
202
+
203
+ const sectionPageEntry: GalleryEntry = {
204
+ id: 'section-page',
205
+ title: 'SectionPage',
206
+ importPath: '@open-mercato/ui/backend/section-page',
207
+ variants: [
208
+ {
209
+ id: 'miniature',
210
+ title: 'Two-item miniature (clipped frame)',
211
+ render: () => <SectionPageMiniDemo />,
212
+ code: `import { UserRound, Lock } from 'lucide-react'
213
+ import { SectionPage, type SectionNavGroup } from '@open-mercato/ui/backend/section-page'
214
+
215
+ const sections: SectionNavGroup[] = [
216
+ {
217
+ id: 'account',
218
+ label: 'Account',
219
+ items: [
220
+ { id: 'profile', label: 'Profile', href: '/backend/profile', icon: <UserRound className="size-4" /> },
221
+ { id: 'security', label: 'Security', href: '/backend/profile/security', icon: <Lock className="size-4" /> },
222
+ ],
223
+ },
224
+ ]
225
+
226
+ <SectionPage title={t('profile.title')} sections={sections} activePath={pathname}>
227
+ {children}
228
+ </SectionPage>`,
229
+ },
230
+ {
231
+ id: 'section-nav',
232
+ title: 'SectionNav (standalone, controlled collapse)',
233
+ render: () => <SectionNavDemo />,
234
+ code: `import { SectionNav, type SectionNavGroup } from '@open-mercato/ui/backend/section-page'
235
+
236
+ const [collapsed, setCollapsed] = React.useState(false)
237
+
238
+ <SectionNav
239
+ title={t('profile.title')}
240
+ sections={sections}
241
+ activePath={pathname}
242
+ collapsed={collapsed}
243
+ onToggleCollapse={() => setCollapsed((value) => !value)}
244
+ />`,
245
+ },
246
+ ],
247
+ }
248
+
249
+ const formHeaderEntry: GalleryEntry = {
250
+ id: 'form-header',
251
+ title: 'FormHeader',
252
+ importPath: '@open-mercato/ui/backend/forms',
253
+ variants: [
254
+ {
255
+ id: 'edit-mode',
256
+ title: 'Edit mode',
257
+ render: () => (
258
+ <div className="w-full">
259
+ <FormHeader
260
+ backHref="#form-header"
261
+ backLabel="Back"
262
+ title="Edit product"
263
+ actions={{
264
+ cancelHref: '#form-header',
265
+ cancelLabel: 'Cancel',
266
+ submit: { label: 'Save', pendingLabel: 'Saving…' },
267
+ }}
268
+ />
269
+ </div>
270
+ ),
271
+ code: `import { FormHeader } from '@open-mercato/ui/backend/forms'
272
+
273
+ <FormHeader
274
+ backHref="/backend/products"
275
+ backLabel={t('ui.navigation.back')}
276
+ title={t('products.edit.title')}
277
+ actions={{
278
+ cancelHref: '/backend/products',
279
+ cancelLabel: t('ui.forms.actions.cancel'),
280
+ submit: { formId: 'product-form', label: t('ui.forms.actions.save'), pendingLabel: t('ui.forms.actions.saving') },
281
+ }}
282
+ />`,
283
+ },
284
+ {
285
+ id: 'detail-mode',
286
+ title: 'Detail mode',
287
+ render: () => (
288
+ <div className="w-full">
289
+ <FormHeader
290
+ mode="detail"
291
+ backHref="#form-header"
292
+ entityTypeLabel="Company"
293
+ title="Acme Logistics"
294
+ subtitle="Created 12 Mar 2026"
295
+ statusBadge={<Badge variant="muted">Active</Badge>}
296
+ menuActions={[
297
+ { id: 'duplicate', label: 'Duplicate', icon: Copy, onSelect: () => {} },
298
+ { id: 'archive', label: 'Archive', icon: Archive, onSelect: () => {} },
299
+ ]}
300
+ onDelete={() => {}}
301
+ deleteLabel="Delete"
302
+ />
303
+ </div>
304
+ ),
305
+ code: `import { Archive, Copy } from 'lucide-react'
306
+ import { FormHeader } from '@open-mercato/ui/backend/forms'
307
+
308
+ <FormHeader
309
+ mode="detail"
310
+ backHref="/backend/customers/companies"
311
+ entityTypeLabel={t('customers.companies.detail.entityType')}
312
+ title={company.name}
313
+ subtitle={t('customers.companies.detail.createdAt', { date: createdAt })}
314
+ statusBadge={<Badge variant="muted">{statusLabel}</Badge>}
315
+ menuActions={[
316
+ { id: 'duplicate', label: t('ui.actions.duplicate'), icon: Copy, onSelect: duplicate },
317
+ { id: 'archive', label: t('ui.actions.archive'), icon: Archive, onSelect: archive },
318
+ ]}
319
+ onDelete={confirmDelete}
320
+ deleteLabel={t('ui.forms.actions.delete')}
321
+ />`,
322
+ },
323
+ ],
324
+ }
325
+
326
+ const formFooterEntry: GalleryEntry = {
327
+ id: 'form-footer',
328
+ title: 'FormFooter',
329
+ importPath: '@open-mercato/ui/backend/forms',
330
+ variants: [
331
+ {
332
+ id: 'default',
333
+ title: 'default',
334
+ render: () => (
335
+ <div className="w-full">
336
+ <FormFooter
337
+ actions={{
338
+ cancelHref: '#form-footer',
339
+ cancelLabel: 'Cancel',
340
+ submit: { label: 'Save changes', pendingLabel: 'Saving…' },
341
+ }}
342
+ />
343
+ </div>
344
+ ),
345
+ code: `import { FormFooter } from '@open-mercato/ui/backend/forms'
346
+
347
+ <FormFooter
348
+ actions={{
349
+ cancelHref: '/backend/products',
350
+ cancelLabel: t('ui.forms.actions.cancel'),
351
+ submit: { formId: 'product-form', label: t('ui.forms.actions.save'), pendingLabel: t('ui.forms.actions.saving') },
352
+ }}
353
+ />`,
354
+ },
355
+ {
356
+ id: 'embedded-with-delete',
357
+ title: 'embedded, with delete',
358
+ render: () => (
359
+ <div className="w-full">
360
+ <FormFooter
361
+ embedded
362
+ actions={{
363
+ showDelete: true,
364
+ onDelete: () => {},
365
+ deleteLabel: 'Delete',
366
+ cancelHref: '#form-footer',
367
+ cancelLabel: 'Cancel',
368
+ submit: { label: 'Save' },
369
+ }}
370
+ />
371
+ </div>
372
+ ),
373
+ code: `import { FormFooter } from '@open-mercato/ui/backend/forms'
374
+
375
+ <FormFooter
376
+ embedded
377
+ actions={{
378
+ showDelete: true,
379
+ onDelete: confirmDelete,
380
+ deleteLabel: t('ui.forms.actions.delete'),
381
+ cancelHref: '/backend/products',
382
+ cancelLabel: t('ui.forms.actions.cancel'),
383
+ submit: { label: t('ui.forms.actions.save') },
384
+ }}
385
+ />`,
386
+ },
387
+ ],
388
+ }
389
+
390
+ const actionsDropdownEntry: GalleryEntry = {
391
+ id: 'actions-dropdown',
392
+ title: 'ActionsDropdown',
393
+ importPath: '@open-mercato/ui/backend/forms',
394
+ variants: [
395
+ {
396
+ id: 'label-trigger',
397
+ title: 'Label trigger',
398
+ render: () => <ActionsDropdown items={demoActions} />,
399
+ code: `import { Copy, Download, Trash2 } from 'lucide-react'
400
+ import { ActionsDropdown, type ActionItem } from '@open-mercato/ui/backend/forms'
401
+
402
+ const items: ActionItem[] = [
403
+ { id: 'duplicate', label: t('ui.actions.duplicate'), icon: Copy, onSelect: duplicate },
404
+ { id: 'export', label: t('ui.actions.export'), icon: Download, onSelect: exportCsv },
405
+ { id: 'delete', label: t('ui.actions.delete'), icon: Trash2, onSelect: confirmDelete },
406
+ ]
407
+
408
+ <ActionsDropdown items={items} />`,
409
+ },
410
+ {
411
+ id: 'icon-trigger',
412
+ title: 'Icon trigger',
413
+ render: () => (
414
+ <ActionsDropdown items={demoActions} triggerMode="icon" ariaLabel="More actions" />
415
+ ),
416
+ code: `import { ActionsDropdown } from '@open-mercato/ui/backend/forms'
417
+
418
+ <ActionsDropdown items={items} triggerMode="icon" ariaLabel={t('ui.actions.more')} />`,
419
+ },
420
+ ],
421
+ }
422
+
423
+ export const entries: GalleryEntry[] = [
424
+ pageEntry,
425
+ sectionHeaderEntry,
426
+ sectionPageEntry,
427
+ formHeaderEntry,
428
+ formFooterEntry,
429
+ actionsDropdownEntry,
430
+ ]