drf-react-by-schema 0.28.25 → 0.28.27

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.
@@ -0,0 +1,665 @@
1
+ # API reference — `drf-react-by-schema` (React)
2
+
3
+ Canonical reference for everything the package exposes to **consuming apps**: the
4
+ provider, `useAPIWrapper()`, the components, their props and the utility functions.
5
+
6
+ The public surface is exactly what `src/index.ts` exports. If a symbol is not
7
+ re-exported there, it is internal — do not import it from a deep path
8
+ (`drf-react-by-schema/dist/...`), because its signature may change without notice.
9
+
10
+ Related docs:
11
+
12
+ - [`FIELD_TYPES.md`](FIELD_TYPES.md) — schema `type` → widget table.
13
+ - [`localization.md`](localization.md) — `theme.texts`, MUI locale setup.
14
+ - [`password-reset-and-email.md`](password-reset-and-email.md) — forgot/change password flow.
15
+
16
+ ---
17
+
18
+ ## 0. Golden rules for consuming apps
19
+
20
+ 1. **Wrap the app with `DRFReactBySchemaProvider`.** It installs the MUI theme,
21
+ `CssBaseline`, the context and the built-in overlays (dialog, snackbar, loading
22
+ backdrop).
23
+ 2. **Use `useAPIWrapper()` for every data/auth action.** It is the only public data
24
+ hook.
25
+ 3. **Never call `useDRFReactBySchema()`.** It exists as an internal building block of
26
+ this package, is **not exported** from the package entry, and only returns raw
27
+ context (`serverEndPoint`, `theme`, `isInBatches`, `firstBatchLength`, `schemas`,
28
+ `pagination`). It has no data, auth or form helpers and is not a supported app API.
29
+ 4. **Fetch helpers return `Item | AxiosError`.** Always check
30
+ `axios.isAxiosError(result)` before using the value.
31
+ 5. **Schemas come from the Django pair** (`drf-react-by-schema-django`) through
32
+ `OPTIONS` or the `sync_react` snapshots passed to the provider. Never hand-write
33
+ field metadata for a model that the backend already describes.
34
+
35
+ ---
36
+
37
+ ## 1. `DRFReactBySchemaProvider`
38
+
39
+ `src/context/DRFReactBySchemaProvider.tsx`
40
+
41
+ Wraps the app. Props (`DRFReactBySchemaProviderProps`):
42
+
43
+ | Prop | Type | Default | Purpose |
44
+ | --- | --- | --- | --- |
45
+ | `serverEndPoint` | `ServerEndPoint \| null` | `null` | Django URLs. See below. |
46
+ | `theme` | `Theme` | package default | Deep-merged over the default theme. Localize through `theme.texts`. |
47
+ | `isInBatches` | `boolean` | `true` | Lists fetch in two batches (schema+first page, then the rest) to render faster. |
48
+ | `firstBatchLength` | `number` | `100` | Batch size used when `isInBatches` is on. |
49
+ | `schemas` | `SchemasType` | `undefined` | `sync_react` output. Avoids `OPTIONS` round-trips. Values may be inline `SchemaOptionsType` objects or `() => import(...)` loaders. |
50
+ | `pagination` | `PaginationConfig` | `{ perPage: 50, pageSizeOptions: [5,10,25,50,100,200], maxPageSize: 500 }` | Defaults for server-side lists. |
51
+ | `children` | `ReactNode` | — | App tree. |
52
+
53
+ `ServerEndPoint`:
54
+
55
+ | Key | Default derived by the provider |
56
+ | --- | --- |
57
+ | `url` | required |
58
+ | `apiTokenUrl` | required (JWT token endpoint) |
59
+ | `api` | `${url}/api` |
60
+ | `autocomplete` | `${url}/autocomplete` |
61
+ | `getToken` | `apiTokenUrl` |
62
+ | `refreshToken` | `${apiTokenUrl}refresh/` |
63
+ | `verifyToken` | `${apiTokenUrl}verify/` |
64
+ | `media` | `${url}/media` |
65
+ | `signUp` | **no default** — set it explicitly to enable sign-up |
66
+ | `requestPasswordReset` | `${api}/password_reset/request/` |
67
+ | `validatePasswordReset` | `${api}/password_reset/validate/` |
68
+ | `confirmPasswordReset` | `${api}/password_reset/confirm/` |
69
+ | `changePassword` | `${api}/my_account/change_password/` |
70
+
71
+ The provider also renders the package overlays, so `setDialog`,
72
+ `onTriggerSnackBar` and the loading backdrop work anywhere below it.
73
+
74
+ ---
75
+
76
+ ## 2. `useAPIWrapper()`
77
+
78
+ `src/context/APIWrapperContext.tsx`
79
+
80
+ The single hook for data, auth, form state and feedback. It returns
81
+ `APIWrapperContextType<T>` (generic `T` is your generated model type; omit it for
82
+ `Record<string, any>`).
83
+
84
+ ```ts
85
+ const {
86
+ usuaria, isLoggedIn, signOut,
87
+ getGenericModelList, getGenericModel, onDeleteModel,
88
+ onTriggerSnackBar, setDialog, isMobile,
89
+ } = useAPIWrapper();
90
+ ```
91
+
92
+ ### 2.1 Session and current user
93
+
94
+ | Member | Signature | Notes |
95
+ | --- | --- | --- |
96
+ | `usuaria` | `Item \| null` | Current user from `GET /api/my_account/`. `null` before load/sign-out. |
97
+ | `updateUsuaria` | `() => void` | Re-fetches `my_account`. On failure stores `{ erro: 'token inválido' }`. |
98
+ | `isLoggedIn` | `() => Promise<false \| Item>` | Validates the stored JWT and returns the user (or `false`). Does not write `usuaria`. |
99
+ | `loginByPayload` | `(payload: Item) => Promise<boolean \| AxiosError>` | `POST` to `getToken`; stores access/refresh tokens. Returns `false` on bad credentials. |
100
+ | `signOut` | `() => void` | Clears tokens, `usuaria`, `optionsAC`, `pageForm` and `editModel`. |
101
+ | `getSignUpOptions` | `() => Promise<SchemaType \| AxiosError>` | Schema of the sign-up form. |
102
+ | `signUp` | `<U>(data: Item<U>) => Promise<Item<U> \| AxiosError>` | `POST` to `serverEndPoint.signUp`. |
103
+ | `isMobile` | `boolean` | `theme.breakpoints.down('md')`. |
104
+
105
+ Password management (see [`password-reset-and-email.md`](password-reset-and-email.md)):
106
+
107
+ | Member | Signature |
108
+ | --- | --- |
109
+ | `requestPasswordReset` | `(email: string) => Promise<Item \| AxiosError>` |
110
+ | `validatePasswordReset` | `(data: Item) => Promise<Item \| AxiosError>` |
111
+ | `confirmPasswordReset` | `(data: Item) => Promise<Item \| AxiosError>` |
112
+ | `changePassword` | `(data: Item) => Promise<Item \| AxiosError>` |
113
+
114
+ ### 2.2 Reading data
115
+
116
+ | Member | Signature | Notes |
117
+ | --- | --- | --- |
118
+ | `getGenericModel` | `({ model, id?, relatedModel?, relatedModelId? }) => Promise<{ data, schema, columns, modelOptions } \| AxiosError>` | One record + its schema/columns. With no `id` it returns the schema and an empty record; create forms should use `loadSinglePageData`/`<Form>`, which map tmp ids to no id. |
119
+ | `getGenericModelList` | `GetGenericModelListPropsLocal<T> => Promise<DataSchemaColumnsType<T> \| AxiosError>` | List with server filter/sort/paginate/totals. Params: `model`, `id`, `relatedModel`, `relatedModelId`, `columnFields`, `creatableFields`, `disabledFields`, `isInBatches`, `loadedSchema`, `loadedModelOptions`, `page`, `pageSize`, `filter`, `queryParams`, `sort`, `sumRows`. Returns `{ data, schema, columns, modelOptions, rowCount, sumRowsTotals, facets }`. |
120
+ | `getAutoComplete` | `(model: string, queryParams?: string[]) => Promise<AutocompleteItem[] \| AxiosError>` | Options for relation fields. Duplicate labels get a `(n)` suffix. |
121
+ | `getRawData` | `<U = void>(route: string) => Promise<U \| AxiosError>` | Authenticated `GET` against `${api}/${route}`. Handles 401 by refreshing the token. |
122
+ | `getAllModels` | `() => Promise<Item[]>` | Model list used by admin-like screens. Returns `[]` on error. |
123
+ | `getModelMeta` | `(model: string) => Promise<{ verboseName, verboseNamePlural }>` | From the loaded schemas; falls back to the model slug. Does not hit the network when `schemas` is provided. |
124
+
125
+ Server query grammar produced by `getGenericModelList` (Django side):
126
+
127
+ - quick filter → `search=<value>`
128
+ - filter item → `field=<field>&operator=<op>&value=<value>`
129
+ - sort → `ordering=field,-other`
130
+ - pagination → `page=<1-based>&page_size=<n>`
131
+ - totals → `sum_rows=<field>[|<facet>...][|<limit>],...`
132
+ - batching → `is_first_batch=1` / `is_last_batch=1`
133
+
134
+ ### 2.3 Writing data
135
+
136
+ | Member | Signature | Notes |
137
+ | --- | --- | --- |
138
+ | `onSubmit` | `(model, id, data, event?) => Promise<Id \| AxiosError>` | Validates/normalizes `data` with the current `pageForm.schema`, then `POST` when `id` is tmp/empty or `PUT` otherwise. Shows snackbars and refreshes `optionsAC`. Requires a loaded `pageForm` (normally provided by `<Form>`/`loadSinglePageData`). Returns the new id. |
139
+ | `postData` | `({ path, data, contentType? }) => Promise<Item \| AxiosError>` | Raw `POST` (no schema normalization). |
140
+ | `updateModel` | `({ path, id, data, contentType? }) => Promise<Item \| AxiosError>` | Raw `PUT`. |
141
+ | `rawUpdateDataBySchema` | `(props: UpdateDataBySchema) => Promise<Item \| AxiosError>` | `POST` (tmp/missing id) or `PUT` after `prepareDataBySchema` normalization. `props`: `{ model, modelObjectId, data, schema, path? }`. |
142
+ | `rawDeleteData` | `(model: string, id: Id) => Promise<true \| AxiosError>` | Raw `DELETE`, no dialog/snackbar. |
143
+ | `onEditModelDataGridSave` | `({ model, newRow, schema }) => Promise<Item \| AxiosError>` | Inline grid save; uses the row id and `path = model`. |
144
+ | `onEditRelatedModelSave` | `({ model, id, relatedModel, newRow, schema, onlyAddExisting? }) => Promise<Item \| ItemSchemaColumnsType \| AxiosError>` | Nested create/update (`onlyAddExisting` links an existing record via `id_to_add`). |
145
+ | `onDeleteRelatedModel` | `({ model, id, relatedModel, relatedModelId }) => Promise<true \| AxiosError>` | Nested `DELETE`, no confirmation dialog. |
146
+ | `exportExcelData` | `({ model, modelParent?, modelParentId?, paginationModel }) => Promise<true \| AxiosError>` | Downloads the current list as an Excel file. |
147
+
148
+ ### 2.4 Dialog-driven CRUD
149
+
150
+ | Member | Signature | Notes |
151
+ | --- | --- | --- |
152
+ | `onEditModel` | `(p: OnEditModelType) => void` | Opens the built-in dialog and loads the record. `p`: `{ model, id, labelKey, fieldKey?, index?, setValue?, getValues?, fieldsLayout?, initialValuesPartial? }`. It updates the parent form value and `optionsAC` after save when `setValue`/`getValues` are given. |
153
+ | `onEditModelSave` | `(data: Item) => Promise<boolean \| AxiosError>` | Closes the dialog and saves the model currently held in `editModel.current` (set by `onEditModel`). |
154
+ | `onDeleteModel` | `(model, id, onSuccess?) => void` | Opens a confirmation dialog and deletes on confirm. Returns immediately — no promise. For nested rows use `onDeleteRelatedModel`. |
155
+ | `editModel` | `RefObject<Item>` | The record being edited in the dialog. Read-only for app code. |
156
+
157
+ ### 2.5 Page form state
158
+
159
+ `<Form>` stores its state here; custom screens can reuse the same machinery.
160
+
161
+ | Member | Signature | Notes |
162
+ | --- | --- | --- |
163
+ | `loadSinglePageData` | `(p: LoadSinglePageDataProps) => Promise<Item \| AxiosError>` | Loads record + schema, builds initial values and the yup validation schema, and populates `pageForm`. `p`: `{ model, objId?, objTitleField?, optionsACModels?, defaults?, extraValidators?, fieldsLayout? }`. |
164
+ | `pageForm` | `PageFormType \| null` | `{ id, model, modelVerboseName, modelVerboseNamePlural, schema, columns, initialValues, validationSchema, objTitleField }`. |
165
+ | `resetPageForm` | `() => void` | Clears `pageForm`. |
166
+ | `formTools` | `FormTools \| undefined` | React-hook-form handles of the mounted `<Form>`: `{ errors, reset, getValues, setValue, isDirty }`. |
167
+ | `optionsAC` | `OptionsAC \| null` | Cache of autocomplete options, keyed by model. |
168
+ | `setOptionsAC` | `(x: Partial<OptionsAC>) => void` | Merges into the cache. |
169
+ | `populateOptionsAC` | `(models: string[]) => void` | Fetches and caches options for the given models. |
170
+ | `handleLoading` | `(p: boolean) => void` | Shows/hides the global loading backdrop. |
171
+
172
+ ### 2.6 Feedback
173
+
174
+ | Member | Signature | Notes |
175
+ | --- | --- | --- |
176
+ | `onTriggerSnackBar` | `({ msg, severity? }) => void` | `severity` is a MUI `AlertColor` (`'info'` default). |
177
+ | `setDialog` | `(state: Partial<DialogType> \| null) => void` | Opens/updates/closes the global dialog. `{ open, loading?, title?, Body?, Actions?, isCancelDisabled?, size? }`. Pass `null` to reset. |
178
+
179
+ ---
180
+
181
+ ## 3. Components
182
+
183
+ All components below are exported from the package root. Generic components accept an
184
+ optional `T` (`TField`) generated by `sync_react`.
185
+
186
+ ### 3.1 Forms
187
+
188
+ #### `<Form />` — full create/edit page
189
+
190
+ `src/components/forms/Form.tsx`
191
+
192
+ Loads schema + record, builds fields and validation, renders top/bottom action bars, and
193
+ navigates after save.
194
+
195
+ | Prop | Type | Purpose |
196
+ | --- | --- | --- |
197
+ | `model` | `string` | Model slug (e.g. `"comunidade"`). |
198
+ | `objId` | `Id` | Record id. Omit/`'novo'`/`tmp*` for create. |
199
+ | `defaults` | `PartialItem<T>` | Initial values for a create form. |
200
+ | `objTitleField` | `FieldKey<T>` | Field used as the record title. |
201
+ | `onSave` | `(currentId, newIdOrError, buttonType: FormSaveActions) => void` | After a save attempt. `buttonType` tells which button was used. |
202
+ | `onDelete` | `(removedId: Id) => void` | After a delete. |
203
+ | `onCancel` | `MouseEventHandler` | Runs before navigating away from cancel. |
204
+ | `navigate` | `(to: string) => void; (delta: number) => void` | Router adapter. Without it, `window.location.href` / `history.back()` are used. |
205
+ | `exit` | `(newId?: Id) => void` | Custom exit. Takes precedence over `exitPath`. |
206
+ | `exitPath` | `string` | Path to go after save/cancel; `{ID}` is replaced by the record id. |
207
+ | `editPath` | `string` | Path of the edit page; `{ID}` marks where the id goes. Defaults to the current directory + id. |
208
+ | `saveAndContinue` | `boolean` | Shows the "save and continue" button. |
209
+ | `title` | `string` | Header title. |
210
+ | `labelSave` / `labelSaveAndContinue` / `labelSaveAndCreateNew` / `labelCancel` / `labelRemove` | `string` | Override button labels (defaults come from `theme.texts.forms`). |
211
+ | `fieldsLayout` | `FormFieldLayout<T>[]` | Sections/rows/custom elements. See §4.1. |
212
+ | `customFieldFormLayouts` | `CustomFormFieldLayouts<T>` | Per-relation-field layouts for nested/editable relations. |
213
+ | `hiddenFields` | `FieldList<T>` | Fields not rendered; their values are preserved as hidden inputs on save. |
214
+ | `fieldsProps` | `FieldsProps<T>` | Per-field widget overrides (see §4.3). |
215
+ | `optionsAC` / `setOptionsAC` | `OptionsAC<T>` / setter | External autocomplete cache. Usually unnecessary inside a page. |
216
+ | `isolatedGetAutoComplete` | `(model) => Promise<AutocompleteItem[] \| AxiosError>` | Replace the autocomplete loader for this form only. |
217
+ | `relatedEditable` | `boolean` | `false` disables the inline edit dialogs for relation fields. |
218
+ | `autoFocusField` | `FieldKey<T>` | Focus this field on mount. |
219
+ | `elevation` | `number` | Card elevation. |
220
+ | `extraValidators` | `ExtraValidators<T>` | Replace the yup validator for individual fields. |
221
+ | `watchValues` | `FieldList<T>` | Fields monitored by `FormWatcher`. Requires `onWatchChange`. |
222
+ | `onWatchChange` | `(values: FormWatchObject<T>) => void` | Called with `{ [field]: value }` whenever a watched value changes (deep-equality guarded). |
223
+ | `reloadStateStamp` | `number \| string` | Change it to force a reload of the record/schema. |
224
+ | `CustomFormActions` | `(props: FormActionsProps) => JSX.Element` | Replace the default action bar. |
225
+ | `multiline`, `fieldKey`, `labelKey`, `optionsModel`, `getOptionLabel`, `renderOption`, `onEditModel`, `options`, `isSemaphoric`, `label`, `decimalScale` | — | Shared field props forwarded to the schema renderer (only these are forwarded). |
226
+ | `sxSection`, `sxSectionTitle`, `sxRow`, `sxRowMultiple` | `SxProps<Theme>` | Layout styling. |
227
+
228
+ Behavior worth knowing:
229
+
230
+ - It renders an empty fragment until the schema for `model` is in `pageForm`.
231
+ - Navigation precedence on exit: `exit` → `exitPath` → `history.back()`.
232
+ - A dirty form installs a `beforeunload` guard.
233
+ - DRF field errors returned by a serializer `validate()` are mapped onto the form fields.
234
+ - On unmount it resets `pageForm`.
235
+
236
+ #### `<FormBySchema />` — form body (no page chrome)
237
+
238
+ `src/components/forms/FormBySchema.tsx`
239
+
240
+ Renders a schema-driven form body. It does **not** create the react-hook-form context, so
241
+ you pass `schema`, `control`, `errors` and (usually) `getValues`/`setValue`.
242
+
243
+ Additional props beyond the shared field props (§4.2):
244
+
245
+ | Prop | Type | Purpose |
246
+ | --- | --- | --- |
247
+ | `schema` | `SchemaType<T>` | Required. |
248
+ | `control` | `Control<Item<T>>` | Required. |
249
+ | `errors` | `FieldErrors<Item<T>>` | Required. |
250
+ | `fieldsLayout` | `FormFieldLayout<T>[]` | Sections/rows/custom elements. |
251
+ | `customFieldFormLayouts` | `CustomFormFieldLayouts<T>` | Per-relation layouts. |
252
+ | `hiddenFields` | `FieldList<T>` | Fields not rendered. |
253
+ | `fieldsProps` | `FieldsProps<T>` | Per-field overrides. |
254
+ | `setOptionsAC` | `(x: OptionsAC<T>) => void` | Cache setter when you own the options. |
255
+ | `isolatedGetAutoComplete` | loader | Replace the autocomplete loader. |
256
+ | `relatedEditable` | `boolean` | Disable inline relation dialogs. |
257
+ | `autoFocusField` | `FieldKey<T>` | Initial focus. |
258
+ | `elevation` | `number` | Card elevation. |
259
+ | `conditionalVisible` / `watchedValues` | internal | Usually supplied by `<Form>`/`DialogFormBySchema`; do not set by hand. |
260
+ | `sxSection`, `sxSectionTitle`, `sxRow`, `sxRowMultiple` | `SxProps<Theme>` | Layout styling. |
261
+
262
+ #### `<DialogFormBySchema />` — schema-driven dialog form
263
+
264
+ `src/components/forms/DialogFormBySchema.tsx`
265
+
266
+ | Prop | Type | Purpose |
267
+ | --- | --- | --- |
268
+ | `schema` | `SchemaType<T>` | Required. |
269
+ | `validationSchema` | `AnyObjectSchema` | Required yup schema. |
270
+ | `initialValues` | `Item<T>` | Required. |
271
+ | `onEditModelSave` | `(data: Item<T>) => Promise<boolean \| AxiosError>` | Save callback. |
272
+ | `setDialog` | `(x: Partial<DialogType>) => void` | Close/update the dialog. |
273
+ | `getAutoComplete` | loader | Autocomplete loader. |
274
+ | `optionsAC` / `hiddenFields` / `fieldsLayout` | — | Options cache, hidden fields, layout. |
275
+ | `setOptionsAC` | `(x: Partial<OptionsAC<T>>) => void` | Required by the prop type, but currently unused (pass the cache through `optionsAC`). |
276
+ | `isCancelDisabled` | `boolean` | Disable cancel (e.g. while saving). |
277
+ | `forceSaveEnabled` | `boolean` | Keep the save button enabled even when the form is not dirty. |
278
+
279
+ #### `<FormWatcher />` — invisible watcher
280
+
281
+ `src/components/forms/FormWatcher.tsx`
282
+
283
+ | Prop | Type | Purpose |
284
+ | --- | --- | --- |
285
+ | `control` | `Control<GenericValue>` | Required form control. |
286
+ | `watch` | `FieldList<T>` | Required list of field names. |
287
+ | `onChange` | `(values: FormWatchObject<T>) => void` | Called on change (deep-equality guarded). |
288
+
289
+ `<Form>` wraps it for you through `watchValues` + `onWatchChange`; use it directly only
290
+ inside a custom `FormProvider`.
291
+
292
+ #### `<FieldBySchema />` — one field
293
+
294
+ `src/components/forms/FieldBySchema.tsx` — routes the schema `type` to the right widget.
295
+
296
+ | Prop | Type | Purpose |
297
+ | --- | --- | --- |
298
+ | `name` | `FieldKey<T>` | Required field name. |
299
+ | `schema` | `SchemaType<T>` | Required full schema. |
300
+ | `control` | `Control<Item<T>>` | Required. |
301
+ | `errors` | `FieldErrors<Item<T>>` | Required. |
302
+ | `autoFocus` | `boolean` | Initial focus. |
303
+ | plus | `CommonFieldProps<T>` | `fieldKey`, `labelKey`, `index`, `optionsAC`, `optionsModel`, `getOptionLabel`, `renderOption`, `onEditModel`, `fieldsLayout`, `options`, `multiline`, `isSemaphoric`, `label`, `onValueChange`, `decimalScale`, `type`, `autoComplete`, `minRows`, `optionIdKey`, `optionLabelKey`, `disabled`, `sx`. |
304
+
305
+ #### Password forms
306
+
307
+ | Component | Props | Purpose |
308
+ | --- | --- | --- |
309
+ | `ChangePasswordForm` | `onSuccess?: () => void`, `submitLabel?: string` | Current/new/confirm password; calls `changePassword`. |
310
+ | `ForgotPasswordForm` | `token?: string`, `uid?: string`, `onSuccess?: () => void`, `onBack?: () => void` | With `token`+`uid` it shows the "set new password" step; without them it requests the reset e-mail. |
311
+
312
+ ### 3.2 Lists and grids
313
+
314
+ #### `<GenericModelList />` — main list page
315
+
316
+ `src/components/GenericModelList.tsx`
317
+
318
+ | Prop | Type | Purpose |
319
+ | --- | --- | --- |
320
+ | `model` | `string` | Required. |
321
+ | `paginationMode` | `'server' \| 'client'` | Required. `client`: fetch all (batched); `server`: filter/sort/paginate/totals on the server. |
322
+ | `columnFields` | `FieldList<T>` | Columns to show, in order. Defaults to every schema field. |
323
+ | `hiddenFields` | `FieldList<T>` | Columns hidden by default. |
324
+ | `creatableFields` | `FieldList<T>` | Fields editable when adding a row. |
325
+ | `disabledFields` | `FieldList<T>` | Fields never editable. |
326
+ | `minWidthFields` | `MinWidthFields<T>` | Per-column min width. |
327
+ | `indexField` | `FieldKey<T>` | Main/link column. |
328
+ | `indexFieldBasePath` / `indexFieldViewBasePath` | `string` | Paths used by the index link. |
329
+ | `addExistingModel` | `string` | Enables "add existing" for a relation list. |
330
+ | `isEditable` | `boolean` | Inline editing. |
331
+ | `hasBulkSelect`, `onSelectActions` | `boolean`, `OnSelectActions[]` | Selection + bulk actions (`'bulkDelete'`, `'bulkCreate'`, or a custom function). |
332
+ | `actions` | `RowAction<T>[]` | Row actions; default `['editInline', 'remove']`. |
333
+ | `nonEditableActions` | `NonEditableRowAction<T>[]` | Read-only row actions (`'edit'`, `'view'`, custom). |
334
+ | `customActions` | `CustomAction[]` | Extra toolbar/grid actions (`{ key, icon, label, handleClick }`). |
335
+ | `customColumnOperations` | `(column) => column \| Promise<column>` | Last-chance column mutation. |
336
+ | `customLinkDestination` | `(params: GridRenderCellParams) => string` | Custom href for cells. |
337
+ | `customFieldFormLayouts` | `CustomFormFieldLayouts<T>` | Nested relation layouts. |
338
+ | `sumRows` | `SumRowsType<T>` | Totals; in `server` mode also sends `sum_rows` and renders facets. |
339
+ | `defaultFilter` | `GridFilterModel` | Initial filter, merged with the toolbar filter in server mode. |
340
+ | `queryParams` | `string[]` | Extra raw query params appended to every request. |
341
+ | `defaultValues` | `PartialItem<T>` | Defaults for new inline rows. |
342
+ | `optionsAC` | `OptionsAC<T>` | External options cache. |
343
+ | `extraValidators` | `ExtraValidators<T>` | Per-field yup overrides for inline editing. |
344
+ | `onProcessRow` | `(row?: Item<T>) => void` | After an inline save (row) or delete. |
345
+ | `onDataChange` | `(rows: Item<T>[]) => void` | After any grid data change. |
346
+ | `watchEditingRowValues` | `WatchEditingRowValues<T>` | Observe the row being edited; receives `{ rowEditingValues, setRowEditingValues }`. |
347
+ | `reloadStateStamp` | `number \| string` | Change to reload the list. |
348
+ | `isAutoHeight` | `boolean` | Default `true`. |
349
+ | `tableAutoHeight` | `boolean` | Fill the parent instead of fixed grid height. |
350
+ | `cardsViewOnly` | `boolean` | Force the mobile card layout. |
351
+ | `hasHeader` | `boolean` | Show the model plural name + add link. |
352
+ | `LinkComponent` | `LinkComponentType \| null` | Router link adapter (`{ to, href?, state?, ... }`). |
353
+ | `MobileListRenderItem` | `(info) => ReactElement \| null` | Custom mobile card renderer. `info`: `{ item, index, id }` where `item` is formatted. |
354
+ | `MobileRenderNoResults` | `() => ReactElement \| null` | Empty state for mobile. |
355
+ | `disableScreenLoading` | `boolean` | Skip the global loading backdrop. |
356
+ | `hideFooterComponent`, `hideToolbarComponent`, `hideColumnsButton`, `hideFilterButton`, `hideDensityButton`, `hideExportButton`, `hideQuickFilterBar` | `boolean` | UI toggles. |
357
+
358
+ #### `<GenericRelatedModelList />` — nested collection
359
+
360
+ `src/components/GenericRelatedModelList.tsx`
361
+
362
+ Most of the same grid props (minus `disableScreenLoading`, `hasHeader` and
363
+ `customLinkDestination`), plus:
364
+
365
+ | Prop | Type | Purpose |
366
+ | --- | --- | --- |
367
+ | `model` | `string` | Parent model. |
368
+ | `id` | `Id` | Parent id. |
369
+ | `relatedModel` | `string` | Related model slug (the nested route segment). |
370
+ | `label` | `string` | Card/table title. |
371
+ | `paginationMode` | `'server' \| 'client'` | Required. |
372
+ | `usuaria` | `Item \| null` | Kept for compatibility; not used by the component. |
373
+ | `isInBatches` | `boolean` | Default `true`. |
374
+ | `noCardWrapper` | `boolean` | Render without the surrounding `Card`. |
375
+ | `hiddenFields` | `FieldList<T>` | Default `['id']`. |
376
+ | `isEditable` | `boolean` | Default `true`. |
377
+
378
+ It requests `/<model>/<id>/<relatedModel>/` and offers add/edit/remove for those rows.
379
+
380
+ #### `<DataGridBySchemaEditable />` — lower-level grid
381
+
382
+ `src/components/DataGridBySchemaEditable.tsx`
383
+
384
+ Desktop DataGrid + mobile cards. Use it for fully custom grids; the list components above
385
+ wrap it. Required: `schema`, `data`, `columns`, `model`, `onEditModel`.
386
+
387
+ Other props: `rowCount`, `columnVisibilityModel`, `fieldKey`, `labelKey`, `index`,
388
+ `name`, `indexField` (default `'nome'`), `addExistingModel`, `indexFieldMinWidth`,
389
+ `indexFieldBasePath`, `indexFieldViewBasePath`, `stateToLink`, `minWidth`, `loading`,
390
+ `modelParent`, `modelParentId`, `customColumnOperations`, `customFieldFormLayouts`,
391
+ `customLinkDestination`, `LinkComponent`, `onProcessRow`, `onDataChange`,
392
+ `watchEditingRowValues`, `isEditable` (default `false`), `nonEditableActions`,
393
+ `hasBulkSelect`, `cardsViewOnly`, `onSelectActions`, `sx`, `isAutoHeight`,
394
+ `defaultValues`, `hideFooterPagination`, `setVisibleRows`, `paginationModel`,
395
+ `setPaginationModel`, `hideFooterComponent`, `hideToolbarComponent`, `tableAutoHeight`,
396
+ `actions` (default `['editInline', 'remove']`), `customActions`, `hideColumnsButton`,
397
+ `hideFilterButton`, `hideDensityButton`, `hideExportButton`, `hideQuickFilterBar`,
398
+ `optionsAC`, `extraValidators`, `MobileListRenderItem`, `MobileRenderNoResults`.
399
+
400
+ #### `<DataTotals />` — client-side totals
401
+
402
+ `src/components/DataTotals.tsx`
403
+
404
+ | Prop | Type |
405
+ | --- | --- |
406
+ | `schema` | `SchemaType` |
407
+ | `data` | `Item[]` |
408
+ | `sumRows` | `SumRowsType` (required to render) |
409
+ | `visibleRows` | `GridRowId[]` (optional; sum only visible rows) |
410
+
411
+ There is no public `DataTotalsServer`: server totals/facets are rendered internally by
412
+ `GenericModelList`. (This is a correction to older docs that listed it as exported.)
413
+
414
+ ### 3.3 Detail views
415
+
416
+ #### `<DetailBySchema />` — read-only detail body
417
+
418
+ `src/components/details/DetailBySchema.tsx`
419
+
420
+ | Prop | Type | Purpose |
421
+ | --- | --- | --- |
422
+ | `values` | `Item<T>` | Required record. |
423
+ | `schema` | `SchemaType<T>` | Required. |
424
+ | `columns` | `GridEnrichedBySchemaColDef<T>[]` | Required; drives field order/labels. |
425
+ | `loading` | `boolean` | Loading state. |
426
+ | `editLink` / `editLabel` | `string` | Optional edit button. |
427
+ | `fieldsLayout` | `DetailFieldLayout<T>[]` | Sections/accordions. See §4.6. |
428
+ | `hiddenFields` | `FieldList<T>` | Default `['id']`. |
429
+ | `fieldsProps` | `DetailFieldsProps<T>` | Per-field overrides/formatters. |
430
+ | `elevation` | `number` | Card elevation. |
431
+ | `sxSection`, `sxSectionTitle`, `sxRow`, `sxRowMultiple`, `sxField`, `sxLabel`, `sxValue`, `sxValueList`, `sxValueListItem`, `sxValueListItemText` | `SxProps<Theme>` | Styling. |
432
+
433
+ #### `<DetailFieldBySchema />` — one read-only field
434
+
435
+ | Prop | Type | Purpose |
436
+ | --- | --- | --- |
437
+ | `fieldKey` | `FieldKey` | Required label/fallback. |
438
+ | `fieldSchema` | `Field` | Metadata; without it the component logs an error and renders nothing. |
439
+ | `value` | `FormattedItemValue` (`{ title, valueStr, value }`) | The formatted value. |
440
+ | `item` | `Item<T>` | Raw record, passed to formatters. |
441
+ | `valueFormatter` | `(value, item, fieldSchema?) => any` | Format before display. |
442
+ | `renderValue` | `({ value, item, fieldSchema, label }) => ReactNode` | Replace the value node. |
443
+ | `emptyValue` | `ReactNode` | Default `-`. |
444
+ | `hideIfEmpty` | `boolean` | Render nothing when empty. |
445
+ | `optionIdKey` / `optionLabelKey` | `string` | Choice/relation keys. |
446
+ | `sxField`, `sxLabel`, `sxValue`, `sxValueList`, `sxValueListItem`, `sxValueListItemText` | `SxProps<Theme>` | Styling. |
447
+
448
+ #### `<DetailPage />` — full detail page
449
+
450
+ `src/components/details/DetailPage.tsx` — header (back/title/edit), optional hero and
451
+ tabs; no router dependency.
452
+
453
+ Props: `model`, `objId`, `titleField`, `title`, `backLink`, `backLabel` (default
454
+ `'Back'`), `onBack` (takes precedence over `backLink`), `editLink`, `editLabel`,
455
+ `hero`, `tabs` (`DetailTabConfig[]`: `{ key, label, sections?, render?, count?,
456
+ hidden?, icon? }`), `sections`, `fieldsLayout` (alias of `sections`), `hiddenFields`,
457
+ `fieldsProps`, `elevation` (default `0`), `renderHeader`, `renderShell`, `emptyLabel`,
458
+ `errorLabel`, `retryLabel`, plus the shared `sx*` props.
459
+
460
+ #### `useDetailData()` — detail data loader
461
+
462
+ `src/hooks/useDetailData.ts`
463
+
464
+ ```ts
465
+ const { values, schema, columns, loading, error, reload } = useDetailData({ model, objId });
466
+ ```
467
+
468
+ `UseDetailDataResult`: `{ values, schema, columns, loading, error, reload }`.
469
+ `UseDetailDataProps` also declares `titleField`, but the hook currently only uses
470
+ `model` and `objId`.
471
+
472
+ ### 3.4 Small shared components
473
+
474
+ | Component | Props | Purpose |
475
+ | --- | --- | --- |
476
+ | `HtmlContent` | `{ html?: string \| null; sx?: SxProps<Theme> }` | Sanitized read-only HTML. Uses `dompurify` when installed, otherwise strips tags. |
477
+ | `FileTypeIcon` | `{ name?: string }` | Icon for a file name/URL extension. Exports `FILE_TYPE_ICONS` and `DEFAULT_FILE_ICON` from the same module (not from the package root). |
478
+ | `DialogActions` | `{ setDialog, handleSave?, btnCancel?, btnConfirm? }` | Cancel/confirm buttons for a `setDialog` body. |
479
+
480
+ ### 3.5 Field widgets and grid cells
481
+
482
+ These are exported for composition inside custom layouts. All form widgets accept
483
+ `FieldBySchemaProps<T>` (`name`, `schema`, `control`, `errors`, plus `CommonFieldProps`):
484
+
485
+ `TextFieldBySchema`, `HtmlFieldBySchema`, `BooleanFieldBySchema`,
486
+ `DesktopDatePickerBySchema`, `DesktopDateTimePickerBySchema`,
487
+ `AutocompleteFieldBySchema`, `EditableAutocompleteFieldBySchema`, `FloatFieldBySchema`.
488
+
489
+ Grid editing primitives (they rely on the DataGrid edit context, not on
490
+ react-hook-form): `SelectEditInputCell`, `GridDateInput`, `GridDecimalInput`,
491
+ `GridPatternInput`, `BooleanInputCell`.
492
+
493
+ Leaflet helpers: `applyDefaultLeafletIcon`, `applyThemeMarkerIcon`,
494
+ `DRF_REACT_BY_SCHEMA_MARKER_ICON`, `DEFAULT_LAYERS`; text lookup: `getThemeTexts`.
495
+
496
+ ---
497
+
498
+ ## 4. Shared prop building blocks
499
+
500
+ These types are exported from `@types` and reused by the components above.
501
+
502
+ ### 4.1 `fieldsLayout` (forms)
503
+
504
+ `FormFieldLayout<T>` = `{ title?, rows?, customLabels?, CustomElement? }` per section.
505
+
506
+ - `rows` is an array of: a field name, an array of fields (same row), or a
507
+ `CustomFormField` (`{ key, CustomElement }`).
508
+ - `customLabels` overrides specific labels in that section.
509
+ - `CustomElement` replaces the whole section body.
510
+
511
+ ### 4.2 `CommonFieldProps` (shared by field widgets/forms)
512
+
513
+ `value`, `multiline`, `setValue`, `getValues`, `fieldKey`, `labelKey`, `index`,
514
+ `optionsAC`, `optionsModel`, `getOptionLabel`, `renderOption`, `onEditModel`,
515
+ `fieldsLayout`, `multiple`, `sx`, `options`, `isSemaphoric`, `label`, `onValueChange`,
516
+ `decimalScale`, `type`, `autoComplete`, `minRows`, `optionIdKey`, `optionLabelKey`,
517
+ `disabled`.
518
+
519
+ ### 4.3 `fieldsProps`
520
+
521
+ `Record<fieldName, CommonFieldProps>` — per-field widget overrides, e.g.
522
+ `fieldsProps={{ observacao: { minRows: 5 }, comunidade: { disabled: true } }}`.
523
+
524
+ ### 4.4 `hiddenFields`, `columnFields`, `creatableFields`, `disabledFields`
525
+
526
+ `FieldList<T>` (array of field names). They filter columns/fields; in forms,
527
+ `hiddenFields` values are preserved as hidden inputs on save.
528
+
529
+ ### 4.5 `sumRows`
530
+
531
+ `SumRowsType<T>` = `{ rows: SumRowsItem<T>[]; severity?: AlertColor }`, where a row is
532
+ `{ field, prefix?, suffix?, isCount?, facetBy?, facetLimit? }`.
533
+
534
+ - In `client` mode `DataTotals` sums the loaded rows (optionally only `visibleRows`).
535
+ - In `server` mode the same rows become the `sum_rows` query and `DataTotalsServer`
536
+ (internal) renders `sumRowsTotals` + `facets`.
537
+ - `facetBy` accepts field keys or nested forward relations (`'comunidade__municipio'`).
538
+
539
+ ### 4.6 `fieldsLayout` (details)
540
+
541
+ `DetailFieldLayout<T>` = `{ title?, rows?, CustomElement?, isAccordion?, isAccordionOpen?,
542
+ hidden?, hideIfEmpty? }`.
543
+
544
+ - `rows` accepts field paths, arrays (same row), or `CustomDetailField`
545
+ (`{ key, CustomElement }`).
546
+ - Dotted paths (`'contratacao.valor'`) resolve nested values/metadata.
547
+ - `hidden(values)` omits the section; `hideIfEmpty` omits it when every field is empty.
548
+
549
+ ### 4.7 `fieldsProps` (details)
550
+
551
+ `DetailFieldsProps<T>` — per-field `DetailCommonFieldProps`: `valueFormatter`,
552
+ `renderValue`, `emptyValue`, `hideIfEmpty`, `item`, `fieldSchema`, plus `sx*` overrides
553
+ and the shared `CommonFieldProps`.
554
+
555
+ ### 4.8 Actions
556
+
557
+ - `ActionType`: `'editInline' | 'remove' | 'edit' | 'view'`.
558
+ - `RowAction<T>`: an `ActionType` or `CustomRowAction` (`{ icon, label?, handleClick(item) }`).
559
+ - `NonEditableRowAction<T>`: `'edit' | 'view' | CustomRowAction`.
560
+ - `CustomAction<T>`: `{ key, icon, label, handleClick(item) }` — extra toolbar action.
561
+ - `OnSelectActions`: `{ title, action: 'bulkDelete' | 'bulkCreate' | custom(selectedData, bulkUpdateData) }`.
562
+
563
+ ### 4.9 `optionsAC` and relation loading
564
+
565
+ `OptionsAC<T>` is `Record<model, OptionsACItem[]>` (usually
566
+ `AutocompleteItem = { id, label }`, choice items are also accepted). It is populated
567
+ automatically for relation fields; pass it only when you need to preload/override
568
+ options.
569
+
570
+ ### 4.10 `extraValidators`
571
+
572
+ `Record<fieldName, YupSchema>` replacing the generated validator for those fields.
573
+ Useful for cross-field rules, e.g.
574
+ `{ fim: yup.date().min(yup.ref('inicio'), '...') }` (resolve the ref against the form
575
+ values).
576
+
577
+ ### 4.11 `LinkComponent`
578
+
579
+ `React.ComponentType<{ to: string | object; href?; state?; className?; style?; children? }>`
580
+ — lets the package navigate with react-router (or any router) instead of plain anchors.
581
+
582
+ ### 4.12 `FormSaveActions`
583
+
584
+ Enum passed to `Form.onSave` as `buttonType`:
585
+ `EXIT_ON_SAVE = 'exitOnSave'`, `SAVE_AND_CONTINUE = 'saveAndContinue'`,
586
+ `SAVE_AND_CREATE_NEW = 'saveAndCreateNew'`.
587
+
588
+ ### 4.13 `reloadStateStamp`
589
+
590
+ Any `number | string`. When it changes, `<Form>`, `<GenericModelList>` and
591
+ `<GenericRelatedModelList>` reload their data. Useful after an external mutation.
592
+
593
+ ### 4.14 `conditional_visible`
594
+
595
+ Schema key (produced by Django) that hides fields until a condition holds. Hidden
596
+ fields are cleared on save. Shapes accepted: a `[field, value]` pair, an array of
597
+ conditions (OR, legacy), or `{ operator: 'AND' | 'OR', conditions: [...] }` (nestable).
598
+ `ui_required` fields are required only while visible. See the README section for a
599
+ worked example.
600
+
601
+ ---
602
+
603
+ ## 5. Utility functions
604
+
605
+ Exported from the package root.
606
+
607
+ | Function | Signature | Purpose |
608
+ | --- | --- | --- |
609
+ | `setAuthToken` | `(token: string \| null) => void` | Stores the access token in `localStorage` and sets/clears the axios `Authorization` header. `null` also removes the refresh token. |
610
+ | `hasJWT` | `() => boolean` | Whether an access token is stored. |
611
+ | `clearJWT` | `() => false` | Removes the access token (and returns `false`). |
612
+ | `AUTH_EXPIRED_EVENT` | `string` constant | Dispatched on `window` when the token refresh fails. Listen to it to sign the user out. |
613
+ | `emptyByType` | `(field: Field, forDatabase = false) => any` | Empty value for a field type (`''`, `[]`, `false`, `0`, `null`, …). `forDatabase` fixes nested-object/`field` edge cases. |
614
+ | `populateValues` | `({ data, schema }) => Item` | Fills missing values with `emptyByType` and normalizes choices/relations for the forms. |
615
+ | `enrichChoices` | `(rawData, schema) => Item<T>` | Shapes raw choice values into `{ value, display_name }` (and lists into arrays of them). |
616
+ | `getChoiceByValue` | `(value, choices?) => string \| null \| undefined` | Choice `display_name` for a raw value. |
617
+ | `buildGenericYupValidationSchema` | `({ data, schema, many?, skipFields?, extraValidators?, uiFields? }) => ObjectSchema` | Builds the yup schema used by forms/grids from field metadata. |
618
+ | `getDataGridColumns` | `(schema, columnFields?, creatableFields?, disabledFields?) => GridEnrichedBySchemaColDef[] \| false` | Derives grid columns from a schema. |
619
+ | `errorProps` | `({ errors, fieldKey, fieldKeyProp, index? }) => { error, helperText }` | Maps react-hook-form errors to MUI `TextField`/grid props. |
620
+ | `getFieldErrorsFromResponse` | `(response, schema?) => Record<field, { type, message }>` | Normalizes DRF validation errors for `setError` (skips unknown keys and `non_field_errors`). |
621
+ | `getTmpId` | `() => string` | Generates a `tmp<n>` id for unsaved rows. |
622
+ | `isTmpId` | `(id) => boolean` | `true` for empty/`'novo'`/`tmp*` ids. |
623
+ | `isReadOnlyField` | `(field?) => boolean` | Read-only derived field (relations are not considered read-only here). |
624
+ | `isRelationField` | `(field?) => boolean` | `type` is `'field'` or `'nested object'`. |
625
+ | `getPatternFormat` | `(type: string) => string` | Input mask for known field types (`telefone`, `cpf`, `cnpj`, …). |
626
+ | `slugToCamelCase` | `(str, includeFirst = false) => string` | Converts slugs to camelCase (`my_model` → `myModel`); `includeFirst` capitalizes the first letter (`MyModel`). |
627
+
628
+ ---
629
+
630
+ ## 6. Error handling contract
631
+
632
+ - Every fetch/write helper resolves to the value **or** an `AxiosError`-shaped object
633
+ (`axios.isAxiosError(result) === true`). Check before use.
634
+ - A `401` triggers a single token refresh; if that fails the package dispatches
635
+ `AUTH_EXPIRED_EVENT` and returns an error.
636
+ - The high-level write helpers (`onSubmit`, `onEditModelSave`, `onDeleteModel`, …) also
637
+ show a snackbar and close dialogs as documented above; the raw helpers (`postData`,
638
+ `updateModel`, `rawDeleteData`) do not.
639
+ - `<Form>` maps per-field DRF errors onto the corresponding form fields; unknown keys
640
+ and `non_field_errors` stay in the generic snackbar message.
641
+
642
+ ---
643
+
644
+ ## 7. Agent notes and gotchas
645
+
646
+ - `watchValues`/`onWatchChange` only run when **both** are provided. `onWatchChange`
647
+ receives an object keyed by the listed field names, and is only called when the
648
+ watched values actually change (deep equality).
649
+ - `GenericModelList` has no `paginationModel` or `title` prop; the pagination model is
650
+ internal and the header title is the model's `verbose_name_plural`. `fieldsLayout`
651
+ belongs to `<Form>`; lists use `customFieldFormLayouts` for nested forms.
652
+ - `onDeleteModel` is fire-and-forget (confirmation dialog), while
653
+ `onDeleteRelatedModel` returns a promise.
654
+ - `getRawData` prefixes `${serverEndPoint.api}/`; do not pass a leading `/api`.
655
+ - `FormBySchema` needs the react-hook-form `control`/`errors`; it does not create a form.
656
+ - `Form` accepts a `register` prop in its type but ignores it — the component owns the
657
+ react-hook-form instance and passes its own `register` down.
658
+ - `FieldBySchema`/`DetailFieldBySchema` silently skip fields with no schema entry
659
+ (`DetailFieldBySchema` logs an error); always pass the matching schema.
660
+ - `conditional_visible` values are cleared on save; do not rely on stale hidden values.
661
+ - `ui_required` is enforced only for fields present in the rendered layout.
662
+ - Mobile: forms/grids switch to cards below the `md` breakpoint; `cardsViewOnly` forces
663
+ cards on desktop. Always check layouts on mobile.
664
+ - The two `*.md` files next to `DataGridBySchemaEditable` and
665
+ `DRFReactBySchemaProvider` are legacy snippets, not references — use this document.