@modern-admin/react 0.1.0

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 (261) hide show
  1. package/dist/action-guard.d.ts +13 -0
  2. package/dist/action-guard.d.ts.map +1 -0
  3. package/dist/action-guard.js +15 -0
  4. package/dist/action-guard.js.map +1 -0
  5. package/dist/action-menu.d.ts +17 -0
  6. package/dist/action-menu.d.ts.map +1 -0
  7. package/dist/action-menu.jsx +80 -0
  8. package/dist/action-menu.jsx.map +1 -0
  9. package/dist/admin-app.d.ts +23 -0
  10. package/dist/admin-app.d.ts.map +1 -0
  11. package/dist/admin-app.jsx +407 -0
  12. package/dist/admin-app.jsx.map +1 -0
  13. package/dist/admin-router.d.ts +29 -0
  14. package/dist/admin-router.d.ts.map +1 -0
  15. package/dist/admin-router.jsx +215 -0
  16. package/dist/admin-router.jsx.map +1 -0
  17. package/dist/breadcrumbs.d.ts +17 -0
  18. package/dist/breadcrumbs.d.ts.map +1 -0
  19. package/dist/breadcrumbs.jsx +40 -0
  20. package/dist/breadcrumbs.jsx.map +1 -0
  21. package/dist/client.d.ts +526 -0
  22. package/dist/client.d.ts.map +1 -0
  23. package/dist/client.js +582 -0
  24. package/dist/client.js.map +1 -0
  25. package/dist/component-loader.d.ts +10 -0
  26. package/dist/component-loader.d.ts.map +1 -0
  27. package/dist/component-loader.js +23 -0
  28. package/dist/component-loader.js.map +1 -0
  29. package/dist/components/ai-assistant-widget.d.ts +3 -0
  30. package/dist/components/ai-assistant-widget.d.ts.map +1 -0
  31. package/dist/components/ai-assistant-widget.jsx +390 -0
  32. package/dist/components/ai-assistant-widget.jsx.map +1 -0
  33. package/dist/components/ai-fill-dialog.d.ts +9 -0
  34. package/dist/components/ai-fill-dialog.d.ts.map +1 -0
  35. package/dist/components/ai-fill-dialog.jsx +105 -0
  36. package/dist/components/ai-fill-dialog.jsx.map +1 -0
  37. package/dist/components/chart-builder-dialog.d.ts +10 -0
  38. package/dist/components/chart-builder-dialog.d.ts.map +1 -0
  39. package/dist/components/chart-builder-dialog.jsx +433 -0
  40. package/dist/components/chart-builder-dialog.jsx.map +1 -0
  41. package/dist/components/chart-widget.d.ts +12 -0
  42. package/dist/components/chart-widget.d.ts.map +1 -0
  43. package/dist/components/chart-widget.jsx +365 -0
  44. package/dist/components/chart-widget.jsx.map +1 -0
  45. package/dist/components/global-search-dialog.d.ts +7 -0
  46. package/dist/components/global-search-dialog.d.ts.map +1 -0
  47. package/dist/components/global-search-dialog.jsx +187 -0
  48. package/dist/components/global-search-dialog.jsx.map +1 -0
  49. package/dist/components/group-settings-dialog.d.ts +13 -0
  50. package/dist/components/group-settings-dialog.d.ts.map +1 -0
  51. package/dist/components/group-settings-dialog.jsx +53 -0
  52. package/dist/components/group-settings-dialog.jsx.map +1 -0
  53. package/dist/components/move-chart-dialog.d.ts +18 -0
  54. package/dist/components/move-chart-dialog.d.ts.map +1 -0
  55. package/dist/components/move-chart-dialog.jsx +68 -0
  56. package/dist/components/move-chart-dialog.jsx.map +1 -0
  57. package/dist/components/reference-multi-table-dialog.d.ts +12 -0
  58. package/dist/components/reference-multi-table-dialog.d.ts.map +1 -0
  59. package/dist/components/reference-multi-table-dialog.jsx +126 -0
  60. package/dist/components/reference-multi-table-dialog.jsx.map +1 -0
  61. package/dist/components/related-records-tabs.d.ts +8 -0
  62. package/dist/components/related-records-tabs.d.ts.map +1 -0
  63. package/dist/components/related-records-tabs.jsx +75 -0
  64. package/dist/components/related-records-tabs.jsx.map +1 -0
  65. package/dist/components/revisions-button.d.ts +7 -0
  66. package/dist/components/revisions-button.d.ts.map +1 -0
  67. package/dist/components/revisions-button.jsx +152 -0
  68. package/dist/components/revisions-button.jsx.map +1 -0
  69. package/dist/components/wizard-form.d.ts +43 -0
  70. package/dist/components/wizard-form.d.ts.map +1 -0
  71. package/dist/components/wizard-form.jsx +136 -0
  72. package/dist/components/wizard-form.jsx.map +1 -0
  73. package/dist/dashboard/time-series.d.ts +20 -0
  74. package/dist/dashboard/time-series.d.ts.map +1 -0
  75. package/dist/dashboard/time-series.js +108 -0
  76. package/dist/dashboard/time-series.js.map +1 -0
  77. package/dist/dialogs.d.ts +35 -0
  78. package/dist/dialogs.d.ts.map +1 -0
  79. package/dist/dialogs.jsx +152 -0
  80. package/dist/dialogs.jsx.map +1 -0
  81. package/dist/export.d.ts +39 -0
  82. package/dist/export.d.ts.map +1 -0
  83. package/dist/export.js +114 -0
  84. package/dist/export.js.map +1 -0
  85. package/dist/extension-registry.d.ts +122 -0
  86. package/dist/extension-registry.d.ts.map +1 -0
  87. package/dist/extension-registry.js +93 -0
  88. package/dist/extension-registry.js.map +1 -0
  89. package/dist/header-controls.d.ts +4 -0
  90. package/dist/header-controls.d.ts.map +1 -0
  91. package/dist/header-controls.jsx +70 -0
  92. package/dist/header-controls.jsx.map +1 -0
  93. package/dist/hooks.d.ts +104 -0
  94. package/dist/hooks.d.ts.map +1 -0
  95. package/dist/hooks.js +374 -0
  96. package/dist/hooks.js.map +1 -0
  97. package/dist/hotkey-help.d.ts +3 -0
  98. package/dist/hotkey-help.d.ts.map +1 -0
  99. package/dist/hotkey-help.jsx +32 -0
  100. package/dist/hotkey-help.jsx.map +1 -0
  101. package/dist/hotkey-registry.d.ts +18 -0
  102. package/dist/hotkey-registry.d.ts.map +1 -0
  103. package/dist/hotkey-registry.jsx +34 -0
  104. package/dist/hotkey-registry.jsx.map +1 -0
  105. package/dist/i18n.d.ts +74 -0
  106. package/dist/i18n.d.ts.map +1 -0
  107. package/dist/i18n.jsx +127 -0
  108. package/dist/i18n.jsx.map +1 -0
  109. package/dist/index.d.ts +35 -0
  110. package/dist/index.d.ts.map +1 -0
  111. package/dist/index.js +36 -0
  112. package/dist/index.js.map +1 -0
  113. package/dist/notify.d.ts +41 -0
  114. package/dist/notify.d.ts.map +1 -0
  115. package/dist/notify.jsx +58 -0
  116. package/dist/notify.jsx.map +1 -0
  117. package/dist/pages/ai-assistant-settings-section.d.ts +3 -0
  118. package/dist/pages/ai-assistant-settings-section.d.ts.map +1 -0
  119. package/dist/pages/ai-assistant-settings-section.jsx +126 -0
  120. package/dist/pages/ai-assistant-settings-section.jsx.map +1 -0
  121. package/dist/pages/audit-log-page.d.ts +3 -0
  122. package/dist/pages/audit-log-page.d.ts.map +1 -0
  123. package/dist/pages/audit-log-page.jsx +354 -0
  124. package/dist/pages/audit-log-page.jsx.map +1 -0
  125. package/dist/pages/edit-page.d.ts +7 -0
  126. package/dist/pages/edit-page.d.ts.map +1 -0
  127. package/dist/pages/edit-page.jsx +614 -0
  128. package/dist/pages/edit-page.jsx.map +1 -0
  129. package/dist/pages/export-dialog.d.ts +11 -0
  130. package/dist/pages/export-dialog.d.ts.map +1 -0
  131. package/dist/pages/export-dialog.jsx +102 -0
  132. package/dist/pages/export-dialog.jsx.map +1 -0
  133. package/dist/pages/home-page.d.ts +3 -0
  134. package/dist/pages/home-page.d.ts.map +1 -0
  135. package/dist/pages/home-page.jsx +211 -0
  136. package/dist/pages/home-page.jsx.map +1 -0
  137. package/dist/pages/list-page.d.ts +42 -0
  138. package/dist/pages/list-page.d.ts.map +1 -0
  139. package/dist/pages/list-page.jsx +1596 -0
  140. package/dist/pages/list-page.jsx.map +1 -0
  141. package/dist/pages/login-page.d.ts +11 -0
  142. package/dist/pages/login-page.d.ts.map +1 -0
  143. package/dist/pages/login-page.jsx +157 -0
  144. package/dist/pages/login-page.jsx.map +1 -0
  145. package/dist/pages/settings-page.d.ts +5 -0
  146. package/dist/pages/settings-page.d.ts.map +1 -0
  147. package/dist/pages/settings-page.jsx +787 -0
  148. package/dist/pages/settings-page.jsx.map +1 -0
  149. package/dist/pages/settings-shared.d.ts +51 -0
  150. package/dist/pages/settings-shared.d.ts.map +1 -0
  151. package/dist/pages/settings-shared.jsx +66 -0
  152. package/dist/pages/settings-shared.jsx.map +1 -0
  153. package/dist/pages/show-page.d.ts +7 -0
  154. package/dist/pages/show-page.d.ts.map +1 -0
  155. package/dist/pages/show-page.jsx +147 -0
  156. package/dist/pages/show-page.jsx.map +1 -0
  157. package/dist/pages/wizard-create-page.d.ts +14 -0
  158. package/dist/pages/wizard-create-page.d.ts.map +1 -0
  159. package/dist/pages/wizard-create-page.jsx +106 -0
  160. package/dist/pages/wizard-create-page.jsx.map +1 -0
  161. package/dist/property-renderer.d.ts +8 -0
  162. package/dist/property-renderer.d.ts.map +1 -0
  163. package/dist/property-renderer.jsx +690 -0
  164. package/dist/property-renderer.jsx.map +1 -0
  165. package/dist/provider.d.ts +20 -0
  166. package/dist/provider.d.ts.map +1 -0
  167. package/dist/provider.jsx +32 -0
  168. package/dist/provider.jsx.map +1 -0
  169. package/dist/realtime.d.ts +22 -0
  170. package/dist/realtime.d.ts.map +1 -0
  171. package/dist/realtime.js +38 -0
  172. package/dist/realtime.js.map +1 -0
  173. package/dist/reference.d.ts +52 -0
  174. package/dist/reference.d.ts.map +1 -0
  175. package/dist/reference.jsx +224 -0
  176. package/dist/reference.jsx.map +1 -0
  177. package/dist/relations.d.ts +11 -0
  178. package/dist/relations.d.ts.map +1 -0
  179. package/dist/relations.js +36 -0
  180. package/dist/relations.js.map +1 -0
  181. package/dist/router.d.ts +82 -0
  182. package/dist/router.d.ts.map +1 -0
  183. package/dist/router.jsx +187 -0
  184. package/dist/router.jsx.map +1 -0
  185. package/dist/show-when.d.ts +7 -0
  186. package/dist/show-when.d.ts.map +1 -0
  187. package/dist/show-when.js +77 -0
  188. package/dist/show-when.js.map +1 -0
  189. package/dist/types.d.ts +194 -0
  190. package/dist/types.d.ts.map +1 -0
  191. package/dist/types.js +18 -0
  192. package/dist/types.js.map +1 -0
  193. package/dist/use-dashboard-charts.d.ts +93 -0
  194. package/dist/use-dashboard-charts.d.ts.map +1 -0
  195. package/dist/use-dashboard-charts.js +263 -0
  196. package/dist/use-dashboard-charts.js.map +1 -0
  197. package/dist/use-hotkey.d.ts +17 -0
  198. package/dist/use-hotkey.d.ts.map +1 -0
  199. package/dist/use-hotkey.js +103 -0
  200. package/dist/use-hotkey.js.map +1 -0
  201. package/dist/user-directory.d.ts +18 -0
  202. package/dist/user-directory.d.ts.map +1 -0
  203. package/dist/user-directory.js +51 -0
  204. package/dist/user-directory.js.map +1 -0
  205. package/dist/validation.d.ts +22 -0
  206. package/dist/validation.d.ts.map +1 -0
  207. package/dist/validation.js +338 -0
  208. package/dist/validation.js.map +1 -0
  209. package/package.json +59 -0
  210. package/src/action-guard.ts +20 -0
  211. package/src/action-menu.tsx +161 -0
  212. package/src/admin-app.tsx +630 -0
  213. package/src/admin-router.tsx +273 -0
  214. package/src/breadcrumbs.tsx +75 -0
  215. package/src/client.ts +1093 -0
  216. package/src/component-loader.ts +33 -0
  217. package/src/components/ai-assistant-widget.tsx +565 -0
  218. package/src/components/ai-fill-dialog.tsx +143 -0
  219. package/src/components/chart-builder-dialog.tsx +618 -0
  220. package/src/components/chart-widget.tsx +654 -0
  221. package/src/components/global-search-dialog.tsx +272 -0
  222. package/src/components/group-settings-dialog.tsx +93 -0
  223. package/src/components/move-chart-dialog.tsx +130 -0
  224. package/src/components/reference-multi-table-dialog.tsx +196 -0
  225. package/src/components/related-records-tabs.tsx +130 -0
  226. package/src/components/revisions-button.tsx +237 -0
  227. package/src/components/wizard-form.tsx +302 -0
  228. package/src/dashboard/time-series.ts +125 -0
  229. package/src/dialogs.tsx +265 -0
  230. package/src/export.ts +140 -0
  231. package/src/extension-registry.ts +195 -0
  232. package/src/header-controls.tsx +125 -0
  233. package/src/hooks.ts +509 -0
  234. package/src/hotkey-help.tsx +56 -0
  235. package/src/hotkey-registry.tsx +60 -0
  236. package/src/i18n.tsx +267 -0
  237. package/src/index.ts +192 -0
  238. package/src/notify.tsx +94 -0
  239. package/src/pages/ai-assistant-settings-section.tsx +167 -0
  240. package/src/pages/audit-log-page.tsx +580 -0
  241. package/src/pages/edit-page.tsx +743 -0
  242. package/src/pages/export-dialog.tsx +154 -0
  243. package/src/pages/home-page.tsx +318 -0
  244. package/src/pages/list-page.tsx +2645 -0
  245. package/src/pages/login-page.tsx +242 -0
  246. package/src/pages/settings-page.tsx +1143 -0
  247. package/src/pages/settings-shared.tsx +134 -0
  248. package/src/pages/show-page.tsx +223 -0
  249. package/src/pages/wizard-create-page.tsx +164 -0
  250. package/src/property-renderer.tsx +1143 -0
  251. package/src/provider.tsx +70 -0
  252. package/src/realtime.ts +55 -0
  253. package/src/reference.tsx +386 -0
  254. package/src/relations.ts +55 -0
  255. package/src/router.tsx +211 -0
  256. package/src/show-when.ts +76 -0
  257. package/src/types.ts +198 -0
  258. package/src/use-dashboard-charts.ts +362 -0
  259. package/src/use-hotkey.ts +128 -0
  260. package/src/user-directory.ts +56 -0
  261. package/src/validation.ts +361 -0
@@ -0,0 +1,338 @@
1
+ // Per-property Zod schema builder with localized error messages.
2
+ //
3
+ // Each PropertyJSON.type maps to a typed validator: numerics get coerced from
4
+ // string inputs, dates parsed via Date, references checked for non-empty,
5
+ // arrays validated as collections of FK-shaped values, and enums (anything
6
+ // with `availableValues`) restricted to the declared set. Required vs
7
+ // optional is honoured uniformly. All error messages route through the
8
+ // passed translator so the active locale wins.
9
+ //
10
+ // The builder returns plain Zod schemas — RHF + zodResolver consume them
11
+ // directly. Keep the builder pure (no React/i18n imports) so it stays
12
+ // testable and reusable from non-React contexts.
13
+ import { z } from 'zod';
14
+ import { evaluateShowWhen } from './show-when.js';
15
+ /** Email pattern: deliberately loose, matches Better Auth / common usage. */
16
+ const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
17
+ const URL_RE = /^https?:\/\/.+/i;
18
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
19
+ const isBlank = (v) => v === undefined || v === null || (typeof v === 'string' && v.trim() === '');
20
+ /** Build a string validator with optional/required + format checks. */
21
+ function stringSchema(p, t, format) {
22
+ const label = p.label;
23
+ const checkFormat = (v) => {
24
+ if (format === 'email')
25
+ return EMAIL_RE.test(v);
26
+ if (format === 'url')
27
+ return URL_RE.test(v);
28
+ if (format === 'uuid')
29
+ return UUID_RE.test(v);
30
+ return true;
31
+ };
32
+ const formatKey = format === 'email'
33
+ ? 'validation:invalidEmail'
34
+ : format === 'url'
35
+ ? 'validation:invalidUrl'
36
+ : format === 'uuid'
37
+ ? 'validation:invalidUuid'
38
+ : null;
39
+ return z
40
+ .union([z.string(), z.null(), z.undefined()])
41
+ .transform((v) => (typeof v === 'string' ? v : v == null ? '' : String(v)))
42
+ .superRefine((value, ctx) => {
43
+ const blank = value.trim() === '';
44
+ if (blank) {
45
+ if (p.isRequired) {
46
+ ctx.addIssue({ code: 'custom', message: t('validation:required', { label }) });
47
+ }
48
+ return;
49
+ }
50
+ if (formatKey && !checkFormat(value)) {
51
+ ctx.addIssue({ code: 'custom', message: t(formatKey, { label }) });
52
+ }
53
+ })
54
+ .transform((v) => (v.trim() === '' ? null : v));
55
+ }
56
+ /** Number validator with coercion from string inputs (HTML inputs ship strings). */
57
+ function numberSchema(p, t, integer = false) {
58
+ const label = p.label;
59
+ // Validate at the unknown layer because Zod 4's z.number() rejects NaN
60
+ // before our refinement can produce a localized message. We coerce here,
61
+ // then assert the type ourselves so all error paths flow through superRefine.
62
+ return z.unknown().superRefine((raw, ctx) => {
63
+ if (isBlank(raw)) {
64
+ if (p.isRequired) {
65
+ ctx.addIssue({ code: 'custom', message: t('validation:required', { label }) });
66
+ }
67
+ return;
68
+ }
69
+ const value = typeof raw === 'number' ? raw : Number(raw);
70
+ if (!Number.isFinite(value)) {
71
+ ctx.addIssue({ code: 'custom', message: t('validation:invalidNumber', { label }) });
72
+ return;
73
+ }
74
+ if (integer && !Number.isInteger(value)) {
75
+ ctx.addIssue({ code: 'custom', message: t('validation:invalidInteger', { label }) });
76
+ }
77
+ }).transform((raw) => {
78
+ if (isBlank(raw))
79
+ return null;
80
+ const n = typeof raw === 'number' ? raw : Number(raw);
81
+ return Number.isFinite(n) ? n : null;
82
+ });
83
+ }
84
+ /** Boolean validator; missing value → false (HTML default for unchecked). */
85
+ function booleanSchema(_p, _t) {
86
+ return z.preprocess((v) => (typeof v === 'boolean' ? v : Boolean(v)), z.boolean());
87
+ }
88
+ /** Date validator: accepts ISO strings or `Date`; rejects unparseable input. */
89
+ function dateSchema(p, t) {
90
+ const label = p.label;
91
+ return z
92
+ .union([z.string(), z.date(), z.null(), z.undefined()])
93
+ .superRefine((value, ctx) => {
94
+ if (isBlank(value)) {
95
+ if (p.isRequired) {
96
+ ctx.addIssue({ code: 'custom', message: t('validation:required', { label }) });
97
+ }
98
+ return;
99
+ }
100
+ const parsed = value instanceof Date ? value : new Date(String(value));
101
+ if (Number.isNaN(parsed.getTime())) {
102
+ ctx.addIssue({ code: 'custom', message: t('validation:invalidDate', { label }) });
103
+ }
104
+ })
105
+ .transform((v) => (isBlank(v) ? null : v));
106
+ }
107
+ /** Single reference validator: requires a non-empty FK when isRequired. */
108
+ function referenceSchema(p, t) {
109
+ const label = p.label;
110
+ return z
111
+ .union([z.string(), z.number(), z.null(), z.undefined()])
112
+ .superRefine((value, ctx) => {
113
+ if (isBlank(value) && p.isRequired) {
114
+ ctx.addIssue({ code: 'custom', message: t('validation:required', { label }) });
115
+ }
116
+ })
117
+ .transform((v) => (isBlank(v) ? null : v));
118
+ }
119
+ /** Multi-reference validator: array of FKs, must be non-empty when required.
120
+ *
121
+ * Pre-processes the input to flatten any accidentally-nested arrays and drop
122
+ * blank items, so a stale form state like `[["3","4"]]` still validates as
123
+ * the expected `["3","4"]` rather than tripping the inner string check. */
124
+ function multiReferenceSchema(p, t) {
125
+ const label = p.label;
126
+ const normalize = (raw) => {
127
+ if (raw == null)
128
+ return [];
129
+ const items = Array.isArray(raw) ? raw : [raw];
130
+ const out = [];
131
+ for (const item of items) {
132
+ if (Array.isArray(item)) {
133
+ for (const sub of item) {
134
+ if (sub != null && sub !== '')
135
+ out.push(sub);
136
+ }
137
+ }
138
+ else if (item != null && item !== '') {
139
+ out.push(item);
140
+ }
141
+ }
142
+ return out;
143
+ };
144
+ return z
145
+ .preprocess(normalize, z.array(z.union([z.string(), z.number()])))
146
+ .superRefine((value, ctx) => {
147
+ if (p.isRequired && value.length === 0) {
148
+ ctx.addIssue({ code: 'custom', message: t('validation:emptySelection', { label }) });
149
+ }
150
+ });
151
+ }
152
+ /** File validator: single-file values are storage keys (string/null), while
153
+ * multi-file values are arrays of storage keys. */
154
+ function fileSchema(p, t) {
155
+ if (p.isArray) {
156
+ return multiReferenceSchema(p, t);
157
+ }
158
+ return stringSchema(p, t);
159
+ }
160
+ /** Enum validator from `availableValues`. Unmatched value → notInList. */
161
+ function enumSchema(p, t) {
162
+ const label = p.label;
163
+ const allowed = new Set((p.availableValues ?? []).map((v) => v.value));
164
+ return z
165
+ .union([z.string(), z.null(), z.undefined()])
166
+ .transform((v) => (v == null ? '' : String(v)))
167
+ .superRefine((value, ctx) => {
168
+ if (value === '') {
169
+ if (p.isRequired) {
170
+ ctx.addIssue({ code: 'custom', message: t('validation:required', { label }) });
171
+ }
172
+ return;
173
+ }
174
+ if (!allowed.has(value)) {
175
+ ctx.addIssue({ code: 'custom', message: t('validation:notInList', { label }) });
176
+ }
177
+ })
178
+ .transform((v) => (v === '' ? null : v));
179
+ }
180
+ /** JSON validator: accepts any JSON-serializable value (object/array/null).
181
+ *
182
+ * Unlike string fields, json fields hold a parsed JavaScript value — the
183
+ * JsonEditor emits objects/arrays directly. The schema passes them through
184
+ * as-is; only the required check (null/undefined → error) is applied. */
185
+ function jsonSchema(p, t) {
186
+ const label = p.label;
187
+ return z
188
+ .unknown()
189
+ .superRefine((v, ctx) => {
190
+ if ((v == null || v === '') && p.isRequired) {
191
+ ctx.addIssue({ code: 'custom', message: t('validation:required', { label }) });
192
+ }
193
+ })
194
+ .transform((v) => (v === '' ? null : v));
195
+ }
196
+ /** Many-to-many validator: array of `{ id, ...extras }` items.
197
+ *
198
+ * The M2M editor emits an array of objects (id of the referenced record plus
199
+ * arbitrary junction extra fields, e.g. `addedAt`, `position`). Bare ids are
200
+ * also accepted and normalized into `{ id }` objects so legacy form state
201
+ * doesn't trip the schema. Extra fields are passed through untouched —
202
+ * the backend's m2m feature persists whatever it recognises and ignores
203
+ * the rest. */
204
+ function m2mSchema(p, t) {
205
+ const label = p.label;
206
+ const normalize = (raw) => {
207
+ if (raw == null)
208
+ return [];
209
+ const items = Array.isArray(raw) ? raw : [raw];
210
+ const out = [];
211
+ for (const item of items) {
212
+ if (item == null || item === '')
213
+ continue;
214
+ if (typeof item === 'string' || typeof item === 'number') {
215
+ out.push({ id: String(item) });
216
+ continue;
217
+ }
218
+ if (typeof item === 'object') {
219
+ const obj = item;
220
+ const id = obj.id ?? obj.value;
221
+ if (id == null || id === '')
222
+ continue;
223
+ out.push({ ...obj, id: String(id) });
224
+ }
225
+ }
226
+ return out;
227
+ };
228
+ return z
229
+ .preprocess(normalize, z.array(z.record(z.string(), z.unknown())))
230
+ .superRefine((value, ctx) => {
231
+ if (p.isRequired && value.length === 0) {
232
+ ctx.addIssue({ code: 'custom', message: t('validation:emptySelection', { label }) });
233
+ }
234
+ });
235
+ }
236
+ /** Build the Zod schema for a property without considering `showWhen`. */
237
+ function buildPropertySchemaInner(p, t) {
238
+ // M2M is structurally different from a multi-reference — its values are
239
+ // `{ id, ...extras }` objects, not bare FKs. Branch first so the
240
+ // `p.reference` check below doesn't capture it.
241
+ if (p.type === 'm2m') {
242
+ return m2mSchema(p, t);
243
+ }
244
+ // Enum-like properties always go through the availableValues path —
245
+ // overrides the raw type (e.g. a string with a fixed set of options).
246
+ if (p.availableValues && p.availableValues.length > 0) {
247
+ return enumSchema(p, t);
248
+ }
249
+ if (p.reference) {
250
+ return p.isArray ? multiReferenceSchema(p, t) : referenceSchema(p, t);
251
+ }
252
+ switch (p.type) {
253
+ case 'boolean':
254
+ return booleanSchema(p, t);
255
+ case 'number':
256
+ case 'float':
257
+ case 'currency':
258
+ case 'money':
259
+ return numberSchema(p, t, false);
260
+ case 'integer':
261
+ return numberSchema(p, t, true);
262
+ case 'date':
263
+ case 'datetime':
264
+ case 'datetime-local':
265
+ return dateSchema(p, t);
266
+ case 'email':
267
+ return stringSchema(p, t, 'email');
268
+ case 'url':
269
+ return stringSchema(p, t, 'url');
270
+ case 'uuid':
271
+ return stringSchema(p, t, 'uuid');
272
+ case 'json':
273
+ return jsonSchema(p, t);
274
+ case 'string':
275
+ case 'text':
276
+ case 'textarea':
277
+ case 'password':
278
+ case 'richtext':
279
+ case 'color':
280
+ case 'file':
281
+ return fileSchema(p, t);
282
+ default:
283
+ return stringSchema(p, t);
284
+ }
285
+ }
286
+ /**
287
+ * Map a PropertyJSON to its Zod schema. When the property has a `showWhen`
288
+ * rule and a `getValues` getter is supplied, the schema short-circuits to
289
+ * a no-op while the rule does not match — letting hidden branches pass
290
+ * validation without their required/format checks tripping submission.
291
+ */
292
+ export function buildPropertySchema(p, t, getValues) {
293
+ const inner = buildPropertySchemaInner(p, t);
294
+ if (!p.showWhen || !getValues)
295
+ return inner;
296
+ // Wrap: only forward to `inner` when visible. Hidden → accept anything,
297
+ // pass it through unchanged. We re-emit issues from `inner` so error
298
+ // messages and paths stay identical to the non-conditional case.
299
+ return z
300
+ .any()
301
+ .superRefine((value, ctx) => {
302
+ if (!evaluateShowWhen(p.showWhen, getValues()))
303
+ return;
304
+ const result = inner.safeParse(value);
305
+ if (!result.success) {
306
+ // Zod 4's `RefinementCtx.addIssue` accepts a structurally looser
307
+ // shape than `$ZodIssue`; spread into a plain object to satisfy
308
+ // the inferred parameter type.
309
+ for (const issue of result.error.issues) {
310
+ ctx.addIssue({ ...issue });
311
+ }
312
+ }
313
+ })
314
+ .transform((value) => {
315
+ if (!evaluateShowWhen(p.showWhen, getValues()))
316
+ return value;
317
+ const result = inner.safeParse(value);
318
+ return result.success ? result.data : value;
319
+ });
320
+ }
321
+ /** Build a Zod object schema covering every editable property in a resource. */
322
+ export function buildValidationSchema(properties, t, getValues) {
323
+ const shape = {};
324
+ for (const p of properties)
325
+ shape[p.path] = buildPropertySchema(p, t, getValues);
326
+ return z.object(shape);
327
+ }
328
+ /** Sensible empty default per type so RHF stays controlled from first render. */
329
+ export function defaultValueFor(p) {
330
+ if (p.type === 'boolean')
331
+ return false;
332
+ if (p.type === 'json')
333
+ return null;
334
+ if (p.isArray)
335
+ return [];
336
+ return '';
337
+ }
338
+ //# sourceMappingURL=validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@modern-admin/react",
3
+ "version": "0.1.0",
4
+ "description": "React 19 frontend layer for Modern Admin — translation boundary, routing, hooks.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/modern-admin/modern-admin.git",
10
+ "directory": "packages/react"
11
+ },
12
+ "main": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "default": "./dist/index.js"
18
+ }
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "src"
23
+ ],
24
+ "publishConfig": {
25
+ "registry": "https://registry.npmjs.org",
26
+ "access": "public",
27
+ "main": "./dist/index.js",
28
+ "types": "./dist/index.d.ts",
29
+ "exports": {
30
+ ".": {
31
+ "types": "./dist/index.d.ts",
32
+ "default": "./dist/index.js"
33
+ }
34
+ }
35
+ },
36
+ "dependencies": {
37
+ "@hookform/resolvers": "^5.2.2",
38
+ "@modern-admin/core": "0.1.0",
39
+ "@modern-admin/i18n": "0.1.0",
40
+ "@modern-admin/ui": "0.1.0",
41
+ "@tanstack/react-query": "^5.100.9",
42
+ "@tanstack/react-router": "^1.169.2",
43
+ "@tanstack/react-table": "^8.21.3",
44
+ "lucide-react": "^1.14.0",
45
+ "react-hook-form": "^7.75.0",
46
+ "zod": "^4.4.3"
47
+ },
48
+ "devDependencies": {
49
+ "@modern-admin/tsconfig": "0.1.0",
50
+ "@types/bun": "^1.3.13",
51
+ "@types/react": "^19.2.14",
52
+ "@types/react-dom": "^19.2.3",
53
+ "typescript": "^6.0.3"
54
+ },
55
+ "peerDependencies": {
56
+ "react": "^19.2.6",
57
+ "react-dom": "^19.2.6"
58
+ }
59
+ }
@@ -0,0 +1,20 @@
1
+ import type { ActionDescriptor } from './types.js'
2
+ import type { ConfirmOptions, DialogsApi } from './dialogs.js'
3
+
4
+ /**
5
+ * If `action.guard` is set, shows a confirm dialog with that text as the
6
+ * description before proceeding. Returns `true` if the action should run
7
+ * (no guard configured, or user clicked Confirm), `false` if cancelled.
8
+ *
9
+ * Usage at an invoke call-site:
10
+ * if (!await confirmGuard(action, dialogs)) return
11
+ * invoke.mutate(...)
12
+ */
13
+ export async function confirmGuard(
14
+ action: Pick<ActionDescriptor, 'guard'>,
15
+ dialogs: Pick<DialogsApi, 'confirm'>,
16
+ extra?: Omit<ConfirmOptions, 'description'>,
17
+ ): Promise<boolean> {
18
+ if (!action.guard) return true
19
+ return dialogs.confirm({ description: action.guard, ...extra })
20
+ }
@@ -0,0 +1,161 @@
1
+ import * as React from 'react'
2
+ import {
3
+ Button,
4
+ DropdownMenu,
5
+ DropdownMenuContent,
6
+ DropdownMenuItem,
7
+ DropdownMenuLabel,
8
+ DropdownMenuPortal,
9
+ DropdownMenuSeparator,
10
+ DropdownMenuSub,
11
+ DropdownMenuSubContent,
12
+ DropdownMenuSubTrigger,
13
+ DropdownMenuTrigger,
14
+ } from '@modern-admin/ui'
15
+ import { MoreHorizontal, Zap } from 'lucide-react'
16
+ import type { ActionDescriptor, ActionGroup } from './types.js'
17
+
18
+ interface ActionMenuGroupNode {
19
+ key: string
20
+ group: ActionGroup
21
+ items: ActionMenuNode[]
22
+ }
23
+
24
+ type ActionMenuNode =
25
+ | { kind: 'action'; action: ActionDescriptor }
26
+ | { kind: 'group'; group: ActionMenuGroupNode }
27
+
28
+ export interface ActionMenuProps {
29
+ actions: ActionDescriptor[]
30
+ onAction(action: ActionDescriptor): void
31
+ t: (key: string, params?: Record<string, string | number>) => string
32
+ trigger?: React.ReactElement
33
+ menuLabel?: string
34
+ align?: 'start' | 'center' | 'end'
35
+ }
36
+
37
+ const getActionLabel = (action: ActionDescriptor): string =>
38
+ typeof action.custom?.label === 'string' ? action.custom.label : action.name
39
+
40
+ const buildActionMenuTree = (actions: ActionDescriptor[]): ActionMenuNode[] => {
41
+ const root: ActionMenuNode[] = []
42
+ const groups = new Map<string, ActionMenuGroupNode>()
43
+
44
+ const ensureGroup = (target: ActionMenuNode[], path: ActionGroup[], depth: number): ActionMenuNode[] => {
45
+ if (depth >= path.length) return target
46
+ const segment = path[depth]!
47
+ const key = path
48
+ .slice(0, depth + 1)
49
+ .map((item) => item.name)
50
+ .join('>')
51
+ let group = groups.get(key)
52
+ if (!group) {
53
+ group = { key, group: segment, items: [] }
54
+ groups.set(key, group)
55
+ target.push({ kind: 'group', group })
56
+ }
57
+ return ensureGroup(group.items, path, depth + 1)
58
+ }
59
+
60
+ for (const action of actions) {
61
+ const nesting = action.nesting ?? []
62
+ const target = nesting.length > 0 ? ensureGroup(root, nesting, 0) : root
63
+ target.push({ kind: 'action', action })
64
+ }
65
+
66
+ return root
67
+ }
68
+
69
+ const renderNodes = (
70
+ nodes: ActionMenuNode[],
71
+ onAction: (action: ActionDescriptor) => void,
72
+ ): React.ReactNode =>
73
+ nodes.map((node) => {
74
+ if (node.kind === 'action') {
75
+ return (
76
+ <DropdownMenuItem
77
+ key={node.action.name}
78
+ onSelect={() => onAction(node.action)}
79
+ >
80
+ <Zap className="size-4" /> {getActionLabel(node.action)}
81
+ </DropdownMenuItem>
82
+ )
83
+ }
84
+ return (
85
+ <DropdownMenuSub key={node.group.key}>
86
+ <DropdownMenuSubTrigger>
87
+ {node.group.group.name}
88
+ </DropdownMenuSubTrigger>
89
+ <DropdownMenuPortal>
90
+ <DropdownMenuSubContent>
91
+ {renderNodes(node.group.items, onAction)}
92
+ </DropdownMenuSubContent>
93
+ </DropdownMenuPortal>
94
+ </DropdownMenuSub>
95
+ )
96
+ })
97
+
98
+ export function ActionMenuItems({
99
+ actions,
100
+ onAction,
101
+ }: {
102
+ actions: ActionDescriptor[]
103
+ onAction(action: ActionDescriptor): void
104
+ }): React.ReactElement | null {
105
+ const nodes = React.useMemo(() => buildActionMenuTree(actions), [actions])
106
+ if (nodes.length === 0) return null
107
+ return <>{renderNodes(nodes, onAction)}</>
108
+ }
109
+
110
+ export function ActionMenu({
111
+ actions,
112
+ onAction,
113
+ t,
114
+ trigger,
115
+ menuLabel,
116
+ align = 'end',
117
+ }: ActionMenuProps): React.ReactElement | null {
118
+ const nodes = React.useMemo(() => buildActionMenuTree(actions), [actions])
119
+ if (nodes.length === 0) return null
120
+
121
+ return (
122
+ <DropdownMenu>
123
+ <DropdownMenuTrigger asChild>
124
+ {trigger ?? (
125
+ <Button variant="outline" size="sm">
126
+ <Zap className="size-4" />
127
+ {t('common:actions')}
128
+ </Button>
129
+ )}
130
+ </DropdownMenuTrigger>
131
+ <DropdownMenuContent align={align}>
132
+ <DropdownMenuLabel>{menuLabel ?? t('common:actions')}</DropdownMenuLabel>
133
+ <DropdownMenuSeparator />
134
+ <ActionMenuItems actions={actions} onAction={onAction} />
135
+ </DropdownMenuContent>
136
+ </DropdownMenu>
137
+ )
138
+ }
139
+
140
+ export function MoreActionsMenu({
141
+ actions,
142
+ onAction,
143
+ t,
144
+ menuLabel,
145
+ }: Omit<ActionMenuProps, 'trigger' | 'align'>): React.ReactElement | null {
146
+ if (actions.length === 0) return null
147
+ return (
148
+ <ActionMenu
149
+ actions={actions}
150
+ onAction={onAction}
151
+ t={t}
152
+ menuLabel={menuLabel}
153
+ trigger={(
154
+ <Button variant="ghost" size="icon" className="h-8 w-8">
155
+ <MoreHorizontal className="size-4" />
156
+ <span className="sr-only">{t('common:openMenu')}</span>
157
+ </Button>
158
+ )}
159
+ />
160
+ )
161
+ }