feat: standardize UI theme, fix dark mode consistency, and implement editorial tags
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 1m24s

This commit is contained in:
Antigravity
2026-05-10 18:43:13 +00:00
parent f6880bd0e1
commit 330c0c61b6
25 changed files with 640 additions and 503 deletions

View File

@@ -1,7 +1,6 @@
'use client'
import { useState, useEffect } from 'react'
import { Button } from './ui/button'
import {
Dialog,
DialogContent,
@@ -10,11 +9,11 @@ import {
DialogHeader,
DialogTitle,
} from './ui/dialog'
import { Checkbox } from './ui/checkbox'
import { Tag, Loader2, Sparkles, CheckCircle2 } from 'lucide-react'
import { Sparkles, CheckCircle2, Loader2, Tag } from 'lucide-react'
import { toast } from 'sonner'
import { useLanguage } from '@/lib/i18n'
import type { AutoLabelSuggestion, SuggestedLabel } from '@/lib/ai/services'
import { cn } from '@/lib/utils'
interface AutoLabelSuggestionDialogProps {
open: boolean
@@ -35,12 +34,10 @@ export function AutoLabelSuggestionDialog({
const [creating, setCreating] = useState(false)
const [selectedLabels, setSelectedLabels] = useState<Set<string>>(new Set())
// Fetch suggestions when dialog opens with a notebook
useEffect(() => {
if (open && notebookId) {
fetchSuggestions()
} else {
// Reset state when closing
setSuggestions(null)
setSelectedLabels(new Set())
}
@@ -65,12 +62,13 @@ export function AutoLabelSuggestionDialog({
if (data.success && data.data) {
setSuggestions(data.data)
// Select all labels by default
const allLabelNames = new Set<string>(data.data.suggestedLabels.map((l: SuggestedLabel) => l.name as string))
setSelectedLabels(allLabelNames)
} else {
// No suggestions is not an error - just close the dialog
if (data.message) {
toast.info(data.message)
} else {
toast.info(t('ai.autoLabels.noSuggestions') || 'Pas assez de notes pour générer des labels (minimum 15)')
}
onOpenChange(false)
}
@@ -136,8 +134,10 @@ export function AutoLabelSuggestionDialog({
<DialogTitle className="sr-only">{t('ai.autoLabels.analyzing')}</DialogTitle>
</DialogHeader>
<div className="flex flex-col items-center justify-center py-12">
<Loader2 className="h-8 w-8 animate-spin text-primary" />
<p className="mt-4 text-sm text-muted-foreground">
<div className="w-16 h-16 rounded-full border border-dashed border-memento-blue/20 flex items-center justify-center mb-4">
<Loader2 className="h-6 w-6 animate-spin text-memento-blue" />
</div>
<p className="text-[10px] text-muted-foreground uppercase tracking-widest font-bold">
{t('ai.autoLabels.analyzing')}
</p>
</div>
@@ -155,7 +155,7 @@ export function AutoLabelSuggestionDialog({
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<Sparkles className="h-5 w-5 text-amber-500" />
<Sparkles className="h-5 w-5 text-memento-blue" />
{t('ai.autoLabels.title')}
</DialogTitle>
<DialogDescription>
@@ -166,60 +166,73 @@ export function AutoLabelSuggestionDialog({
</DialogDescription>
</DialogHeader>
<div className="space-y-3 py-4">
{suggestions.suggestedLabels.map((label) => (
<div
key={label.name}
className="flex items-start gap-3 p-3 rounded-lg border hover:bg-muted/50 cursor-pointer"
onClick={() => toggleLabelSelection(label.name)}
>
<Checkbox
checked={selectedLabels.has(label.name)}
onCheckedChange={() => toggleLabelSelection(label.name)}
aria-label={`Select label: ${label.name}`}
/>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2">
<Tag className="h-4 w-4 text-muted-foreground" />
<span className="font-medium">{label.name}</span>
<div className="space-y-2 py-4">
{suggestions.suggestedLabels.map((label) => {
const isSelected = selectedLabels.has(label.name)
return (
<div
key={label.name}
className={cn(
"flex items-start gap-3 p-3 rounded-xl border cursor-pointer transition-all",
isSelected
? "bg-memento-blue/5 border-memento-blue/30 hover:bg-memento-blue/10"
: "border-border hover:bg-muted/50"
)}
onClick={() => toggleLabelSelection(label.name)}
>
<div className={cn(
"w-5 h-5 rounded-full border-2 flex items-center justify-center mt-0.5 transition-all shrink-0",
isSelected
? "bg-memento-blue border-memento-blue"
: "border-border"
)}>
{isSelected && <CheckCircle2 className="h-3.5 w-3.5 text-white" />}
</div>
<div className="flex items-center gap-3 mt-1">
<span className="text-xs text-muted-foreground">
{t('ai.autoLabels.notesCount', { count: label.count })}
</span>
<span className="text-xs px-2 py-0.5 rounded-full bg-primary/10 text-primary">
{Math.round(label.confidence * 100)}% {t('notebook.confidence')}
</span>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2">
<Tag className="h-3.5 w-3.5 text-memento-blue/60" />
<span className="font-medium text-sm">{label.name}</span>
<Sparkles className="h-3 w-3 text-memento-blue/40" />
</div>
<div className="flex items-center gap-3 mt-1.5">
<span className="text-[10px] text-muted-foreground">
{t('ai.autoLabels.notesCount', { count: label.count })}
</span>
<span className="text-[10px] px-2 py-0.5 rounded-full bg-memento-blue/10 text-memento-blue font-bold">
{Math.round(label.confidence * 100)}%
</span>
</div>
</div>
</div>
</div>
))}
)
})}
</div>
<DialogFooter>
<Button
variant="outline"
<DialogFooter className="gap-2">
<button
onClick={() => onOpenChange(false)}
disabled={creating}
className="flex-1 py-3 border border-border rounded-xl text-[10px] font-bold uppercase tracking-widest hover:bg-muted transition-all"
>
{t('general.cancel')}
</Button>
<Button
</button>
<button
onClick={handleCreateLabels}
disabled={selectedLabels.size === 0 || creating}
className="flex-1 py-3 bg-memento-blue text-white rounded-xl text-[10px] font-bold uppercase tracking-widest hover:opacity-90 transition-all shadow-lg shadow-memento-blue/20 disabled:opacity-50"
>
{creating ? (
<>
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
<span className="flex items-center justify-center gap-2">
<Loader2 className="h-4 w-4 animate-spin" />
{t('ai.autoLabels.creating')}
</>
</span>
) : (
<>
<CheckCircle2 className="h-4 w-4 mr-2" />
<span className="flex items-center justify-center gap-2">
<CheckCircle2 className="h-4 w-4" />
{t('ai.autoLabels.create')}
</>
</span>
)}
</Button>
</button>
</DialogFooter>
</DialogContent>
</Dialog>