@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,87 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { cn } from "@open-mercato/shared/lib/utils";
5
+ const TabsContext = React.createContext(void 0);
6
+ function useTabsContext() {
7
+ const context = React.useContext(TabsContext);
8
+ if (!context) {
9
+ throw new Error("Tabs components must be used within a Tabs provider");
10
+ }
11
+ return context;
12
+ }
13
+ function Tabs({
14
+ value: controlledValue,
15
+ defaultValue,
16
+ onValueChange,
17
+ children,
18
+ className
19
+ }) {
20
+ const [uncontrolledValue, setUncontrolledValue] = React.useState(defaultValue ?? "");
21
+ const isControlled = controlledValue !== void 0;
22
+ const value = isControlled ? controlledValue : uncontrolledValue;
23
+ const handleValueChange = React.useCallback(
24
+ (newValue) => {
25
+ if (!isControlled) {
26
+ setUncontrolledValue(newValue);
27
+ }
28
+ onValueChange?.(newValue);
29
+ },
30
+ [isControlled, onValueChange]
31
+ );
32
+ return /* @__PURE__ */ jsx(TabsContext.Provider, { value: { value, onValueChange: handleValueChange }, children: /* @__PURE__ */ jsx("div", { className, children }) });
33
+ }
34
+ function TabsList({ children, className }) {
35
+ return /* @__PURE__ */ jsx(
36
+ "div",
37
+ {
38
+ className: cn(
39
+ "inline-flex h-9 items-center justify-start rounded-lg bg-muted p-1 text-muted-foreground",
40
+ className
41
+ ),
42
+ role: "tablist",
43
+ children
44
+ }
45
+ );
46
+ }
47
+ function TabsTrigger({ value, children, className, disabled }) {
48
+ const { value: selectedValue, onValueChange } = useTabsContext();
49
+ const isSelected = selectedValue === value;
50
+ return /* @__PURE__ */ jsx(
51
+ "button",
52
+ {
53
+ type: "button",
54
+ role: "tab",
55
+ "aria-selected": isSelected,
56
+ disabled,
57
+ onClick: () => onValueChange(value),
58
+ className: cn(
59
+ "inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
60
+ isSelected ? "bg-background text-foreground shadow" : "hover:bg-background/50 hover:text-foreground",
61
+ className
62
+ ),
63
+ children
64
+ }
65
+ );
66
+ }
67
+ function TabsContent({ value, children, className }) {
68
+ const { value: selectedValue } = useTabsContext();
69
+ if (selectedValue !== value) {
70
+ return null;
71
+ }
72
+ return /* @__PURE__ */ jsx(
73
+ "div",
74
+ {
75
+ role: "tabpanel",
76
+ className: cn("mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", className),
77
+ children
78
+ }
79
+ );
80
+ }
81
+ export {
82
+ Tabs,
83
+ TabsContent,
84
+ TabsList,
85
+ TabsTrigger
86
+ };
87
+ //# sourceMappingURL=tabs.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/primitives/tabs.tsx"],
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\ntype TabsContextValue = {\n value: string\n onValueChange: (value: string) => void\n}\n\nconst TabsContext = React.createContext<TabsContextValue | undefined>(undefined)\n\nfunction useTabsContext() {\n const context = React.useContext(TabsContext)\n if (!context) {\n throw new Error('Tabs components must be used within a Tabs provider')\n }\n return context\n}\n\nexport type TabsProps = {\n value?: string\n defaultValue?: string\n onValueChange?: (value: string) => void\n children: React.ReactNode\n className?: string\n}\n\nexport function Tabs({\n value: controlledValue,\n defaultValue,\n onValueChange,\n children,\n className,\n}: TabsProps) {\n const [uncontrolledValue, setUncontrolledValue] = React.useState(defaultValue ?? '')\n const isControlled = controlledValue !== undefined\n const value = isControlled ? controlledValue : uncontrolledValue\n\n const handleValueChange = React.useCallback(\n (newValue: string) => {\n if (!isControlled) {\n setUncontrolledValue(newValue)\n }\n onValueChange?.(newValue)\n },\n [isControlled, onValueChange],\n )\n\n return (\n <TabsContext.Provider value={{ value, onValueChange: handleValueChange }}>\n <div className={className}>{children}</div>\n </TabsContext.Provider>\n )\n}\n\nexport type TabsListProps = {\n children: React.ReactNode\n className?: string\n}\n\nexport function TabsList({ children, className }: TabsListProps) {\n return (\n <div\n className={cn(\n 'inline-flex h-9 items-center justify-start rounded-lg bg-muted p-1 text-muted-foreground',\n className,\n )}\n role=\"tablist\"\n >\n {children}\n </div>\n )\n}\n\nexport type TabsTriggerProps = {\n value: string\n children: React.ReactNode\n className?: string\n disabled?: boolean\n}\n\nexport function TabsTrigger({ value, children, className, disabled }: TabsTriggerProps) {\n const { value: selectedValue, onValueChange } = useTabsContext()\n const isSelected = selectedValue === value\n\n return (\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={isSelected}\n disabled={disabled}\n onClick={() => onValueChange(value)}\n className={cn(\n 'inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',\n isSelected\n ? 'bg-background text-foreground shadow'\n : 'hover:bg-background/50 hover:text-foreground',\n className,\n )}\n >\n {children}\n </button>\n )\n}\n\nexport type TabsContentProps = {\n value: string\n children: React.ReactNode\n className?: string\n}\n\nexport function TabsContent({ value, children, className }: TabsContentProps) {\n const { value: selectedValue } = useTabsContext()\n\n if (selectedValue !== value) {\n return null\n }\n\n return (\n <div\n role=\"tabpanel\"\n className={cn('mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2', className)}\n >\n {children}\n </div>\n )\n}\n"],
5
+ "mappings": ";AAmDM;AAjDN,YAAY,WAAW;AACvB,SAAS,UAAU;AAOnB,MAAM,cAAc,MAAM,cAA4C,MAAS;AAE/E,SAAS,iBAAiB;AACxB,QAAM,UAAU,MAAM,WAAW,WAAW;AAC5C,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,qDAAqD;AAAA,EACvE;AACA,SAAO;AACT;AAUO,SAAS,KAAK;AAAA,EACnB,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAc;AACZ,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,MAAM,SAAS,gBAAgB,EAAE;AACnF,QAAM,eAAe,oBAAoB;AACzC,QAAM,QAAQ,eAAe,kBAAkB;AAE/C,QAAM,oBAAoB,MAAM;AAAA,IAC9B,CAAC,aAAqB;AACpB,UAAI,CAAC,cAAc;AACjB,6BAAqB,QAAQ;AAAA,MAC/B;AACA,sBAAgB,QAAQ;AAAA,IAC1B;AAAA,IACA,CAAC,cAAc,aAAa;AAAA,EAC9B;AAEA,SACE,oBAAC,YAAY,UAAZ,EAAqB,OAAO,EAAE,OAAO,eAAe,kBAAkB,GACrE,8BAAC,SAAI,WAAuB,UAAS,GACvC;AAEJ;AAOO,SAAS,SAAS,EAAE,UAAU,UAAU,GAAkB;AAC/D,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACA,MAAK;AAAA,MAEJ;AAAA;AAAA,EACH;AAEJ;AASO,SAAS,YAAY,EAAE,OAAO,UAAU,WAAW,SAAS,GAAqB;AACtF,QAAM,EAAE,OAAO,eAAe,cAAc,IAAI,eAAe;AAC/D,QAAM,aAAa,kBAAkB;AAErC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,MAAK;AAAA,MACL,iBAAe;AAAA,MACf;AAAA,MACA,SAAS,MAAM,cAAc,KAAK;AAAA,MAClC,WAAW;AAAA,QACT;AAAA,QACA,aACI,yCACA;AAAA,QACJ;AAAA,MACF;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;AAQO,SAAS,YAAY,EAAE,OAAO,UAAU,UAAU,GAAqB;AAC5E,QAAM,EAAE,OAAO,cAAc,IAAI,eAAe;AAEhD,MAAI,kBAAkB,OAAO;AAC3B,WAAO;AAAA,EACT;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,WAAW,GAAG,mIAAmI,SAAS;AAAA,MAEzJ;AAAA;AAAA,EACH;AAEJ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,21 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "@open-mercato/shared/lib/utils";
4
+ const Textarea = React.forwardRef(
5
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
6
+ "textarea",
7
+ {
8
+ ref,
9
+ className: cn(
10
+ "flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
11
+ className
12
+ ),
13
+ ...props
14
+ }
15
+ )
16
+ );
17
+ Textarea.displayName = "Textarea";
18
+ export {
19
+ Textarea
20
+ };
21
+ //# sourceMappingURL=textarea.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/primitives/textarea.tsx"],
4
+ "sourcesContent": ["import * as React from 'react'\n\nimport { cn } from '@open-mercato/shared/lib/utils'\n\ntype TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>\n\nexport const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(\n ({ className, ...props }, ref) => (\n <textarea\n ref={ref}\n className={cn(\n 'flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',\n className\n )}\n {...props}\n />\n )\n)\n\nTextarea.displayName = 'Textarea'\n"],
5
+ "mappings": "AAQI;AARJ,YAAY,WAAW;AAEvB,SAAS,UAAU;AAIZ,MAAM,WAAW,MAAM;AAAA,EAC5B,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QACxB;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,cAAc;",
6
+ "names": []
7
+ }
@@ -0,0 +1,60 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import * as TooltipPrimitive from "@radix-ui/react-tooltip";
5
+ import { cn } from "@open-mercato/shared/lib/utils";
6
+ const TooltipProvider = TooltipPrimitive.Provider;
7
+ const Tooltip = TooltipPrimitive.Root;
8
+ const TooltipTrigger = TooltipPrimitive.Trigger;
9
+ const TooltipContent = React.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsx(
10
+ TooltipPrimitive.Content,
11
+ {
12
+ ref,
13
+ sideOffset,
14
+ className: cn(
15
+ "z-50 overflow-hidden rounded-md bg-slate-900 px-3 py-1.5 text-xs text-slate-50 animate-in fade-in-0 zoom-in-95",
16
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
17
+ "data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2",
18
+ "data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
19
+ "max-w-xs break-words",
20
+ className
21
+ ),
22
+ ...props
23
+ }
24
+ ) }));
25
+ TooltipContent.displayName = TooltipPrimitive.Content.displayName;
26
+ function SimpleTooltip({
27
+ content,
28
+ children,
29
+ delayDuration = 300,
30
+ side = "top",
31
+ align = "center",
32
+ open,
33
+ onOpenChange,
34
+ disabled = false
35
+ }) {
36
+ const isDisabled = disabled || !content;
37
+ if (isDisabled) {
38
+ return /* @__PURE__ */ jsx(Fragment, { children });
39
+ }
40
+ return /* @__PURE__ */ jsxs(
41
+ Tooltip,
42
+ {
43
+ open,
44
+ onOpenChange,
45
+ delayDuration,
46
+ children: [
47
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children }),
48
+ /* @__PURE__ */ jsx(TooltipContent, { side, align, children: content })
49
+ ]
50
+ }
51
+ );
52
+ }
53
+ export {
54
+ SimpleTooltip,
55
+ Tooltip,
56
+ TooltipContent,
57
+ TooltipProvider,
58
+ TooltipTrigger
59
+ };
60
+ //# sourceMappingURL=tooltip.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/primitives/tooltip.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport * as TooltipPrimitive from '@radix-ui/react-tooltip'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\nexport const TooltipProvider = TooltipPrimitive.Provider\n\nexport const Tooltip = TooltipPrimitive.Root\n\nexport const TooltipTrigger = TooltipPrimitive.Trigger\n\nexport const TooltipContent = React.forwardRef<\n React.ElementRef<typeof TooltipPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n>(({ className, sideOffset = 4, ...props }, ref) => (\n <TooltipPrimitive.Portal>\n <TooltipPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n className={cn(\n 'z-50 overflow-hidden rounded-md bg-slate-900 px-3 py-1.5 text-xs text-slate-50 animate-in fade-in-0 zoom-in-95',\n 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',\n 'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2',\n 'data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',\n 'max-w-xs break-words',\n className\n )}\n {...props}\n />\n </TooltipPrimitive.Portal>\n))\nTooltipContent.displayName = TooltipPrimitive.Content.displayName\n\nexport type TooltipProps = {\n content: React.ReactNode\n children: React.ReactNode\n delayDuration?: number\n side?: 'top' | 'right' | 'bottom' | 'left'\n align?: 'start' | 'center' | 'end'\n open?: boolean\n onOpenChange?: (open: boolean) => void\n disabled?: boolean\n}\n\n/**\n * Simple tooltip wrapper component for common use cases.\n *\n * @example\n * <SimpleTooltip content=\"Full text here\">\n * <span>Truncated...</span>\n * </SimpleTooltip>\n */\nexport function SimpleTooltip({\n content,\n children,\n delayDuration = 300,\n side = 'top',\n align = 'center',\n open,\n onOpenChange,\n disabled = false,\n}: TooltipProps) {\n // If disabled or no content, just render children without tooltip\n const isDisabled = disabled || !content\n\n if (isDisabled) {\n return <>{children}</>\n }\n\n return (\n <Tooltip\n open={open}\n onOpenChange={onOpenChange}\n delayDuration={delayDuration}\n >\n <TooltipTrigger asChild>\n {children}\n </TooltipTrigger>\n <TooltipContent side={side} align={align}>\n {content}\n </TooltipContent>\n </Tooltip>\n )\n}\n"],
5
+ "mappings": ";AAiBI,SAkDO,UAlDP,KAsDA,YAtDA;AAfJ,YAAY,WAAW;AACvB,YAAY,sBAAsB;AAClC,SAAS,UAAU;AAEZ,MAAM,kBAAkB,iBAAiB;AAEzC,MAAM,UAAU,iBAAiB;AAEjC,MAAM,iBAAiB,iBAAiB;AAExC,MAAM,iBAAiB,MAAM,WAGlC,CAAC,EAAE,WAAW,aAAa,GAAG,GAAG,MAAM,GAAG,QAC1C,oBAAC,iBAAiB,QAAjB,EACC;AAAA,EAAC,iBAAiB;AAAA,EAAjB;AAAA,IACC;AAAA,IACA;AAAA,IACA,WAAW;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACC,GAAG;AAAA;AACN,GACF,CACD;AACD,eAAe,cAAc,iBAAiB,QAAQ;AAqB/C,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB,OAAO;AAAA,EACP,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA,WAAW;AACb,GAAiB;AAEf,QAAM,aAAa,YAAY,CAAC;AAEhC,MAAI,YAAY;AACd,WAAO,gCAAG,UAAS;AAAA,EACrB;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MAEA;AAAA,4BAAC,kBAAe,SAAO,MACpB,UACH;AAAA,QACA,oBAAC,kBAAe,MAAY,OACzB,mBACH;AAAA;AAAA;AAAA,EACF;AAEJ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,44 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { MutationCache, QueryCache, QueryClient, QueryClientProvider } from "@tanstack/react-query";
5
+ import { redirectToSessionRefresh, redirectToForbiddenLogin, UnauthorizedError, ForbiddenError, apiFetch, setAuthRedirectConfig } from "../backend/utils/api.js";
6
+ function ensureGlobalFetchInterception() {
7
+ if (typeof window === "undefined") return;
8
+ const w = window;
9
+ if (w.__omFetchPatched) return;
10
+ w.__omFetchPatched = true;
11
+ w.__omOriginalFetch = window.fetch;
12
+ window.fetch = ((input, init) => apiFetch(input, init));
13
+ }
14
+ const client = new QueryClient({
15
+ queryCache: new QueryCache({
16
+ onError: (error) => {
17
+ if (error instanceof UnauthorizedError) redirectToSessionRefresh();
18
+ else if (error instanceof ForbiddenError) redirectToForbiddenLogin();
19
+ else if (error?.status === 401) redirectToSessionRefresh();
20
+ else if (error?.status === 403) redirectToForbiddenLogin();
21
+ }
22
+ }),
23
+ mutationCache: new MutationCache({
24
+ onError: (error) => {
25
+ if (error instanceof UnauthorizedError) redirectToSessionRefresh();
26
+ else if (error instanceof ForbiddenError) redirectToForbiddenLogin();
27
+ else if (error?.status === 401) redirectToSessionRefresh();
28
+ else if (error?.status === 403) redirectToForbiddenLogin();
29
+ }
30
+ })
31
+ });
32
+ function QueryProvider({ children, defaultForbiddenRoles }) {
33
+ React.useEffect(() => {
34
+ ensureGlobalFetchInterception();
35
+ if (defaultForbiddenRoles && defaultForbiddenRoles.length) {
36
+ setAuthRedirectConfig({ defaultForbiddenRoles });
37
+ }
38
+ }, []);
39
+ return /* @__PURE__ */ jsx(QueryClientProvider, { client, children });
40
+ }
41
+ export {
42
+ QueryProvider
43
+ };
44
+ //# sourceMappingURL=QueryProvider.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/theme/QueryProvider.tsx"],
4
+ "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { MutationCache, QueryCache, QueryClient, QueryClientProvider } from '@tanstack/react-query'\nimport { redirectToSessionRefresh, redirectToForbiddenLogin, UnauthorizedError, ForbiddenError, apiFetch, setAuthRedirectConfig } from '../backend/utils/api'\n\n// Ensure global fetch calls also respect our redirect-on-401/403 policy.\nfunction ensureGlobalFetchInterception() {\n if (typeof window === 'undefined') return\n const w = window as any\n if (w.__omFetchPatched) return\n w.__omFetchPatched = true\n w.__omOriginalFetch = window.fetch\n window.fetch = ((input: RequestInfo | URL, init?: RequestInit) => apiFetch(input, init)) as any\n}\n\nconst client = new QueryClient({\n queryCache: new QueryCache({\n onError: (error) => {\n if (error instanceof UnauthorizedError) redirectToSessionRefresh()\n else if (error instanceof ForbiddenError) redirectToForbiddenLogin()\n // As a fallback, try to detect common cases\n else if ((error as any)?.status === 401) redirectToSessionRefresh()\n else if ((error as any)?.status === 403) redirectToForbiddenLogin()\n },\n }),\n mutationCache: new MutationCache({\n onError: (error) => {\n if (error instanceof UnauthorizedError) redirectToSessionRefresh()\n else if (error instanceof ForbiddenError) redirectToForbiddenLogin()\n else if ((error as any)?.status === 401) redirectToSessionRefresh()\n else if ((error as any)?.status === 403) redirectToForbiddenLogin()\n },\n }),\n})\n\ntype QueryProviderProps = { children: React.ReactNode; defaultForbiddenRoles?: string[] }\n\nexport function QueryProvider({ children, defaultForbiddenRoles }: QueryProviderProps) {\n React.useEffect(() => {\n ensureGlobalFetchInterception()\n if (defaultForbiddenRoles && defaultForbiddenRoles.length) {\n setAuthRedirectConfig({ defaultForbiddenRoles })\n }\n }, [])\n return <QueryClientProvider client={client}>{children}</QueryClientProvider>\n}\n"],
5
+ "mappings": ";AA4CS;AA3CT,YAAY,WAAW;AACvB,SAAS,eAAe,YAAY,aAAa,2BAA2B;AAC5E,SAAS,0BAA0B,0BAA0B,mBAAmB,gBAAgB,UAAU,6BAA6B;AAGvI,SAAS,gCAAgC;AACvC,MAAI,OAAO,WAAW,YAAa;AACnC,QAAM,IAAI;AACV,MAAI,EAAE,iBAAkB;AACxB,IAAE,mBAAmB;AACrB,IAAE,oBAAoB,OAAO;AAC7B,SAAO,SAAS,CAAC,OAA0B,SAAuB,SAAS,OAAO,IAAI;AACxF;AAEA,MAAM,SAAS,IAAI,YAAY;AAAA,EAC7B,YAAY,IAAI,WAAW;AAAA,IACzB,SAAS,CAAC,UAAU;AAClB,UAAI,iBAAiB,kBAAmB,0BAAyB;AAAA,eACxD,iBAAiB,eAAgB,0BAAyB;AAAA,eAEzD,OAAe,WAAW,IAAK,0BAAyB;AAAA,eACxD,OAAe,WAAW,IAAK,0BAAyB;AAAA,IACpE;AAAA,EACF,CAAC;AAAA,EACD,eAAe,IAAI,cAAc;AAAA,IAC/B,SAAS,CAAC,UAAU;AAClB,UAAI,iBAAiB,kBAAmB,0BAAyB;AAAA,eACxD,iBAAiB,eAAgB,0BAAyB;AAAA,eACzD,OAAe,WAAW,IAAK,0BAAyB;AAAA,eACxD,OAAe,WAAW,IAAK,0BAAyB;AAAA,IACpE;AAAA,EACF,CAAC;AACH,CAAC;AAIM,SAAS,cAAc,EAAE,UAAU,sBAAsB,GAAuB;AACrF,QAAM,UAAU,MAAM;AACpB,kCAA8B;AAC9B,QAAI,yBAAyB,sBAAsB,QAAQ;AACzD,4BAAsB,EAAE,sBAAsB,CAAC;AAAA,IACjD;AAAA,EACF,GAAG,CAAC,CAAC;AACL,SAAO,oBAAC,uBAAoB,QAAiB,UAAS;AACxD;",
6
+ "names": []
7
+ }
@@ -0,0 +1,95 @@
1
+ "use client";
2
+ import { Fragment, jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ const ThemeContext = React.createContext(void 0);
5
+ const THEME_STORAGE_KEY = "om-theme";
6
+ function getSystemTheme() {
7
+ if (typeof window === "undefined") return "light";
8
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
9
+ }
10
+ function getStoredTheme() {
11
+ if (typeof window === "undefined") return "system";
12
+ try {
13
+ const stored = localStorage.getItem(THEME_STORAGE_KEY);
14
+ if (stored === "light" || stored === "dark" || stored === "system") {
15
+ return stored;
16
+ }
17
+ } catch (error) {
18
+ if (process.env.NODE_ENV === "development") {
19
+ console.warn("[ThemeProvider] localStorage read failed:", error);
20
+ }
21
+ }
22
+ return "system";
23
+ }
24
+ function applyTheme(resolvedTheme) {
25
+ const root = document.documentElement;
26
+ if (resolvedTheme === "dark") {
27
+ root.classList.add("dark");
28
+ } else {
29
+ root.classList.remove("dark");
30
+ }
31
+ }
32
+ function ThemeProvider({ children }) {
33
+ const [theme, setThemeState] = React.useState("system");
34
+ const [resolvedTheme, setResolvedTheme] = React.useState("light");
35
+ const [mounted, setMounted] = React.useState(false);
36
+ React.useEffect(() => {
37
+ const stored = getStoredTheme();
38
+ setThemeState(stored);
39
+ const resolved = stored === "system" ? getSystemTheme() : stored;
40
+ setResolvedTheme(resolved);
41
+ applyTheme(resolved);
42
+ setMounted(true);
43
+ }, []);
44
+ React.useEffect(() => {
45
+ if (typeof window === "undefined") return;
46
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
47
+ const handleChange = () => {
48
+ if (theme === "system") {
49
+ const newResolved = getSystemTheme();
50
+ setResolvedTheme(newResolved);
51
+ applyTheme(newResolved);
52
+ }
53
+ };
54
+ mediaQuery.addEventListener("change", handleChange);
55
+ return () => mediaQuery.removeEventListener("change", handleChange);
56
+ }, [theme]);
57
+ const setTheme = React.useCallback((newTheme) => {
58
+ setThemeState(newTheme);
59
+ try {
60
+ localStorage.setItem(THEME_STORAGE_KEY, newTheme);
61
+ } catch (error) {
62
+ if (process.env.NODE_ENV === "development") {
63
+ console.warn("[ThemeProvider] localStorage write failed:", error);
64
+ }
65
+ }
66
+ const resolved = newTheme === "system" ? getSystemTheme() : newTheme;
67
+ setResolvedTheme(resolved);
68
+ applyTheme(resolved);
69
+ }, []);
70
+ const value = React.useMemo(
71
+ () => ({ theme, resolvedTheme, setTheme }),
72
+ [theme, resolvedTheme, setTheme]
73
+ );
74
+ if (!mounted) {
75
+ return /* @__PURE__ */ jsx(Fragment, { children });
76
+ }
77
+ return /* @__PURE__ */ jsx(ThemeContext.Provider, { value, children });
78
+ }
79
+ function useTheme() {
80
+ const context = React.useContext(ThemeContext);
81
+ if (context === void 0) {
82
+ return {
83
+ theme: "system",
84
+ resolvedTheme: "light",
85
+ setTheme: () => {
86
+ }
87
+ };
88
+ }
89
+ return context;
90
+ }
91
+ export {
92
+ ThemeProvider,
93
+ useTheme
94
+ };
95
+ //# sourceMappingURL=ThemeProvider.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/theme/ThemeProvider.tsx"],
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\n\nexport type Theme = 'light' | 'dark' | 'system'\n\ntype ThemeContextValue = {\n theme: Theme\n resolvedTheme: 'light' | 'dark'\n setTheme: (theme: Theme) => void\n}\n\nconst ThemeContext = React.createContext<ThemeContextValue | undefined>(undefined)\n\nconst THEME_STORAGE_KEY = 'om-theme'\n\nfunction getSystemTheme(): 'light' | 'dark' {\n if (typeof window === 'undefined') return 'light'\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'\n}\n\nfunction getStoredTheme(): Theme {\n if (typeof window === 'undefined') return 'system'\n try {\n const stored = localStorage.getItem(THEME_STORAGE_KEY)\n if (stored === 'light' || stored === 'dark' || stored === 'system') {\n return stored\n }\n } catch (error) {\n // localStorage may be unavailable in private browsing, iframes, or restricted contexts\n // Theme will default to system preference - this is expected graceful degradation\n if (process.env.NODE_ENV === 'development') {\n console.warn('[ThemeProvider] localStorage read failed:', error)\n }\n }\n return 'system'\n}\n\nfunction applyTheme(resolvedTheme: 'light' | 'dark') {\n const root = document.documentElement\n if (resolvedTheme === 'dark') {\n root.classList.add('dark')\n } else {\n root.classList.remove('dark')\n }\n}\n\nexport function ThemeProvider({ children }: { children: React.ReactNode }) {\n const [theme, setThemeState] = React.useState<Theme>('system')\n const [resolvedTheme, setResolvedTheme] = React.useState<'light' | 'dark'>('light')\n const [mounted, setMounted] = React.useState(false)\n\n // Initialize theme from localStorage on mount\n React.useEffect(() => {\n const stored = getStoredTheme()\n setThemeState(stored)\n const resolved = stored === 'system' ? getSystemTheme() : stored\n setResolvedTheme(resolved)\n applyTheme(resolved)\n setMounted(true)\n }, [])\n\n // Listen for system theme changes\n React.useEffect(() => {\n if (typeof window === 'undefined') return\n\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')\n const handleChange = () => {\n if (theme === 'system') {\n const newResolved = getSystemTheme()\n setResolvedTheme(newResolved)\n applyTheme(newResolved)\n }\n }\n\n mediaQuery.addEventListener('change', handleChange)\n return () => mediaQuery.removeEventListener('change', handleChange)\n }, [theme])\n\n const setTheme = React.useCallback((newTheme: Theme) => {\n setThemeState(newTheme)\n try {\n localStorage.setItem(THEME_STORAGE_KEY, newTheme)\n } catch (error) {\n // localStorage may be unavailable - theme still works for this session, just won't persist\n if (process.env.NODE_ENV === 'development') {\n console.warn('[ThemeProvider] localStorage write failed:', error)\n }\n }\n const resolved = newTheme === 'system' ? getSystemTheme() : newTheme\n setResolvedTheme(resolved)\n applyTheme(resolved)\n }, [])\n\n const value = React.useMemo(\n () => ({ theme, resolvedTheme, setTheme }),\n [theme, resolvedTheme, setTheme]\n )\n\n // Prevent flash of wrong theme during hydration\n if (!mounted) {\n return <>{children}</>\n }\n\n return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>\n}\n\nexport function useTheme(): ThemeContextValue {\n const context = React.useContext(ThemeContext)\n if (context === undefined) {\n // Return safe defaults when not in provider (e.g., server render)\n return {\n theme: 'system',\n resolvedTheme: 'light',\n setTheme: () => {},\n }\n }\n return context\n}\n\n"],
5
+ "mappings": ";AAqGW;AAnGX,YAAY,WAAW;AAUvB,MAAM,eAAe,MAAM,cAA6C,MAAS;AAEjF,MAAM,oBAAoB;AAE1B,SAAS,iBAAmC;AAC1C,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,SAAO,OAAO,WAAW,8BAA8B,EAAE,UAAU,SAAS;AAC9E;AAEA,SAAS,iBAAwB;AAC/B,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,MAAI;AACF,UAAM,SAAS,aAAa,QAAQ,iBAAiB;AACrD,QAAI,WAAW,WAAW,WAAW,UAAU,WAAW,UAAU;AAClE,aAAO;AAAA,IACT;AAAA,EACF,SAAS,OAAO;AAGd,QAAI,QAAQ,IAAI,aAAa,eAAe;AAC1C,cAAQ,KAAK,6CAA6C,KAAK;AAAA,IACjE;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,WAAW,eAAiC;AACnD,QAAM,OAAO,SAAS;AACtB,MAAI,kBAAkB,QAAQ;AAC5B,SAAK,UAAU,IAAI,MAAM;AAAA,EAC3B,OAAO;AACL,SAAK,UAAU,OAAO,MAAM;AAAA,EAC9B;AACF;AAEO,SAAS,cAAc,EAAE,SAAS,GAAkC;AACzE,QAAM,CAAC,OAAO,aAAa,IAAI,MAAM,SAAgB,QAAQ;AAC7D,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAA2B,OAAO;AAClF,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,KAAK;AAGlD,QAAM,UAAU,MAAM;AACpB,UAAM,SAAS,eAAe;AAC9B,kBAAc,MAAM;AACpB,UAAM,WAAW,WAAW,WAAW,eAAe,IAAI;AAC1D,qBAAiB,QAAQ;AACzB,eAAW,QAAQ;AACnB,eAAW,IAAI;AAAA,EACjB,GAAG,CAAC,CAAC;AAGL,QAAM,UAAU,MAAM;AACpB,QAAI,OAAO,WAAW,YAAa;AAEnC,UAAM,aAAa,OAAO,WAAW,8BAA8B;AACnE,UAAM,eAAe,MAAM;AACzB,UAAI,UAAU,UAAU;AACtB,cAAM,cAAc,eAAe;AACnC,yBAAiB,WAAW;AAC5B,mBAAW,WAAW;AAAA,MACxB;AAAA,IACF;AAEA,eAAW,iBAAiB,UAAU,YAAY;AAClD,WAAO,MAAM,WAAW,oBAAoB,UAAU,YAAY;AAAA,EACpE,GAAG,CAAC,KAAK,CAAC;AAEV,QAAM,WAAW,MAAM,YAAY,CAAC,aAAoB;AACtD,kBAAc,QAAQ;AACtB,QAAI;AACF,mBAAa,QAAQ,mBAAmB,QAAQ;AAAA,IAClD,SAAS,OAAO;AAEd,UAAI,QAAQ,IAAI,aAAa,eAAe;AAC1C,gBAAQ,KAAK,8CAA8C,KAAK;AAAA,MAClE;AAAA,IACF;AACA,UAAM,WAAW,aAAa,WAAW,eAAe,IAAI;AAC5D,qBAAiB,QAAQ;AACzB,eAAW,QAAQ;AAAA,EACrB,GAAG,CAAC,CAAC;AAEL,QAAM,QAAQ,MAAM;AAAA,IAClB,OAAO,EAAE,OAAO,eAAe,SAAS;AAAA,IACxC,CAAC,OAAO,eAAe,QAAQ;AAAA,EACjC;AAGA,MAAI,CAAC,SAAS;AACZ,WAAO,gCAAG,UAAS;AAAA,EACrB;AAEA,SAAO,oBAAC,aAAa,UAAb,EAAsB,OAAe,UAAS;AACxD;AAEO,SAAS,WAA8B;AAC5C,QAAM,UAAU,MAAM,WAAW,YAAY;AAC7C,MAAI,YAAY,QAAW;AAEzB,WAAO;AAAA,MACL,OAAO;AAAA,MACP,eAAe;AAAA,MACf,UAAU,MAAM;AAAA,MAAC;AAAA,IACnB;AAAA,EACF;AACA,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,88 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Moon, Sun } from "lucide-react";
5
+ import { useTheme } from "./ThemeProvider.js";
6
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
7
+ import { cn } from "@open-mercato/shared/lib/utils";
8
+ function ThemeToggle({ className }) {
9
+ const { resolvedTheme, setTheme } = useTheme();
10
+ const t = useT();
11
+ const [mounted, setMounted] = React.useState(false);
12
+ React.useEffect(() => {
13
+ setMounted(true);
14
+ }, []);
15
+ const isDark = resolvedTheme === "dark";
16
+ const toggleLabel = t("common.theme.toggle", "Toggle theme");
17
+ const toggle = () => {
18
+ setTheme(isDark ? "light" : "dark");
19
+ };
20
+ if (!mounted) {
21
+ return /* @__PURE__ */ jsx(
22
+ "div",
23
+ {
24
+ className: cn(
25
+ "relative flex h-7 w-14 items-center rounded-full bg-muted p-1",
26
+ className
27
+ ),
28
+ "aria-hidden": "true",
29
+ children: /* @__PURE__ */ jsxs("div", { className: "flex w-full justify-between px-1", children: [
30
+ /* @__PURE__ */ jsx(Sun, { className: "size-3.5 text-muted-foreground" }),
31
+ /* @__PURE__ */ jsx(Moon, { className: "size-3.5 text-muted-foreground" })
32
+ ] })
33
+ }
34
+ );
35
+ }
36
+ return /* @__PURE__ */ jsxs(
37
+ "button",
38
+ {
39
+ type: "button",
40
+ role: "switch",
41
+ "aria-checked": isDark,
42
+ "aria-label": toggleLabel,
43
+ onClick: toggle,
44
+ className: cn(
45
+ "relative flex h-7 w-14 cursor-pointer items-center rounded-full p-1 transition-colors",
46
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
47
+ isDark ? "bg-primary" : "bg-muted",
48
+ className
49
+ ),
50
+ children: [
51
+ /* @__PURE__ */ jsx(
52
+ "span",
53
+ {
54
+ className: cn(
55
+ "absolute size-5 rounded-full bg-background shadow-sm transition-transform duration-200 motion-reduce:transition-none",
56
+ isDark ? "translate-x-7" : "translate-x-0"
57
+ )
58
+ }
59
+ ),
60
+ /* @__PURE__ */ jsxs("span", { className: "relative flex w-full justify-between px-0.5", children: [
61
+ /* @__PURE__ */ jsx(
62
+ Sun,
63
+ {
64
+ className: cn(
65
+ "size-3.5 transition-colors motion-reduce:transition-none",
66
+ isDark ? "text-muted-foreground" : "text-amber-500"
67
+ )
68
+ }
69
+ ),
70
+ /* @__PURE__ */ jsx(
71
+ Moon,
72
+ {
73
+ className: cn(
74
+ "size-3.5 transition-colors motion-reduce:transition-none",
75
+ isDark ? "text-primary-foreground" : "text-muted-foreground"
76
+ )
77
+ }
78
+ )
79
+ ] }),
80
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: isDark ? t("common.theme.dark", "Dark") : t("common.theme.light", "Light") })
81
+ ]
82
+ }
83
+ );
84
+ }
85
+ export {
86
+ ThemeToggle
87
+ };
88
+ //# sourceMappingURL=ThemeToggle.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/theme/ThemeToggle.tsx"],
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { Moon, Sun } from 'lucide-react'\nimport { useTheme } from './ThemeProvider'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\ntype ThemeToggleProps = {\n className?: string\n}\n\nexport function ThemeToggle({ className }: ThemeToggleProps) {\n const { resolvedTheme, setTheme } = useTheme()\n const t = useT()\n const [mounted, setMounted] = React.useState(false)\n\n React.useEffect(() => {\n setMounted(true)\n }, [])\n\n const isDark = resolvedTheme === 'dark'\n const toggleLabel = t('common.theme.toggle', 'Toggle theme')\n\n const toggle = () => {\n setTheme(isDark ? 'light' : 'dark')\n }\n\n // Render placeholder during SSR to prevent hydration mismatch\n if (!mounted) {\n return (\n <div\n className={cn(\n 'relative flex h-7 w-14 items-center rounded-full bg-muted p-1',\n className\n )}\n aria-hidden=\"true\"\n >\n <div className=\"flex w-full justify-between px-1\">\n <Sun className=\"size-3.5 text-muted-foreground\" />\n <Moon className=\"size-3.5 text-muted-foreground\" />\n </div>\n </div>\n )\n }\n\n return (\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={isDark}\n aria-label={toggleLabel}\n onClick={toggle}\n className={cn(\n 'relative flex h-7 w-14 cursor-pointer items-center rounded-full p-1 transition-colors',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n isDark ? 'bg-primary' : 'bg-muted',\n className\n )}\n >\n {/* Sliding indicator */}\n <span\n className={cn(\n 'absolute size-5 rounded-full bg-background shadow-sm transition-transform duration-200 motion-reduce:transition-none',\n isDark ? 'translate-x-7' : 'translate-x-0'\n )}\n />\n {/* Icons */}\n <span className=\"relative flex w-full justify-between px-0.5\">\n <Sun\n className={cn(\n 'size-3.5 transition-colors motion-reduce:transition-none',\n isDark ? 'text-muted-foreground' : 'text-amber-500'\n )}\n />\n <Moon\n className={cn(\n 'size-3.5 transition-colors motion-reduce:transition-none',\n isDark ? 'text-primary-foreground' : 'text-muted-foreground'\n )}\n />\n </span>\n <span className=\"sr-only\">\n {isDark ? t('common.theme.dark', 'Dark') : t('common.theme.light', 'Light')}\n </span>\n </button>\n )\n}\n"],
5
+ "mappings": ";AAsCQ,SACE,KADF;AApCR,YAAY,WAAW;AACvB,SAAS,MAAM,WAAW;AAC1B,SAAS,gBAAgB;AACzB,SAAS,YAAY;AACrB,SAAS,UAAU;AAMZ,SAAS,YAAY,EAAE,UAAU,GAAqB;AAC3D,QAAM,EAAE,eAAe,SAAS,IAAI,SAAS;AAC7C,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,KAAK;AAElD,QAAM,UAAU,MAAM;AACpB,eAAW,IAAI;AAAA,EACjB,GAAG,CAAC,CAAC;AAEL,QAAM,SAAS,kBAAkB;AACjC,QAAM,cAAc,EAAE,uBAAuB,cAAc;AAE3D,QAAM,SAAS,MAAM;AACnB,aAAS,SAAS,UAAU,MAAM;AAAA,EACpC;AAGA,MAAI,CAAC,SAAS;AACZ,WACE;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA;AAAA,QACF;AAAA,QACA,eAAY;AAAA,QAEZ,+BAAC,SAAI,WAAU,oCACb;AAAA,8BAAC,OAAI,WAAU,kCAAiC;AAAA,UAChD,oBAAC,QAAK,WAAU,kCAAiC;AAAA,WACnD;AAAA;AAAA,IACF;AAAA,EAEJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,MAAK;AAAA,MACL,gBAAc;AAAA,MACd,cAAY;AAAA,MACZ,SAAS;AAAA,MACT,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA,SAAS,eAAe;AAAA,QACxB;AAAA,MACF;AAAA,MAGA;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,WAAW;AAAA,cACT;AAAA,cACA,SAAS,kBAAkB;AAAA,YAC7B;AAAA;AAAA,QACF;AAAA,QAEA,qBAAC,UAAK,WAAU,+CACd;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,WAAW;AAAA,gBACT;AAAA,gBACA,SAAS,0BAA0B;AAAA,cACrC;AAAA;AAAA,UACF;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,WAAW;AAAA,gBACT;AAAA,gBACA,SAAS,4BAA4B;AAAA,cACvC;AAAA;AAAA,UACF;AAAA,WACF;AAAA,QACA,oBAAC,UAAK,WAAU,WACb,mBAAS,EAAE,qBAAqB,MAAM,IAAI,EAAE,sBAAsB,OAAO,GAC5E;AAAA;AAAA;AAAA,EACF;AAEJ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,10 @@
1
+ import { ThemeProvider, useTheme } from "./ThemeProvider.js";
2
+ import { ThemeToggle } from "./ThemeToggle.js";
3
+ import { QueryProvider } from "./QueryProvider.js";
4
+ export {
5
+ QueryProvider,
6
+ ThemeProvider,
7
+ ThemeToggle,
8
+ useTheme
9
+ };
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/theme/index.ts"],
4
+ "sourcesContent": ["export { ThemeProvider, useTheme } from './ThemeProvider'\nexport { ThemeToggle } from './ThemeToggle'\nexport { QueryProvider } from './QueryProvider'\n"],
5
+ "mappings": "AAAA,SAAS,eAAe,gBAAgB;AACxC,SAAS,mBAAmB;AAC5B,SAAS,qBAAqB;",
6
+ "names": []
7
+ }
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=react-big-calendar.d.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
@@ -0,0 +1,23 @@
1
+ /** @type {import('jest').Config} */
2
+ module.exports = {
3
+ preset: 'ts-jest',
4
+ testEnvironment: 'jsdom',
5
+ rootDir: '.',
6
+ moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
7
+ moduleNameMapper: {
8
+ '^@open-mercato/ui/(.*)$': '<rootDir>/src/$1',
9
+ },
10
+ transform: {
11
+ '^.+\\.(t|j)sx?$': [
12
+ 'ts-jest',
13
+ {
14
+ tsconfig: {
15
+ jsx: 'react-jsx',
16
+ },
17
+ },
18
+ ],
19
+ },
20
+ setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
21
+ testMatch: ['<rootDir>/src/**/__tests__/**/*.test.(ts|tsx)'],
22
+ passWithNoTests: true,
23
+ }
package/jest.setup.ts ADDED
@@ -0,0 +1,55 @@
1
+ import '@testing-library/jest-dom'
2
+
3
+ // Mock Response/Request/Headers for tests that need them in jsdom environment
4
+ // These are available natively in Node 18+ but jsdom doesn't expose them
5
+ class MockResponse {
6
+ body: string
7
+ status: number
8
+ ok: boolean
9
+ headers: Map<string, string>
10
+
11
+ constructor(body: string = '', init: { status?: number; headers?: Record<string, string> } = {}) {
12
+ this.body = body
13
+ this.status = init.status ?? 200
14
+ this.ok = this.status >= 200 && this.status < 300
15
+ this.headers = new Map(Object.entries(init.headers ?? {}))
16
+ }
17
+
18
+ async json() {
19
+ return JSON.parse(this.body)
20
+ }
21
+
22
+ async text() {
23
+ return this.body
24
+ }
25
+ }
26
+
27
+ if (typeof globalThis.Response === 'undefined') {
28
+ (globalThis as any).Response = MockResponse
29
+ }
30
+
31
+ // Mock window.location.reload globally for all tests
32
+ if (typeof window !== 'undefined' && window.location) {
33
+ try {
34
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
+ delete (window.location as any).reload
36
+ } catch (e) {
37
+ // Ignore if property can't be deleted
38
+ }
39
+
40
+ try {
41
+ Object.defineProperty(window.location, 'reload', {
42
+ configurable: true,
43
+ writable: true,
44
+ value: jest.fn(),
45
+ })
46
+ } catch (e) {
47
+ // If we still can't define it, try direct assignment
48
+ try {
49
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
+ (window.location as any).reload = jest.fn()
51
+ } catch (innerError) {
52
+ // If all else fails, silently ignore - window.location.reload is completely locked
53
+ }
54
+ }
55
+ }