'use client'; import { useMemo, useState } from 'react'; import { useQuery } from '@tanstack/react-query'; import { apiFetch } from '../../../lib/api'; import { useSession } from '../../../components/session-provider'; interface Task { id: string; status: string; priority: string; createdAt: string; } interface Goal { id: string; title: string; status: string; targetDate: string | null; progress: number | null; createdAt: string; } interface Decision { id: string; title: string; status: string; createdAt: string; } interface OutcomeReview { id: string; wasSuccessful: boolean | null; rating: number | null; createdAt: string; } interface Observation { id: string; subjectType: string; metric: string; value: string; unit: string | null; createdAt: string; } const PERSONAL_TYPES = ['self','person','health','energy','sleep','mood','fitness','nutrition','wellbeing','productivity']; export default function LifeReportsPage() { const { activeTenant } = useSession(); const tenantId = activeTenant?.tenantId ?? ''; const [period, setPeriod] = useState<30 | 90 | 180>(30); const { data: tasks = [], isLoading: tL } = useQuery({ queryKey: ['lr-tasks', tenantId], queryFn: () => apiFetch('/v1/tasks?limit=500', { tenantId }), enabled: Boolean(tenantId), staleTime: 120_000, }); const { data: goals = [], isLoading: gL } = useQuery({ queryKey: ['lr-goals', tenantId], queryFn: () => apiFetch('/v1/goals?limit=100', { tenantId }), enabled: Boolean(tenantId), staleTime: 120_000, }); const { data: decisions = [], isLoading: dL } = useQuery({ queryKey: ['lr-decisions', tenantId], queryFn: () => apiFetch('/v1/decisions?limit=200', { tenantId }), enabled: Boolean(tenantId), staleTime: 120_000, }); const { data: outcomes = [], isLoading: oRL } = useQuery({ queryKey: ['lr-outcomes', tenantId], queryFn: () => apiFetch('/v1/outcome-reviews?limit=100', { tenantId }), enabled: Boolean(tenantId), staleTime: 120_000, }); const { data: rawObs = [], isLoading: obsL } = useQuery({ queryKey: ['lr-obs', tenantId], queryFn: () => apiFetch('/v1/observations', { tenantId }), enabled: Boolean(tenantId), staleTime: 120_000, }); const isLoading = tL || gL || dL || oRL || obsL; const cutoff = Date.now() - period * 86400_000; const report = useMemo(() => { const inPeriod = (d: string) => new Date(d).getTime() >= cutoff; const periodTasks = tasks.filter((t) => inPeriod(t.createdAt)); const completed = periodTasks.filter((t) => t.status === 'completed'); const completionRate = periodTasks.length > 0 ? Math.round((completed.length / periodTasks.length) * 100) : 0; const periodGoals = goals.filter((g) => inPeriod(g.createdAt) || g.status === 'active'); const goalsDone = goals.filter((g) => g.status === 'completed' && inPeriod(g.createdAt)); const avgProgress = periodGoals.reduce((s, g) => s + (g.progress ?? 0), 0) / (periodGoals.length || 1); const periodDecisions = decisions.filter((d) => inPeriod(d.createdAt)); const periodOutcomes = outcomes.filter((o) => inPeriod(o.createdAt)); const successfulOutcomes = periodOutcomes.filter((o) => o.wasSuccessful === true); const avgRating = periodOutcomes.filter((o) => o.rating != null).reduce((s, o) => s + (o.rating ?? 0), 0) / (periodOutcomes.filter((o) => o.rating != null).length || 1); const personalObs = rawObs.filter((o) => PERSONAL_TYPES.includes(o.subjectType) && inPeriod(o.createdAt)); const byMetric: Record = {}; for (const o of personalObs) { const v = parseFloat(o.value); if (!isNaN(v)) byMetric[o.metric] = [...(byMetric[o.metric] ?? []), v]; } const metricSummary = Object.entries(byMetric).map(([m, vals]) => ({ metric: m, avg: vals.reduce((a, b) => a + b, 0) / vals.length, latest: vals[vals.length - 1], count: vals.length, })).sort((a, b) => b.count - a.count); return { completionRate, completed: completed.length, total: periodTasks.length, goalsDone: goalsDone.length, activeGoals: periodGoals.filter(g => g.status === 'active').length, avgProgress: Math.round(avgProgress), decisions: periodDecisions.length, outcomes: periodOutcomes.length, successRate: periodOutcomes.length > 0 ? Math.round((successfulOutcomes.length / periodOutcomes.length) * 100) : null, avgRating: periodOutcomes.length > 0 ? avgRating : null, observations: personalObs.length, metricSummary, }; }, [tasks, goals, decisions, outcomes, rawObs, cutoff]); const periodLabel = { 30: '30 zile', 90: '90 zile', 180: '6 luni' }[period]; const generateText = () => { const lines = [ `# Raport de viață — ultimele ${periodLabel}`, `Data generării: ${new Date().toLocaleDateString('ro-RO')}`, ``, `## Execuție & Productivitate`, `- Taskuri finalizate: ${report.completed} din ${report.total} (${report.completionRate}%)`, `- Obiective active: ${report.activeGoals} | Finalizate în perioadă: ${report.goalsDone}`, `- Progres mediu obiective: ${report.avgProgress}%`, ``, `## Decizii & Outcome-uri`, `- Decizii luate: ${report.decisions}`, `- Revizuiri outcome: ${report.outcomes}`, report.successRate != null ? `- Rată de succes: ${report.successRate}%` : '', report.avgRating != null ? `- Rating mediu: ${report.avgRating.toFixed(1)}/10` : '', ``, `## Observații personale (${report.observations} înregistrări)`, ...report.metricSummary.slice(0, 8).map((m) => `- ${m.metric}: medie ${m.avg.toFixed(1)} (${m.count} măsurători)`), ].filter(Boolean).join('\n'); const blob = new Blob([lines], { type: 'text/plain;charset=utf-8' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `raport-viata-${periodLabel.replace(' ', '')}-${new Date().toISOString().slice(0, 10)}.txt`; a.click(); URL.revokeObjectURL(url); }; return (

Rapoarte de Viață

Analiză personală: obiective, timp, energie și decizii.

{([30, 90, 180] as const).map((d) => ( ))}
{isLoading ? (
Se generează raportul…
) : ( <> {/* Execution */}

Execuție & Productivitate · {periodLabel}

{report.completionRate}%

rată completare taskuri

{report.completed} din {report.total}

{report.activeGoals}

obiective active

{report.avgProgress}% progres mediu

{report.goalsDone}

obiective finalizate

în {periodLabel}

{/* Completion bar */}
Rată completare{report.completionRate}%
{/* Decision quality */}

Calitate decizii · {periodLabel}

{report.decisions}

decizii luate

{report.outcomes}

outcome reviews

= 70 ? 'text-signal-ok' : report.successRate >= 50 ? 'text-warn' : 'text-signal-danger') : 'text-ink-faint'}`}> {report.successRate !== null ? `${report.successRate}%` : '—'}

rată de succes

= 7 ? 'text-signal-ok' : report.avgRating >= 5 ? 'text-warn' : 'text-signal-danger') : 'text-ink-faint'}`}> {report.avgRating !== null ? report.avgRating.toFixed(1) : '—'}

rating mediu /10

{/* Personal metrics */}

Metrici personale · {periodLabel}

{report.metricSummary.length === 0 ? (

Nicio observație personală în această perioadă. Adaugă din Personal KPI sau Energy & Wellbeing.

) : (

{report.observations} înregistrări total

{report.metricSummary.slice(0, 10).map((m) => (

{m.metric}

{m.avg.toFixed(1)} ({m.count}x)
))}
)}
)}
); }