Release govoplan-risk-compliance v0.1.21: unify interface contracts and documentation
This commit is contained in:
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@govoplan/risk-compliance-webui",
|
"name": "@govoplan/risk-compliance-webui",
|
||||||
"version": "0.1.20",
|
"version": "0.1.21",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "webui/src/index.ts",
|
"main": "webui/src/index.ts",
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
"LICENSE"
|
"LICENSE"
|
||||||
],
|
],
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@govoplan/core-webui": "^0.1.18",
|
"@govoplan/core-webui": "^0.1.45",
|
||||||
"lucide-react": "^1.23.0",
|
"lucide-react": "^1.23.0",
|
||||||
"react": ">=19.2.7 <20",
|
"react": ">=19.2.7 <20",
|
||||||
"react-dom": ">=19.2.7 <20",
|
"react-dom": ">=19.2.7 <20",
|
||||||
|
|||||||
+2
-2
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "govoplan-risk-compliance"
|
name = "govoplan-risk-compliance"
|
||||||
version = "0.1.20"
|
version = "0.1.21"
|
||||||
description = "GovOPlaN Risk Compliance platform module seed."
|
description = "GovOPlaN Risk Compliance platform module seed."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
@@ -12,7 +12,7 @@ license = { file = "LICENSE" }
|
|||||||
authors = [{ name = "GovOPlaN" }]
|
authors = [{ name = "GovOPlaN" }]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"defusedxml>=0.7.1",
|
"defusedxml>=0.7.1",
|
||||||
"govoplan-core>=0.1.37",
|
"govoplan-core>=0.1.45",
|
||||||
"govoplan-access>=0.1.18",
|
"govoplan-access>=0.1.18",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ from govoplan_risk_compliance.backend.permissions import (
|
|||||||
|
|
||||||
MODULE_ID = "risk_compliance"
|
MODULE_ID = "risk_compliance"
|
||||||
MODULE_NAME = "Risk Compliance"
|
MODULE_NAME = "Risk Compliance"
|
||||||
MODULE_VERSION = "0.1.20"
|
MODULE_VERSION = "0.1.21"
|
||||||
OPTIONAL_DEPENDENCIES = (
|
OPTIONAL_DEPENDENCIES = (
|
||||||
"audit",
|
"audit",
|
||||||
"policy",
|
"policy",
|
||||||
@@ -670,6 +670,21 @@ manifest = ModuleManifest(
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
documentation=(
|
documentation=(
|
||||||
|
DocumentationTopic(
|
||||||
|
id="risk_compliance.workspace-layout",
|
||||||
|
title="Risk Compliance workspace layout",
|
||||||
|
summary="Find workspace actions and read consistently arranged content.",
|
||||||
|
body="Sources, screening, review, and assurance use the same workspace action bar, cards, typography, metrics, and responsive columns as the rest of GovOPlaN. Reload is at the upper right; source imports, dispositions, and assurance edits remain with their relevant card. Narrow windows stack the cards instead of requiring a separate miniature interface. Reloading does not import a source, execute a screening, or record a disposition. Administrators continue to grant the separate read, screening, review, and administration permissions; the shared presentation does not change legal assurance or review policy.",
|
||||||
|
layer="static",
|
||||||
|
documentation_types=("user", "admin"),
|
||||||
|
audience=("user", "module_admin", "operator"),
|
||||||
|
order=5,
|
||||||
|
translations={"de": {
|
||||||
|
"title": "Risiko und Compliance: Aufbau des Arbeitsbereichs",
|
||||||
|
"summary": "Arbeitsbereichsaktionen finden und einheitlich angeordnete Inhalte lesen.",
|
||||||
|
"body": "Quellen, Screening, Prüfung und Assurance verwenden dieselbe Arbeitsbereichsleiste, Karten, Typografie, Kennzahlen und responsiven Spalten wie das übrige GovOPlaN. Neu laden steht oben rechts; Quellenimporte, Prüfentscheidungen und Assurance-Bearbeitung bleiben bei ihrer jeweiligen Karte. Schmale Fenster ordnen die Karten untereinander an, statt eine verkleinerte Sonderoberfläche zu verwenden. Neu laden importiert keine Quelle, startet kein Screening und erfasst keine Entscheidung. Administratoren vergeben weiterhin getrennte Lese-, Screening-, Prüf- und Administrationsrechte; die gemeinsame Darstellung verändert weder rechtliche Zusicherungen noch Prüfrichtlinien.",
|
||||||
|
}},
|
||||||
|
),
|
||||||
DocumentationTopic(
|
DocumentationTopic(
|
||||||
id="risk_compliance.data-subject-requests",
|
id="risk_compliance.data-subject-requests",
|
||||||
title="Risk and compliance data-subject requests",
|
title="Risk and compliance data-subject requests",
|
||||||
|
|||||||
@@ -11,7 +11,22 @@ from govoplan_risk_compliance.backend.manifest import manifest
|
|||||||
|
|
||||||
class RiskComplianceDocumentationTests(unittest.TestCase):
|
class RiskComplianceDocumentationTests(unittest.TestCase):
|
||||||
def test_public_topics_have_complete_german_reference_content(self) -> None:
|
def test_public_topics_have_complete_german_reference_content(self) -> None:
|
||||||
self.assertEqual(2, len(manifest.documentation))
|
topics = {topic.id: topic for topic in manifest.documentation}
|
||||||
|
self.assertEqual(
|
||||||
|
len(manifest.documentation), len(topics), "Documentation topic IDs must be unique"
|
||||||
|
)
|
||||||
|
self.assertLessEqual(
|
||||||
|
{
|
||||||
|
"risk_compliance.module-boundary",
|
||||||
|
"risk_compliance.data-subject-requests",
|
||||||
|
"risk_compliance.workspace-layout",
|
||||||
|
},
|
||||||
|
set(topics),
|
||||||
|
)
|
||||||
|
self.assertLessEqual(
|
||||||
|
{"user", "admin"},
|
||||||
|
set(topics["risk_compliance.workspace-layout"].documentation_types),
|
||||||
|
)
|
||||||
for topic in manifest.documentation:
|
for topic in manifest.documentation:
|
||||||
translation = topic.translations.get("de", {})
|
translation = topic.translations.get("de", {})
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@govoplan/risk-compliance-webui",
|
"name": "@govoplan/risk-compliance-webui",
|
||||||
"version": "0.1.20",
|
"version": "0.1.21",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
"./styles/risk-compliance.css": "./src/styles/risk-compliance.css"
|
"./styles/risk-compliance.css": "./src/styles/risk-compliance.css"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@govoplan/core-webui": "^0.1.18",
|
"@govoplan/core-webui": "^0.1.45",
|
||||||
"lucide-react": "^1.23.0",
|
"lucide-react": "^1.23.0",
|
||||||
"react": ">=19.2.7 <20",
|
"react": ">=19.2.7 <20",
|
||||||
"react-dom": ">=19.2.7 <20",
|
"react-dom": ">=19.2.7 <20",
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
Pencil,
|
Pencil,
|
||||||
Play,
|
Play,
|
||||||
Plus,
|
Plus,
|
||||||
RefreshCw,
|
|
||||||
Scale,
|
Scale,
|
||||||
Upload
|
Upload
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
@@ -18,23 +17,29 @@ import {
|
|||||||
type FormEvent
|
type FormEvent
|
||||||
} from "react";
|
} from "react";
|
||||||
import { useSearchParams } from "react-router";
|
import { useSearchParams } from "react-router";
|
||||||
import { FormGrid, ActionToolbar, ToolbarSpacer,
|
import { FormGrid,
|
||||||
ActionBlockerHint,
|
ActionBlockerHint,
|
||||||
Button,
|
Button,
|
||||||
|
Card,
|
||||||
ConfirmDialog,
|
ConfirmDialog,
|
||||||
|
ContentGrid,
|
||||||
|
DescriptionList,
|
||||||
|
DescriptionItem,
|
||||||
Dialog,
|
Dialog,
|
||||||
DismissibleAlert,
|
DismissibleAlert,
|
||||||
DocumentationHelpLink,
|
DocumentationHelpLink,
|
||||||
FormField,
|
FormField,
|
||||||
IconButton,
|
IconButton,
|
||||||
LoadingIndicator,
|
|
||||||
MetricCard,
|
MetricCard,
|
||||||
|
PageScrollViewport,
|
||||||
SegmentedControl,
|
SegmentedControl,
|
||||||
SelectionList,
|
SelectionList,
|
||||||
SelectionListItem,
|
SelectionListItem,
|
||||||
StatePanel,
|
StatePanel,
|
||||||
StatusBadge,
|
StatusBadge,
|
||||||
ToggleSwitch,
|
ToggleSwitch,
|
||||||
|
WorkspaceActionBar,
|
||||||
|
WorkspaceFrame,
|
||||||
hasScope,
|
hasScope,
|
||||||
i18nMessage,
|
i18nMessage,
|
||||||
useUnsavedDraftGuard,
|
useUnsavedDraftGuard,
|
||||||
@@ -248,9 +253,14 @@ export default function RiskCompliancePage({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="risk-page">
|
<WorkspaceFrame as="main" surface="plain" label="Risk Compliance workspace" interfaceId="risk_compliance.workspace" helpContextId="risk_compliance.workspace" helpModuleId="risk_compliance">
|
||||||
<ActionToolbar className="risk-toolbar">
|
<WorkspaceActionBar
|
||||||
<SegmentedControl
|
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}
|
value={view}
|
||||||
onChange={setView}
|
onChange={setView}
|
||||||
ariaLabel="Risk Compliance view"
|
ariaLabel="Risk Compliance view"
|
||||||
@@ -282,7 +292,7 @@ export default function RiskCompliancePage({
|
|||||||
<Scale size={15} />
|
<Scale size={15} />
|
||||||
Review
|
Review
|
||||||
{queue.length > 0 && (
|
{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
|
title: !canReadAssurance ? RISK_COMPLIANCE_I18N.assuranceReadRequired : undefined
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
|
/>}
|
||||||
/>
|
/>
|
||||||
<ToolbarSpacer className="risk-toolbar-spacer" />
|
<PageScrollViewport>
|
||||||
<DocumentationHelpLink reference={RISK_COMPLIANCE_DOCUMENTATION} />
|
<ContentGrid columns={1} className="content-pad">
|
||||||
{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>
|
|
||||||
{(error || notice) && (
|
{(error || notice) && (
|
||||||
<div className="risk-alerts">
|
<div className="risk-alerts">
|
||||||
{error && (
|
{error && (
|
||||||
<DismissibleAlert
|
<DismissibleAlert
|
||||||
tone="danger"
|
tone="danger"
|
||||||
resetKey={error}
|
resetKey={error}
|
||||||
onDismiss={() => setError("")}
|
|
||||||
>
|
>
|
||||||
{error}
|
{error}
|
||||||
</DismissibleAlert>
|
</DismissibleAlert>
|
||||||
@@ -328,14 +329,12 @@ export default function RiskCompliancePage({
|
|||||||
<DismissibleAlert
|
<DismissibleAlert
|
||||||
tone="success"
|
tone="success"
|
||||||
resetKey={notice}
|
resetKey={notice}
|
||||||
onDismiss={() => setNotice("")}
|
|
||||||
>
|
>
|
||||||
{notice}
|
{notice}
|
||||||
</DismissibleAlert>
|
</DismissibleAlert>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="risk-workspace">
|
|
||||||
{view === "sources" && (
|
{view === "sources" && (
|
||||||
<SourcesPane
|
<SourcesPane
|
||||||
available={sourcesAvailable}
|
available={sourcesAvailable}
|
||||||
@@ -385,7 +384,8 @@ export default function RiskCompliancePage({
|
|||||||
onRefresh={refresh}
|
onRefresh={refresh}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</ContentGrid>
|
||||||
|
</PageScrollViewport>
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
open={Boolean(pendingImport)}
|
open={Boolean(pendingImport)}
|
||||||
title="i18n:govoplan-risk-compliance.import_snapshot_title"
|
title="i18n:govoplan-risk-compliance.import_snapshot_title"
|
||||||
@@ -398,7 +398,7 @@ export default function RiskCompliancePage({
|
|||||||
onCancel={() => setPendingImport(null)}
|
onCancel={() => setPendingImport(null)}
|
||||||
onConfirm={() => pendingImport && void importSnapshot(pendingImport)}
|
onConfirm={() => pendingImport && void importSnapshot(pendingImport)}
|
||||||
/>
|
/>
|
||||||
</main>
|
</WorkspaceFrame>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,15 +422,8 @@ function SourcesPane({
|
|||||||
[imported]
|
[imported]
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<section className="risk-source-layout">
|
<ContentGrid columns={2} collapseAt="wide" align="stretch">
|
||||||
<div className="risk-panel">
|
<Card title="Connector evidence" actions={<DocumentationHelpLink reference={RISK_COMPLIANCE_ADMIN_DOCUMENTATION} />}>
|
||||||
<header>
|
|
||||||
<div>
|
|
||||||
<strong>Connector evidence</strong>
|
|
||||||
<span>Immutable acquired source snapshots</span>
|
|
||||||
</div>
|
|
||||||
<DocumentationHelpLink reference={RISK_COMPLIANCE_ADMIN_DOCUMENTATION} />
|
|
||||||
</header>
|
|
||||||
{!available && (
|
{!available && (
|
||||||
<ActionBlockerHint
|
<ActionBlockerHint
|
||||||
tone="info"
|
tone="info"
|
||||||
@@ -472,14 +465,8 @@ function SourcesPane({
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Card>
|
||||||
<div className="risk-panel">
|
<Card title="Screening catalogues">
|
||||||
<header>
|
|
||||||
<div>
|
|
||||||
<strong>Screening catalogues</strong>
|
|
||||||
<span>Normalized, immutable list versions</span>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<div className="risk-list">
|
<div className="risk-list">
|
||||||
{imported.map((item) => (
|
{imported.map((item) => (
|
||||||
<div className="risk-list-row" key={item.id}>
|
<div className="risk-list-row" key={item.id}>
|
||||||
@@ -498,8 +485,8 @@ function SourcesPane({
|
|||||||
<StatePanel size="compact" description="No source snapshot has been imported." />
|
<StatePanel size="compact" description="No source snapshot has been imported." />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Card>
|
||||||
</section>
|
</ContentGrid>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -582,16 +569,10 @@ function ScreenPane({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="risk-screen-layout">
|
<ContentGrid columns={2} collapseAt="wide" align="stretch">
|
||||||
<form className="risk-panel risk-screen-form" onSubmit={submit}>
|
<Card title="New screening" actions={<DocumentationHelpLink reference={RISK_COMPLIANCE_DOCUMENTATION} />}>
|
||||||
<header>
|
<form onSubmit={submit}>
|
||||||
<div>
|
<FormGrid columns={1}>
|
||||||
<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">
|
|
||||||
{!snapshots.length && (
|
{!snapshots.length && (
|
||||||
<ActionBlockerHint
|
<ActionBlockerHint
|
||||||
reason={{
|
reason={{
|
||||||
@@ -653,16 +634,10 @@ function ScreenPane({
|
|||||||
<Play size={16} />
|
<Play size={16} />
|
||||||
Run screening
|
Run screening
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</FormGrid>
|
||||||
</form>
|
</form>
|
||||||
<div className="risk-panel risk-run-result">
|
</Card>
|
||||||
<header>
|
<Card title="Result" actions={run && <StatusBadge status={run.outcome} />}>
|
||||||
<div>
|
|
||||||
<strong>Result</strong>
|
|
||||||
<span>Version-pinned candidate evidence</span>
|
|
||||||
</div>
|
|
||||||
{run && <StatusBadge status={run.outcome} />}
|
|
||||||
</header>
|
|
||||||
{!run && (
|
{!run && (
|
||||||
<StatePanel size="compact" description="Run a screening to inspect the result." />
|
<StatePanel size="compact" description="Run a screening to inspect the result." />
|
||||||
)}
|
)}
|
||||||
@@ -693,8 +668,8 @@ function ScreenPane({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Card>
|
||||||
</section>
|
</ContentGrid>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -785,14 +760,8 @@ function ReviewPane({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="risk-review-layout">
|
<ContentGrid columns={2} collapseAt="wide" align="stretch">
|
||||||
<aside className="risk-panel risk-review-queue">
|
<Card title="Review queue" actions={<StatusBadge status="info" label={String(queue.length)} />}>
|
||||||
<header>
|
|
||||||
<div>
|
|
||||||
<strong>Review queue</strong>
|
|
||||||
<span>{queue.length} candidates need review</span>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<div className="risk-list">
|
<div className="risk-list">
|
||||||
{queue.length > 0 && (
|
{queue.length > 0 && (
|
||||||
<SelectionList label="Review candidates">
|
<SelectionList label="Review candidates">
|
||||||
@@ -817,14 +786,8 @@ function ReviewPane({
|
|||||||
<StatePanel size="compact" description="No candidates currently need review." />
|
<StatePanel size="compact" description="No candidates currently need review." />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</Card>
|
||||||
<div className="risk-panel risk-evidence">
|
<Card title="Candidate evidence" actions={<>
|
||||||
<header>
|
|
||||||
<div>
|
|
||||||
<strong>Candidate evidence</strong>
|
|
||||||
<span>Subject and immutable list entry comparison</span>
|
|
||||||
</div>
|
|
||||||
<div className="risk-header-actions">
|
|
||||||
<DocumentationHelpLink reference={RISK_COMPLIANCE_DOCUMENTATION} />
|
<DocumentationHelpLink reference={RISK_COMPLIANCE_DOCUMENTATION} />
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
@@ -836,14 +799,13 @@ function ReviewPane({
|
|||||||
>
|
>
|
||||||
Record disposition
|
Record disposition
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</>}>
|
||||||
</header>
|
|
||||||
{!detail && (
|
{!detail && (
|
||||||
<StatePanel size="compact" description="Select a candidate from the queue." />
|
<StatePanel size="compact" description="Select a candidate from the queue." />
|
||||||
)}
|
)}
|
||||||
{detail && (
|
{detail && (
|
||||||
<div className="risk-evidence-body">
|
<div className="risk-evidence-body">
|
||||||
<div className="risk-comparison">
|
<ContentGrid columns={2}>
|
||||||
<EvidenceColumn
|
<EvidenceColumn
|
||||||
title="Screening subject"
|
title="Screening subject"
|
||||||
name={detail.subject.primary_name || "Identifier-only"}
|
name={detail.subject.primary_name || "Identifier-only"}
|
||||||
@@ -869,7 +831,7 @@ function ReviewPane({
|
|||||||
(item) => item.value
|
(item) => item.value
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</ContentGrid>
|
||||||
<div className="risk-match-evidence">
|
<div className="risk-match-evidence">
|
||||||
<strong>
|
<strong>
|
||||||
{detail.candidate.score}% ·{" "}
|
{detail.candidate.score}% ·{" "}
|
||||||
@@ -886,7 +848,7 @@ function ReviewPane({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Card>
|
||||||
<Dialog
|
<Dialog
|
||||||
open={dialogOpen}
|
open={dialogOpen}
|
||||||
title="Record screening disposition"
|
title="Record screening disposition"
|
||||||
@@ -957,7 +919,7 @@ function ReviewPane({
|
|||||||
<>
|
<>
|
||||||
<ToggleSwitch
|
<ToggleSwitch
|
||||||
checked={reusable}
|
checked={reusable}
|
||||||
onChange={(event) => setReusable(event.target.checked)}
|
onChange={setReusable}
|
||||||
label="Apply as a time-bounded exception to this subject and list entry"
|
label="Apply as a time-bounded exception to this subject and list entry"
|
||||||
/>
|
/>
|
||||||
{reusable && (
|
{reusable && (
|
||||||
@@ -974,7 +936,7 @@ function ReviewPane({
|
|||||||
)}
|
)}
|
||||||
</form>
|
</form>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</section>
|
</ContentGrid>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1220,8 +1182,8 @@ function AssurancePane({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="risk-assurance-layout">
|
<ContentGrid columns={1}>
|
||||||
<div className="risk-assurance-summary">
|
<ContentGrid columns={1}>
|
||||||
{!canWrite && (
|
{!canWrite && (
|
||||||
<ActionBlockerHint
|
<ActionBlockerHint
|
||||||
tone="info"
|
tone="info"
|
||||||
@@ -1246,15 +1208,9 @@ function AssurancePane({
|
|||||||
tone={(summary?.by_state.open ?? 0) > 0 ? "danger" : "good"}
|
tone={(summary?.by_state.open ?? 0) > 0 ? "danger" : "good"}
|
||||||
/>
|
/>
|
||||||
</MetricGrid>
|
</MetricGrid>
|
||||||
</div>
|
</ContentGrid>
|
||||||
<div className="risk-assurance-columns">
|
<ContentGrid columns={2} collapseAt="wide" align="stretch">
|
||||||
<aside className="risk-panel">
|
<Card title="Assurance objects" actions={<>
|
||||||
<header>
|
|
||||||
<div>
|
|
||||||
<strong>Assurance objects</strong>
|
|
||||||
<span>Current effective revisions</span>
|
|
||||||
</div>
|
|
||||||
<div className="risk-header-actions">
|
|
||||||
<DocumentationHelpLink reference={RISK_COMPLIANCE_DOCUMENTATION} />
|
<DocumentationHelpLink reference={RISK_COMPLIANCE_DOCUMENTATION} />
|
||||||
<IconButton
|
<IconButton
|
||||||
label="Add assurance object"
|
label="Add assurance object"
|
||||||
@@ -1263,9 +1219,8 @@ function AssurancePane({
|
|||||||
disabled={!canWrite || busy}
|
disabled={!canWrite || busy}
|
||||||
disabledReason={!canWrite ? RISK_COMPLIANCE_I18N.assuranceWriteRequired : busy ? RISK_COMPLIANCE_I18N.busy : undefined}
|
disabledReason={!canWrite ? RISK_COMPLIANCE_I18N.assuranceWriteRequired : busy ? RISK_COMPLIANCE_I18N.busy : undefined}
|
||||||
/>
|
/>
|
||||||
</div>
|
</>}>
|
||||||
</header>
|
<FormGrid columns={2} gap="small" spacing="block">
|
||||||
<div className="risk-assurance-filter">
|
|
||||||
<input
|
<input
|
||||||
type="search"
|
type="search"
|
||||||
value={query}
|
value={query}
|
||||||
@@ -1283,7 +1238,7 @@ function AssurancePane({
|
|||||||
<option value={item} key={item}>{formatToken(item)}</option>
|
<option value={item} key={item}>{formatToken(item)}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</FormGrid>
|
||||||
<div className="risk-list">
|
<div className="risk-list">
|
||||||
{visibleNodes.length > 0 && (
|
{visibleNodes.length > 0 && (
|
||||||
<SelectionList label="Assurance objects">
|
<SelectionList label="Assurance objects">
|
||||||
@@ -1307,14 +1262,8 @@ function AssurancePane({
|
|||||||
<StatePanel size="compact" description="No assurance objects match." />
|
<StatePanel size="compact" description="No assurance objects match." />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</Card>
|
||||||
<div className="risk-panel risk-assurance-detail">
|
<Card title={selected?.label || "Assurance object"} actions={<>
|
||||||
<header>
|
|
||||||
<div>
|
|
||||||
<strong>{selected?.label || "Assurance object"}</strong>
|
|
||||||
<span>{selected ? formatToken(selected.kind) : "Select an object"}</span>
|
|
||||||
</div>
|
|
||||||
<div className="risk-header-actions">
|
|
||||||
<DocumentationHelpLink reference={RISK_COMPLIANCE_DOCUMENTATION} />
|
<DocumentationHelpLink reference={RISK_COMPLIANCE_DOCUMENTATION} />
|
||||||
<IconButton
|
<IconButton
|
||||||
label="Edit assurance object"
|
label="Edit assurance object"
|
||||||
@@ -1323,23 +1272,23 @@ function AssurancePane({
|
|||||||
disabled={!selected || !canWrite || selected.stable_id.startsWith("sanctions-") || busy}
|
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}
|
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 && (
|
{!selected && (
|
||||||
<StatePanel size="compact" description="Select an assurance object." />
|
<StatePanel size="compact" description="Select an assurance object." />
|
||||||
)}
|
)}
|
||||||
{selected && (
|
{selected && (
|
||||||
<div className="risk-assurance-detail-body">
|
<div className="risk-assurance-detail-body">
|
||||||
<dl className="risk-assurance-properties">
|
<DescriptionList columns={2}>
|
||||||
<div><dt>State</dt><dd><StatusBadge status={selected.state} /></dd></div>
|
<DescriptionItem term="Type">{formatToken(selected.kind)}</DescriptionItem>
|
||||||
<div><dt>Owner</dt><dd>{selected.owner_ref}</dd></div>
|
<DescriptionItem term="State"><StatusBadge status={selected.state} /></DescriptionItem>
|
||||||
<div><dt>Scope</dt><dd>{selected.scope_ref || "Tenant"}</dd></div>
|
<DescriptionItem term="Owner">{selected.owner_ref}</DescriptionItem>
|
||||||
<div><dt>Valid from</dt><dd>{formatDate(selected.valid_from)}</dd></div>
|
<DescriptionItem term="Scope">{selected.scope_ref || "Tenant"}</DescriptionItem>
|
||||||
|
<DescriptionItem term="Valid from">{formatDate(selected.valid_from)}</DescriptionItem>
|
||||||
{selected.governed_object_ref && (
|
{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>
|
<DescriptionItem term="Classification">{selected.classification}</DescriptionItem>
|
||||||
</dl>
|
</DescriptionList>
|
||||||
{selected.description && <p>{selected.description}</p>}
|
{selected.description && <p>{selected.description}</p>}
|
||||||
<div className="risk-assurance-links-header">
|
<div className="risk-assurance-links-header">
|
||||||
<div>
|
<div>
|
||||||
@@ -1376,8 +1325,8 @@ function AssurancePane({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Card>
|
||||||
</div>
|
</ContentGrid>
|
||||||
<AssuranceNodeDialog
|
<AssuranceNodeDialog
|
||||||
open={nodeDialogOpen}
|
open={nodeDialogOpen}
|
||||||
busy={busy}
|
busy={busy}
|
||||||
@@ -1438,7 +1387,7 @@ function AssurancePane({
|
|||||||
</FormField>
|
</FormField>
|
||||||
</form>
|
</form>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</section>
|
</ContentGrid>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1666,7 +1615,7 @@ function dateTimeLocalValue(value: Date) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function formatToken(value: string) {
|
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({
|
function EvidenceColumn({
|
||||||
|
|||||||
@@ -1,194 +1,114 @@
|
|||||||
.risk-page {
|
/* Domain-specific evidence arrangements only. Workspace geometry, cards,
|
||||||
display: flex;
|
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-width: 0;
|
||||||
min-height: 0;
|
gap: var(--space-1);
|
||||||
height: 100%;
|
|
||||||
flex-direction: column;
|
|
||||||
background: var(--panel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.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;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: var(--space-2);
|
||||||
flex: 0 0 auto;
|
padding-block: var(--space-3);
|
||||||
min-height: 50px;
|
}
|
||||||
|
|
||||||
|
.risk-list-row,
|
||||||
|
.risk-candidate-summary,
|
||||||
|
.risk-assurance-links-header {
|
||||||
border-bottom: var(--border-line);
|
border-bottom: var(--border-line);
|
||||||
background: var(--panel-header);
|
|
||||||
padding: 8px 14px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.risk-toolbar .segmented-control-option {
|
.risk-list-main strong,
|
||||||
gap: 6px;
|
.risk-list-main span,
|
||||||
|
.risk-list-main code {
|
||||||
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
.risk-toolbar-spacer {
|
.risk-list-main span,
|
||||||
flex: 1;
|
.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 {
|
.risk-evidence-values strong {
|
||||||
min-width: 18px;
|
overflow-wrap: anywhere;
|
||||||
border-radius: var(--radius-md);
|
font-weight: 500;
|
||||||
background: var(--accent);
|
|
||||||
color: var(--on-dark);
|
|
||||||
padding: 1px 5px;
|
|
||||||
font-size: 10px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.risk-alerts {
|
.risk-score {
|
||||||
|
display: inline-grid;
|
||||||
|
min-width: 2.5em;
|
||||||
flex: 0 0 auto;
|
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 {
|
.risk-clear {
|
||||||
min-width: 0;
|
color: var(--success);
|
||||||
min-height: 0;
|
|
||||||
flex: 1;
|
|
||||||
overflow: hidden;
|
|
||||||
padding: 14px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.risk-source-layout,
|
.risk-evidence-values,
|
||||||
.risk-screen-layout,
|
.risk-match-evidence {
|
||||||
.risk-review-layout,
|
margin-block-start: var(--space-3);
|
||||||
.risk-assurance-layout {
|
}
|
||||||
|
|
||||||
|
.risk-assurance-detail-body,
|
||||||
|
.risk-assurance-form,
|
||||||
|
.risk-disposition-form {
|
||||||
display: grid;
|
display: grid;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
min-height: 0;
|
gap: var(--space-3);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.risk-assurance-detail-body > p {
|
.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;
|
margin: 0;
|
||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
color: var(--text-strong);
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.risk-assurance-links-header {
|
.risk-assurance-links-header {
|
||||||
display: flex;
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
min-height: 52px;
|
|
||||||
border-bottom: var(--border-line);
|
|
||||||
padding: 8px 12px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.risk-assurance-links-header > div {
|
.risk-assurance-links-header > div {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 2px;
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
gap: var(--space-1);
|
||||||
|
|
||||||
.risk-assurance-links-header span {
|
|
||||||
color: var(--muted);
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.risk-assurance-links {
|
|
||||||
min-height: 0;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.risk-assurance-links > button {
|
.risk-assurance-links > button {
|
||||||
display: grid;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
grid-template-columns: minmax(110px, 0.45fr) minmax(0, 1fr) auto;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: var(--space-2);
|
||||||
border: 0;
|
border: 0;
|
||||||
border-bottom: var(--border-line);
|
border-bottom: var(--border-line);
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
padding: 9px 12px;
|
padding: var(--space-3) var(--space-1);
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font: inherit;
|
font: inherit;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -198,296 +118,15 @@
|
|||||||
background: var(--sidebar-hover-bg);
|
background: var(--sidebar-hover-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.risk-assurance-links > button span {
|
.risk-assurance-links > button strong {
|
||||||
color: var(--muted);
|
flex: 1;
|
||||||
font-size: 11px;
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
.risk-assurance-links > button strong {
|
.risk-assurance-links > button span {
|
||||||
overflow: hidden;
|
color: var(--muted);
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.risk-assurance-truncated {
|
.risk-assurance-truncated {
|
||||||
border-top: var(--border-line);
|
|
||||||
color: var(--warning);
|
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