components/notes/note-section.tsx

component·app·5.6 KB · 153 lignes· Voir l'itinéraire
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>
  );
}