- Rename directory keep-notes -> memento-note with all code references - Prisma: SQLite -> PostgreSQL (both app and MCP server schemas) - Sync MCP schema with main app (add missing fields, relations, indexes) - Delete 17 SQLite migrations (clean slate for PostgreSQL) - Remove SQLite dependencies (@libsql/client, better-sqlite3, etc.) - Fix MCP server: hardcoded Windows DB paths -> DATABASE_URL env var - Fix MCP server: .dockerignore excluded index-sse.js (SSE mode broken) - MCP Dockerfile: node:20 -> node:22 - Docker Compose: add postgres service, remove SQLite volume - Generate favicon.ico, icon-192.png, icon-512.png, apple-icon.png - Update layout.tsx icons and manifest.json for PNG icons - Update all .env files for PostgreSQL - Rewrite README.md with updated sections - Remove mcp-server/node_modules and prisma/client-generated from git tracking Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
156 lines
5.4 KiB
TypeScript
156 lines
5.4 KiB
TypeScript
'use client'
|
||
|
||
import { useState } from 'react'
|
||
import { SettingsNav, SettingsSection, SettingToggle, SettingInput, SettingSelect } from '@/components/settings'
|
||
import { updateAISettings } from '@/app/actions/ai-settings'
|
||
import { toast } from 'sonner'
|
||
import { useLanguage } from '@/lib/i18n'
|
||
|
||
export default function ProfileSettingsPage() {
|
||
const { t } = useLanguage()
|
||
|
||
// Mock user data - in real implementation, load from server
|
||
const [user, setUser] = useState({
|
||
name: 'John Doe',
|
||
email: 'john@example.com'
|
||
})
|
||
|
||
const [language, setLanguage] = useState('auto')
|
||
const [showRecentNotes, setShowRecentNotes] = useState(false)
|
||
|
||
const handleNameChange = async (value: string) => {
|
||
setUser(prev => ({ ...prev, name: value }))
|
||
// TODO: Implement profile update
|
||
|
||
}
|
||
|
||
const handleEmailChange = async (value: string) => {
|
||
setUser(prev => ({ ...prev, email: value }))
|
||
// TODO: Implement email update
|
||
|
||
}
|
||
|
||
const handleLanguageChange = async (value: string) => {
|
||
setLanguage(value)
|
||
try {
|
||
await updateAISettings({ preferredLanguage: value as any })
|
||
} catch (error) {
|
||
console.error('Error updating language:', error)
|
||
toast.error(t('aiSettings.error'))
|
||
}
|
||
}
|
||
|
||
const handleRecentNotesChange = async (enabled: boolean) => {
|
||
setShowRecentNotes(enabled)
|
||
try {
|
||
await updateAISettings({ showRecentNotes: enabled })
|
||
} catch (error) {
|
||
console.error('Error updating recent notes setting:', error)
|
||
toast.error(t('aiSettings.error'))
|
||
}
|
||
}
|
||
|
||
return (
|
||
<div className="container mx-auto py-10 px-4 max-w-6xl">
|
||
<div className="grid grid-cols-1 lg:grid-cols-4 gap-6">
|
||
{/* Sidebar Navigation */}
|
||
<aside className="lg:col-span-1">
|
||
<SettingsNav />
|
||
</aside>
|
||
|
||
{/* Main Content */}
|
||
<main className="lg:col-span-3 space-y-6">
|
||
<div>
|
||
<h1 className="text-3xl font-bold mb-2">{t('profile.title')}</h1>
|
||
<p className="text-gray-600 dark:text-gray-400">
|
||
{t('profile.description')}
|
||
</p>
|
||
</div>
|
||
|
||
{/* Profile Information */}
|
||
<SettingsSection
|
||
title={t('profile.accountSettings')}
|
||
icon={<span className="text-2xl">👤</span>}
|
||
description={t('profile.description')}
|
||
>
|
||
<SettingInput
|
||
label={t('profile.displayName')}
|
||
description={t('profile.displayName')}
|
||
value={user.name}
|
||
onChange={handleNameChange}
|
||
placeholder={t('auth.namePlaceholder')}
|
||
/>
|
||
|
||
<SettingInput
|
||
label={t('profile.email')}
|
||
description={t('profile.email')}
|
||
value={user.email}
|
||
type="email"
|
||
onChange={handleEmailChange}
|
||
placeholder={t('auth.emailPlaceholder')}
|
||
/>
|
||
</SettingsSection>
|
||
|
||
{/* Preferences */}
|
||
<SettingsSection
|
||
title={t('settings.language')}
|
||
icon={<span className="text-2xl">⚙️</span>}
|
||
description={t('profile.languagePreferencesDescription')}
|
||
>
|
||
<SettingSelect
|
||
label={t('profile.preferredLanguage')}
|
||
description={t('profile.languageDescription')}
|
||
value={language}
|
||
options={[
|
||
{ value: 'auto', label: t('profile.autoDetect') },
|
||
{ value: 'en', label: 'English' },
|
||
{ value: 'fr', label: 'Français' },
|
||
{ value: 'es', label: 'Español' },
|
||
{ value: 'de', label: 'Deutsch' },
|
||
{ value: 'fa', label: 'فارسی' },
|
||
{ value: 'it', label: 'Italiano' },
|
||
{ value: 'pt', label: 'Português' },
|
||
{ value: 'ru', label: 'Русский' },
|
||
{ value: 'zh', label: '中文' },
|
||
{ value: 'ja', label: '日本語' },
|
||
{ value: 'ko', label: '한국어' },
|
||
{ value: 'ar', label: 'العربية' },
|
||
{ value: 'hi', label: 'हिन्दी' },
|
||
{ value: 'nl', label: 'Nederlands' },
|
||
{ value: 'pl', label: 'Polski' },
|
||
]}
|
||
onChange={handleLanguageChange}
|
||
/>
|
||
|
||
<SettingToggle
|
||
label={t('profile.showRecentNotes')}
|
||
description={t('profile.showRecentNotesDescription')}
|
||
checked={showRecentNotes}
|
||
onChange={handleRecentNotesChange}
|
||
/>
|
||
</SettingsSection>
|
||
|
||
{/* AI Settings Link */}
|
||
<div className="p-6 border rounded-lg bg-gradient-to-r from-purple-50 to-pink-50 dark:from-purple-950 dark:to-pink-950">
|
||
<div className="flex items-center gap-4">
|
||
<div className="text-4xl">✨</div>
|
||
<div className="flex-1">
|
||
<h3 className="font-semibold text-lg mb-1">{t('aiSettings.title')}</h3>
|
||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||
{t('aiSettings.description')}
|
||
</p>
|
||
</div>
|
||
<button
|
||
onClick={() => window.location.href = '/settings/ai'}
|
||
className="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-colors"
|
||
>
|
||
{t('nav.configureAI')}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|