diff --git a/.gitignore b/.gitignore index 1329850..9ab55a5 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ coverage *.env.local .env.production sessions/ +*.tsbuildinfo diff --git a/apps/web/app/drafts/page.tsx b/apps/web/app/(chapter)/drafts/page.tsx similarity index 95% rename from apps/web/app/drafts/page.tsx rename to apps/web/app/(chapter)/drafts/page.tsx index 0a510a2..b066727 100644 --- a/apps/web/app/drafts/page.tsx +++ b/apps/web/app/(chapter)/drafts/page.tsx @@ -1,7 +1,7 @@ -import { apiFetch } from '../_lib/api'; +import { apiFetch } from '@/app/_lib/api'; import { redirect } from 'next/navigation'; -import { getToken } from '../_lib/api'; -import { DraftCard } from '../admin/drafts/DraftCard'; +import { getToken } from '@/app/_lib/api'; +import { DraftCard } from '@/app/_components/draft-card'; import Link from 'next/link'; type DraftType = 'EVENT' | 'SEVA_TASK' | 'FAQ_ITEM'; diff --git a/apps/web/app/groups/GroupsTabs.tsx b/apps/web/app/(chapter)/groups/GroupsTabs.tsx similarity index 100% rename from apps/web/app/groups/GroupsTabs.tsx rename to apps/web/app/(chapter)/groups/GroupsTabs.tsx diff --git a/apps/web/app/groups/RouteManager.test.tsx b/apps/web/app/(chapter)/groups/RouteManager.test.tsx similarity index 100% rename from apps/web/app/groups/RouteManager.test.tsx rename to apps/web/app/(chapter)/groups/RouteManager.test.tsx diff --git a/apps/web/app/groups/RouteManager.tsx b/apps/web/app/(chapter)/groups/RouteManager.tsx similarity index 100% rename from apps/web/app/groups/RouteManager.tsx rename to apps/web/app/(chapter)/groups/RouteManager.tsx diff --git a/apps/web/app/groups/page.tsx b/apps/web/app/(chapter)/groups/page.tsx similarity index 98% rename from apps/web/app/groups/page.tsx rename to apps/web/app/(chapter)/groups/page.tsx index cfd600a..7773387 100644 --- a/apps/web/app/groups/page.tsx +++ b/apps/web/app/(chapter)/groups/page.tsx @@ -1,6 +1,6 @@ import { RouteManager } from './RouteManager'; import { GroupsTabs } from './GroupsTabs'; -import { apiFetch } from '../_lib/api'; +import { apiFetch } from '@/app/_lib/api'; interface Group { id: string; diff --git a/apps/web/app/(chapter)/layout.tsx b/apps/web/app/(chapter)/layout.tsx new file mode 100644 index 0000000..f66f1a6 --- /dev/null +++ b/apps/web/app/(chapter)/layout.tsx @@ -0,0 +1,40 @@ +'use client'; + +import { useEffect } from 'react'; +import { usePathname, useRouter } from 'next/navigation'; +import { useAuth } from '@/app/_lib/auth-context'; +import { PortalShell } from '@/app/_components/portal-shell'; +import { Search, Users, Inbox, FileText, SlidersHorizontal, Bot } from 'lucide-react'; + +const NAV = [ + { href: '/search', label: 'Search', icon: }, + { href: '/groups', label: 'Groups & Routes', icon: }, + { href: '/messages/pending', label: 'Pending', icon: }, + { href: '/drafts', label: 'AI Drafts', icon: }, + { href: '/settings/rules', label: 'Rules', icon: }, + { href: '/settings/bot', label: 'Bot', icon: }, +]; + +export default function ChapterLayout({ children }: { children: React.ReactNode }) { + const { admin, loading, logout } = useAuth(); + const router = useRouter(); + const pathname = usePathname(); + + useEffect(() => { + if (!loading && !admin) router.replace(`/login?next=${encodeURIComponent(pathname)}`); + }, [admin, loading, pathname, router]); + + return ( + { void logout(); router.replace('/login'); }} + > + {children} + + ); +} diff --git a/apps/web/app/messages/[id]/page.tsx b/apps/web/app/(chapter)/messages/[id]/page.tsx similarity index 99% rename from apps/web/app/messages/[id]/page.tsx rename to apps/web/app/(chapter)/messages/[id]/page.tsx index 4fed4b4..b552503 100644 --- a/apps/web/app/messages/[id]/page.tsx +++ b/apps/web/app/(chapter)/messages/[id]/page.tsx @@ -1,5 +1,5 @@ import Link from 'next/link'; -import { apiFetch } from '../../_lib/api'; +import { apiFetch } from '@/app/_lib/api'; interface MessageDetail { id: string; diff --git a/apps/web/app/messages/pending/page.tsx b/apps/web/app/(chapter)/messages/pending/page.tsx similarity index 98% rename from apps/web/app/messages/pending/page.tsx rename to apps/web/app/(chapter)/messages/pending/page.tsx index 6709e65..314c744 100644 --- a/apps/web/app/messages/pending/page.tsx +++ b/apps/web/app/(chapter)/messages/pending/page.tsx @@ -1,4 +1,4 @@ -import { apiFetch } from '../../_lib/api'; +import { apiFetch } from '@/app/_lib/api'; interface PendingMessage { id: string; diff --git a/apps/web/app/search/page.test.tsx b/apps/web/app/(chapter)/search/page.test.tsx similarity index 100% rename from apps/web/app/search/page.test.tsx rename to apps/web/app/(chapter)/search/page.test.tsx diff --git a/apps/web/app/search/page.tsx b/apps/web/app/(chapter)/search/page.tsx similarity index 98% rename from apps/web/app/search/page.tsx rename to apps/web/app/(chapter)/search/page.tsx index f126e12..12089e9 100644 --- a/apps/web/app/search/page.tsx +++ b/apps/web/app/(chapter)/search/page.tsx @@ -1,5 +1,5 @@ import Link from 'next/link'; -import { apiFetch } from '../_lib/api'; +import { apiFetch } from '@/app/_lib/api'; interface MeiliHit { id: string; diff --git a/apps/web/app/settings/bot/BotSettingsCard.tsx b/apps/web/app/(chapter)/settings/bot/BotSettingsCard.tsx similarity index 100% rename from apps/web/app/settings/bot/BotSettingsCard.tsx rename to apps/web/app/(chapter)/settings/bot/BotSettingsCard.tsx diff --git a/apps/web/app/settings/bot/page.tsx b/apps/web/app/(chapter)/settings/bot/page.tsx similarity index 96% rename from apps/web/app/settings/bot/page.tsx rename to apps/web/app/(chapter)/settings/bot/page.tsx index a384605..d394b09 100644 --- a/apps/web/app/settings/bot/page.tsx +++ b/apps/web/app/(chapter)/settings/bot/page.tsx @@ -1,5 +1,5 @@ import { BotSettingsCard } from './BotSettingsCard'; -import { apiFetch } from '../../_lib/api'; +import { apiFetch } from '@/app/_lib/api'; interface BotSummary { id: string; diff --git a/apps/web/app/settings/rules/RuleManager.tsx b/apps/web/app/(chapter)/settings/rules/RuleManager.tsx similarity index 100% rename from apps/web/app/settings/rules/RuleManager.tsx rename to apps/web/app/(chapter)/settings/rules/RuleManager.tsx diff --git a/apps/web/app/settings/rules/page.tsx b/apps/web/app/(chapter)/settings/rules/page.tsx similarity index 95% rename from apps/web/app/settings/rules/page.tsx rename to apps/web/app/(chapter)/settings/rules/page.tsx index ab309b4..5a20157 100644 --- a/apps/web/app/settings/rules/page.tsx +++ b/apps/web/app/(chapter)/settings/rules/page.tsx @@ -1,5 +1,5 @@ import { RuleManager } from './RuleManager'; -import { apiFetch } from '../../_lib/api'; +import { apiFetch } from '@/app/_lib/api'; interface RuleData { id: string; diff --git a/apps/web/app/threads/[id]/page.tsx b/apps/web/app/(chapter)/threads/[id]/page.tsx similarity index 98% rename from apps/web/app/threads/[id]/page.tsx rename to apps/web/app/(chapter)/threads/[id]/page.tsx index 2958d6a..291ad27 100644 --- a/apps/web/app/threads/[id]/page.tsx +++ b/apps/web/app/(chapter)/threads/[id]/page.tsx @@ -1,5 +1,5 @@ import Link from 'next/link'; -import { apiFetch } from '../../_lib/api'; +import { apiFetch } from '@/app/_lib/api'; interface ThreadMessage { id: string; diff --git a/apps/web/app/threads/page.tsx b/apps/web/app/(chapter)/threads/page.tsx similarity index 98% rename from apps/web/app/threads/page.tsx rename to apps/web/app/(chapter)/threads/page.tsx index cf6c4e1..379cee8 100644 --- a/apps/web/app/threads/page.tsx +++ b/apps/web/app/(chapter)/threads/page.tsx @@ -1,5 +1,5 @@ import Link from 'next/link'; -import { apiFetch } from '../_lib/api'; +import { apiFetch } from '@/app/_lib/api'; interface ThreadSummary { id: string; diff --git a/apps/web/app/admin/drafts/DraftCard.tsx b/apps/web/app/_components/draft-card.tsx similarity index 100% rename from apps/web/app/admin/drafts/DraftCard.tsx rename to apps/web/app/_components/draft-card.tsx diff --git a/apps/web/app/_components/portal-login-shell.tsx b/apps/web/app/_components/portal-login-shell.tsx index c300647..cb80742 100644 --- a/apps/web/app/_components/portal-login-shell.tsx +++ b/apps/web/app/_components/portal-login-shell.tsx @@ -1,47 +1,83 @@ +import Link from 'next/link'; import { cn } from '../_lib/utils'; +import { PORTAL_THEMES, type PortalTheme } from './portal-theme'; +import { Check, ArrowLeft } from 'lucide-react'; interface PortalLoginShellProps { title: string; subtitle: string; - accentClass?: string; + theme: PortalTheme; + /** Short value-prop bullets shown on the branding panel */ + highlights?: string[]; children: React.ReactNode; footer?: React.ReactNode; } -export function PortalLoginShell({ title, subtitle, accentClass = 'bg-primary', children, footer }: PortalLoginShellProps) { +export function PortalLoginShell({ title, subtitle, theme, highlights = [], children, footer }: PortalLoginShellProps) { + const t = PORTAL_THEMES[theme]; + return (
- {/* Left branding panel */} -
-
- TOWER + {/* Branding panel */} +