drf-react-by-schema 0.28.26 → 0.28.28

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/README.md CHANGED
@@ -34,6 +34,16 @@ On the React side, `drf-react-by-schema-react` offers a range of powerful compon
34
34
 
35
35
  The _DRF and React By Schema_ Suite is available for React at https://www.npmjs.com/package/drf-react-by-schema and for Django at https://pypi.org/project/drf-react-by-schema/.
36
36
 
37
+ ## API reference
38
+
39
+ Every exported component (purpose and props), the full `useAPIWrapper()` API (data,
40
+ auth, form state, dialogs, feedback), the shared prop building blocks
41
+ (`fieldsLayout`, `fieldsProps`, `sumRows`, actions, …) and the utility functions are
42
+ documented in **[`docs/API_REFERENCE.md`](docs/API_REFERENCE.md)**.
43
+
44
+ Consuming apps must use `useAPIWrapper()`. The lower-level `useDRFReactBySchema()` is
45
+ internal, is not exported from the package entry and must not be used by app code.
46
+
37
47
  ## Field types & widgets
38
48
 
39
49
  Every schema field `type` maps to a specific widget. This includes the standard
@@ -5,6 +5,9 @@ import { AnySchema } from 'yup';
5
5
  import { CustomToolbarProps } from './CustomToolbar';
6
6
  declare module '@mui/x-data-grid' {
7
7
  interface ToolbarPropsOverrides extends CustomToolbarProps {
8
+ isEditableToolbar?: boolean;
9
+ hideToolbarComponent?: boolean;
10
+ watchEditingRowValues?: WatchEditingRowValues;
8
11
  }
9
12
  }
10
13
  import { SxProps, Theme } from '@mui/material/styles';
@@ -1 +1 @@
1
- {"version":3,"file":"DataGridDesktop.d.ts","sourceRoot":"","sources":["../../../src/components/DataGridBySchemaEditable/DataGridDesktop.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DataGridDesktop.d.ts","sourceRoot":"","sources":["../../../src/components/DataGridBySchemaEditable/DataGridDesktop.tsx"],"names":[],"mappings":"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"}
@@ -32,6 +32,17 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
+ var __rest = (this && this.__rest) || function (s, e) {
36
+ var t = {};
37
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
38
+ t[p] = s[p];
39
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
40
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
41
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
42
+ t[p[i]] = s[p[i]];
43
+ }
44
+ return t;
45
+ };
35
46
  var __importDefault = (this && this.__importDefault) || function (mod) {
36
47
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
48
  };
@@ -59,6 +70,13 @@ const usePreparedColumns_1 = require("./hooks/usePreparedColumns");
59
70
  const useDataGridActions_1 = require("./hooks/useDataGridActions");
60
71
  const useProcessRowUpdate_1 = require("./hooks/useProcessRowUpdate");
61
72
  const RowEditingWatcher_1 = __importDefault(require("./RowEditingWatcher"));
73
+ // Module-level so the slot type stays stable across renders; an inline slot component remounts the toolbar and its quick filter input.
74
+ const InternalToolbar = (_a) => {
75
+ var { isEditableToolbar, hideToolbarComponent, watchEditingRowValues } = _a, props = __rest(_a, ["isEditableToolbar", "hideToolbarComponent", "watchEditingRowValues"]);
76
+ return (react_1.default.createElement(react_1.default.Fragment, null,
77
+ isEditableToolbar && watchEditingRowValues && (react_1.default.createElement(RowEditingWatcher_1.default, { watchEditingRowValues: watchEditingRowValues })),
78
+ !hideToolbarComponent && react_1.default.createElement(CustomToolbar_1.CustomToolbar, Object.assign({}, props))));
79
+ };
62
80
  function DataGridDesktop({ schema, apiRef, dataGridRows, setDataGridRows, rowCount = 0, columns, columnVisibilityModel, model, name = Math.floor(Math.random() * 1000000).toString(), indexField = 'nome', addExistingModel, indexFieldMinWidth = 350, indexFieldBasePath = '', indexFieldViewBasePath, stateToLink = {}, minWidth = 80, loading, modelParent, modelParentId, customColumnOperations, customFieldFormLayouts, customLinkDestination, LinkComponent, onProcessRow, watchEditingRowValues, onEditModel, isEditable = false, nonEditableActions, hasBulkSelect, onSelectActions, sx: sxProp, isAutoHeight, defaultValues, hideFooterPagination, setVisibleRows, paginationModel, setPaginationModel, hideFooterComponent, hideToolbarComponent, tableAutoHeight, actions = ['editInline', 'remove'], customActions, hideColumnsButton, hideFilterButton, hideDensityButton, hideExportButton, hideQuickFilterBar, optionsAC: optionsACExternal, extraValidators, emptyItem, handleDeleteClick, setSnackBar, yupValidationSchema, }) {
63
81
  var _a;
64
82
  const [dataGridLoading, setDataGridLoading] = (0, react_1.useState)(false);
@@ -365,10 +383,7 @@ function DataGridDesktop({ schema, apiRef, dataGridRows, setDataGridRows, rowCou
365
383
  });
366
384
  console.log(e);
367
385
  }, slots: {
368
- // toolbar: hideToolbarComponent ? () => <React.Fragment /> : CustomToolbar,
369
- toolbar: (props) => (react_1.default.createElement(react_1.default.Fragment, null,
370
- isEditable && watchEditingRowValues && (react_1.default.createElement(RowEditingWatcher_1.default, { watchEditingRowValues: watchEditingRowValues })),
371
- !hideToolbarComponent && react_1.default.createElement(CustomToolbar_1.CustomToolbar, Object.assign({}, props)))),
386
+ toolbar: InternalToolbar,
372
387
  footer: hideFooterComponent ? () => react_1.default.createElement(react_1.default.Fragment, null) : InternalFooter,
373
388
  }, slotProps: {
374
389
  toolbar: {
@@ -384,6 +399,9 @@ function DataGridDesktop({ schema, apiRef, dataGridRows, setDataGridRows, rowCou
384
399
  hideDensityButton,
385
400
  hideExportButton,
386
401
  hideQuickFilterBar,
402
+ hideToolbarComponent,
403
+ isEditableToolbar: isEditable,
404
+ watchEditingRowValues,
387
405
  showQuickFilter: true,
388
406
  quickFilterProps: { debounceMs: 500 },
389
407
  handleExportExcel: paginationModel
@@ -52,7 +52,9 @@ interface FormProps<T extends TField = void> extends Omit<CommonFieldProps<T>, '
52
52
  elevation?: number;
53
53
  CustomFormActions?: (props: FormActionsProps) => React.JSX.Element;
54
54
  extraValidators?: ExtraValidators<T>;
55
+ /** Field names monitored on change; requires `onWatchChange`. */
55
56
  watchValues?: FieldList<T>;
57
+ /** Called with `{ [field]: value }` when a watched value changes. */
56
58
  onWatchChange?: (values: FormWatchObject<T>) => void;
57
59
  reloadStateStamp?: number | string;
58
60
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Form.d.ts","sourceRoot":"","sources":["../../../src/components/forms/Form.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Form.d.ts","sourceRoot":"","sources":["../../../src/components/forms/Form.tsx"],"names":[],"mappings":"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"}
@@ -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.
@@ -0,0 +1,145 @@
1
+ # Field types & widgets
2
+
3
+ `drf-react-by-schema` derives every form widget, validation rule and DataGrid column
4
+ directly from the schema generated by `drf-react-by-schema-django` (via the model
5
+ serializers + `OPTIONS`/`sync_react`). This page documents each schema field `type`
6
+ and the matching widget it renders.
7
+
8
+ ## Quick reference
9
+
10
+ The `type` value comes from the schema that Django exposes for each model field.
11
+ The table below maps every type to its **form widget** and its **DataGrid** behavior.
12
+
13
+ | Schema type | Form widget | DataGrid read (cell) | DataGrid edit | Notes |
14
+ | --- | --- | --- | --- | --- |
15
+ | `string` | `TextField` (single line) | text | text input (mask if `pattern_format`) | |
16
+ | `string` + `model_html` | **Tiptap WYSIWYG** (`HtmlFieldBySchema`) | sanitized **HTML** | text input | optional peers; falls back to multiline `TextField` |
17
+ | `email` | `TextField` (email) | text | text input | |
18
+ | `password` | `TextField` (`type="password"`) | text (hidden) | text input | |
19
+ | `number`, `integer` | `TextField` (numeric) | number (pt-BR formatted) | numeric input | `min`/`max` from validators |
20
+ | `decimal`, `float` | `FloatFieldBySchema` (decimal input) | number (pt-BR, `decimal_places`) | `GridDecimalInput` | honors `decimal_places`, `is_currency` |
21
+ | `date` | `DesktopDatePickerBySchema` (MUI date picker) | date (`date_views` format) | `GridDateInput` | |
22
+ | `datetime` | `DesktopDateTimePickerBySchema` (MUI date-time picker) | date-time (`DD/MM/YYYY HH:mm`) | `GridDateTimeInput` | |
23
+ | `boolean` | `BooleanFieldBySchema` (switch) | `Sim` / `Não` | `BooleanInputCell` | |
24
+ | `choice` | `AutocompleteFieldBySchema` (single select) | label (join) | `SelectEditInputCell` | `choices` provide options |
25
+ | `list` / `multiple choice` | `AutocompleteFieldBySchema` (multi-select) | label (comma-joined) | `SelectEditInputCell` (`multiple`) | `many: true`; child `choices` |
26
+ | `field` / `nested object` | `EditableAutocompleteFieldBySchema` (related) | related label | `SelectEditInputCell` | `related_editable` enables editing via dialog |
27
+ | `file` / `file upload` | `FileFieldBySchema` (upload) | file name | `FileInputCell` | `allowed_mime_types` |
28
+ | `image` / `image upload` | `FileFieldBySchema` (image upload) | thumbnail preview | `FileInputCell` | `allowed_mime_types` |
29
+ | `array` | `TextField` (default fallback) | text | text input | |
30
+ | `slug` | `TextField` | text | text input | forced `read_only` |
31
+ | `point` | Leaflet **point picker** (see Geometry below) | **thumb map** | **modal** point picker | requires PostGIS + leaflet |
32
+ | `polygon` | Leaflet **polygon draw** (see Geometry below) | **thumb map** | **modal** polygon draw | requires PostGIS + leaflet |
33
+ | `multipoint` | Leaflet **multi-point picker** (see Geometry below) | **thumb map** | **modal** multi-point picker | requires PostGIS + leaflet |
34
+ | `multipolygon` | Leaflet **multi-polygon draw** (see Geometry below) | **thumb map** | **modal** multi-polygon draw | requires PostGIS + leaflet |
35
+ | `geometrycollection` | Leaflet **points + polygons draw** (see Geometry below) | **thumb map** | **modal** geometry draw | requires PostGIS + leaflet |
36
+
37
+ ## Rich text fields (`model_html`)
38
+
39
+ A Django `TextField(rich_text=True)` (from `drf_react_by_schema.fields`) keeps its plain
40
+ `string` type but adds `model_html: true` to the schema. The value **is HTML** — the same
41
+ format the Django admin's Summernote widgets store — so no conversion or migration is
42
+ needed for existing admin-authored content.
43
+
44
+ ### Requirements
45
+
46
+ The editor is optional, like the map widgets. Install Tiptap in the **consuming app**:
47
+
48
+ ```bash
49
+ npm install @tiptap/react @tiptap/starter-kit
50
+ # optional: sanitizes rich text in detail views
51
+ npm install dompurify
52
+ ```
53
+
54
+ Without Tiptap the field falls back to the multiline text field (raw HTML editable as
55
+ text), and the form keeps working. Without `dompurify` the detail view renders the value
56
+ as plain text (tags stripped) instead of injecting HTML.
57
+
58
+ ### Widgets and behaviour
59
+
60
+ - **Form** (`HtmlFieldBySchema` → `RichTextEditorInput`): a Tiptap editor with an MUI
61
+ toolbar — paragraph/H1–H3, bold, italic, underline, strikethrough, bullet and ordered
62
+ lists, quote, code block, horizontal rule, link (dialog), clear formatting, undo/redo.
63
+ The form value is the editor's HTML (`getHTML()`), empty content is saved as `''`.
64
+ - **Detail** (`DetailFieldBySchema` → `HtmlContent`): the HTML is sanitized with
65
+ DOMPurify against an allow-list of tags/attributes, then rendered read-only with the
66
+ package's typography scale.
67
+ - **Grid**: the cell shows the stored HTML as text (no rendering); the toolbar strings are
68
+ localized through `theme.texts.richText` (see `docs/localization.md`).
69
+
70
+ Sanitization happens on **render**, not on save: the API keeps the HTML verbatim, so any
71
+ other client of the same column must sanitize it too.
72
+
73
+ ## Geometry fields (PostGIS)
74
+
75
+ Geometry field types map to Django's GeoDjango model fields (`PointField`,
76
+ `PolygonField`, `MultiPointField`, `MultiPolygonField`, `GeometryCollectionField`)
77
+ serialized as **GeoJSON**. They are only emitted when the database engine is PostGIS.
78
+
79
+ ### Requirements
80
+
81
+ The map widgets are optional. They require the following packages to be installed in
82
+ the **consuming app** (they are declared as *optional* peer dependencies of
83
+ `drf-react-by-schema`):
84
+
85
+ ```bash
86
+ npm install leaflet react-leaflet leaflet-draw
87
+ ```
88
+
89
+ If any of them is missing, the field falls back to a plain widget so the app keeps
90
+ working:
91
+
92
+ - **`point`** → a single-line text input.
93
+ - **`polygon`, `multipoint`, `multipolygon`, `geometrycollection`** → a multiline text area.
94
+
95
+ In both cases the value stays a GeoJSON string.
96
+
97
+ ### Widgets and behaviour
98
+
99
+ All geometry widgets share the same map + text editing UI:
100
+
101
+ - The **map** shows and edits the geometry over an OpenStreetMap base layer.
102
+ - A **text field** below the map is **always synchronized in both directions**:
103
+ - paste/edit a GeoJSON string → it renders immediately on the map;
104
+ - draw/move on the map → the text field updates to the generated GeoJSON.
105
+ - **Malformed (or wrong-type) GeoJSON** text is flagged as an **input error** — the
106
+ message shows `Dados malformados, corrija o GeoJSON para refletir no mapa.` and the
107
+ previously drawn features are kept on the map (they are not wiped).
108
+ - The read-only **grid cell** renders a **thumb map**: a static mini-map fitted to the
109
+ geometry with padding + a zoom cap, so the shape is shown with some surrounding
110
+ context.
111
+ - The **grid edit cell** is a button that opens a **modal** with the picker/drawer + the
112
+ synced GeoJSON text field.
113
+
114
+ Behaviour per type:
115
+
116
+ | `type` | Can add | Move existing | Remove existing | Single instance? |
117
+ | --- | --- | --- | --- | --- |
118
+ | `point` | add **one** marker | drag marker | via grid edit delete | **yes** |
119
+ | `polygon` | draw **one** polygon | draw-edit (drag/reshape) | via edit delete | **yes** |
120
+ | `multipoint` | add **many** markers | drag each marker | via edit delete | no |
121
+ | `multipolygon` | add **many** polygons | draw-edit each | via edit delete | no |
122
+ | `geometrycollection` | **many** points **and** polygons | both | both | no |
123
+
124
+ - For the **single-instance** types (`point`, `polygon`), once one shape exists the
125
+ corresponding "add" tool is hidden from the toolbar — the shape can be **moved** and
126
+ **removed**, and removing it re-enables adding.
127
+ - Points (`point`, `multipoint`, and the points in `geometrycollection`) are **draggable**
128
+ markers: drag to move, and the new coordinates are written back immediately.
129
+ - Polygons (and the polygon parts of `multipolygon` / `geometrycollection`) use
130
+ Leaflet-Draw's edit control to **move/resize** and its delete tool to remove.
131
+
132
+ ### Server side
133
+
134
+ | Model field (drf-react-by-schema) | Schema `type` | GeoJSON |
135
+ | --- | --- | --- |
136
+ | `fields.PointField` | `point` | `Point` |
137
+ | `fields.PolygonField` | `polygon` | `Polygon` |
138
+ | `fields.MultiPointField` | `multipoint` | `MultiPoint` |
139
+ | `fields.MultiPolygonField` | `multipolygon` | `MultiPolygon` |
140
+ | `fields.GeometryCollectionField` | `geometrycollection` | `GeometryCollection` |
141
+
142
+ > Tip: after adding/editing these model fields, run Django's `manage.py sync_react` to
143
+ > regenerate the TypeScript types — geometry fields map to the corresponding GeoJSON
144
+ > types (`Point`, `Polygon`, `MultiPoint`, `MultiPolygon`, `GeometryCollection`) rather
145
+ > than `any`.
@@ -0,0 +1,173 @@
1
+ # Localization & theming
2
+
3
+ ## Overview
4
+
5
+ `drf-react-by-schema` exposes its user-facing strings through the **MUI theme**, so a
6
+ consuming app can localize (translate) every widget in **one place** — its own theme.
7
+ This follows MUI's own approach (MUI components take `localeText`/`locale` props and the
8
+ DataGrid locale lives on the theme), keeps the strings out of `DRFReactBySchemaProvider`,
9
+ and works out of the box with sensible defaults.
10
+
11
+ ## How it works
12
+
13
+ - The package **augments the MUI theme** (`@mui/material/styles`) with a typed `texts`
14
+ field (`Theme.texts` / `ThemeOptions.texts`).
15
+ - Package **defaults** (English) are baked into the package's **default theme**; apps
16
+ override any namespace they want in their own theme.
17
+ - `DRFReactBySchemaProvider` **deepmerges** the app's `theme` onto that default theme
18
+ (`deepmerge(defaultTheme, theme)`), so an app only sets the keys it wants.
19
+ - Widgets read the texts through `useTheme()` plus the exported `getThemeTexts(theme)`
20
+ helper, falling back to the built-in defaults when `theme.texts` is absent.
21
+
22
+ Because everything lives in the theme, it works under `DRFReactBySchemaProvider` **and**
23
+ under any plain `ThemeProvider` (texts simply fall back to the package defaults).
24
+
25
+ ## Available texts (namespaces)
26
+
27
+ The `texts` shape is extensible — add new namespaces over time. Currently:
28
+
29
+ | Namespace | Used by | Keys |
30
+ | --------- | ------- | ---- |
31
+ | `texts.geometry` | geometry picker (`MapGeometryPicker`) and grid edit/thumb cells (`GeoEditCell`) | `labelPoint`, `labelOther`, `malformed`, `synced`, `edit`, `define`, `drawSelectTitle`, `close` |
32
+ | `texts.forms` | form action buttons (`FormActions`, `DialogFormBySchema`) and dialogs (`DialogActions`) | `save`, `saveAndContinue`, `saveAndCreateNew`, `cancel`, `remove`, `dialogCancel`, `dialogSave` |
33
+ | `texts.richText` | rich-text toolbar (`RichTextEditorInput`) and link dialog | `paragraph`, `heading1`–`heading3`, `bold`, `italic`, `underline`, `strike`, `bulletList`, `orderedList`, `blockquote`, `codeBlock`, `horizontalRule`, `link`, `linkTitle`, `linkUrl`, `linkApply`, `linkRemove`, `linkCancel`, `clearFormatting`, `undo`, `redo` |
34
+
35
+ ## Setting texts in your app
36
+
37
+ Add a `texts` key to the `theme` you pass to `DRFReactBySchemaProvider` (or to your own
38
+ `ThemeProvider`). The provider deepmerges it, so you can provide only the keys you want
39
+ to change.
40
+
41
+ ```tsx
42
+ // theme.ts
43
+ import { createTheme } from '@mui/material/styles'
44
+
45
+ export const theme = createTheme({
46
+ // ...palette, typography, etc.
47
+ texts: {
48
+ geometry: {
49
+ labelPoint: 'Coordenadas (GeoJSON)',
50
+ labelOther: 'GeoJSON (cole aqui)',
51
+ malformed: 'Dados malformados, corrija o GeoJSON para refletir no mapa.',
52
+ synced: 'O GeoJSON abaixo é sincronizado com o mapa.',
53
+ edit: 'Editar geometria',
54
+ define: 'Definir geometria',
55
+ drawSelectTitle: 'Desenhar / selecionar geometria',
56
+ close: 'Fechar',
57
+ },
58
+ forms: {
59
+ save: 'Salvar',
60
+ saveAndContinue: 'Salvar e continuar',
61
+ saveAndCreateNew: 'Salvar e criar novo',
62
+ cancel: 'Cancelar',
63
+ remove: 'Apagar',
64
+ dialogCancel: 'Cancelar',
65
+ dialogSave: 'Salvar',
66
+ },
67
+ richText: {
68
+ paragraph: 'Parágrafo',
69
+ heading1: 'Título 1',
70
+ heading2: 'Título 2',
71
+ heading3: 'Título 3',
72
+ bold: 'Negrito',
73
+ italic: 'Itálico',
74
+ underline: 'Sublinhado',
75
+ strike: 'Riscado',
76
+ bulletList: 'Lista com marcadores',
77
+ orderedList: 'Lista numerada',
78
+ blockquote: 'Citação',
79
+ codeBlock: 'Bloco de código',
80
+ horizontalRule: 'Linha horizontal',
81
+ link: 'Link',
82
+ linkTitle: 'Inserir link',
83
+ linkUrl: 'URL',
84
+ linkApply: 'Aplicar',
85
+ linkRemove: 'Remover link',
86
+ linkCancel: 'Cancelar',
87
+ clearFormatting: 'Limpar formatação',
88
+ undo: 'Desfazer',
89
+ redo: 'Refazer',
90
+ },
91
+ },
92
+ })
93
+
94
+ // App.js
95
+ import { DRFReactBySchemaProvider } from 'drf-react-by-schema'
96
+ import { theme } from './theme'
97
+
98
+ <DRFReactBySchemaProvider serverEndPoint={...} schema={...} theme={theme}>
99
+ <YourWonderfulApp />
100
+ </DRFReactBySchemaProvider>
101
+ ```
102
+
103
+ TypeScript picks up the `texts` keys automatically from the theme augmentation; you can
104
+ also import and reuse the text types (`DRFReactBySchemaTexts`, `GeometryTexts`,
105
+ `FormTexts`) from `drf-react-by-schema` for reference.
106
+
107
+ ## Precedence
108
+
109
+ Strings resolve in this order, so the most specific wins:
110
+
111
+ 1. **Explicit component prop** — e.g. `FormActions` has `labelSave`/`labelCancel`/
112
+ `labelRemove`/`labelSaveAndContinue`/`labelSaveAndCreateNew`; `DialogActions` has
113
+ `btnCancel`/`btnConfirm`; `MapGeometryPicker` has `label`/`helperText`.
114
+ 2. **`theme.texts.<namespace>`** — set app-wide in your theme.
115
+ 3. **Package `DEFAULT_TEXTS`** — the built-in fallback.
116
+
117
+ Since the explicit props still exist, both strategies can coexist: pass per-component
118
+ strings for a one-off button, or set theme texts for whole-app localization.
119
+
120
+ ## MUI component localization
121
+
122
+ The package's default theme already localizes the MUI components it renders to
123
+ **pt-BR** by composing MUI's official locale packages:
124
+
125
+ - `@mui/material/locale` (core components)
126
+ - `@mui/x-data-grid/locales` (DataGrid), plus DataGrid `localeText` overrides for the
127
+ package's custom toolbar strings (`Buscar...`, `Limpar busca`, …)
128
+ - `@mui/x-date-pickers/locales` (date pickers)
129
+
130
+ Date/time inputs additionally wrap each field in
131
+ `<LocalizationProvider adapterLocale="pt-br">`. To switch to another language, set the
132
+ corresponding MUI locale + DataGrid `localeText` in your theme and the desired
133
+ `adapterLocale` on the picker inputs.
134
+
135
+ ## Markers (geometry)
136
+
137
+ By default the geometry widgets render points with a **built-in inline-SVG marker
138
+ icon** (a data-URI), so markers appear out of the box under any bundler — Leaflet's
139
+ stock page-relative PNGs 404 inside an SPA. To use a custom marker, call the imperative
140
+ helper once at app startup (it always applies/overrides):
141
+
142
+ ```ts
143
+ import { applyDefaultLeafletIcon } from 'drf-react-by-schema'
144
+
145
+ applyDefaultLeafletIcon({
146
+ iconUrl: '/assets/my-marker.png',
147
+ iconSize: [30, 42],
148
+ iconAnchor: [15, 42],
149
+ })
150
+ ```
151
+
152
+ If you don't call it, the package's widgets apply the built-in default the first time it
153
+ is needed (and never override an icon you already placed on `L.Icon.Default`). Because
154
+ the application is one-shot, call it before rendering your maps; subsequent calls without
155
+ arguments are no-ops.
156
+
157
+ ## Reading texts in custom components
158
+
159
+ If you build custom widgets that should share the same themed strings, use the exported
160
+ `getThemeTexts(theme)` helper (returns theme texts merged over the package defaults):
161
+
162
+ ```tsx
163
+ import { useTheme } from '@mui/material/styles'
164
+ import { getThemeTexts } from 'drf-react-by-schema'
165
+
166
+ function MyActions() {
167
+ const formTexts = getThemeTexts(useTheme()).forms
168
+ return <button>{formTexts.save}</button>
169
+ }
170
+ ```
171
+
172
+ The text types (`DRFReactBySchemaTexts`, `GeometryTexts`, `FormTexts`) are exported from
173
+ `drf-react-by-schema` too, so you can type helper objects separately.
@@ -0,0 +1,145 @@
1
+ # Password reset, change password & e-mail setup
2
+
3
+ End-to-end guide for the **forgot-password / change-password** flow on a
4
+ Django + DRF + simplejwt backend with this React package.
5
+
6
+ The **React** side ships:
7
+
8
+ - `ForgotPasswordForm` — works in two modes:
9
+ - *request*: asks for the e-mail and calls `requestPasswordReset`.
10
+ - *confirm*: given `uid` + `token` (from the e-mail link), sets a new password
11
+ via `confirmPasswordReset`.
12
+ - `ChangePasswordForm` — for an already signed-in user (current password + new
13
+ password), backed by `changePassword`.
14
+ - `useAPIWrapper()` exposes: `requestPasswordReset(email)`,
15
+ `validatePasswordReset({ uid, token })`,
16
+ `confirmPasswordReset({ uid, token, new_password })`, `changePassword({ old_password, new_password })`.
17
+ `validatePasswordReset` resolves the account behind a reset link (name +
18
+ `has_usable_password`) without changing anything, so the page can greet the user
19
+ by name and tell a first-time invite (no usable password yet) from a reset.
20
+
21
+ Everything is mobile-friendly (the forms use `fullWidth` fields) and lives under
22
+ `DRFReactBySchemaProvider`, so the helpers are typed through `useAPIWrapper()`.
23
+
24
+ ## 1. Server contract (provided by drf-react-by-schema-django)
25
+
26
+ The Django package exposes these endpoints (auto-registered):
27
+
28
+ | Method | Endpoint | Auth | Body |
29
+ |---|---|---|---|
30
+ | POST | `password_reset/request/` | none | `{ email }` |
31
+ | POST | `password_reset/validate/` | none | `{ uid, token }` |
32
+ | POST | `password_reset/confirm/` | none | `{ uid, token, new_password }` |
33
+ | POST | `my_account/change_password/` | JWT | `{ old_password, new_password }` |
34
+
35
+ It reuses `django.contrib.auth` for token generation and password hashing, so no
36
+ extra package (allauth / dj-rest-auth) is required. See the Django package README
37
+ for the full details of the pluggable delivery callback.
38
+
39
+ ## 2. Frontend wiring (no extra config needed)
40
+
41
+ `DRFReactBySchemaProvider` derives `requestPasswordReset`, `validatePasswordReset`,
42
+ `confirmPasswordReset` and `changePassword` from your `serverEndPoint` automatically
43
+ (`{api}/password_reset/…`, `{api}/my_account/change_password/`). You only need the
44
+ usual `url`/`apiTokenUrl`, e.g.:
45
+
46
+ ```ts
47
+ const serverEndPoint = {
48
+ url: 'http://localhost:8000',
49
+ apiTokenUrl: 'http://localhost:8000/api/token/',
50
+ }
51
+ ```
52
+
53
+ Example — a public reset page reached both from the login screen and from the
54
+ e-mail link (`/esqueci-senha?uid=..&token=..`):
55
+
56
+ ```tsx
57
+ import { ForgotPasswordForm } from 'drf-react-by-schema'
58
+
59
+ function ForgotPasswordPage() {
60
+ const [params] = useSearchParams()
61
+ const navigate = useNavigate()
62
+ const uid = params.get('uid') ?? undefined
63
+ const token = params.get('token') ?? undefined
64
+ return (
65
+ <ForgotPasswordForm
66
+ uid={uid} // present -> confirm mode
67
+ token={token}
68
+ onBack={() => navigate('/')}
69
+ onSuccess={() => navigate('/')}
70
+ />
71
+ )
72
+ }
73
+ ```
74
+
75
+ An authenticated "change my password" screen:
76
+
77
+ ```tsx
78
+ import { ChangePasswordForm } from 'drf-react-by-schema'
79
+ // <ChangePasswordForm onSuccess={() => signOut()} />
80
+ ```
81
+
82
+ ## 3. Delivering the e-mail (the "e-mail infrastructure")
83
+
84
+ Sending is handled by the **backend** — the React app only composes the reset
85
+ link `{scheme}://{host}\{path}?uid=..&token=..`. Two levels of configuration:
86
+
87
+ **Simple (no code):** configure Django e-mail settings and the reset link path;
88
+
89
+ ```python
90
+ # settings.py / settings_local.py
91
+ EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" # or console in dev
92
+ EMAIL_HOST = "smtp.your-provider.com"
93
+ EMAIL_PORT = 587
94
+ EMAIL_USE_TLS = True
95
+ EMAIL_HOST_USER = "no-reply@your-domain"
96
+ EMAIL_HOST_PASSWORD = "…"
97
+ DEFAULT_FROM_EMAIL = "Your App <no-reply@your-domain>"
98
+
99
+ PASSWORD_RESET_SCHEME = "https"
100
+ PASSWORD_RESET_DOMAIN = "app.your-domain" # avoids trusting request scheme behind a proxy
101
+ PASSWORD_RESET_CONFIRM_PATH = "/esqueci-senha" # your page
102
+ ```
103
+
104
+ With that, `drf-react-by-schema-django` sends a plain-text message via Django's
105
+ mail backend.
106
+
107
+ **Customised (still small):** point `DRF_REACT_BY_SCHEMA['PASSWORD_RESET_CALLBACK']`
108
+ at a callable `(request, user, uid, token)`. The library already mints
109
+ `uid`/`token` before invoking it, so the change is a single function (branding,
110
+ HTML, another channel, queueing…):
111
+
112
+ ```python
113
+ # myapp/auth.py
114
+ def send_reset_email(request, user, uid, token):
115
+ send_mail("Redefina sua senha", f"Link: https://app.suaorg.org/esqueci-senha?uid={uid}&token={token}", None, [user.email])
116
+ ```
117
+
118
+ ```python
119
+ DRF_REACT_BY_SCHEMA = { ...
120
+ "PASSWORD_RESET_CALLBACK": "myapp.auth.send_reset_email",
121
+ "PASSWORD_RESET_CONFIRM_PATH": "/esqueci-senha",
122
+ }
123
+ ```
124
+
125
+ ### Dev vs prod
126
+
127
+ - **Dev**: `EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"` prints
128
+ the message to the terminal — great to test the flow without real SMTP.
129
+ - **Prod (Ubuntu/VPS)**: point `EMAIL_BACKEND` to a real SMTP host (transactional
130
+ provider recommended), set `EMAIL_*`, fix `PASSWORD_RESET_DOMAIN`/`SCHEME` and
131
+ list the domain in `ALLOWED_HOSTS`. Keep real credentials out of the repo
132
+ (e.g. in a gitignored `settings_local.py` + a gitignored server guide).
133
+
134
+ ## 4. Real-world flow
135
+
136
+ 1. User taps "Esqueci minha senha" on the login → goes to the public page.
137
+ 2. Page calls `requestPasswordReset(email)` → backend mints `uid`/`token` and
138
+ fires the callback → e-mail with the link.
139
+ 3. User opens the link (`…?uid=..&token=..`) → the same page is in *confirm* mode
140
+ → `confirmPasswordReset({ uid, token, new_password })` sets the password.
141
+ 4. (Optional) sign-in and use `ChangePasswordForm` → `changePassword(...)`.
142
+
143
+ Inviting a new user works the same way: create the account with an **unusable
144
+ password** on the backend and fire `requestPasswordReset(email)` so the invited
145
+ person receives a link to set their own password.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drf-react-by-schema",
3
- "version": "0.28.26",
3
+ "version": "0.28.28",
4
4
  "description": "Components and Tools for building a React App having Django Rest Framework (DRF) as server",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -132,6 +132,7 @@
132
132
  "not dead"
133
133
  ],
134
134
  "files": [
135
- "dist"
135
+ "dist",
136
+ "docs"
136
137
  ]
137
138
  }