feat(09-04): page shell, drawer wiring, skeleton helper, channels placeholder

- app/mobile/profile/page.tsx: server-component shell gated by requireAuth() + redirect('/auth/sign-in')
- ProfileSectionSkeleton.tsx: generic 3-row pulsing skeleton Card
- ProfileChannelsSectionPlaceholder.tsx: stub Channels card (Plan 05 swaps real component)
- MoreDrawer.tsx Account section: identity row wrapped in Link, new Profile & preferences row above Sign-out
This commit is contained in:
lorentz 2026-05-10 07:40:40 -04:00
parent 47cab788fc
commit da13caf9cb
4 changed files with 102 additions and 18 deletions

View file

@ -0,0 +1,30 @@
// /mobile/profile (PROF-01) — gated by requireAuth(), server-rendered shell
// hosts the four client sections.
import { redirect } from 'next/navigation';
import { requireAuth } from '@/lib/auth-utils';
import { ProfileTimezoneSection } from '@/components/mobile/profile/ProfileTimezoneSection';
import { ProfileThemeSection } from '@/components/mobile/profile/ProfileThemeSection';
import { ProfileNotificationMatrix } from '@/components/mobile/profile/ProfileNotificationMatrix';
import { ProfileChannelsSection } from '@/components/mobile/profile/ProfileChannelsSectionPlaceholder';
export default async function MobileProfilePage() {
const { session, error } = await requireAuth();
// CRITICAL: requireAuth() returns NextResponse on miss — that shape is for
// API routes only. A Page route MUST call redirect() instead. Returning
// `error` from here would render a JSON 401 body as the page, which is wrong.
if (error || !session) {
redirect('/auth/sign-in');
}
return (
<main className="px-4 pb-safe">
<h1 className="text-xl font-semibold pt-4 pb-2">Profile &amp; Preferences</h1>
<div className="space-y-4">
<ProfileTimezoneSection />
<ProfileThemeSection />
<ProfileNotificationMatrix />
<ProfileChannelsSection />
</div>
</main>
);
}