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,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.25",
3
+ "version": "0.28.27",
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
  }