Files
MOPC-Portal/src/app/(admin)/layout.tsx
Matt 3e70de3a5a Add Anthropic API, test environment, remove locale settings
Feature 1: Anthropic API Integration
- Add @anthropic-ai/sdk with adapter wrapping OpenAI-shaped interface
- Support Claude models (opus, sonnet, haiku) with extended thinking
- Auto-reset model on provider switch, JSON retry logic
- Add Claude model pricing to ai-usage tracker
- Update AI settings form with Anthropic provider option

Feature 2: Remove Locale Settings UI
- Strip Localization tab from admin settings
- Remove i18n settings from router inferCategory and getFeatureFlags
- Keep franc document language detection intact

Feature 3: Test Environment with Role Impersonation
- Add isTest field to User, Program, Project, Competition models
- Test environment service: create/teardown with realistic dummy data
- JWT-based impersonation for test users (@test.local emails)
- Impersonation banner with quick-switch between test roles
- Test environment panel in admin settings (SUPER_ADMIN only)
- Email redirect: @test.local emails routed to admin with [TEST] prefix
- Complete data isolation: 45+ isTest:false filters across platform
  - All global queries on User/Project/Program/Competition
  - AI services blocked from processing test data
  - Cron jobs skip test rounds/users
  - Analytics/exports exclude test data
  - Admin layout/pickers hide test programs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 17:28:07 +01:00

44 lines
1.2 KiB
TypeScript

import { prisma } from '@/lib/prisma'
import { requireRole } from '@/lib/auth-redirect'
import { AdminSidebar } from '@/components/layouts/admin-sidebar'
import { AdminEditionWrapper } from '@/components/layouts/admin-edition-wrapper'
export default async function AdminLayout({
children,
}: {
children: React.ReactNode
}) {
const session = await requireRole('SUPER_ADMIN', 'PROGRAM_ADMIN')
// Fetch all editions (programs) for the edition selector
const editions = await prisma.program.findMany({
where: { isTest: false },
select: {
id: true,
name: true,
year: true,
status: true,
},
orderBy: { year: 'desc' },
})
return (
<AdminEditionWrapper editions={editions}>
<div className="min-h-screen bg-background">
<AdminSidebar
user={{
name: session.user.name,
email: session.user.email,
role: session.user.role,
}}
/>
<main className="lg:pl-64">
{/* Spacer for mobile header */}
<div className="h-16 lg:hidden" />
<div className="container-app py-6 lg:py-8">{children}</div>
</main>
</div>
</AdminEditionWrapper>
)
}