feat: hierarchical notebooks (tree), remove all list view code, delete 22 unused files
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m3s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m3s
- Add parentId to Notebook model (tree structure) - Update sidebar to render parent/child notebooks with expand/collapse - Add sub-notebook creation from parent notebook - Remove 'list' from NotesViewMode type everywhere - Delete 22 unused components, hooks, and UI files - Wrap revalidatePath in try-catch to prevent save 500 - Update notebook API to support parentId in creation
This commit is contained in:
@@ -8,20 +8,29 @@ import { useNotebooks } from '@/context/notebooks-context'
|
||||
interface CreateNotebookDialogProps {
|
||||
open?: boolean
|
||||
onOpenChange?: (open: boolean) => void
|
||||
parentNotebookId?: string | null
|
||||
}
|
||||
|
||||
export function CreateNotebookDialog({ open, onOpenChange }: CreateNotebookDialogProps) {
|
||||
export function CreateNotebookDialog({ open, onOpenChange, parentNotebookId }: CreateNotebookDialogProps) {
|
||||
const { t } = useLanguage()
|
||||
const { createNotebookOptimistic } = useNotebooks()
|
||||
const { createNotebookOptimistic, notebooks } = useNotebooks()
|
||||
const [name, setName] = useState('')
|
||||
const [selectedParentId, setSelectedParentId] = useState<string | null>(parentNotebookId ?? null)
|
||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||
|
||||
const rootNotebooks = notebooks.filter(nb => !nb.parentId)
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
if (!name.trim()) return
|
||||
setIsSubmitting(true)
|
||||
try {
|
||||
await createNotebookOptimistic({ name: name.trim(), icon: 'folder', color: '#64748B' })
|
||||
await createNotebookOptimistic({
|
||||
name: name.trim(),
|
||||
icon: 'folder',
|
||||
color: '#64748B',
|
||||
parentId: selectedParentId,
|
||||
})
|
||||
setName('')
|
||||
onOpenChange?.(false)
|
||||
} catch (err) {
|
||||
@@ -33,6 +42,7 @@ export function CreateNotebookDialog({ open, onOpenChange }: CreateNotebookDialo
|
||||
|
||||
const handleClose = () => {
|
||||
setName('')
|
||||
setSelectedParentId(parentNotebookId ?? null)
|
||||
onOpenChange?.(false)
|
||||
}
|
||||
|
||||
@@ -40,7 +50,6 @@ export function CreateNotebookDialog({ open, onOpenChange }: CreateNotebookDialo
|
||||
<AnimatePresence>
|
||||
{open && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
|
||||
{/* Backdrop */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
@@ -49,7 +58,6 @@ export function CreateNotebookDialog({ open, onOpenChange }: CreateNotebookDialo
|
||||
className="absolute inset-0 bg-black/50 backdrop-blur-sm"
|
||||
/>
|
||||
|
||||
{/* Card */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.92, y: 20 }}
|
||||
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||
@@ -79,6 +87,24 @@ export function CreateNotebookDialog({ open, onOpenChange }: CreateNotebookDialo
|
||||
/>
|
||||
</div>
|
||||
|
||||
{!parentNotebookId && rootNotebooks.length > 0 && (
|
||||
<div>
|
||||
<label className="block text-[11px] uppercase tracking-widest font-bold text-muted-foreground mb-2">
|
||||
{t('notebook.parentNotebook') || 'Carnet parent'}
|
||||
</label>
|
||||
<select
|
||||
value={selectedParentId || ''}
|
||||
onChange={(e) => setSelectedParentId(e.target.value || null)}
|
||||
className="w-full bg-white dark:bg-zinc-800 border border-black/12 dark:border-white/15 rounded-lg px-4 py-3 outline-none focus:border-foreground/40 transition-colors text-sm text-foreground"
|
||||
>
|
||||
<option value="">{t('notebook.noParent') || 'Aucun (racine)'}</option>
|
||||
{rootNotebooks.map(nb => (
|
||||
<option key={nb.id} value={nb.id}>{nb.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex gap-3 pt-2">
|
||||
<button
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user