import { useEffect, useState } from 'react'; import Layout from '../../components/Layout'; import LoadingButton from '../../components/LoadingButton'; import { supabase } from '../../lib/supabase'; import { useAuth } from '../../context/AuthContext'; function emptyForm() { return { title: '', attendees: '', meeting_at: new Date().toISOString().slice(0, 16), notes: '', }; } function formatMeetingDate(value) { if (!value) return 'Unknown date'; return new Date(value).toLocaleString('en-US', { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', }); } export default function MeetingNotes() { const { currentUser } = useAuth(); const [notes, setNotes] = useState([]); const [form, setForm] = useState(emptyForm()); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [deletingId, setDeletingId] = useState(''); const [status, setStatus] = useState(''); useEffect(() => { let isMounted = true; async function loadInitialNotes() { const { data, error } = await supabase .from('meeting_notes') .select('*') .order('meeting_at', { ascending: false }) .order('created_at', { ascending: false }); if (!isMounted) return; if (error) { setStatus(`Failed to load meeting notes: ${error.message}`); setNotes([]); } else { setNotes(data || []); setStatus(''); } setLoading(false); } loadInitialNotes(); return () => { isMounted = false; }; }, []); const handleSubmit = async (e) => { e.preventDefault(); if (!form.title.trim() || !form.notes.trim()) { setStatus('Meeting title and notes are required.'); return; } setSaving(true); const payload = { title: form.title.trim(), attendees: form.attendees.trim(), meeting_at: form.meeting_at ? new Date(form.meeting_at).toISOString() : new Date().toISOString(), notes: form.notes.trim(), created_by: currentUser?.id || null, updated_at: new Date().toISOString(), }; const { data, error } = await supabase .from('meeting_notes') .insert(payload) .select('*') .single(); setSaving(false); if (error) { setStatus(`Failed to save note: ${error.message}`); return; } setNotes(prev => [data, ...prev]); setForm(emptyForm()); setStatus('Meeting note added.'); }; const handleDelete = async (entry) => { if (!window.confirm(`Delete meeting note "${entry.title}"?`)) return; setDeletingId(entry.id); const { error } = await supabase.from('meeting_notes').delete().eq('id', entry.id); setDeletingId(''); if (error) { setStatus(`Failed to delete note: ${error.message}`); return; } setNotes(prev => prev.filter(note => note.id !== entry.id)); setStatus('Meeting note deleted.'); }; return (
Meeting Notes
Internal team timeline for meeting recaps, decisions, and follow-ups.
Add Note
setForm(prev => ({ ...prev, title: e.target.value }))} placeholder="Weekly team sync" />
setForm(prev => ({ ...prev, meeting_at: e.target.value }))} />
setForm(prev => ({ ...prev, attendees: e.target.value }))} placeholder="Team, client, subcontractor" />