fix: unify theme system - fix theme switching persistence
- Unified localStorage key to 'theme-preference' across all components
- Fixed header.tsx using wrong localStorage key ('theme' instead of 'theme-preference')
- Added localStorage hybrid persistence for instant theme changes
- Removed router.refresh() which was causing stale data revert
- Replaced Blue theme with Sepia
- Consolidated auth() calls to prevent race conditions
- Updated UserSettingsData types to include all themes
This commit is contained in:
@@ -20,11 +20,16 @@ import { useLabels } from '@/context/LabelContext'
|
||||
import { useNoteRefresh } from '@/context/NoteRefreshContext'
|
||||
import { useReminderCheck } from '@/hooks/use-reminder-check'
|
||||
import { useAutoLabelSuggestion } from '@/hooks/use-auto-label-suggestion'
|
||||
import { useNotebooks } from '@/context/notebooks-context'
|
||||
import { Folder, Briefcase, FileText, Zap, BarChart3, Globe, Sparkles, Book, Heart, Crown, Music, Building2, Plane, ChevronRight, Plus } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { LabelFilter } from '@/components/label-filter'
|
||||
|
||||
export default function HomePage() {
|
||||
console.log('[HomePage] Component rendering')
|
||||
const searchParams = useSearchParams()
|
||||
const router = useRouter()
|
||||
// Force re-render when search params change (for filtering)
|
||||
const [notes, setNotes] = useState<Note[]>([])
|
||||
const [pinnedNotes, setPinnedNotes] = useState<Note[]>([])
|
||||
const [recentNotes, setRecentNotes] = useState<Note[]>([])
|
||||
@@ -227,21 +232,154 @@ export default function HomePage() {
|
||||
loadNotes()
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [searchParams, refreshKey, showRecentNotes]) // Intentionally omit 'labels' and 'semantic' to prevent reload when adding tags or from router.push
|
||||
return (
|
||||
<main className="container mx-auto px-4 py-8 max-w-7xl">
|
||||
<NoteInput onNoteCreated={handleNoteCreated} />
|
||||
// Get notebooks context to display header
|
||||
const { notebooks } = useNotebooks()
|
||||
const currentNotebook = notebooks.find((n: any) => n.id === searchParams.get('notebook'))
|
||||
const [showNoteInput, setShowNoteInput] = useState(false)
|
||||
|
||||
{/* Batch Organization Button - Only show in Inbox with 5+ notes */}
|
||||
{isInbox && !isLoading && notes.length >= 5 && (
|
||||
<div className="mb-4 flex justify-end">
|
||||
<Button
|
||||
onClick={() => setBatchOrganizationOpen(true)}
|
||||
variant="default"
|
||||
className="gap-2"
|
||||
>
|
||||
<Wand2 className="h-4 w-4" />
|
||||
Organiser avec l'IA ({notes.length})
|
||||
</Button>
|
||||
// Get icon component for header
|
||||
const getNotebookIcon = (iconName: string) => {
|
||||
const ICON_MAP: Record<string, any> = {
|
||||
'folder': Folder,
|
||||
'briefcase': Briefcase,
|
||||
'document': FileText,
|
||||
'lightning': Zap,
|
||||
'chart': BarChart3,
|
||||
'globe': Globe,
|
||||
'sparkle': Sparkles,
|
||||
'book': Book,
|
||||
'heart': Heart,
|
||||
'crown': Crown,
|
||||
'music': Music,
|
||||
'building': Building2,
|
||||
'flight_takeoff': Plane,
|
||||
}
|
||||
return ICON_MAP[iconName] || Folder
|
||||
}
|
||||
|
||||
// Handle Note Created to close the input if desired, or keep open
|
||||
const handleNoteCreatedWrapper = (note: any) => {
|
||||
handleNoteCreated(note)
|
||||
setShowNoteInput(false)
|
||||
}
|
||||
|
||||
// Helper for Breadcrumbs
|
||||
const Breadcrumbs = ({ notebookName }: { notebookName: string }) => (
|
||||
<div className="flex items-center gap-2 text-sm text-gray-500 mb-1">
|
||||
<span>Notebooks</span>
|
||||
<ChevronRight className="w-4 h-4" />
|
||||
<span className="font-medium text-blue-600">{notebookName}</span>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<main className="w-full px-8 py-6 flex flex-col h-full">
|
||||
{/* Notebook Specific Header */}
|
||||
{currentNotebook ? (
|
||||
<div className="flex flex-col gap-6 mb-8 animate-in fade-in slide-in-from-top-2 duration-300">
|
||||
{/* Breadcrumbs */}
|
||||
<Breadcrumbs notebookName={currentNotebook.name} />
|
||||
|
||||
<div className="flex items-start justify-between">
|
||||
{/* Title Section */}
|
||||
<div className="flex items-center gap-5">
|
||||
<div className="p-3 bg-blue-50 dark:bg-blue-900/20 rounded-xl">
|
||||
{(() => {
|
||||
const Icon = getNotebookIcon(currentNotebook.icon || 'folder')
|
||||
return (
|
||||
<Icon
|
||||
className={cn("w-8 h-8", !currentNotebook.color && "text-blue-600 dark:text-blue-400")}
|
||||
style={currentNotebook.color ? { color: currentNotebook.color } : undefined}
|
||||
/>
|
||||
)
|
||||
})()}
|
||||
</div>
|
||||
<h1 className="text-4xl font-bold text-gray-900 dark:text-white tracking-tight">{currentNotebook.name}</h1>
|
||||
</div>
|
||||
|
||||
{/* Actions Section */}
|
||||
<div className="flex items-center gap-3">
|
||||
<LabelFilter
|
||||
selectedLabels={searchParams.get('labels')?.split(',').filter(Boolean) || []}
|
||||
onFilterChange={(newLabels) => {
|
||||
const params = new URLSearchParams(searchParams.toString())
|
||||
if (newLabels.length > 0) params.set('labels', newLabels.join(','))
|
||||
else params.delete('labels')
|
||||
router.push(`/?${params.toString()}`)
|
||||
}}
|
||||
className="border-gray-200"
|
||||
/>
|
||||
<Button
|
||||
onClick={() => setShowNoteInput(!showNoteInput)}
|
||||
className="h-10 px-6 rounded-full bg-blue-600 hover:bg-blue-700 text-white font-medium shadow-sm gap-2 transition-all"
|
||||
>
|
||||
<Plus className="w-5 h-5" />
|
||||
Add Note
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
/* Default Header for Home/Inbox */
|
||||
<div className="flex flex-col gap-6 mb-8 animate-in fade-in slide-in-from-top-2 duration-300">
|
||||
{/* Breadcrumbs Placeholder or just spacing */}
|
||||
<div className="h-5 mb-1"></div>
|
||||
|
||||
<div className="flex items-start justify-between">
|
||||
{/* Title Section */}
|
||||
<div className="flex items-center gap-5">
|
||||
<div className="p-3 bg-white border border-gray-100 dark:bg-gray-800 dark:border-gray-700 rounded-xl shadow-sm">
|
||||
<FileText className="w-8 h-8 text-blue-600" />
|
||||
</div>
|
||||
<h1 className="text-4xl font-bold text-gray-900 dark:text-white tracking-tight">Notes</h1>
|
||||
</div>
|
||||
|
||||
{/* Actions Section */}
|
||||
<div className="flex items-center gap-3">
|
||||
<LabelFilter
|
||||
selectedLabels={searchParams.get('labels')?.split(',').filter(Boolean) || []}
|
||||
onFilterChange={(newLabels) => {
|
||||
const params = new URLSearchParams(searchParams.toString())
|
||||
if (newLabels.length > 0) params.set('labels', newLabels.join(','))
|
||||
else params.delete('labels')
|
||||
router.push(`/?${params.toString()}`)
|
||||
}}
|
||||
className="border-gray-200"
|
||||
/>
|
||||
|
||||
{/* AI Organization Button - Moved to Header */}
|
||||
{isInbox && !isLoading && notes.length >= 5 && (
|
||||
<Button
|
||||
onClick={() => setBatchOrganizationOpen(true)}
|
||||
variant="outline"
|
||||
className="h-10 px-4 rounded-full border-gray-200 text-gray-700 hover:bg-gray-50 gap-2 shadow-sm"
|
||||
title="Organiser avec l'IA"
|
||||
>
|
||||
<Wand2 className="h-4 w-4 text-purple-600" />
|
||||
<span className="hidden sm:inline">Organiser</span>
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button
|
||||
onClick={() => setShowNoteInput(!showNoteInput)}
|
||||
className="h-10 px-6 rounded-full bg-blue-600 hover:bg-blue-700 text-white font-medium shadow-sm gap-2 transition-all"
|
||||
>
|
||||
<Plus className="w-5 h-5" />
|
||||
Add Note
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Note Input - Conditionally Visible or Always Visible on Home */}
|
||||
{/* Note Input - Conditionally Rendered */}
|
||||
{showNoteInput && (
|
||||
<div className="mb-8 animate-in fade-in slide-in-from-top-4 duration-300">
|
||||
<NoteInput
|
||||
onNoteCreated={handleNoteCreatedWrapper}
|
||||
forceExpanded={true}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user