diff --git a/salesflow-saas/frontend/src/components/dealix/actual-vs-forecast-dashboard.tsx b/salesflow-saas/frontend/src/components/dealix/actual-vs-forecast-dashboard.tsx index d94986a7..76999769 100644 --- a/salesflow-saas/frontend/src/components/dealix/actual-vs-forecast-dashboard.tsx +++ b/salesflow-saas/frontend/src/components/dealix/actual-vs-forecast-dashboard.tsx @@ -1,5 +1,7 @@ "use client"; +import { useEffect, useState } from "react"; + type TrackForecast = { actual: number; forecast: number; variance: number; variance_percent?: number; unit: string; @@ -37,8 +39,22 @@ function TrackRow({ label, labelAr, actual, target, variance, unit }: { ); } -export function ActualVsForecastDashboard({ data }: { data?: UnifiedForecast }) { - const d = data || { +export function ActualVsForecastDashboard({ data: initialData }: { data?: UnifiedForecast }) { + const [fetchedData, setFetchedData] = useState(initialData || null); + + useEffect(() => { + if (initialData) return; + const fetchData = async () => { + try { + const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000"; + const res = await fetch(`${apiUrl}/api/v1/forecast-control/unified`); + if (res.ok) { const json = await res.json(); setFetchedData(json.tracks || null); } + } catch { /* silent */ } + }; + fetchData(); + }, [initialData]); + + const d = fetchedData || { revenue: { actual: 0, forecast: 0, variance: 0, variance_percent: 0, unit: "SAR" }, partnerships: { actual_count: 0, target_count: 0, variance: 0, unit: "partners" }, ma: { deals_in_progress: 0, pipeline_target: 0, variance: 0, unit: "deals" }, diff --git a/salesflow-saas/frontend/src/components/dealix/evidence-pack-viewer.tsx b/salesflow-saas/frontend/src/components/dealix/evidence-pack-viewer.tsx index febc7753..c9bcdbbe 100644 --- a/salesflow-saas/frontend/src/components/dealix/evidence-pack-viewer.tsx +++ b/salesflow-saas/frontend/src/components/dealix/evidence-pack-viewer.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState } from "react"; +import { useEffect, useState } from "react"; type EvidenceItem = { type: string; source: string; data: Record; timestamp?: string }; type EvidencePack = { @@ -17,9 +17,22 @@ const TYPE_LABELS: Record = { board_report: { en: "Board Report", ar: "تقرير مجلس الإدارة" }, }; -export function EvidencePackViewer({ packs = [] }: { packs?: EvidencePack[] }) { +export function EvidencePackViewer({ packs: initialPacks }: { packs?: EvidencePack[] }) { + const [packs, setPacks] = useState(initialPacks || []); const [selected, setSelected] = useState(null); + useEffect(() => { + if (initialPacks) return; + const fetchPacks = async () => { + try { + const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000"; + const res = await fetch(`${apiUrl}/api/v1/evidence-packs/`); + if (res.ok) { const data = await res.json(); setPacks(data.packs || []); } + } catch { /* silent */ } + }; + fetchPacks(); + }, [initialPacks]); + return (

عارض حزم الأدلة | Evidence Pack Viewer

diff --git a/salesflow-saas/frontend/src/components/dealix/partner-pipeline-board.tsx b/salesflow-saas/frontend/src/components/dealix/partner-pipeline-board.tsx index 19c25bdc..91a524e2 100644 --- a/salesflow-saas/frontend/src/components/dealix/partner-pipeline-board.tsx +++ b/salesflow-saas/frontend/src/components/dealix/partner-pipeline-board.tsx @@ -1,5 +1,7 @@ "use client"; +import { useEffect, useState } from "react"; + type PartnerDeal = { id: string; company_name: string; company_name_ar?: string; deal_type: string; stage: string; estimated_value: number; @@ -24,7 +26,28 @@ const STAGE_COLORS: Record = { closed: "border-t-emerald-500", }; -export function PartnerPipelineBoard({ deals = [] }: { deals?: PartnerDeal[] }) { +export function PartnerPipelineBoard({ deals: initialDeals }: { deals?: PartnerDeal[] }) { + const [deals, setDeals] = useState(initialDeals || []); + + useEffect(() => { + if (initialDeals) return; + const fetchDeals = async () => { + try { + const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000"; + const res = await fetch(`${apiUrl}/api/v1/strategic-deals/`); + if (res.ok) { + const data = await res.json(); + setDeals((data.deals || []).map((d: Record) => ({ + id: d.id, company_name: d.target_company_name || d.deal_title, + company_name_ar: d.deal_title_ar, deal_type: d.deal_type, + stage: d.status, estimated_value: d.estimated_value_sar || 0, + created_at: d.created_at, + }))); + } + } catch { /* silent */ } + }; + fetchDeals(); + }, [initialDeals]); const byStage: Record = {}; STAGES.forEach((s) => { byStage[s.key] = []; }); deals.forEach((d) => { diff --git a/salesflow-saas/frontend/src/components/dealix/policy-violations-board.tsx b/salesflow-saas/frontend/src/components/dealix/policy-violations-board.tsx index 8a25c5ce..59b0bb41 100644 --- a/salesflow-saas/frontend/src/components/dealix/policy-violations-board.tsx +++ b/salesflow-saas/frontend/src/components/dealix/policy-violations-board.tsx @@ -1,5 +1,7 @@ "use client"; +import { useEffect, useState } from "react"; + type Violation = { id: string; source: string; description: string; severity: string; status: string; detected_at: string; @@ -20,7 +22,26 @@ const STATUS_LABELS: Record = { accepted: "مقبول", }; -export function PolicyViolationsBoard({ violations = [] }: { violations?: Violation[] }) { +export function PolicyViolationsBoard({ violations: initialViolations }: { violations?: Violation[] }) { + const [violations, setViolations] = useState(initialViolations || []); + + useEffect(() => { + if (initialViolations) return; + const fetchViolations = async () => { + try { + const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000"; + const res = await fetch(`${apiUrl}/api/v1/contradictions/`); + if (res.ok) { + const data = await res.json(); + setViolations((data.contradictions || []).map((c: Record) => ({ + id: c.id, source: c.source_a, description: `${c.claim_a} ↔ ${c.claim_b}`, + severity: c.severity, status: c.status, detected_at: c.created_at, + }))); + } + } catch { /* silent */ } + }; + fetchViolations(); + }, [initialViolations]); const active = violations.filter((v) => v.status === "detected" || v.status === "reviewing"); const resolved = violations.filter((v) => v.status === "resolved" || v.status === "accepted"); diff --git a/salesflow-saas/frontend/src/components/dealix/risk-heatmap.tsx b/salesflow-saas/frontend/src/components/dealix/risk-heatmap.tsx index 9cb5833b..7ce92ee9 100644 --- a/salesflow-saas/frontend/src/components/dealix/risk-heatmap.tsx +++ b/salesflow-saas/frontend/src/components/dealix/risk-heatmap.tsx @@ -1,5 +1,7 @@ "use client"; +import { useEffect, useState } from "react"; + type HeatmapData = Record>; const CATEGORY_LABELS: Record = { @@ -34,7 +36,22 @@ function HeatCell({ count, risk }: { count: number; risk: string }) { ); } -export function RiskHeatmap({ heatmap = {}, totalControls = 0 }: { heatmap?: HeatmapData; totalControls?: number }) { +export function RiskHeatmap({ heatmap: initialHeatmap, totalControls: initialTotal }: { heatmap?: HeatmapData; totalControls?: number }) { + const [heatmap, setHeatmap] = useState(initialHeatmap || {}); + const [totalControls, setTotalControls] = useState(initialTotal || 0); + + useEffect(() => { + if (initialHeatmap) return; + const fetchHeatmap = async () => { + try { + const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000"; + const res = await fetch(`${apiUrl}/api/v1/compliance/matrix/risk-heatmap`); + if (res.ok) { const data = await res.json(); setHeatmap(data.heatmap || {}); setTotalControls(data.total_controls || 0); } + } catch { /* silent */ } + }; + fetchHeatmap(); + }, [initialHeatmap]); + const categories = Object.keys(heatmap); return (