@anri1214/dynamic-forms-mui 0.1.7

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.
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 AI Hub
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,403 @@
1
+ # @ai-hub/dynamic-forms-mui
2
+
3
+ Type-safe declarative form builder for React with Material-UI.
4
+
5
+ Define your forms with a JSON schema — get fully validated, accessible MUI forms with zero boilerplate.
6
+
7
+ ## Features
8
+
9
+ - **JSON-driven forms** — describe fields, sections, and validation in a single schema object
10
+ - **15 built-in field types** — text, email, password, textarea, number, select, multiselect, autocomplete, checkbox, switch, radio, date, date-range, file upload
11
+ - **Dynamic validation** — Zod schemas generated at runtime from your config; async validation supported
12
+ - **Conditional visibility** — show/hide fields based on other field values
13
+ - **Dynamic options** — load select/radio/autocomplete options from an API with dependency support
14
+ - **Async search** — autocomplete fields with server-side search via `dependsOnInput`
15
+ - **Customizable section styles** — override spacing and styles per section slot via `sectionSlotProps`
16
+ - **Fully customizable UI** — replace any or all default MUI components via Context
17
+ - **API client injection** — bring your own HTTP client (Axios, fetch, etc.)
18
+ - **Type-safe** — full TypeScript coverage for schemas, fields, and components
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ npm install @ai-hub/dynamic-forms-mui
24
+ ```
25
+
26
+ ### Peer Dependencies
27
+
28
+ ```bash
29
+ npm install react react-dom @mui/material @mui/icons-material @emotion/react @emotion/styled react-hook-form zod @tanstack/react-query @base-ui/react
30
+ ```
31
+
32
+ ## Quick Start
33
+
34
+ ### 1. Wrap your app with `DynamicFormsProvider`
35
+
36
+ ```tsx
37
+ import { DynamicFormsProvider } from '@ai-hub/dynamic-forms-mui'
38
+ import type { ApiClient } from '@ai-hub/dynamic-forms-mui'
39
+
40
+ // Adapt your HTTP client to the ApiClient interface
41
+ const apiClient: ApiClient = {
42
+ get: (url, config) => fetch(url).then((r) => r.json()),
43
+ }
44
+
45
+ function App() {
46
+ return (
47
+ <DynamicFormsProvider
48
+ apiClient={apiClient}
49
+ onError={(msg) => console.error(msg)}
50
+ >
51
+ {/* your app */}
52
+ </DynamicFormsProvider>
53
+ )
54
+ }
55
+ ```
56
+
57
+ ### 2. Define a form schema
58
+
59
+ ```tsx
60
+ import type { FormSchema } from '@ai-hub/dynamic-forms-mui'
61
+
62
+ const schema: FormSchema = {
63
+ sections: [
64
+ {
65
+ id: 'contact',
66
+ title: 'Contact Info',
67
+ fields: ['name', 'email'],
68
+ },
69
+ ],
70
+ fields: {
71
+ name: {
72
+ type: 'text',
73
+ label: 'Full Name',
74
+ placeholder: 'John Doe',
75
+ validation: { required: true, minLength: 2 },
76
+ },
77
+ email: {
78
+ type: 'email',
79
+ label: 'Email',
80
+ validation: { required: true },
81
+ },
82
+ },
83
+ }
84
+ ```
85
+
86
+ ### 3. Render the form
87
+
88
+ ```tsx
89
+ import { DynamicForm } from '@ai-hub/dynamic-forms-mui'
90
+ import type { LabelMap } from '@ai-hub/dynamic-forms-mui'
91
+
92
+ function ContactPage() {
93
+ const handleSubmit = (data: Record<string, unknown>, labelMap: LabelMap) => {
94
+ console.log('Form data:', data)
95
+ console.log('Labels:', labelMap) // { country: { 'US': 'United States', ... }, ... }
96
+ }
97
+
98
+ return (
99
+ <DynamicForm schema={schema} onSubmit={handleSubmit}>
100
+ {({ isValid, isSubmitting }) => (
101
+ <button type="submit" disabled={!isValid || isSubmitting}>
102
+ Submit
103
+ </button>
104
+ )}
105
+ </DynamicForm>
106
+ )
107
+ }
108
+ ```
109
+
110
+ ## Field Types
111
+
112
+ | Type | Config Interface | Description |
113
+ |------|-----------------|-------------|
114
+ | `text` | `TextFieldConfig` | Standard text input |
115
+ | `email` | `TextFieldConfig` | Email input with format validation |
116
+ | `password` | `TextFieldConfig` | Password input |
117
+ | `textarea` | `TextFieldConfig` | Multi-line text input |
118
+ | `number` | `NumberFieldConfig` | Numeric input with min/max/step |
119
+ | `select` | `SelectFieldConfig` | Single-value dropdown |
120
+ | `multiselect` | `SelectFieldConfig` | Multi-value dropdown with chips |
121
+ | `autocomplete` | `AutocompleteFieldConfig` | Single-value autocomplete with async search |
122
+ | `checkbox` | `CheckboxFieldConfig` | Boolean checkbox |
123
+ | `switch` | `CheckboxFieldConfig` | Toggle switch |
124
+ | `radio` | `RadioFieldConfig` | Radio button group |
125
+ | `date` | `DateFieldConfig` | Date picker |
126
+ | `dateRange` | `DateFieldConfig` | Date range picker |
127
+ | `file` | `FileFieldConfig` | File upload |
128
+
129
+ ## Validation
130
+
131
+ Validation rules are defined per-field in the `validation` property:
132
+
133
+ ```ts
134
+ validation: {
135
+ required: true,
136
+ minLength: 2,
137
+ maxLength: 100,
138
+ pattern: '^[a-zA-Z]+$',
139
+ patternMessage: 'Only letters allowed',
140
+
141
+ // Async validation (e.g. check username availability)
142
+ asyncValidation: {
143
+ apiUrl: '/api/check-username',
144
+ debounceMs: 500,
145
+ message: 'Username is already taken',
146
+ },
147
+ }
148
+ ```
149
+
150
+ ## Conditional Fields
151
+
152
+ Show or hide fields based on other values:
153
+
154
+ ```ts
155
+ fields: {
156
+ hasCompany: {
157
+ type: 'checkbox',
158
+ label: 'I have a company',
159
+ },
160
+ companyName: {
161
+ type: 'text',
162
+ label: 'Company Name',
163
+ showIf: { field: 'hasCompany', operator: 'equals', value: true },
164
+ },
165
+ }
166
+ ```
167
+
168
+ Supported operators: `equals`, `notEquals`, `in`, `notIn`, `isEmpty`, `isNotEmpty`, `gt`, `gte`, `lt`, `lte`. Conditions can be combined with `and` / `or`.
169
+
170
+ ## Dynamic Options
171
+
172
+ Load select/radio/autocomplete options from an API:
173
+
174
+ ```ts
175
+ country: {
176
+ type: 'select',
177
+ label: 'Country',
178
+ optionsSource: {
179
+ apiUrl: '/api/countries',
180
+ valueKey: 'code',
181
+ labelKey: 'name',
182
+ },
183
+ },
184
+ city: {
185
+ type: 'select',
186
+ label: 'City',
187
+ optionsSource: {
188
+ apiUrl: '/api/cities',
189
+ queryParams: [{ key: 'country', dependsOn: 'country' }],
190
+ valueKey: 'id',
191
+ labelKey: 'name',
192
+ },
193
+ },
194
+ ```
195
+
196
+ When `country` changes, `city` options reload automatically.
197
+
198
+ ### Async Search (Autocomplete)
199
+
200
+ Use `dependsOnInput` in `queryParams` to send the user's typed text as a search query:
201
+
202
+ ```ts
203
+ customer: {
204
+ type: 'autocomplete',
205
+ label: 'Customer',
206
+ optionsSource: {
207
+ apiUrl: '/api/customers',
208
+ queryParams: [{ key: 'search', dependsOnInput: true }],
209
+ valueKey: 'id',
210
+ labelKey: 'name',
211
+ },
212
+ },
213
+ ```
214
+
215
+ The input is debounced (300ms) before triggering the API call.
216
+
217
+ ### Pre-selected Options (`initialOptions`)
218
+
219
+ When editing a form with pre-existing values, the API options may not have loaded yet. Use `initialOptions` to display the current selection immediately:
220
+
221
+ ```ts
222
+ companies: {
223
+ type: 'multiselect',
224
+ label: 'Companies',
225
+ optionsSource: { apiUrl: '/api/companies', valueKey: 'id', labelKey: 'name' },
226
+ initialOptions: [
227
+ { value: '1', label: 'Acme Corp' },
228
+ { value: '2', label: 'Globex' },
229
+ ],
230
+ },
231
+ ```
232
+
233
+ `initialOptions` are merged with API results — duplicates are deduplicated automatically.
234
+
235
+ ## Customizing UI Components
236
+
237
+ Replace any default MUI component with your own:
238
+
239
+ ```tsx
240
+ import { DynamicFormsProvider } from '@ai-hub/dynamic-forms-mui'
241
+ import type { DFTextFieldProps } from '@ai-hub/dynamic-forms-mui'
242
+
243
+ function MyTextField(props: DFTextFieldProps) {
244
+ return <input className="my-input" {...props} />
245
+ }
246
+
247
+ <DynamicFormsProvider
248
+ apiClient={apiClient}
249
+ components={{ TextField: MyTextField }}
250
+ >
251
+ {/* Only TextField is replaced; the other 9 stay default */}
252
+ </DynamicFormsProvider>
253
+ ```
254
+
255
+ ### Overridable Components
256
+
257
+ `TextField` · `NumberField` · `Select` · `Autocomplete` · `Checkbox` · `Switch` · `RadioGroup` · `DatePicker` · `DateRangePicker` · `FileUpload`
258
+
259
+ ## Section Styling (`sectionSlotProps`)
260
+
261
+ Customize spacing and styles for section elements via `sectionSlotProps`. Can be set globally via the provider or per-form (form-level merges on top of provider-level):
262
+
263
+ ```tsx
264
+ // Global defaults for all forms
265
+ <DynamicFormsProvider
266
+ apiClient={apiClient}
267
+ sectionSlotProps={{
268
+ root: { sx: { mb: 2 } },
269
+ content: { sx: { gap: 1 } },
270
+ }}
271
+ >
272
+ {/* Per-form override — merges with provider defaults */}
273
+ <DynamicForm
274
+ schema={schema}
275
+ onSubmit={handleSubmit}
276
+ sectionSlotProps={{
277
+ title: { sx: { mb: 1, fontSize: '1rem' } },
278
+ description: { sx: { mb: 1 } },
279
+ }}
280
+ >
281
+ {({ isValid }) => <button type="submit" disabled={!isValid}>Save</button>}
282
+ </DynamicForm>
283
+ </DynamicFormsProvider>
284
+ ```
285
+
286
+ Available slots: `root` (section wrapper), `title` (section heading), `description` (section description), `content` (fields container). Custom `sx` is merged on top of defaults. When both provider and form define the same slot, `sx` values are merged with form-level taking priority.
287
+
288
+ Section `title` is optional — omit it from the schema to render sections without a heading.
289
+
290
+ ## Component Size
291
+
292
+ By default, all form inputs use MUI's `"medium"` size. You can set `size` globally via the provider or per-form:
293
+
294
+ ```tsx
295
+ // All forms render with small inputs
296
+ <DynamicFormsProvider apiClient={apiClient} size="small">
297
+ {/* ... */}
298
+ </DynamicFormsProvider>
299
+ ```
300
+
301
+ Per-form override (takes priority over the provider):
302
+
303
+ ```tsx
304
+ <DynamicForm schema={schema} onSubmit={handleSubmit} size="small" />
305
+ ```
306
+
307
+ Supported values: `"small"` | `"medium"` (default).
308
+
309
+ ## Per-Form API Client Override
310
+
311
+ Override the global `apiClient` for a specific form:
312
+
313
+ ```tsx
314
+ <DynamicForm
315
+ schema={schema}
316
+ onSubmit={handleSubmit}
317
+ apiClient={differentApiClient}
318
+ />
319
+ ```
320
+
321
+ ## Custom Field Types
322
+
323
+ Register custom field components for new field types:
324
+
325
+ ```tsx
326
+ import { registerFieldComponent } from '@ai-hub/dynamic-forms-mui'
327
+
328
+ registerFieldComponent('rating', MyRatingField)
329
+ ```
330
+
331
+ ## Label Map (`onSubmit` second argument)
332
+
333
+ The `onSubmit` callback receives a second argument — `labelMap` of type `LabelMap` — a nested record mapping field names to their loaded option labels:
334
+
335
+ ```ts
336
+ type LabelMap = Record<string, Record<string | number, string>>
337
+ // { [fieldName]: { [optionValue]: optionLabel } }
338
+ ```
339
+
340
+ This is useful for multi-step forms where you need to display human-readable labels in a review step without making extra API calls:
341
+
342
+ ```tsx
343
+ const [formData, setFormData] = useState<Record<string, unknown>>({})
344
+ const [labels, setLabels] = useState<LabelMap>({})
345
+
346
+ const handleSubmit = (data: Record<string, unknown>, labelMap: LabelMap) => {
347
+ setFormData(data) // { country: 'US', city: '42' }
348
+ setLabels(labelMap) // { country: { 'US': 'United States', 'UK': 'United Kingdom', ... }, city: { '42': 'New York', ... } }
349
+ }
350
+
351
+ // In the review step:
352
+ const countryLabel = labels.country?.[formData.country as string] // "United States"
353
+ ```
354
+
355
+ The label map includes all currently loaded options for fields with `options`, `optionsSource`, or `initialOptions`. Fields without options (text, number, checkbox, etc.) are not included.
356
+
357
+ ## Exports
358
+
359
+ ### Components
360
+ - `DynamicFormsProvider` — context provider (wrap your app)
361
+ - `DynamicForm` — main form renderer
362
+ - `FormErrorBoundary` — error boundary for forms
363
+
364
+ ### Hooks
365
+ - `useDynamicFormsContext()` — access apiClient, components, notifications
366
+ - `useFieldVisibility(schema)` — get visible field set
367
+ - `useOptionsSource(source, staticOptions, initialOptions?)` — load options from API with async search support
368
+ - `useAsyncValidation(fieldName, config)` — debounced async validation
369
+
370
+ ### Utils
371
+ - `buildZodSchema` — generate Zod schema from FormSchema
372
+ - `evaluateCondition` — evaluate showIf conditions
373
+ - `extractDefaultValues` — extract defaults from schema
374
+ - `registerFieldComponent` — register custom field types
375
+
376
+ ### Types
377
+ - `FormSchema`, `Section`, `FieldConfig`, `FieldType`, `FormMode`, `SectionSlotProps`
378
+ - `AutocompleteFieldConfig`, `SelectFieldConfig`, `TextFieldConfig`, etc.
379
+ - `ApiClient`, `UIComponents`, `NotificationCallbacks`, `ComponentSize`
380
+ - `LabelMap` — nested record of resolved option labels (`Record<fieldName, Record<value, label>>`)
381
+ - `DFTextFieldProps`, `DFAutocompleteProps`, `DFSelectProps`, `DFCheckboxProps`, etc.
382
+
383
+ ## AI Assistant Integration (MCP)
384
+
385
+ This library has a companion MCP server — **@ai-hub/dynamic-forms-mui-mcp** — that lets AI assistants (Cursor, Claude Code, VS Code Copilot) understand the `FormSchema` structure and help generate correct form configurations.
386
+
387
+ ```bash
388
+ npx @ai-hub/dynamic-forms-mui-mcp
389
+ ```
390
+
391
+ The MCP server provides:
392
+ - **Schema validation** — validate JSON against the FormSchema spec
393
+ - **Field type docs** — descriptions, props, and examples for all 14 field types
394
+ - **Code generation** — generate FormSchema JSON from natural language
395
+ - **Backend endpoint generation** — produce API endpoints in any language/framework
396
+ - **Version-aware** — serves documentation matching your installed library version
397
+
398
+ See [@ai-hub/dynamic-forms-mui-mcp README](../dynamic-forms-mui-mcp/README.md) for setup instructions.
399
+
400
+ ## License
401
+
402
+ MIT
403
+
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("react/jsx-runtime"),y=require("react"),m=require("@mui/material"),L=require("@base-ui/react/number-field"),je=require("@mui/icons-material/KeyboardArrowUp"),Ce=require("@mui/icons-material/KeyboardArrowDown"),Se=require("@mui/x-date-pickers/DatePicker"),De=require("@mui/x-date-pickers/DateTimePicker"),G=require("@mui/icons-material"),x=require("react-hook-form"),F=require("zod"),qe=require("@tanstack/react-query");function we(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const r in e)if(r!=="default"){const n=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,n.get?n:{enumerable:!0,get:()=>e[r]})}}return t.default=e,Object.freeze(t)}const Te=we(y),H=y.createContext(null);function R(){const e=y.useContext(H);if(!e)throw new Error("useDynamicFormsContext must be used within a DynamicFormsProvider. Wrap your app (or the part that uses DynamicForm) with <DynamicFormsProvider>.");return e}function Re({variant:e="outlined",...t}){return u.jsx(m.TextField,{variant:e,fullWidth:!0,...t})}function te(e){return null}te.muiName="Input";function ke({label:e,error:t,helperText:r,placeholder:n,size:a="medium",ref:i,...s}){const l=Te.useId(),c=a==="small"?"small":"medium";return u.jsxs(L.NumberField.Root,{...s,render:(o,d)=>u.jsx(m.FormControl,{size:a,ref:o.ref,disabled:d.disabled,required:d.required,error:t,variant:"outlined",fullWidth:!0,children:o.children}),children:[u.jsx(te,{...s}),u.jsx(m.InputLabel,{htmlFor:l,children:e}),u.jsx(L.NumberField.Input,{id:l,render:(o,d)=>u.jsx(m.OutlinedInput,{label:e,placeholder:n,size:a,inputRef:i||o.ref,value:d.inputValue,onBlur:o.onBlur,onChange:o.onChange,onKeyUp:o.onKeyUp,onKeyDown:o.onKeyDown,onFocus:o.onFocus,slotProps:{input:o},endAdornment:u.jsxs(m.InputAdornment,{position:"end",sx:{flexDirection:"column",maxHeight:"unset",alignSelf:"stretch",borderLeft:"1px solid",borderColor:"divider",ml:0,"& button":{py:0,flex:1,borderRadius:.5}},children:[u.jsx(L.NumberField.Increment,{render:u.jsx(m.IconButton,{size:c,"aria-label":"Increase"}),children:u.jsx(je,{fontSize:c,sx:{transform:"translateY(2px)"}})}),u.jsx(L.NumberField.Decrement,{render:u.jsx(m.IconButton,{size:c,"aria-label":"Decrease"}),children:u.jsx(Ce,{fontSize:c,sx:{transform:"translateY(-2px)"}})})]}),sx:{pr:0}})}),r&&u.jsx(m.FormHelperText,{sx:{ml:0},children:r})]})}function ze({options:e,onChange:t,ref:r,size:n,...a}){return u.jsx(m.Select,{...a,size:n,onChange:t,children:e.map(i=>u.jsx(m.MenuItem,{value:i.value,disabled:i.disabled,children:i.label},i.value))})}function Ee({label:e,placeholder:t,error:r,helperText:n,required:a,size:i,...s}){return u.jsx(m.Autocomplete,{...s,size:i,getOptionLabel:l=>typeof l=="string"?l:l.label,renderInput:l=>u.jsx(m.TextField,{...l,label:e,placeholder:t,error:r,helperText:n,required:a})})}function Ie({label:e,error:t,helperText:r,size:n,ref:a,...i}){return u.jsxs(m.FormControl,{error:t,size:n,children:[u.jsx(m.FormControlLabel,{control:u.jsx(m.Checkbox,{...i,size:n}),label:e}),r&&u.jsx(m.FormHelperText,{children:r})]})}function Ae({label:e,error:t,helperText:r,size:n,ref:a,...i}){return u.jsxs(m.FormControl,{error:t,size:n,children:[u.jsx(m.FormControlLabel,{control:u.jsx(m.Switch,{...i,size:n}),label:e}),r&&u.jsx(m.FormHelperText,{children:r})]})}function Pe({label:e,options:t,error:r,helperText:n,disabled:a,required:i,size:s,...l}){return u.jsxs(m.FormControl,{component:"fieldset",error:r,disabled:a,required:i,size:s,children:[e&&u.jsx(m.FormLabel,{component:"legend",children:e}),u.jsx(m.RadioGroup,{...l,children:t.map(c=>u.jsx(m.FormControlLabel,{value:c.value,control:u.jsx(m.Radio,{size:s}),label:c.label},c.value))}),n&&u.jsx(m.FormHelperText,{children:n})]})}function W({includeTime:e,error:t,helperText:r,required:n,format:a,size:i,ref:s,...l}){const c=e?De.DateTimePicker:Se.DatePicker;return u.jsx(c,{...l,format:a,inputRef:s,slotProps:{textField:{error:t,helperText:r,required:n,fullWidth:!0,size:i}}})}function Ve({value:e,onChange:t,label:r,disabled:n,error:a,helperText:i,minDate:s,maxDate:l,required:c,includeTime:o,format:d,size:f}){const h=j=>{t?.({start:j,end:e?.end??null})},p=j=>{t?.({start:e?.start??null,end:j})},v=o?"Start Date/Time":"Start Date",b=o?"End Date/Time":"End Date";return u.jsxs(m.Box,{children:[r&&u.jsxs(m.Typography,{variant:"body2",sx:{mb:1,color:a?"error.main":"text.secondary"},children:[r,c&&u.jsx("span",{style:{color:"inherit"},children:" *"})]}),u.jsxs(m.Box,{sx:{display:"flex",gap:2},children:[u.jsx(W,{label:v,includeTime:o,format:d,value:e?.start??null,onChange:h,disabled:n,minDate:s,maxDate:e?.end??l,error:a,size:f}),u.jsx(W,{label:b,includeTime:o,format:d,value:e?.end??null,onChange:p,disabled:n,minDate:e?.start??s,maxDate:l,error:a,size:f})]}),i&&u.jsx(m.Typography,{variant:"caption",sx:{mt:.5,ml:1.75,color:a?"error.main":"text.secondary"},children:i})]})}function re(e){if(e===0)return"0 KB";const t=1024,r=t*1024,n=r*1024;return e>=n?`${(e/n).toFixed(2)} GB`:e>=r?`${(e/r).toFixed(2)} MB`:`${(e/t).toFixed(2)} KB`}function Oe({value:e,onChange:t,onBlur:r,multiple:n,accept:a,label:i,disabled:s,error:l,helperText:c,required:o,ref:d}){const[f,h]=y.useState(!1),p=y.useRef(null),v=y.useId(),b=y.useId(),j=g=>{g.preventDefault(),g.stopPropagation(),s||h(!0)},S=g=>{g.preventDefault(),g.stopPropagation(),h(!1)},w=g=>{g.preventDefault(),g.stopPropagation()},D=(g,T)=>{const I=new Set(g.map(z=>`${z.name}-${z.size}`));return T.filter(z=>!I.has(`${z.name}-${z.size}`))},C=g=>{if(g.preventDefault(),g.stopPropagation(),h(!1),s)return;const T=Array.from(g.dataTransfer.files);if(T.length!==0)if(n){const I=Array.isArray(e)?e:[],z=D(I,T);t?.([...I,...z])}else t?.(T[0]||null)},q=g=>{const T=g.target.files;if(!T||T.length===0)return;const I=Array.from(T);if(n){const z=Array.isArray(e)?e:[],Fe=D(z,I);t?.([...z,...Fe])}else t?.(I[0]||null);p.current&&(p.current.value="")},k=()=>{t?.(null),p.current&&(p.current.value="")},P=g=>{if(!Array.isArray(e))return;const T=e.filter((I,z)=>z!==g);t?.(T.length>0?T:null),T.length===0&&p.current&&(p.current.value="")},E=g=>{s||(g.key==="Enter"||g.key===" ")&&(g.preventDefault(),p.current?.click())},$=e?Array.isArray(e)?e:[e]:[],be=()=>l?"error.main":f?"primary.main":"divider",ve=()=>s?"divider":l?"error.main":"primary.main";return u.jsxs(m.FormControl,{fullWidth:!0,error:l,children:[i&&u.jsxs(m.Typography,{component:"label",htmlFor:v,variant:"body2",sx:{mb:1,display:"block",fontWeight:500},children:[i,o&&u.jsx(m.Typography,{component:"span",sx:{ml:.5},children:"*"})]}),u.jsxs(m.Box,{role:"button",tabIndex:s?-1:0,"aria-label":`Upload ${n?"files":"file"}${i?` for ${i}`:""}`,"aria-disabled":s,"aria-invalid":l,"aria-required":o,"aria-describedby":c?b:void 0,onDragEnter:j,onDragOver:w,onDragLeave:S,onDrop:C,onKeyDown:E,sx:{border:2,borderStyle:"dashed",borderColor:be(),borderRadius:1,p:3,textAlign:"center",bgcolor:f?"action.hover":"background.paper",cursor:s?"not-allowed":"pointer",transition:"all 0.2s ease-in-out","&:hover":{borderColor:ve(),bgcolor:s?"background.paper":"action.hover"}},onClick:()=>!s&&p.current?.click(),children:[u.jsx(G.CloudUpload,{sx:{fontSize:48,color:l?"error.main":"primary.main",mb:1}}),u.jsxs(m.Typography,{variant:"body1",color:s?"text.disabled":"text.primary",children:["Drag & drop ",n?"files":"a file"," or click to select"]}),a&&u.jsxs(m.Typography,{variant:"caption",color:"text.secondary",sx:{mt:.5,display:"block"},children:["Accepted types: ",a]})]}),u.jsx("input",{id:v,ref:g=>{p.current=g,typeof d=="function"?d(g):d&&(d.current=g)},type:"file",accept:a,multiple:n,onChange:q,onBlur:r,style:{display:"none"},disabled:s,required:o,"aria-invalid":l,"aria-required":o,"aria-describedby":c?b:void 0}),$.length>0&&u.jsx(m.Box,{sx:{mt:2},role:"status","aria-live":"polite","aria-label":`${$.length} ${$.length===1?"file":"files"} selected`,children:$.map((g,T)=>u.jsxs(m.Box,{sx:{display:"flex",alignItems:"center",justifyContent:"space-between",p:1.5,mb:1,border:1,borderColor:"divider",borderRadius:1,bgcolor:"background.paper","&:last-child":{mb:0}},children:[u.jsxs(m.Box,{sx:{flex:1,minWidth:0},children:[u.jsx(m.Typography,{variant:"body2",sx:{fontWeight:500,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:g.name}),u.jsx(m.Typography,{variant:"caption",color:"text.secondary",children:re(g.size)})]}),u.jsx(m.IconButton,{size:"small",onClick:I=>{I.stopPropagation(),n?P(T):k()},disabled:s,"aria-label":`Remove ${g.name}`,sx:{ml:1},children:u.jsx(G.Close,{fontSize:"small"})})]},`${g.name}-${g.size}-${T}`))}),c&&u.jsx(m.FormHelperText,{id:b,children:c})]})}function $e(){return{TextField:Re,NumberField:ke,Select:ze,Autocomplete:Ee,Checkbox:Ie,Switch:Ae,RadioGroup:Pe,DatePicker:W,DateRangePicker:Ve,FileUpload:Oe}}function Le({apiClient:e,components:t,size:r,sectionSlotProps:n,onError:a,children:i}){const l={...$e(),...t},o={apiClient:e,components:l,notifications:{onError:a??console.error},size:r,sectionSlotProps:n};return u.jsx(H,{value:o,children:i})}function A(e,t){return"and"in e?e.and.every(r=>A(r,t)):"or"in e?e.or.some(r=>A(r,t)):Ne(e,t)}function Ne(e,t){const r=t[e.field],n=e.value;switch(e.operator){case"equals":return r===n;case"notEquals":return r!==n;case"in":return Array.isArray(n)&&n.includes(r);case"notIn":return Array.isArray(n)&&!n.includes(r);case"isEmpty":return M(r);case"isNotEmpty":return!M(r);case"gt":return typeof r=="number"&&typeof n=="number"&&r>n;case"gte":return typeof r=="number"&&typeof n=="number"&&r>=n;case"lt":return typeof r=="number"&&typeof n=="number"&&r<n;case"lte":return typeof r=="number"&&typeof n=="number"&&r<=n;default:return!1}}function M(e){return e==null?!0:typeof e=="string"?e.trim()==="":Array.isArray(e)?e.length===0:!1}function Q(e){if(e==null||e==="")return;if(e instanceof Date)return isNaN(e.getTime())||e.getTime()===0?void 0:e;const t=new Date(e);if(!(isNaN(t.getTime())||t.getTime()===0))return t}function ne(e){const t={};for(const[n,a]of Object.entries(e.fields))t[n]=Be(a);let r=F.z.object(t);return r=Ue(r,e),r}function Be(e){const{type:t,validation:r={}}=e;let n;switch(t){case"number":n=Me(r);break;case"checkbox":case"switch":n=F.z.boolean();break;case"date":{let a=F.z.preprocess(Q,F.z.date({message:r.messages?.invalidDate||"Invalid date"}).optional());r.required&&(a=a.refine(i=>i!=null&&i instanceof Date&&!isNaN(i.getTime())&&i.getTime()!==0,{message:r.messages?.required||"Date is required"})),r.minDate&&(a=a.refine(i=>!i||i>=r.minDate,{message:r.messages?.minDate||`Date must be after ${r.minDate.toLocaleDateString()}`})),r.maxDate&&(a=a.refine(i=>!i||i<=r.maxDate,{message:r.messages?.maxDate||`Date must be before ${r.maxDate.toLocaleDateString()}`})),n=a;break}case"dateRange":{const a=F.z.preprocess(Q,F.z.date({message:r.messages?.invalidDate||"Invalid date"}).optional().nullable());let i=F.z.object({start:a,end:a});r.required&&(i=i.refine(s=>{const l=s.start!==null&&s.start!==void 0&&s.start instanceof Date&&!isNaN(s.start.getTime())&&s.start.getTime()!==0,c=s.end!==null&&s.end!==void 0&&s.end instanceof Date&&!isNaN(s.end.getTime())&&s.end.getTime()!==0;return l&&c},{message:r.messages?.required||"Both start and end dates are required",path:["start"]})),i=i.refine(s=>!s.start||!s.end?!0:s.start<=s.end,{message:r.messages?.invalidRange||"End date must be after start date",path:["end"]}),n=i;break}case"autocomplete":case"multiselect":n=t==="autocomplete"?F.z.string():F.z.array(F.z.string()),r.required&&(t==="autocomplete"?n=n.min(1,r.messages?.required||"Required"):n=n.min(1,r.messages?.required||"Select at least one option"));break;case"file":{const a=e;let i=a.multiple?F.z.array(F.z.instanceof(File)).nullable():F.z.instanceof(File).nullable();r.required&&(a.multiple?i=i.refine(s=>s===null?!1:s.length>=1,{message:r.messages?.required||"At least one file is required"}):i=i.refine(s=>s!=null,{message:r.messages?.required||"File is required"})),r.maxSize&&(a.multiple?i=i.refine(s=>s===null?!0:s.every(l=>l?.size<=r.maxSize),{message:r.messages?.maxSize||`File size must be less than ${r.maxSize/1024/1024}MB`}):i=i.refine(s=>s===null?!0:s.size<=r.maxSize,{message:r.messages?.maxSize||`File size must be less than ${r.maxSize/1024/1024}MB`})),a.accept&&(a.multiple?i=i.refine(s=>s===null?!0:s.every(l=>a.accept.split(",").map(o=>o.trim()).some(o=>o.startsWith(".")?l.name.toLowerCase().endsWith(o.toLowerCase()):l.type.match(o.replace("*",".*")))),{message:r.messages?.fileType||`Invalid file type. Accepted: ${a.accept}`}):i=i.refine(s=>{if(s===null)return!0;const l=s;return a.accept.split(",").map(o=>o.trim()).some(o=>o.startsWith(".")?l.name?.toLowerCase().endsWith(o.toLowerCase()):l.type?.match(o.replace("*",".*")))},{message:r.messages?.fileType||`Invalid file type. Accepted: ${a.accept}`})),r.minFiles&&a.multiple&&(i=i.refine(s=>s===null?!1:s.length>=r.minFiles,{message:r.messages?.minFiles||`At least ${r.minFiles} file(s) required`})),r.maxFiles&&a.multiple&&(i=i.refine(s=>s===null?!0:s.length<=r.maxFiles,{message:r.messages?.maxFiles||`Max ${r.maxFiles} file(s) allowed`})),n=i;break}default:n=_e(r)}return!r.required&&!r.requiredIf&&(n=n.optional()),n}function _e(e){if(e.email)return e.required?F.z.string().min(1,{message:e.messages?.required||"Email is required"}).pipe(F.z.email({message:e.messages?.email||"Invalid email"})):F.z.union([F.z.literal(""),F.z.email({message:e.messages?.email||"Invalid email"})]);if(e.url)return e.required?F.z.string().min(1,{message:e.messages?.required||"URL is required"}).pipe(F.z.url({message:e.messages?.url||"Invalid URL"})):F.z.union([F.z.literal(""),F.z.url({message:e.messages?.url||"Invalid URL"})]);let t=F.z.string();if(e.required&&(t=t.min(1,e.messages?.required||"Required")),e.minLength&&(t=t.min(e.minLength,e.messages?.minLength||`Min ${e.minLength} characters`)),e.maxLength&&(t=t.max(e.maxLength,e.messages?.maxLength||`Max ${e.maxLength} characters`)),e.pattern&&(t=t.regex(new RegExp(e.pattern),e.messages?.pattern||"Invalid format")),e.hasUppercase){const r=e.hasUppercase===!0?1:e.hasUppercase,n=new RegExp(`(?=(.*[A-Z]){${r}})`);t=t.regex(n,e.messages?.hasUppercase||`At least ${r} uppercase letter(s)`)}if(e.hasLowercase){const r=e.hasLowercase===!0?1:e.hasLowercase,n=new RegExp(`(?=(.*[a-z]){${r}})`);t=t.regex(n,e.messages?.hasLowercase||`At least ${r} lowercase letter(s)`)}if(e.hasNumber){const r=e.hasNumber===!0?1:e.hasNumber,n=new RegExp(`(?=(.*[0-9]){${r}})`);t=t.regex(n,e.messages?.hasNumber||`At least ${r} number(s)`)}if(e.hasSpecialChar){const r=e.hasSpecialChar===!0?1:e.hasSpecialChar,n=new RegExp(`(?=(.*[!@#$%^&*()_+\\-=\\[\\]{};':"\\\\|,.<>\\/?]){${r}})`);t=t.regex(n,e.messages?.hasSpecialChar||`At least ${r} special character(s)`)}return e.includes&&(t=t.includes(e.includes,{message:e.messages?.includes||`Must include "${e.includes}"`})),t}function Me(e){let t=F.z.coerce.number();return e.min!==void 0&&(t=t.min(e.min,e.messages?.min||`Min value is ${e.min}`)),e.max!==void 0&&(t=t.max(e.max,e.messages?.max||`Max value is ${e.max}`)),t}function Ue(e,t){const r=[],n=[];for(const[i,s]of Object.entries(t.fields))s.validation?.matchField&&r.push({fieldName:i,matchField:s.validation.matchField,message:s.validation.messages?.matchField||"Fields do not match"}),s.validation?.requiredIf&&n.push({fieldName:i,condition:s.validation.requiredIf,message:s.validation.messages?.required||`${s.label} is required`});let a=e;for(const{fieldName:i,matchField:s,message:l}of r)a=a.refine(c=>c[i]===c[s],{message:l,path:[i]});return n.length>0&&(a=a.superRefine((i,s)=>{for(const{fieldName:l,condition:c,message:o}of n)if(A(c,i)){const d=i[l];M(d)&&s.issues.push({code:"custom",message:o,path:[l],input:d})}})),a}function se(e,t,r){const n=new URL(e.apiUrl,window.location.origin);if(e.queryParams)for(const a of e.queryParams){let i;if(a.dependsOnInput)i=r||void 0;else if(a.dependsOn){const s=t[a.dependsOn];i=s!=null?String(s):void 0}else a.value&&(i=a.value);i!==void 0&&n.searchParams.set(a.key,i)}return n.pathname+n.search}function ie(e){return e.queryParams?e.queryParams.filter(t=>t.dependsOn).map(t=>t.dependsOn):[]}function oe(e,t){return e.every(r=>{const n=t[r];return n!=null&&n!==""})}function ae(e){const t={};for(const[r,n]of Object.entries(e.fields))if(n.defaultValue!==void 0)t[r]=n.defaultValue;else switch(n.type){case"text":case"email":case"password":case"textarea":case"select":case"autocomplete":case"radio":t[r]="";break;case"number":t[r]=null;break;case"checkbox":case"switch":t[r]=!1;break;case"multiselect":t[r]=[];break;case"date":t[r]=void 0;break;case"dateRange":t[r]={start:void 0,end:void 0};break;case"file":t[r]=n.multiple?[]:null;break;default:t[r]=""}return t}const V=(e,t)=>{if(!t)return e;const r=Array.isArray(e)?e:[e],n=Array.isArray(t)?t:[t];return[...r,...n]},N=(e,t)=>e?t?{sx:V(e.sx??{},t.sx)}:e:t,We=(e,t)=>e?t?{root:N(e.root,t.root),title:N(e.title,t.title),description:N(e.description,t.description),content:N(e.content,t.content)}:e:t;function le(e){const{control:t}=x.useFormContext(),r=[];for(const[c,o]of Object.entries(e.fields))if(o.showIf){const d=K(o.showIf);r.push({name:c,dependsOn:d})}const n=[],a=new Set;for(const{dependsOn:c}of r)for(const o of c)a.has(o)||(a.add(o),n.push(o));const i=x.useWatch({control:t,name:n}),s={};n.length>0&&i&&n.forEach((c,o)=>{s[c]=Array.isArray(i)?i[o]:i});const l=new Set;for(const[c,o]of Object.entries(e.fields))o.showIf?A(o.showIf,s)&&l.add(c):l.add(c);return l}function K(e){return"and"in e?e.and.flatMap(K):"or"in e?e.or.flatMap(K):[e.field]}function O(e,t,r){const{control:n}=x.useFormContext(),{apiClient:a,notifications:i}=R(),s=e?.queryParams?.some(q=>q.dependsOnInput)??!1,[l,c]=y.useState(""),[o,d]=y.useState("");y.useEffect(()=>{if(!s)return;const q=setTimeout(()=>d(l),300);return()=>clearTimeout(q)},[l,s]);const f=e?ie(e):[],h=x.useWatch({control:n,name:f.length>0?f:[]}),p={};f.length>0&&f.forEach((q,k)=>{p[q]=Array.isArray(h)?h[k]:h});const v=e?se(e,p,s?o:void 0):"",b=e?oe(f,p):!1,{data:j,isLoading:S,error:w}=qe.useQuery({queryKey:["options",v],queryFn:async()=>a.get(v),enabled:!!e&&b,staleTime:300*1e3,retry:2});y.useEffect(()=>{w&&i.onError&&i.onError(`Failed to load options: ${w.message}`)},[w,i]);const D=y.useMemo(()=>{if(!e)return t??[];const q=j?j.map(E=>({value:E[e.valueKey??"value"],label:E[e.labelKey??"label"]})):[];if(!r?.length)return q;const k=new Set(q.map(E=>String(E.value)));return[...r.filter(E=>!k.has(String(E.value))),...q]},[j,r,e,t]),C=y.useCallback((q,k)=>{c(k)},[]);return{options:D,isLoading:S,error:w,inputValue:s?l:void 0,onInputChange:s?C:void 0}}function ue(e,t){const{apiClient:r}=R(),n=y.useRef(void 0),a=y.useRef(null),i=y.useCallback(async c=>{if(!t||!c||typeof c=="string"&&c.trim()==="")return!0;a.current&&a.current.abort();const o=new AbortController;a.current=o;try{const d=new URL(t.apiUrl,window.location.origin);if(t.queryParams)for(const p of t.queryParams)(p.dependsOn===e||!p.dependsOn)&&d.searchParams.set(p.key,String(c));else d.searchParams.set(e,String(c));const f=await r.get(d.pathname+d.search,{signal:o.signal});return f.valid??!f.exists?!0:t.message||"Validation failed"}catch(d){return d instanceof Error&&d.name==="AbortError"?!0:"⚠️ Validation unavailable (API error)"}},[t,e,r]),s=y.useCallback(c=>new Promise(o=>{n.current&&clearTimeout(n.current),n.current=setTimeout(async()=>{const d=await i(c);o(d)},t?.debounce??500)}),[i,t?.debounce]),l=y.useCallback(async c=>(n.current&&clearTimeout(n.current),i(c)),[i]);return{validateDebounced:s,validateImmediate:l}}const Y=(e,t,r)=>{if(e&&"reportValidity"in e){const n=x.get(r,t);e.setCustomValidity(n&&n.message||""),e.reportValidity()}},Z=(e,t)=>{for(const r in t.fields){const n=t.fields[r];n&&n.ref&&"reportValidity"in n.ref?Y(n.ref,r,e):n&&n.refs&&n.refs.forEach(a=>Y(a,r,e))}},J=(e,t)=>{t.shouldUseNativeValidation&&Z(e,t);const r={};for(const n in e){const a=x.get(t.fields,n),i=Object.assign(e[n]||{},{ref:a&&a.ref});if(Ke(t.names||Object.keys(e),n)){const s=Object.assign({},x.get(r,n));x.set(s,"root",i),x.set(r,n,s)}else x.set(r,n,i)}return r},Ke=(e,t)=>{const r=X(t);return e.some(n=>X(n).match(`^${r}\\.\\d+`))};function X(e){return e.replace(/\]|\[/g,"")}function ce(e,t,r){function n(l,c){if(l._zod||Object.defineProperty(l,"_zod",{value:{def:c,constr:s,traits:new Set},enumerable:!1}),l._zod.traits.has(e))return;l._zod.traits.add(e),t(l,c);const o=s.prototype,d=Object.keys(o);for(let f=0;f<d.length;f++){const h=d[f];h in l||(l[h]=o[h].bind(l))}}const a=r?.Parent??Object;class i extends a{}Object.defineProperty(i,"name",{value:e});function s(l){var c;const o=r?.Parent?new i:this;n(o,l),(c=o._zod).deferred??(c.deferred=[]);for(const d of o._zod.deferred)d();return o}return Object.defineProperty(s,"init",{value:n}),Object.defineProperty(s,Symbol.hasInstance,{value:l=>r?.Parent&&l instanceof r.Parent?!0:l?._zod?.traits?.has(e)}),Object.defineProperty(s,"name",{value:e}),s}class Ze extends Error{constructor(){super("Encountered Promise during synchronous parse. Use .parseAsync() instead.")}}const He={};function de(e){return He}function Ge(e,t){return typeof t=="bigint"?t.toString():t}const fe="captureStackTrace"in Error?Error.captureStackTrace:(...e)=>{};function B(e){return typeof e=="string"?e:e?.message}function me(e,t,r){const n={...e,path:e.path??[]};if(!e.message){const a=B(e.inst?._zod.def?.error?.(e))??B(t?.error?.(e))??B(r.customError?.(e))??B(r.localeError?.(e))??"Invalid input";n.message=a}return delete n.inst,delete n.continue,t?.reportInput||delete n.input,n}const pe=(e,t)=>{e.name="$ZodError",Object.defineProperty(e,"_zod",{value:e._zod,enumerable:!1}),Object.defineProperty(e,"issues",{value:t,enumerable:!1}),e.message=JSON.stringify(t,Ge,2),Object.defineProperty(e,"toString",{value:()=>e.message,enumerable:!1})},Qe=ce("$ZodError",pe),he=ce("$ZodError",pe,{Parent:Error}),Ye=e=>(t,r,n,a)=>{const i=n?Object.assign(n,{async:!1}):{async:!1},s=t._zod.run({value:r,issues:[]},i);if(s instanceof Promise)throw new Ze;if(s.issues.length){const l=new(a?.Err??e)(s.issues.map(c=>me(c,i,de())));throw fe(l,a?.callee),l}return s.value},Je=Ye(he),Xe=e=>async(t,r,n,a)=>{const i=n?Object.assign(n,{async:!0}):{async:!0};let s=t._zod.run({value:r,issues:[]},i);if(s instanceof Promise&&(s=await s),s.issues.length){const l=new(a?.Err??e)(s.issues.map(c=>me(c,i,de())));throw fe(l,a?.callee),l}return s.value},et=Xe(he);function ee(e,t){try{var r=e()}catch(n){return t(n)}return r&&r.then?r.then(void 0,t):r}function tt(e,t){for(var r={};e.length;){var n=e[0],a=n.code,i=n.message,s=n.path.join(".");if(!r[s])if("unionErrors"in n){var l=n.unionErrors[0].errors[0];r[s]={message:l.message,type:l.code}}else r[s]={message:i,type:a};if("unionErrors"in n&&n.unionErrors.forEach(function(d){return d.errors.forEach(function(f){return e.push(f)})}),t){var c=r[s].types,o=c&&c[n.code];r[s]=x.appendErrors(s,t,r,a,o?[].concat(o,n.message):n.message)}e.shift()}return r}function rt(e,t){for(var r={};e.length;){var n=e[0],a=n.code,i=n.message,s=n.path.join(".");if(!r[s])if(n.code==="invalid_union"&&n.errors.length>0){var l=n.errors[0][0];r[s]={message:l.message,type:l.code}}else r[s]={message:i,type:a};if(n.code==="invalid_union"&&n.errors.forEach(function(d){return d.forEach(function(f){return e.push(f)})}),t){var c=r[s].types,o=c&&c[n.code];r[s]=x.appendErrors(s,t,r,a,o?[].concat(o,n.message):n.message)}e.shift()}return r}function nt(e,t,r){if(r===void 0&&(r={}),(function(n){return"_def"in n&&typeof n._def=="object"&&"typeName"in n._def})(e))return function(n,a,i){try{return Promise.resolve(ee(function(){return Promise.resolve(e[r.mode==="sync"?"parse":"parseAsync"](n,t)).then(function(s){return i.shouldUseNativeValidation&&Z({},i),{errors:{},values:r.raw?Object.assign({},n):s}})},function(s){if((function(l){return Array.isArray(l?.issues)})(s))return{values:{},errors:J(tt(s.errors,!i.shouldUseNativeValidation&&i.criteriaMode==="all"),i)};throw s}))}catch(s){return Promise.reject(s)}};if((function(n){return"_zod"in n&&typeof n._zod=="object"})(e))return function(n,a,i){try{return Promise.resolve(ee(function(){return Promise.resolve((r.mode==="sync"?Je:et)(e,n,t)).then(function(s){return i.shouldUseNativeValidation&&Z({},i),{errors:{},values:r.raw?Object.assign({},n):s}})},function(s){if((function(l){return l instanceof Qe})(s))return{values:{},errors:J(rt(s.issues,!i.shouldUseNativeValidation&&i.criteriaMode==="all"),i)};throw s}))}catch(s){return Promise.reject(s)}};throw new Error("Invalid input: not a Zod schema")}function st(e){return y.useCallback(async(t,r,n)=>{const a=new Set;for(const[o,d]of Object.entries(e.fields))(!d.showIf||A(d.showIf,t))&&a.add(o);const i=Object.entries(e.fields).filter(([o])=>a.has(o)).reduce((o,[d,f])=>(o[d]=f,o),{}),s=ne({...e,fields:i});return await nt(s)(t,r,n)},[e])}const xe=y.createContext(null);function it(){return y.useContext(xe)}function U(e,t){const r=it();y.useEffect(()=>(r?.register(e,t),()=>{r?.unregister(e)}),[e,t,r])}function _({name:e,config:t,disabled:r}){const{control:n}=x.useFormContext(),{components:a,size:i}=R(),s=a.TextField,l=t,{validateDebounced:c,validateImmediate:o}=ue(e,l.validation?.asyncValidation);if(t.type!=="text"&&t.type!=="password"&&t.type!=="email"&&t.type!=="textarea")return null;const d=t.type==="textarea",f=d?t.rows||4:void 0,h=!!(l.validation?.required||l.validation?.requiredIf);return u.jsx(x.Controller,{name:e,control:n,rules:{validate:{asyncValidation:async p=>c(p)}},render:({field:{value:p,onChange:v,onBlur:b,ref:j},fieldState:{error:S}})=>u.jsx(s,{value:p||"",onChange:v,onBlur:async()=>{b(),l.validation?.asyncValidation&&await o(p)},inputRef:j,type:t.type==="textarea"?"text":t.type,label:l.label,required:h,placeholder:l.placeholder,helperText:S?.message||l.helperText,error:!!S,disabled:r||l.disabled,autoComplete:l.autoComplete,multiline:d,rows:f,size:i})})}function ot({name:e,config:t,disabled:r}){const{control:n}=x.useFormContext(),{components:a,size:i}=R(),s=a.NumberField;if(t.type!=="number")return null;const l=!!(t.validation?.required||t.validation?.requiredIf);return u.jsx(x.Controller,{name:e,control:n,render:({field:{value:c,onChange:o,onBlur:d,name:f},fieldState:{error:h}})=>u.jsx(s,{name:f,value:c,onValueChange:p=>o(p),onBlur:d,label:t.label,required:l,placeholder:t.placeholder,helperText:h?.message||t.helperText,error:!!h,disabled:r||t.disabled,min:t.min,max:t.max,step:t.step,size:i})})}function at({name:e,config:t,disabled:r}){const{control:n}=x.useFormContext(),{components:a,size:i}=R(),s=a.Checkbox;if(t.type!=="checkbox")return null;const l=!!(t.validation?.required||t.validation?.requiredIf);return u.jsx(x.Controller,{name:e,control:n,render:({field:{value:c,onChange:o,...d},fieldState:{error:f}})=>u.jsx(s,{...d,label:t.label,required:l,checked:!!c,onChange:h=>o(h.target.checked),disabled:r||t.disabled,error:!!f,helperText:f?.message||t.helperText,size:i})})}function lt({name:e,config:t,disabled:r}){const{control:n}=x.useFormContext(),{components:a,size:i}=R(),s=a.Switch;if(t.type!=="switch")return null;const l=!!(t.validation?.required||t.validation?.requiredIf);return u.jsx(x.Controller,{name:e,control:n,render:({field:{value:c,onChange:o,...d},fieldState:{error:f}})=>u.jsx(s,{...d,label:t.label,required:l,checked:!!c,onChange:h=>o(h.target.checked),disabled:r||t.disabled,error:!!f,helperText:f?.message||t.helperText,size:i})})}function ut({name:e,config:t,disabled:r}){const{control:n,setValue:a,getValues:i}=x.useFormContext(),{components:s,size:l}=R(),c=s.Select,o=t,{options:d,isLoading:f}=O(t.type==="select"?o.optionsSource:void 0,t.type==="select"?o.options:void 0);if(U(e,d),y.useEffect(()=>{if(t.type!=="select")return;const p=i(e);p&&!d.some(v=>v.value===p)&&a(e,"",{shouldValidate:!1})},[d,e,a,i,t.type]),t.type!=="select")return null;const h=!!(o.validation?.required||o.validation?.requiredIf);return u.jsx(x.Controller,{name:e,control:n,render:({field:{value:p,onChange:v,onBlur:b,ref:j},fieldState:{error:S}})=>u.jsxs(m.FormControl,{fullWidth:!0,error:!!S,disabled:r||o.disabled||f,size:l,children:[o.label&&u.jsx(m.InputLabel,{required:h,children:o.label}),u.jsx(c,{name:e,value:p||"",onChange:v,onBlur:b,label:o.label,options:d,size:l,ref:j}),(S?.message||o.helperText)&&u.jsx(m.FormHelperText,{children:S?.message||o.helperText})]})})}function ct({name:e,config:t,disabled:r}){const{control:n,setValue:a,getValues:i}=x.useFormContext(),{components:s,size:l}=R(),c=s.Autocomplete,o=t,{options:d,isLoading:f,inputValue:h,onInputChange:p}=O(t.type==="multiselect"?o.optionsSource:void 0,t.type==="multiselect"?o.options:void 0,t.type==="multiselect"?o.initialOptions:void 0);if(U(e,d),y.useEffect(()=>{if(t.type!=="multiselect"||f||p)return;const b=i(e);if(Array.isArray(b)&&b.length>0){const j=b.filter(S=>d.some(w=>w.value===S));j.length!==b.length&&a(e,j,{shouldValidate:!1})}},[d,f,p,e,a,i,t.type]),t.type!=="multiselect")return null;const v=!!(o.validation?.required||o.validation?.requiredIf);return u.jsx(x.Controller,{name:e,control:n,render:({field:{value:b,onChange:j,...S},fieldState:{error:w}})=>u.jsx(c,{...S,multiple:!0,options:d,value:Array.isArray(b)?d.filter(D=>b.includes(D.value)):[],onChange:(D,C)=>{const q=C.map(k=>k.value);j(q)},isOptionEqualToValue:(D,C)=>D.value===C.value,label:o.label,required:v,placeholder:o.placeholder,error:!!w,helperText:w?.message||o.helperText,disabled:r||o.disabled||f&&!p,loading:f,size:l,inputValue:h,onInputChange:p,filterOptions:p?D=>D:void 0})})}function dt({name:e,config:t,disabled:r}){const{control:n,setValue:a,getValues:i}=x.useFormContext(),{components:s,size:l}=R(),c=s.Autocomplete,o=t,{options:d,isLoading:f,inputValue:h,onInputChange:p}=O(t.type==="autocomplete"?o.optionsSource:void 0,t.type==="autocomplete"?o.options:void 0,t.type==="autocomplete"?o.initialOptions:void 0);if(U(e,d),y.useEffect(()=>{if(t.type!=="autocomplete"||f||p)return;const b=i(e);b&&!d.some(j=>j.value===b)&&a(e,"",{shouldValidate:!1})},[d,f,p,e,a,i,t.type]),t.type!=="autocomplete")return null;const v=!!(o.validation?.required||o.validation?.requiredIf);return u.jsx(x.Controller,{name:e,control:n,render:({field:{value:b,onChange:j,...S},fieldState:{error:w}})=>u.jsx(c,{...S,options:d,value:d.find(D=>D.value===b)??null,onChange:(D,C)=>{j(C?.value??"")},isOptionEqualToValue:(D,C)=>D.value===C.value,label:o.label,required:v,placeholder:o.placeholder,error:!!w,helperText:w?.message||o.helperText,disabled:r||o.disabled||f&&!p,loading:f,size:l,inputValue:h,onInputChange:p,filterOptions:p?D=>D:void 0})})}function ft({name:e,config:t,disabled:r}){const{control:n,setValue:a,getValues:i}=x.useFormContext(),{components:s,size:l}=R(),c=s.RadioGroup,o=t,{options:d,isLoading:f}=O(t.type==="radio"?o.optionsSource:void 0,t.type==="radio"?o.options:void 0);U(e,d);const h=d.map(v=>({label:v.label,value:String(v.value)}));if(y.useEffect(()=>{if(t.type!=="radio")return;const v=i(e);v&&!d.some(b=>String(b.value)===String(v))&&a(e,"",{shouldValidate:!1})},[d,e,a,i,t.type]),t.type!=="radio")return null;const p=!!(o.validation?.required||o.validation?.requiredIf);return f?u.jsx(m.Skeleton,{variant:"rectangular",height:100}):u.jsx(x.Controller,{name:e,control:n,render:({field:v,fieldState:{error:b}})=>u.jsx(c,{...v,label:o.label,required:p,options:h,row:o.direction==="row",error:!!b,helperText:b?.message||o.helperText,disabled:r||o.disabled,size:l})})}function mt({name:e,config:t,disabled:r}){const{control:n}=x.useFormContext(),{components:a,size:i}=R(),s=a.DatePicker,l=t;if(t.type!=="date")return null;const c=!!(l.validation?.required||l.validation?.requiredIf);return u.jsx(x.Controller,{name:e,control:n,render:({field:{value:o,onChange:d},fieldState:{error:f}})=>u.jsx(s,{label:l.label,required:c,includeTime:l.includeTime,format:l.format,value:o||null,onChange:d,disabled:r||l.disabled,minDate:l.validation?.minDate,maxDate:l.validation?.maxDate,error:!!f,helperText:f?.message||l.helperText,size:i})})}function pt({name:e,config:t,disabled:r}){const{control:n}=x.useFormContext(),{components:a,size:i}=R(),s=a.DateRangePicker,l=t;if(t.type!=="dateRange")return null;const c=!!(l.validation?.required||l.validation?.requiredIf);return u.jsx(x.Controller,{name:e,control:n,render:({field:{value:o,onChange:d},fieldState:{error:f}})=>u.jsx(s,{label:l.label,required:c,includeTime:l.includeTime,format:l.format,value:o||{start:null,end:null},onChange:d,disabled:r||l.disabled,minDate:l.validation?.minDate,maxDate:l.validation?.maxDate,error:!!f,helperText:f?.message||l.helperText,size:i})})}function ht({name:e,config:t,disabled:r}){const{control:n}=x.useFormContext(),{components:a}=R(),i=a.FileUpload,s=t;if(t.type!=="file")return null;const l=!!(s.validation?.required||s.validation?.requiredIf);return u.jsx(x.Controller,{name:e,control:n,render:({field:{value:c,onChange:o,onBlur:d,ref:f},fieldState:{error:h}})=>u.jsx(i,{label:s.label,required:l,value:c||null,onChange:o,onBlur:d,disabled:r||s.disabled,multiple:s.multiple,accept:s.accept,error:!!h,helperText:h?.message||s.helperText,ref:f})})}const ge={text:_,password:_,email:_,textarea:_,number:ot,checkbox:at,switch:lt,select:ut,multiselect:ct,autocomplete:dt,radio:ft,date:mt,dateRange:pt,file:ht};function xt(e){return ge[e]??null}function gt(e,t){ge[e]=t}function yt({name:e,config:t,disabled:r}){const n=xt(t.type);return n?u.jsx(n,{name:e,config:t,disabled:r}):(console.warn(`Unknown field type: ${t.type}`),u.jsxs(m.Alert,{severity:"warning",sx:{my:1},children:["Unknown field type: ",u.jsx("code",{children:t.type})]}))}function bt({section:e,fields:t,disabled:r,visibleFields:n,slotProps:a}){const i=e.fields.filter(s=>n.has(s));return i.length===0?null:u.jsxs(m.Box,{sx:V({mb:4},a?.root?.sx),children:[e.title&&u.jsx(m.Typography,{variant:"h6",sx:V({mb:2},a?.title?.sx),children:e.title}),e.description&&u.jsx(m.Typography,{variant:"body2",color:"text.secondary",sx:V({mb:2},a?.description?.sx),children:e.description}),u.jsx(m.Box,{sx:V({display:"flex",flexDirection:"column",gap:2},a?.content?.sx),children:i.map(s=>u.jsx(yt,{name:s,config:t[s],disabled:r},s))})]})}class ye extends y.Component{constructor(t){super(t),this.handleReset=()=>{this.setState({hasError:!1,error:null})},this.state={hasError:!1,error:null}}static getDerivedStateFromError(t){return{hasError:!0,error:t}}componentDidCatch(t,r){console.error("DynamicForm Error:",t,r),this.props.onError?.(t,r)}render(){return this.state.hasError?this.props.fallback?this.props.fallback:u.jsxs(m.Box,{sx:{p:3},children:[u.jsxs(m.Alert,{severity:"error",sx:{mb:2},children:[u.jsx(m.Typography,{variant:"h6",gutterBottom:!0,children:"Form Error"}),u.jsx(m.Typography,{variant:"body2",sx:{mb:2},children:"Something went wrong while rendering the form."}),this.state.error&&u.jsx(m.Typography,{variant:"caption",sx:{fontFamily:"monospace"},children:this.state.error.message})]}),u.jsx(m.Button,{variant:"outlined",onClick:this.handleReset,children:"Reset Form"})]}):this.props.children}}function vt({schema:e,mode:t,sectionSlotProps:r,children:n,formState:a}){const i=le(e),s=t==="disabled",l=new Map(e.sections.map(d=>[d.id,d])),c=d=>{const f=l.get(d);return f?u.jsx(bt,{section:f,fields:e.fields,disabled:s,visibleFields:i,slotProps:r},f.id):null},o=(d,f)=>{const h=d.columns??d.sections.length;return h<=1?u.jsx(m.Box,{children:d.sections.map(c)},f):u.jsx(m.Box,{sx:{display:"grid",gridTemplateColumns:{xs:"1fr",md:`repeat(${h}, 1fr)`},gap:3,alignItems:"start"},children:d.sections.map(c)},f)};return u.jsxs(u.Fragment,{children:[e.layout?e.layout.map(o):e.sections.map(d=>c(d.id)),typeof n=="function"?n(a):n]})}function Ft({schema:e,onSubmit:t,mode:r="edit",defaultValues:n,apiClient:a,size:i,sectionSlotProps:s,children:l}){const c=R(),o=!!(a||i!==void 0),d=y.useMemo(()=>o?{...c,...a&&{apiClient:a},...i!==void 0&&{size:i}}:c,[c,a,i,o]),f=y.useMemo(()=>We(c.sectionSlotProps,s),[c.sectionSlotProps,s]),h=y.useRef(new Map),p=y.useMemo(()=>({register:(C,q)=>{h.current.set(C,q)},unregister:C=>{h.current.delete(C)},buildLabelMap:()=>{const C={};for(const[q,k]of h.current.entries()){if(k.length===0)continue;const P={};for(const E of k)P[String(E.value)]=E.label;C[q]=P}return C}}),[]),v=y.useCallback(C=>t(C,p.buildLabelMap()),[t,p]),b=st(e),j=ae(e),S=x.useForm({resolver:b,defaultValues:{...j,...n},mode:"onChange",reValidateMode:"onChange"}),w={isValid:S.formState.isValid,isSubmitting:S.formState.isSubmitting},D=u.jsx(ye,{children:u.jsx(x.FormProvider,{...S,children:u.jsx(xe,{value:p,children:u.jsx(m.Box,{component:"form",onSubmit:S.handleSubmit(v,C=>{console.error("❌ Form validation errors:",C)}),noValidate:!0,children:u.jsx(vt,{schema:e,mode:r,sectionSlotProps:f,children:l,formState:w})})})})});return o?u.jsx(H,{value:d,children:D}):D}exports.DynamicForm=Ft;exports.DynamicFormsProvider=Le;exports.FormErrorBoundary=ye;exports.areDependenciesFilled=oe;exports.buildOptionsUrl=se;exports.buildZodSchema=ne;exports.evaluateCondition=A;exports.extractDefaultValues=ae;exports.extractOptionsDependencies=ie;exports.formatFileSize=re;exports.isEmpty=M;exports.registerFieldComponent=gt;exports.useAsyncValidation=ue;exports.useDynamicFormsContext=R;exports.useFieldVisibility=le;exports.useOptionsSource=O;