From 4739293077df81299d0e955209e32f5b1c364491 Mon Sep 17 00:00:00 2001 From: admin-valentin Date: Sun, 2 Aug 2026 17:45:00 +0000 Subject: [PATCH] feat(CC-088): add Weekly Review page (auto 7-day summary: wins, attention, decisions, prep) --- src/app/dashboard/weekly-review/page.tsx | 125 +++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 src/app/dashboard/weekly-review/page.tsx diff --git a/src/app/dashboard/weekly-review/page.tsx b/src/app/dashboard/weekly-review/page.tsx new file mode 100644 index 0000000..0387fa1 --- /dev/null +++ b/src/app/dashboard/weekly-review/page.tsx @@ -0,0 +1,125 @@ +'use client'; + +import { useMemo } from 'react'; +import { useQuery } from '@tanstack/react-query'; +import Link from 'next/link'; +import { apiFetch } from '../../../lib/api'; +import { useSession } from '../../../components/session-provider'; + +interface Task { id: string; title: string; status: string; priority: string | null; createdAt: string; } +interface Goal { id: string; title: string; status: string; progress: number | null; updatedAt: string; } +interface Decision { id: string; title: string; impact: string | null; createdAt: string; } +interface Observation { id: string; metric: string; value: string; confidence: number | null; createdAt: string; } + +function last7(): string { const d = new Date(); d.setDate(d.getDate() - 7); return d.toISOString(); } + +export default function WeeklyReviewPage() { + const { activeTenant } = useSession(); + const tenantId = activeTenant?.tenantId ?? ''; + const since = last7(); + + const { data: tasks = [] } = useQuery({ queryKey: ['wr-tasks', tenantId], queryFn: () => apiFetch('/v1/tasks?limit=500', { tenantId }), enabled: Boolean(tenantId), staleTime: 60_000 }); + const { data: goals = [] } = useQuery({ queryKey: ['wr-goals', tenantId], queryFn: () => apiFetch('/v1/goals?limit=200', { tenantId }), enabled: Boolean(tenantId), staleTime: 60_000 }); + const { data: decisions = [] } = useQuery({ queryKey: ['wr-decisions', tenantId], queryFn: () => apiFetch('/v1/decisions?limit=200', { tenantId }), enabled: Boolean(tenantId), staleTime: 60_000 }); + const { data: observations = [] } = useQuery({ queryKey: ['wr-obs', tenantId], queryFn: () => apiFetch('/v1/observations', { tenantId }), enabled: Boolean(tenantId), staleTime: 60_000 }); + + const week = useMemo(() => { + const isRecent = (d: string) => d >= since; + const newTasks = tasks.filter((t) => isRecent(t.createdAt)); + const doneTasks = tasks.filter((t) => t.status === 'completed' && isRecent(t.createdAt)); + const overdueT = tasks.filter((t) => t.status !== 'completed' && t.status !== 'cancelled'); + const updGoals = goals.filter((g) => isRecent(g.updatedAt)); + const newDecisions = decisions.filter((d) => isRecent(d.createdAt)); + const newObs = observations.filter((o) => isRecent(o.createdAt)); + const urgentOpen = tasks.filter((t) => t.priority === 'urgent' && !['completed','cancelled'].includes(t.status)); + return { newTasks, doneTasks, overdueT, updGoals, newDecisions, newObs, urgentOpen }; + }, [tasks, goals, decisions, observations, since]); + + const weekLabel = new Date().toLocaleDateString('ro-RO', { day: '2-digit', month: 'long', year: 'numeric' }); + + return ( +
+
+

Weekly Review

+

Rezumat săptămânal automat — ce s-a întâmplat în ultimele 7 zile.

+

Generat: {weekLabel}

+
+ + {/* Wins */} +
+

Realizări săptămâna asta

+
+ {[ + { label: 'Taskuri finalizate', value: week.doneTasks.length }, + { label: 'Decizii luate', value: week.newDecisions.length }, + { label: 'Observații logate', value: week.newObs.length }, + { label: 'Obiective actualizate', value: week.updGoals.length }, + ].map((s) => ( +
+

{s.value}

+

{s.label}

+
+ ))} +
+ {week.doneTasks.length > 0 && ( +
+ {week.doneTasks.slice(0, 5).map((t) => ( +

+ {t.title} +

+ ))} + {week.doneTasks.length > 5 &&

+{week.doneTasks.length - 5} mai multe

} +
+ )} +
+ + {/* Attention items */} + {(week.urgentOpen.length > 0 || week.overdueT.length > 10) && ( +
+

Necesită atenție

+ {week.urgentOpen.length > 0 && ( +
+

Urgente deschise:

+ {week.urgentOpen.slice(0, 3).map((t) => ( +

⚡ {t.title}

+ ))} +
+ )} +

+ {week.overdueT.length} taskuri active total — vezi lista → +

+
+ )} + + {/* New decisions */} + {week.newDecisions.length > 0 && ( +
+

Decizii luate această săptămână

+ {week.newDecisions.slice(0, 5).map((d) => ( +
+ +

{d.title}

+ {d.impact && {d.impact}} +
+ ))} +
+ )} + + {/* Next week prep */} +
+

Pregătire pentru săptămâna viitoare

+
+

→ Revizuiește obiectivele active cu progres mai mic de 50%

+

→ Prioritizează taskurile urgente deschise

+

→ Loghează cel puțin 3 observații personale

+

→ Documentează deciziile importante cu rațional

+
+
+ Obiective → + Taskuri → + Loghează observații → +
+
+
+ ); +}