fix: unify theme system - fix theme switching persistence
- Unified localStorage key to 'theme-preference' across all components
- Fixed header.tsx using wrong localStorage key ('theme' instead of 'theme-preference')
- Added localStorage hybrid persistence for instant theme changes
- Removed router.refresh() which was causing stale data revert
- Replaced Blue theme with Sepia
- Consolidated auth() calls to prevent race conditions
- Updated UserSettingsData types to include all themes
This commit is contained in:
127
keep-notes/app/(main)/admin/ai/page.tsx
Normal file
127
keep-notes/app/(main)/admin/ai/page.tsx
Normal file
@@ -0,0 +1,127 @@
|
||||
import { AdminMetrics } from '@/components/admin-metrics'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Zap, Settings, Activity, TrendingUp } from 'lucide-react'
|
||||
|
||||
export default async function AdminAIPage() {
|
||||
// Mock AI metrics - in a real app, these would come from analytics
|
||||
const aiMetrics = [
|
||||
{
|
||||
title: 'Total Requests',
|
||||
value: '856',
|
||||
trend: { value: 12, isPositive: true },
|
||||
icon: <Zap className="h-5 w-5 text-yellow-600 dark:text-yellow-400" />,
|
||||
},
|
||||
{
|
||||
title: 'Success Rate',
|
||||
value: '98.5%',
|
||||
trend: { value: 2, isPositive: true },
|
||||
icon: <TrendingUp className="h-5 w-5 text-green-600 dark:text-green-400" />,
|
||||
},
|
||||
{
|
||||
title: 'Avg Response Time',
|
||||
value: '1.2s',
|
||||
trend: { value: 5, isPositive: true },
|
||||
icon: <Activity className="h-5 w-5 text-blue-600 dark:text-blue-400" />,
|
||||
},
|
||||
{
|
||||
title: 'Active Features',
|
||||
value: '6',
|
||||
icon: <Settings className="h-5 w-5 text-purple-600 dark:text-purple-400" />,
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex justify-between items-center">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900 dark:text-white">
|
||||
AI Management
|
||||
</h1>
|
||||
<p className="text-gray-600 dark:text-gray-400 mt-1">
|
||||
Monitor and configure AI features
|
||||
</p>
|
||||
</div>
|
||||
<Button variant="outline">
|
||||
<Settings className="mr-2 h-4 w-4" />
|
||||
Configure
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<AdminMetrics metrics={aiMetrics} />
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<div className="bg-white dark:bg-zinc-900 rounded-lg shadow overflow-hidden border border-gray-200 dark:border-gray-800 p-6">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
||||
Active AI Features
|
||||
</h2>
|
||||
<div className="space-y-3">
|
||||
{[
|
||||
'Title Suggestions',
|
||||
'Semantic Search',
|
||||
'Paragraph Reformulation',
|
||||
'Memory Echo',
|
||||
'Language Detection',
|
||||
'Auto Labeling',
|
||||
].map((feature) => (
|
||||
<div
|
||||
key={feature}
|
||||
className="flex items-center justify-between p-3 bg-gray-50 dark:bg-zinc-800 rounded-lg"
|
||||
>
|
||||
<span className="text-sm text-gray-900 dark:text-white">
|
||||
{feature}
|
||||
</span>
|
||||
<span className="px-2 py-1 text-xs font-medium text-green-700 dark:text-green-400 bg-green-100 dark:bg-green-900 rounded-full">
|
||||
Active
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white dark:bg-zinc-900 rounded-lg shadow overflow-hidden border border-gray-200 dark:border-gray-800 p-6">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
||||
AI Provider Status
|
||||
</h2>
|
||||
<div className="space-y-3">
|
||||
{[
|
||||
{ name: 'OpenAI', status: 'Connected', requests: '642' },
|
||||
{ name: 'Ollama', status: 'Available', requests: '214' },
|
||||
].map((provider) => (
|
||||
<div
|
||||
key={provider.name}
|
||||
className="flex items-center justify-between p-3 bg-gray-50 dark:bg-zinc-800 rounded-lg"
|
||||
>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-900 dark:text-white">
|
||||
{provider.name}
|
||||
</p>
|
||||
<p className="text-xs text-gray-600 dark:text-gray-400">
|
||||
{provider.requests} requests
|
||||
</p>
|
||||
</div>
|
||||
<span
|
||||
className={`px-2 py-1 text-xs font-medium rounded-full ${
|
||||
provider.status === 'Connected'
|
||||
? 'text-green-700 dark:text-green-400 bg-green-100 dark:bg-green-900'
|
||||
: 'text-blue-700 dark:text-blue-400 bg-blue-100 dark:bg-blue-900'
|
||||
}`}
|
||||
>
|
||||
{provider.status}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white dark:bg-zinc-900 rounded-lg shadow overflow-hidden border border-gray-200 dark:border-gray-800 p-6">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
||||
Recent AI Requests
|
||||
</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400">
|
||||
Recent AI requests will be displayed here.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
23
keep-notes/app/(main)/admin/layout.tsx
Normal file
23
keep-notes/app/(main)/admin/layout.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { AdminSidebar } from '@/components/admin-sidebar'
|
||||
import { AdminContentArea } from '@/components/admin-content-area'
|
||||
import { auth } from '@/auth'
|
||||
import { redirect } from 'next/navigation'
|
||||
|
||||
export default async function AdminLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
const session = await auth()
|
||||
|
||||
if ((session?.user as any)?.role !== 'ADMIN') {
|
||||
redirect('/')
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen bg-gray-50 dark:bg-zinc-950">
|
||||
<AdminSidebar />
|
||||
<AdminContentArea>{children}</AdminContentArea>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,39 +1,58 @@
|
||||
import { getUsers } from '@/app/actions/admin'
|
||||
import { UserList } from './user-list'
|
||||
import { CreateUserDialog } from './create-user-dialog'
|
||||
import { auth } from '@/auth'
|
||||
import { redirect } from 'next/navigation'
|
||||
import Link from 'next/link'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Settings } from 'lucide-react'
|
||||
import { AdminPageHeader, SettingsButton } from '@/components/admin-page-header'
|
||||
import { AdminMetrics } from '@/components/admin-metrics'
|
||||
import { Users, Activity, Database, Zap } from 'lucide-react'
|
||||
|
||||
export default async function AdminPage() {
|
||||
const session = await auth()
|
||||
|
||||
if ((session?.user as any)?.role !== 'ADMIN') {
|
||||
redirect('/')
|
||||
}
|
||||
|
||||
const users = await getUsers()
|
||||
|
||||
// Mock metrics data - in a real app, these would come from analytics
|
||||
const metrics = [
|
||||
{
|
||||
title: 'Total Users',
|
||||
value: users.length,
|
||||
trend: { value: 12, isPositive: true },
|
||||
icon: <Users className="h-5 w-5 text-blue-600 dark:text-blue-400" />,
|
||||
},
|
||||
{
|
||||
title: 'Active Sessions',
|
||||
value: '24',
|
||||
trend: { value: 8, isPositive: true },
|
||||
icon: <Activity className="h-5 w-5 text-green-600 dark:text-green-400" />,
|
||||
},
|
||||
{
|
||||
title: 'Total Notes',
|
||||
value: '1,234',
|
||||
trend: { value: 24, isPositive: true },
|
||||
icon: <Database className="h-5 w-5 text-purple-600 dark:text-purple-400" />,
|
||||
},
|
||||
{
|
||||
title: 'AI Requests',
|
||||
value: '856',
|
||||
trend: { value: 5, isPositive: false },
|
||||
icon: <Zap className="h-5 w-5 text-yellow-600 dark:text-yellow-400" />,
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="container mx-auto py-10 px-4">
|
||||
<div className="flex justify-between items-center mb-8">
|
||||
<AdminPageHeader />
|
||||
<div className="flex gap-2">
|
||||
<Link href="/admin/settings">
|
||||
<Button variant="outline">
|
||||
<Settings className="mr-2 h-4 w-4" />
|
||||
<SettingsButton />
|
||||
</Button>
|
||||
</Link>
|
||||
<CreateUserDialog />
|
||||
</div>
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900 dark:text-white">
|
||||
Dashboard
|
||||
</h1>
|
||||
<p className="text-gray-600 dark:text-gray-400 mt-1">
|
||||
Overview of your application metrics
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white dark:bg-zinc-900 rounded-lg shadow overflow-hidden border border-gray-200 dark:border-gray-800">
|
||||
<UserList initialUsers={users} />
|
||||
<AdminMetrics metrics={metrics} />
|
||||
|
||||
<div className="bg-white dark:bg-zinc-900 rounded-lg shadow overflow-hidden border border-gray-200 dark:border-gray-800 p-6">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
||||
Recent Activity
|
||||
</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400">
|
||||
Recent activity will be displayed here.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
import { auth } from '@/auth'
|
||||
import { redirect } from 'next/navigation'
|
||||
import { getSystemConfig } from '@/app/actions/admin-settings'
|
||||
import { AdminSettingsForm } from './admin-settings-form'
|
||||
|
||||
export default async function AdminSettingsPage() {
|
||||
const session = await auth()
|
||||
|
||||
if ((session?.user as any)?.role !== 'ADMIN') {
|
||||
redirect('/')
|
||||
}
|
||||
|
||||
const config = await getSystemConfig()
|
||||
|
||||
return (
|
||||
<div className="container max-w-4xl mx-auto py-10 px-4">
|
||||
<h1 className="text-3xl font-bold mb-8">System Configuration</h1>
|
||||
<AdminSettingsForm config={config} />
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900 dark:text-white">
|
||||
Settings
|
||||
</h1>
|
||||
<p className="text-gray-600 dark:text-gray-400 mt-1">
|
||||
Configure application-wide settings
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white dark:bg-zinc-900 rounded-lg shadow overflow-hidden border border-gray-200 dark:border-gray-800 p-6">
|
||||
<AdminSettingsForm config={config} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
29
keep-notes/app/(main)/admin/users/page.tsx
Normal file
29
keep-notes/app/(main)/admin/users/page.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { getUsers } from '@/app/actions/admin'
|
||||
import { CreateUserDialog } from '../create-user-dialog'
|
||||
import { UserList } from '../user-list'
|
||||
import { Plus } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
export default async function AdminUsersPage() {
|
||||
const users = await getUsers()
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex justify-between items-center">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900 dark:text-white">
|
||||
Users
|
||||
</h1>
|
||||
<p className="text-gray-600 dark:text-gray-400 mt-1">
|
||||
Manage application users and permissions
|
||||
</p>
|
||||
</div>
|
||||
<CreateUserDialog />
|
||||
</div>
|
||||
|
||||
<div className="bg-white dark:bg-zinc-900 rounded-lg shadow overflow-hidden border border-gray-200 dark:border-gray-800">
|
||||
<UserList initialUsers={users} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user