From 6c0996ccfdd7e3cd58726ddd38f690a88eec0968 Mon Sep 17 00:00:00 2001 From: admin-valentin Date: Sun, 2 Aug 2026 17:45:01 +0000 Subject: [PATCH] feat(CC-088): add Meeting Prep page (brief builder, urgent tasks context, action items, export) --- src/app/dashboard/meeting-prep/page.tsx | 133 ++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 src/app/dashboard/meeting-prep/page.tsx diff --git a/src/app/dashboard/meeting-prep/page.tsx b/src/app/dashboard/meeting-prep/page.tsx new file mode 100644 index 0000000..590432b --- /dev/null +++ b/src/app/dashboard/meeting-prep/page.tsx @@ -0,0 +1,133 @@ +'use client'; + +import { useMemo, useState } from 'react'; +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; +import { apiFetch } from '../../../lib/api'; +import { useSession } from '../../../components/session-provider'; + +interface Task { id: string; title: string; status: string; tags: string[]; dueDate: string | null; } +interface Decision { id: string; title: string; impact: string | null; status: string; createdAt: string; } +interface Contact { id: string; firstName: string; lastName: string | null; role: string | null; organization: string | null; } + +export default function MeetingPrepPage() { + const { activeTenant } = useSession(); + const tenantId = activeTenant?.tenantId ?? ''; + const qc = useQueryClient(); + const [meetingTitle, setMeetingTitle] = useState(''); + const [participants, setParticipants] = useState(''); + const [objective, setObjective] = useState(''); + const [agenda, setAgenda] = useState(''); + const [generated, setGenerated] = useState(false); + + const { data: tasks = [] } = useQuery({ queryKey: ['mp-tasks', tenantId], queryFn: () => apiFetch('/v1/tasks?limit=500', { tenantId }), enabled: Boolean(tenantId), staleTime: 60_000 }); + const { data: decisions = [] } = useQuery({ queryKey: ['mp-decisions', tenantId], queryFn: () => apiFetch('/v1/decisions?limit=50', { tenantId }), enabled: Boolean(tenantId), staleTime: 60_000 }); + const { data: contacts = [] } = useQuery({ queryKey: ['mp-contacts', tenantId], queryFn: () => apiFetch('/v1/contacts?limit=500', { tenantId }), enabled: Boolean(tenantId), staleTime: 120_000 }); + + const urgentTasks = useMemo(() => + tasks.filter((t) => (t.tags.includes('meeting') || t.priority === 'urgent') && t.status !== 'completed').slice(0, 5), + [tasks]); + + const recentDecisions = useMemo(() => + decisions.filter((d) => d.status !== 'cancelled').slice(0, 5), + [decisions]); + + const createMeetingTaskMut = useMutation({ + mutationFn: (title: string) => apiFetch('/v1/tasks', { tenantId, method: 'POST', body: { + title, tags: ['meeting', 'action-item'], status: 'todo', priority: 'normal', + }}), + onSuccess: () => qc.invalidateQueries({ queryKey: ['mp-tasks', tenantId] }), + }); + + function exportBrief() { + const lines: string[] = []; + lines.push(`MEETING BRIEF: ${meetingTitle || 'Întâlnire'}`); + lines.push(`Data: ${new Date().toLocaleDateString('ro-RO', { dateStyle: 'full' })}`); + lines.push(''); + if (participants) { lines.push(`PARTICIPANȚI: ${participants}`); lines.push(''); } + if (objective) { lines.push(`OBIECTIV: ${objective}`); lines.push(''); } + if (agenda) { lines.push(`AGENDĂ:\n${agenda}`); lines.push(''); } + if (urgentTasks.length > 0) { + lines.push('TASKURI URGENTE DE DISCUTAT:'); + urgentTasks.forEach((t) => lines.push(`• ${t.title}`)); + lines.push(''); + } + if (recentDecisions.length > 0) { + lines.push('DECIZII RECENTE RELEVANTE:'); + recentDecisions.slice(0, 3).forEach((d) => lines.push(`• ${d.title}`)); + lines.push(''); + } + lines.push('---'); + lines.push('Generat de CEO OS'); + const blob = new Blob([lines.join('\n')], { type: 'text/plain;charset=utf-8' }); + const a = document.createElement('a'); a.href = URL.createObjectURL(blob); + a.download = `meeting-brief-${new Date().toISOString().slice(0,10)}.txt`; a.click(); + setGenerated(true); + } + + const [newActionItem, setNewActionItem] = useState(''); + + return ( +
+
+

Pregătire Întâlnire

+

Brieful tău pentru orice meeting — contextualizat din datele CEO OS.

+
+ + {/* Meeting info */} +
+

Detalii întâlnire

+ setMeetingTitle(e.target.value)} + placeholder="Titlu meeting (ex: QBR Q3 cu investitorii)" + className="w-full rounded-lg border bg-background px-3 py-2 text-sm text-ink focus:outline-none focus:ring-2 focus:ring-ring" /> + setParticipants(e.target.value)} + placeholder="Participanți (ex: Maria, Alexandru, echipa produs)" + className="w-full rounded-lg border bg-background px-3 py-2 text-sm text-ink focus:outline-none focus:ring-2 focus:ring-ring" /> + setObjective(e.target.value)} + placeholder="Obiectivul principal al meetingului" + className="w-full rounded-lg border bg-background px-3 py-2 text-sm text-ink focus:outline-none focus:ring-2 focus:ring-ring" /> +