Каталог сущностей
Главная вкладка приложения. Здесь живут все ноды кампании — PC, NPC, локации, петли, сессии, предметы и прочее. Никакой жёсткой иерархии папок: всё связано рёбрами, навигация — плоская.
Граф нод
В основе каталога — три таблицы: node_types, nodes, edges (миграция 001_initial_schema.sql).
node_types — типы нод кампании. Каждый тип имеет slug, label, icon,
default_fields (jsonb-шаблон формы) и sort_order. Типы не глобальные —
у каждой кампании своё множество. Стандартные типы, сидируемые при создании
кампании: pc, npc, location, creature, item, loop, session,
elective, encounter. Тип encounter — особый: его ноды являются зеркалами
энкаунтеров и в каталоге не отображаются (фильтруются в lib/sidebar-cache.ts
и в запросах каталога).
nodes — сами сущности. Поля: title, fields jsonb (значения по шаблону
default_fields), content text (markdown-блок, добавлен миграцией
011_node_content.sql), search_vector tsvector (поддерживается триггером
trg_nodes_search_vector).
edges — связи между нодами через edge_types. Направленные: source_id → target_id. Базовые типы рёбер (is_base=true): contains (петля→сессия),
participated_in (сессия→PC). Кампанийный тип: has_elective (PC→факультатив).
Подробнее — concepts/node-graph.md.
Сайдбар
Сайдбар отображает все типы нод и ноды кампании, сгруппированные по типу.
Данные кэшируются через unstable_cache с тегом sidebar:<campaignId> и
реинвалидируются при любой мутации нод или типов — контракт описан в
AGENTS.md и реализован в lib/sidebar-cache.ts.
app/c/[slug]/catalog/ — основной маршрут каталога.
Ключевые компоненты: components/catalog-sidebar-wrapper.tsx,
components/node-list.tsx, components/node-card.tsx.
Поиск, фильтры, сортировка
- Полнотекстовый поиск —
tsvectorпоtitle + content + fields.*. Язык —russian. Индекс:idx_nodes_search (GIN). Поиск работает черезto_tsquery('russian', ...)на стороне сервера. - Фильтр по типу — выбор одного или нескольких
node_type.slug. - Фильтр по статусу — для типов, у которых в
fieldsесть полеstatus(петли:past/current/future). - Сортировки — по
title(алфавит) илиcreated_at(хронология). По умолчанию —title ASC.
Создание ноды
Форма создания — компонент create-node-form.tsx. При создании ноды:
- Форма отправляет данные в server action.
- Server action пишет через admin-client (bypass RLS).
- После записи вызывается
invalidateSidebar(campaignId), чтобы новая нода немедленно появилась в сайдбаре.
Черновики форм не теряются при случайном закрытии — они сохраняются в
localStorage через hooks/use-form-draft.ts
(см. architecture/form-drafts.md).
Навигация
Каталог предоставляет плоский список — никакой иерархии папок. Иерархию (если нужна) DM выстраивает рёбрами. Это сознательное решение: в west-marches контекст одной локации часто принадлежит сразу нескольким «папкам», а граф гибче дерева.
Нода encounter (node_type.slug = 'encounter') исключена из каталога и
сайдбара — энкаунтеры доступны через отдельную вкладку Encounters.