components/notes/note-section.tsx
Annotation non disponible
Lance npm run annotate (nécessite ANTHROPIC_API_KEY dans .env.local) pour générer une annotation française par Claude Haiku 4.5.
1 export
NoteSection
Code source· tsx
"use client";
import { useEffect, useState } from "react";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Icon } from "@/components/icons";
import {
addNote,
deleteNote,
getNotesForContext,
updateNote,
type Note,
type NoteContextType,
} from "@/lib/notes";
interface Props {
contextType: NoteContextType;
contextId: string;
contextLabel: string;
}
export function NoteSection({ contextType, contextId, contextLabel }: Props) {
const [notes, setNotes] = useState<Note[]>([]);
const [hydrated, setHydrated] = useState(false);
const [draft, setDraft] = useState("");
const [editingId, setEditingId] = useState<string | null>(null);
const [editingContent, setEditingContent] = useState("");
useEffect(() => {
setNotes(getNotesForContext(contextType, contextId));
setHydrated(true);
}, [contextType, contextId]);
const handleAdd = () => {
if (!draft.trim()) return;
addNote({ contextType, contextId, contextLabel, content: draft });
setNotes(getNotesForContext(contextType, contextId));
setDraft("");
};
const handleEdit = (id: string) => {
const note = notes.find((n) => n.id === id);
if (!note) return;
setEditingId(id);
setEditingContent(note.content);
};
const handleSaveEdit = () => {
if (!editingId) return;
updateNote(editingId, editingContent);
setNotes(getNotesForContext(contextType, contextId));
setEditingId(null);
setEditingContent("");
};
const handleDelete = (id: string) => {
if (!confirm("Supprimer cette note ?")) return;
deleteNote(id);
setNotes(getNotesForContext(contextType, contextId));
};
if (!hydrated) return null;
return (
<Card className="bg-violet-50/40 dark:bg-violet-950/20 border-violet-200 dark:border-violet-900">
<CardContent className="pt-4 space-y-3">
<div className="flex items-center justify-between">
<p className="inline-flex items-center gap-1.5 text-xs font-medium text-violet-700 dark:text-violet-400 uppercase tracking-wider">
<Icon name="carnet" size={12} weight="duotone" /> Mon carnet — {notes.length} note
{notes.length !== 1 ? "s" : ""} ici
</p>
</div>
{notes.length > 0 && (
<div className="space-y-2">
{notes.map((note) => (
<div
key={note.id}
className="bg-white dark:bg-zinc-950 rounded-md border border-zinc-200 dark:border-zinc-800 p-3 space-y-2"
>
{editingId === note.id ? (
<>
<textarea
value={editingContent}
onChange={(e) => setEditingContent(e.target.value)}
rows={3}
className="w-full text-sm bg-transparent border border-zinc-200 dark:border-zinc-800 rounded px-2 py-1 focus:outline-none focus:ring-1 focus:ring-violet-400"
autoFocus
/>
<div className="flex gap-2 justify-end">
<Button size="sm" variant="ghost" onClick={() => setEditingId(null)}>
Annuler
</Button>
<Button size="sm" onClick={handleSaveEdit}>
Enregistrer
</Button>
</div>
</>
) : (
<>
<p className="text-sm text-zinc-700 dark:text-zinc-300 whitespace-pre-wrap leading-relaxed">
{note.content}
</p>
<div className="flex items-center justify-between text-xs">
<span className="text-zinc-400">
{new Date(note.updatedAt).toLocaleString("fr-FR", {
day: "2-digit",
month: "short",
hour: "2-digit",
minute: "2-digit",
})}
</span>
<div className="flex gap-3">
<button
onClick={() => handleEdit(note.id)}
className="inline-flex items-center gap-1 text-zinc-400 hover:text-zinc-700 dark:hover:text-zinc-200 touch-manipulation"
>
<Icon name="edit" size={12} /> Modifier
</button>
<button
onClick={() => handleDelete(note.id)}
className="inline-flex items-center gap-1 text-zinc-400 hover:text-red-600 touch-manipulation"
>
<Icon name="trash" size={12} /> Supprimer
</button>
</div>
</div>
</>
)}
</div>
))}
</div>
)}
<div className="space-y-2">
<textarea
value={draft}
onChange={(e) => setDraft(e.target.value)}
placeholder="Note ce que tu viens d'apprendre. Ce sera lié à cette page et retrouvable dans /carnet."
rows={2}
className="w-full text-sm bg-white dark:bg-zinc-950 border border-zinc-200 dark:border-zinc-800 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-violet-400 placeholder:text-zinc-400"
/>
<div className="flex justify-end">
<Button size="sm" onClick={handleAdd} disabled={!draft.trim()}>
Ajouter au carnet
</Button>
</div>
</div>
</CardContent>
</Card>
);
}
"use client";
import { useEffect, useState } from "react";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Icon } from "@/components/icons";
import {
addNote,
deleteNote,
getNotesForContext,
updateNote,
type Note,
type NoteContextType,
} from "@/lib/notes";
interface Props {
contextType: NoteContextType;
contextId: string;
contextLabel: string;
}
export function NoteSection({ contextType, contextId, contextLabel }: Props) {
const [notes, setNotes] = useState<Note[]>([]);
const [hydrated, setHydrated] = useState(false);
const [draft, setDraft] = useState("");
const [editingId, setEditingId] = useState<string | null>(null);
const [editingContent, setEditingContent] = useState("");
useEffect(() => {
setNotes(getNotesForContext(contextType, contextId));
setHydrated(true);
}, [contextType, contextId]);
const handleAdd = () => {
if (!draft.trim()) return;
addNote({ contextType, contextId, contextLabel, content: draft });
setNotes(getNotesForContext(contextType, contextId));
setDraft("");
};
const handleEdit = (id: string) => {
const note = notes.find((n) => n.id === id);
if (!note) return;
setEditingId(id);
setEditingContent(note.content);
};
const handleSaveEdit = () => {
if (!editingId) return;
updateNote(editingId, editingContent);
setNotes(getNotesForContext(contextType, contextId));
setEditingId(null);
setEditingContent("");
};
const handleDelete = (id: string) => {
if (!confirm("Supprimer cette note ?")) return;
deleteNote(id);
setNotes(getNotesForContext(contextType, contextId));
};
if (!hydrated) return null;
return (
<Card className="bg-violet-50/40 dark:bg-violet-950/20 border-violet-200 dark:border-violet-900">
<CardContent className="pt-4 space-y-3">
<div className="flex items-center justify-between">
<p className="inline-flex items-center gap-1.5 text-xs font-medium text-violet-700 dark:text-violet-400 uppercase tracking-wider">
<Icon name="carnet" size={12} weight="duotone" /> Mon carnet — {notes.length} note
{notes.length !== 1 ? "s" : ""} ici
</p>
</div>
{notes.length > 0 && (
<div className="space-y-2">
{notes.map((note) => (
<div
key={note.id}
className="bg-white dark:bg-zinc-950 rounded-md border border-zinc-200 dark:border-zinc-800 p-3 space-y-2"
>
{editingId === note.id ? (
<>
<textarea
value={editingContent}
onChange={(e) => setEditingContent(e.target.value)}
rows={3}
className="w-full text-sm bg-transparent border border-zinc-200 dark:border-zinc-800 rounded px-2 py-1 focus:outline-none focus:ring-1 focus:ring-violet-400"
autoFocus
/>
<div className="flex gap-2 justify-end">
<Button size="sm" variant="ghost" onClick={() => setEditingId(null)}>
Annuler
</Button>
<Button size="sm" onClick={handleSaveEdit}>
Enregistrer
</Button>
</div>
</>
) : (
<>
<p className="text-sm text-zinc-700 dark:text-zinc-300 whitespace-pre-wrap leading-relaxed">
{note.content}
</p>
<div className="flex items-center justify-between text-xs">
<span className="text-zinc-400">
{new Date(note.updatedAt).toLocaleString("fr-FR", {
day: "2-digit",
month: "short",
hour: "2-digit",
minute: "2-digit",
})}
</span>
<div className="flex gap-3">
<button
onClick={() => handleEdit(note.id)}
className="inline-flex items-center gap-1 text-zinc-400 hover:text-zinc-700 dark:hover:text-zinc-200 touch-manipulation"
>
<Icon name="edit" size={12} /> Modifier
</button>
<button
onClick={() => handleDelete(note.id)}
className="inline-flex items-center gap-1 text-zinc-400 hover:text-red-600 touch-manipulation"
>
<Icon name="trash" size={12} /> Supprimer
</button>
</div>
</div>
</>
)}
</div>
))}
</div>
)}
<div className="space-y-2">
<textarea
value={draft}
onChange={(e) => setDraft(e.target.value)}
placeholder="Note ce que tu viens d'apprendre. Ce sera lié à cette page et retrouvable dans /carnet."
rows={2}
className="w-full text-sm bg-white dark:bg-zinc-950 border border-zinc-200 dark:border-zinc-800 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-violet-400 placeholder:text-zinc-400"
/>
<div className="flex justify-end">
<Button size="sm" onClick={handleAdd} disabled={!draft.trim()}>
Ajouter au carnet
</Button>
</div>
</div>
</CardContent>
</Card>
);
}