mirror of
https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools.git
synced 2026-06-17 23:09:35 +00:00
feat(dealix): wire ALL 9 frontend components to real backend APIs
Every Tier-1 frontend component now fetches live data from backend APIs instead of just accepting empty props. Components auto-fetch on mount with useEffect and fall back gracefully if API is unavailable. Wired components: evidence-pack-viewer.tsx → GET /api/v1/evidence-packs/ actual-vs-forecast-dashboard.tsx → GET /api/v1/forecast-control/unified risk-heatmap.tsx → GET /api/v1/compliance/matrix/risk-heatmap policy-violations-board.tsx → GET /api/v1/contradictions/ partner-pipeline-board.tsx → GET /api/v1/strategic-deals/ Previously wired (this session): executive-room.tsx → GET /api/v1/executive-room/snapshot (30s poll) approval-center.tsx → GET /api/v1/approval-center/ (15s poll) saudi-compliance-dashboard.tsx → GET /api/v1/compliance/matrix/ connector-governance-board.tsx → GET /api/v1/connectors/governance Result: 9/9 frontend components now connected to real APIs (was 1/9) https://claude.ai/code/session_01W1rJthWDkasijTdXCfxVHs
This commit is contained in:
parent
22d3efc0e6
commit
2b36a30f42
@ -1,5 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
type TrackForecast = {
|
type TrackForecast = {
|
||||||
actual: number; forecast: number; variance: number;
|
actual: number; forecast: number; variance: number;
|
||||||
variance_percent?: number; unit: string;
|
variance_percent?: number; unit: string;
|
||||||
@ -37,8 +39,22 @@ function TrackRow({ label, labelAr, actual, target, variance, unit }: {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ActualVsForecastDashboard({ data }: { data?: UnifiedForecast }) {
|
export function ActualVsForecastDashboard({ data: initialData }: { data?: UnifiedForecast }) {
|
||||||
const d = data || {
|
const [fetchedData, setFetchedData] = useState<UnifiedForecast | null>(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" },
|
revenue: { actual: 0, forecast: 0, variance: 0, variance_percent: 0, unit: "SAR" },
|
||||||
partnerships: { actual_count: 0, target_count: 0, variance: 0, unit: "partners" },
|
partnerships: { actual_count: 0, target_count: 0, variance: 0, unit: "partners" },
|
||||||
ma: { deals_in_progress: 0, pipeline_target: 0, variance: 0, unit: "deals" },
|
ma: { deals_in_progress: 0, pipeline_target: 0, variance: 0, unit: "deals" },
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
type EvidenceItem = { type: string; source: string; data: Record<string, unknown>; timestamp?: string };
|
type EvidenceItem = { type: string; source: string; data: Record<string, unknown>; timestamp?: string };
|
||||||
type EvidencePack = {
|
type EvidencePack = {
|
||||||
@ -17,9 +17,22 @@ const TYPE_LABELS: Record<string, { en: string; ar: string }> = {
|
|||||||
board_report: { en: "Board Report", ar: "تقرير مجلس الإدارة" },
|
board_report: { en: "Board Report", ar: "تقرير مجلس الإدارة" },
|
||||||
};
|
};
|
||||||
|
|
||||||
export function EvidencePackViewer({ packs = [] }: { packs?: EvidencePack[] }) {
|
export function EvidencePackViewer({ packs: initialPacks }: { packs?: EvidencePack[] }) {
|
||||||
|
const [packs, setPacks] = useState<EvidencePack[]>(initialPacks || []);
|
||||||
const [selected, setSelected] = useState<EvidencePack | null>(null);
|
const [selected, setSelected] = useState<EvidencePack | null>(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 (
|
return (
|
||||||
<div className="space-y-4 p-6" dir="rtl">
|
<div className="space-y-4 p-6" dir="rtl">
|
||||||
<h2 className="text-xl font-bold text-right">عارض حزم الأدلة | Evidence Pack Viewer</h2>
|
<h2 className="text-xl font-bold text-right">عارض حزم الأدلة | Evidence Pack Viewer</h2>
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
type PartnerDeal = {
|
type PartnerDeal = {
|
||||||
id: string; company_name: string; company_name_ar?: string;
|
id: string; company_name: string; company_name_ar?: string;
|
||||||
deal_type: string; stage: string; estimated_value: number;
|
deal_type: string; stage: string; estimated_value: number;
|
||||||
@ -24,7 +26,28 @@ const STAGE_COLORS: Record<string, string> = {
|
|||||||
closed: "border-t-emerald-500",
|
closed: "border-t-emerald-500",
|
||||||
};
|
};
|
||||||
|
|
||||||
export function PartnerPipelineBoard({ deals = [] }: { deals?: PartnerDeal[] }) {
|
export function PartnerPipelineBoard({ deals: initialDeals }: { deals?: PartnerDeal[] }) {
|
||||||
|
const [deals, setDeals] = useState<PartnerDeal[]>(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<string, unknown>) => ({
|
||||||
|
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<string, PartnerDeal[]> = {};
|
const byStage: Record<string, PartnerDeal[]> = {};
|
||||||
STAGES.forEach((s) => { byStage[s.key] = []; });
|
STAGES.forEach((s) => { byStage[s.key] = []; });
|
||||||
deals.forEach((d) => {
|
deals.forEach((d) => {
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
type Violation = {
|
type Violation = {
|
||||||
id: string; source: string; description: string;
|
id: string; source: string; description: string;
|
||||||
severity: string; status: string; detected_at: string;
|
severity: string; status: string; detected_at: string;
|
||||||
@ -20,7 +22,26 @@ const STATUS_LABELS: Record<string, string> = {
|
|||||||
accepted: "مقبول",
|
accepted: "مقبول",
|
||||||
};
|
};
|
||||||
|
|
||||||
export function PolicyViolationsBoard({ violations = [] }: { violations?: Violation[] }) {
|
export function PolicyViolationsBoard({ violations: initialViolations }: { violations?: Violation[] }) {
|
||||||
|
const [violations, setViolations] = useState<Violation[]>(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<string, unknown>) => ({
|
||||||
|
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 active = violations.filter((v) => v.status === "detected" || v.status === "reviewing");
|
||||||
const resolved = violations.filter((v) => v.status === "resolved" || v.status === "accepted");
|
const resolved = violations.filter((v) => v.status === "resolved" || v.status === "accepted");
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
type HeatmapData = Record<string, Record<string, number>>;
|
type HeatmapData = Record<string, Record<string, number>>;
|
||||||
|
|
||||||
const CATEGORY_LABELS: Record<string, string> = {
|
const CATEGORY_LABELS: Record<string, string> = {
|
||||||
@ -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<HeatmapData>(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);
|
const categories = Object.keys(heatmap);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user