Bine ai revenit{activeTenant ? `, ${activeTenant.tenantName}` : ''}
-
Briefingul zilei — prioritizare deterministă.
+
Executive Dashboard
- {isLoading &&
Se încarcă…
}
-
- {briefing && (
-
-
-
-
Companii noi (7 zile)
-
- {briefing.weekInReview.newOrganizations}
-
-
-
-
Segmente noi
-
- {briefing.weekInReview.newSegments}
-
-
-
-
Research briefs noi
-
- {briefing.weekInReview.newResearchBriefs}
-
-
-
-
- {briefing.aiExplanation && (
-
- Sinteza zilei
- {briefing.aiExplanation}
-
- )}
-
- {briefing.overdueTasks.length > 0 && (
-
- Sarcini restante
-
- {briefing.overdueTasks.map((task) => (
-
- {task.title}
-
- {task.dueAt && new Date(task.dueAt).toLocaleDateString('ro-RO')}
-
-
- ))}
-
-
- )}
-
-
- Următoarele 7 zile
- {briefing.upcomingTasks.length === 0 ? (
- Nimic programat în următoarea săptămână.
- ) : (
-
- {briefing.upcomingTasks.map((task) => (
-
- {task.title}
-
- {task.dueAt && new Date(task.dueAt).toLocaleDateString('ro-RO')}
-
-
- ))}
-
- )}
-
-
-
- Prioritizarea e deterministă; AI-ul doar explică rezultatul, nu decide ordinea.
+
+
+
Restante
+
0 ? 'text-signal-danger' : 'text-ink'
+ }`}
+ >
+ {overdueCount}
+
+
+
+
Companii noi (7z)
+
+ {briefing?.weekInReview.newOrganizations ?? 0}
+
+
Segmente noi
+
+ {briefing?.weekInReview.newSegments ?? 0}
+
+
+
+
Research nou
+
+ {briefing?.weekInReview.newResearchBriefs ?? 0}
+
+
+
+
+ {briefing?.aiExplanation && (
+
+ Sinteza zilei
+ {briefing.aiExplanation}
+
+ Vezi briefingul complet →
+
+
)}
-
-
- Caută companii →
-
-
- Segmente salvate →
-
-
- Research briefs →
-
-
+
+ Secțiuni active
+
+ {readyItems.map((item) => (
+
+
{item.section}
+
{item.label}
+
+ ))}
+
+
+ {progress.ready} din {progress.total} secțiuni sunt active. Restul apar în meniu cu un
+ punct și explică exact ce lipsește pentru fiecare.
+
+
);
}
diff --git a/src/components/dashboard-shell.tsx b/src/components/dashboard-shell.tsx
index f8e310d..a035222 100644
--- a/src/components/dashboard-shell.tsx
+++ b/src/components/dashboard-shell.tsx
@@ -5,16 +5,7 @@ import Link from 'next/link';
import { usePathname, useRouter } from 'next/navigation';
import { useSession } from './session-provider';
import { TenantSwitcher } from './tenant-switcher';
-
-const NAV_ITEMS = [
- { href: '/dashboard', label: 'Briefing' },
- { href: '/dashboard/organizations', label: 'Organizațiile mele' },
- { href: '/dashboard/tasks', label: 'Sarcini' },
- { href: '/dashboard/companies', label: 'Căutare companii' },
- { href: '/dashboard/segments', label: 'Segmente' },
- { href: '/dashboard/research', label: 'Research' },
- { href: '/dashboard/members', label: 'Echipă' },
-];
+import { NAV_SECTIONS, countByStatus } from '../lib/navigation';
/**
* Guard + shell pentru tot ce e sub /dashboard: redirecteaza catre /login sau
@@ -26,6 +17,7 @@ export function DashboardShell({ children }: { children: React.ReactNode }) {
const router = useRouter();
const pathname = usePathname();
const { authSession, authLoading, me, meLoading, activeTenant, signOut } = useSession();
+ const progress = countByStatus();
useEffect(() => {
if (authLoading) return;
@@ -52,23 +44,50 @@ export function DashboardShell({ children }: { children: React.ReactNode }) {
CEO OS
-
- {NAV_ITEMS.map((item) => {
- const isActive = item.href === '/dashboard' ? pathname === item.href : pathname.startsWith(item.href);
- return (
-
- {item.label}
-
- );
- })}
+
+ {NAV_SECTIONS.map((section) => (
+
+
+ {section.label}
+
+
+ {section.items.map((item) => {
+ const isActive =
+ item.href === '/dashboard' ? pathname === item.href : pathname === item.href;
+ const isPlanned = item.status === 'planned';
+ return (
+
+ {item.label}
+ {/* Punctul marcheaza ce nu e construit inca -- forma produsului
+ ramane vizibila, dar nu pretindem ca exista. */}
+ {isPlanned && (
+
+ )}
+
+ );
+ })}
+
+
+ ))}
-
+
+
+ {progress.ready} din {progress.total} secțiuni active
+
{me?.email}
void signOut()} className="mt-1 w-full px-2 py-1.5 text-left text-sm text-ink-faint hover:text-ink">
Deconectare
diff --git a/src/lib/navigation.ts b/src/lib/navigation.ts
new file mode 100644
index 0000000..293308c
--- /dev/null
+++ b/src/lib/navigation.ts
@@ -0,0 +1,331 @@
+/**
+ * Sursa unica de adevar pentru arhitectura de informatie a platformei
+ * (cele 11 sectiuni din specul de navigatie). Sidebar-ul si ruta catch-all
+ * pentru sectiunile neconstruite citesc amandoua de aici, ca sa nu existe
+ * doua liste care se desincronizeaza.
+ *
+ * status:
+ * 'ready' -> pagina exista si e conectata la ceo-api
+ * 'planned' -> ruta cade pe catch-all si afiseaza explicit ce lipseste
+ */
+export type NavStatus = 'ready' | 'planned';
+
+export interface NavItem {
+ label: string;
+ href: string;
+ status: NavStatus;
+ /** Ce lipseste concret ca sa devina 'ready' -- afisat pe pagina planned. */
+ missing?: string;
+}
+
+export interface NavSection {
+ label: string;
+ items: NavItem[];
+}
+
+const BASE = '/dashboard';
+
+export const NAV_SECTIONS: NavSection[] = [
+ {
+ label: 'Home',
+ items: [
+ { label: 'Executive Dashboard', href: BASE, status: 'ready' },
+ { label: 'Daily Briefing', href: `${BASE}/briefing`, status: 'ready' },
+ {
+ label: 'Alerts',
+ href: `${BASE}/alerts`,
+ status: 'planned',
+ missing: 'Motor de alerte peste Event Fabric (praguri, deduplicare, canale de notificare).',
+ },
+ ],
+ },
+ {
+ label: 'My Life',
+ items: [
+ {
+ label: 'Goals',
+ href: `${BASE}/goals`,
+ status: 'planned',
+ missing: 'Tabela goals exista in schema; lipsesc endpointurile /v1/goals si UI-ul.',
+ },
+ {
+ label: 'Calendar',
+ href: `${BASE}/calendar`,
+ status: 'planned',
+ missing: 'Connector de calendar (CalDAV/Google) prin Connector Fabric.',
+ },
+ {
+ label: 'Routines',
+ href: `${BASE}/routines`,
+ status: 'planned',
+ missing: 'Model de rutine + scheduler recurent.',
+ },
+ {
+ label: 'Energy & Performance',
+ href: `${BASE}/energy`,
+ status: 'planned',
+ missing: 'Telemetrie din Device Fabric (BLE gateway) -> observations.',
+ },
+ {
+ label: 'Personal KPI',
+ href: `${BASE}/personal-kpi`,
+ status: 'planned',
+ missing: 'Definitii de metrici personale peste observations.',
+ },
+ {
+ label: 'Family',
+ href: `${BASE}/family`,
+ status: 'planned',
+ missing: 'Rolul Family Member + partajare cu scope restrans (Policy Fabric).',
+ },
+ ],
+ },
+ {
+ label: 'Business',
+ items: [
+ { label: 'Companies', href: `${BASE}/organizations`, status: 'ready' },
+ {
+ label: 'CRM',
+ href: `${BASE}/crm`,
+ status: 'planned',
+ missing: 'Pipeline de leaduri + adapter ERPNext (blueprint ERP-001).',
+ },
+ {
+ label: 'Projects',
+ href: `${BASE}/projects`,
+ status: 'planned',
+ missing: 'Model de proiecte legat de organizations si tasks.',
+ },
+ { label: 'Tasks', href: `${BASE}/tasks`, status: 'ready' },
+ {
+ label: 'Transactions',
+ href: `${BASE}/transactions`,
+ status: 'planned',
+ missing: 'Tabela transactions exista in schema; lipsesc /v1/transactions si UI-ul.',
+ },
+ {
+ label: 'Cash-flow',
+ href: `${BASE}/cash-flow`,
+ status: 'planned',
+ missing: 'Proiectii peste transactions + import din ERPNext.',
+ },
+ {
+ label: 'Contracts',
+ href: `${BASE}/contracts`,
+ status: 'planned',
+ missing: 'Tip de document dedicat + termene si reinnoiri.',
+ },
+ {
+ label: 'Accountant Pack',
+ href: `${BASE}/accountant-pack`,
+ status: 'planned',
+ missing: 'Export pe perioada + verificare de evidence gaps + partajare limitata in timp.',
+ },
+ ],
+ },
+ {
+ label: 'Documents',
+ items: [
+ {
+ label: 'Archive',
+ href: `${BASE}/documents`,
+ status: 'planned',
+ missing: 'Paperless-ngx ruleaza pe server dar nu e conectat la ceo-api (adapter DOC-001).',
+ },
+ {
+ label: 'OCR Inbox',
+ href: `${BASE}/ocr-inbox`,
+ status: 'planned',
+ missing: 'Webhook Paperless -> status OCR -> extragere campuri candidate.',
+ },
+ {
+ label: 'Expirations',
+ href: `${BASE}/expirations`,
+ status: 'planned',
+ missing: 'Scan programat pe documents.expires_at -> alerta -> task.',
+ },
+ {
+ label: 'Evidence Center',
+ href: `${BASE}/evidence`,
+ status: 'planned',
+ missing: 'Legaturi document <-> tranzactie/termen cu verification status.',
+ },
+ ],
+ },
+ {
+ label: 'Intelligence',
+ items: [
+ { label: 'Research', href: `${BASE}/research`, status: 'ready' },
+ { label: 'Data Search', href: `${BASE}/companies`, status: 'ready' },
+ { label: 'Saved Segments', href: `${BASE}/segments`, status: 'ready' },
+ {
+ label: 'Insights',
+ href: `${BASE}/insights`,
+ status: 'planned',
+ missing: 'Detectie de tipare peste Event Fabric, cu sursa si confidence.',
+ },
+ {
+ label: 'Decisions',
+ href: `${BASE}/decisions`,
+ status: 'planned',
+ missing: 'Tabela decisions exista; lipsesc /v1/decisions, scenarii si review post-decizie.',
+ },
+ {
+ label: 'Opportunities',
+ href: `${BASE}/opportunities`,
+ status: 'planned',
+ missing: 'Tabela opportunities exista; lipseste motorul de candidati + owner review.',
+ },
+ {
+ label: 'Digital Twin',
+ href: `${BASE}/digital-twin`,
+ status: 'planned',
+ missing: 'Model dinamic al utilizatorului peste Memory Fabric. Faza A4.',
+ },
+ ],
+ },
+ {
+ label: 'Relationships',
+ items: [
+ {
+ label: 'Contacts',
+ href: `${BASE}/contacts`,
+ status: 'planned',
+ missing: 'intelligence-api are /contacts/match; lipsesc contactele proprii ale tenantului.',
+ },
+ {
+ label: 'Follow-ups',
+ href: `${BASE}/follow-ups`,
+ status: 'planned',
+ missing: 'Taskuri legate de contacte, cu cadenta.',
+ },
+ {
+ label: 'Promises',
+ href: `${BASE}/promises`,
+ status: 'planned',
+ missing: 'Angajamente extrase din conversatii/documente, cu termen si dovada.',
+ },
+ {
+ label: 'Introductions',
+ href: `${BASE}/introductions`,
+ status: 'planned',
+ missing: 'Flux de intermediere intre contacte, cu consimtamant explicit.',
+ },
+ {
+ label: 'Network Map',
+ href: `${BASE}/network-map`,
+ status: 'planned',
+ missing: 'Graf de relatii peste memory-graph (Apache AGE ruleaza deja pe server).',
+ },
+ ],
+ },
+ {
+ label: 'Trust & Reputation',
+ items: [
+ {
+ label: 'Credibility Profile',
+ href: `${BASE}/credibility`,
+ status: 'planned',
+ missing: 'Factori de reputatie derivati DOAR din dovezi verificate (Trust Engine).',
+ },
+ {
+ label: 'Social Reputation',
+ href: `${BASE}/social-reputation`,
+ status: 'planned',
+ missing: 'Semnale publice, cu sursa si data; explicit nu scor oficial.',
+ },
+ {
+ label: 'Certifications',
+ href: `${BASE}/certifications`,
+ status: 'planned',
+ missing: 'Certificari cu document doveditor si status de verificare.',
+ },
+ {
+ label: 'Projects & Outcomes',
+ href: `${BASE}/outcomes`,
+ status: 'planned',
+ missing: 'Rezultate masurabile legate de proiecte.',
+ },
+ {
+ label: 'References',
+ href: `${BASE}/references`,
+ status: 'planned',
+ missing: 'Recomandari cu sursa verificabila.',
+ },
+ {
+ label: 'Community Contributions',
+ href: `${BASE}/community`,
+ status: 'planned',
+ missing: 'Componenta 15 (Community & Professional Network), neinceputa.',
+ },
+ {
+ label: 'Compliance',
+ href: `${BASE}/compliance`,
+ status: 'planned',
+ missing: 'Obligatii de conformitate cu termene si dovezi.',
+ },
+ {
+ label: 'Disputes',
+ href: `${BASE}/disputes`,
+ status: 'planned',
+ missing: 'Litigii/dispute cu istoric si status.',
+ },
+ {
+ label: 'Credit Readiness',
+ href: `${BASE}/credit-readiness`,
+ status: 'planned',
+ missing: 'Pregatire pentru finantare. NU scor de credit oficial (exclus explicit din blueprint).',
+ },
+ ],
+ },
+ {
+ label: 'Platform',
+ items: [
+ {
+ label: 'AI Assistant',
+ href: `${BASE}/assistant`,
+ status: 'planned',
+ missing:
+ 'AI Gateway exista si Hermes ruleaza izolat pe Hetzner, dar ceo-api nu expune inca ' +
+ 'un server MCP cu uneltele CEO OS -- agentul nu poate atinge datele tenantului.',
+ },
+ {
+ label: 'Automations',
+ href: `${BASE}/automations`,
+ status: 'planned',
+ missing: 'n8n ruleaza pe Hetzner; lipsesc callback-urile semnate si coada de aprobare.',
+ },
+ {
+ label: 'Integrations',
+ href: `${BASE}/integrations`,
+ status: 'planned',
+ missing: 'Registru de connectori (ERPNext, Paperless, calendar, BLE) cu status si credentiale.',
+ },
+ { label: 'Privacy & Audit', href: `${BASE}/privacy-audit`, status: 'planned', missing: 'audit_log si consent_records exista in baza; lipseste UI-ul de consultare si export/stergere.' },
+ { label: 'Echipă', href: `${BASE}/members`, status: 'ready' },
+ ],
+ },
+];
+
+const ITEMS_BY_HREF = new Map();
+for (const section of NAV_SECTIONS) {
+ for (const item of section.items) {
+ ITEMS_BY_HREF.set(item.href, { section, item });
+ }
+}
+
+export function findNavItem(href: string) {
+ return ITEMS_BY_HREF.get(href);
+}
+
+export function countByStatus() {
+ let ready = 0;
+ let planned = 0;
+ for (const section of NAV_SECTIONS) {
+ for (const item of section.items) {
+ if (item.status === 'ready') ready += 1;
+ else planned += 1;
+ }
+ }
+ return { ready, planned, total: ready + planned };
+}