@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,403 @@
1
+ import * as React from 'react'
2
+ import { Badge } from '@open-mercato/ui/primitives/badge'
3
+ import { Button } from '@open-mercato/ui/primitives/button'
4
+ import {
5
+ AddressesSection,
6
+ AttachmentsSection,
7
+ DetailFieldsSection,
8
+ ErrorMessage,
9
+ LoadingMessage,
10
+ NotesSection,
11
+ type AddressDataAdapter,
12
+ type DetailFieldConfig,
13
+ type NotesSectionProps,
14
+ } from '@open-mercato/ui/backend/detail'
15
+ import type { GalleryEntry } from '../types'
16
+
17
+ // Component titles and variant names are proper nouns from the codebase and
18
+ // are deliberately not translated. `code` MUST contain the entry's importPath
19
+ // (enforced by the registry-integrity test) and is always reviewed alongside
20
+ // its sibling `render`.
21
+
22
+ // Data-backed sections (Notes/Addresses) take adapter objects — the demos pass
23
+ // inline in-memory adapters, so previews never touch an API. AttachmentsSection
24
+ // fetches through `apiCall` internally and cannot take an adapter; it is shown
25
+ // in its `recordId={null}` state, where fetching is disabled by design.
26
+
27
+ const saveNoop = async () => {}
28
+
29
+ const fieldGrid: DetailFieldConfig[] = [
30
+ {
31
+ key: 'first-name',
32
+ kind: 'text',
33
+ label: 'First name',
34
+ value: 'Anna',
35
+ emptyLabel: 'Add first name',
36
+ onSave: saveNoop,
37
+ },
38
+ {
39
+ key: 'phone',
40
+ kind: 'text',
41
+ label: 'Phone',
42
+ value: null,
43
+ emptyLabel: 'Add phone',
44
+ onSave: saveNoop,
45
+ },
46
+ {
47
+ key: 'status',
48
+ kind: 'select',
49
+ label: 'Status',
50
+ value: 'active',
51
+ emptyLabel: 'Set status',
52
+ options: [
53
+ { value: 'active', label: 'Active' },
54
+ { value: 'inactive', label: 'Inactive' },
55
+ ],
56
+ onSave: saveNoop,
57
+ },
58
+ {
59
+ key: 'notes',
60
+ kind: 'multiline',
61
+ label: 'Notes',
62
+ value: 'Prefers email contact.',
63
+ emptyLabel: 'Add notes',
64
+ onSave: saveNoop,
65
+ gridClassName: 'sm:col-span-2 md:col-span-3',
66
+ },
67
+ ]
68
+
69
+ const fieldGridWithCustom: DetailFieldConfig[] = [
70
+ {
71
+ key: 'email',
72
+ kind: 'text',
73
+ label: 'Email',
74
+ value: 'anna@example.com',
75
+ emptyLabel: 'Add email',
76
+ onSave: saveNoop,
77
+ },
78
+ {
79
+ key: 'owner',
80
+ kind: 'custom',
81
+ label: 'Owner',
82
+ emptyLabel: 'Unassigned',
83
+ render: () => (
84
+ <div className="space-y-1">
85
+ <div className="text-xs text-muted-foreground">Owner</div>
86
+ <Badge variant="muted">Unassigned</Badge>
87
+ </div>
88
+ ),
89
+ },
90
+ ]
91
+
92
+ // Sections resolve their labels through an app translator in real pages; the
93
+ // gallery translator just surfaces each key's fallback (or last segment).
94
+ const galleryTranslator = (key: string, fallback?: string) =>
95
+ fallback ?? key.split('.').pop() ?? key
96
+
97
+ const notesAdapter: NotesSectionProps['dataAdapter'] = {
98
+ list: async () => [
99
+ {
100
+ id: 'note-1',
101
+ body: 'Asked for a revised quote — follow up on Friday.',
102
+ createdAt: '2026-07-15T09:30:00.000Z',
103
+ authorName: 'Anna Nowak',
104
+ },
105
+ {
106
+ id: 'note-2',
107
+ body: 'Prefers email contact over phone.',
108
+ createdAt: '2026-07-10T14:05:00.000Z',
109
+ authorName: 'Jan Kowalski',
110
+ },
111
+ ],
112
+ create: async () => {},
113
+ update: async () => {},
114
+ delete: async () => {},
115
+ }
116
+
117
+ const addressesAdapter: AddressDataAdapter = {
118
+ list: async () => [
119
+ {
120
+ id: 'address-1',
121
+ name: 'Headquarters',
122
+ addressLine1: 'Prosta 51',
123
+ city: 'Warszawa',
124
+ postalCode: '00-838',
125
+ country: 'PL',
126
+ isPrimary: true,
127
+ },
128
+ {
129
+ id: 'address-2',
130
+ name: 'Warehouse',
131
+ addressLine1: 'Magazynowa 7',
132
+ city: 'Pruszków',
133
+ postalCode: '05-800',
134
+ country: 'PL',
135
+ },
136
+ ],
137
+ create: async () => {},
138
+ update: async () => {},
139
+ delete: async () => {},
140
+ }
141
+
142
+ const detailFieldsSectionEntry: GalleryEntry = {
143
+ id: 'detail-fields-section',
144
+ title: 'DetailFieldsSection',
145
+ importPath: '@open-mercato/ui/backend/detail',
146
+ variants: [
147
+ {
148
+ id: 'field-grid',
149
+ title: 'Inline-editable field grid',
150
+ render: () => (
151
+ <div className="w-full">
152
+ <DetailFieldsSection fields={fieldGrid} />
153
+ </div>
154
+ ),
155
+ code: `import { DetailFieldsSection, type DetailFieldConfig } from '@open-mercato/ui/backend/detail'
156
+
157
+ const fields: DetailFieldConfig[] = [
158
+ { key: 'first-name', kind: 'text', label: 'First name', value: person.firstName, emptyLabel: 'Add first name', onSave: saveFirstName },
159
+ { key: 'phone', kind: 'text', label: 'Phone', value: person.phone, emptyLabel: 'Add phone', onSave: savePhone },
160
+ {
161
+ key: 'status', kind: 'select', label: 'Status', value: person.status, emptyLabel: 'Set status',
162
+ options: [{ value: 'active', label: 'Active' }, { value: 'inactive', label: 'Inactive' }],
163
+ onSave: saveStatus,
164
+ },
165
+ {
166
+ key: 'notes', kind: 'multiline', label: 'Notes', value: person.notes, emptyLabel: 'Add notes',
167
+ onSave: saveNotes, gridClassName: 'sm:col-span-2 md:col-span-3',
168
+ },
169
+ ]
170
+
171
+ <DetailFieldsSection fields={fields} />`,
172
+ },
173
+ {
174
+ id: 'custom-field',
175
+ title: 'With a custom field cell',
176
+ render: () => (
177
+ <div className="w-full">
178
+ <DetailFieldsSection fields={fieldGridWithCustom} />
179
+ </div>
180
+ ),
181
+ code: `import { DetailFieldsSection, type DetailFieldConfig } from '@open-mercato/ui/backend/detail'
182
+ import { Badge } from '@open-mercato/ui/primitives/badge'
183
+
184
+ const fields: DetailFieldConfig[] = [
185
+ { key: 'email', kind: 'text', label: 'Email', value: person.email, emptyLabel: 'Add email', onSave: saveEmail },
186
+ {
187
+ key: 'owner', kind: 'custom', label: 'Owner', emptyLabel: 'Unassigned',
188
+ render: () => (
189
+ <div className="space-y-1">
190
+ <div className="text-xs text-muted-foreground">Owner</div>
191
+ <Badge variant="muted">Unassigned</Badge>
192
+ </div>
193
+ ),
194
+ },
195
+ ]
196
+
197
+ <DetailFieldsSection fields={fields} />`,
198
+ },
199
+ ],
200
+ }
201
+
202
+ const loadingMessageEntry: GalleryEntry = {
203
+ id: 'loading-message',
204
+ title: 'LoadingMessage',
205
+ importPath: '@open-mercato/ui/backend/detail',
206
+ variants: [
207
+ {
208
+ id: 'default',
209
+ title: 'default',
210
+ render: () => (
211
+ <div className="w-full max-w-md">
212
+ <LoadingMessage label="Loading customer…" />
213
+ </div>
214
+ ),
215
+ code: `import { LoadingMessage } from '@open-mercato/ui/backend/detail'
216
+
217
+ if (isLoading) return <LoadingMessage label={t('customers.people.detail.loading')} />`,
218
+ },
219
+ ],
220
+ }
221
+
222
+ const errorMessageEntry: GalleryEntry = {
223
+ id: 'error-message',
224
+ title: 'ErrorMessage',
225
+ importPath: '@open-mercato/ui/backend/detail',
226
+ variants: [
227
+ {
228
+ id: 'basic',
229
+ title: 'Label only',
230
+ render: () => (
231
+ <div className="w-full max-w-md">
232
+ <ErrorMessage label="Failed to load customer." />
233
+ </div>
234
+ ),
235
+ code: `import { ErrorMessage } from '@open-mercato/ui/backend/detail'
236
+
237
+ if (error) return <ErrorMessage label={error} />`,
238
+ },
239
+ {
240
+ id: 'with-description-action',
241
+ title: 'With description and action',
242
+ render: () => (
243
+ <div className="w-full max-w-md">
244
+ <ErrorMessage
245
+ label="Failed to load attachments."
246
+ description="The storage service did not respond in time."
247
+ action={
248
+ <Button variant="outline" size="sm">
249
+ Retry
250
+ </Button>
251
+ }
252
+ />
253
+ </div>
254
+ ),
255
+ code: `import { ErrorMessage } from '@open-mercato/ui/backend/detail'
256
+ import { Button } from '@open-mercato/ui/primitives/button'
257
+
258
+ <ErrorMessage
259
+ label="Failed to load attachments."
260
+ description="The storage service did not respond in time."
261
+ action={<Button variant="outline" size="sm" onClick={retry}>Retry</Button>}
262
+ />`,
263
+ },
264
+ ],
265
+ }
266
+
267
+ const notesSectionEntry: GalleryEntry = {
268
+ id: 'notes-section',
269
+ title: 'NotesSection',
270
+ importPath: '@open-mercato/ui/backend/detail',
271
+ variants: [
272
+ {
273
+ id: 'mock-adapter',
274
+ title: 'Timeline (inline mock adapter — no API)',
275
+ render: () => (
276
+ <div className="w-full max-w-2xl">
277
+ <NotesSection
278
+ entityId="person-1"
279
+ viewerUserId="user-1"
280
+ viewerName="Anna Nowak"
281
+ emptyLabel="No notes yet"
282
+ addActionLabel="Add note"
283
+ emptyState={{
284
+ title: 'No notes yet',
285
+ actionLabel: 'Add note',
286
+ description: 'Notes added here are visible to the whole team.',
287
+ }}
288
+ translator={galleryTranslator}
289
+ dataAdapter={notesAdapter}
290
+ />
291
+ </div>
292
+ ),
293
+ code: `import { NotesSection, type NotesDataAdapter } from '@open-mercato/ui/backend/detail'
294
+
295
+ // Implement the adapter against your module's API (apiCall) — never fork the section.
296
+ const notesAdapter: NotesDataAdapter = {
297
+ list: async ({ entityId }) => fetchNotes(entityId),
298
+ create: async ({ entityId, body }) => createNote(entityId, body),
299
+ update: async ({ id, patch }) => updateNote(id, patch),
300
+ delete: async ({ id }) => deleteNote(id),
301
+ }
302
+
303
+ <NotesSection
304
+ entityId={personId}
305
+ viewerUserId={viewer.id}
306
+ viewerName={viewer.name}
307
+ emptyLabel={t('customers.people.detail.notes.empty')}
308
+ addActionLabel={t('customers.people.detail.notes.add')}
309
+ emptyState={{
310
+ title: t('customers.people.detail.notes.emptyTitle'),
311
+ actionLabel: t('customers.people.detail.notes.add'),
312
+ }}
313
+ translator={t}
314
+ dataAdapter={notesAdapter}
315
+ />`,
316
+ },
317
+ ],
318
+ }
319
+
320
+ const addressesSectionEntry: GalleryEntry = {
321
+ id: 'addresses-section',
322
+ title: 'AddressesSection',
323
+ importPath: '@open-mercato/ui/backend/detail',
324
+ variants: [
325
+ {
326
+ id: 'mock-adapter',
327
+ title: 'Address tiles (inline mock adapter — no API)',
328
+ render: () => (
329
+ <div className="w-full max-w-2xl">
330
+ <AddressesSection
331
+ entityId="person-1"
332
+ emptyLabel="No addresses yet"
333
+ addActionLabel="Add address"
334
+ emptyState={{ title: 'No addresses yet', actionLabel: 'Add address' }}
335
+ translator={galleryTranslator}
336
+ dataAdapter={addressesAdapter}
337
+ />
338
+ </div>
339
+ ),
340
+ code: `import { AddressesSection, type AddressDataAdapter } from '@open-mercato/ui/backend/detail'
341
+
342
+ const addressesAdapter: AddressDataAdapter = {
343
+ list: async ({ entityId }) => fetchAddresses(entityId),
344
+ create: async ({ entityId, payload }) => createAddress(entityId, payload),
345
+ update: async ({ id, payload }) => updateAddress(id, payload),
346
+ delete: async ({ id }) => deleteAddress(id),
347
+ }
348
+
349
+ <AddressesSection
350
+ entityId={personId}
351
+ emptyLabel={t('customers.people.detail.addresses.empty')}
352
+ addActionLabel={t('customers.people.detail.addresses.add')}
353
+ emptyState={{
354
+ title: t('customers.people.detail.addresses.emptyTitle'),
355
+ actionLabel: t('customers.people.detail.addresses.add'),
356
+ }}
357
+ translator={t}
358
+ dataAdapter={addressesAdapter}
359
+ />`,
360
+ },
361
+ ],
362
+ }
363
+
364
+ const attachmentsSectionEntry: GalleryEntry = {
365
+ id: 'attachments-section',
366
+ title: 'AttachmentsSection',
367
+ importPath: '@open-mercato/ui/backend/detail',
368
+ variants: [
369
+ {
370
+ id: 'unsaved-record',
371
+ title: 'Unsaved record (recordId null — API fetch disabled)',
372
+ render: () => (
373
+ <div className="w-full max-w-2xl">
374
+ <AttachmentsSection
375
+ entityId="customers:person"
376
+ recordId={null}
377
+ title="Attachments"
378
+ description="Files linked to this record."
379
+ />
380
+ </div>
381
+ ),
382
+ code: `import { AttachmentsSection } from '@open-mercato/ui/backend/detail'
383
+
384
+ // Fetches /api/attachments internally once recordId is set;
385
+ // with recordId={null} it renders the save-first placeholder.
386
+ <AttachmentsSection
387
+ entityId="customers:person"
388
+ recordId={person?.id ?? null}
389
+ title={t('customers.people.detail.attachments.title')}
390
+ description={t('customers.people.detail.attachments.description')}
391
+ />`,
392
+ },
393
+ ],
394
+ }
395
+
396
+ export const entries: GalleryEntry[] = [
397
+ detailFieldsSectionEntry,
398
+ loadingMessageEntry,
399
+ errorMessageEntry,
400
+ notesSectionEntry,
401
+ addressesSectionEntry,
402
+ attachmentsSectionEntry,
403
+ ]