Release govoplan-risk-compliance v0.1.21: unify interface contracts and documentation
Module Package Release / publish-packages (push) Successful in 10s
Module Package Release / publish-packages (push) Successful in 10s
This commit is contained in:
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@govoplan/risk-compliance-webui",
|
||||
"version": "0.1.20",
|
||||
"version": "0.1.21",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
@@ -14,7 +14,7 @@
|
||||
"./styles/risk-compliance.css": "./src/styles/risk-compliance.css"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.18",
|
||||
"@govoplan/core-webui": "^0.1.45",
|
||||
"lucide-react": "^1.23.0",
|
||||
"react": ">=19.2.7 <20",
|
||||
"react-dom": ">=19.2.7 <20",
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
Pencil,
|
||||
Play,
|
||||
Plus,
|
||||
RefreshCw,
|
||||
Scale,
|
||||
Upload
|
||||
} from "lucide-react";
|
||||
@@ -18,23 +17,29 @@ import {
|
||||
type FormEvent
|
||||
} from "react";
|
||||
import { useSearchParams } from "react-router";
|
||||
import { FormGrid, ActionToolbar, ToolbarSpacer,
|
||||
import { FormGrid,
|
||||
ActionBlockerHint,
|
||||
Button,
|
||||
Card,
|
||||
ConfirmDialog,
|
||||
ContentGrid,
|
||||
DescriptionList,
|
||||
DescriptionItem,
|
||||
Dialog,
|
||||
DismissibleAlert,
|
||||
DocumentationHelpLink,
|
||||
FormField,
|
||||
IconButton,
|
||||
LoadingIndicator,
|
||||
MetricCard,
|
||||
PageScrollViewport,
|
||||
SegmentedControl,
|
||||
SelectionList,
|
||||
SelectionListItem,
|
||||
StatePanel,
|
||||
StatusBadge,
|
||||
ToggleSwitch,
|
||||
WorkspaceActionBar,
|
||||
WorkspaceFrame,
|
||||
hasScope,
|
||||
i18nMessage,
|
||||
useUnsavedDraftGuard,
|
||||
@@ -248,9 +253,14 @@ export default function RiskCompliancePage({
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="risk-page">
|
||||
<ActionToolbar className="risk-toolbar">
|
||||
<SegmentedControl
|
||||
<WorkspaceFrame as="main" surface="plain" label="Risk Compliance workspace" interfaceId="risk_compliance.workspace" helpContextId="risk_compliance.workspace" helpModuleId="risk_compliance">
|
||||
<WorkspaceActionBar
|
||||
scope="workspace"
|
||||
variant="workspace"
|
||||
refreshable
|
||||
reloadAction={{ onReload: () => void refresh(), loading, disabled: busy, disabledReason: busy ? RISK_COMPLIANCE_I18N.busy : undefined }}
|
||||
helpAction={<DocumentationHelpLink reference={RISK_COMPLIANCE_DOCUMENTATION} />}
|
||||
contextActions={<SegmentedControl
|
||||
value={view}
|
||||
onChange={setView}
|
||||
ariaLabel="Risk Compliance view"
|
||||
@@ -282,7 +292,7 @@ export default function RiskCompliancePage({
|
||||
<Scale size={15} />
|
||||
Review
|
||||
{queue.length > 0 && (
|
||||
<span className="risk-count">{queue.length}</span>
|
||||
<StatusBadge status="info" label={String(queue.length)} />
|
||||
)}
|
||||
</>
|
||||
),
|
||||
@@ -301,25 +311,16 @@ export default function RiskCompliancePage({
|
||||
title: !canReadAssurance ? RISK_COMPLIANCE_I18N.assuranceReadRequired : undefined
|
||||
}
|
||||
]}
|
||||
/>
|
||||
<ToolbarSpacer className="risk-toolbar-spacer" />
|
||||
<DocumentationHelpLink reference={RISK_COMPLIANCE_DOCUMENTATION} />
|
||||
{loading && <LoadingIndicator size="sm" label="Loading" />}
|
||||
<IconButton
|
||||
label="Refresh"
|
||||
icon={<RefreshCw size={16} />}
|
||||
onClick={() => void refresh()}
|
||||
disabled={loading || busy}
|
||||
disabledReason={loading ? RISK_COMPLIANCE_I18N.loading : busy ? RISK_COMPLIANCE_I18N.busy : undefined}
|
||||
/>
|
||||
</ActionToolbar>
|
||||
/>}
|
||||
/>
|
||||
<PageScrollViewport>
|
||||
<ContentGrid columns={1} className="content-pad">
|
||||
{(error || notice) && (
|
||||
<div className="risk-alerts">
|
||||
{error && (
|
||||
<DismissibleAlert
|
||||
tone="danger"
|
||||
resetKey={error}
|
||||
onDismiss={() => setError("")}
|
||||
>
|
||||
{error}
|
||||
</DismissibleAlert>
|
||||
@@ -328,14 +329,12 @@ export default function RiskCompliancePage({
|
||||
<DismissibleAlert
|
||||
tone="success"
|
||||
resetKey={notice}
|
||||
onDismiss={() => setNotice("")}
|
||||
>
|
||||
{notice}
|
||||
</DismissibleAlert>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="risk-workspace">
|
||||
{view === "sources" && (
|
||||
<SourcesPane
|
||||
available={sourcesAvailable}
|
||||
@@ -385,7 +384,8 @@ export default function RiskCompliancePage({
|
||||
onRefresh={refresh}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</ContentGrid>
|
||||
</PageScrollViewport>
|
||||
<ConfirmDialog
|
||||
open={Boolean(pendingImport)}
|
||||
title="i18n:govoplan-risk-compliance.import_snapshot_title"
|
||||
@@ -398,7 +398,7 @@ export default function RiskCompliancePage({
|
||||
onCancel={() => setPendingImport(null)}
|
||||
onConfirm={() => pendingImport && void importSnapshot(pendingImport)}
|
||||
/>
|
||||
</main>
|
||||
</WorkspaceFrame>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -422,15 +422,8 @@ function SourcesPane({
|
||||
[imported]
|
||||
);
|
||||
return (
|
||||
<section className="risk-source-layout">
|
||||
<div className="risk-panel">
|
||||
<header>
|
||||
<div>
|
||||
<strong>Connector evidence</strong>
|
||||
<span>Immutable acquired source snapshots</span>
|
||||
</div>
|
||||
<DocumentationHelpLink reference={RISK_COMPLIANCE_ADMIN_DOCUMENTATION} />
|
||||
</header>
|
||||
<ContentGrid columns={2} collapseAt="wide" align="stretch">
|
||||
<Card title="Connector evidence" actions={<DocumentationHelpLink reference={RISK_COMPLIANCE_ADMIN_DOCUMENTATION} />}>
|
||||
{!available && (
|
||||
<ActionBlockerHint
|
||||
tone="info"
|
||||
@@ -472,14 +465,8 @@ function SourcesPane({
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className="risk-panel">
|
||||
<header>
|
||||
<div>
|
||||
<strong>Screening catalogues</strong>
|
||||
<span>Normalized, immutable list versions</span>
|
||||
</div>
|
||||
</header>
|
||||
</Card>
|
||||
<Card title="Screening catalogues">
|
||||
<div className="risk-list">
|
||||
{imported.map((item) => (
|
||||
<div className="risk-list-row" key={item.id}>
|
||||
@@ -498,8 +485,8 @@ function SourcesPane({
|
||||
<StatePanel size="compact" description="No source snapshot has been imported." />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</Card>
|
||||
</ContentGrid>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -582,16 +569,10 @@ function ScreenPane({
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="risk-screen-layout">
|
||||
<form className="risk-panel risk-screen-form" onSubmit={submit}>
|
||||
<header>
|
||||
<div>
|
||||
<strong>New screening</strong>
|
||||
<span>Use only the data needed for comparison</span>
|
||||
</div>
|
||||
<DocumentationHelpLink reference={RISK_COMPLIANCE_DOCUMENTATION} />
|
||||
</header>
|
||||
<div className="risk-form-body">
|
||||
<ContentGrid columns={2} collapseAt="wide" align="stretch">
|
||||
<Card title="New screening" actions={<DocumentationHelpLink reference={RISK_COMPLIANCE_DOCUMENTATION} />}>
|
||||
<form onSubmit={submit}>
|
||||
<FormGrid columns={1}>
|
||||
{!snapshots.length && (
|
||||
<ActionBlockerHint
|
||||
reason={{
|
||||
@@ -653,16 +634,10 @@ function ScreenPane({
|
||||
<Play size={16} />
|
||||
Run screening
|
||||
</Button>
|
||||
</div>
|
||||
</FormGrid>
|
||||
</form>
|
||||
<div className="risk-panel risk-run-result">
|
||||
<header>
|
||||
<div>
|
||||
<strong>Result</strong>
|
||||
<span>Version-pinned candidate evidence</span>
|
||||
</div>
|
||||
{run && <StatusBadge status={run.outcome} />}
|
||||
</header>
|
||||
</Card>
|
||||
<Card title="Result" actions={run && <StatusBadge status={run.outcome} />}>
|
||||
{!run && (
|
||||
<StatePanel size="compact" description="Run a screening to inspect the result." />
|
||||
)}
|
||||
@@ -693,8 +668,8 @@ function ScreenPane({
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
</Card>
|
||||
</ContentGrid>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -785,14 +760,8 @@ function ReviewPane({
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="risk-review-layout">
|
||||
<aside className="risk-panel risk-review-queue">
|
||||
<header>
|
||||
<div>
|
||||
<strong>Review queue</strong>
|
||||
<span>{queue.length} candidates need review</span>
|
||||
</div>
|
||||
</header>
|
||||
<ContentGrid columns={2} collapseAt="wide" align="stretch">
|
||||
<Card title="Review queue" actions={<StatusBadge status="info" label={String(queue.length)} />}>
|
||||
<div className="risk-list">
|
||||
{queue.length > 0 && (
|
||||
<SelectionList label="Review candidates">
|
||||
@@ -817,14 +786,8 @@ function ReviewPane({
|
||||
<StatePanel size="compact" description="No candidates currently need review." />
|
||||
)}
|
||||
</div>
|
||||
</aside>
|
||||
<div className="risk-panel risk-evidence">
|
||||
<header>
|
||||
<div>
|
||||
<strong>Candidate evidence</strong>
|
||||
<span>Subject and immutable list entry comparison</span>
|
||||
</div>
|
||||
<div className="risk-header-actions">
|
||||
</Card>
|
||||
<Card title="Candidate evidence" actions={<>
|
||||
<DocumentationHelpLink reference={RISK_COMPLIANCE_DOCUMENTATION} />
|
||||
<Button
|
||||
variant="primary"
|
||||
@@ -836,14 +799,13 @@ function ReviewPane({
|
||||
>
|
||||
Record disposition
|
||||
</Button>
|
||||
</div>
|
||||
</header>
|
||||
</>}>
|
||||
{!detail && (
|
||||
<StatePanel size="compact" description="Select a candidate from the queue." />
|
||||
)}
|
||||
{detail && (
|
||||
<div className="risk-evidence-body">
|
||||
<div className="risk-comparison">
|
||||
<ContentGrid columns={2}>
|
||||
<EvidenceColumn
|
||||
title="Screening subject"
|
||||
name={detail.subject.primary_name || "Identifier-only"}
|
||||
@@ -869,7 +831,7 @@ function ReviewPane({
|
||||
(item) => item.value
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</ContentGrid>
|
||||
<div className="risk-match-evidence">
|
||||
<strong>
|
||||
{detail.candidate.score}% ·{" "}
|
||||
@@ -886,7 +848,7 @@ function ReviewPane({
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
<Dialog
|
||||
open={dialogOpen}
|
||||
title="Record screening disposition"
|
||||
@@ -957,7 +919,7 @@ function ReviewPane({
|
||||
<>
|
||||
<ToggleSwitch
|
||||
checked={reusable}
|
||||
onChange={(event) => setReusable(event.target.checked)}
|
||||
onChange={setReusable}
|
||||
label="Apply as a time-bounded exception to this subject and list entry"
|
||||
/>
|
||||
{reusable && (
|
||||
@@ -974,7 +936,7 @@ function ReviewPane({
|
||||
)}
|
||||
</form>
|
||||
</Dialog>
|
||||
</section>
|
||||
</ContentGrid>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1220,8 +1182,8 @@ function AssurancePane({
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="risk-assurance-layout">
|
||||
<div className="risk-assurance-summary">
|
||||
<ContentGrid columns={1}>
|
||||
<ContentGrid columns={1}>
|
||||
{!canWrite && (
|
||||
<ActionBlockerHint
|
||||
tone="info"
|
||||
@@ -1246,15 +1208,9 @@ function AssurancePane({
|
||||
tone={(summary?.by_state.open ?? 0) > 0 ? "danger" : "good"}
|
||||
/>
|
||||
</MetricGrid>
|
||||
</div>
|
||||
<div className="risk-assurance-columns">
|
||||
<aside className="risk-panel">
|
||||
<header>
|
||||
<div>
|
||||
<strong>Assurance objects</strong>
|
||||
<span>Current effective revisions</span>
|
||||
</div>
|
||||
<div className="risk-header-actions">
|
||||
</ContentGrid>
|
||||
<ContentGrid columns={2} collapseAt="wide" align="stretch">
|
||||
<Card title="Assurance objects" actions={<>
|
||||
<DocumentationHelpLink reference={RISK_COMPLIANCE_DOCUMENTATION} />
|
||||
<IconButton
|
||||
label="Add assurance object"
|
||||
@@ -1263,9 +1219,8 @@ function AssurancePane({
|
||||
disabled={!canWrite || busy}
|
||||
disabledReason={!canWrite ? RISK_COMPLIANCE_I18N.assuranceWriteRequired : busy ? RISK_COMPLIANCE_I18N.busy : undefined}
|
||||
/>
|
||||
</div>
|
||||
</header>
|
||||
<div className="risk-assurance-filter">
|
||||
</>}>
|
||||
<FormGrid columns={2} gap="small" spacing="block">
|
||||
<input
|
||||
type="search"
|
||||
value={query}
|
||||
@@ -1283,7 +1238,7 @@ function AssurancePane({
|
||||
<option value={item} key={item}>{formatToken(item)}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</FormGrid>
|
||||
<div className="risk-list">
|
||||
{visibleNodes.length > 0 && (
|
||||
<SelectionList label="Assurance objects">
|
||||
@@ -1307,14 +1262,8 @@ function AssurancePane({
|
||||
<StatePanel size="compact" description="No assurance objects match." />
|
||||
)}
|
||||
</div>
|
||||
</aside>
|
||||
<div className="risk-panel risk-assurance-detail">
|
||||
<header>
|
||||
<div>
|
||||
<strong>{selected?.label || "Assurance object"}</strong>
|
||||
<span>{selected ? formatToken(selected.kind) : "Select an object"}</span>
|
||||
</div>
|
||||
<div className="risk-header-actions">
|
||||
</Card>
|
||||
<Card title={selected?.label || "Assurance object"} actions={<>
|
||||
<DocumentationHelpLink reference={RISK_COMPLIANCE_DOCUMENTATION} />
|
||||
<IconButton
|
||||
label="Edit assurance object"
|
||||
@@ -1323,23 +1272,23 @@ function AssurancePane({
|
||||
disabled={!selected || !canWrite || selected.stable_id.startsWith("sanctions-") || busy}
|
||||
disabledReason={!selected ? RISK_COMPLIANCE_I18N.assuranceObjectRequired : !canWrite ? RISK_COMPLIANCE_I18N.assuranceWriteRequired : selected.stable_id.startsWith("sanctions-") ? RISK_COMPLIANCE_I18N.systemManagedObject : busy ? RISK_COMPLIANCE_I18N.busy : undefined}
|
||||
/>
|
||||
</div>
|
||||
</header>
|
||||
</>}>
|
||||
{!selected && (
|
||||
<StatePanel size="compact" description="Select an assurance object." />
|
||||
)}
|
||||
{selected && (
|
||||
<div className="risk-assurance-detail-body">
|
||||
<dl className="risk-assurance-properties">
|
||||
<div><dt>State</dt><dd><StatusBadge status={selected.state} /></dd></div>
|
||||
<div><dt>Owner</dt><dd>{selected.owner_ref}</dd></div>
|
||||
<div><dt>Scope</dt><dd>{selected.scope_ref || "Tenant"}</dd></div>
|
||||
<div><dt>Valid from</dt><dd>{formatDate(selected.valid_from)}</dd></div>
|
||||
<DescriptionList columns={2}>
|
||||
<DescriptionItem term="Type">{formatToken(selected.kind)}</DescriptionItem>
|
||||
<DescriptionItem term="State"><StatusBadge status={selected.state} /></DescriptionItem>
|
||||
<DescriptionItem term="Owner">{selected.owner_ref}</DescriptionItem>
|
||||
<DescriptionItem term="Scope">{selected.scope_ref || "Tenant"}</DescriptionItem>
|
||||
<DescriptionItem term="Valid from">{formatDate(selected.valid_from)}</DescriptionItem>
|
||||
{selected.governed_object_ref && (
|
||||
<div><dt>Governed object</dt><dd><code>{selected.governed_object_ref}</code></dd></div>
|
||||
<DescriptionItem term="Governed object"><code>{selected.governed_object_ref}</code></DescriptionItem>
|
||||
)}
|
||||
<div><dt>Classification</dt><dd>{selected.classification}</dd></div>
|
||||
</dl>
|
||||
<DescriptionItem term="Classification">{selected.classification}</DescriptionItem>
|
||||
</DescriptionList>
|
||||
{selected.description && <p>{selected.description}</p>}
|
||||
<div className="risk-assurance-links-header">
|
||||
<div>
|
||||
@@ -1376,8 +1325,8 @@ function AssurancePane({
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</ContentGrid>
|
||||
<AssuranceNodeDialog
|
||||
open={nodeDialogOpen}
|
||||
busy={busy}
|
||||
@@ -1438,7 +1387,7 @@ function AssurancePane({
|
||||
</FormField>
|
||||
</form>
|
||||
</Dialog>
|
||||
</section>
|
||||
</ContentGrid>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1666,7 +1615,7 @@ function dateTimeLocalValue(value: Date) {
|
||||
}
|
||||
|
||||
function formatToken(value: string) {
|
||||
return value.replaceAll("_", " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
|
||||
return value.replace(/_/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
|
||||
}
|
||||
|
||||
function EvidenceColumn({
|
||||
|
||||
@@ -1,194 +1,114 @@
|
||||
.risk-page {
|
||||
display: flex;
|
||||
/* Domain-specific evidence arrangements only. Workspace geometry, cards,
|
||||
headings, controls, metrics and responsive columns belong to Core. */
|
||||
.risk-list-main,
|
||||
.risk-candidate-summary > div,
|
||||
.risk-evidence-column,
|
||||
.risk-match-evidence,
|
||||
.risk-evidence-values {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
background: var(--panel);
|
||||
gap: var(--space-1);
|
||||
}
|
||||
|
||||
.risk-toolbar {
|
||||
.risk-list-main,
|
||||
.risk-candidate-summary > div {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.risk-list-row,
|
||||
.risk-candidate-summary,
|
||||
.risk-clear,
|
||||
.risk-assurance-links-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex: 0 0 auto;
|
||||
min-height: 50px;
|
||||
gap: var(--space-2);
|
||||
padding-block: var(--space-3);
|
||||
}
|
||||
|
||||
.risk-list-row,
|
||||
.risk-candidate-summary,
|
||||
.risk-assurance-links-header {
|
||||
border-bottom: var(--border-line);
|
||||
background: var(--panel-header);
|
||||
padding: 8px 14px;
|
||||
}
|
||||
|
||||
.risk-toolbar .segmented-control-option {
|
||||
gap: 6px;
|
||||
.risk-list-main strong,
|
||||
.risk-list-main span,
|
||||
.risk-list-main code {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.risk-toolbar-spacer {
|
||||
flex: 1;
|
||||
.risk-list-main span,
|
||||
.risk-list-main code,
|
||||
.risk-candidate-summary > div > span,
|
||||
.risk-evidence-column > span,
|
||||
.risk-evidence-values span,
|
||||
.risk-match-evidence span,
|
||||
.risk-match-evidence code,
|
||||
.risk-assurance-links-header span {
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.risk-count {
|
||||
min-width: 18px;
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--accent);
|
||||
color: var(--on-dark);
|
||||
padding: 1px 5px;
|
||||
font-size: 10px;
|
||||
text-align: center;
|
||||
.risk-evidence-values strong {
|
||||
overflow-wrap: anywhere;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.risk-alerts {
|
||||
.risk-score {
|
||||
display: inline-grid;
|
||||
min-width: 2.5em;
|
||||
flex: 0 0 auto;
|
||||
padding: 10px 14px 0;
|
||||
place-items: center;
|
||||
padding: var(--space-1);
|
||||
border: 1px solid var(--warning-border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--warning-soft);
|
||||
color: var(--text-strong);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.risk-workspace {
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
padding: 14px;
|
||||
.risk-clear {
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
.risk-source-layout,
|
||||
.risk-screen-layout,
|
||||
.risk-review-layout,
|
||||
.risk-assurance-layout {
|
||||
.risk-evidence-values,
|
||||
.risk-match-evidence {
|
||||
margin-block-start: var(--space-3);
|
||||
}
|
||||
|
||||
.risk-assurance-detail-body,
|
||||
.risk-assurance-form,
|
||||
.risk-disposition-form {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
height: 100%;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.risk-source-layout,
|
||||
.risk-screen-layout {
|
||||
grid-template-columns: minmax(320px, 1fr) minmax(360px, 1.35fr);
|
||||
}
|
||||
|
||||
.risk-review-layout {
|
||||
grid-template-columns: minmax(300px, 0.7fr) minmax(480px, 1.6fr);
|
||||
}
|
||||
|
||||
.risk-assurance-layout {
|
||||
grid-template-rows: auto minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.risk-assurance-summary {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.risk-assurance-columns {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
grid-template-columns: minmax(300px, 0.75fr) minmax(480px, 1.55fr);
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.risk-assurance-filter {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(120px, 0.45fr);
|
||||
gap: 8px;
|
||||
border-bottom: var(--border-line);
|
||||
padding: 8px 10px;
|
||||
}
|
||||
|
||||
.risk-assurance-filter input,
|
||||
.risk-assurance-filter select,
|
||||
.risk-assurance-form input,
|
||||
.risk-assurance-form select,
|
||||
.risk-assurance-form textarea {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.risk-assurance-detail-body {
|
||||
display: flex;
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.risk-assurance-detail-body > p {
|
||||
margin: 0;
|
||||
border-bottom: var(--border-line);
|
||||
color: var(--text);
|
||||
padding: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.risk-assurance-properties {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
margin: 0;
|
||||
border-bottom: var(--border-line);
|
||||
}
|
||||
|
||||
.risk-assurance-properties > div {
|
||||
min-width: 0;
|
||||
border-right: var(--border-line);
|
||||
border-bottom: var(--border-line);
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
.risk-assurance-properties > div:nth-child(2n) {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.risk-assurance-properties dt {
|
||||
margin-bottom: 4px;
|
||||
color: var(--muted);
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.risk-assurance-properties dd {
|
||||
min-width: 0;
|
||||
margin: 0;
|
||||
overflow-wrap: anywhere;
|
||||
color: var(--text-strong);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.risk-assurance-links-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
min-height: 52px;
|
||||
border-bottom: var(--border-line);
|
||||
padding: 8px 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.risk-assurance-links-header > div {
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.risk-assurance-links-header span {
|
||||
color: var(--muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.risk-assurance-links {
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
gap: var(--space-1);
|
||||
}
|
||||
|
||||
.risk-assurance-links > button {
|
||||
display: grid;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
grid-template-columns: minmax(110px, 0.45fr) minmax(0, 1fr) auto;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
gap: var(--space-2);
|
||||
border: 0;
|
||||
border-bottom: var(--border-line);
|
||||
background: transparent;
|
||||
color: var(--text);
|
||||
padding: 9px 12px;
|
||||
padding: var(--space-3) var(--space-1);
|
||||
text-align: left;
|
||||
font: inherit;
|
||||
cursor: pointer;
|
||||
@@ -198,296 +118,15 @@
|
||||
background: var(--sidebar-hover-bg);
|
||||
}
|
||||
|
||||
.risk-assurance-links > button span {
|
||||
color: var(--muted);
|
||||
font-size: 11px;
|
||||
.risk-assurance-links > button strong {
|
||||
flex: 1;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.risk-assurance-links > button strong {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 12px;
|
||||
.risk-assurance-links > button span {
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.risk-assurance-truncated {
|
||||
border-top: var(--border-line);
|
||||
color: var(--warning);
|
||||
padding: 9px 12px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.risk-assurance-form {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.risk-panel {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
flex-direction: column;
|
||||
border: var(--border-line);
|
||||
border-radius: var(--radius-compact);
|
||||
background: var(--surface);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.risk-panel > header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
min-height: 56px;
|
||||
flex: 0 0 auto;
|
||||
border-bottom: var(--border-line);
|
||||
background: var(--panel-header);
|
||||
padding: 9px 12px;
|
||||
}
|
||||
|
||||
.risk-panel > header > div:first-child {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
gap: 2px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.risk-panel > header strong {
|
||||
color: var(--text-strong);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.risk-panel > header span {
|
||||
color: var(--muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.risk-header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.risk-list,
|
||||
.risk-result-body,
|
||||
.risk-evidence-body {
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.risk-list-row,
|
||||
.risk-candidate-summary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
min-height: 58px;
|
||||
border: 0;
|
||||
border-bottom: var(--border-line);
|
||||
background: transparent;
|
||||
color: var(--text);
|
||||
padding: 8px 11px;
|
||||
}
|
||||
|
||||
.risk-queue-row {
|
||||
display: flex;
|
||||
min-height: 58px;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.risk-list > .selection-list {
|
||||
gap: 2px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.risk-list-main {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
gap: 3px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.risk-list-main strong,
|
||||
.risk-list-main span,
|
||||
.risk-list-main code {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.risk-list-main strong {
|
||||
color: var(--text-strong);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.risk-list-main span,
|
||||
.risk-list-main code {
|
||||
color: var(--muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.risk-form-body {
|
||||
display: grid;
|
||||
align-content: start;
|
||||
gap: 14px;
|
||||
overflow: auto;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.risk-form-body input,
|
||||
.risk-form-body select,
|
||||
.risk-disposition-form input,
|
||||
.risk-disposition-form select,
|
||||
.risk-disposition-form textarea {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.risk-form-body .btn {
|
||||
justify-self: start;
|
||||
}
|
||||
|
||||
.risk-score {
|
||||
display: inline-grid;
|
||||
width: 38px;
|
||||
height: 32px;
|
||||
flex: 0 0 38px;
|
||||
place-items: center;
|
||||
border: 1px solid var(--warning-border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--warning-soft);
|
||||
color: var(--text-strong);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.risk-candidate-summary > div {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
gap: 3px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.risk-candidate-summary span {
|
||||
color: var(--muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.risk-clear {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: var(--success);
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
.risk-comparison {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
border-bottom: var(--border-line);
|
||||
}
|
||||
|
||||
.risk-evidence-column {
|
||||
display: grid;
|
||||
align-content: start;
|
||||
gap: 5px;
|
||||
min-width: 0;
|
||||
border-right: var(--border-line);
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.risk-evidence-column:last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.risk-evidence-column > strong {
|
||||
color: var(--text-strong);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.risk-evidence-column > span {
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.risk-eyebrow {
|
||||
text-transform: uppercase;
|
||||
font-size: 10px !important;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.risk-evidence-values {
|
||||
display: grid;
|
||||
gap: 3px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.risk-evidence-values span {
|
||||
color: var(--muted);
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.risk-evidence-values strong {
|
||||
overflow-wrap: anywhere;
|
||||
color: var(--text);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.risk-match-evidence {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.risk-match-evidence span,
|
||||
.risk-match-evidence code {
|
||||
color: var(--muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.risk-disposition-form {
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.risk-workspace {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.risk-source-layout,
|
||||
.risk-screen-layout,
|
||||
.risk-review-layout,
|
||||
.risk-assurance-layout,
|
||||
.risk-assurance-columns {
|
||||
height: auto;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.risk-assurance-metrics {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.risk-assurance-properties {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.risk-assurance-properties > div {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.risk-panel {
|
||||
min-height: 340px;
|
||||
}
|
||||
|
||||
.risk-comparison {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.risk-evidence-column {
|
||||
border-right: 0;
|
||||
border-bottom: var(--border-line);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user