Initial commit: MOPC platform with Docker deployment setup
Full Next.js 15 platform with tRPC, Prisma, PostgreSQL, NextAuth. Includes production Dockerfile (multi-stage, port 7600), docker-compose with registry-based image pull, Gitea Actions CI workflow, nginx config for portal.monaco-opc.com, deployment scripts, and DEPLOYMENT.md guide. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
76
src/app/(admin)/admin/settings/page.tsx
Normal file
76
src/app/(admin)/admin/settings/page.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
import { Suspense } from 'react'
|
||||
import { redirect } from 'next/navigation'
|
||||
import { auth } from '@/lib/auth'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
import { Card, CardContent, CardHeader } from '@/components/ui/card'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { SettingsContent } from '@/components/settings/settings-content'
|
||||
|
||||
async function SettingsLoader() {
|
||||
const settings = await prisma.systemSettings.findMany({
|
||||
orderBy: [{ category: 'asc' }, { key: 'asc' }],
|
||||
})
|
||||
|
||||
// Convert settings array to key-value map
|
||||
// For secrets, pass a marker but not the actual value
|
||||
const settingsMap: Record<string, string> = {}
|
||||
settings.forEach((setting) => {
|
||||
if (setting.isSecret && setting.value) {
|
||||
// Pass marker for UI to show "existing" state
|
||||
settingsMap[setting.key] = '********'
|
||||
} else {
|
||||
settingsMap[setting.key] = setting.value
|
||||
}
|
||||
})
|
||||
|
||||
return <SettingsContent initialSettings={settingsMap} />
|
||||
}
|
||||
|
||||
function SettingsSkeleton() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<Skeleton className="h-10 w-full" />
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Skeleton className="h-6 w-32" />
|
||||
<Skeleton className="h-4 w-64" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-4">
|
||||
{[...Array(4)].map((_, i) => (
|
||||
<Skeleton key={i} className="h-16 w-full" />
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default async function SettingsPage() {
|
||||
const session = await auth()
|
||||
|
||||
// Only super admins can access settings
|
||||
if (session?.user?.role !== 'SUPER_ADMIN') {
|
||||
redirect('/admin')
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Header */}
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold tracking-tight">Settings</h1>
|
||||
<p className="text-muted-foreground">
|
||||
Configure platform settings and preferences
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<Suspense fallback={<SettingsSkeleton />}>
|
||||
<SettingsLoader />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user