Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | 5x 5x 5x 5x 5x 2x 2x 2x 5x 2x 2x 1x 1x 1x 5x 5x 5x | import { FileText } from "lucide-react";
import { useCallback, useMemo } from "react";
import { useSearchParams } from "react-router-dom";
import { toast } from "sonner";
import {
SeverityEnum,
useDiagnosticsEvaluateFlowsheetPropertyRulesQuery,
useUnitopsSimulationobjectsListQuery,
} from "@/api/apiStore.gen";
import { useProjectId } from "@/hooks/project";
import { ContentTypes } from "../LeftSideBar/LeftSideBarTabDefinitions";
import type { DiagnosticFinding } from "./DiagnosticFindingCard";
import { DiagnosticPanel } from "./DiagnosticPanel";
export function DiagnosticRunPanel() {
const flowsheetId = useProjectId();
const { data: flowsheetObjects } = useUnitopsSimulationobjectsListQuery(
{ flowsheet: flowsheetId },
{ skip: !flowsheetId },
);
const [, setParams] = useSearchParams();
const {
data: diagnosticsData,
isLoading,
isFetching,
} = useDiagnosticsEvaluateFlowsheetPropertyRulesQuery(
{
flowsheetId,
flowsheet: flowsheetId,
},
{ skip: !flowsheetId },
);
const ruleFindings = useMemo<DiagnosticFinding[]>(() => {
const findings = diagnosticsData?.findings ?? [];
return findings.map((finding, index) => {
return {
...finding,
id:
finding.id ??
`${finding.ruleReference ?? "finding"}-${finding.propertyId ?? "property"}-${index}`,
severity: finding.severity,
};
});
}, [diagnosticsData]);
// 0 is most severe, 3 is least severe
const sortedFindings = useMemo(() => {
const severityRank: Record<DiagnosticFinding["severity"], number> = {
[SeverityEnum.Error]: 0,
[SeverityEnum.Warning]: 1,
[SeverityEnum.Info]: 2,
[SeverityEnum.Suggestion]: 3,
};
// Sort by severity (worst first), then component name, then title.
return [...ruleFindings].sort((a, b) => {
const aRank = severityRank[a.severity];
const bRank = severityRank[b.severity];
if (aRank !== bRank) return aRank - bRank;
// Same severity: order by component name.
const aName = a.componentName ?? "";
const bName = b.componentName ?? "";
Iif (aName !== bName) return aName.localeCompare(bName);
// Same component: order by title.
return a.title.localeCompare(b.title);
});
}, [ruleFindings]);
const handleFocusComponent = useCallback(
(componentName: string) => {
const match = flowsheetObjects?.find(
(o) => o.componentName === componentName,
);
Iif (!match) {
toast.error(`Couldn't find ${componentName} on this flowsheet`);
return;
}
setParams((prev) => {
const next = new URLSearchParams(prev);
next.set("object", String(match.id));
next.set("content", ContentTypes.objectDetails);
return next;
});
},
[flowsheetObjects, setParams],
);
Iif (!flowsheetId) {
return (
<div className="h-full w-full min-w-0 flex flex-col p-4">
<div className="rounded-lg border bg-card p-4">
<div className="flex items-center gap-2">
<div className="rounded-md bg-muted p-1">
<FileText className="h-4 w-4 text-muted-foreground" />
</div>
<div className="font-semibold text-sm">Diagnostics</div>
</div>
<div className="mt-2 text-xs text-muted-foreground">
Open a flowsheet to view diagnostics.
</div>
</div>
</div>
);
}
return (
<div className="h-full w-full min-w-0 flex flex-col">
<DiagnosticPanel
findings={sortedFindings}
onFocusComponent={handleFocusComponent}
isSearching={isLoading || isFetching}
/>
</div>
);
}
|