Руководство по Cabinet
Когда Использовать
Используйте cabinet, когда нужен переиспользуемый dashboard shell с навигацией, виджетами и типизированными компонентами страниц. Cabinet предоставляет layout и структурные шаблоны; ваши feature apps добавляют навигационные записи и строят данные страниц через типизированные контракты.
Установка
Добавьте в INSTALLED_APPS и подключите context processor:
INSTALLED_APPS = [
...
"codex_django.cabinet",
"cabinet", # ваш project-level cabinet app
]
TEMPLATES = [{
"OPTIONS": {
"context_processors": [
...
"codex_django.cabinet.context_processors.cabinet",
]
}
}]
Регистрация Feature
Создайте cabinet.py в любом установленном app. CabinetConfig.ready() обнаруживает его автоматически через autodiscover_modules("cabinet").
# features/booking/cabinet.py
from codex_django.cabinet import declare, TopbarEntry, SidebarItem, Shortcut
# Staff пространство
declare(
space="staff",
module="booking",
topbar=TopbarEntry(
group="services",
label="Бронирование",
icon="bi-calendar-check",
url="/cabinet/booking/",
order=10,
),
sidebar=[
SidebarItem(label="Расписание", url="booking:schedule", icon="bi-calendar3"),
SidebarItem(label="Новая запись", url="booking:new", icon="bi-plus-circle"),
SidebarItem(label="Ожидают", url="booking:pending", icon="bi-hourglass-split",
badge_key="pending_count"),
],
shortcuts=[
Shortcut(label="Новая", url="booking:new", icon="bi-plus"),
],
)
Активный Модуль во View
Context processor читает request.cabinet_module, чтобы определить какой sidebar показывать.
Установите его в начале любого cabinet view:
def schedule_view(request):
request.cabinet_module = "booking"
...
return render(request, "booking/schedule.html", context)
Если не установлено, модуль определяется автоматически из URL namespace (request.resolver_match.app_name),
с финальным fallback на "admin".
Использование Компонента Страницы
Постройте типизированный контракт во view, передайте его в шаблон и отрендерите компонент через шаблонный include.
Таблица Данных
# view
from codex_django.cabinet import DataTableData, TableColumn, TableFilter, TableAction
def appointments_view(request):
request.cabinet_module = "booking"
table = DataTableData(
columns=[
TableColumn(key="client", label="Клиент", bold=True),
TableColumn(key="date", label="Дата"),
TableColumn(key="status", label="Статус"),
],
rows=list(Appointment.objects.values("client", "date", "status", "detail_url")),
filters=[
TableFilter(key="pending", label="Ожидают", value="pending"),
TableFilter(key="confirmed", label="Подтверждены", value="confirmed"),
],
actions=[TableAction(label="Открыть", url_key="detail_url", icon="bi-eye")],
search_placeholder="Поиск...",
)
return render(request, "booking/appointments.html", {"table": table})
{# booking/appointments.html #}
{% extends "cabinet/base_cabinet.html" %}
{% block content %}
{% templatetag openblock %} include "cabinet/components/data_table.html" with table=table {% templatetag closeblock %}
{% templatetag openblock %} include "cabinet/includes/_modal_base.html" {% templatetag closeblock %}
{% endblock %}
Сетка Календаря
Columns и rows — это что угодно: мастера, кабинеты, дни недели.
Backend вычисляет CalendarSlot.span из длительности; шаблон только рендерит.
from codex_django.cabinet import CalendarGridData, CalendarSlot
# 30-минутные слоты с 08:00 до 20:00
rows = [f"{h}:{m:02d}" for h in range(8, 20) for m in (0, 30)]
calendar = CalendarGridData(
cols=["Мастер 1", "Мастер 2", "Мастер 3"],
rows=rows,
events=[
CalendarSlot(
col=0, row=2, span=2, # индекс колонки, индекс строки, длительность/slot_size
title="Иван П.", subtitle="Стрижка",
color="#6366f1", url="/booking/42/",
),
],
slot_height_px=40,
new_event_url="/booking/new/", # hx-get при клике на пустой слот
)
return render(request, "booking/schedule.html", {"calendar": calendar})
{% extends "cabinet/base_cabinet.html" %}
{% block content %}
{% templatetag openblock %} include "cabinet/components/calendar_grid.html" with calendar=calendar {% templatetag closeblock %}
{% templatetag openblock %} include "cabinet/includes/_modal_base.html" {% templatetag closeblock %}
{% endblock %}
Сетка Карточек
from codex_django.cabinet import CardGridData, CardItem
cards = CardGridData(
items=[
CardItem(
id=str(c.pk),
title=c.full_name,
subtitle=c.category,
avatar=c.initials,
url=f"/cabinet/clients/{c.pk}/",
meta=[("bi-telephone", c.phone)],
)
for c in Client.objects.all()
],
search_placeholder="Поиск клиентов...",
)
{% templatetag openblock %} include "cabinet/components/card_grid.html" with cards=cards {% templatetag closeblock %}
Split Panel
from codex_django.cabinet import SplitPanelData, ListRow
panel = SplitPanelData(
items=[
ListRow(id=str(c.pk), primary=c.subject, secondary=c.last_message[:60])
for c in conversations
],
detail_url="/cabinet/conversations", # добавляет /<id> при клике
empty_message="Выберите переписку",
)
{% templatetag openblock %} include "cabinet/components/split_panel.html" with panel=panel {% templatetag closeblock %}
Клиентское Пространство
Зарегистрируйте отдельный вызов declare(space="client", ...) и наследуйтесь от base_client.html:
declare(
space="client",
module="booking",
sidebar=[
SidebarItem(label="Мои записи", url="booking:my_bookings",
icon="bi-calendar2-check"),
],
)
{# my_appointments.html #}
{% extends "cabinet/base_client.html" %}
{% block content %}
{% templatetag openblock %} include "cabinet/components/data_table.html" with table=table {% templatetag closeblock %}
{% endblock %}
CSS-токены клиентского пространства переопределяются независимо через .cab-wrapper--client { --cab-* } в theme/tokens.css проекта.
Доступные Компоненты
| Шаблон | Контракт | Для чего |
|---|---|---|
cabinet/components/data_table.html |
DataTableData |
Таблицы с фильтрами |
cabinet/components/calendar_grid.html |
CalendarGridData |
Сетки расписания |
cabinet/components/card_grid.html |
CardGridData |
Карточки клиентов/объектов |
cabinet/components/list_view.html |
ListViewData |
Простые списки |
cabinet/components/split_panel.html |
SplitPanelData |
Переписка/detail views |
Точки Входа Runtime
codex_django.cabinetcodex_django.cabinet.selectorcodex_django.cabinet.redis
Связанные Разделы
- Архитектура: Модуль Cabinet
- API reference: Cabinet Public API
- Внутренности: Cabinet Internal Modules