Iteration 3: Risikoanalyse + Menü-Trennung + Prozesskategorie
- Risiko-Modul (SPEC §4.2): Risk/RiskAsset-Schema mit RLS, laufende Nummer je Mandant (R-001), Brutto-/Rest-Bewertung (5×5), Behandlung, Status, Owner- und Prozess-Bezug, n:m betroffene Assets - /risks im Mockup-Layout: 5×5-Heatmap (Farb-Bänder + R-Chips, Legende, Achsen) und Risikoregister mit Score-Pillen, Behandlungs-Tags, Status - Risiko-Popups nach App-Muster: Read-only-Detail (Bewertungs-Band Brutto vs. Rest, betroffene Assets, Maßnahmen-Platzhalter für Iteration 4), Bearbeiten im Popup (Bewertung, Assets verknüpfen, Löschen), Anlegen; Server-Actions mit Zod, RBAC und Audit-Log - Menü aufgetrennt: eigene Punkte "Asset-Inventar", "Business Impact Analyse" und "Risikoanalyse" (Modul-Tabs entfernt) - Prozesskategorie (Kernprozess/Managementprozess/Unterstützender Prozess): Schema-Feld, Formulare, Listen-Spalte, Popup-Tag - Asset-Detail-Popup optisch ans Prozess-Popup angeglichen (Stammdaten- Karte mit violettem Rand, Abhängigkeiten-Tabelle, Risiken-Band) - "Zugeordnete Risiken" jetzt echt: im Asset-Popup (verknüpfte Risiken) und im Prozess-Popup (direkt + über Assets aggregiert); Dashboard-KPI "Offene Risiken" mit echten Zahlen; Seed mit 4 Beispiel-Risiken Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,8 +5,7 @@ import { requireSession } from "@/server/auth";
|
||||
import { dbForTenant } from "@/server/db";
|
||||
import { hasPermission, requirePermission } from "@/server/rbac";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ModuleTabs } from "@/components/module-tabs";
|
||||
import { CriticalityPill, PageHead, SectTitle } from "@/components/mockup-ui";
|
||||
import { CriticalityPill, PageHead, SectTitle, Tag } from "@/components/mockup-ui";
|
||||
import {
|
||||
ProcessCreateModal,
|
||||
ProcessDetailModal,
|
||||
@@ -51,6 +50,7 @@ export default async function ProcessesPage({
|
||||
const t = await getTranslations("processes");
|
||||
const tAssets = await getTranslations("assets");
|
||||
const tCrit = await getTranslations("criticality");
|
||||
const tCat = await getTranslations("processCategory");
|
||||
const tc = await getTranslations("common");
|
||||
|
||||
const params = await searchParams;
|
||||
@@ -80,6 +80,20 @@ export default async function ProcessesPage({
|
||||
orderBy: { name: "asc" },
|
||||
})
|
||||
: [];
|
||||
// Zugeordnete Risiken: direkt am Prozess ODER an einem seiner Assets
|
||||
const modalRisks = modalProcess
|
||||
? await db.risk.findMany({
|
||||
where: {
|
||||
OR: [
|
||||
{ processId: modalProcess.id },
|
||||
{ riskAssets: { some: { assetId: { in: modalProcess.processAssets.map((pa) => pa.assetId) } } } },
|
||||
],
|
||||
},
|
||||
select: { id: true, refNo: true, title: true, score: true },
|
||||
orderBy: { score: "desc" },
|
||||
})
|
||||
: [];
|
||||
|
||||
const availableAssets =
|
||||
canWrite && params.edit && modalProcess
|
||||
? await db.asset.findMany({
|
||||
@@ -111,13 +125,6 @@ export default async function ProcessesPage({
|
||||
}
|
||||
/>
|
||||
|
||||
<ModuleTabs
|
||||
active="/processes"
|
||||
tabs={[
|
||||
{ href: "/assets", label: tAssets("tabAssets") },
|
||||
{ href: "/processes", label: tAssets("tabProcesses") },
|
||||
]}
|
||||
/>
|
||||
|
||||
<div className="shadow-card mt-4 rounded-xl border bg-card">
|
||||
<div className="p-4 pb-0">
|
||||
@@ -127,6 +134,7 @@ export default async function ProcessesPage({
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>{t("process")}</TableHead>
|
||||
<TableHead>{t("category")}</TableHead>
|
||||
<TableHead>{t("owner")}</TableHead>
|
||||
<TableHead>RTO</TableHead>
|
||||
<TableHead>RPO</TableHead>
|
||||
@@ -137,7 +145,7 @@ export default async function ProcessesPage({
|
||||
<TableBody>
|
||||
{processes.length === 0 && (
|
||||
<TableRow>
|
||||
<TableCell colSpan={6} className="py-8 text-center text-muted-foreground">
|
||||
<TableCell colSpan={7} className="py-8 text-center text-muted-foreground">
|
||||
{t("empty")}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@@ -149,6 +157,9 @@ export default async function ProcessesPage({
|
||||
{process.name}
|
||||
</Link>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Tag>{tCat(process.category)}</Tag>
|
||||
</TableCell>
|
||||
<TableCell className="text-muted-foreground">
|
||||
{process.owner?.name ?? tc("none")}
|
||||
</TableCell>
|
||||
@@ -174,7 +185,7 @@ export default async function ProcessesPage({
|
||||
{modalProcess && params.edit && canWrite ? (
|
||||
<ProcessEditModal process={modalProcess} users={users} availableAssets={availableAssets} />
|
||||
) : modalProcess ? (
|
||||
<ProcessDetailModal process={modalProcess} canWrite={canWrite} />
|
||||
<ProcessDetailModal process={modalProcess} risks={modalRisks} canWrite={canWrite} />
|
||||
) : params.new && canWrite ? (
|
||||
<ProcessCreateModal users={users} />
|
||||
) : null}
|
||||
|
||||
Reference in New Issue
Block a user