fix(ui): sélection groupée dans la boîte de réception
All checks were successful
CI / Lint, Unit Tests & Build (push) Successful in 7m17s
CI / Deploy production (on server) (push) Successful in 24s

Permet de cocher plusieurs notes, les déplacer vers un carnet ou les envoyer à la corbeille, et rend la carte mentale plus lisible.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Antigravity
2026-09-01 19:40:10 +00:00
parent 4fc5e3ce04
commit a7b16870a4
15 changed files with 509 additions and 37 deletions

View File

@@ -53,6 +53,7 @@ import { useHydrated } from '@/lib/use-hydrated'
import { formatDistanceToNow } from 'date-fns'
import { fr } from 'date-fns/locale/fr'
import { enUS } from 'date-fns/locale/en-US'
import { NoteSelectCheckbox } from '@/components/note-select-checkbox'
export type NotesLayoutMode = 'grid' | 'list' | 'table' | 'kanban' | 'gallery'
export type NotesClassicLayoutMode = 'grid' | 'list' | 'table'
@@ -253,6 +254,12 @@ type NotesListViewsProps = {
onOpenHistory?: (note: Note) => void
notebookName?: string
onGridReorder?: (orderedIds: string[]) => void | Promise<void>
selection?: {
selectedIds: ReadonlySet<string>
onToggle: (id: string) => void
onToggleAll?: () => void
allSelected?: boolean
}
} & Partial<NoteCollectionActions>
export function NotesListViews({
@@ -269,6 +276,7 @@ export function NotesListViews({
onNotePatch,
onNoteIllustrationGenerated,
onGridReorder,
selection,
}: NotesListViewsProps) {
const { t, language } = useLanguage()
const { data: session } = useSession()
@@ -352,6 +360,7 @@ export function NotesListViews({
onOpenHistory={onOpenHistory}
onGridReorder={onGridReorder}
pinnedLabel={t('notes.pinned')}
selection={selection}
/>
)
}
@@ -363,6 +372,15 @@ export function NotesListViews({
<table className="w-full text-left border-collapse min-w-[720px]">
<thead>
<tr className="border-b border-border/30">
{selection && (
<th className="w-10 px-3 py-3">
<NoteSelectCheckbox
checked={!!selection.allSelected}
onToggle={() => selection.onToggleAll?.()}
label={selection.allSelected ? t('notes.deselectAll') : t('notes.selectAll')}
/>
</th>
)}
<th
className="w-[32%] px-4 py-3 text-[10px] uppercase tracking-widest font-black text-muted-foreground cursor-pointer hover:text-foreground"
onClick={() => handleSort('title')}
@@ -407,6 +425,15 @@ export function NotesListViews({
role="button"
className="h-11 hover:bg-foreground/[0.02] cursor-pointer transition-colors group focus-visible:bg-foreground/[0.04]"
>
{selection && (
<td className="px-3 py-2 w-10">
<NoteSelectCheckbox
checked={selection.selectedIds.has(note.id)}
onToggle={() => selection.onToggle(note.id)}
label={t('notes.selectNote')}
/>
</td>
)}
<td className="px-4 py-2 font-memento-serif text-[13px] font-medium truncate max-w-[280px]">
<span className="inline-flex items-center gap-2 truncate group-hover:text-brand-accent transition-colors">
{note.isPinned && <Pin size={11} className="text-amber-500 fill-amber-500 shrink-0" />}
@@ -464,6 +491,7 @@ export function NotesListViews({
onMoveToNotebook={onMoveToNotebook}
onNotePatch={onNotePatch}
onNoteIllustrationGenerated={onNoteIllustrationGenerated}
selection={selection}
/>
</div>
)}
@@ -479,6 +507,7 @@ export function NotesListViews({
onMoveToNotebook={onMoveToNotebook}
onNotePatch={onNotePatch}
onNoteIllustrationGenerated={onNoteIllustrationGenerated}
selection={selection}
/>
)}
</div>
@@ -497,6 +526,13 @@ function formatGridCardDate(date: Date | string, language: string): string {
return `${month.toUpperCase()} ${day}, ${year}`
}
type NotesSelection = {
selectedIds: ReadonlySet<string>
onToggle: (id: string) => void
onToggleAll?: () => void
allSelected?: boolean
}
type GridCardSharedProps = {
note: Note
index: number
@@ -512,6 +548,7 @@ type GridCardSharedProps = {
onNoteIllustrationDeleted?: (noteId: string) => void | Promise<void>
onOpenHistory?: (note: Note) => void
isOverlay?: boolean
selection?: NotesSelection
}
function NotesMasonryGrid({
@@ -624,6 +661,7 @@ function NotesGridSection({
onDeleteNote,
onMoveToNotebook,
onNoteIllustrationGenerated,
selection,
className,
}: Omit<GridCardSharedProps, 'note' | 'index' | 'isOverlay'> & {
notes: Note[]
@@ -650,6 +688,7 @@ function NotesGridSection({
onDeleteNote={onDeleteNote}
onMoveToNotebook={onMoveToNotebook}
onNoteIllustrationGenerated={onNoteIllustrationGenerated}
selection={selection}
/>
) : (
<GridCard
@@ -665,6 +704,7 @@ function NotesGridSection({
onDeleteNote={onDeleteNote}
onMoveToNotebook={onMoveToNotebook}
onNoteIllustrationGenerated={onNoteIllustrationGenerated}
selection={selection}
/>
),
)}
@@ -721,6 +761,7 @@ const GridCard = memo(function GridCard({
onNoteIllustrationDeleted,
onOpenHistory,
isOverlay = false,
selection,
}: GridCardSharedProps) {
const router = useRouter()
const { t, language } = useLanguage()
@@ -760,8 +801,21 @@ const GridCard = memo(function GridCard({
onNoteIllustrationGenerated={onNoteIllustrationGenerated}
onNoteIllustrationDeleted={onNoteIllustrationDeleted}
/>
{selection && !isOverlay && (
<div className="absolute top-2 start-2 z-20">
<NoteSelectCheckbox
checked={selection.selectedIds.has(note.id)}
onToggle={() => selection.onToggle(note.id)}
label={t('notes.selectNote')}
className="shadow-sm"
/>
</div>
)}
{note.isPinned && (
<div className="absolute top-3 start-3 bg-background/90 backdrop-blur-sm p-1.5 rounded-full shadow-sm border border-border/40 text-amber-500">
<div className={cn(
'absolute top-3 bg-background/90 backdrop-blur-sm p-1.5 rounded-full shadow-sm border border-border/40 text-amber-500',
selection ? 'start-10' : 'start-3',
)}>
<Pin size={11} className="fill-amber-500" />
</div>
)}