feat: RTL/i18n, AI translate+undo, no-refresh saves, settings perf
- RTL: force dir=rtl on LabelFilter, NotesViewToggle, LabelManagementDialog - i18n: add missing keys (notifications, privacy, edit/preview, AI translate/undo) - Settings pages: convert to Server Components (general, appearance) + loading skeleton - AI menu: add Translate option (10 languages) + Undo AI button in toolbar - Fix: saveInline uses REST API instead of Server Action → eliminates all implicit refreshes in list mode - Fix: NotesTabsView notes sync effect preserves selected note on content changes - Fix: auto-tag suggestions now filter already-assigned labels - Fix: color change in card view uses local state (no refresh) - Fix: nav links use <Link> for prefetching (Settings, Admin) - Fix: suppress duplicate label suggestions already on note - Route: add /api/ai/translate endpoint
This commit is contained in:
@@ -64,21 +64,7 @@ export function AISettingsPanel({ initialSettings }: AISettingsPanelProps) {
|
||||
}
|
||||
}
|
||||
|
||||
const handleProviderChange = async (value: 'auto' | 'openai' | 'ollama') => {
|
||||
setSettings(prev => ({ ...prev, aiProvider: value }))
|
||||
|
||||
try {
|
||||
setIsPending(true)
|
||||
await updateAISettings({ aiProvider: value })
|
||||
toast.success(t('aiSettings.saved'))
|
||||
} catch (error) {
|
||||
console.error('Error updating provider:', error)
|
||||
toast.error(t('aiSettings.error'))
|
||||
setSettings(initialSettings)
|
||||
} finally {
|
||||
setIsPending(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleLanguageChange = async (value: 'auto' | 'en' | 'fr' | 'es' | 'de' | 'fa' | 'it' | 'pt' | 'ru' | 'zh' | 'ja' | 'ko' | 'ar' | 'hi' | 'nl' | 'pl') => {
|
||||
setSettings(prev => ({ ...prev, preferredLanguage: value }))
|
||||
@@ -188,54 +174,7 @@ export function AISettingsPanel({ initialSettings }: AISettingsPanelProps) {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* AI Provider Selection */}
|
||||
<Card className="p-4">
|
||||
<Label className="text-base font-medium mb-1">{t('aiSettings.provider')}</Label>
|
||||
<p className="text-sm text-gray-500 mb-4">
|
||||
{t('aiSettings.providerDesc')}
|
||||
</p>
|
||||
|
||||
<RadioGroup
|
||||
value={settings.aiProvider}
|
||||
onValueChange={handleProviderChange}
|
||||
>
|
||||
<div className="flex items-start space-x-2 py-2">
|
||||
<RadioGroupItem value="auto" id="auto" />
|
||||
<div className="grid gap-1.5">
|
||||
<Label htmlFor="auto" className="font-medium">
|
||||
{t('aiSettings.providerAuto')}
|
||||
</Label>
|
||||
<p className="text-sm text-gray-500">
|
||||
{t('aiSettings.providerAutoDesc')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start space-x-2 py-2">
|
||||
<RadioGroupItem value="ollama" id="ollama" />
|
||||
<div className="grid gap-1.5">
|
||||
<Label htmlFor="ollama" className="font-medium">
|
||||
{t('aiSettings.providerOllama')}
|
||||
</Label>
|
||||
<p className="text-sm text-gray-500">
|
||||
{t('aiSettings.providerOllamaDesc')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start space-x-2 py-2">
|
||||
<RadioGroupItem value="openai" id="openai" />
|
||||
<div className="grid gap-1.5">
|
||||
<Label htmlFor="openai" className="font-medium">
|
||||
{t('aiSettings.providerOpenAI')}
|
||||
</Label>
|
||||
<p className="text-sm text-gray-500">
|
||||
{t('aiSettings.providerOpenAIDesc')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user