dashboard: кастомизируемый дашборд из виджетов + шаблоны по умолчанию

Дашборд теперь собирается из виджетов (приветствие, метрики, мои задачи,
новости, быстрый доступ). Сотрудник настраивает состав и порядок под себя
(вкл/выкл + перемещение ↑↓), а для тех, кто не настраивал, действует набор
по умолчанию по типу локации (Офис/Аптека/Склад), задаётся в админке.

- core/dashboard.py: реестр виджетов + сборка раскладки
- core/models/dashboard.py: DashboardLayout (персональная), DashboardDefaults
  (синглтон по типу локации)
- core/views.py: dashboard_view через реестр + dashboard_customize_view
- templates/pages/dashboard_widgets/*: виджеты-партиалы; dashboard.html —
  цикл по виджетам; dashboard_customize.html — настройка (HTMX + ↑↓)
- admin: DashboardDefaults (чекбоксами по локации) + read-only раскладки;
  пункт меню «Дашборд по умолчанию»
- core/tests_dashboard.py: 11 тестов (логика раскладки + view)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-09-21 18:42:21 +03:00
parent b2f0c2a1e2
commit d07b9c6e97
16 changed files with 804 additions and 201 deletions
@@ -0,0 +1,42 @@
# Generated by Django 6.0.3 on 2026-09-21 15:36
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0020_alter_connectorconfig_extra_config'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='DashboardDefaults',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('office', models.JSONField(blank=True, default=list, verbose_name='Офис')),
('pharmacy', models.JSONField(blank=True, default=list, verbose_name='Аптека')),
('warehouse', models.JSONField(blank=True, default=list, verbose_name='Склад')),
],
options={
'verbose_name': 'Дашборд по умолчанию',
'verbose_name_plural': 'Дашборд по умолчанию',
},
),
migrations.CreateModel(
name='DashboardLayout',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('widgets', models.JSONField(blank=True, default=list, verbose_name='Виджеты (порядок)')),
('updated_at', models.DateTimeField(auto_now=True)),
('employee', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='dashboard_layout', to=settings.AUTH_USER_MODEL, verbose_name='Сотрудник')),
],
options={
'verbose_name': 'Раскладка дашборда',
'verbose_name_plural': 'Раскладки дашборда',
},
),
]