You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
232 lines
7.3 KiB
232 lines
7.3 KiB
import json
|
|
from django import forms
|
|
from django.conf import settings
|
|
from django.contrib.humanize.templatetags.humanize import intcomma
|
|
from django.urls import reverse, path
|
|
from django.utils.safestring import mark_safe
|
|
from django.utils.translation import gettext_lazy as _
|
|
from django.views.generic import RedirectView
|
|
from django.utils.translation import get_language
|
|
from django.http import JsonResponse
|
|
from django.views.decorators.http import require_POST
|
|
|
|
# --- Unicode slugify override for Unfold tabs ---
|
|
import django.utils.text
|
|
from django.template.defaultfilters import register
|
|
|
|
_original_slugify = django.utils.text.slugify
|
|
|
|
def _unicode_slugify(value, allow_unicode=True):
|
|
return _original_slugify(value, allow_unicode=True)
|
|
|
|
django.utils.text.slugify = _unicode_slugify
|
|
|
|
@register.filter(is_safe=True)
|
|
def slugify(value):
|
|
return _unicode_slugify(value)
|
|
|
|
# Unfold Imports
|
|
from unfold.sites import UnfoldAdminSite
|
|
|
|
|
|
# ---------------------------------------------------------
|
|
# 1. Helper Functions
|
|
# ---------------------------------------------------------
|
|
|
|
def admin_url_generator(request, url_name):
|
|
"""
|
|
Dynamically generates admin URLs for the active admin site namespace.
|
|
"""
|
|
namespace = 'admin'
|
|
full_view_name = f"{namespace}:{url_name}"
|
|
try:
|
|
return reverse(full_view_name)
|
|
except Exception:
|
|
return "#"
|
|
|
|
def variables(request):
|
|
return {"plausible_domain": getattr(settings, 'PLAUSIBLE_DOMAIN', '')}
|
|
|
|
@require_POST
|
|
def toggle_sidebar(request):
|
|
"""Toggle sidebar state for Unfold admin interface"""
|
|
return JsonResponse({'status': 'success'})
|
|
|
|
|
|
# ---------------------------------------------------------
|
|
# 2. Custom Login Form
|
|
# ---------------------------------------------------------
|
|
|
|
class LoginForm:
|
|
"""Lazy login form to avoid circular imports during settings loading"""
|
|
|
|
@staticmethod
|
|
def get_form():
|
|
from unfold.forms import AuthenticationForm
|
|
|
|
class CustomLoginForm(AuthenticationForm):
|
|
password = forms.CharField(widget=forms.PasswordInput(render_value=True))
|
|
|
|
def __init__(self, request=None, *args, **kwargs):
|
|
super().__init__(request, *args, **kwargs)
|
|
self.fields["username"].label = _("Email")
|
|
|
|
return CustomLoginForm
|
|
|
|
|
|
# ---------------------------------------------------------
|
|
# 3. Unified Admin Site Definition
|
|
# ---------------------------------------------------------
|
|
|
|
class AppAdminSite(UnfoldAdminSite):
|
|
"""Unified Modern Admin Site with Unfold Theme"""
|
|
site_header = _("Management Dashboard")
|
|
site_title = _("Admin Portal")
|
|
index_title = _("System Administration")
|
|
site_subheader = _("Application Console")
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.login_form = LoginForm.get_form()
|
|
|
|
def get_urls(self):
|
|
urls = super().get_urls()
|
|
custom_urls = [
|
|
path('toggle_sidebar/', toggle_sidebar, name='toggle_sidebar'),
|
|
]
|
|
return custom_urls + urls
|
|
|
|
def _get_colors(self, key, *args):
|
|
if key != "COLORS":
|
|
return super()._get_colors(key, *args)
|
|
|
|
# Modern, sleek slate-indigo color palette
|
|
return {
|
|
"base": {
|
|
"50": "248 250 252",
|
|
"100": "241 245 249",
|
|
"200": "226 232 240",
|
|
"300": "203 213 225",
|
|
"400": "148 163 184",
|
|
"500": "100 116 139",
|
|
"600": "71 85 105",
|
|
"700": "51 65 85",
|
|
"800": "30 41 59",
|
|
"900": "15 23 42",
|
|
"950": "2 6 23",
|
|
},
|
|
"primary": {
|
|
"50": "238 242 255",
|
|
"100": "224 231 255",
|
|
"200": "199 210 254",
|
|
"300": "165 180 252",
|
|
"400": "129 140 248",
|
|
"500": "99 102 241",
|
|
"600": "79 70 229",
|
|
"700": "67 56 202",
|
|
"800": "55 48 163",
|
|
"900": "49 46 129",
|
|
"950": "30 27 75",
|
|
},
|
|
"font": {
|
|
"subtle-light": "var(--color-base-500)",
|
|
"subtle-dark": "var(--color-base-400)",
|
|
"default-light": "var(--color-base-700)",
|
|
"default-dark": "var(--color-base-200)",
|
|
"important-light": "var(--color-base-900)",
|
|
"important-dark": "255 255 255",
|
|
},
|
|
}
|
|
|
|
|
|
class LazyAdminSite(UnfoldAdminSite):
|
|
def __init__(self, site_class, name):
|
|
self._site_class = site_class
|
|
self._name = name
|
|
self._instance = None
|
|
self.name = name
|
|
|
|
def _ensure_instance(self):
|
|
if self._instance is None:
|
|
self._instance = self._site_class(name=self._name)
|
|
essential_attrs = ['site_header', 'site_title', 'index_title', 'site_url', 'login_template']
|
|
for attr in essential_attrs:
|
|
if hasattr(self._instance, attr):
|
|
setattr(self, attr, getattr(self._instance, attr))
|
|
|
|
def _get_instance(self):
|
|
self._ensure_instance()
|
|
return self._instance
|
|
|
|
def __getattr__(self, name):
|
|
self._ensure_instance()
|
|
return getattr(self._instance, name)
|
|
|
|
def __call__(self, *args, **kwargs):
|
|
return self._get_instance()(*args, **kwargs)
|
|
|
|
@property
|
|
def urls(self):
|
|
return self._get_instance().urls
|
|
|
|
def get_urls(self):
|
|
return self._get_instance().get_urls()
|
|
|
|
def each_context(self, request):
|
|
return self._get_instance().each_context(request)
|
|
|
|
def register(self, model_or_iterable, admin_class=None, **options):
|
|
self._ensure_instance()
|
|
return self._instance.register(model_or_iterable, admin_class, **options)
|
|
|
|
|
|
# Singleton instances
|
|
project_admin_site = LazyAdminSite(AppAdminSite, 'admin')
|
|
# Compatibility alias
|
|
dovoodi_admin_site = project_admin_site
|
|
|
|
|
|
class HomeView(RedirectView):
|
|
def get_redirect_url(self, *args, **kwargs):
|
|
language = get_language() or 'en'
|
|
return f'/{language}/admin/'
|
|
|
|
|
|
# ---------------------------------------------------------
|
|
# 4. Dynamic Custom Dashboard
|
|
# ---------------------------------------------------------
|
|
|
|
def dashboard_callback(request, context):
|
|
"""Generic Dashboard KPI statistics"""
|
|
from django.apps import apps
|
|
|
|
if context is None:
|
|
context = {}
|
|
|
|
context.update({
|
|
"navigation": [{"title": _("Dashboard"), "link": "/admin/", "active": True}],
|
|
"kpi": [],
|
|
})
|
|
|
|
if not hasattr(request, "user") or not request.user.is_authenticated:
|
|
return context
|
|
|
|
try:
|
|
User = apps.get_model('account', 'User')
|
|
LoginHistory = apps.get_model('account', 'LoginHistory')
|
|
|
|
total_users = User.objects.count()
|
|
active_users = User.objects.filter(is_active=True).count()
|
|
staff_users = User.objects.filter(is_staff=True).count()
|
|
total_logins = LoginHistory.objects.count()
|
|
|
|
context["kpi"] = [
|
|
{"title": _("Total Users"), "metric": f"{total_users:,}"},
|
|
{"title": _("Active Users"), "metric": f"{active_users:,}"},
|
|
{"title": _("Staff Members"), "metric": f"{staff_users:,}"},
|
|
{"title": _("Total Logins"), "metric": f"{total_logins:,}"},
|
|
]
|
|
except Exception as e:
|
|
pass
|
|
|
|
return context
|