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:
@@ -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)}
|
||||
|
||||
Reference in New Issue
Block a user