epic-ux-design #1
@@ -1,7 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useState } from 'react'
|
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 { Plus, X, Folder, Briefcase, FileText, Zap, BarChart3, Globe, Sparkles, Book, Heart, Crown, Music, Building2 } from 'lucide-react'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import {
|
import {
|
||||||
@@ -47,7 +46,6 @@ interface CreateNotebookDialogProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function CreateNotebookDialog({ open, onOpenChange }: CreateNotebookDialogProps) {
|
export function CreateNotebookDialog({ open, onOpenChange }: CreateNotebookDialogProps) {
|
||||||
const router = useRouter()
|
|
||||||
const { t } = useLanguage()
|
const { t } = useLanguage()
|
||||||
const { createNotebookOptimistic } = useNotebooks()
|
const { createNotebookOptimistic } = useNotebooks()
|
||||||
const [name, setName] = useState('')
|
const [name, setName] = useState('')
|
||||||
@@ -68,9 +66,8 @@ export function CreateNotebookDialog({ open, onOpenChange }: CreateNotebookDialo
|
|||||||
icon: selectedIcon,
|
icon: selectedIcon,
|
||||||
color: selectedColor,
|
color: selectedColor,
|
||||||
})
|
})
|
||||||
// Close dialog — no full page reload needed
|
// Close dialog — context already updated sidebar state
|
||||||
onOpenChange?.(false)
|
onOpenChange?.(false)
|
||||||
router.refresh()
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to create notebook:', error)
|
console.error('Failed to create notebook:', error)
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ export function DeleteNotebookDialog({ notebook, open, onOpenChange }: DeleteNot
|
|||||||
try {
|
try {
|
||||||
await deleteNotebook(notebook.id)
|
await deleteNotebook(notebook.id)
|
||||||
onOpenChange(false)
|
onOpenChange(false)
|
||||||
window.location.reload()
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Error already handled in UI
|
// Error already handled in UI
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useState } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { useRouter } from 'next/navigation'
|
|
||||||
import { useLanguage } from '@/lib/i18n'
|
import { useLanguage } from '@/lib/i18n'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import {
|
import {
|
||||||
@@ -14,41 +13,36 @@ import {
|
|||||||
} from '@/components/ui/dialog'
|
} from '@/components/ui/dialog'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { Label } from '@/components/ui/label'
|
import { Label } from '@/components/ui/label'
|
||||||
|
import { useNotebooks } from '@/context/notebooks-context'
|
||||||
|
import { Notebook } from '@/lib/types'
|
||||||
|
|
||||||
interface EditNotebookDialogProps {
|
interface EditNotebookDialogProps {
|
||||||
notebook: any
|
notebook: Notebook
|
||||||
open: boolean
|
open: boolean
|
||||||
onOpenChange: (open: boolean) => void
|
onOpenChange: (open: boolean) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export function EditNotebookDialog({ notebook, open, onOpenChange }: EditNotebookDialogProps) {
|
export function EditNotebookDialog({ notebook, open, onOpenChange }: EditNotebookDialogProps) {
|
||||||
const router = useRouter()
|
const { updateNotebook } = useNotebooks()
|
||||||
const { t } = useLanguage()
|
const { t } = useLanguage()
|
||||||
const [name, setName] = useState(notebook?.name || '')
|
const [name, setName] = useState(notebook?.name || '')
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open) {
|
||||||
|
setName(notebook?.name || '')
|
||||||
|
}
|
||||||
|
}, [open, notebook?.name])
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
if (!name.trim()) return
|
if (!name.trim()) return
|
||||||
|
|
||||||
setIsSubmitting(true)
|
setIsSubmitting(true)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/notebooks/${notebook.id}`, {
|
await updateNotebook(notebook.id, { name: name.trim() })
|
||||||
method: 'PATCH',
|
onOpenChange(false)
|
||||||
headers: { 'Content-Type': 'application/json' },
|
} catch {
|
||||||
body: JSON.stringify({ name: name.trim() }),
|
// Error handled in UI
|
||||||
})
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
onOpenChange(false)
|
|
||||||
window.location.reload()
|
|
||||||
} else {
|
|
||||||
const error = await response.json()
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// Error already handled in UI
|
|
||||||
} finally {
|
} finally {
|
||||||
setIsSubmitting(false)
|
setIsSubmitting(false)
|
||||||
}
|
}
|
||||||
@@ -63,7 +57,6 @@ export function EditNotebookDialog({ notebook, open, onOpenChange }: EditNoteboo
|
|||||||
{t('notebook.editDescription')}
|
{t('notebook.editDescription')}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className="grid gap-4 py-4">
|
<div className="grid gap-4 py-4">
|
||||||
<div className="grid grid-cols-4 items-center gap-4">
|
<div className="grid grid-cols-4 items-center gap-4">
|
||||||
@@ -80,7 +73,6 @@ export function EditNotebookDialog({ notebook, open, onOpenChange }: EditNoteboo
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -9,47 +9,46 @@ import {
|
|||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from '@/components/ui/dropdown-menu'
|
} from '@/components/ui/dropdown-menu'
|
||||||
|
import { Notebook } from '@/lib/types'
|
||||||
|
|
||||||
interface NotebookActionsProps {
|
interface NotebookActionsProps {
|
||||||
notebook: any
|
notebook: Notebook
|
||||||
onEdit: () => void
|
onEdit: () => void
|
||||||
onDelete: () => void
|
onDelete: () => void
|
||||||
onSummary?: () => void // NEW: Summary action callback (IA6)
|
onSummary?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export function NotebookActions({ notebook, onEdit, onDelete, onSummary }: NotebookActionsProps) {
|
export function NotebookActions({ notebook, onEdit, onDelete, onSummary }: NotebookActionsProps) {
|
||||||
const { t } = useLanguage()
|
const { t } = useLanguage()
|
||||||
return (
|
return (
|
||||||
<div className="opacity-0 group-hover:opacity-100 transition-opacity flex items-center">
|
<DropdownMenu>
|
||||||
<DropdownMenu>
|
<DropdownMenuTrigger asChild>
|
||||||
<DropdownMenuTrigger asChild>
|
<Button
|
||||||
<Button
|
variant="ghost"
|
||||||
variant="ghost"
|
size="sm"
|
||||||
size="sm"
|
className="h-8 w-8 p-0"
|
||||||
className="h-8 w-8 p-0"
|
>
|
||||||
>
|
<MoreVertical className="h-3 w-3" />
|
||||||
<MoreVertical className="h-3 w-3" />
|
</Button>
|
||||||
</Button>
|
</DropdownMenuTrigger>
|
||||||
</DropdownMenuTrigger>
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuContent align="end">
|
{onSummary && (
|
||||||
{onSummary && (
|
<DropdownMenuItem onClick={onSummary}>
|
||||||
<DropdownMenuItem onClick={onSummary}>
|
<FileText className="h-4 w-4 mr-2" />
|
||||||
<FileText className="h-4 w-4 mr-2" />
|
{t('notebook.summary')}
|
||||||
{t('notebook.summary')}
|
|
||||||
</DropdownMenuItem>
|
|
||||||
)}
|
|
||||||
<DropdownMenuItem onClick={onEdit}>
|
|
||||||
<Edit2 className="h-4 w-4 mr-2" />
|
|
||||||
{t('notebook.edit')}
|
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
)}
|
||||||
onClick={onDelete}
|
<DropdownMenuItem onClick={onEdit}>
|
||||||
className="text-red-600"
|
<Edit2 className="h-4 w-4 mr-2" />
|
||||||
>
|
{t('notebook.edit')}
|
||||||
{t('notebook.delete')}
|
</DropdownMenuItem>
|
||||||
</DropdownMenuItem>
|
<DropdownMenuItem
|
||||||
</DropdownMenuContent>
|
onClick={onDelete}
|
||||||
</DropdownMenu>
|
className="text-red-600"
|
||||||
</div>
|
>
|
||||||
|
{t('notebook.delete')}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { EditNotebookDialog } from './edit-notebook-dialog'
|
|||||||
import { NotebookSummaryDialog } from './notebook-summary-dialog'
|
import { NotebookSummaryDialog } from './notebook-summary-dialog'
|
||||||
import { useLanguage } from '@/lib/i18n'
|
import { useLanguage } from '@/lib/i18n'
|
||||||
import { useLabels } from '@/context/LabelContext'
|
import { useLabels } from '@/context/LabelContext'
|
||||||
|
import { Notebook } from '@/lib/types'
|
||||||
|
|
||||||
// Map icon names to lucide-react components
|
// Map icon names to lucide-react components
|
||||||
const ICON_MAP: Record<string, LucideIcon> = {
|
const ICON_MAP: Record<string, LucideIcon> = {
|
||||||
@@ -35,8 +36,7 @@ const ICON_MAP: Record<string, LucideIcon> = {
|
|||||||
|
|
||||||
// Function to get icon component by name
|
// Function to get icon component by name
|
||||||
const getNotebookIcon = (iconName: string) => {
|
const getNotebookIcon = (iconName: string) => {
|
||||||
const IconComponent = ICON_MAP[iconName] || Folder
|
return ICON_MAP[iconName] || Folder
|
||||||
return IconComponent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function NotebooksList() {
|
export function NotebooksList() {
|
||||||
@@ -49,9 +49,9 @@ export function NotebooksList() {
|
|||||||
const { labels } = useLabels()
|
const { labels } = useLabels()
|
||||||
|
|
||||||
const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false)
|
const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false)
|
||||||
const [editingNotebook, setEditingNotebook] = useState<any>(null)
|
const [editingNotebook, setEditingNotebook] = useState<Notebook | null>(null)
|
||||||
const [deletingNotebook, setDeletingNotebook] = useState<any>(null)
|
const [deletingNotebook, setDeletingNotebook] = useState<Notebook | null>(null)
|
||||||
const [summaryNotebook, setSummaryNotebook] = useState<any>(null)
|
const [summaryNotebook, setSummaryNotebook] = useState<Notebook | null>(null)
|
||||||
const [expandedNotebook, setExpandedNotebook] = useState<string | null>(null)
|
const [expandedNotebook, setExpandedNotebook] = useState<string | null>(null)
|
||||||
|
|
||||||
const currentNotebookId = searchParams.get('notebook')
|
const currentNotebookId = searchParams.get('notebook')
|
||||||
@@ -140,7 +140,7 @@ export function NotebooksList() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Notebooks Loop */}
|
{/* Notebooks Loop */}
|
||||||
{notebooks.map((notebook: any) => {
|
{notebooks.map((notebook: Notebook) => {
|
||||||
const isActive = currentNotebookId === notebook.id
|
const isActive = currentNotebookId === notebook.id
|
||||||
const isExpanded = expandedNotebook === notebook.id
|
const isExpanded = expandedNotebook === notebook.id
|
||||||
const isDragOver = dragOverNotebookId === notebook.id
|
const isDragOver = dragOverNotebookId === notebook.id
|
||||||
@@ -151,39 +151,48 @@ export function NotebooksList() {
|
|||||||
return (
|
return (
|
||||||
<div key={notebook.id} className="group flex flex-col">
|
<div key={notebook.id} className="group flex flex-col">
|
||||||
{isActive ? (
|
{isActive ? (
|
||||||
// Active notebook with expanded labels - STYLE MATCH Sidebar
|
// Active notebook with expanded labels
|
||||||
<div
|
<div
|
||||||
onDrop={(e) => handleDrop(e, notebook.id)}
|
onDrop={(e) => handleDrop(e, notebook.id)}
|
||||||
onDragOver={(e) => handleDragOver(e, notebook.id)}
|
onDragOver={(e) => handleDragOver(e, notebook.id)}
|
||||||
onDragLeave={handleDragLeave}
|
onDragLeave={handleDragLeave}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col mr-2 rounded-r-full overflow-hidden transition-all",
|
"flex flex-col mr-2 rounded-r-full overflow-hidden transition-all relative",
|
||||||
!notebook.color && "bg-primary/10 dark:bg-primary/20",
|
!notebook.color && "bg-primary/10 dark:bg-primary/20",
|
||||||
isDragOver && "ring-2 ring-primary ring-dashed"
|
isDragOver && "ring-2 ring-primary ring-dashed"
|
||||||
)}
|
)}
|
||||||
style={notebook.color ? { backgroundColor: `${notebook.color}20` } : undefined}
|
style={notebook.color ? { backgroundColor: `${notebook.color}20` } : undefined}
|
||||||
>
|
>
|
||||||
{/* Header - allow pointer events for expand button */}
|
{/* Header */}
|
||||||
<div className="pointer-events-auto flex items-center justify-between px-6 py-3">
|
<div className="pointer-events-auto flex items-center justify-between px-6 py-3">
|
||||||
<div className="flex items-center gap-4 min-w-0">
|
<div className="flex items-center gap-4 min-w-0 flex-1">
|
||||||
<NotebookIcon
|
<NotebookIcon
|
||||||
className={cn("w-5 h-5 flex-shrink-0 fill-current", !notebook.color && "text-primary dark:text-primary-foreground")}
|
className={cn("w-5 h-5 flex-shrink-0 fill-current", !notebook.color && "text-primary dark:text-primary-foreground")}
|
||||||
style={notebook.color ? { color: notebook.color } : undefined}
|
style={notebook.color ? { color: notebook.color } : undefined}
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
className={cn("text-sm font-medium tracking-wide truncate max-w-[120px]", !notebook.color && "text-primary dark:text-primary-foreground")}
|
className={cn("text-sm font-medium tracking-wide truncate min-w-0", !notebook.color && "text-primary dark:text-primary-foreground")}
|
||||||
style={notebook.color ? { color: notebook.color } : undefined}
|
style={notebook.color ? { color: notebook.color } : undefined}
|
||||||
>
|
>
|
||||||
{notebook.name}
|
{notebook.name}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<div className="flex items-center gap-1 flex-shrink-0">
|
||||||
onClick={() => handleToggleExpand(notebook.id)}
|
{/* Actions menu for active notebook */}
|
||||||
className={cn("transition-colors p-1 flex-shrink-0", !notebook.color && "text-primary hover:text-primary/80 dark:text-primary-foreground dark:hover:text-primary-foreground/80")}
|
<NotebookActions
|
||||||
style={notebook.color ? { color: notebook.color } : undefined}
|
notebook={notebook}
|
||||||
>
|
onEdit={() => setEditingNotebook(notebook)}
|
||||||
<ChevronDown className={cn("w-4 h-4 transition-transform", isExpanded && "rotate-180")} />
|
onDelete={() => setDeletingNotebook(notebook)}
|
||||||
</button>
|
onSummary={() => setSummaryNotebook(notebook)}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={() => handleToggleExpand(notebook.id)}
|
||||||
|
className={cn("transition-colors p-1", !notebook.color && "text-primary hover:text-primary/80 dark:text-primary-foreground dark:hover:text-primary-foreground/80")}
|
||||||
|
style={notebook.color ? { color: notebook.color } : undefined}
|
||||||
|
>
|
||||||
|
<ChevronDown className={cn("w-4 h-4 transition-transform", isExpanded && "rotate-180")} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Contextual Labels Tree */}
|
{/* Contextual Labels Tree */}
|
||||||
@@ -221,45 +230,41 @@ export function NotebooksList() {
|
|||||||
onDragOver={(e) => handleDragOver(e, notebook.id)}
|
onDragOver={(e) => handleDragOver(e, notebook.id)}
|
||||||
onDragLeave={handleDragLeave}
|
onDragLeave={handleDragLeave}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex items-center group relative",
|
"flex items-center relative",
|
||||||
isDragOver && "ring-2 ring-blue-500 ring-dashed rounded-r-full mr-2"
|
isDragOver && "ring-2 ring-blue-500 ring-dashed rounded-r-full mr-2"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="w-full flex">
|
<button
|
||||||
<button
|
onClick={() => handleSelectNotebook(notebook.id)}
|
||||||
onClick={() => handleSelectNotebook(notebook.id)}
|
className={cn(
|
||||||
className={cn(
|
"pointer-events-auto flex items-center gap-4 px-6 py-3 rounded-r-full mr-2 text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800/50 transition-colors w-full pr-24",
|
||||||
"pointer-events-auto flex items-center gap-4 px-6 py-3 rounded-r-full mr-2 text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800/50 transition-colors w-full pr-20",
|
isDragOver && "opacity-50"
|
||||||
isDragOver && "opacity-50"
|
)}
|
||||||
)}
|
>
|
||||||
>
|
<NotebookIcon className="w-5 h-5 flex-shrink-0" />
|
||||||
<NotebookIcon className="w-5 h-5 flex-shrink-0" />
|
<span className="text-sm font-medium tracking-wide truncate min-w-0 text-left">{notebook.name}</span>
|
||||||
<span className="text-sm font-medium tracking-wide truncate min-w-0 text-left">{notebook.name}</span>
|
{(notebook as any).notesCount > 0 && (
|
||||||
{notebook.notesCount > 0 && (
|
<span className="text-xs text-gray-400 ml-2 flex-shrink-0">({(notebook as any).notesCount})</span>
|
||||||
<span className="text-xs text-gray-400 ml-2 flex-shrink-0">({notebook.notesCount})</span>
|
)}
|
||||||
)}
|
</button>
|
||||||
</button>
|
|
||||||
|
|
||||||
{/* Expand button separate from main click */}
|
{/* Actions + expand on the right — always rendered, visible on hover */}
|
||||||
<button
|
<div className="absolute right-3 top-1/2 -translate-y-1/2 flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity z-10">
|
||||||
onClick={(e) => { e.stopPropagation(); handleToggleExpand(notebook.id); }}
|
|
||||||
className={cn(
|
|
||||||
"absolute right-4 top-1/2 -translate-y-1/2 p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 transition-colors rounded-full hover:bg-gray-200 dark:hover:bg-gray-700 opacity-0 group-hover:opacity-100",
|
|
||||||
expandedNotebook === notebook.id && "opacity-100 rotate-180"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<ChevronDown className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Actions (visible on hover) */}
|
|
||||||
<div className="absolute right-2 opacity-0 group-hover:opacity-100 transition-opacity z-10" style={{ right: '40px' }}>
|
|
||||||
<NotebookActions
|
<NotebookActions
|
||||||
notebook={notebook}
|
notebook={notebook}
|
||||||
onEdit={() => setEditingNotebook(notebook)}
|
onEdit={() => setEditingNotebook(notebook)}
|
||||||
onDelete={() => setDeletingNotebook(notebook)}
|
onDelete={() => setDeletingNotebook(notebook)}
|
||||||
onSummary={() => setSummaryNotebook(notebook)}
|
onSummary={() => setSummaryNotebook(notebook)}
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
onClick={(e) => { e.stopPropagation(); handleToggleExpand(notebook.id); }}
|
||||||
|
className={cn(
|
||||||
|
"p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 transition-colors rounded-full hover:bg-gray-200 dark:hover:bg-gray-700",
|
||||||
|
expandedNotebook === notebook.id && "rotate-180"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<ChevronDown className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -302,7 +307,7 @@ export function NotebooksList() {
|
|||||||
onOpenChange={(open) => {
|
onOpenChange={(open) => {
|
||||||
if (!open) setSummaryNotebook(null)
|
if (!open) setSummaryNotebook(null)
|
||||||
}}
|
}}
|
||||||
notebookId={summaryNotebook?.id}
|
notebookId={summaryNotebook?.id ?? null}
|
||||||
notebookName={summaryNotebook?.name}
|
notebookName={summaryNotebook?.name}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export function NoteRefreshProvider({ children }: { children: React.ReactNode })
|
|||||||
|
|
||||||
const triggerRefresh = useCallback(() => {
|
const triggerRefresh = useCallback(() => {
|
||||||
setRefreshKey(prev => prev + 1)
|
setRefreshKey(prev => prev + 1)
|
||||||
}, [refreshKey])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NoteRefreshContext.Provider value={{ refreshKey, triggerRefresh }}>
|
<NoteRefreshContext.Provider value={{ refreshKey, triggerRefresh }}>
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export interface NotebooksContextValue {
|
|||||||
error: string | null
|
error: string | null
|
||||||
|
|
||||||
// Actions: Notebooks
|
// Actions: Notebooks
|
||||||
createNotebookOptimistic: (data: CreateNotebookInput) => Promise<Notebook>
|
createNotebookOptimistic: (data: CreateNotebookInput) => Promise<void>
|
||||||
updateNotebook: (notebookId: string, data: UpdateNotebookInput) => Promise<void>
|
updateNotebook: (notebookId: string, data: UpdateNotebookInput) => Promise<void>
|
||||||
deleteNotebook: (notebookId: string) => Promise<void>
|
deleteNotebook: (notebookId: string) => Promise<void>
|
||||||
updateNotebookOrderOptimistic: (notebookIds: string[]) => Promise<void>
|
updateNotebookOrderOptimistic: (notebookIds: string[]) => Promise<void>
|
||||||
@@ -114,7 +114,6 @@ export function NotebooksProvider({ children, initialNotebooks = [] }: Notebooks
|
|||||||
|
|
||||||
// ===== ACTIONS: NOTEBOOKS =====
|
// ===== ACTIONS: NOTEBOOKS =====
|
||||||
const createNotebookOptimistic = useCallback(async (data: CreateNotebookInput) => {
|
const createNotebookOptimistic = useCallback(async (data: CreateNotebookInput) => {
|
||||||
// Server action sera implémenté plus tard
|
|
||||||
const response = await fetch('/api/notebooks', {
|
const response = await fetch('/api/notebooks', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
@@ -125,9 +124,10 @@ export function NotebooksProvider({ children, initialNotebooks = [] }: Notebooks
|
|||||||
throw new Error('Failed to create notebook')
|
throw new Error('Failed to create notebook')
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await response.json()
|
// Reload notebooks from server to update sidebar state
|
||||||
return result
|
await loadNotebooks()
|
||||||
}, [])
|
triggerRefresh()
|
||||||
|
}, [loadNotebooks, triggerRefresh])
|
||||||
|
|
||||||
const updateNotebook = useCallback(async (notebookId: string, data: UpdateNotebookInput) => {
|
const updateNotebook = useCallback(async (notebookId: string, data: UpdateNotebookInput) => {
|
||||||
const response = await fetch(`/api/notebooks/${notebookId}`, {
|
const response = await fetch(`/api/notebooks/${notebookId}`, {
|
||||||
|
|||||||
Reference in New Issue
Block a user