django-admin-react 1.10.0__tar.gz → 1.11.0__tar.gz
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.
- {django_admin_react-1.10.0 → django_admin_react-1.11.0}/PKG-INFO +10 -10
- {django_admin_react-1.10.0 → django_admin_react-1.11.0}/README.md +8 -8
- {django_admin_react-1.10.0 → django_admin_react-1.11.0}/django_admin_react/apps.py +13 -0
- django_admin_react-1.11.0/django_admin_react/checks.py +161 -0
- django_admin_react-1.11.0/django_admin_react/static/admin_react/.vite/manifest.json +53 -0
- django_admin_react-1.10.0/django_admin_react/static/admin_react/assets/ColumnLayoutModal-CpPqLNPJ.js → django_admin_react-1.11.0/django_admin_react/static/admin_react/assets/ColumnLayoutModal-CpSw6n_b.js +1 -1
- django_admin_react-1.11.0/django_admin_react/static/admin_react/assets/CreatePage-CoaPWEK9.js +1 -0
- django_admin_react-1.10.0/django_admin_react/static/admin_react/assets/JsonViewer-15Acvrg3.js → django_admin_react-1.11.0/django_admin_react/static/admin_react/assets/JsonViewer-wN9G0-Ud.js +1 -1
- django_admin_react-1.11.0/django_admin_react/static/admin_react/assets/LoginPage-Vd67Q6rx.js +1 -0
- django_admin_react-1.11.0/django_admin_react/static/admin_react/assets/index-Bh66HmjW.js +8 -0
- django_admin_react-1.11.0/django_admin_react/static/admin_react/assets/index-HGZBq8II.css +1 -0
- {django_admin_react-1.10.0 → django_admin_react-1.11.0}/django_admin_react/static/admin_react/index.html +2 -2
- {django_admin_react-1.10.0 → django_admin_react-1.11.0}/pyproject.toml +5 -2
- django_admin_react-1.10.0/django_admin_react/static/admin_react/.vite/manifest.json +0 -33
- django_admin_react-1.10.0/django_admin_react/static/admin_react/assets/index-B0Yq03p8.css +0 -1
- django_admin_react-1.10.0/django_admin_react/static/admin_react/assets/index-BpgtQBnO.js +0 -8
- {django_admin_react-1.10.0 → django_admin_react-1.11.0}/LICENSE +0 -0
- {django_admin_react-1.10.0 → django_admin_react-1.11.0}/django_admin_react/README.md +0 -0
- {django_admin_react-1.10.0 → django_admin_react-1.11.0}/django_admin_react/__init__.py +0 -0
- {django_admin_react-1.10.0 → django_admin_react-1.11.0}/django_admin_react/conf.py +0 -0
- {django_admin_react-1.10.0 → django_admin_react-1.11.0}/django_admin_react/pwa.py +0 -0
- {django_admin_react-1.10.0 → django_admin_react-1.11.0}/django_admin_react/templates/README.md +0 -0
- {django_admin_react-1.10.0 → django_admin_react-1.11.0}/django_admin_react/templates/admin/base_site.html +0 -0
- {django_admin_react-1.10.0 → django_admin_react-1.11.0}/django_admin_react/templates/admin_react/README.md +0 -0
- {django_admin_react-1.10.0 → django_admin_react-1.11.0}/django_admin_react/templates/admin_react/index.html +0 -0
- {django_admin_react-1.10.0 → django_admin_react-1.11.0}/django_admin_react/templates/admin_react/login.html +0 -0
- {django_admin_react-1.10.0 → django_admin_react-1.11.0}/django_admin_react/templates/admin_react/sw.js +0 -0
- {django_admin_react-1.10.0 → django_admin_react-1.11.0}/django_admin_react/templates/django_admin_react/_experience_toggle_strip.html +0 -0
- {django_admin_react-1.10.0 → django_admin_react-1.11.0}/django_admin_react/templatetags/__init__.py +0 -0
- {django_admin_react-1.10.0 → django_admin_react-1.11.0}/django_admin_react/templatetags/experience_toggle.py +0 -0
- {django_admin_react-1.10.0 → django_admin_react-1.11.0}/django_admin_react/urls.py +0 -0
- {django_admin_react-1.10.0 → django_admin_react-1.11.0}/django_admin_react/views.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-admin-react
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.11.0
|
|
4
4
|
Summary: A drop-in React single-page admin for Django, driven entirely by ModelAdmin.
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -28,7 +28,7 @@ Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
|
|
|
28
28
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
29
29
|
Requires-Dist: django (>=4.2,<7.0)
|
|
30
30
|
Requires-Dist: django-admin-mcp-api (>=1.3.0,<2.0.0)
|
|
31
|
-
Requires-Dist: django-admin-rest-api (>=1.
|
|
31
|
+
Requires-Dist: django-admin-rest-api (>=1.6.0,<2.0.0)
|
|
32
32
|
Project-URL: Documentation, https://github.com/MartinCastroAlvarez/django-admin-react#readme
|
|
33
33
|
Project-URL: Homepage, https://github.com/MartinCastroAlvarez/django-admin-react
|
|
34
34
|
Project-URL: Repository, https://github.com/MartinCastroAlvarez/django-admin-react
|
|
@@ -654,8 +654,9 @@ all share the v1 wire contract. Per-feature live status below.
|
|
|
654
654
|
| `list_editable` + bulk PATCH | ✅ |
|
|
655
655
|
| `actions` — batch + detail (signature-classified) | ✅ |
|
|
656
656
|
| `autocomplete_fields` | ✅ |
|
|
657
|
-
| `raw_id_fields` (pk text input + lookup popup) |
|
|
658
|
-
| `radio_fields` (inline radio buttons vs `<select>`) |
|
|
657
|
+
| `raw_id_fields` (pk text input + lookup popup) | ✅ |
|
|
658
|
+
| `radio_fields` (inline radio buttons vs `<select>`) | ✅ |
|
|
659
|
+
| `filter_horizontal` / `filter_vertical` (M2M shuttle) | ✅ |
|
|
659
660
|
| `ManyToManyField` read + write | ✅ |
|
|
660
661
|
| `inlines` (TabularInline / StackedInline) — read + write | ✅ |
|
|
661
662
|
| `FileField` / `ImageField` — read | ✅ |
|
|
@@ -684,14 +685,13 @@ issues link the work to close each gap.
|
|
|
684
685
|
|---|---|---|
|
|
685
686
|
| `change_form_template` / `add_form_template` overrides | **Embedded in an iframe** (since 1.9.0, #659): the change/add form-spec endpoint returns a `legacy-iframe` pointer and the SPA embeds the legacy admin page inside the SPA shell (breadcrumb / sidebar / toolbar stay SPA-rendered). Port the form to documented ModelAdmin hooks at your own pace. | [#624](https://github.com/MartinCastroAlvarez/django-admin-react/issues/624) |
|
|
686
687
|
| `change_list_template` / `change_password_template` / `object_history_template` overrides | Silently ignored — those surfaces render entirely from the JSON wire. | [#624](https://github.com/MartinCastroAlvarez/django-admin-react/issues/624) |
|
|
687
|
-
| `formfield_overrides = {Field: {"widget": CustomWidget}}` | Custom widget
|
|
688
|
-
| `
|
|
689
|
-
| `
|
|
690
|
-
| `
|
|
688
|
+
| `formfield_overrides = {Field: {"widget": CustomWidget}}` | Custom widget rendered via the React widget-registration API (`registerFieldWidget`, #625) when the consumer registers a renderer for the widget class; otherwise falls back to the default control + an operator-visible "not registered" note. | [#625](https://github.com/MartinCastroAlvarez/django-admin-react/issues/625) |
|
|
689
|
+
| `empty_value_display` | **Hard-coded to `—`.** A per-`ModelAdmin` / per-field `empty_value_display` override is **not** surfaced — the SPA renders the literal em-dash for every empty value, regardless of the consumer's chosen placeholder. | [#629](https://github.com/MartinCastroAlvarez/django-admin-react/issues/629) |
|
|
690
|
+
| Custom `AdminSite.each_context(request)` extra keys | Not surfaced. Only a fixed set of site attributes (`site_header` / `site_title` / `site_logo` / `site_primary_color`) reaches the SPA; any extra keys a consumer adds in a custom `each_context` are dropped. | [#629](https://github.com/MartinCastroAlvarez/django-admin-react/issues/629) |
|
|
691
|
+
| `list_select_related` | A backend query-optimisation concern, applied server-side by the REST API's queryset; it changes query efficiency, **not** the wire shape, so it is intentionally invisible to the SPA (no client-visible effect to surface). | [#629](https://github.com/MartinCastroAlvarez/django-admin-react/issues/629) |
|
|
691
692
|
| `GenericForeignKey` / `GenericInlineModelAdmin` | Support gap — verify per-model before relying on the SPA. | [#628](https://github.com/MartinCastroAlvarez/django-admin-react/issues/628) |
|
|
692
|
-
| `LANGUAGE_CODE` / `gettext` / `Accept-Language` |
|
|
693
|
+
| `LANGUAGE_CODE` / `gettext` / `Accept-Language` | SPA chrome strings translate via the bundled catalogs (es / fr / pt; #630); translated `verbose_name` / `help_text` / `@admin.action(description=_("..."))` flow through when `LocaleMiddleware` is installed. | [#630](https://github.com/MartinCastroAlvarez/django-admin-react/issues/630) |
|
|
693
694
|
| `ModelAdmin.get_urls()` custom views | Opens as a popout (`<a target="_blank">`) into the Django-rendered HTML page — no SPA chrome, no breadcrumb. The link IS surfaced; the UX is just outside the SPA. | [#623](https://github.com/MartinCastroAlvarez/django-admin-react/issues/623) |
|
|
694
|
-
| Django 4.2 LTS support | Not yet — the package pins `django >= 5.0,<7.0`. | [#622](https://github.com/MartinCastroAlvarez/django-admin-react/issues/622) |
|
|
695
695
|
|
|
696
696
|
If your admin relies on any "silently ignored" hook above, the
|
|
697
697
|
typical workaround is to keep that model on the legacy
|
|
@@ -618,8 +618,9 @@ all share the v1 wire contract. Per-feature live status below.
|
|
|
618
618
|
| `list_editable` + bulk PATCH | ✅ |
|
|
619
619
|
| `actions` — batch + detail (signature-classified) | ✅ |
|
|
620
620
|
| `autocomplete_fields` | ✅ |
|
|
621
|
-
| `raw_id_fields` (pk text input + lookup popup) |
|
|
622
|
-
| `radio_fields` (inline radio buttons vs `<select>`) |
|
|
621
|
+
| `raw_id_fields` (pk text input + lookup popup) | ✅ |
|
|
622
|
+
| `radio_fields` (inline radio buttons vs `<select>`) | ✅ |
|
|
623
|
+
| `filter_horizontal` / `filter_vertical` (M2M shuttle) | ✅ |
|
|
623
624
|
| `ManyToManyField` read + write | ✅ |
|
|
624
625
|
| `inlines` (TabularInline / StackedInline) — read + write | ✅ |
|
|
625
626
|
| `FileField` / `ImageField` — read | ✅ |
|
|
@@ -648,14 +649,13 @@ issues link the work to close each gap.
|
|
|
648
649
|
|---|---|---|
|
|
649
650
|
| `change_form_template` / `add_form_template` overrides | **Embedded in an iframe** (since 1.9.0, #659): the change/add form-spec endpoint returns a `legacy-iframe` pointer and the SPA embeds the legacy admin page inside the SPA shell (breadcrumb / sidebar / toolbar stay SPA-rendered). Port the form to documented ModelAdmin hooks at your own pace. | [#624](https://github.com/MartinCastroAlvarez/django-admin-react/issues/624) |
|
|
650
651
|
| `change_list_template` / `change_password_template` / `object_history_template` overrides | Silently ignored — those surfaces render entirely from the JSON wire. | [#624](https://github.com/MartinCastroAlvarez/django-admin-react/issues/624) |
|
|
651
|
-
| `formfield_overrides = {Field: {"widget": CustomWidget}}` | Custom widget
|
|
652
|
-
| `
|
|
653
|
-
| `
|
|
654
|
-
| `
|
|
652
|
+
| `formfield_overrides = {Field: {"widget": CustomWidget}}` | Custom widget rendered via the React widget-registration API (`registerFieldWidget`, #625) when the consumer registers a renderer for the widget class; otherwise falls back to the default control + an operator-visible "not registered" note. | [#625](https://github.com/MartinCastroAlvarez/django-admin-react/issues/625) |
|
|
653
|
+
| `empty_value_display` | **Hard-coded to `—`.** A per-`ModelAdmin` / per-field `empty_value_display` override is **not** surfaced — the SPA renders the literal em-dash for every empty value, regardless of the consumer's chosen placeholder. | [#629](https://github.com/MartinCastroAlvarez/django-admin-react/issues/629) |
|
|
654
|
+
| Custom `AdminSite.each_context(request)` extra keys | Not surfaced. Only a fixed set of site attributes (`site_header` / `site_title` / `site_logo` / `site_primary_color`) reaches the SPA; any extra keys a consumer adds in a custom `each_context` are dropped. | [#629](https://github.com/MartinCastroAlvarez/django-admin-react/issues/629) |
|
|
655
|
+
| `list_select_related` | A backend query-optimisation concern, applied server-side by the REST API's queryset; it changes query efficiency, **not** the wire shape, so it is intentionally invisible to the SPA (no client-visible effect to surface). | [#629](https://github.com/MartinCastroAlvarez/django-admin-react/issues/629) |
|
|
655
656
|
| `GenericForeignKey` / `GenericInlineModelAdmin` | Support gap — verify per-model before relying on the SPA. | [#628](https://github.com/MartinCastroAlvarez/django-admin-react/issues/628) |
|
|
656
|
-
| `LANGUAGE_CODE` / `gettext` / `Accept-Language` |
|
|
657
|
+
| `LANGUAGE_CODE` / `gettext` / `Accept-Language` | SPA chrome strings translate via the bundled catalogs (es / fr / pt; #630); translated `verbose_name` / `help_text` / `@admin.action(description=_("..."))` flow through when `LocaleMiddleware` is installed. | [#630](https://github.com/MartinCastroAlvarez/django-admin-react/issues/630) |
|
|
657
658
|
| `ModelAdmin.get_urls()` custom views | Opens as a popout (`<a target="_blank">`) into the Django-rendered HTML page — no SPA chrome, no breadcrumb. The link IS surfaced; the UX is just outside the SPA. | [#623](https://github.com/MartinCastroAlvarez/django-admin-react/issues/623) |
|
|
658
|
-
| Django 4.2 LTS support | Not yet — the package pins `django >= 5.0,<7.0`. | [#622](https://github.com/MartinCastroAlvarez/django-admin-react/issues/622) |
|
|
659
659
|
|
|
660
660
|
If your admin relies on any "silently ignored" hook above, the
|
|
661
661
|
typical workaround is to keep that model on the legacy
|
|
@@ -6,6 +6,7 @@ templates, static assets) is opt-in via the consumer's own `urls.py`.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from django.apps import AppConfig
|
|
9
|
+
from django.core.checks import register
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class DjangoAdminReactConfig(AppConfig):
|
|
@@ -25,3 +26,15 @@ class DjangoAdminReactConfig(AppConfig):
|
|
|
25
26
|
label = "django_admin_react"
|
|
26
27
|
verbose_name = "Django Admin React"
|
|
27
28
|
default_auto_field = "django.db.models.BigAutoField"
|
|
29
|
+
|
|
30
|
+
def ready(self) -> None:
|
|
31
|
+
"""Register the package's system checks at app-load (#667).
|
|
32
|
+
|
|
33
|
+
Importing + registering here (not at module import time) keeps the
|
|
34
|
+
checks tied to the app registry being ready, matching Django's
|
|
35
|
+
documented pattern. The import is local so adding the app has no
|
|
36
|
+
eager import cost beyond the AppConfig itself.
|
|
37
|
+
"""
|
|
38
|
+
from django_admin_react.checks import check_django_admin_react
|
|
39
|
+
|
|
40
|
+
register(check_django_admin_react)
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""System checks for django_admin_react (#667).
|
|
2
|
+
|
|
3
|
+
Registered against ``django.core.checks`` so common misconfigurations
|
|
4
|
+
surface at ``manage.py check`` (and on every ``runserver`` boot) with an
|
|
5
|
+
actionable hint — instead of a lazy ``ValueError`` on first settings
|
|
6
|
+
access, a runtime 500, or a silent template fallback.
|
|
7
|
+
|
|
8
|
+
What we validate:
|
|
9
|
+
|
|
10
|
+
- ``django_admin_rest_api`` is installed (the package implements no API of
|
|
11
|
+
its own — every JSON endpoint lives in that sibling package).
|
|
12
|
+
- ``DJANGO_ADMIN_REACT["ADMIN_SITE"]`` (dotted path) actually imports.
|
|
13
|
+
- ``DJANGO_ADMIN_REACT`` has no unknown keys (the same guard ``conf._load``
|
|
14
|
+
enforces lazily — surfaced here at startup as a clean check error).
|
|
15
|
+
- ``API_URL_PREFIX`` mounting is coherent: when it is set, the package
|
|
16
|
+
skips its inline ``api/v1/`` include, so the consumer must mount
|
|
17
|
+
``django_admin_rest_api.urls`` themselves — a Warning reminds them.
|
|
18
|
+
- The built SPA bundle / Vite manifest exists (a Warning, not an Error —
|
|
19
|
+
the package serves a friendly "not built" shell, and the manifest is
|
|
20
|
+
absent in a source checkout / before ``pnpm build``).
|
|
21
|
+
|
|
22
|
+
Severities follow Django's convention: an ``Error`` blocks (the package
|
|
23
|
+
cannot work); a ``Warning`` is a likely-misconfiguration heads-up that
|
|
24
|
+
does not hard-block.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
from typing import Any
|
|
30
|
+
|
|
31
|
+
from django.core.checks import Error
|
|
32
|
+
from django.core.checks import Warning as CheckWarning
|
|
33
|
+
|
|
34
|
+
# Stable check IDs (Django convention ``<app_label>.E/W###``) so a consumer
|
|
35
|
+
# can silence a specific one via ``SILENCED_SYSTEM_CHECKS`` if they must.
|
|
36
|
+
ID_REST_API_MISSING = "django_admin_react.E001"
|
|
37
|
+
ID_ADMIN_SITE_IMPORT = "django_admin_react.E002"
|
|
38
|
+
ID_UNKNOWN_SETTINGS = "django_admin_react.E003"
|
|
39
|
+
ID_API_PREFIX_MOUNT = "django_admin_react.W001"
|
|
40
|
+
ID_BUNDLE_MISSING = "django_admin_react.W002"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def check_django_admin_react(app_configs: Any, **kwargs: Any) -> list[Any]:
|
|
44
|
+
"""Run every package configuration check.
|
|
45
|
+
|
|
46
|
+
Registered as a single callable (rather than many) so the import
|
|
47
|
+
surface stays small and the ordering is explicit. ``app_configs`` /
|
|
48
|
+
``kwargs`` are the Django check-framework signature; unused here
|
|
49
|
+
because the checks are global to the package, not per-app.
|
|
50
|
+
"""
|
|
51
|
+
errors: list[Any] = []
|
|
52
|
+
errors.extend(_check_rest_api_installed())
|
|
53
|
+
errors.extend(_check_admin_site_imports())
|
|
54
|
+
errors.extend(_check_settings_keys())
|
|
55
|
+
errors.extend(_check_api_prefix_coherence())
|
|
56
|
+
errors.extend(_check_bundle_built())
|
|
57
|
+
return errors
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _check_rest_api_installed() -> list[Any]:
|
|
61
|
+
from django.apps import apps as django_apps
|
|
62
|
+
|
|
63
|
+
if django_apps.is_installed("django_admin_rest_api"):
|
|
64
|
+
return []
|
|
65
|
+
return [
|
|
66
|
+
Error(
|
|
67
|
+
"'django_admin_rest_api' is not in INSTALLED_APPS.",
|
|
68
|
+
hint=(
|
|
69
|
+
"django-admin-react is a React SPA over django-admin-rest-api "
|
|
70
|
+
"and implements no API of its own. Add 'django_admin_rest_api' "
|
|
71
|
+
"to INSTALLED_APPS (it ships as a dependency)."
|
|
72
|
+
),
|
|
73
|
+
id=ID_REST_API_MISSING,
|
|
74
|
+
)
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _check_admin_site_imports() -> list[Any]:
|
|
79
|
+
from django.utils.module_loading import import_string
|
|
80
|
+
|
|
81
|
+
from django_admin_react import conf as dar_conf
|
|
82
|
+
|
|
83
|
+
dotted = dar_conf.ADMIN_SITE
|
|
84
|
+
try:
|
|
85
|
+
import_string(dotted)
|
|
86
|
+
except ImportError as exc:
|
|
87
|
+
return [
|
|
88
|
+
Error(
|
|
89
|
+
f"DJANGO_ADMIN_REACT['ADMIN_SITE'] = {dotted!r} could not be imported " f"({exc}).",
|
|
90
|
+
hint=(
|
|
91
|
+
"Point ADMIN_SITE at the dotted path of your AdminSite "
|
|
92
|
+
"instance (e.g. 'myproject.admin.site' or the default "
|
|
93
|
+
"'django.contrib.admin.site')."
|
|
94
|
+
),
|
|
95
|
+
id=ID_ADMIN_SITE_IMPORT,
|
|
96
|
+
)
|
|
97
|
+
]
|
|
98
|
+
return []
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _check_settings_keys() -> list[Any]:
|
|
102
|
+
from django.conf import settings as django_settings
|
|
103
|
+
|
|
104
|
+
from django_admin_react.conf import DEFAULTS
|
|
105
|
+
|
|
106
|
+
overrides = getattr(django_settings, "DJANGO_ADMIN_REACT", {}) or {}
|
|
107
|
+
unknown = sorted(set(overrides) - set(DEFAULTS))
|
|
108
|
+
if not unknown:
|
|
109
|
+
return []
|
|
110
|
+
return [
|
|
111
|
+
Error(
|
|
112
|
+
"Unknown DJANGO_ADMIN_REACT key(s): " + ", ".join(unknown) + ".",
|
|
113
|
+
hint=("Remove the typo'd key(s). Valid keys are: " + ", ".join(sorted(DEFAULTS)) + "."),
|
|
114
|
+
id=ID_UNKNOWN_SETTINGS,
|
|
115
|
+
)
|
|
116
|
+
]
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _check_api_prefix_coherence() -> list[Any]:
|
|
120
|
+
from django_admin_react import conf as dar_conf
|
|
121
|
+
|
|
122
|
+
prefix = dar_conf.API_URL_PREFIX
|
|
123
|
+
if prefix is None:
|
|
124
|
+
# Inline mount is active; the package mounts the API itself. Nothing
|
|
125
|
+
# the consumer must do.
|
|
126
|
+
return []
|
|
127
|
+
# The override is set, so `django_admin_react.urls` skips the inline
|
|
128
|
+
# `api/v1/` include — the consumer MUST mount the REST API themselves at
|
|
129
|
+
# that prefix or every SPA data call 404s.
|
|
130
|
+
return [
|
|
131
|
+
CheckWarning(
|
|
132
|
+
f"DJANGO_ADMIN_REACT['API_URL_PREFIX'] = {prefix!r} is set, so this "
|
|
133
|
+
"package does NOT mount the REST API inline.",
|
|
134
|
+
hint=(
|
|
135
|
+
"Mount the sibling API yourself at that prefix, e.g.\n"
|
|
136
|
+
f" path({prefix!r}, include('django_admin_rest_api.api.urls'))\n"
|
|
137
|
+
"or unset API_URL_PREFIX to use the package's inline 'api/v1/' mount."
|
|
138
|
+
),
|
|
139
|
+
id=ID_API_PREFIX_MOUNT,
|
|
140
|
+
)
|
|
141
|
+
]
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _check_bundle_built() -> list[Any]:
|
|
145
|
+
# Imported lazily so the check module has no import-time dependency on
|
|
146
|
+
# the view layer.
|
|
147
|
+
from django_admin_react.views import _MANIFEST_PATH
|
|
148
|
+
|
|
149
|
+
if _MANIFEST_PATH.is_file():
|
|
150
|
+
return []
|
|
151
|
+
return [
|
|
152
|
+
CheckWarning(
|
|
153
|
+
"The built SPA bundle / Vite manifest was not found at " f"{_MANIFEST_PATH}.",
|
|
154
|
+
hint=(
|
|
155
|
+
"Install the published wheel (which ships the built bundle), or "
|
|
156
|
+
"build the frontend from source with 'pnpm --dir frontend build'. "
|
|
157
|
+
"Until then the SPA shell renders a 'not built yet' page."
|
|
158
|
+
),
|
|
159
|
+
id=ID_BUNDLE_MISSING,
|
|
160
|
+
)
|
|
161
|
+
]
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"../../packages/details/src/JsonViewer.tsx": {
|
|
3
|
+
"file": "assets/JsonViewer-wN9G0-Ud.js",
|
|
4
|
+
"name": "JsonViewer",
|
|
5
|
+
"src": "../../packages/details/src/JsonViewer.tsx",
|
|
6
|
+
"isDynamicEntry": true,
|
|
7
|
+
"imports": [
|
|
8
|
+
"index.html"
|
|
9
|
+
]
|
|
10
|
+
},
|
|
11
|
+
"index.html": {
|
|
12
|
+
"file": "assets/index-Bh66HmjW.js",
|
|
13
|
+
"name": "index",
|
|
14
|
+
"src": "index.html",
|
|
15
|
+
"isEntry": true,
|
|
16
|
+
"dynamicImports": [
|
|
17
|
+
"../../packages/details/src/JsonViewer.tsx",
|
|
18
|
+
"src/ColumnLayoutModal.tsx",
|
|
19
|
+
"src/pages/LoginPage.tsx",
|
|
20
|
+
"src/pages/CreatePage.tsx"
|
|
21
|
+
],
|
|
22
|
+
"css": [
|
|
23
|
+
"assets/index-HGZBq8II.css"
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
"src/ColumnLayoutModal.tsx": {
|
|
27
|
+
"file": "assets/ColumnLayoutModal-CpSw6n_b.js",
|
|
28
|
+
"name": "ColumnLayoutModal",
|
|
29
|
+
"src": "src/ColumnLayoutModal.tsx",
|
|
30
|
+
"isDynamicEntry": true,
|
|
31
|
+
"imports": [
|
|
32
|
+
"index.html"
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
"src/pages/CreatePage.tsx": {
|
|
36
|
+
"file": "assets/CreatePage-CoaPWEK9.js",
|
|
37
|
+
"name": "CreatePage",
|
|
38
|
+
"src": "src/pages/CreatePage.tsx",
|
|
39
|
+
"isDynamicEntry": true,
|
|
40
|
+
"imports": [
|
|
41
|
+
"index.html"
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
"src/pages/LoginPage.tsx": {
|
|
45
|
+
"file": "assets/LoginPage-Vd67Q6rx.js",
|
|
46
|
+
"name": "LoginPage",
|
|
47
|
+
"src": "src/pages/LoginPage.tsx",
|
|
48
|
+
"isDynamicEntry": true,
|
|
49
|
+
"imports": [
|
|
50
|
+
"index.html"
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{g as tt,i as c,R as $,r as ke,j as I,M as kn,e as On,c as Jt}from"./index-Bh66HmjW.js";const Tn=[["circle",{cx:"9",cy:"12",r:"1",key:"1vctgf"}],["circle",{cx:"9",cy:"5",r:"1",key:"hp0tcf"}],["circle",{cx:"9",cy:"19",r:"1",key:"fkjjf6"}],["circle",{cx:"15",cy:"12",r:"1",key:"1tmaij"}],["circle",{cx:"15",cy:"5",r:"1",key:"19l28e"}],["circle",{cx:"15",cy:"19",r:"1",key:"f4zoj3"}]],Qt=tt("grip-vertical",Tn);const Ln=[["rect",{width:"18",height:"11",x:"3",y:"11",rx:"2",ry:"2",key:"1w4ew1"}],["path",{d:"M7 11V7a5 5 0 0 1 9.9-1",key:"1mm8w8"}]],jn=tt("lock-open",Ln);const zn=[["rect",{width:"18",height:"11",x:"3",y:"11",rx:"2",ry:"2",key:"1w4ew1"}],["path",{d:"M7 11V7a5 5 0 0 1 10 0v4",key:"fwvmzm"}]],jt=tt("lock",zn);const $n=[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}]],Bn=tt("rotate-ccw",$n);function Pn(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return c.useMemo(()=>r=>{t.forEach(o=>o(r))},t)}const nt=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";function xe(e){const t=Object.prototype.toString.call(e);return t==="[object Window]"||t==="[object global]"}function mt(e){return"nodeType"in e}function B(e){var t,n;return e?xe(e)?e:mt(e)&&(t=(n=e.ownerDocument)==null?void 0:n.defaultView)!=null?t:window:window}function yt(e){const{Document:t}=B(e);return e instanceof t}function Pe(e){return xe(e)?!1:e instanceof B(e).HTMLElement}function Zt(e){return e instanceof B(e).SVGElement}function we(e){return e?xe(e)?e.document:mt(e)?yt(e)?e:Pe(e)||Zt(e)?e.ownerDocument:document:document:document}const Z=nt?c.useLayoutEffect:c.useEffect;function xt(e){const t=c.useRef(e);return Z(()=>{t.current=e}),c.useCallback(function(){for(var n=arguments.length,r=new Array(n),o=0;o<n;o++)r[o]=arguments[o];return t.current==null?void 0:t.current(...r)},[])}function Fn(){const e=c.useRef(null),t=c.useCallback((r,o)=>{e.current=setInterval(r,o)},[]),n=c.useCallback(()=>{e.current!==null&&(clearInterval(e.current),e.current=null)},[]);return[t,n]}function je(e,t){t===void 0&&(t=[e]);const n=c.useRef(e);return Z(()=>{n.current!==e&&(n.current=e)},t),n}function Fe(e,t){const n=c.useRef();return c.useMemo(()=>{const r=e(n.current);return n.current=r,r},[...t])}function Je(e){const t=xt(e),n=c.useRef(null),r=c.useCallback(o=>{o!==n.current&&t?.(o,n.current),n.current=o},[]);return[n,r]}function ht(e){const t=c.useRef();return c.useEffect(()=>{t.current=e},[e]),t.current}let lt={};function Xe(e,t){return c.useMemo(()=>{if(t)return t;const n=lt[e]==null?0:lt[e]+1;return lt[e]=n,e+"-"+n},[e,t])}function en(e){return function(t){for(var n=arguments.length,r=new Array(n>1?n-1:0),o=1;o<n;o++)r[o-1]=arguments[o];return r.reduce((i,s)=>{const a=Object.entries(s);for(const[l,u]of a){const f=i[l];f!=null&&(i[l]=f+e*u)}return i},{...t})}}const ye=en(1),ze=en(-1);function Xn(e){return"clientX"in e&&"clientY"in e}function wt(e){if(!e)return!1;const{KeyboardEvent:t}=B(e.target);return t&&e instanceof t}function Un(e){if(!e)return!1;const{TouchEvent:t}=B(e.target);return t&&e instanceof t}function gt(e){if(Un(e)){if(e.touches&&e.touches.length){const{clientX:t,clientY:n}=e.touches[0];return{x:t,y:n}}else if(e.changedTouches&&e.changedTouches.length){const{clientX:t,clientY:n}=e.changedTouches[0];return{x:t,y:n}}}return Xn(e)?{x:e.clientX,y:e.clientY}:null}const $e=Object.freeze({Translate:{toString(e){if(!e)return;const{x:t,y:n}=e;return"translate3d("+(t?Math.round(t):0)+"px, "+(n?Math.round(n):0)+"px, 0)"}},Scale:{toString(e){if(!e)return;const{scaleX:t,scaleY:n}=e;return"scaleX("+t+") scaleY("+n+")"}},Transform:{toString(e){if(e)return[$e.Translate.toString(e),$e.Scale.toString(e)].join(" ")}},Transition:{toString(e){let{property:t,duration:n,easing:r}=e;return t+" "+n+"ms "+r}}}),zt="a,frame,iframe,input:not([type=hidden]):not(:disabled),select:not(:disabled),textarea:not(:disabled),button:not(:disabled),*[tabindex]";function Yn(e){return e.matches(zt)?e:e.querySelector(zt)}const Wn={display:"none"};function Vn(e){let{id:t,value:n}=e;return $.createElement("div",{id:t,style:Wn},n)}function Hn(e){let{id:t,announcement:n,ariaLiveType:r="assertive"}=e;const o={position:"fixed",top:0,left:0,width:1,height:1,margin:-1,border:0,padding:0,overflow:"hidden",clip:"rect(0 0 0 0)",clipPath:"inset(100%)",whiteSpace:"nowrap"};return $.createElement("div",{id:t,style:o,role:"status","aria-live":r,"aria-atomic":!0},n)}function Kn(){const[e,t]=c.useState("");return{announce:c.useCallback(r=>{r!=null&&t(r)},[]),announcement:e}}const tn=c.createContext(null);function _n(e){const t=c.useContext(tn);c.useEffect(()=>{if(!t)throw new Error("useDndMonitor must be used within a children of <DndContext>");return t(e)},[e,t])}function qn(){const[e]=c.useState(()=>new Set),t=c.useCallback(r=>(e.add(r),()=>e.delete(r)),[e]);return[c.useCallback(r=>{let{type:o,event:i}=r;e.forEach(s=>{var a;return(a=s[o])==null?void 0:a.call(s,i)})},[e]),t]}const Gn={draggable:`
|
|
2
2
|
To pick up a draggable item, press the space bar.
|
|
3
3
|
While dragging, use the arrow keys to move the item.
|
|
4
4
|
Press space again to drop the item in its new position, or press escape to cancel.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{n as $,u as O,m as L,p as T,o as J,l as R,k as B,i as u,j as t,E as K,d as z,B as D,L as I,h as M,f as q,q as G,C as H,a as F,F as U,A as V}from"./index-Bh66HmjW.js";function W(i){return i.normalize("NFKD").replace(/[̀-ͯ]/g,"").toLowerCase().trim().replace(/[^a-z0-9\s-]/g,"").replace(/[\s-]+/g,"-").replace(/^-+|-+$/g,"")}function Q({values:i,prepopulated:c,editedTargets:d}){const a={...i};for(const[y,b]of Object.entries(c)){if(d.has(y))continue;const h=b.map(p=>a[p]==null?"":String(a[p])).join(" ");a[y]=W(h)}return a}function Z(){const i=$(),c=i.appLabel??"",d=i.modelName??"",a=O(),y=L(),b=T(),[h]=J(),p=R(`/${c}/${d}`,h),{singular:g,plural:v}=B(c,d),[S,C]=u.useState(null),[A,N]=u.useState(null),[k,j]=u.useState(0);return u.useEffect(()=>{let f=!0;return C(null),N(null),a.addForm(c,d).then(o=>{f&&C(o)}).catch(o=>{f&&N(o instanceof Error?o.message:"Could not load the add form.")}),()=>{f=!1}},[a,c,d]),A?t.jsx(K,{title:"Couldn't open the add form",description:A}):S?t.jsxs("div",{className:"space-y-4",children:[t.jsxs("header",{className:"space-y-1",children:[t.jsx(D,{items:[{label:"Home",to:"/"},{label:v,to:p},{label:`Add ${g}`}],renderLink:(f,o,E)=>t.jsx(I,{to:f,className:o,children:E})}),t.jsxs("h1",{className:"text-2xl font-semibold",children:["Add ",g]})]}),t.jsx(X,{schema:S,onCreate:async(f,o)=>{const E=await M({client:a,appLabel:c,modelName:d,payload:f});if(b.success("Added."),o==="addAnother"){j(x=>x+1);return}if(o==="continue"){y(q(`/${c}/${d}/${E.pk}?edit=1`,h));return}y(p)},onCancel:()=>y(p)},k)]}):t.jsx(z,{})}function X({schema:i,onCreate:c,onCancel:d}){const[a,y]=u.useState(()=>{const e={};for(const[n,r]of Object.entries(i.fields)){if(r.readonly)continue;if(r.type==="manytomany"){e[n]=[];continue}const s=r.value;if(r.type==="json"){e[n]=s==null?null:JSON.stringify(s,null,2);continue}if(r.type==="array"){e[n]=Array.isArray(s)?s.join(","):null;continue}if(r.type==="range"){if(s&&typeof s=="object"&&"value"in s){const m=s.value;if(m&&typeof m=="object"){const l=m.lower,_=m.upper;e[n]=[l==null?"":String(l),_==null?"":String(_)];continue}}e[n]=["",""];continue}e[n]=s!==null&&typeof s!="object"?s:null}return e}),[b,h]=u.useState({}),[p,g]=u.useState(null),[v,S]=u.useState(!1),C=i.prepopulated_fields??{},A=new Set(Object.keys(C)),N=u.useRef(new Set);function k(e,n){y(r=>(A.has(e)&&N.current.add(e),Q({values:{...r,[e]:n},prepopulated:C,editedTargets:N.current})))}const j=u.useRef(null);j.current===null&&(j.current=JSON.stringify(a));const f=JSON.stringify(a)!==j.current;G(f&&!v);async function o(e){S(!0),h({}),g(null);try{await c(a,e),j.current=JSON.stringify(a)}catch(n){if(n instanceof V&&n.envelope?.error){const{[""]:r,...s}=n.envelope.error.fields??{};h(s);const m=r?.join(" ")||(Object.keys(s).length===0?n.envelope.error.message:"");g(m||null)}else g(n instanceof Error?n.message:"Create failed.")}finally{S(!1)}}async function E(e){e.preventDefault(),await o("save")}const x=i.save_options,w=()=>t.jsxs("div",{className:"flex flex-wrap gap-2",children:[(x?.show_save??!0)&&t.jsx(F,{type:"submit",variant:"primary",disabled:v,children:v?"Saving…":"Add"}),x?.show_save_and_add_another&&t.jsx(F,{type:"button",variant:"secondary",disabled:v,onClick:()=>{o("addAnother")},children:"Save and add another"}),x?.show_save_and_continue&&t.jsx(F,{type:"button",variant:"secondary",disabled:v,onClick:()=>{o("continue")},children:"Save and continue editing"}),t.jsx(F,{type:"button",variant:"ghost",onClick:d,disabled:v,children:"Cancel"})]});return t.jsxs("form",{onSubmit:E,className:"space-y-4",children:[p&&t.jsx("div",{className:"rounded border border-red-300 bg-red-50 px-3 py-2 text-sm text-red-700",children:p}),x?.save_on_top&&w(),i.fieldsets.map((e,n)=>t.jsx(H,{title:e.title??void 0,children:t.jsx("div",{className:"divide-y divide-gray-100",children:(e.field_rows??e.fields.map(r=>[r])).map((r,s)=>{const m=l=>{const _=i.fields[l];return _?t.jsx(U,{name:l,field:_,value:a[l]??null,error:b[l],onChange:P=>k(l,P)},l):null};return r.length===1?m(r[0]):t.jsx("div",{className:"grid gap-4 py-1",style:{gridTemplateColumns:`repeat(${r.length}, minmax(0, 1fr))`},children:r.map(l=>m(l))},s)})})},`cfs-${n}-${e.title??"default"}`)),w()]})}export{Z as CreatePage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{g as x,i as l,j as e,b as g}from"./index-Bh66HmjW.js";const h=[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]],p=x("chevron-right",h);const y=[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]],j=x("copy",y);function N({raw:t,parsed:r}){const[s,c]=l.useState(!1);async function a(){try{await navigator.clipboard.writeText(t),c(!0),setTimeout(()=>c(!1),2e3)}catch{}}return e.jsxs("div",{className:"relative w-full overflow-x-auto rounded border border-gray-200 bg-gray-50 p-3 font-mono text-xs leading-relaxed text-gray-800 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-200",children:[e.jsx("button",{type:"button",onClick:a,"aria-label":"Copy JSON",title:s?"Copied":"Copy",className:"absolute right-2 top-2 inline-flex h-6 w-6 items-center justify-center rounded border border-gray-300 bg-white text-gray-600 hover:bg-gray-100 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700",children:s?e.jsx(g,{className:"h-3.5 w-3.5 text-green-600","aria-hidden":!0}):e.jsx(j,{className:"h-3.5 w-3.5","aria-hidden":!0})}),e.jsx(o,{value:r,depth:0})]})}function o({value:t,depth:r}){return t===null?e.jsx("span",{className:"text-purple-600 dark:text-purple-400",children:"null"}):typeof t=="boolean"?e.jsx("span",{className:"text-purple-600 dark:text-purple-400",children:t?"true":"false"}):typeof t=="number"?e.jsx("span",{className:"text-blue-700",children:String(t)}):typeof t=="string"?e.jsxs("span",{className:"text-green-700 dark:text-green-400",children:['"',t,'"']}):Array.isArray(t)?e.jsx(u,{value:t,depth:r}):typeof t=="object"?e.jsx(m,{value:t,depth:r}):e.jsx("span",{className:"text-gray-500",children:String(t)})}function m({value:t,depth:r}){const s=Object.keys(t),[c,a]=l.useState(r<2);return s.length===0?e.jsx("span",{className:"text-gray-500",children:"{}"}):e.jsx(d,{open:c,onToggle:()=>a(n=>!n),collapsedLabel:`{…} ${s.length} ${s.length===1?"key":"keys"}`,openBracket:"{",closeBracket:"}",depth:r,children:s.map((n,i)=>e.jsxs("div",{className:"pl-4",children:[e.jsxs("span",{className:"text-rose-700 dark:text-rose-400",children:['"',n,'"']}),e.jsx("span",{className:"text-gray-500",children:": "}),e.jsx(o,{value:t[n],depth:r+1}),i<s.length-1?e.jsx("span",{className:"text-gray-500",children:","}):null]},n))})}function u({value:t,depth:r}){const[s,c]=l.useState(r<2);return t.length===0?e.jsx("span",{className:"text-gray-500",children:"[]"}):e.jsx(d,{open:s,onToggle:()=>c(a=>!a),collapsedLabel:`[…] ${t.length} ${t.length===1?"item":"items"}`,openBracket:"[",closeBracket:"]",depth:r,children:t.map((a,n)=>e.jsxs("div",{className:"pl-4",children:[e.jsx(o,{value:a,depth:r+1}),n<t.length-1?e.jsx("span",{className:"text-gray-500",children:","}):null]},n))})}function d({open:t,onToggle:r,collapsedLabel:s,openBracket:c,closeBracket:a,depth:n,children:i}){return e.jsxs("span",{children:[e.jsx("button",{type:"button",onClick:r,"aria-expanded":t,className:"inline-flex items-center align-baseline text-gray-500 hover:text-gray-800 dark:hover:text-gray-200",children:e.jsx(p,{className:`h-3 w-3 shrink-0 transition-transform ${t?"rotate-90":""}`,"aria-hidden":!0})}),e.jsx("span",{className:"text-gray-500",children:c}),t?e.jsxs(e.Fragment,{children:[i,e.jsx("div",{className:n===0?"":"pl-0",children:e.jsx("span",{className:"text-gray-500",children:a})})]}):e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"px-1 text-gray-500",children:s}),e.jsx("span",{className:"text-gray-500",children:a})]})]})}export{N as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{u as j,i as r,j as e,C as v,I as u,a as w,A as c}from"./index-Bh66HmjW.js";function b({onSuccess:m,brandTitle:d}){const p=j(),[n,f]=r.useState(""),[i,x]=r.useState(""),[g,a]=r.useState(null),[o,l]=r.useState(!1);async function h(s){s.preventDefault(),a(null),l(!0);try{await p.login(n,i),m()}catch(t){t instanceof c&&t.status===403?a("Invalid credentials or insufficient permissions."):t instanceof c?a(t.message):a("Could not reach the server. Please try again."),l(!1)}}return e.jsx("div",{className:"flex min-h-screen items-center justify-center bg-gray-50 px-4",children:e.jsx(v,{className:"w-full max-w-sm",children:e.jsxs("form",{onSubmit:h,className:"flex flex-col gap-4 p-2",children:[e.jsx("h1",{className:"text-center text-lg font-semibold text-gray-900",children:d??"Sign in"}),e.jsx(u,{label:"Username",name:"username",autoComplete:"username",autoFocus:!0,required:!0,value:n,onChange:s=>f(s.target.value)}),e.jsx(u,{label:"Password",name:"password",type:"password",autoComplete:"current-password",required:!0,value:i,onChange:s=>x(s.target.value),error:g??void 0}),e.jsx(w,{type:"submit",variant:"primary",loading:o,disabled:o,children:"Sign in"})]})})})}export{b as LoginPage};
|