Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion suite-ui/aldeci-ui-new/src/hooks/use-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
llmApi,
brainApi,
containerApi,
complianceEvidenceApi,
getStoredOrgId,
} from "@/lib/api";
import { toast } from "sonner";
Expand Down Expand Up @@ -453,8 +454,49 @@ export function useAssessCompliance() {
}

// ═══════════════════════════════════════════
export function useComplianceOverallStatus() {
return useQuery({
queryKey: ["compliance", "overall-status"],
queryFn: async () => {
const { data } = await complianceApi.overallStatus();
return data;
},
});
}

// Compliance Evidence hooks

export function useComplianceEvidenceRequests(params?: Record<string, unknown>) {
return useQuery({
queryKey: ["compliance-evidence", "requests", params],
queryFn: async () => {
const { data } = await complianceEvidenceApi.requests(params);
return data;
},
});
}

export function useEvidenceSummary() {
return useQuery({
queryKey: ["evidence", "summary"],
queryFn: async () => {
const { data } = await evidenceApi.summary();
return data;
},
});
}

export function useEvidenceList(params?: Record<string, unknown>) {
return useQuery({
queryKey: ["evidence", "list", params],
queryFn: async () => {
const { data } = await evidenceApi.list(params);
return data;
},
});
}

// Apps hooks
// ═══════════════════════════════════════════

export function useApps(params?: Record<string, unknown>) {
return useQuery({
Expand Down
15 changes: 15 additions & 0 deletions suite-ui/aldeci-ui-new/src/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,15 +258,30 @@ export const remediationApi = {

export const evidenceApi = {
bundles: (params?: Record<string, unknown>) => api.get("/api/v1/evidence/bundles", { params }),
list: (params?: Record<string, unknown>) => api.get("/api/v1/evidence/bundles", { params }),
summary: () => api.get("/api/v1/evidence/compliance-status"),
get: (id: string) => api.get(`/api/v1/evidence/bundles/${id}`),
generate: (data: unknown) => api.post("/api/v1/evidence/generate", data),
verify: (id: string) => api.get(`/api/v1/evidence/bundles/${id}/verify`),
export: (data: unknown) => api.post("/api/v1/evidence/export", data),
complianceStatus: () => api.get("/api/v1/evidence/compliance-status"),
};

export const complianceEvidenceApi = {
requests: (params?: Record<string, unknown>) => api.get("/api/v1/compliance-evidence/requests", { params }),
createRequest: (data: unknown) => api.post("/api/v1/compliance-evidence/requests", data),
listEvidence: (requestId: string, params?: Record<string, unknown>) => api.get(`/api/v1/compliance-evidence/requests/${requestId}/evidence`, { params }),
submitEvidence: (requestId: string, data: unknown) => api.post(`/api/v1/compliance-evidence/requests/${requestId}/evidence`, data),
approve: (requestId: string, data: unknown) => api.post(`/api/v1/compliance-evidence/requests/${requestId}/approve`, data),
reject: (requestId: string, data: unknown) => api.post(`/api/v1/compliance-evidence/requests/${requestId}/reject`, data),
autoCollect: (data: unknown) => api.post("/api/v1/compliance-evidence/auto-collect", data),
auditReadiness: (params?: Record<string, unknown>) => api.get("/api/v1/compliance-evidence/audit-readiness", { params }),
stats: (params?: Record<string, unknown>) => api.get("/api/v1/compliance-evidence/stats", { params }),
};

export const complianceApi = {
status: () => api.get("/api/v1/compliance-engine/status"),
overallStatus: () => api.get("/api/v1/compliance/status"),
frameworks: () => api.get("/api/v1/compliance-engine/frameworks"),
gaps: () => api.get("/api/v1/compliance-engine/gaps"),
assess: (data: unknown) => api.post("/api/v1/compliance-engine/assess", data),
Expand Down
19 changes: 11 additions & 8 deletions suite-ui/aldeci-ui-new/src/pages/comply/Analytics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer,
LineChart, Line, BarChart, Bar, Legend
} from "recharts";
import { useDashboardTrends, useDashboardOverview } from "@/hooks/use-api";
import { useDashboardTrends, useDashboardOverview, useComplianceOverallStatus } from "@/hooks/use-api";

const CHART_THEME = {
grid: "#1e293b",
Expand Down Expand Up @@ -67,20 +67,23 @@ export default function Analytics() {
const [timeRange, setTimeRange] = useState("6m");
const trendsQuery = useDashboardTrends({ range: timeRange });
const overviewQuery = useDashboardOverview();
const complianceQuery = useComplianceOverallStatus();

const refetchAll = useCallback(() => {
trendsQuery.refetch();
overviewQuery.refetch();
}, [trendsQuery, overviewQuery]);
complianceQuery.refetch();
}, [trendsQuery, overviewQuery, complianceQuery]);

const isLoading = trendsQuery.isLoading || overviewQuery.isLoading;
const isError = trendsQuery.isError;
const isError = trendsQuery.isError && complianceQuery.isError;

if (isLoading) return <PageSkeleton />;
if (isError) return <ErrorState message="Failed to load analytics data" onRetry={refetchAll} />;

const trends: any = trendsQuery.data?.data ?? trendsQuery.data ?? {};
const overview: any = overviewQuery.data?.data ?? overviewQuery.data ?? {};
const complianceStatus: any = complianceQuery.data?.data ?? complianceQuery.data ?? {};

// Extract trend arrays
const mttrTrend: any[] = trends.mttr_trend ?? trends.mttr ?? [];
Expand All @@ -89,11 +92,11 @@ export default function Analytics() {
const scannerData: any[] = trends.scanner_effectiveness ?? trends.scanners ?? [];
const costPerFixTrend: any[] = trends.cost_per_fix ?? [];

// KPIs
const currentMttr = overview.mttr ?? trends.current_mttr ?? "—";
const noiseReduction = overview.noise_reduction ?? trends.current_noise_reduction ?? "—";
const slaCompliance = overview.sla_compliance ?? trends.current_sla_compliance ?? "—";
const scannerRoi = overview.scanner_roi ?? trends.scanner_roi ?? "—";
// KPIs — prefer live compliance status, then overview, then trends, then fallback
const currentMttr = complianceStatus.mttr ?? overview.mttr ?? trends.current_mttr ?? "—";
const noiseReduction = complianceStatus.noise_reduction ?? overview.noise_reduction ?? trends.current_noise_reduction ?? "—";
const slaCompliance = complianceStatus.sla_compliance ?? complianceStatus.overall_compliance ?? overview.sla_compliance ?? trends.current_sla_compliance ?? "—";
const scannerRoi = complianceStatus.scanner_roi ?? overview.scanner_roi ?? trends.scanner_roi ?? "—";

const handleExportCSV = () => {
const rows = [
Expand Down
18 changes: 12 additions & 6 deletions suite-ui/aldeci-ui-new/src/pages/comply/EvidenceBundles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
Calendar, CheckCircle, AlertTriangle, Layers, Download, Zap,
ArrowLeftRight, GitMerge, Eye
} from "lucide-react";
import { useEvidenceBundles, useApps, useComplianceFrameworks, useGenerateEvidence } from "@/hooks/use-api";
import { useEvidenceBundles, useEvidenceList, useApps, useComplianceFrameworks, useGenerateEvidence } from "@/hooks/use-api";
import { toast } from "sonner";

const FRAMEWORKS = ["SOC2", "PCI-DSS", "HIPAA", "ISO27001", "NIST"];
Expand Down Expand Up @@ -270,23 +270,29 @@ function GenerateBundleDialog({ apps, frameworks, onGenerate }: {

export default function EvidenceBundles() {
const [selectedApp, setSelectedApp] = useState("all");
const bundlesQuery = useEvidenceBundles(selectedApp !== "all" ? { app_id: selectedApp } : {});
const listParams = selectedApp !== "all" ? { app_id: selectedApp } : {};
const evidenceListQuery = useEvidenceList(listParams);
const bundlesQuery = useEvidenceBundles(listParams);
const appsQuery = useApps();
const frameworksQuery = useComplianceFrameworks();
const generateMutation = useGenerateEvidence();

const refetchAll = useCallback(() => {
evidenceListQuery.refetch();
bundlesQuery.refetch();
appsQuery.refetch();
}, [bundlesQuery, appsQuery]);
}, [evidenceListQuery, bundlesQuery, appsQuery]);

const isLoading = bundlesQuery.isLoading || appsQuery.isLoading;
const isError = bundlesQuery.isError;
const isLoading = (evidenceListQuery.isLoading && bundlesQuery.isLoading) || appsQuery.isLoading;
const isError = evidenceListQuery.isError && bundlesQuery.isError;

if (isLoading) return <PageSkeleton />;
if (isError) return <ErrorState message="Failed to load evidence bundles" onRetry={refetchAll} />;

const bundles: any[] = toArray(bundlesQuery.data);
// Prefer evidence/list data, fall back to evidence/bundles
const bundles: any[] = toArray(evidenceListQuery.data).length > 0
? toArray(evidenceListQuery.data)
: toArray(bundlesQuery.data);
const apps: any[] = toArray(appsQuery.data);
const frameworks: string[] = toArray(frameworksQuery.data).map((f: any) => {
if (typeof f === "string") return f;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,12 +229,15 @@ export default function EvidenceExportCenter() {
const [lastExport, setLastExport] = useState<any>(null);

const isLoading = bundlesQuery.isLoading || appsQuery.isLoading;
const isError = bundlesQuery.isError;
const isError = bundlesQuery.isError && summaryQuery.isError;

if (isLoading) return <PageSkeleton />;
if (isError) return <ErrorState message="Failed to load export data" onRetry={refetchAll} />;

const bundles: any[] = toArray(bundlesQuery.data);
const summaryData: any = summaryQuery.data?.data ?? summaryQuery.data ?? {};
const bundles: any[] = toArray(summaryData.bundles ?? summaryData).length > 0
? toArray(summaryData.bundles ?? summaryData)
: toArray(bundlesQuery.data);
const apps: any[] = toArray(appsQuery.data);
const frameworks: string[] = toArray(frameworksQuery.data).map((f: any) => {
if (typeof f === "string") return f;
Expand Down
20 changes: 17 additions & 3 deletions suite-ui/aldeci-ui-new/src/pages/comply/SOC2Evidence.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,22 +85,36 @@ function CategoryProgressBar({ controls }: { controls: any[] }) {
export default function SOC2Evidence() {
const soc2Query = useComplianceSoc2();
const bundlesQuery = useEvidenceBundles({ framework: "SOC2" });
const evidenceRequestsQuery = useComplianceEvidenceRequests({ org_id: "default" });
const refetchAll = useCallback(() => {
soc2Query.refetch();
bundlesQuery.refetch();
}, [soc2Query, bundlesQuery]);
evidenceRequestsQuery.refetch();
}, [soc2Query, bundlesQuery, evidenceRequestsQuery]);

const [activeTab, setActiveTab] = useState("CC");
const [auditorView, setAuditorView] = useState(false);

const isLoading = soc2Query.isLoading || bundlesQuery.isLoading;
const isError = soc2Query.isError;
const isError = soc2Query.isError && evidenceRequestsQuery.isError;

if (isLoading) return <PageSkeleton />;
if (isError) return <ErrorState message="Failed to load SOC2 evidence data" onRetry={refetchAll} />;

const evidenceRequests: any[] = toArray(evidenceRequestsQuery.data);
const soc2Data: any = soc2Query.data?.data ?? {};
const controls: any[] = soc2Data.controls ?? soc2Data ?? [];
const controls: any[] = evidenceRequests.length > 0
? evidenceRequests.map((r: any) => ({
control_id: r.control_id ?? r.id,
title: r.control_name ?? r.description ?? "Evidence request",
description: r.description ?? "",
status: r.status === "approved" ? "passed" : r.status === "rejected" ? "failed" : "partial",
evidence_status: r.status === "approved" ? "collected" : r.status === "submitted" ? "partial" : "pending",
last_verified: r.updated_at ?? r.created_at ?? "",
has_gap: r.status !== "approved",
framework: r.framework ?? "SOC2",
}))
: (soc2Data.controls ?? soc2Data ?? []);
const bundles: any[] = toArray(bundlesQuery.data);

// Group controls by category
Expand Down