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:
Sepehr Ramezani
2026-04-15 23:48:28 +02:00
parent 39671c6472
commit b6a548acd8
68 changed files with 5014 additions and 485 deletions

View File

@@ -68,9 +68,16 @@ interface NoteInputProps {
onNoteCreated?: (note: Note) => void
defaultExpanded?: boolean
forceExpanded?: boolean
/** Mode onglets : occupe toute la largeur du contenu principal (plus de carte étroite centrée) */
fullWidth?: boolean
}
export function NoteInput({ onNoteCreated, defaultExpanded = false, forceExpanded = false }: NoteInputProps) {
export function NoteInput({
onNoteCreated,
defaultExpanded = false,
forceExpanded = false,
fullWidth = false,
}: NoteInputProps) {
const { labels: globalLabels, addLabel } = useLabels()
const { data: session } = useSession()
const { t } = useLanguage()
@@ -109,7 +116,8 @@ export function NoteInput({ onNoteCreated, defaultExpanded = false, forceExpande
// Auto-tagging hook
const { suggestions, isAnalyzing } = useAutoTagging({
content: type === 'text' ? fullContentForAI : '',
enabled: type === 'text' && isExpanded
enabled: type === 'text' && isExpanded,
notebookId: currentNotebookId
})
// Title suggestions
@@ -559,11 +567,13 @@ export function NoteInput({ onNoteCreated, defaultExpanded = false, forceExpande
setDismissedTitleSuggestions(false)
}
const widthClass = fullWidth ? 'w-full max-w-none mx-0' : 'max-w-2xl mx-auto'
if (!isExpanded) {
return (
<Card className="p-4 max-w-2xl mx-auto mb-8 cursor-text shadow-md hover:shadow-lg transition-shadow">
<Card className={cn('p-4 mb-8 cursor-text shadow-md hover:shadow-lg transition-shadow', widthClass)}>
<div className="flex items-center gap-4">
<Input
<Input dir="auto"
placeholder={t('notes.placeholder')}
onClick={() => setIsExpanded(true)}
readOnly
@@ -590,12 +600,9 @@ export function NoteInput({ onNoteCreated, defaultExpanded = false, forceExpande
return (
<>
<Card className={cn(
"p-4 max-w-2xl mx-auto mb-8 shadow-lg border",
colorClasses.card
)}>
<Card className={cn('p-4 mb-8 shadow-lg border', widthClass, colorClasses.card)}>
<div className="space-y-3">
<Input
<Input dir="auto"
placeholder={t('notes.titlePlaceholder')}
value={title}
onChange={(e) => setTitle(e.target.value)}
@@ -707,7 +714,7 @@ export function NoteInput({ onNoteCreated, defaultExpanded = false, forceExpande
className="min-h-[100px] p-3 rounded-md border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900/50"
/>
) : (
<Textarea
<Textarea dir="auto"
placeholder={isMarkdown ? t('notes.markdownPlaceholder') : t('notes.placeholder')}
value={content}
onChange={(e) => setContent(e.target.value)}
@@ -743,7 +750,7 @@ export function NoteInput({ onNoteCreated, defaultExpanded = false, forceExpande
{checkItems.map((item) => (
<div key={item.id} className="flex items-start gap-2 group">
<Checkbox className="mt-2" />
<Input
<Input dir="auto"
value={item.text}
onChange={(e) => handleUpdateCheckItem(item.id, e.target.value)}
placeholder={t('notes.listItem')}
@@ -1015,7 +1022,7 @@ export function NoteInput({ onNoteCreated, defaultExpanded = false, forceExpande
<label htmlFor="reminder-date" className="text-sm font-medium">
{t('notes.date')}
</label>
<Input
<Input dir="auto"
id="reminder-date"
type="date"
value={reminderDate}
@@ -1027,7 +1034,7 @@ export function NoteInput({ onNoteCreated, defaultExpanded = false, forceExpande
<label htmlFor="reminder-time" className="text-sm font-medium">
{t('notes.time')}
</label>
<Input
<Input dir="auto"
id="reminder-time"
type="time"
value={reminderTime}
@@ -1077,7 +1084,7 @@ export function NoteInput({ onNoteCreated, defaultExpanded = false, forceExpande
<DialogTitle>{t('notes.addLink')}</DialogTitle>
</DialogHeader>
<div className="space-y-4 py-4">
<Input
<Input dir="auto"
placeholder="https://example.com"
value={linkUrl}
onChange={(e) => setLinkUrl(e.target.value)}