'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 Goal { id: string; title: string; description: string | null; status: string; progress: number | null; tags: string[]; targetDate: string | null; createdAt: string; } interface Task { id: string; title: string; status: string; tags: string[]; createdAt: string; } const PROJECT_TAGS = ['proiect','project','initiative','program']; const STATUS_COLORS: Record = { active:'text-signal-ok', completed:'text-ink-faint', cancelled:'text-signal-danger', paused:'text-warn', archived:'text-ink-faint', }; export default function ProjectsPage() { const { activeTenant } = useSession(); const tenantId = activeTenant?.tenantId ?? ''; const qc = useQueryClient(); const [showAdd, setShowAdd] = useState(false); const [expanded, setExpanded] = useState(null); const [form, setForm] = useState({ title: '', description: '', targetDate: '' }); const { data: goals = [], isLoading: loadG } = useQuery({ queryKey: ['projects-goals', tenantId], queryFn: () => apiFetch('/v1/goals?limit=200', { tenantId }), enabled: Boolean(tenantId), staleTime: 60_000, }); const { data: tasks = [], isLoading: loadT } = useQuery({ queryKey: ['projects-tasks', tenantId], queryFn: () => apiFetch('/v1/tasks?limit=500', { tenantId }), enabled: Boolean(tenantId), staleTime: 60_000, }); const projects = useMemo(() => goals.filter((g) => g.tags.some((t) => PROJECT_TAGS.includes(t.toLowerCase()))), [goals]); function projectTasks(projectId: string): Task[] { return tasks.filter((t) => t.tags.includes(`project-${projectId}`) || t.tags.includes(`goal-${projectId}`)); } function computeProgress(p: Goal): number { if (p.progress !== null) return p.progress; const pts = projectTasks(p.id); if (pts.length === 0) return 0; return Math.round((pts.filter((t) => t.status === 'completed').length / pts.length) * 100); } const patchMut = useMutation({ mutationFn: ({ id, status }: { id: string; status: string }) => apiFetch(`/v1/goals/${id}`, { tenantId, method: 'PATCH', body: { status } }), onSuccess: () => qc.invalidateQueries({ queryKey: ['projects-goals', tenantId] }), }); const addMut = useMutation({ mutationFn: () => apiFetch('/v1/goals', { tenantId, method: 'POST', body: { title: form.title, description: form.description || undefined, tags: ['proiect', 'project'], status: 'active', progress: 0, targetDate: form.targetDate || undefined, }}), onSuccess: () => { qc.invalidateQueries({ queryKey: ['projects-goals', tenantId] }); setShowAdd(false); setForm({ title: '', description: '', targetDate: '' }); }, }); const byStatus = useMemo(() => { const map: Record = {}; for (const p of projects) { const s = p.status ?? 'active'; map[s] = [...(map[s] ?? []), p]; } return map; }, [projects]); return (

Project Tracker

{projects.filter((p) => p.status === 'active').length} active · {projects.filter((p) => p.status === 'completed').length} finalizate

{showAdd && (

Proiect nou

setForm((p) => ({ ...p, title: e.target.value }))} 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" />