@open-mercato/ui 0.4.2-canary-c02407ff85

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 (319) hide show
  1. package/build.mjs +62 -0
  2. package/dist/backend/AppShell.js +902 -0
  3. package/dist/backend/AppShell.js.map +7 -0
  4. package/dist/backend/ConfirmDialog.js +17 -0
  5. package/dist/backend/ConfirmDialog.js.map +7 -0
  6. package/dist/backend/ContextHelp.js +31 -0
  7. package/dist/backend/ContextHelp.js.map +7 -0
  8. package/dist/backend/CrudForm.js +2028 -0
  9. package/dist/backend/CrudForm.js.map +7 -0
  10. package/dist/backend/DataTable.js +1363 -0
  11. package/dist/backend/DataTable.js.map +7 -0
  12. package/dist/backend/EmptyState.js +52 -0
  13. package/dist/backend/EmptyState.js.map +7 -0
  14. package/dist/backend/FilterBar.js +140 -0
  15. package/dist/backend/FilterBar.js.map +7 -0
  16. package/dist/backend/FilterOverlay.js +279 -0
  17. package/dist/backend/FilterOverlay.js.map +7 -0
  18. package/dist/backend/FlashMessages.js +66 -0
  19. package/dist/backend/FlashMessages.js.map +7 -0
  20. package/dist/backend/JsonBuilder.js +322 -0
  21. package/dist/backend/JsonBuilder.js.map +7 -0
  22. package/dist/backend/JsonDisplay.js +203 -0
  23. package/dist/backend/JsonDisplay.js.map +7 -0
  24. package/dist/backend/Page.js +27 -0
  25. package/dist/backend/Page.js.map +7 -0
  26. package/dist/backend/PerspectiveSidebar.js +282 -0
  27. package/dist/backend/PerspectiveSidebar.js.map +7 -0
  28. package/dist/backend/RowActions.js +148 -0
  29. package/dist/backend/RowActions.js.map +7 -0
  30. package/dist/backend/TruncatedCell.js +92 -0
  31. package/dist/backend/TruncatedCell.js.map +7 -0
  32. package/dist/backend/UserMenu.js +107 -0
  33. package/dist/backend/UserMenu.js.map +7 -0
  34. package/dist/backend/ValueIcons.js +34 -0
  35. package/dist/backend/ValueIcons.js.map +7 -0
  36. package/dist/backend/custom-fields/FieldDefinitionsEditor.js +1264 -0
  37. package/dist/backend/custom-fields/FieldDefinitionsEditor.js.map +7 -0
  38. package/dist/backend/custom-fields/FieldDefinitionsManager.js +332 -0
  39. package/dist/backend/custom-fields/FieldDefinitionsManager.js.map +7 -0
  40. package/dist/backend/dashboard/DashboardScreen.js +578 -0
  41. package/dist/backend/dashboard/DashboardScreen.js.map +7 -0
  42. package/dist/backend/dashboard/index.js +5 -0
  43. package/dist/backend/dashboard/index.js.map +7 -0
  44. package/dist/backend/dashboard/widgetRegistry.js +55 -0
  45. package/dist/backend/dashboard/widgetRegistry.js.map +7 -0
  46. package/dist/backend/detail/ActivitiesSection.js +962 -0
  47. package/dist/backend/detail/ActivitiesSection.js.map +7 -0
  48. package/dist/backend/detail/AddressEditor.js +413 -0
  49. package/dist/backend/detail/AddressEditor.js.map +7 -0
  50. package/dist/backend/detail/AddressTiles.js +437 -0
  51. package/dist/backend/detail/AddressTiles.js.map +7 -0
  52. package/dist/backend/detail/AddressesSection.js +264 -0
  53. package/dist/backend/detail/AddressesSection.js.map +7 -0
  54. package/dist/backend/detail/AttachmentDeleteDialog.js +41 -0
  55. package/dist/backend/detail/AttachmentDeleteDialog.js.map +7 -0
  56. package/dist/backend/detail/AttachmentMetadataDialog.js +517 -0
  57. package/dist/backend/detail/AttachmentMetadataDialog.js.map +7 -0
  58. package/dist/backend/detail/AttachmentsSection.js +367 -0
  59. package/dist/backend/detail/AttachmentsSection.js.map +7 -0
  60. package/dist/backend/detail/CustomDataSection.js +433 -0
  61. package/dist/backend/detail/CustomDataSection.js.map +7 -0
  62. package/dist/backend/detail/DetailFieldsSection.js +75 -0
  63. package/dist/backend/detail/DetailFieldsSection.js.map +7 -0
  64. package/dist/backend/detail/ErrorMessage.js +28 -0
  65. package/dist/backend/detail/ErrorMessage.js.map +7 -0
  66. package/dist/backend/detail/InlineEditors.js +681 -0
  67. package/dist/backend/detail/InlineEditors.js.map +7 -0
  68. package/dist/backend/detail/LoadingMessage.js +14 -0
  69. package/dist/backend/detail/LoadingMessage.js.map +7 -0
  70. package/dist/backend/detail/NotesSection.js +1032 -0
  71. package/dist/backend/detail/NotesSection.js.map +7 -0
  72. package/dist/backend/detail/TabEmptyState.js +25 -0
  73. package/dist/backend/detail/TabEmptyState.js.map +7 -0
  74. package/dist/backend/detail/TagsSection.js +254 -0
  75. package/dist/backend/detail/TagsSection.js.map +7 -0
  76. package/dist/backend/detail/addressFormat.js +77 -0
  77. package/dist/backend/detail/addressFormat.js.map +7 -0
  78. package/dist/backend/detail/index.js +34 -0
  79. package/dist/backend/detail/index.js.map +7 -0
  80. package/dist/backend/fields/registry.generated.js +8 -0
  81. package/dist/backend/fields/registry.generated.js.map +7 -0
  82. package/dist/backend/fields/registry.js +29 -0
  83. package/dist/backend/fields/registry.js.map +7 -0
  84. package/dist/backend/indexes/PartialIndexBanner.js +58 -0
  85. package/dist/backend/indexes/PartialIndexBanner.js.map +7 -0
  86. package/dist/backend/indexes/store.js +62 -0
  87. package/dist/backend/indexes/store.js.map +7 -0
  88. package/dist/backend/injection/InjectionSpot.js +179 -0
  89. package/dist/backend/injection/InjectionSpot.js.map +7 -0
  90. package/dist/backend/injection/PageInjectionBoundary.js +26 -0
  91. package/dist/backend/injection/PageInjectionBoundary.js.map +7 -0
  92. package/dist/backend/injection/helpers.js +26 -0
  93. package/dist/backend/injection/helpers.js.map +7 -0
  94. package/dist/backend/injection/widgetRegistry.js +55 -0
  95. package/dist/backend/injection/widgetRegistry.js.map +7 -0
  96. package/dist/backend/inputs/ComboboxInput.js +225 -0
  97. package/dist/backend/inputs/ComboboxInput.js.map +7 -0
  98. package/dist/backend/inputs/LookupSelect.js +191 -0
  99. package/dist/backend/inputs/LookupSelect.js.map +7 -0
  100. package/dist/backend/inputs/PhoneNumberField.js +100 -0
  101. package/dist/backend/inputs/PhoneNumberField.js.map +7 -0
  102. package/dist/backend/inputs/SwitchableMarkdownInput.js +92 -0
  103. package/dist/backend/inputs/SwitchableMarkdownInput.js.map +7 -0
  104. package/dist/backend/inputs/TagsInput.js +222 -0
  105. package/dist/backend/inputs/TagsInput.js.map +7 -0
  106. package/dist/backend/inputs/index.js +6 -0
  107. package/dist/backend/inputs/index.js.map +7 -0
  108. package/dist/backend/operations/LastOperationBanner.js +80 -0
  109. package/dist/backend/operations/LastOperationBanner.js.map +7 -0
  110. package/dist/backend/operations/store.js +183 -0
  111. package/dist/backend/operations/store.js.map +7 -0
  112. package/dist/backend/schedule/ScheduleAgenda.js +107 -0
  113. package/dist/backend/schedule/ScheduleAgenda.js.map +7 -0
  114. package/dist/backend/schedule/ScheduleGrid.js +107 -0
  115. package/dist/backend/schedule/ScheduleGrid.js.map +7 -0
  116. package/dist/backend/schedule/ScheduleToolbar.js +166 -0
  117. package/dist/backend/schedule/ScheduleToolbar.js.map +7 -0
  118. package/dist/backend/schedule/ScheduleView.js +165 -0
  119. package/dist/backend/schedule/ScheduleView.js.map +7 -0
  120. package/dist/backend/schedule/index.js +6 -0
  121. package/dist/backend/schedule/index.js.map +7 -0
  122. package/dist/backend/schedule/recurrence.js +83 -0
  123. package/dist/backend/schedule/recurrence.js.map +7 -0
  124. package/dist/backend/schedule/types.js +1 -0
  125. package/dist/backend/schedule/types.js.map +7 -0
  126. package/dist/backend/upgrades/UpgradeActionBanner.js +91 -0
  127. package/dist/backend/upgrades/UpgradeActionBanner.js.map +7 -0
  128. package/dist/backend/utils/api.js +127 -0
  129. package/dist/backend/utils/api.js.map +7 -0
  130. package/dist/backend/utils/apiCall.js +48 -0
  131. package/dist/backend/utils/apiCall.js.map +7 -0
  132. package/dist/backend/utils/crud.js +126 -0
  133. package/dist/backend/utils/crud.js.map +7 -0
  134. package/dist/backend/utils/customFieldColumns.js +56 -0
  135. package/dist/backend/utils/customFieldColumns.js.map +7 -0
  136. package/dist/backend/utils/customFieldDefs.js +143 -0
  137. package/dist/backend/utils/customFieldDefs.js.map +7 -0
  138. package/dist/backend/utils/customFieldFilters.js +126 -0
  139. package/dist/backend/utils/customFieldFilters.js.map +7 -0
  140. package/dist/backend/utils/customFieldForms.js +162 -0
  141. package/dist/backend/utils/customFieldForms.js.map +7 -0
  142. package/dist/backend/utils/customFieldValues.js +26 -0
  143. package/dist/backend/utils/customFieldValues.js.map +7 -0
  144. package/dist/backend/utils/flash.js +16 -0
  145. package/dist/backend/utils/flash.js.map +7 -0
  146. package/dist/backend/utils/nav.js +185 -0
  147. package/dist/backend/utils/nav.js.map +7 -0
  148. package/dist/backend/utils/serverErrors.js +230 -0
  149. package/dist/backend/utils/serverErrors.js.map +7 -0
  150. package/dist/frontend/AuthFooter.js +23 -0
  151. package/dist/frontend/AuthFooter.js.map +7 -0
  152. package/dist/frontend/LanguageSwitcher.js +57 -0
  153. package/dist/frontend/LanguageSwitcher.js.map +7 -0
  154. package/dist/frontend/Layout.js +14 -0
  155. package/dist/frontend/Layout.js.map +7 -0
  156. package/dist/index.js +32 -0
  157. package/dist/index.js.map +7 -0
  158. package/dist/primitives/DataLoader.js +67 -0
  159. package/dist/primitives/DataLoader.js.map +7 -0
  160. package/dist/primitives/ErrorNotice.js +20 -0
  161. package/dist/primitives/ErrorNotice.js.map +7 -0
  162. package/dist/primitives/alert.js +38 -0
  163. package/dist/primitives/alert.js.map +7 -0
  164. package/dist/primitives/badge.js +28 -0
  165. package/dist/primitives/badge.js.map +7 -0
  166. package/dist/primitives/button.js +44 -0
  167. package/dist/primitives/button.js.map +7 -0
  168. package/dist/primitives/card.js +91 -0
  169. package/dist/primitives/card.js.map +7 -0
  170. package/dist/primitives/checkbox.js +28 -0
  171. package/dist/primitives/checkbox.js.map +7 -0
  172. package/dist/primitives/dialog.js +90 -0
  173. package/dist/primitives/dialog.js.map +7 -0
  174. package/dist/primitives/input.js +22 -0
  175. package/dist/primitives/input.js.map +7 -0
  176. package/dist/primitives/label.js +21 -0
  177. package/dist/primitives/label.js.map +7 -0
  178. package/dist/primitives/separator.js +9 -0
  179. package/dist/primitives/separator.js.map +7 -0
  180. package/dist/primitives/spinner.js +24 -0
  181. package/dist/primitives/spinner.js.map +7 -0
  182. package/dist/primitives/switch.js +80 -0
  183. package/dist/primitives/switch.js.map +7 -0
  184. package/dist/primitives/table.js +29 -0
  185. package/dist/primitives/table.js.map +7 -0
  186. package/dist/primitives/tabs.js +87 -0
  187. package/dist/primitives/tabs.js.map +7 -0
  188. package/dist/primitives/textarea.js +21 -0
  189. package/dist/primitives/textarea.js.map +7 -0
  190. package/dist/primitives/tooltip.js +60 -0
  191. package/dist/primitives/tooltip.js.map +7 -0
  192. package/dist/theme/QueryProvider.js +44 -0
  193. package/dist/theme/QueryProvider.js.map +7 -0
  194. package/dist/theme/ThemeProvider.js +95 -0
  195. package/dist/theme/ThemeProvider.js.map +7 -0
  196. package/dist/theme/ThemeToggle.js +88 -0
  197. package/dist/theme/ThemeToggle.js.map +7 -0
  198. package/dist/theme/index.js +10 -0
  199. package/dist/theme/index.js.map +7 -0
  200. package/dist/types/react-big-calendar.d.js +1 -0
  201. package/dist/types/react-big-calendar.d.js.map +7 -0
  202. package/jest.config.cjs +23 -0
  203. package/jest.setup.ts +55 -0
  204. package/package.json +105 -0
  205. package/src/backend/AppShell.tsx +1096 -0
  206. package/src/backend/ConfirmDialog.tsx +19 -0
  207. package/src/backend/ContextHelp.tsx +38 -0
  208. package/src/backend/CrudForm.tsx +2503 -0
  209. package/src/backend/DataTable.tsx +1730 -0
  210. package/src/backend/EmptyState.tsx +65 -0
  211. package/src/backend/FilterBar.tsx +161 -0
  212. package/src/backend/FilterOverlay.tsx +328 -0
  213. package/src/backend/FlashMessages.tsx +82 -0
  214. package/src/backend/JsonBuilder.tsx +362 -0
  215. package/src/backend/JsonDisplay.tsx +254 -0
  216. package/src/backend/Page.tsx +30 -0
  217. package/src/backend/PerspectiveSidebar.tsx +337 -0
  218. package/src/backend/RowActions.tsx +151 -0
  219. package/src/backend/TruncatedCell.tsx +133 -0
  220. package/src/backend/UserMenu.tsx +118 -0
  221. package/src/backend/ValueIcons.tsx +48 -0
  222. package/src/backend/__tests__/AppShell.test.tsx +115 -0
  223. package/src/backend/__tests__/CrudForm.render.test.tsx +30 -0
  224. package/src/backend/__tests__/DataTable.render.test.tsx +48 -0
  225. package/src/backend/__tests__/custom-field-filters.test.ts +72 -0
  226. package/src/backend/__tests__/custom-field-forms.test.ts +54 -0
  227. package/src/backend/__tests__/serverErrors.test.ts +83 -0
  228. package/src/backend/custom-fields/FieldDefinitionsEditor.tsx +1292 -0
  229. package/src/backend/custom-fields/FieldDefinitionsManager.tsx +381 -0
  230. package/src/backend/dashboard/DashboardScreen.tsx +684 -0
  231. package/src/backend/dashboard/__tests__/DashboardScreen.test.tsx +112 -0
  232. package/src/backend/dashboard/index.ts +1 -0
  233. package/src/backend/dashboard/widgetRegistry.ts +68 -0
  234. package/src/backend/detail/ActivitiesSection.tsx +1284 -0
  235. package/src/backend/detail/AddressEditor.tsx +472 -0
  236. package/src/backend/detail/AddressTiles.tsx +587 -0
  237. package/src/backend/detail/AddressesSection.tsx +346 -0
  238. package/src/backend/detail/AttachmentDeleteDialog.tsx +56 -0
  239. package/src/backend/detail/AttachmentMetadataDialog.tsx +672 -0
  240. package/src/backend/detail/AttachmentsSection.tsx +414 -0
  241. package/src/backend/detail/CustomDataSection.tsx +530 -0
  242. package/src/backend/detail/DetailFieldsSection.tsx +147 -0
  243. package/src/backend/detail/ErrorMessage.tsx +32 -0
  244. package/src/backend/detail/InlineEditors.tsx +877 -0
  245. package/src/backend/detail/LoadingMessage.tsx +14 -0
  246. package/src/backend/detail/NotesSection.tsx +1275 -0
  247. package/src/backend/detail/TabEmptyState.tsx +48 -0
  248. package/src/backend/detail/TagsSection.tsx +314 -0
  249. package/src/backend/detail/addressFormat.tsx +121 -0
  250. package/src/backend/detail/index.ts +44 -0
  251. package/src/backend/fields/registry.generated.ts +8 -0
  252. package/src/backend/fields/registry.ts +38 -0
  253. package/src/backend/indexes/PartialIndexBanner.tsx +68 -0
  254. package/src/backend/indexes/store.ts +88 -0
  255. package/src/backend/injection/InjectionSpot.tsx +236 -0
  256. package/src/backend/injection/PageInjectionBoundary.tsx +31 -0
  257. package/src/backend/injection/helpers.ts +35 -0
  258. package/src/backend/injection/widgetRegistry.ts +68 -0
  259. package/src/backend/inputs/ComboboxInput.tsx +269 -0
  260. package/src/backend/inputs/LookupSelect.tsx +247 -0
  261. package/src/backend/inputs/PhoneNumberField.tsx +129 -0
  262. package/src/backend/inputs/SwitchableMarkdownInput.tsx +128 -0
  263. package/src/backend/inputs/TagsInput.tsx +259 -0
  264. package/src/backend/inputs/index.ts +5 -0
  265. package/src/backend/operations/LastOperationBanner.tsx +85 -0
  266. package/src/backend/operations/__tests__/LastOperationBanner.test.tsx +99 -0
  267. package/src/backend/operations/store.ts +230 -0
  268. package/src/backend/schedule/ScheduleAgenda.tsx +136 -0
  269. package/src/backend/schedule/ScheduleGrid.tsx +136 -0
  270. package/src/backend/schedule/ScheduleToolbar.tsx +178 -0
  271. package/src/backend/schedule/ScheduleView.tsx +198 -0
  272. package/src/backend/schedule/index.ts +5 -0
  273. package/src/backend/schedule/recurrence.ts +99 -0
  274. package/src/backend/schedule/types.ts +26 -0
  275. package/src/backend/upgrades/UpgradeActionBanner.tsx +128 -0
  276. package/src/backend/utils/__tests__/apiCall.test.ts +109 -0
  277. package/src/backend/utils/__tests__/crud.test.ts +87 -0
  278. package/src/backend/utils/__tests__/customFieldDefs.test.ts +25 -0
  279. package/src/backend/utils/__tests__/customFieldValues.test.ts +35 -0
  280. package/src/backend/utils/api.ts +149 -0
  281. package/src/backend/utils/apiCall.ts +96 -0
  282. package/src/backend/utils/crud.ts +174 -0
  283. package/src/backend/utils/customFieldColumns.ts +71 -0
  284. package/src/backend/utils/customFieldDefs.ts +245 -0
  285. package/src/backend/utils/customFieldFilters.ts +145 -0
  286. package/src/backend/utils/customFieldForms.ts +196 -0
  287. package/src/backend/utils/customFieldValues.ts +41 -0
  288. package/src/backend/utils/flash.ts +17 -0
  289. package/src/backend/utils/nav.ts +238 -0
  290. package/src/backend/utils/serverErrors.ts +302 -0
  291. package/src/frontend/AuthFooter.tsx +29 -0
  292. package/src/frontend/LanguageSwitcher.tsx +66 -0
  293. package/src/frontend/Layout.tsx +13 -0
  294. package/src/index.ts +32 -0
  295. package/src/primitives/DataLoader.tsx +92 -0
  296. package/src/primitives/ErrorNotice.tsx +26 -0
  297. package/src/primitives/alert.tsx +52 -0
  298. package/src/primitives/badge.tsx +31 -0
  299. package/src/primitives/button.tsx +47 -0
  300. package/src/primitives/card.tsx +92 -0
  301. package/src/primitives/checkbox.tsx +28 -0
  302. package/src/primitives/dialog.tsx +110 -0
  303. package/src/primitives/input.tsx +20 -0
  304. package/src/primitives/label.tsx +18 -0
  305. package/src/primitives/separator.tsx +7 -0
  306. package/src/primitives/spinner.tsx +27 -0
  307. package/src/primitives/switch.tsx +86 -0
  308. package/src/primitives/table.tsx +27 -0
  309. package/src/primitives/tabs.tsx +128 -0
  310. package/src/primitives/textarea.tsx +20 -0
  311. package/src/primitives/tooltip.tsx +85 -0
  312. package/src/theme/QueryProvider.tsx +46 -0
  313. package/src/theme/ThemeProvider.tsx +120 -0
  314. package/src/theme/ThemeToggle.tsx +88 -0
  315. package/src/theme/index.ts +3 -0
  316. package/src/types/react-big-calendar.d.ts +16 -0
  317. package/tsconfig.build.json +11 -0
  318. package/tsconfig.json +9 -0
  319. package/watch.mjs +6 -0
@@ -0,0 +1,230 @@
1
+ const JSON_FIELD_KEYS = ["fieldErrors", "fields", "errors", "data"];
2
+ const ISSUE_KEYS = ["details", "issues", "errors"];
3
+ function coerceFieldErrors(input) {
4
+ if (!input || typeof input !== "object") return null;
5
+ const result = {};
6
+ for (const [rawKey, rawValue] of Object.entries(input)) {
7
+ const key = typeof rawKey === "string" && rawKey.trim().length > 0 ? rawKey.trim() : null;
8
+ if (!key) continue;
9
+ if (rawValue === void 0 || rawValue === null) continue;
10
+ const message = typeof rawValue === "string" ? rawValue : typeof rawValue?.message === "string" ? rawValue.message : String(rawValue);
11
+ if (!message) continue;
12
+ result[key] = message;
13
+ }
14
+ return Object.keys(result).length ? result : null;
15
+ }
16
+ function mapIssueArray(issues) {
17
+ if (!Array.isArray(issues)) return null;
18
+ const result = {};
19
+ for (const issue of issues) {
20
+ if (!issue || typeof issue !== "object") continue;
21
+ const pathValue = Array.isArray(issue.path) ? issue.path : [];
22
+ let field = null;
23
+ for (const part of pathValue) {
24
+ if (typeof part === "string" && part.trim().length > 0) {
25
+ field = part.trim();
26
+ break;
27
+ }
28
+ }
29
+ if (!field && typeof issue.field === "string") {
30
+ const fromField = issue.field.trim();
31
+ field = fromField.length > 0 ? fromField : null;
32
+ }
33
+ if (!field && pathValue.length > 0) {
34
+ const joined = pathValue.map((part) => String(part)).join(".");
35
+ if (joined) field = joined;
36
+ }
37
+ if (!field) continue;
38
+ const message = typeof issue.message === "string" ? issue.message : null;
39
+ if (!message) continue;
40
+ result[field] = message;
41
+ }
42
+ return Object.keys(result).length ? result : null;
43
+ }
44
+ function tryParseJson(text) {
45
+ try {
46
+ return JSON.parse(text);
47
+ } catch {
48
+ return null;
49
+ }
50
+ }
51
+ function collectCandidatePayloads(err) {
52
+ const candidates = [];
53
+ if (!err) return candidates;
54
+ candidates.push(err);
55
+ if (typeof err === "string") {
56
+ const parsed = tryParseJson(err);
57
+ if (parsed) candidates.push(parsed);
58
+ } else if (err instanceof Error) {
59
+ if (typeof err.message === "string" && err.message.trim()) {
60
+ const parsed = tryParseJson(err.message.trim());
61
+ if (parsed) candidates.push(parsed);
62
+ }
63
+ if (err.cause) {
64
+ candidates.push(err.cause);
65
+ }
66
+ } else if (typeof err === "object") {
67
+ const maybeMessage = err?.message;
68
+ if (typeof maybeMessage === "string") {
69
+ const parsed = tryParseJson(maybeMessage);
70
+ if (parsed) candidates.push(parsed);
71
+ }
72
+ if (err?.body) candidates.push(err.body);
73
+ if (err?.response) candidates.push(err.response);
74
+ if (err?.data) candidates.push(err.data);
75
+ }
76
+ return candidates;
77
+ }
78
+ function normalizeCrudServerError(err) {
79
+ let message;
80
+ let fieldErrors;
81
+ const processed = /* @__PURE__ */ new Set();
82
+ const queue = collectCandidatePayloads(err);
83
+ while (queue.length) {
84
+ const current = queue.shift();
85
+ if (!current || processed.has(current)) continue;
86
+ processed.add(current);
87
+ if (typeof current === "string") {
88
+ if (!message) message = current;
89
+ const parsed = tryParseJson(current);
90
+ if (parsed) queue.push(parsed);
91
+ continue;
92
+ }
93
+ if (current instanceof Response) {
94
+ const body = current?._bodyInit;
95
+ if (body) queue.push(body);
96
+ continue;
97
+ }
98
+ if (typeof current !== "object") continue;
99
+ const candidateMessage = typeof current.error === "string" ? current.error : typeof current.message === "string" ? current.message : void 0;
100
+ if (candidateMessage && !message) message = candidateMessage;
101
+ for (const key of JSON_FIELD_KEYS) {
102
+ const value = current[key];
103
+ if (value && typeof value === "object") {
104
+ const mapped = coerceFieldErrors(value);
105
+ if (mapped) {
106
+ fieldErrors = { ...fieldErrors || {}, ...mapped };
107
+ }
108
+ }
109
+ }
110
+ for (const key of ISSUE_KEYS) {
111
+ const value = current[key];
112
+ const mapped = mapIssueArray(value);
113
+ if (mapped) {
114
+ fieldErrors = { ...fieldErrors || {}, ...mapped };
115
+ }
116
+ }
117
+ const nestedKeys = ["body", "response", "data", "details"];
118
+ for (const nestedKey of nestedKeys) {
119
+ const nested = current[nestedKey];
120
+ if (nested && !processed.has(nested)) queue.push(nested);
121
+ }
122
+ }
123
+ if (!message && fieldErrors && Object.keys(fieldErrors).length === 1) {
124
+ const [, firstMessage] = Object.entries(fieldErrors)[0];
125
+ message = firstMessage;
126
+ }
127
+ if (!message && err instanceof Error && err.message) {
128
+ message = err.message;
129
+ } else if (!message && typeof err === "string") {
130
+ message = err;
131
+ }
132
+ return { message, fieldErrors };
133
+ }
134
+ function mapServerFieldNameToFormId(field, options) {
135
+ const trimmed = field.trim();
136
+ const customEntity = !!options?.customEntity;
137
+ if (customEntity) {
138
+ if (trimmed.startsWith("cf_")) return trimmed.slice(3);
139
+ if (trimmed.startsWith("cf:")) return trimmed.slice(3);
140
+ return trimmed;
141
+ }
142
+ if (trimmed.startsWith("cf_")) return trimmed;
143
+ if (trimmed.startsWith("cf:")) return `cf_${trimmed.slice(3)}`;
144
+ return trimmed;
145
+ }
146
+ function mapCrudServerErrorToFormErrors(err, options) {
147
+ const normalized = normalizeCrudServerError(err);
148
+ const fieldErrors = normalized.fieldErrors;
149
+ if (!fieldErrors) return { message: normalized.message };
150
+ const mapped = {};
151
+ for (const [key, value] of Object.entries(fieldErrors)) {
152
+ const formId = mapServerFieldNameToFormId(key, options);
153
+ if (!formId) continue;
154
+ mapped[formId] = value;
155
+ }
156
+ let message = normalized.message;
157
+ const firstEntry = Object.entries(mapped)[0];
158
+ if (firstEntry && (!message || typeof message === "string" && message.trim().toLowerCase() === "invalid input")) {
159
+ const [, fieldMessage] = firstEntry;
160
+ if (typeof fieldMessage === "string" && fieldMessage.trim().length) {
161
+ message = fieldMessage;
162
+ }
163
+ }
164
+ return {
165
+ message,
166
+ fieldErrors: mapped
167
+ };
168
+ }
169
+ function parseServerMessage(input) {
170
+ const trimmed = input.trim();
171
+ if (!trimmed) return trimmed;
172
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
173
+ try {
174
+ const parsed = JSON.parse(trimmed);
175
+ const text = typeof parsed?.error === "string" && parsed.error.trim() ? parsed.error.trim() : typeof parsed?.message === "string" && parsed.message.trim() ? parsed.message.trim() : null;
176
+ if (text) return text;
177
+ } catch {
178
+ }
179
+ }
180
+ return trimmed;
181
+ }
182
+ async function raiseCrudError(res, fallbackMessage) {
183
+ let raw = null;
184
+ try {
185
+ raw = await res.text();
186
+ } catch {
187
+ raw = null;
188
+ }
189
+ const trimmed = raw && raw.trim() ? raw.trim() : null;
190
+ const parsed = trimmed ? tryParseJson(trimmed) : null;
191
+ if (parsed && typeof parsed === "object") {
192
+ const data = parsed;
193
+ const rawMessage = typeof data.error === "string" && data.error.trim() ? data.error.trim() : typeof data.message === "string" && data.message.trim() ? data.message.trim() : fallbackMessage ?? `Request failed (${res.status})`;
194
+ const message2 = parseServerMessage(rawMessage);
195
+ throw {
196
+ ...data,
197
+ status: res.status,
198
+ message: message2,
199
+ raw: trimmed ?? null
200
+ };
201
+ }
202
+ const message = parseServerMessage(fallbackMessage ?? `Request failed (${res.status})`);
203
+ throw { message, status: res.status, raw: trimmed ?? null };
204
+ }
205
+ function createCrudFormError(message, fieldErrors, extras) {
206
+ const error = new Error(message);
207
+ if (fieldErrors && Object.keys(fieldErrors).length) error.fieldErrors = fieldErrors;
208
+ if (extras?.status !== void 0) error.status = extras.status;
209
+ if (extras?.details !== void 0) error.details = extras.details;
210
+ return error;
211
+ }
212
+ async function readJsonSafe(res, fallback = null) {
213
+ try {
214
+ const text = await res.text();
215
+ if (!text) return fallback;
216
+ return JSON.parse(text);
217
+ } catch {
218
+ return fallback;
219
+ }
220
+ }
221
+ export {
222
+ createCrudFormError,
223
+ mapCrudServerErrorToFormErrors,
224
+ mapServerFieldNameToFormId,
225
+ normalizeCrudServerError,
226
+ parseServerMessage,
227
+ raiseCrudError,
228
+ readJsonSafe
229
+ };
230
+ //# sourceMappingURL=serverErrors.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/backend/utils/serverErrors.ts"],
4
+ "sourcesContent": ["export type CrudServerFieldErrors = Record<string, string>\n\nexport type NormalizedCrudServerError = {\n message?: string\n fieldErrors?: CrudServerFieldErrors\n details?: unknown\n status?: number\n raw?: string | null\n [key: string]: unknown\n}\n\nconst JSON_FIELD_KEYS = ['fieldErrors', 'fields', 'errors', 'data'] as const\nconst ISSUE_KEYS = ['details', 'issues', 'errors'] as const\n\nfunction coerceFieldErrors(input: unknown): CrudServerFieldErrors | null {\n if (!input || typeof input !== 'object') return null\n const result: CrudServerFieldErrors = {}\n for (const [rawKey, rawValue] of Object.entries(input as Record<string, unknown>)) {\n const key = typeof rawKey === 'string' && rawKey.trim().length > 0 ? rawKey.trim() : null\n if (!key) continue\n if (rawValue === undefined || rawValue === null) continue\n const message =\n typeof rawValue === 'string'\n ? rawValue\n : typeof (rawValue as any)?.message === 'string'\n ? (rawValue as any).message\n : String(rawValue)\n if (!message) continue\n result[key] = message\n }\n return Object.keys(result).length ? result : null\n}\n\nfunction mapIssueArray(issues: unknown): CrudServerFieldErrors | null {\n if (!Array.isArray(issues)) return null\n const result: CrudServerFieldErrors = {}\n for (const issue of issues) {\n if (!issue || typeof issue !== 'object') continue\n const pathValue: unknown[] = Array.isArray((issue as any).path) ? (issue as any).path : []\n let field: string | null = null\n for (const part of pathValue) {\n if (typeof part === 'string' && part.trim().length > 0) {\n field = part.trim()\n break\n }\n }\n if (!field && typeof (issue as any).field === 'string') {\n const fromField = ((issue as any).field as string).trim()\n field = fromField.length > 0 ? fromField : null\n }\n if (!field && pathValue.length > 0) {\n const joined = pathValue.map((part) => String(part)).join('.')\n if (joined) field = joined\n }\n if (!field) continue\n const message = typeof (issue as any).message === 'string' ? (issue as any).message : null\n if (!message) continue\n result[field] = message\n }\n return Object.keys(result).length ? result : null\n}\n\nfunction tryParseJson(text: string): unknown {\n try {\n return JSON.parse(text)\n } catch {\n return null\n }\n}\n\nfunction collectCandidatePayloads(err: unknown): unknown[] {\n const candidates: unknown[] = []\n if (!err) return candidates\n candidates.push(err)\n\n if (typeof err === 'string') {\n const parsed = tryParseJson(err)\n if (parsed) candidates.push(parsed)\n } else if (err instanceof Error) {\n if (typeof err.message === 'string' && err.message.trim()) {\n const parsed = tryParseJson(err.message.trim())\n if (parsed) candidates.push(parsed)\n }\n if ((err as any).cause) {\n candidates.push((err as any).cause)\n }\n } else if (typeof err === 'object') {\n const maybeMessage = (err as any)?.message\n if (typeof maybeMessage === 'string') {\n const parsed = tryParseJson(maybeMessage)\n if (parsed) candidates.push(parsed)\n }\n if ((err as any)?.body) candidates.push((err as any).body)\n if ((err as any)?.response) candidates.push((err as any).response)\n if ((err as any)?.data) candidates.push((err as any).data)\n }\n\n return candidates\n}\n\nexport function normalizeCrudServerError(err: unknown): NormalizedCrudServerError {\n let message: string | undefined\n let fieldErrors: CrudServerFieldErrors | undefined\n const processed = new Set<unknown>()\n\n const queue = collectCandidatePayloads(err)\n while (queue.length) {\n const current = queue.shift()\n if (!current || processed.has(current)) continue\n processed.add(current)\n\n if (typeof current === 'string') {\n if (!message) message = current\n const parsed = tryParseJson(current)\n if (parsed) queue.push(parsed)\n continue\n }\n\n if (current instanceof Response) {\n const body = (current as any)?._bodyInit\n if (body) queue.push(body)\n continue\n }\n\n if (typeof current !== 'object') continue\n\n const candidateMessage =\n typeof (current as any).error === 'string'\n ? (current as any).error\n : typeof (current as any).message === 'string'\n ? (current as any).message\n : undefined\n if (candidateMessage && !message) message = candidateMessage\n\n for (const key of JSON_FIELD_KEYS) {\n const value = (current as any)[key]\n if (value && typeof value === 'object') {\n const mapped = coerceFieldErrors(value)\n if (mapped) {\n fieldErrors = { ...(fieldErrors || {}), ...mapped }\n }\n }\n }\n\n for (const key of ISSUE_KEYS) {\n const value = (current as any)[key]\n const mapped = mapIssueArray(value)\n if (mapped) {\n fieldErrors = { ...(fieldErrors || {}), ...mapped }\n }\n }\n\n const nestedKeys = ['body', 'response', 'data', 'details']\n for (const nestedKey of nestedKeys) {\n const nested = (current as any)[nestedKey]\n if (nested && !processed.has(nested)) queue.push(nested)\n }\n }\n\n if (!message && fieldErrors && Object.keys(fieldErrors).length === 1) {\n const [, firstMessage] = Object.entries(fieldErrors)[0]\n message = firstMessage\n }\n\n if (!message && err instanceof Error && err.message) {\n message = err.message\n } else if (!message && typeof err === 'string') {\n message = err\n }\n\n return { message, fieldErrors }\n}\n\nexport type FieldNameMapperOptions = {\n customEntity?: boolean\n}\n\nexport function mapServerFieldNameToFormId(field: string, options?: FieldNameMapperOptions): string {\n const trimmed = field.trim()\n const customEntity = !!options?.customEntity\n if (customEntity) {\n if (trimmed.startsWith('cf_')) return trimmed.slice(3)\n if (trimmed.startsWith('cf:')) return trimmed.slice(3)\n return trimmed\n }\n if (trimmed.startsWith('cf_')) return trimmed\n if (trimmed.startsWith('cf:')) return `cf_${trimmed.slice(3)}`\n return trimmed\n}\n\nexport function mapCrudServerErrorToFormErrors(\n err: unknown,\n options?: FieldNameMapperOptions,\n): { message?: string; fieldErrors?: CrudServerFieldErrors } {\n const normalized = normalizeCrudServerError(err)\n const fieldErrors = normalized.fieldErrors\n if (!fieldErrors) return { message: normalized.message }\n\n const mapped: CrudServerFieldErrors = {}\n for (const [key, value] of Object.entries(fieldErrors)) {\n const formId = mapServerFieldNameToFormId(key, options)\n if (!formId) continue\n mapped[formId] = value\n }\n\n let message = normalized.message\n const firstEntry = Object.entries(mapped)[0]\n if (\n firstEntry &&\n (!message || (typeof message === 'string' && message.trim().toLowerCase() === 'invalid input'))\n ) {\n const [, fieldMessage] = firstEntry\n if (typeof fieldMessage === 'string' && fieldMessage.trim().length) {\n message = fieldMessage\n }\n }\n\n return {\n message,\n fieldErrors: mapped,\n }\n}\n\nexport function parseServerMessage(input: string): string {\n const trimmed = input.trim()\n if (!trimmed) return trimmed\n if (trimmed.startsWith('{') || trimmed.startsWith('[')) {\n try {\n const parsed = JSON.parse(trimmed) as Record<string, unknown>\n const text =\n typeof parsed?.error === 'string' && parsed.error.trim()\n ? parsed.error.trim()\n : typeof parsed?.message === 'string' && parsed.message.trim()\n ? parsed.message.trim()\n : null\n if (text) return text\n } catch {\n // ignore JSON parse failure, fall through to trimmed string\n }\n }\n return trimmed\n}\n\nexport async function raiseCrudError(res: Response, fallbackMessage?: string): Promise<never> {\n let raw: string | null = null\n try {\n raw = await res.text()\n } catch {\n raw = null\n }\n\n const trimmed = raw && raw.trim() ? raw.trim() : null\n const parsed = trimmed ? tryParseJson(trimmed) : null\n\n if (parsed && typeof parsed === 'object') {\n const data = parsed as Record<string, unknown>\n const rawMessage =\n typeof data.error === 'string' && data.error.trim()\n ? data.error.trim()\n : typeof data.message === 'string' && data.message.trim()\n ? data.message.trim()\n : fallbackMessage ?? `Request failed (${res.status})`\n const message = parseServerMessage(rawMessage)\n throw {\n ...data,\n status: res.status,\n message,\n raw: trimmed ?? null,\n }\n }\n\n const message = parseServerMessage(fallbackMessage ?? `Request failed (${res.status})`)\n throw { message, status: res.status, raw: trimmed ?? null }\n}\n\nexport type CrudFormError = Error & {\n status?: number\n fieldErrors?: CrudServerFieldErrors\n details?: unknown\n}\n\nexport function createCrudFormError(\n message: string,\n fieldErrors?: CrudServerFieldErrors,\n extras?: Partial<Pick<CrudFormError, 'status' | 'details'>>,\n): CrudFormError {\n const error = new Error(message) as CrudFormError\n if (fieldErrors && Object.keys(fieldErrors).length) error.fieldErrors = fieldErrors\n if (extras?.status !== undefined) error.status = extras.status\n if (extras?.details !== undefined) error.details = extras.details\n return error\n}\n\nexport async function readJsonSafe<T>(res: Response, fallback: T | null = null): Promise<T | null> {\n try {\n const text = await res.text()\n if (!text) return fallback\n return JSON.parse(text) as T\n } catch {\n return fallback\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["message"]
7
+ }
@@ -0,0 +1,23 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import Link from "next/link";
4
+ import { usePathname } from "next/navigation";
5
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
6
+ import { LanguageSwitcher } from "./LanguageSwitcher.js";
7
+ function AuthFooter() {
8
+ const pathname = usePathname();
9
+ const t = useT();
10
+ const shouldShow = pathname === "/login" || typeof pathname === "string" && pathname.startsWith("/onboarding");
11
+ if (!shouldShow) return null;
12
+ return /* @__PURE__ */ jsx("footer", { className: "w-full border-t bg-background/80 backdrop-blur supports-[backdrop-filter]:bg-background/50", children: /* @__PURE__ */ jsxs("div", { className: "max-w-screen-lg mx-auto px-4 py-3 flex flex-wrap items-center justify-end gap-4", children: [
13
+ /* @__PURE__ */ jsxs("nav", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: [
14
+ /* @__PURE__ */ jsx(Link, { href: "/terms", className: "transition hover:text-foreground", children: t("common.terms") }),
15
+ /* @__PURE__ */ jsx(Link, { href: "/privacy", className: "transition hover:text-foreground", children: t("common.privacy") })
16
+ ] }),
17
+ /* @__PURE__ */ jsx(LanguageSwitcher, {})
18
+ ] }) });
19
+ }
20
+ export {
21
+ AuthFooter
22
+ };
23
+ //# sourceMappingURL=AuthFooter.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/frontend/AuthFooter.tsx"],
4
+ "sourcesContent": ["\"use client\"\nimport Link from 'next/link'\nimport { usePathname } from 'next/navigation'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { LanguageSwitcher } from './LanguageSwitcher'\n\nexport function AuthFooter() {\n const pathname = usePathname()\n const t = useT()\n const shouldShow =\n pathname === '/login' ||\n (typeof pathname === 'string' && pathname.startsWith('/onboarding'))\n if (!shouldShow) return null\n return (\n <footer className=\"w-full border-t bg-background/80 backdrop-blur supports-[backdrop-filter]:bg-background/50\">\n <div className=\"max-w-screen-lg mx-auto px-4 py-3 flex flex-wrap items-center justify-end gap-4\">\n <nav className=\"flex items-center gap-3 text-xs text-muted-foreground\">\n <Link href=\"/terms\" className=\"transition hover:text-foreground\">\n {t('common.terms')}\n </Link>\n <Link href=\"/privacy\" className=\"transition hover:text-foreground\">\n {t('common.privacy')}\n </Link>\n </nav>\n <LanguageSwitcher />\n </div>\n </footer>\n )\n}\n"],
5
+ "mappings": ";AAgBQ,SACE,KADF;AAfR,OAAO,UAAU;AACjB,SAAS,mBAAmB;AAC5B,SAAS,YAAY;AACrB,SAAS,wBAAwB;AAE1B,SAAS,aAAa;AAC3B,QAAM,WAAW,YAAY;AAC7B,QAAM,IAAI,KAAK;AACf,QAAM,aACJ,aAAa,YACZ,OAAO,aAAa,YAAY,SAAS,WAAW,aAAa;AACpE,MAAI,CAAC,WAAY,QAAO;AACxB,SACE,oBAAC,YAAO,WAAU,8FAChB,+BAAC,SAAI,WAAU,mFACb;AAAA,yBAAC,SAAI,WAAU,yDACb;AAAA,0BAAC,QAAK,MAAK,UAAS,WAAU,oCAC3B,YAAE,cAAc,GACnB;AAAA,MACA,oBAAC,QAAK,MAAK,YAAW,WAAU,oCAC7B,YAAE,gBAAgB,GACrB;AAAA,OACF;AAAA,IACA,oBAAC,oBAAiB;AAAA,KACpB,GACF;AAEJ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,57 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useId, useTransition } from "react";
4
+ import { useLocale, useT } from "@open-mercato/shared/lib/i18n/context";
5
+ import { useRouter } from "next/navigation";
6
+ import { locales } from "@open-mercato/shared/lib/i18n/config";
7
+ function LanguageSwitcher() {
8
+ const current = useLocale();
9
+ const t = useT();
10
+ const router = useRouter();
11
+ const [pending, startTransition] = useTransition();
12
+ const selectId = useId();
13
+ const languageLabels = {
14
+ en: t("common.languages.english", "English"),
15
+ pl: t("common.languages.polish", "Polski"),
16
+ es: t("common.languages.spanish", "Espa\xF1ol"),
17
+ de: t("common.languages.german", "Deutsch")
18
+ };
19
+ async function setLocale(locale) {
20
+ if (locale === current) return;
21
+ try {
22
+ const res = await fetch("/api/auth/locale", {
23
+ method: "POST",
24
+ headers: { "content-type": "application/json" },
25
+ body: JSON.stringify({ locale })
26
+ });
27
+ if (!res.ok) return;
28
+ startTransition(() => router.refresh());
29
+ try {
30
+ window.dispatchEvent(new Event("om:refresh-sidebar"));
31
+ } catch {
32
+ }
33
+ } catch {
34
+ }
35
+ }
36
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
37
+ /* @__PURE__ */ jsx("label", { htmlFor: selectId, children: t("common.language") }),
38
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
39
+ /* @__PURE__ */ jsx(
40
+ "select",
41
+ {
42
+ id: selectId,
43
+ className: "appearance-none rounded-md border bg-background px-3 py-1 pr-8 text-xs focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-1 disabled:opacity-60",
44
+ value: current,
45
+ onChange: (event) => setLocale(event.target.value),
46
+ disabled: pending,
47
+ children: locales.map((locale) => /* @__PURE__ */ jsx("option", { value: locale, children: languageLabels[locale] }, locale))
48
+ }
49
+ ),
50
+ /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute right-2 top-1/2 -translate-y-1/2 text-muted-foreground", children: /* @__PURE__ */ jsx("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx("path", { d: "M6 9l6 6 6-6" }) }) })
51
+ ] })
52
+ ] });
53
+ }
54
+ export {
55
+ LanguageSwitcher
56
+ };
57
+ //# sourceMappingURL=LanguageSwitcher.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/frontend/LanguageSwitcher.tsx"],
4
+ "sourcesContent": ["\"use client\"\nimport { useId, useTransition } from 'react'\nimport { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useRouter } from 'next/navigation'\nimport { locales, type Locale } from '@open-mercato/shared/lib/i18n/config'\n\nexport function LanguageSwitcher() {\n const current = useLocale()\n const t = useT()\n const router = useRouter()\n const [pending, startTransition] = useTransition()\n const selectId = useId()\n\n const languageLabels: Record<Locale, string> = {\n en: t('common.languages.english', 'English'),\n pl: t('common.languages.polish', 'Polski'),\n es: t('common.languages.spanish', 'Espa\u00F1ol'),\n de: t('common.languages.german', 'Deutsch'),\n }\n\n async function setLocale(locale: Locale) {\n if (locale === current) return\n try {\n const res = await fetch('/api/auth/locale', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ locale }),\n })\n if (!res.ok) return\n startTransition(() => router.refresh())\n try {\n window.dispatchEvent(new Event('om:refresh-sidebar'))\n } catch {\n // Ignore if window is unavailable\n }\n } catch {\n // Ignore network errors; UX fallback keeps previous locale\n }\n }\n\n return (\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <label htmlFor={selectId}>{t('common.language')}</label>\n <div className=\"relative\">\n <select\n id={selectId}\n className=\"appearance-none rounded-md border bg-background px-3 py-1 pr-8 text-xs focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-1 disabled:opacity-60\"\n value={current}\n onChange={(event) => setLocale(event.target.value as Locale)}\n disabled={pending}\n >\n {locales.map((locale) => (\n <option key={locale} value={locale}>\n {languageLabels[locale]}\n </option>\n ))}\n </select>\n <span className=\"pointer-events-none absolute right-2 top-1/2 -translate-y-1/2 text-muted-foreground\">\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M6 9l6 6 6-6\" />\n </svg>\n </span>\n </div>\n </div>\n )\n}\n"],
5
+ "mappings": ";AA0CM,cACA,YADA;AAzCN,SAAS,OAAO,qBAAqB;AACrC,SAAS,WAAW,YAAY;AAChC,SAAS,iBAAiB;AAC1B,SAAS,eAA4B;AAE9B,SAAS,mBAAmB;AACjC,QAAM,UAAU,UAAU;AAC1B,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,SAAS,eAAe,IAAI,cAAc;AACjD,QAAM,WAAW,MAAM;AAEvB,QAAM,iBAAyC;AAAA,IAC7C,IAAI,EAAE,4BAA4B,SAAS;AAAA,IAC3C,IAAI,EAAE,2BAA2B,QAAQ;AAAA,IACzC,IAAI,EAAE,4BAA4B,YAAS;AAAA,IAC3C,IAAI,EAAE,2BAA2B,SAAS;AAAA,EAC5C;AAEA,iBAAe,UAAU,QAAgB;AACvC,QAAI,WAAW,QAAS;AACxB,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,oBAAoB;AAAA,QAC1C,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,OAAO,CAAC;AAAA,MACjC,CAAC;AACD,UAAI,CAAC,IAAI,GAAI;AACb,sBAAgB,MAAM,OAAO,QAAQ,CAAC;AACtC,UAAI;AACF,eAAO,cAAc,IAAI,MAAM,oBAAoB,CAAC;AAAA,MACtD,QAAQ;AAAA,MAER;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,SACE,qBAAC,SAAI,WAAU,yDACb;AAAA,wBAAC,WAAM,SAAS,UAAW,YAAE,iBAAiB,GAAE;AAAA,IAChD,qBAAC,SAAI,WAAU,YACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,IAAI;AAAA,UACJ,WAAU;AAAA,UACV,OAAO;AAAA,UACP,UAAU,CAAC,UAAU,UAAU,MAAM,OAAO,KAAe;AAAA,UAC3D,UAAU;AAAA,UAET,kBAAQ,IAAI,CAAC,WACZ,oBAAC,YAAoB,OAAO,QACzB,yBAAe,MAAM,KADX,MAEb,CACD;AAAA;AAAA,MACH;AAAA,MACA,oBAAC,UAAK,WAAU,uFACd,8BAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAC5F,8BAAC,UAAK,GAAE,gBAAe,GACzB,GACF;AAAA,OACF;AAAA,KACF;AAEJ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,14 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { FlashMessages } from "../backend/FlashMessages.js";
3
+ function FrontendLayout({ header, footer, children }) {
4
+ return /* @__PURE__ */ jsxs("div", { className: "min-h-svh flex flex-col", children: [
5
+ /* @__PURE__ */ jsx(FlashMessages, {}),
6
+ header ? /* @__PURE__ */ jsx("div", { className: "border-b bg-background/60", children: header }) : null,
7
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0", children }),
8
+ footer ? /* @__PURE__ */ jsx("div", { className: "border-t bg-background/60", children: footer }) : null
9
+ ] });
10
+ }
11
+ export {
12
+ FrontendLayout
13
+ };
14
+ //# sourceMappingURL=Layout.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/frontend/Layout.tsx"],
4
+ "sourcesContent": ["import * as React from 'react'\nimport { FlashMessages } from '../backend/FlashMessages'\n\nexport function FrontendLayout({ header, footer, children }: { header?: React.ReactNode; footer?: React.ReactNode; children: React.ReactNode }) {\n return (\n <div className=\"min-h-svh flex flex-col\">\n <FlashMessages />\n {header ? <div className=\"border-b bg-background/60\">{header}</div> : null}\n <div className=\"flex-1 min-h-0\">{children}</div>\n {footer ? <div className=\"border-t bg-background/60\">{footer}</div> : null}\n </div>\n )\n}\n"],
5
+ "mappings": "AAKI,SACE,KADF;AAJJ,SAAS,qBAAqB;AAEvB,SAAS,eAAe,EAAE,QAAQ,QAAQ,SAAS,GAAsF;AAC9I,SACE,qBAAC,SAAI,WAAU,2BACb;AAAA,wBAAC,iBAAc;AAAA,IACd,SAAS,oBAAC,SAAI,WAAU,6BAA6B,kBAAO,IAAS;AAAA,IACtE,oBAAC,SAAI,WAAU,kBAAkB,UAAS;AAAA,IACzC,SAAS,oBAAC,SAAI,WAAU,6BAA6B,kBAAO,IAAS;AAAA,KACxE;AAEJ;",
6
+ "names": []
7
+ }
package/dist/index.js ADDED
@@ -0,0 +1,32 @@
1
+ export * from "./theme/ThemeProvider.js";
2
+ export * from "./theme/ThemeToggle.js";
3
+ export * from "./theme/QueryProvider.js";
4
+ export * from "./backend/AppShell.js";
5
+ export * from "./backend/Page.js";
6
+ export * from "./backend/DataTable.js";
7
+ export * from "./backend/FilterBar.js";
8
+ export * from "./backend/ValueIcons.js";
9
+ export * from "./backend/ConfirmDialog.js";
10
+ export * from "./backend/UserMenu.js";
11
+ export * from "./backend/RowActions.js";
12
+ export * from "./backend/utils/nav.js";
13
+ export * from "./backend/CrudForm.js";
14
+ export * from "./backend/JsonBuilder.js";
15
+ export * from "./backend/detail/index.js";
16
+ export * from "./backend/schedule/index.js";
17
+ export * from "./backend/inputs/index.js";
18
+ export * from "./backend/ContextHelp.js";
19
+ export * from "./backend/dashboard/index.js";
20
+ export * from "./frontend/Layout.js";
21
+ export * from "./frontend/AuthFooter.js";
22
+ export * from "./frontend/LanguageSwitcher.js";
23
+ export * from "./primitives/button.js";
24
+ export * from "./primitives/label.js";
25
+ export * from "./primitives/separator.js";
26
+ export * from "./primitives/spinner.js";
27
+ export * from "./primitives/tabs.js";
28
+ export * from "./primitives/DataLoader.js";
29
+ export * from "./primitives/table.js";
30
+ export * from "./primitives/ErrorNotice.js";
31
+ export * from "./primitives/dialog.js";
32
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts"],
4
+ "sourcesContent": ["export * from './theme/ThemeProvider'\nexport * from './theme/ThemeToggle'\nexport * from './theme/QueryProvider'\nexport * from './backend/AppShell'\nexport * from './backend/Page'\nexport * from './backend/DataTable'\nexport * from './backend/FilterBar'\nexport * from './backend/ValueIcons'\nexport * from './backend/ConfirmDialog'\nexport * from './backend/UserMenu'\nexport * from './backend/RowActions'\nexport * from './backend/utils/nav'\nexport * from './backend/CrudForm'\nexport * from './backend/JsonBuilder'\nexport * from './backend/detail'\nexport * from './backend/schedule'\n\nexport * from './backend/inputs'\nexport * from './backend/ContextHelp'\nexport * from './backend/dashboard'\nexport * from './frontend/Layout'\nexport * from './frontend/AuthFooter'\nexport * from './frontend/LanguageSwitcher'\nexport * from './primitives/button'\nexport * from './primitives/label'\nexport * from './primitives/separator'\nexport * from './primitives/spinner'\nexport * from './primitives/tabs'\nexport * from './primitives/DataLoader'\nexport * from './primitives/table'\nexport * from './primitives/ErrorNotice'\nexport * from './primitives/dialog'\n"],
5
+ "mappings": "AAAA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAEd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;",
6
+ "names": []
7
+ }
@@ -0,0 +1,67 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Spinner } from "./spinner.js";
3
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
4
+ function DataLoader({
5
+ isLoading,
6
+ children,
7
+ loadingMessage,
8
+ spinnerSize = "md",
9
+ className = "",
10
+ loadingClassName = "",
11
+ showSkeleton = false,
12
+ skeletonComponent
13
+ }) {
14
+ const t = useT();
15
+ const resolvedLoadingMessage = loadingMessage ?? t("ui.dataLoader.loading", "Loading...");
16
+ if (isLoading) {
17
+ if (showSkeleton && skeletonComponent) {
18
+ return /* @__PURE__ */ jsx("div", { className, children: skeletonComponent });
19
+ }
20
+ return /* @__PURE__ */ jsxs("div", { className: `flex items-center justify-center gap-2 py-4 ${loadingClassName} ${className}`, children: [
21
+ /* @__PURE__ */ jsx(Spinner, { size: spinnerSize }),
22
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: resolvedLoadingMessage })
23
+ ] });
24
+ }
25
+ return /* @__PURE__ */ jsx("div", { className, children });
26
+ }
27
+ function InlineLoader({
28
+ isLoading,
29
+ children,
30
+ loadingMessage,
31
+ spinnerSize = "sm"
32
+ }) {
33
+ return /* @__PURE__ */ jsx(
34
+ DataLoader,
35
+ {
36
+ isLoading,
37
+ loadingMessage,
38
+ spinnerSize,
39
+ className: "inline-flex items-center",
40
+ loadingClassName: "py-2",
41
+ children
42
+ }
43
+ );
44
+ }
45
+ function PageLoader({
46
+ isLoading,
47
+ children,
48
+ loadingMessage,
49
+ spinnerSize = "lg"
50
+ }) {
51
+ return /* @__PURE__ */ jsx(
52
+ DataLoader,
53
+ {
54
+ isLoading,
55
+ loadingMessage,
56
+ spinnerSize,
57
+ className: "min-h-[200px] flex items-center justify-center",
58
+ children
59
+ }
60
+ );
61
+ }
62
+ export {
63
+ DataLoader,
64
+ InlineLoader,
65
+ PageLoader
66
+ };
67
+ //# sourceMappingURL=DataLoader.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/primitives/DataLoader.tsx"],
4
+ "sourcesContent": ["import * as React from 'react'\nimport { Spinner } from './spinner'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\nexport interface DataLoaderProps {\n isLoading: boolean\n children: React.ReactNode\n loadingMessage?: string\n spinnerSize?: 'sm' | 'md' | 'lg'\n className?: string\n loadingClassName?: string\n // Optional: show a skeleton or placeholder instead of just spinner\n showSkeleton?: boolean\n skeletonComponent?: React.ReactNode\n}\n\nexport function DataLoader({\n isLoading,\n children,\n loadingMessage,\n spinnerSize = 'md',\n className = '',\n loadingClassName = '',\n showSkeleton = false,\n skeletonComponent\n}: DataLoaderProps) {\n const t = useT()\n const resolvedLoadingMessage = loadingMessage ?? t('ui.dataLoader.loading', 'Loading...')\n if (isLoading) {\n if (showSkeleton && skeletonComponent) {\n return <div className={className}>{skeletonComponent}</div>\n }\n\n return (\n <div className={`flex items-center justify-center gap-2 py-4 ${loadingClassName} ${className}`}>\n <Spinner size={spinnerSize} />\n <span className=\"text-sm text-muted-foreground\">{resolvedLoadingMessage}</span>\n </div>\n )\n }\n\n return <div className={className}>{children}</div>\n}\n\n// Convenience component for inline loading states\nexport function InlineLoader({\n isLoading,\n children,\n loadingMessage,\n spinnerSize = 'sm'\n}: {\n isLoading: boolean\n children: React.ReactNode\n loadingMessage?: string\n spinnerSize?: 'sm' | 'md' | 'lg'\n}) {\n return (\n <DataLoader\n isLoading={isLoading}\n loadingMessage={loadingMessage}\n spinnerSize={spinnerSize}\n className=\"inline-flex items-center\"\n loadingClassName=\"py-2\"\n >\n {children}\n </DataLoader>\n )\n}\n\n// Convenience component for full-page loading states\nexport function PageLoader({\n isLoading,\n children,\n loadingMessage,\n spinnerSize = 'lg'\n}: {\n isLoading: boolean\n children: React.ReactNode\n loadingMessage?: string\n spinnerSize?: 'sm' | 'md' | 'lg'\n}) {\n return (\n <DataLoader\n isLoading={isLoading}\n loadingMessage={loadingMessage}\n spinnerSize={spinnerSize}\n className=\"min-h-[200px] flex items-center justify-center\"\n >\n {children}\n </DataLoader>\n )\n}\n"],
5
+ "mappings": "AA8Ba,cAIP,YAJO;AA7Bb,SAAS,eAAe;AACxB,SAAS,YAAY;AAcd,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,mBAAmB;AAAA,EACnB,eAAe;AAAA,EACf;AACF,GAAoB;AAClB,QAAM,IAAI,KAAK;AACf,QAAM,yBAAyB,kBAAkB,EAAE,yBAAyB,YAAY;AACxF,MAAI,WAAW;AACb,QAAI,gBAAgB,mBAAmB;AACrC,aAAO,oBAAC,SAAI,WAAuB,6BAAkB;AAAA,IACvD;AAEA,WACE,qBAAC,SAAI,WAAW,+CAA+C,gBAAgB,IAAI,SAAS,IAC1F;AAAA,0BAAC,WAAQ,MAAM,aAAa;AAAA,MAC5B,oBAAC,UAAK,WAAU,iCAAiC,kCAAuB;AAAA,OAC1E;AAAA,EAEJ;AAEA,SAAO,oBAAC,SAAI,WAAuB,UAAS;AAC9C;AAGO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAChB,GAKG;AACD,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAU;AAAA,MACV,kBAAiB;AAAA,MAEhB;AAAA;AAAA,EACH;AAEJ;AAGO,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAChB,GAKG;AACD,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAU;AAAA,MAET;AAAA;AAAA,EACH;AAEJ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,20 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
4
+ function ErrorNotice({ title, message, action }) {
5
+ const t = useT();
6
+ const defaultTitle = title ?? t("ui.errors.defaultTitle", "Something went wrong");
7
+ const defaultMessage = message ?? t("ui.errors.defaultMessage", "Unable to load data. Please try again.");
8
+ return /* @__PURE__ */ jsx("div", { className: "rounded-md border border-red-200 bg-red-50 p-4 text-red-800", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
9
+ /* @__PURE__ */ jsx("span", { className: "inline-block mt-0.5 h-4 w-4 rounded-full border-2 border-red-500", "aria-hidden": true }),
10
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
11
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: defaultTitle }),
12
+ /* @__PURE__ */ jsx("div", { className: "text-sm opacity-90", children: defaultMessage }),
13
+ action ? /* @__PURE__ */ jsx("div", { className: "mt-2", children: action }) : null
14
+ ] })
15
+ ] }) });
16
+ }
17
+ export {
18
+ ErrorNotice
19
+ };
20
+ //# sourceMappingURL=ErrorNotice.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/primitives/ErrorNotice.tsx"],
4
+ "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\nexport function ErrorNotice({ title, message, action }: {\n title?: string\n message?: string\n action?: React.ReactNode\n}) {\n const t = useT()\n const defaultTitle = title ?? t('ui.errors.defaultTitle', 'Something went wrong')\n const defaultMessage = message ?? t('ui.errors.defaultMessage', 'Unable to load data. Please try again.')\n return (\n <div className=\"rounded-md border border-red-200 bg-red-50 p-4 text-red-800\">\n <div className=\"flex items-start gap-3\">\n <span className=\"inline-block mt-0.5 h-4 w-4 rounded-full border-2 border-red-500\" aria-hidden />\n <div className=\"space-y-1\">\n <div className=\"text-sm font-medium\">{defaultTitle}</div>\n <div className=\"text-sm opacity-90\">{defaultMessage}</div>\n {action ? <div className=\"mt-2\">{action}</div> : null}\n </div>\n </div>\n </div>\n )\n}\n\n"],
5
+ "mappings": ";AAeQ,cACA,YADA;AAbR,SAAS,YAAY;AAEd,SAAS,YAAY,EAAE,OAAO,SAAS,OAAO,GAIlD;AACD,QAAM,IAAI,KAAK;AACf,QAAM,eAAe,SAAS,EAAE,0BAA0B,sBAAsB;AAChF,QAAM,iBAAiB,WAAW,EAAE,4BAA4B,wCAAwC;AACxG,SACE,oBAAC,SAAI,WAAU,+DACb,+BAAC,SAAI,WAAU,0BACb;AAAA,wBAAC,UAAK,WAAU,oEAAmE,eAAW,MAAC;AAAA,IAC/F,qBAAC,SAAI,WAAU,aACb;AAAA,0BAAC,SAAI,WAAU,uBAAuB,wBAAa;AAAA,MACnD,oBAAC,SAAI,WAAU,sBAAsB,0BAAe;AAAA,MACnD,SAAS,oBAAC,SAAI,WAAU,QAAQ,kBAAO,IAAS;AAAA,OACnD;AAAA,KACF,GACF;AAEJ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,38 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cva } from "class-variance-authority";
4
+ import { cn } from "@open-mercato/shared/lib/utils";
5
+ const alertVariants = cva(
6
+ "relative w-full rounded-lg border px-4 py-3 text-sm [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg~*]:pl-8 [&>svg]:text-current",
7
+ {
8
+ variants: {
9
+ variant: {
10
+ default: "border-border bg-background text-foreground",
11
+ destructive: "border-destructive/60 bg-destructive/10 text-destructive dark:border-destructive/40 dark:bg-destructive/20 dark:text-destructive-foreground",
12
+ success: "border-emerald-600/30 bg-emerald-500/10 text-emerald-900 dark:border-emerald-500/50 dark:bg-emerald-500/15 dark:text-emerald-50",
13
+ warning: "border-amber-500/30 bg-amber-400/10 text-amber-950 dark:border-amber-400/40 dark:bg-amber-400/20 dark:text-amber-50",
14
+ info: "border-sky-600/30 bg-sky-500/10 text-sky-900 dark:border-sky-500/40 dark:bg-sky-500/20 dark:text-sky-50"
15
+ }
16
+ },
17
+ defaultVariants: {
18
+ variant: "default"
19
+ }
20
+ }
21
+ );
22
+ const Alert = React.forwardRef(({ className, variant, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, role: "alert", className: cn(alertVariants({ variant }), className), ...props }));
23
+ Alert.displayName = "Alert";
24
+ const AlertTitle = React.forwardRef(
25
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx("h5", { ref, className: cn("mb-1 text-sm font-semibold leading-tight", className), ...props })
26
+ );
27
+ AlertTitle.displayName = "AlertTitle";
28
+ const AlertDescription = React.forwardRef(
29
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx("p", { ref, className: cn("text-sm leading-relaxed", className), ...props })
30
+ );
31
+ AlertDescription.displayName = "AlertDescription";
32
+ export {
33
+ Alert,
34
+ AlertDescription,
35
+ AlertTitle,
36
+ alertVariants
37
+ };
38
+ //# sourceMappingURL=alert.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/primitives/alert.tsx"],
4
+ "sourcesContent": ["import * as React from 'react'\nimport { cva, type VariantProps } from 'class-variance-authority'\n\nimport { cn } from '@open-mercato/shared/lib/utils'\n\nconst alertVariants = cva(\n \"relative w-full rounded-lg border px-4 py-3 text-sm [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg~*]:pl-8 [&>svg]:text-current\",\n {\n variants: {\n variant: {\n default: 'border-border bg-background text-foreground',\n destructive:\n 'border-destructive/60 bg-destructive/10 text-destructive dark:border-destructive/40 dark:bg-destructive/20 dark:text-destructive-foreground',\n success:\n 'border-emerald-600/30 bg-emerald-500/10 text-emerald-900 dark:border-emerald-500/50 dark:bg-emerald-500/15 dark:text-emerald-50',\n warning:\n 'border-amber-500/30 bg-amber-400/10 text-amber-950 dark:border-amber-400/40 dark:bg-amber-400/20 dark:text-amber-50',\n info:\n 'border-sky-600/30 bg-sky-500/10 text-sky-900 dark:border-sky-500/40 dark:bg-sky-500/20 dark:text-sky-50',\n },\n },\n defaultVariants: {\n variant: 'default',\n },\n }\n)\n\ntype AlertProps = React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>\n\nconst Alert = React.forwardRef<HTMLDivElement, AlertProps>(({ className, variant, ...props }, ref) => (\n <div ref={ref} role=\"alert\" className={cn(alertVariants({ variant }), className)} {...props} />\n))\n\nAlert.displayName = 'Alert'\n\nconst AlertTitle = React.forwardRef<HTMLHeadingElement, React.HTMLAttributes<HTMLHeadingElement>>(\n ({ className, ...props }, ref) => (\n <h5 ref={ref} className={cn('mb-1 text-sm font-semibold leading-tight', className)} {...props} />\n )\n)\n\nAlertTitle.displayName = 'AlertTitle'\n\nconst AlertDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(\n ({ className, ...props }, ref) => (\n <p ref={ref} className={cn('text-sm leading-relaxed', className)} {...props} />\n )\n)\n\nAlertDescription.displayName = 'AlertDescription'\n\nexport { Alert, AlertDescription, AlertTitle, alertVariants }\n"],
5
+ "mappings": "AA8BE;AA9BF,YAAY,WAAW;AACvB,SAAS,WAA8B;AAEvC,SAAS,UAAU;AAEnB,MAAM,gBAAgB;AAAA,EACpB;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,SAAS;AAAA,QACT,aACE;AAAA,QACF,SACE;AAAA,QACF,SACE;AAAA,QACF,MACE;AAAA,MACJ;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,SAAS;AAAA,IACX;AAAA,EACF;AACF;AAIA,MAAM,QAAQ,MAAM,WAAuC,CAAC,EAAE,WAAW,SAAS,GAAG,MAAM,GAAG,QAC5F,oBAAC,SAAI,KAAU,MAAK,SAAQ,WAAW,GAAG,cAAc,EAAE,QAAQ,CAAC,GAAG,SAAS,GAAI,GAAG,OAAO,CAC9F;AAED,MAAM,cAAc;AAEpB,MAAM,aAAa,MAAM;AAAA,EACvB,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QACxB,oBAAC,QAAG,KAAU,WAAW,GAAG,4CAA4C,SAAS,GAAI,GAAG,OAAO;AAEnG;AAEA,WAAW,cAAc;AAEzB,MAAM,mBAAmB,MAAM;AAAA,EAC7B,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QACxB,oBAAC,OAAE,KAAU,WAAW,GAAG,2BAA2B,SAAS,GAAI,GAAG,OAAO;AAEjF;AAEA,iBAAiB,cAAc;",
6
+ "names": []
7
+ }