import { ChevronDown, MoreHorizontal, Pin, Plus, Trash2, } from "lucide-react"; import { useCallback, useEffect, useMemo, useState } from "react"; import { createStickyNote, deleteStickyNote, listStickyNotes, } from "../api"; import bluePin from "../data/blue.png"; import greenPin from "../data/green.png"; import pinkPin from "../data/pink.png"; import yellowPin from "../data/yellow.png"; import { senderDisplayName } from "../utils/senderAvatar"; import { formatAbsoluteDateTime } from "../utils/datetime"; const NOTE_COLORS = ["yellow", "green", "blue", "pink"]; const PIN_IMAGES = { yellow: yellowPin, green: greenPin, blue: bluePin, pink: pinkPin, }; function noteMatchesContext(note, contextEmail) { const needle = senderDisplayName(contextEmail).toLowerCase(); if (!needle) return false; return ( note.title.toLowerCase().includes(needle) || note.body.toLowerCase().includes(needle) || note.tags.some((tag) => tag.toLowerCase().includes(needle)) ); } function NoteCard({ note, onDelete, menuOpen, onMenuToggle }) { return (

{note.title}

{menuOpen && (
)}
{note.body &&

{note.body}

}
); } export default function StickyNotesPanel({ compact = false, contextEmail = null }) { const token = sessionStorage.getItem("agmail_access_token"); const [notes, setNotes] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(""); const [expanded, setExpanded] = useState(false); const [adding, setAdding] = useState(false); const [menuOpenId, setMenuOpenId] = useState(null); const [draft, setDraft] = useState({ title: "", body: "", tags: "", color: "yellow", }); const [saving, setSaving] = useState(false); const contextualNotes = useMemo(() => { if (!contextEmail) return []; return notes.filter((note) => noteMatchesContext(note, contextEmail)); }, [contextEmail, notes]); const visibleNotes = useMemo(() => { if (contextEmail && contextualNotes.length) return contextualNotes; return notes; }, [contextEmail, contextualNotes, notes]); const load = useCallback(async () => { setLoading(true); setError(""); try { const items = await listStickyNotes(token); setNotes(items); } catch (loadError) { setError(loadError.message); } finally { setLoading(false); } }, [token]); useEffect(() => { load(); }, [load]); useEffect(() => { function closeMenu() { setMenuOpenId(null); } window.addEventListener("click", closeMenu); return () => window.removeEventListener("click", closeMenu); }, []); async function handleCreate(event) { event.preventDefault(); if (!draft.title.trim()) return; setSaving(true); setError(""); try { const created = await createStickyNote( { title: draft.title.trim(), body: draft.body.trim(), tags: draft.tags .split(",") .map((tag) => tag.trim()) .filter(Boolean), color: draft.color, }, token, ); setNotes((current) => [created, ...current]); setDraft({ title: "", body: "", tags: "", color: NOTE_COLORS[(notes.length + 1) % NOTE_COLORS.length], }); setAdding(false); setExpanded(true); } catch (createError) { setError(createError.message); } finally { setSaving(false); } } async function handleDelete(noteId) { setMenuOpenId(null); setError(""); try { await deleteStickyNote(noteId, token); setNotes((current) => current.filter((note) => note.id !== noteId)); } catch (deleteError) { setError(deleteError.message); } } function handleBodyKeyDown(event) { if (event.key !== "Enter" || event.shiftKey) return; event.preventDefault(); const textarea = event.currentTarget; const { selectionStart, selectionEnd, value } = textarea; const insertion = "\n• "; const nextValue = value.slice(0, selectionStart) + insertion + value.slice(selectionEnd); setDraft((current) => ({ ...current, body: nextValue })); window.requestAnimationFrame(() => { textarea.selectionStart = selectionStart + insertion.length; textarea.selectionEnd = selectionStart + insertion.length; }); } const contextLabel = contextEmail ? senderDisplayName(contextEmail) : null; return (