## Translation Files - Add 11 new language files (es, de, pt, ru, zh, ja, ko, ar, hi, nl, pl) - Add 100+ missing translation keys across all 15 languages - New sections: notebook, pagination, ai.batchOrganization, ai.autoLabels - Update nav section with workspace, quickAccess, myLibrary keys ## Component Updates - Update 15+ components to use translation keys instead of hardcoded text - Components: notebook dialogs, sidebar, header, note-input, ghost-tags, etc. - Replace 80+ hardcoded English/French strings with t() calls - Ensure consistent UI across all supported languages ## Code Quality - Remove 77+ console.log statements from codebase - Clean up API routes, components, hooks, and services - Keep only essential error handling (no debugging logs) ## UI/UX Improvements - Update Keep logo to yellow post-it style (from-yellow-400 to-amber-500) - Change selection colors to #FEF3C6 (notebooks) and #EFB162 (nav items) - Make "+" button permanently visible in notebooks section - Fix grammar and syntax errors in multiple components ## Bug Fixes - Fix JSON syntax errors in it.json, nl.json, pl.json, zh.json - Fix syntax errors in notebook-suggestion-toast.tsx - Fix syntax errors in use-auto-tagging.ts - Fix syntax errors in paragraph-refactor.service.ts - Fix duplicate "fusion" section in nl.json 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> Ou une version plus courte si vous préférez : feat(i18n): Add 15 languages, remove logs, update UI components - Create 11 new translation files (es, de, pt, ru, zh, ja, ko, ar, hi, nl, pl) - Add 100+ translation keys: notebook, pagination, AI features - Update 15+ components to use translations (80+ strings) - Remove 77+ console.log statements from codebase - Fix JSON syntax errors in 4 translation files - Fix component syntax errors (toast, hooks, services) - Update logo to yellow post-it style - Change selection colors (#FEF3C6, #EFB162) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
226 lines
8.1 KiB
TypeScript
226 lines
8.1 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
import { useRouter } from 'next/navigation'
|
|
import { Plus, X, Folder, Briefcase, FileText, Zap, BarChart3, Globe, Sparkles, Book, Heart, Crown, Music, Building2 } from 'lucide-react'
|
|
import { Button } from '@/components/ui/button'
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
} from '@/components/ui/dialog'
|
|
import { Input } from '@/components/ui/input'
|
|
import { cn } from '@/lib/utils'
|
|
import { useLanguage } from '@/lib/i18n'
|
|
|
|
const NOTEBOOK_ICONS = [
|
|
{ icon: Folder, name: 'folder' },
|
|
{ icon: Briefcase, name: 'briefcase' },
|
|
{ icon: FileText, name: 'document' },
|
|
{ icon: Zap, name: 'lightning' },
|
|
{ icon: BarChart3, name: 'chart' },
|
|
{ icon: Globe, name: 'globe' },
|
|
{ icon: Sparkles, name: 'sparkle' },
|
|
{ icon: Book, name: 'book' },
|
|
{ icon: Heart, name: 'heart' },
|
|
{ icon: Crown, name: 'crown' },
|
|
{ icon: Music, name: 'music' },
|
|
{ icon: Building2, name: 'building' },
|
|
]
|
|
|
|
const NOTEBOOK_COLORS = [
|
|
{ name: 'Blue', value: '#3B82F6', bg: 'bg-blue-500' },
|
|
{ name: 'Purple', value: '#8B5CF6', bg: 'bg-purple-500' },
|
|
{ name: 'Red', value: '#EF4444', bg: 'bg-red-500' },
|
|
{ name: 'Orange', value: '#F59E0B', bg: 'bg-orange-500' },
|
|
{ name: 'Green', value: '#10B981', bg: 'bg-green-500' },
|
|
{ name: 'Teal', value: '#14B8A6', bg: 'bg-teal-500' },
|
|
{ name: 'Gray', value: '#6B7280', bg: 'bg-gray-500' },
|
|
]
|
|
|
|
interface CreateNotebookDialogProps {
|
|
open?: boolean
|
|
onOpenChange?: (open: boolean) => void
|
|
}
|
|
|
|
export function CreateNotebookDialog({ open, onOpenChange }: CreateNotebookDialogProps) {
|
|
const router = useRouter()
|
|
const { t } = useLanguage()
|
|
const [name, setName] = useState('')
|
|
const [selectedIcon, setSelectedIcon] = useState('folder')
|
|
const [selectedColor, setSelectedColor] = useState('#3B82F6')
|
|
const [isSubmitting, setIsSubmitting] = useState(false)
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault()
|
|
|
|
if (!name.trim()) return
|
|
|
|
setIsSubmitting(true)
|
|
|
|
try {
|
|
const response = await fetch('/api/notebooks', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
name: name.trim(),
|
|
icon: selectedIcon,
|
|
color: selectedColor,
|
|
}),
|
|
})
|
|
|
|
if (response.ok) {
|
|
// Close dialog and reload
|
|
onOpenChange?.(false)
|
|
window.location.reload()
|
|
} else {
|
|
const error = await response.json()
|
|
console.error('Failed to create notebook:', error)
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to create notebook:', error)
|
|
} finally {
|
|
setIsSubmitting(false)
|
|
}
|
|
}
|
|
|
|
const handleReset = () => {
|
|
setName('')
|
|
setSelectedIcon('folder')
|
|
setSelectedColor('#3B82F6')
|
|
}
|
|
|
|
const SelectedIconComponent = NOTEBOOK_ICONS.find(i => i.name === selectedIcon)?.icon || Folder
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={(val) => {
|
|
onOpenChange?.(val)
|
|
if (!val) handleReset()
|
|
}}>
|
|
<DialogContent className="sm:max-w-[500px] p-0">
|
|
<button
|
|
onClick={() => onOpenChange?.(false)}
|
|
className="absolute right-4 top-4 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 transition-colors z-10"
|
|
>
|
|
<X className="h-5 w-5" />
|
|
</button>
|
|
<DialogHeader className="px-8 pt-8 pb-4">
|
|
<DialogTitle className="text-2xl font-bold text-gray-900 dark:text-white mb-2">
|
|
{t('notebook.createNew')}
|
|
</DialogTitle>
|
|
<DialogDescription className="text-sm text-gray-500 dark:text-gray-400">
|
|
{t('notebook.createDescription')}
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<form onSubmit={handleSubmit} className="px-8 pb-8">
|
|
<div className="space-y-6">
|
|
{/* Notebook Name */}
|
|
<div>
|
|
<label className="text-[11px] font-bold text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-2 block">
|
|
{t('notebook.name')}
|
|
</label>
|
|
<Input
|
|
value={name}
|
|
onChange={(e) => setName(e.target.value)}
|
|
placeholder="e.g. Q4 Marketing Strategy"
|
|
className="w-full"
|
|
autoFocus
|
|
/>
|
|
</div>
|
|
|
|
{/* Icon Selection */}
|
|
<div>
|
|
<label className="text-[11px] font-bold text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-3 block">
|
|
{t('notebook.selectIcon')}
|
|
</label>
|
|
<div className="grid grid-cols-6 gap-3">
|
|
{NOTEBOOK_ICONS.map((item) => {
|
|
const IconComponent = item.icon
|
|
const isSelected = selectedIcon === item.name
|
|
return (
|
|
<button
|
|
key={item.name}
|
|
type="button"
|
|
onClick={() => setSelectedIcon(item.name)}
|
|
className={cn(
|
|
"h-14 w-full rounded-xl border-2 flex items-center justify-center transition-all duration-200",
|
|
isSelected
|
|
? 'border-indigo-600 bg-indigo-50 dark:bg-indigo-900/20 text-indigo-600'
|
|
: 'border-gray-200 dark:border-gray-700 text-gray-400 hover:border-gray-300 dark:hover:border-gray-600'
|
|
)}
|
|
>
|
|
<IconComponent className="h-5 w-5" />
|
|
</button>
|
|
)
|
|
})}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Color Selection */}
|
|
<div>
|
|
<label className="text-[11px] font-bold text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-3 block">
|
|
{t('notebook.selectColor')}
|
|
</label>
|
|
<div className="flex items-center gap-3">
|
|
{NOTEBOOK_COLORS.map((color) => {
|
|
const isSelected = selectedColor === color.value
|
|
return (
|
|
<button
|
|
key={color.value}
|
|
type="button"
|
|
onClick={() => setSelectedColor(color.value)}
|
|
className={cn(
|
|
"h-10 w-10 rounded-full border-2 transition-all duration-200",
|
|
isSelected
|
|
? 'border-white scale-110 shadow-lg'
|
|
: 'border-gray-200 dark:border-gray-700 hover:scale-105'
|
|
)}
|
|
style={{ backgroundColor: color.value }}
|
|
title={color.name}
|
|
/>
|
|
)
|
|
})}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Preview */}
|
|
{name.trim() && (
|
|
<div className="flex items-center gap-3 p-4 rounded-xl bg-gray-50 dark:bg-gray-900/50 border border-gray-200 dark:border-gray-700">
|
|
<div
|
|
className="w-10 h-10 rounded-xl flex items-center justify-center text-white shadow-md"
|
|
style={{ backgroundColor: selectedColor }}
|
|
>
|
|
<SelectedIconComponent className="h-5 w-5" />
|
|
</div>
|
|
<span className="font-semibold text-gray-900 dark:text-white">{name.trim()}</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Action Buttons */}
|
|
<div className="flex items-center justify-between mt-8 pt-6 border-t border-gray-200 dark:border-gray-700">
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
onClick={() => onOpenChange?.(false)}
|
|
className="text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200"
|
|
>
|
|
{t('notebook.cancel')}
|
|
</Button>
|
|
<Button
|
|
type="submit"
|
|
disabled={!name.trim() || isSubmitting}
|
|
className="bg-indigo-600 hover:bg-indigo-700 text-white px-6"
|
|
>
|
|
{isSubmitting ? t('notebook.creating') : t('notebook.create')}
|
|
</Button>
|
|
</div>
|
|
</form>
|
|
</DialogContent>
|
|
</Dialog>
|
|
)
|
|
}
|