@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,162 @@
1
+ import {
2
+ filterCustomFieldDefs,
3
+ fetchCustomFieldDefinitionsPayload
4
+ } from "./customFieldDefs.js";
5
+ import { FieldRegistry, loadGeneratedFieldRegistrations } from "../fields/registry.js";
6
+ import { apiCall } from "./apiCall.js";
7
+ import { normalizeCustomFieldOptions } from "@open-mercato/shared/modules/entities/options";
8
+ import { CURRENCY_OPTIONS_URL } from "@open-mercato/shared/modules/entities/kinds";
9
+ let registryReady = null;
10
+ async function ensureFieldRegistryReady() {
11
+ if (!registryReady) {
12
+ registryReady = loadGeneratedFieldRegistrations().catch((err) => {
13
+ registryReady = null;
14
+ throw err;
15
+ });
16
+ }
17
+ await registryReady;
18
+ }
19
+ function buildOptionsUrl(base, query) {
20
+ if (!query) return base;
21
+ try {
22
+ const isAbsolute = /^([a-z][a-z\d+\-.]*:)?\/\//i.test(base);
23
+ const origin = typeof window !== "undefined" ? window.location.origin : "http://localhost";
24
+ const url = isAbsolute ? new URL(base) : new URL(base, origin);
25
+ if (!url.searchParams.has("query")) url.searchParams.append("query", query);
26
+ if (!url.searchParams.has("q")) url.searchParams.append("q", query);
27
+ if (isAbsolute) return url.toString();
28
+ return `${url.pathname}${url.search}`;
29
+ } catch {
30
+ const sep = base.includes("?") ? "&" : "?";
31
+ if (base.includes("query=")) return `${base}${sep}q=${encodeURIComponent(query)}`;
32
+ return `${base}${sep}query=${encodeURIComponent(query)}`;
33
+ }
34
+ }
35
+ async function loadRemoteOptions(url) {
36
+ try {
37
+ const call = await apiCall(url, void 0, { fallback: { items: [] } });
38
+ if (!call.ok) return [];
39
+ const payload = call.result ?? { items: [] };
40
+ const items = Array.isArray(payload?.items) ? payload.items : [];
41
+ return items.map((it) => ({
42
+ value: String(it?.value ?? it),
43
+ label: String(it?.label ?? it?.value ?? it)
44
+ }));
45
+ } catch {
46
+ return [];
47
+ }
48
+ }
49
+ function buildFormFieldFromCustomFieldDef(def, opts) {
50
+ const id = opts?.bareIds ? def.key : `cf_${def.key}`;
51
+ const label = def.label || def.key;
52
+ const required = Array.isArray(def.validation) ? def.validation.some((rule) => rule && rule.rule === "required") : false;
53
+ switch (def.kind) {
54
+ case "boolean":
55
+ return { id, label, type: "checkbox", description: def.description, required };
56
+ case "integer":
57
+ case "float":
58
+ return { id, label, type: "number", description: def.description, required };
59
+ case "multiline": {
60
+ let editor = "uiw";
61
+ if (def.editor === "simpleMarkdown") editor = "simple";
62
+ else if (def.editor === "htmlRichText") editor = "html";
63
+ return { id, label, type: "richtext", description: def.description, editor, required };
64
+ }
65
+ case "select":
66
+ case "currency":
67
+ case "relation":
68
+ return {
69
+ id,
70
+ label,
71
+ type: "select",
72
+ description: def.description,
73
+ options: normalizeCustomFieldOptions(def.options || []).map((option) => ({
74
+ value: option.value,
75
+ label: option.label
76
+ })),
77
+ multiple: !!def.multi,
78
+ required,
79
+ ...def.kind === "currency" ? {
80
+ loadOptions: async (query) => {
81
+ const url = buildOptionsUrl(CURRENCY_OPTIONS_URL, query);
82
+ return loadRemoteOptions(url);
83
+ }
84
+ } : def.optionsUrl ? {
85
+ loadOptions: async (query) => {
86
+ const url = buildOptionsUrl(def.optionsUrl, query);
87
+ return loadRemoteOptions(url);
88
+ }
89
+ } : {},
90
+ ...def.multi && def.input === "listbox" ? { listbox: true } : {}
91
+ };
92
+ default: {
93
+ if (def.kind === "text" && def.multi) {
94
+ const base = { id, label, type: "tags", description: def.description, required };
95
+ const resolvedOptions = normalizeCustomFieldOptions(def.options || []);
96
+ if (resolvedOptions.length > 0) {
97
+ base.options = resolvedOptions.map((option) => ({ value: option.value, label: option.label }));
98
+ }
99
+ if (def.optionsUrl) {
100
+ base.loadOptions = async (query) => {
101
+ const url = buildOptionsUrl(def.optionsUrl, query);
102
+ return loadRemoteOptions(url);
103
+ };
104
+ }
105
+ return base;
106
+ }
107
+ if (def.kind === "text" && typeof def.editor === "string" && def.editor) {
108
+ let editor = "uiw";
109
+ if (def.editor === "simpleMarkdown") editor = "simple";
110
+ else if (def.editor === "htmlRichText") editor = "html";
111
+ return { id, label, type: "richtext", description: def.description, editor, required };
112
+ }
113
+ const input = FieldRegistry.getInput(def.kind);
114
+ if (input) {
115
+ return {
116
+ id,
117
+ label,
118
+ type: "custom",
119
+ required,
120
+ description: def.description,
121
+ component: (props) => input({ ...props, def })
122
+ };
123
+ }
124
+ return { id, label, type: "text", description: def.description, required };
125
+ }
126
+ }
127
+ }
128
+ function buildFormFieldsFromCustomFields(defs, opts) {
129
+ const fields = [];
130
+ const visible = filterCustomFieldDefs(defs, "form");
131
+ const seenKeys = /* @__PURE__ */ new Set();
132
+ for (const def of visible) {
133
+ const keyLower = String(def.key).toLowerCase();
134
+ if (seenKeys.has(keyLower)) continue;
135
+ seenKeys.add(keyLower);
136
+ const field = buildFormFieldFromCustomFieldDef(def, opts);
137
+ if (field) fields.push(field);
138
+ }
139
+ return fields;
140
+ }
141
+ async function fetchCustomFieldFormStructure(entityIds, fetchImpl, options) {
142
+ await ensureFieldRegistryReady();
143
+ const metadata = await fetchCustomFieldDefinitionsPayload(entityIds, fetchImpl);
144
+ const definitions = Array.isArray(metadata.items) ? metadata.items : [];
145
+ const fields = buildFormFieldsFromCustomFields(definitions, options);
146
+ return { fields, definitions, metadata };
147
+ }
148
+ async function fetchCustomFieldFormFields(entityIds, fetchImpl, options) {
149
+ const { fields } = await fetchCustomFieldFormStructure(entityIds, fetchImpl, options);
150
+ return fields;
151
+ }
152
+ async function fetchCustomFieldFormFieldsWithDefinitions(entityIds, fetchImpl, options) {
153
+ return fetchCustomFieldFormStructure(entityIds, fetchImpl, options);
154
+ }
155
+ export {
156
+ buildFormFieldFromCustomFieldDef,
157
+ buildFormFieldsFromCustomFields,
158
+ fetchCustomFieldFormFields,
159
+ fetchCustomFieldFormFieldsWithDefinitions,
160
+ fetchCustomFieldFormStructure
161
+ };
162
+ //# sourceMappingURL=customFieldForms.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/backend/utils/customFieldForms.ts"],
4
+ "sourcesContent": ["import type { CrudField } from '../CrudForm'\nimport type {\n CustomFieldDefDto,\n CustomFieldDefinitionsPayload,\n} from './customFieldDefs'\nimport {\n filterCustomFieldDefs,\n fetchCustomFieldDefs,\n fetchCustomFieldDefinitionsPayload,\n} from './customFieldDefs'\nimport { FieldRegistry, loadGeneratedFieldRegistrations } from '../fields/registry'\nimport { apiCall } from './apiCall'\nimport { normalizeCustomFieldOptions } from '@open-mercato/shared/modules/entities/options'\nimport { CURRENCY_OPTIONS_URL } from '@open-mercato/shared/modules/entities/kinds'\n\nlet registryReady: Promise<void> | null = null\n\nasync function ensureFieldRegistryReady() {\n if (!registryReady) {\n registryReady = loadGeneratedFieldRegistrations().catch((err) => {\n registryReady = null\n throw err\n })\n }\n await registryReady\n}\n\nfunction buildOptionsUrl(base: string, query?: string): string {\n if (!query) return base\n try {\n const isAbsolute = /^([a-z][a-z\\d+\\-.]*:)?\\/\\//i.test(base)\n const origin = typeof window !== 'undefined' ? window.location.origin : 'http://localhost'\n const url = isAbsolute ? new URL(base) : new URL(base, origin)\n if (!url.searchParams.has('query')) url.searchParams.append('query', query)\n if (!url.searchParams.has('q')) url.searchParams.append('q', query)\n if (isAbsolute) return url.toString()\n return `${url.pathname}${url.search}`\n } catch {\n const sep = base.includes('?') ? '&' : '?'\n if (base.includes('query=')) return `${base}${sep}q=${encodeURIComponent(query)}`\n return `${base}${sep}query=${encodeURIComponent(query)}`\n }\n}\n\ntype OptionsResponse = { items?: unknown[] }\n\nasync function loadRemoteOptions(url: string): Promise<Array<{ value: string; label: string }>> {\n try {\n const call = await apiCall<OptionsResponse>(url, undefined, { fallback: { items: [] } })\n if (!call.ok) return []\n const payload = call.result ?? { items: [] }\n const items = Array.isArray(payload?.items) ? payload.items : []\n return items.map((it: any) => ({\n value: String(it?.value ?? it),\n label: String(it?.label ?? it?.value ?? it),\n }))\n } catch {\n return []\n }\n}\n\nexport function buildFormFieldFromCustomFieldDef(\n def: CustomFieldDefDto,\n opts?: { bareIds?: boolean }\n): CrudField | null {\n const id = opts?.bareIds ? def.key : `cf_${def.key}`\n const label = def.label || def.key\n const required = Array.isArray((def as any).validation)\n ? ((def as any).validation as any[]).some((rule) => rule && rule.rule === 'required')\n : false\n\n switch (def.kind) {\n case 'boolean':\n return { id, label, type: 'checkbox', description: def.description, required }\n case 'integer':\n case 'float':\n return { id, label, type: 'number', description: def.description, required }\n case 'multiline': {\n let editor: 'simple' | 'uiw' | 'html' = 'uiw'\n if (def.editor === 'simpleMarkdown') editor = 'simple'\n else if (def.editor === 'htmlRichText') editor = 'html'\n return { id, label, type: 'richtext', description: def.description, editor, required }\n }\n case 'select':\n case 'currency':\n case 'relation':\n return {\n id,\n label,\n type: 'select',\n description: def.description,\n options: normalizeCustomFieldOptions(def.options || []).map((option) => ({\n value: option.value,\n label: option.label,\n })),\n multiple: !!def.multi,\n required,\n ...(def.kind === 'currency'\n ? {\n loadOptions: async (query?: string) => {\n const url = buildOptionsUrl(CURRENCY_OPTIONS_URL, query)\n return loadRemoteOptions(url)\n },\n }\n : def.optionsUrl\n ? {\n loadOptions: async (query?: string) => {\n const url = buildOptionsUrl(def.optionsUrl!, query)\n return loadRemoteOptions(url)\n },\n }\n : {}),\n ...(def.multi && def.input === 'listbox' ? ({ listbox: true } as any) : {}),\n }\n default: {\n if (def.kind === 'text' && def.multi) {\n const base: any = { id, label, type: 'tags', description: def.description, required }\n const resolvedOptions = normalizeCustomFieldOptions(def.options || [])\n if (resolvedOptions.length > 0) {\n base.options = resolvedOptions.map((option) => ({ value: option.value, label: option.label }))\n }\n if (def.optionsUrl) {\n base.loadOptions = async (query?: string) => {\n const url = buildOptionsUrl(def.optionsUrl!, query)\n return loadRemoteOptions(url)\n }\n }\n return base\n }\n if (def.kind === 'text' && typeof def.editor === 'string' && def.editor) {\n let editor: 'simple' | 'uiw' | 'html' = 'uiw'\n if (def.editor === 'simpleMarkdown') editor = 'simple'\n else if (def.editor === 'htmlRichText') editor = 'html'\n return { id, label, type: 'richtext', description: def.description, editor, required }\n }\n const input = FieldRegistry.getInput(def.kind)\n if (input) {\n return {\n id,\n label,\n type: 'custom',\n required,\n description: def.description,\n component: (props) => input({ ...props, def }),\n }\n }\n return { id, label, type: 'text', description: def.description, required }\n }\n }\n}\n\nexport function buildFormFieldsFromCustomFields(\n defs: CustomFieldDefDto[],\n opts?: { bareIds?: boolean }\n): CrudField[] {\n const fields: CrudField[] = []\n const visible = filterCustomFieldDefs(defs, 'form')\n const seenKeys = new Set<string>()\n for (const def of visible) {\n const keyLower = String(def.key).toLowerCase()\n if (seenKeys.has(keyLower)) continue\n seenKeys.add(keyLower)\n const field = buildFormFieldFromCustomFieldDef(def, opts)\n if (field) fields.push(field)\n }\n return fields\n}\n\nexport async function fetchCustomFieldFormStructure(\n entityIds: string | string[],\n fetchImpl?: typeof fetch,\n options?: { bareIds?: boolean },\n): Promise<{ fields: CrudField[]; definitions: CustomFieldDefDto[]; metadata: CustomFieldDefinitionsPayload }> {\n await ensureFieldRegistryReady()\n const metadata = await fetchCustomFieldDefinitionsPayload(entityIds, fetchImpl)\n const definitions = Array.isArray(metadata.items) ? metadata.items : []\n const fields = buildFormFieldsFromCustomFields(definitions, options)\n return { fields, definitions, metadata }\n}\n\nexport async function fetchCustomFieldFormFields(\n entityIds: string | string[],\n fetchImpl?: typeof fetch,\n options?: { bareIds?: boolean },\n): Promise<CrudField[]> {\n const { fields } = await fetchCustomFieldFormStructure(entityIds, fetchImpl, options)\n return fields\n}\n\nexport async function fetchCustomFieldFormFieldsWithDefinitions(\n entityIds: string | string[],\n fetchImpl?: typeof fetch,\n options?: { bareIds?: boolean },\n): Promise<{ fields: CrudField[]; definitions: CustomFieldDefDto[]; metadata: CustomFieldDefinitionsPayload }> {\n return fetchCustomFieldFormStructure(entityIds, fetchImpl, options)\n}\n"],
5
+ "mappings": "AAKA;AAAA,EACE;AAAA,EAEA;AAAA,OACK;AACP,SAAS,eAAe,uCAAuC;AAC/D,SAAS,eAAe;AACxB,SAAS,mCAAmC;AAC5C,SAAS,4BAA4B;AAErC,IAAI,gBAAsC;AAE1C,eAAe,2BAA2B;AACxC,MAAI,CAAC,eAAe;AAClB,oBAAgB,gCAAgC,EAAE,MAAM,CAAC,QAAQ;AAC/D,sBAAgB;AAChB,YAAM;AAAA,IACR,CAAC;AAAA,EACH;AACA,QAAM;AACR;AAEA,SAAS,gBAAgB,MAAc,OAAwB;AAC7D,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI;AACF,UAAM,aAAa,8BAA8B,KAAK,IAAI;AAC1D,UAAM,SAAS,OAAO,WAAW,cAAc,OAAO,SAAS,SAAS;AACxE,UAAM,MAAM,aAAa,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,MAAM,MAAM;AAC7D,QAAI,CAAC,IAAI,aAAa,IAAI,OAAO,EAAG,KAAI,aAAa,OAAO,SAAS,KAAK;AAC1E,QAAI,CAAC,IAAI,aAAa,IAAI,GAAG,EAAG,KAAI,aAAa,OAAO,KAAK,KAAK;AAClE,QAAI,WAAY,QAAO,IAAI,SAAS;AACpC,WAAO,GAAG,IAAI,QAAQ,GAAG,IAAI,MAAM;AAAA,EACrC,QAAQ;AACN,UAAM,MAAM,KAAK,SAAS,GAAG,IAAI,MAAM;AACvC,QAAI,KAAK,SAAS,QAAQ,EAAG,QAAO,GAAG,IAAI,GAAG,GAAG,KAAK,mBAAmB,KAAK,CAAC;AAC/E,WAAO,GAAG,IAAI,GAAG,GAAG,SAAS,mBAAmB,KAAK,CAAC;AAAA,EACxD;AACF;AAIA,eAAe,kBAAkB,KAA+D;AAC9F,MAAI;AACF,UAAM,OAAO,MAAM,QAAyB,KAAK,QAAW,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC;AACvF,QAAI,CAAC,KAAK,GAAI,QAAO,CAAC;AACtB,UAAM,UAAU,KAAK,UAAU,EAAE,OAAO,CAAC,EAAE;AAC3C,UAAM,QAAQ,MAAM,QAAQ,SAAS,KAAK,IAAI,QAAQ,QAAQ,CAAC;AAC/D,WAAO,MAAM,IAAI,CAAC,QAAa;AAAA,MAC7B,OAAO,OAAO,IAAI,SAAS,EAAE;AAAA,MAC7B,OAAO,OAAO,IAAI,SAAS,IAAI,SAAS,EAAE;AAAA,IAC5C,EAAE;AAAA,EACJ,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEO,SAAS,iCACd,KACA,MACkB;AAClB,QAAM,KAAK,MAAM,UAAU,IAAI,MAAM,MAAM,IAAI,GAAG;AAClD,QAAM,QAAQ,IAAI,SAAS,IAAI;AAC/B,QAAM,WAAW,MAAM,QAAS,IAAY,UAAU,IAChD,IAAY,WAAqB,KAAK,CAAC,SAAS,QAAQ,KAAK,SAAS,UAAU,IAClF;AAEJ,UAAQ,IAAI,MAAM;AAAA,IAChB,KAAK;AACH,aAAO,EAAE,IAAI,OAAO,MAAM,YAAY,aAAa,IAAI,aAAa,SAAS;AAAA,IAC/E,KAAK;AAAA,IACL,KAAK;AACH,aAAO,EAAE,IAAI,OAAO,MAAM,UAAU,aAAa,IAAI,aAAa,SAAS;AAAA,IAC7E,KAAK,aAAa;AAChB,UAAI,SAAoC;AACxC,UAAI,IAAI,WAAW,iBAAkB,UAAS;AAAA,eACrC,IAAI,WAAW,eAAgB,UAAS;AACjD,aAAO,EAAE,IAAI,OAAO,MAAM,YAAY,aAAa,IAAI,aAAa,QAAQ,SAAS;AAAA,IACvF;AAAA,IACA,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA,MAAM;AAAA,QACN,aAAa,IAAI;AAAA,QACjB,SAAS,4BAA4B,IAAI,WAAW,CAAC,CAAC,EAAE,IAAI,CAAC,YAAY;AAAA,UACvE,OAAO,OAAO;AAAA,UACd,OAAO,OAAO;AAAA,QAChB,EAAE;AAAA,QACF,UAAU,CAAC,CAAC,IAAI;AAAA,QAChB;AAAA,QACA,GAAI,IAAI,SAAS,aACb;AAAA,UACE,aAAa,OAAO,UAAmB;AACrC,kBAAM,MAAM,gBAAgB,sBAAsB,KAAK;AACvD,mBAAO,kBAAkB,GAAG;AAAA,UAC9B;AAAA,QACF,IACA,IAAI,aACJ;AAAA,UACE,aAAa,OAAO,UAAmB;AACrC,kBAAM,MAAM,gBAAgB,IAAI,YAAa,KAAK;AAClD,mBAAO,kBAAkB,GAAG;AAAA,UAC9B;AAAA,QACF,IACA,CAAC;AAAA,QACL,GAAI,IAAI,SAAS,IAAI,UAAU,YAAa,EAAE,SAAS,KAAK,IAAY,CAAC;AAAA,MAC3E;AAAA,IACF,SAAS;AACP,UAAI,IAAI,SAAS,UAAU,IAAI,OAAO;AACpC,cAAM,OAAY,EAAE,IAAI,OAAO,MAAM,QAAQ,aAAa,IAAI,aAAa,SAAS;AACpF,cAAM,kBAAkB,4BAA4B,IAAI,WAAW,CAAC,CAAC;AACrE,YAAI,gBAAgB,SAAS,GAAG;AAC9B,eAAK,UAAU,gBAAgB,IAAI,CAAC,YAAY,EAAE,OAAO,OAAO,OAAO,OAAO,OAAO,MAAM,EAAE;AAAA,QAC/F;AACA,YAAI,IAAI,YAAY;AAClB,eAAK,cAAc,OAAO,UAAmB;AAC3C,kBAAM,MAAM,gBAAgB,IAAI,YAAa,KAAK;AAClD,mBAAO,kBAAkB,GAAG;AAAA,UAC9B;AAAA,QACF;AACA,eAAO;AAAA,MACT;AACA,UAAI,IAAI,SAAS,UAAU,OAAO,IAAI,WAAW,YAAY,IAAI,QAAQ;AACvE,YAAI,SAAoC;AACxC,YAAI,IAAI,WAAW,iBAAkB,UAAS;AAAA,iBACrC,IAAI,WAAW,eAAgB,UAAS;AACjD,eAAO,EAAE,IAAI,OAAO,MAAM,YAAY,aAAa,IAAI,aAAa,QAAQ,SAAS;AAAA,MACvF;AACA,YAAM,QAAQ,cAAc,SAAS,IAAI,IAAI;AAC7C,UAAI,OAAO;AACT,eAAO;AAAA,UACL;AAAA,UACA;AAAA,UACA,MAAM;AAAA,UACN;AAAA,UACA,aAAa,IAAI;AAAA,UACjB,WAAW,CAAC,UAAU,MAAM,EAAE,GAAG,OAAO,IAAI,CAAC;AAAA,QAC/C;AAAA,MACF;AACA,aAAO,EAAE,IAAI,OAAO,MAAM,QAAQ,aAAa,IAAI,aAAa,SAAS;AAAA,IAC3E;AAAA,EACF;AACF;AAEO,SAAS,gCACd,MACA,MACa;AACb,QAAM,SAAsB,CAAC;AAC7B,QAAM,UAAU,sBAAsB,MAAM,MAAM;AAClD,QAAM,WAAW,oBAAI,IAAY;AACjC,aAAW,OAAO,SAAS;AACzB,UAAM,WAAW,OAAO,IAAI,GAAG,EAAE,YAAY;AAC7C,QAAI,SAAS,IAAI,QAAQ,EAAG;AAC5B,aAAS,IAAI,QAAQ;AACrB,UAAM,QAAQ,iCAAiC,KAAK,IAAI;AACxD,QAAI,MAAO,QAAO,KAAK,KAAK;AAAA,EAC9B;AACA,SAAO;AACT;AAEA,eAAsB,8BACpB,WACA,WACA,SAC6G;AAC7G,QAAM,yBAAyB;AAC/B,QAAM,WAAW,MAAM,mCAAmC,WAAW,SAAS;AAC9E,QAAM,cAAc,MAAM,QAAQ,SAAS,KAAK,IAAI,SAAS,QAAQ,CAAC;AACtE,QAAM,SAAS,gCAAgC,aAAa,OAAO;AACnE,SAAO,EAAE,QAAQ,aAAa,SAAS;AACzC;AAEA,eAAsB,2BACpB,WACA,WACA,SACsB;AACtB,QAAM,EAAE,OAAO,IAAI,MAAM,8BAA8B,WAAW,WAAW,OAAO;AACpF,SAAO;AACT;AAEA,eAAsB,0CACpB,WACA,WACA,SAC6G;AAC7G,SAAO,8BAA8B,WAAW,WAAW,OAAO;AACpE;",
6
+ "names": []
7
+ }
@@ -0,0 +1,26 @@
1
+ const DEFAULT_PREFIXES = ["cf_", "cf:"];
2
+ function collectCustomFieldValues(values, options = {}) {
3
+ const {
4
+ prefixes = DEFAULT_PREFIXES,
5
+ stripPrefix = true,
6
+ transform,
7
+ accept,
8
+ omitUndefined = true
9
+ } = options;
10
+ const result = {};
11
+ for (const [rawKey, rawValue] of Object.entries(values)) {
12
+ const prefix = prefixes.find((candidate) => rawKey.startsWith(candidate));
13
+ if (!prefix) continue;
14
+ const fieldId = stripPrefix ? rawKey.slice(prefix.length) : rawKey;
15
+ if (!fieldId) continue;
16
+ if (accept && !accept(fieldId, rawKey, rawValue)) continue;
17
+ const nextValue = transform ? transform(rawValue, fieldId, rawKey) : rawValue;
18
+ if (omitUndefined && nextValue === void 0) continue;
19
+ result[fieldId] = nextValue;
20
+ }
21
+ return result;
22
+ }
23
+ export {
24
+ collectCustomFieldValues
25
+ };
26
+ //# sourceMappingURL=customFieldValues.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/backend/utils/customFieldValues.ts"],
4
+ "sourcesContent": ["export type CollectCustomFieldOptions = {\n prefixes?: string[]\n stripPrefix?: boolean\n transform?: (value: unknown, fieldId: string, rawKey: string) => unknown\n accept?: (fieldId: string, rawKey: string, value: unknown) => boolean\n omitUndefined?: boolean\n}\n\nconst DEFAULT_PREFIXES = ['cf_', 'cf:']\n\nexport function collectCustomFieldValues(\n values: Record<string, unknown>,\n options: CollectCustomFieldOptions = {},\n): Record<string, unknown> {\n const {\n prefixes = DEFAULT_PREFIXES,\n stripPrefix = true,\n transform,\n accept,\n omitUndefined = true,\n } = options\n\n const result: Record<string, unknown> = {}\n\n for (const [rawKey, rawValue] of Object.entries(values)) {\n const prefix = prefixes.find((candidate) => rawKey.startsWith(candidate))\n if (!prefix) continue\n\n const fieldId = stripPrefix ? rawKey.slice(prefix.length) : rawKey\n if (!fieldId) continue\n\n if (accept && !accept(fieldId, rawKey, rawValue)) continue\n\n const nextValue = transform ? transform(rawValue, fieldId, rawKey) : rawValue\n if (omitUndefined && nextValue === undefined) continue\n\n result[fieldId] = nextValue\n }\n\n return result\n}\n"],
5
+ "mappings": "AAQA,MAAM,mBAAmB,CAAC,OAAO,KAAK;AAE/B,SAAS,yBACd,QACA,UAAqC,CAAC,GACb;AACzB,QAAM;AAAA,IACJ,WAAW;AAAA,IACX,cAAc;AAAA,IACd;AAAA,IACA;AAAA,IACA,gBAAgB;AAAA,EAClB,IAAI;AAEJ,QAAM,SAAkC,CAAC;AAEzC,aAAW,CAAC,QAAQ,QAAQ,KAAK,OAAO,QAAQ,MAAM,GAAG;AACvD,UAAM,SAAS,SAAS,KAAK,CAAC,cAAc,OAAO,WAAW,SAAS,CAAC;AACxE,QAAI,CAAC,OAAQ;AAEb,UAAM,UAAU,cAAc,OAAO,MAAM,OAAO,MAAM,IAAI;AAC5D,QAAI,CAAC,QAAS;AAEd,QAAI,UAAU,CAAC,OAAO,SAAS,QAAQ,QAAQ,EAAG;AAElD,UAAM,YAAY,YAAY,UAAU,UAAU,SAAS,MAAM,IAAI;AACrE,QAAI,iBAAiB,cAAc,OAAW;AAE9C,WAAO,OAAO,IAAI;AAAA,EACpB;AAEA,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,16 @@
1
+ function withFlash(url, message, type = "success") {
2
+ const base = typeof window !== "undefined" && window.location ? window.location.origin : "http://localhost";
3
+ const u = new URL(url, base);
4
+ u.searchParams.set("flash", message);
5
+ u.searchParams.set("type", type);
6
+ const qs = u.searchParams.toString();
7
+ return `${u.pathname}${qs ? `?${qs}` : ""}`;
8
+ }
9
+ function pushWithFlash(router, url, message, type = "success") {
10
+ router.push(withFlash(url, message, type));
11
+ }
12
+ export {
13
+ pushWithFlash,
14
+ withFlash
15
+ };
16
+ //# sourceMappingURL=flash.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/backend/utils/flash.ts"],
4
+ "sourcesContent": ["export type FlashType = 'success' | 'error' | 'warning' | 'info'\n\n// Append flash message and type to a URL (relative or absolute) and return a relative URL string.\nexport function withFlash(url: string, message: string, type: FlashType = 'success'): string {\n const base = typeof window !== 'undefined' && window.location ? window.location.origin : 'http://localhost'\n const u = new URL(url, base)\n u.searchParams.set('flash', message)\n u.searchParams.set('type', type)\n const qs = u.searchParams.toString()\n return `${u.pathname}${qs ? `?${qs}` : ''}`\n}\n\n// Helper to push a URL with flash via Next.js router\nexport function pushWithFlash(router: { push: (href: string) => any }, url: string, message: string, type: FlashType = 'success') {\n router.push(withFlash(url, message, type))\n}\n\n"],
5
+ "mappings": "AAGO,SAAS,UAAU,KAAa,SAAiB,OAAkB,WAAmB;AAC3F,QAAM,OAAO,OAAO,WAAW,eAAe,OAAO,WAAW,OAAO,SAAS,SAAS;AACzF,QAAM,IAAI,IAAI,IAAI,KAAK,IAAI;AAC3B,IAAE,aAAa,IAAI,SAAS,OAAO;AACnC,IAAE,aAAa,IAAI,QAAQ,IAAI;AAC/B,QAAM,KAAK,EAAE,aAAa,SAAS;AACnC,SAAO,GAAG,EAAE,QAAQ,GAAG,KAAK,IAAI,EAAE,KAAK,EAAE;AAC3C;AAGO,SAAS,cAAc,QAAyC,KAAa,SAAiB,OAAkB,WAAW;AAChI,SAAO,KAAK,UAAU,KAAK,SAAS,IAAI,CAAC;AAC3C;",
6
+ "names": []
7
+ }
@@ -0,0 +1,185 @@
1
+ import React from "react";
2
+ async function fetchFeatureGrants(requestFeatures) {
3
+ const granted = /* @__PURE__ */ new Set();
4
+ if (!requestFeatures.length) return granted;
5
+ let url = "/api/auth/feature-check";
6
+ let headersInit;
7
+ if (typeof window === "undefined") {
8
+ try {
9
+ const { headers: getHeaders } = await import("next/headers");
10
+ const h = await getHeaders();
11
+ const host = h.get("x-forwarded-host") || h.get("host") || "";
12
+ const proto = h.get("x-forwarded-proto") || "http";
13
+ const cookie = h.get("cookie") || "";
14
+ if (host) url = `${proto}://${host}/api/auth/feature-check`;
15
+ headersInit = { cookie };
16
+ } catch {
17
+ }
18
+ }
19
+ try {
20
+ const res = await fetch(url, {
21
+ method: "POST",
22
+ credentials: "include",
23
+ headers: { "content-type": "application/json", ...headersInit || {} },
24
+ body: JSON.stringify({ features: requestFeatures })
25
+ });
26
+ if (res.ok) {
27
+ const data = await res.json().catch(() => ({ granted: [] }));
28
+ if (Array.isArray(data?.granted)) {
29
+ data.granted.forEach((f) => granted.add(f));
30
+ }
31
+ }
32
+ } catch {
33
+ }
34
+ return granted;
35
+ }
36
+ async function buildAdminNav(modules, ctx, userEntities, translate, options) {
37
+ function capitalize(s) {
38
+ return s.charAt(0).toUpperCase() + s.slice(1);
39
+ }
40
+ function deriveTitleFromPath(p) {
41
+ const seg = p.split("/").filter(Boolean).pop() || "";
42
+ return seg ? seg.split("-").map(capitalize).join(" ") : "Home";
43
+ }
44
+ const entries = [];
45
+ const allRequiredFeatures = /* @__PURE__ */ new Set();
46
+ for (const m of modules) {
47
+ for (const r of m.backendRoutes ?? []) {
48
+ const features = r.requireFeatures;
49
+ if (features && features.length) {
50
+ features.forEach((f) => allRequiredFeatures.add(f));
51
+ }
52
+ }
53
+ }
54
+ let userFeatures = /* @__PURE__ */ new Set();
55
+ if (allRequiredFeatures.size > 0) {
56
+ const requestFeatures = Array.from(allRequiredFeatures);
57
+ if (options?.checkFeatures) {
58
+ try {
59
+ const resolved = await options.checkFeatures(requestFeatures);
60
+ if (resolved) {
61
+ userFeatures = new Set(resolved);
62
+ }
63
+ } catch {
64
+ }
65
+ } else {
66
+ userFeatures = await fetchFeatureGrants(requestFeatures);
67
+ }
68
+ }
69
+ function hasAllFeatures(required) {
70
+ if (!required || required.length === 0) return true;
71
+ return required.every((f) => userFeatures.has(f));
72
+ }
73
+ for (const m of modules) {
74
+ const groupDefault = capitalize(m.id);
75
+ for (const r of m.backendRoutes ?? []) {
76
+ const href = r.pattern ?? r.path ?? "";
77
+ if (!href || href.includes("[")) continue;
78
+ if (r.navHidden) continue;
79
+ const title = r.title || deriveTitleFromPath(href);
80
+ const titleKey = r.pageTitleKey ?? r.titleKey;
81
+ const group = r.group || groupDefault;
82
+ const groupKey = r.pageGroupKey ?? r.groupKey;
83
+ const groupId = groupKey ?? group;
84
+ const displayGroup = translate ? translate(groupKey, group) : group;
85
+ const displayTitle = translate ? translate(titleKey, title) : title;
86
+ const visible = r.visible ? await Promise.resolve(r.visible(ctx)) : true;
87
+ if (!visible) continue;
88
+ const enabled = r.enabled ? await Promise.resolve(r.enabled(ctx)) : true;
89
+ const required = r.requireRoles || [];
90
+ if (required.length) {
91
+ const roles = ctx.auth?.roles || [];
92
+ const ok = required.some((role) => roles.includes(role));
93
+ if (!ok) continue;
94
+ }
95
+ const features = r.requireFeatures;
96
+ if (features && features.length) {
97
+ const ok = hasAllFeatures(features);
98
+ if (!ok) continue;
99
+ }
100
+ const order = r.order;
101
+ const priority = r.priority ?? order;
102
+ let icon = r.icon;
103
+ entries.push({
104
+ group: displayGroup,
105
+ groupId,
106
+ groupKey,
107
+ groupDefaultName: displayGroup,
108
+ title: displayTitle,
109
+ defaultTitle: displayTitle,
110
+ titleKey,
111
+ href,
112
+ enabled,
113
+ order,
114
+ priority,
115
+ icon
116
+ });
117
+ }
118
+ }
119
+ const byHref = /* @__PURE__ */ new Map();
120
+ for (const e of entries) byHref.set(e.href, e);
121
+ const roots = [];
122
+ for (const e of entries) {
123
+ let parent;
124
+ for (const p of entries) {
125
+ if (p === e) continue;
126
+ if (p.groupId !== e.groupId) continue;
127
+ if (!e.href.startsWith(p.href + "/")) continue;
128
+ if (!parent || p.href.length > parent.href.length) parent = p;
129
+ }
130
+ if (parent) {
131
+ parent.children = parent.children || [];
132
+ parent.children.push(e);
133
+ } else {
134
+ roots.push(e);
135
+ }
136
+ }
137
+ if (userEntities && userEntities.length > 0) {
138
+ const tableIcon = React.createElement(
139
+ "svg",
140
+ { width: 16, height: 16, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2 },
141
+ React.createElement("rect", { x: 3, y: 4, width: 18, height: 16, rx: 2 }),
142
+ React.createElement("path", { d: "M3 10h18M9 4v16M15 4v16" })
143
+ );
144
+ const userEntitiesItem = roots.find((item) => item.groupKey === "entities.nav.group" && item.titleKey === "entities.nav.userEntities");
145
+ if (userEntitiesItem) {
146
+ const existingChildren = userEntitiesItem.children || [];
147
+ const dynamicUserEntities = userEntities.map((entity) => ({
148
+ group: userEntitiesItem.group,
149
+ groupId: userEntitiesItem.groupId,
150
+ groupKey: userEntitiesItem.groupKey,
151
+ groupDefaultName: userEntitiesItem.groupDefaultName,
152
+ title: entity.label,
153
+ defaultTitle: entity.label,
154
+ href: entity.href,
155
+ enabled: true,
156
+ order: 1e3,
157
+ // High order to appear at the end
158
+ priority: 1e3,
159
+ icon: tableIcon
160
+ }));
161
+ const merged = [...existingChildren, ...dynamicUserEntities];
162
+ const byHref2 = /* @__PURE__ */ new Map();
163
+ for (const it of merged) {
164
+ if (!byHref2.has(it.href)) byHref2.set(it.href, it);
165
+ }
166
+ userEntitiesItem.children = Array.from(byHref2.values());
167
+ }
168
+ }
169
+ const sortItems = (arr) => {
170
+ arr.sort((a, b) => {
171
+ if (a.groupId !== b.groupId) return a.groupId.localeCompare(b.groupId);
172
+ const ap = a.priority ?? a.order ?? 1e4;
173
+ const bp = b.priority ?? b.order ?? 1e4;
174
+ if (ap !== bp) return ap - bp;
175
+ return a.title.localeCompare(b.title);
176
+ });
177
+ for (const it of arr) if (it.children?.length) sortItems(it.children);
178
+ };
179
+ sortItems(roots);
180
+ return roots;
181
+ }
182
+ export {
183
+ buildAdminNav
184
+ };
185
+ //# sourceMappingURL=nav.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/backend/utils/nav.ts"],
4
+ "sourcesContent": ["import type { ReactNode } from 'react'\nimport React from 'react'\n\nexport type AdminNavItem = {\n group: string\n groupId: string\n groupKey?: string\n groupDefaultName: string\n title: string\n defaultTitle: string\n titleKey?: string\n href: string\n enabled: boolean\n hidden?: boolean\n order?: number\n priority?: number\n icon?: ReactNode\n children?: AdminNavItem[]\n}\n\nexport type AdminNavFeatureChecker = (features: string[]) => Promise<Iterable<string> | null | undefined>\n\nexport type BuildAdminNavOptions = {\n checkFeatures?: AdminNavFeatureChecker\n}\n\n/**\n * @deprecated The internal fetch-based feature check will be removed.\n * Provide `options.checkFeatures` so buildAdminNav can reuse your RBAC context.\n */\nasync function fetchFeatureGrants(requestFeatures: string[]): Promise<Set<string>> {\n const granted = new Set<string>()\n if (!requestFeatures.length) return granted\n let url = '/api/auth/feature-check'\n let headersInit: Record<string, string> | undefined\n if (typeof window === 'undefined') {\n // On the server, build absolute URL and forward cookies so auth is available\n try {\n const { headers: getHeaders } = await import('next/headers')\n const h = await getHeaders()\n const host = h.get('x-forwarded-host') || h.get('host') || ''\n const proto = h.get('x-forwarded-proto') || 'http'\n const cookie = h.get('cookie') || ''\n if (host) url = `${proto}://${host}/api/auth/feature-check`\n headersInit = { cookie }\n } catch {\n // ignore; fall back to relative URL without forwarded cookies\n }\n }\n try {\n const res = await fetch(url, {\n method: 'POST',\n credentials: 'include' as any,\n headers: { 'content-type': 'application/json', ...(headersInit || {}) },\n body: JSON.stringify({ features: requestFeatures }),\n } as any)\n if (res.ok) {\n const data = await res.json().catch(() => ({ granted: [] }))\n if (Array.isArray(data?.granted)) {\n data.granted.forEach((f: string) => granted.add(f))\n }\n }\n } catch {\n // ignore fetch failures and keep feature set empty\n }\n return granted\n}\n\nexport async function buildAdminNav(\n modules: any[],\n ctx: { auth?: { roles?: string[]; sub?: string; orgId?: string | null; tenantId?: string | null }; path?: string },\n userEntities?: Array<{ entityId: string; label: string; href: string }>,\n translate?: (key: string | undefined, fallback: string) => string,\n options?: BuildAdminNavOptions\n): Promise<AdminNavItem[]> {\n function capitalize(s: string) {\n return s.charAt(0).toUpperCase() + s.slice(1)\n }\n function deriveTitleFromPath(p: string) {\n const seg = p.split('/').filter(Boolean).pop() || ''\n return seg ? seg.split('-').map(capitalize).join(' ') : 'Home'\n }\n const entries: AdminNavItem[] = []\n\n // Collect all unique features needed across all routes first\n const allRequiredFeatures = new Set<string>()\n for (const m of modules) {\n for (const r of m.backendRoutes ?? []) {\n const features = (r as any).requireFeatures as string[] | undefined\n if (features && features.length) {\n features.forEach(f => allRequiredFeatures.add(f))\n }\n }\n }\n\n // Batch check all features in a single API call\n\t let userFeatures = new Set<string>()\n\t if (allRequiredFeatures.size > 0) {\n\t const requestFeatures = Array.from(allRequiredFeatures)\n\t if (options?.checkFeatures) {\n\t try {\n\t const resolved = await options.checkFeatures(requestFeatures)\n\t if (resolved) {\n\t userFeatures = new Set(resolved)\n\t }\n\t } catch {\n\t // ignore and fall back to empty feature set\n\t }\n\t } else {\n\t userFeatures = await fetchFeatureGrants(requestFeatures)\n\t }\n\t }\n\n // Helper: check if user has all required features (from cache)\n function hasAllFeatures(required: string[]): boolean {\n if (!required || required.length === 0) return true\n return required.every(f => userFeatures.has(f))\n }\n\n // Icons are defined per-page in metadata; no heuristic derivation here.\n for (const m of modules) {\n const groupDefault = capitalize(m.id)\n for (const r of m.backendRoutes ?? []) {\n const href = (r.pattern ?? r.path ?? '') as string\n if (!href || href.includes('[')) continue\n if ((r as any).navHidden) continue\n const title = (r.title as string) || deriveTitleFromPath(href)\n const titleKey = (r as any).pageTitleKey ?? (r as any).titleKey\n const group = (r.group as string) || groupDefault\n const groupKey = (r as any).pageGroupKey ?? (r as any).groupKey\n const groupId = (groupKey as string | undefined) ?? group\n const displayGroup = translate ? translate(groupKey, group) : group\n const displayTitle = translate ? translate(titleKey, title) : title\n const visible = r.visible ? await Promise.resolve(r.visible(ctx)) : true\n if (!visible) continue\n const enabled = r.enabled ? await Promise.resolve(r.enabled(ctx)) : true\n // If roles are required, check; otherwise include\n const required = (r.requireRoles as string[]) || []\n if (required.length) {\n const roles = ctx.auth?.roles || []\n const ok = required.some((role) => roles.includes(role))\n if (!ok) continue\n }\n // If features are required, check from cached batch result\n const features = (r as any).requireFeatures as string[] | undefined\n if (features && features.length) {\n const ok = hasAllFeatures(features)\n if (!ok) continue\n }\n const order = (r as any).order as number | undefined\n const priority = ((r as any).priority as number | undefined) ?? order\n let icon = (r as any).icon as ReactNode | undefined\n entries.push({\n group: displayGroup,\n groupId,\n groupKey,\n groupDefaultName: displayGroup,\n title: displayTitle,\n defaultTitle: displayTitle,\n titleKey,\n href,\n enabled,\n order,\n priority,\n icon,\n })\n }\n }\n // Build hierarchy: treat routes whose href starts with a parent href + '/'\n const byHref = new Map<string, AdminNavItem>()\n for (const e of entries) byHref.set(e.href, e)\n const roots: AdminNavItem[] = []\n for (const e of entries) {\n // Find the longest parent href that is a strict prefix and within same group\n let parent: AdminNavItem | undefined\n for (const p of entries) {\n if (p === e) continue\n if (p.groupId !== e.groupId) continue\n if (!e.href.startsWith(p.href + '/')) continue\n if (!parent || p.href.length > parent.href.length) parent = p\n }\n if (parent) {\n parent.children = parent.children || []\n parent.children.push(e)\n } else {\n roots.push(e)\n }\n }\n\n // Add dynamic user entities to the navigation\n if (userEntities && userEntities.length > 0) {\n const tableIcon = React.createElement(\n 'svg',\n { width: 16, height: 16, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2 },\n React.createElement('rect', { x: 3, y: 4, width: 18, height: 16, rx: 2 }),\n React.createElement('path', { d: 'M3 10h18M9 4v16M15 4v16' }),\n )\n // Find the \"User Entities\" item in the Data designer group (it should be a root item)\n const userEntitiesItem = roots.find(item => item.groupKey === 'entities.nav.group' && item.titleKey === 'entities.nav.userEntities')\n if (userEntitiesItem) {\n const existingChildren = userEntitiesItem.children || []\n const dynamicUserEntities = userEntities.map((entity) => ({\n group: userEntitiesItem.group,\n groupId: userEntitiesItem.groupId,\n groupKey: userEntitiesItem.groupKey,\n groupDefaultName: userEntitiesItem.groupDefaultName,\n title: entity.label,\n defaultTitle: entity.label,\n href: entity.href,\n enabled: true,\n order: 1000, // High order to appear at the end\n priority: 1000,\n icon: tableIcon,\n }))\n // Merge and deduplicate by href to avoid duplicates coming from server or generator\n const merged = [...existingChildren, ...dynamicUserEntities]\n const byHref = new Map<string, AdminNavItem>()\n for (const it of merged) {\n if (!byHref.has(it.href)) byHref.set(it.href, it)\n }\n userEntitiesItem.children = Array.from(byHref.values())\n }\n }\n\n // Sorting: group, then priority/order, then title. Apply within children too.\n const sortItems = (arr: AdminNavItem[]) => {\n arr.sort((a, b) => {\n if (a.groupId !== b.groupId) return a.groupId.localeCompare(b.groupId)\n const ap = a.priority ?? a.order ?? 10_000\n const bp = b.priority ?? b.order ?? 10_000\n if (ap !== bp) return ap - bp\n return a.title.localeCompare(b.title)\n })\n for (const it of arr) if (it.children?.length) sortItems(it.children)\n }\n sortItems(roots)\n return roots\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["byHref"]
7
+ }