feat: entity cache system, nav UX improvements, split CLAUDE.md

- Add $state-based entity cache layer with 30s TTL, request deduplication,
  and local mutation helpers (entity-cache.svelte.ts + caches.svelte.ts)
- Wire all 10 page components to use shared caches for cross-page data
- Add slide animation for nav tree expand/collapse with rotating chevron
- Remove aggregate count badges from container nav nodes (keep on leaves)
- Convert Targets from flat leaf to group with per-type children
  (Telegram, Webhook, Email, Discord, Slack, ntfy, Matrix)
- Add URL-based type filtering on Targets page with per-type descriptions
- Add Bots group children for Email and Matrix alongside Telegram
- Tab-based routing for bots page (?tab=telegram/email/matrix)
- Add per-type target counts and email/matrix bot counts to /status/counts
- Split CLAUDE.md into focused context files under .claude/docs/
- Fix .gitignore: scope lib/ to root, allow .claude/docs/ tracking
- Clear all caches on logout
- Reset form state when switching target type tabs
This commit is contained in:
2026-03-21 23:35:50 +03:00
parent 2c740ff2d2
commit 563716fa76
25 changed files with 551 additions and 155 deletions
@@ -12,7 +12,9 @@ from ..database.models import (
CommandConfig,
CommandTemplateConfig,
CommandTracker,
EmailBot,
EventLog,
MatrixBot,
NotificationTarget,
NotificationTracker,
ServiceProvider,
@@ -119,6 +121,8 @@ async def get_nav_counts(
(TemplateConfig, "template_configs"),
(NotificationTarget, "targets"),
(TelegramBot, "telegram_bots"),
(EmailBot, "email_bots"),
(MatrixBot, "matrix_bots"),
(CommandTracker, "command_trackers"),
(CommandConfig, "command_configs"),
(CommandTemplateConfig, "command_template_configs"),
@@ -138,6 +142,16 @@ async def get_nav_counts(
)).one()
counts[key] += system_count
# Per-type target counts for nav badges
for target_type in ("telegram", "webhook", "email", "discord", "slack", "ntfy", "matrix"):
type_count = (await session.exec(
select(func.count()).select_from(NotificationTarget).where(
NotificationTarget.user_id == user.id,
NotificationTarget.type == target_type,
)
)).one()
counts[f"targets_{target_type}"] = type_count
return counts