Files
Momento/memento-note/components/structured-views/notes-gallery-view.tsx
Antigravity 0784c94242
Some checks failed
CI / Lint, Test & Build (push) Failing after 57s
CI / Deploy production (on server) (push) Has been skipped
feat(notes): vues structurées tableau/kanban, flashcards et MCP robuste
Ajoute la base organisable par carnet (schéma, champs partagés, valeurs par note)
avec activation guidée, tableau éditable, kanban et suppression de colonnes.
Corrige le multiselect en vue tableau et enrichit sidebar, grille et i18n FR/EN.
Inclut aussi les améliorations flashcards SM-2, l'audit consentement IA et la
robustesse du serveur MCP (config, validation, rate-limit, métriques).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 23:03:16 +00:00

130 lines
4.1 KiB
TypeScript

'use client'
import { useState } from 'react'
import type { Note } from '@/lib/types'
import type { NotebookSchemaPayload, NotePropertyValues } from '@/lib/structured-views/types'
import { formatPropertyDisplay } from '@/lib/structured-views/property-utils'
import { getNoteDisplayTitle, getNoteFeedImage } from '@/lib/note-preview'
import { useLanguage } from '@/lib/i18n'
type NotesGalleryViewProps = {
notes: Note[]
schema: NotebookSchemaPayload
noteValues: Record<string, NotePropertyValues>
notebookColor?: string | null
onOpen: (note: Note) => void
}
export function NotesGalleryView({
notes,
schema,
noteValues,
notebookColor,
onOpen,
}: NotesGalleryViewProps) {
const { t } = useLanguage()
const untitled = t('notes.untitled')
const previewProps = schema.properties.slice(0, 2)
return (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 max-w-7xl mx-auto">
{notes.map((note) => (
<GalleryCard
key={note.id}
note={note}
title={getNoteDisplayTitle(note, untitled)}
image={getNoteFeedImage(note)}
notebookColor={notebookColor}
previewProps={previewProps}
allProps={schema.properties}
values={noteValues[note.id] ?? {}}
onOpen={() => onOpen(note)}
/>
))}
</div>
)
}
function GalleryCard({
note,
title,
image,
notebookColor,
previewProps,
allProps,
values,
onOpen,
}: {
note: Note
title: string
image: string | null
notebookColor?: string | null
previewProps: NotebookSchemaPayload['properties']
allProps: NotebookSchemaPayload['properties']
values: NotePropertyValues
onOpen: () => void
}) {
const [hover, setHover] = useState(false)
const accent = notebookColor || '#A47148'
return (
<button
type="button"
onClick={onOpen}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
className="text-left rounded-2xl border border-border/40 bg-card/40 overflow-hidden shadow-sm hover:shadow-md hover:border-border transition-all group"
>
<div
className="aspect-[4/3] relative overflow-hidden"
style={{ backgroundColor: `${accent}18` }}
>
{image ? (
<img src={image} alt="" className="w-full h-full object-cover grayscale group-hover:grayscale-0 transition-all duration-500" />
) : note.illustrationSvg ? (
<div
className="w-full h-full p-4 [&_svg]:w-full [&_svg]:h-full opacity-80"
dangerouslySetInnerHTML={{ __html: note.illustrationSvg }}
/>
) : (
<div className="absolute inset-0 flex items-center justify-center">
<span
className="font-memento-serif text-4xl opacity-20"
style={{ color: accent }}
>
{title.charAt(0).toUpperCase()}
</span>
</div>
)}
</div>
<div className="p-4 space-y-2">
<h3 className="font-memento-serif text-[15px] font-medium line-clamp-2 group-hover:text-brand-accent transition-colors">
{title}
</h3>
{!hover && previewProps.length > 0 && (
<div className="space-y-1">
{previewProps.map((p) => (
<div key={p.id} className="flex gap-2 text-[11px]">
<span className="text-muted-foreground shrink-0">{p.name}:</span>
<span className="truncate">{formatPropertyDisplay(p.type, values[p.id])}</span>
</div>
))}
</div>
)}
{hover && allProps.length > 0 ? (
<div className="space-y-1.5 pt-1 border-t border-border/30">
{allProps.map((p) => (
<div key={p.id} className="flex justify-between gap-2 text-[11px]">
<span className="text-muted-foreground">{p.name}</span>
<span className="font-medium text-right truncate max-w-[55%]">
{formatPropertyDisplay(p.type, values[p.id])}
</span>
</div>
))}
</div>
) : null}
</div>
</button>
)
}