From 2706424c08a590dd78f98a8e4a88d21206742a87 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 6 Jul 2026 13:55:46 +0200 Subject: [PATCH] =?UTF-8?q?Lieferanten-/IT-Service-Cockpit=20direkt=20auf?= =?UTF-8?q?=20der=20Asset-Seite=20=C3=B6ffnen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Statt beim Klick auf ein Lieferanten-/IT-Service-Asset zur Lieferanten-Seite zu wechseln, wird das Fach-Cockpit jetzt als Popup auf /assets gerendert; Schließen, Bearbeiten, Speichern und Löschen bleiben auf der Asset-Seite. - backHref-Prop an SupplierDetail/Edit- und ServiceDetail/Edit-Modal: alle Schließen-/Bearbeiten-/Abbrechen-Links leiten sich davon ab (Default /suppliers) - withQuery-Helper (lib/supplier) für korrekte ?/&-Verkettung - updateSupplier/updateService lesen returnTo (verstecktes Feld) und leiten dorthin zurück; deleteSupplier/deleteService bekommen returnTo-Parameter - Kind-Actions revalidieren zusätzlich /assets, damit das Popup dort aktuell bleibt - SUPPLIER_INCLUDE/SERVICE_INCLUDE in lib/supplier-include ausgelagert und von beiden Seiten genutzt - assets/page.tsx bestimmt die Popup-Art vorab (supplier/service/asset) und rendert das passende Cockpit mit backHref="/assets"; profillose Alt-Assets bleiben Assets Co-Authored-By: Claude Opus 4.8 --- src/app/(app)/assets/page.tsx | 132 ++++++++++++++++++----------- src/app/(app)/suppliers/page.tsx | 24 +----- src/components/service-modals.tsx | 23 ++--- src/components/supplier-modals.tsx | 27 +++--- src/lib/supplier-include.ts | 29 +++++++ src/lib/supplier.ts | 9 ++ src/server/actions/services.ts | 11 ++- src/server/actions/suppliers.ts | 15 +++- 8 files changed, 172 insertions(+), 98 deletions(-) create mode 100644 src/lib/supplier-include.ts diff --git a/src/app/(app)/assets/page.tsx b/src/app/(app)/assets/page.tsx index 808722a..737e1a4 100644 --- a/src/app/(app)/assets/page.tsx +++ b/src/app/(app)/assets/page.tsx @@ -1,5 +1,4 @@ import Link from "next/link"; -import { redirect } from "next/navigation"; import { getTranslations } from "next-intl/server"; import { Plus, Search } from "lucide-react"; import type { AssetType } from "@prisma/client"; @@ -22,6 +21,17 @@ import { AssetDetailModal, AssetEditModal, } from "@/components/asset-modals"; +import { + SupplierDetailModal, + SupplierEditModal, + type SupplierAssetDetail, +} from "@/components/supplier-modals"; +import { + ServiceDetailModal, + ServiceEditModal, + type ServiceAssetDetail, +} from "@/components/service-modals"; +import { SUPPLIER_INCLUDE, SERVICE_INCLUDE } from "@/lib/supplier-include"; import { Table, TableBody, @@ -35,22 +45,6 @@ const ASSET_TYPES = ["INFORMATION", "SYSTEM", "APPLICATION", "LOCATION", "SUPPLI const STATUS_TONE = { ACTIVE: "ok", PLANNED: "info", RETIRED: "mut" } as const; -/** - * Lieferanten- und IT-Service-Assets werden in ihrem Fach-Cockpit geöffnet - * (sofern ein Fachprofil existiert), alle übrigen Assets in der normalen - * Asset-Detailansicht. - */ -function assetDetailHref(asset: { - id: string; - type: AssetType; - supplierProfile: { id: string } | null; - serviceProfile: { id: string } | null; -}) { - if (asset.type === "SUPPLIER" && asset.supplierProfile) return `/suppliers?detail=${asset.id}`; - if (asset.type === "IT_SERVICE" && asset.serviceProfile) return `/suppliers?tab=services&detail=${asset.id}`; - return `/assets?detail=${asset.id}`; -} - export default async function AssetsPage({ searchParams, }: { @@ -103,43 +97,75 @@ export default async function AssetsPage({ // Popup-Zustand aus searchParams: ?detail= (read-only), ?edit=, ?new=1 const modalId = params.edit && canWrite ? params.edit : params.detail; - const modalAsset = modalId + const isEdit = Boolean(params.edit && canWrite); + + // Art des Popups vorab bestimmen: Lieferanten & IT-Services mit Fachprofil + // werden direkt hier als Fach-Cockpit gerendert (backHref="/assets"), sodass + // man auf der Asset-Seite bleibt; profillose Alt-Assets bleiben normale Assets. + const modalMeta = modalId ? await db.asset.findUnique({ where: { id: modalId }, - include: { - owner: { select: { name: true } }, + select: { + type: true, supplierProfile: { select: { id: true } }, serviceProfile: { select: { id: true } }, - relationsFrom: { - include: { - relatedAsset: { - select: { - id: true, - name: true, - type: true, - confidentiality: true, - integrity: true, - availability: true, - }, - }, - }, - }, - relationsTo: { include: { asset: { select: { id: true, name: true } } } }, - processAssets: { include: { process: { select: { id: true, name: true } } } }, - riskAssets: { - include: { risk: { select: { id: true, refNo: true, title: true, score: true } } }, - orderBy: { risk: { score: "desc" } }, - }, }, }) : null; + const modalKind: "supplier" | "service" | "asset" | null = !modalMeta + ? null + : modalMeta.type === "SUPPLIER" && modalMeta.supplierProfile + ? "supplier" + : modalMeta.type === "IT_SERVICE" && modalMeta.serviceProfile + ? "service" + : "asset"; - // Lieferanten/IT-Services im Fach-Cockpit öffnen, auch bei direktem Aufruf - // (nur wenn ein Fachprofil existiert; profillose Alt-Assets bleiben im Asset-Detail) - if (modalAsset && ((modalAsset.type === "SUPPLIER" && modalAsset.supplierProfile) || (modalAsset.type === "IT_SERVICE" && modalAsset.serviceProfile))) { - const base = modalAsset.type === "IT_SERVICE" ? "/suppliers?tab=services" : "/suppliers"; - redirect(`${base}${base.includes("?") ? "&" : "?"}${params.edit && canWrite ? "edit" : "detail"}=${modalAsset.id}`); - } + const supplierModal = + modalKind === "supplier" + ? ((await db.asset.findUnique({ where: { id: modalId! }, include: SUPPLIER_INCLUDE })) as SupplierAssetDetail | null) + : null; + const serviceModal = + modalKind === "service" + ? ((await db.asset.findUnique({ where: { id: modalId! }, include: SERVICE_INCLUDE })) as ServiceAssetDetail | null) + : null; + const providers = + modalKind === "service" && isEdit + ? await db.asset.findMany({ + where: { type: "SUPPLIER", supplierProfile: { isNot: null } }, + select: { id: true, name: true }, + orderBy: { name: "asc" }, + }) + : []; + + const modalAsset = + modalKind === "asset" + ? await db.asset.findUnique({ + where: { id: modalId! }, + include: { + owner: { select: { name: true } }, + relationsFrom: { + include: { + relatedAsset: { + select: { + id: true, + name: true, + type: true, + confidentiality: true, + integrity: true, + availability: true, + }, + }, + }, + }, + relationsTo: { include: { asset: { select: { id: true, name: true } } } }, + processAssets: { include: { process: { select: { id: true, name: true } } } }, + riskAssets: { + include: { risk: { select: { id: true, refNo: true, title: true, score: true } } }, + orderBy: { risk: { score: "desc" } }, + }, + }, + }) + : null; const users = canWrite && (params.edit || params.new) @@ -247,7 +273,7 @@ export default async function AssetsPage({ {assets.map((asset) => ( - + {asset.name} @@ -274,7 +300,17 @@ export default async function AssetsPage({ - {modalAsset && params.edit && canWrite ? ( + {/* Lieferanten- & IT-Service-Cockpits werden hier direkt gerendert + (backHref="/assets"), damit man auf der Asset-Seite bleibt. */} + {supplierModal && isEdit ? ( + + ) : supplierModal ? ( + + ) : serviceModal && isEdit ? ( + + ) : serviceModal ? ( + + ) : modalAsset && isEdit ? ( ) : modalAsset ? ( diff --git a/src/app/(app)/suppliers/page.tsx b/src/app/(app)/suppliers/page.tsx index 55ffd84..d28ddac 100644 --- a/src/app/(app)/suppliers/page.tsx +++ b/src/app/(app)/suppliers/page.tsx @@ -31,6 +31,7 @@ import { CONFORMITY_LABEL, TARGET_MATURITY, } from "@/lib/supplier"; +import { SUPPLIER_INCLUDE, SERVICE_INCLUDE } from "@/lib/supplier-include"; import { Table, TableBody, @@ -40,29 +41,6 @@ import { TableRow, } from "@/components/ui/table"; -const SUPPLIER_INCLUDE = { - supplierProfile: true, - contracts: true, - ndas: true, - evidence: true, - assessments: true, - subcontractors: true, - decisions: true, - maturity: true, - riskAssets: { include: { risk: { select: { id: true, refNo: true, title: true, score: true } } } }, - relationsFrom: { include: { relatedAsset: { select: { name: true, confidentiality: true, integrity: true, availability: true } } } }, - relationsTo: { include: { asset: { select: { name: true, confidentiality: true, integrity: true, availability: true } } } }, -} as const; - -const SERVICE_INCLUDE = { - serviceProfile: { include: { provider: { select: { id: true, name: true } } } }, - raci: { orderBy: { controlRef: "asc" } }, - riskAssets: { include: { risk: { select: { id: true, refNo: true, title: true, score: true } } } }, - relationsFrom: { include: { relatedAsset: { select: { id: true, name: true, type: true, confidentiality: true, integrity: true, availability: true } } } }, - relationsTo: { include: { asset: { select: { id: true, name: true } } } }, - processAssets: { include: { process: { select: { id: true, name: true } } } }, -} as const; - export default async function SuppliersPage({ searchParams, }: { diff --git a/src/components/service-modals.tsx b/src/components/service-modals.tsx index 497d765..a570a27 100644 --- a/src/components/service-modals.tsx +++ b/src/components/service-modals.tsx @@ -17,7 +17,7 @@ import { Textarea } from "@/components/ui/textarea"; import { Modal } from "@/components/modal"; import { SegmentedRating } from "@/components/segmented-rating"; import { CiaBadge, CiaLegend, CriticalityPill, Pill, Tag } from "@/components/mockup-ui"; -import { serviceRef } from "@/lib/supplier"; +import { serviceRef, withQuery } from "@/lib/supplier"; import { riskLevel, riskRef, RISK_PILL_TONE } from "@/lib/risk"; import { ISA_CONTROLS } from "@/lib/isa-controls"; @@ -37,7 +37,7 @@ const RACI_TONE = { PROVIDER: "info", US: "violet", SHARED: "warn" } as const; /* ─────────────────────── Detail-Cockpit ─────────────────────── */ -export async function ServiceDetailModal({ service, canWrite }: { service: ServiceAssetDetail; canWrite: boolean }) { +export async function ServiceDetailModal({ service, canWrite, backHref = "/suppliers?tab=services" }: { service: ServiceAssetDetail; canWrite: boolean; backHref?: string }) { const t = await getTranslations("services"); const ta = await getTranslations("assets"); const tParty = await getTranslations("raciParty"); @@ -60,16 +60,16 @@ export async function ServiceDetailModal({ service, canWrite }: { service: Servi } - closeHref="/suppliers?tab=services" + closeHref={backHref} closeLabel={tp("close")} footer={ <> {canWrite && ( - )} - + } > @@ -334,10 +334,11 @@ export async function ServiceCreateModal({ providers }: { providers: { id: strin ); } -export async function ServiceEditModal({ service, providers }: { service: ServiceAssetDetail; providers: { id: string; name: string }[] }) { +export async function ServiceEditModal({ service, providers, backHref = "/suppliers?tab=services" }: { service: ServiceAssetDetail; providers: { id: string; name: string }[]; backHref?: string }) { const t = await getTranslations("services"); const tc = await getTranslations("common"); const FORM = "service-edit"; + const detailHref = withQuery(backHref, "detail", service.id); return (
-
+
} - closeHref={`/suppliers?tab=services&detail=${service.id}`} + closeHref={detailHref} closeLabel={t("close")} footer={ <> - + } > -
+ + +
diff --git a/src/components/supplier-modals.tsx b/src/components/supplier-modals.tsx index e529ee3..81c2784 100644 --- a/src/components/supplier-modals.tsx +++ b/src/components/supplier-modals.tsx @@ -25,6 +25,7 @@ import { Pill } from "@/components/mockup-ui"; import { SupplierRequirements } from "@/components/supplier-cockpit"; import { supplierRef, + withQuery, SUPPLIER_LIFECYCLE_TONE, protectionLevel, PROTECTION_LABEL, @@ -80,7 +81,7 @@ function contextOf(s: SupplierAssetDetail) { /* ─────────────────────── Cockpit (Detail) ─────────────────────── */ -export async function SupplierDetailModal({ supplier, canWrite }: { supplier: SupplierAssetDetail; canWrite: boolean }) { +export async function SupplierDetailModal({ supplier, canWrite, backHref = "/suppliers" }: { supplier: SupplierAssetDetail; canWrite: boolean; backHref?: string }) { const t = await getTranslations("suppliers"); const tLife = await getTranslations("supplierStatus"); const tEKind = await getTranslations("evidenceKind"); @@ -94,8 +95,9 @@ export async function SupplierDetailModal({ supplier, canWrite }: { supplier: Su const computed = computedMaturity(level, ctx); const reqs = REQUIREMENTS.map((r) => ({ key: r.key, label: r.label, control: r.control, tier: r.tier, theme: r.theme, met: r.met(ctx) })); - const gateEvidenceHref = `/suppliers?edit=${supplier.id}`; - const gateDecisionHref = `/suppliers?edit=${supplier.id}`; + const editHref = withQuery(backHref, "edit", supplier.id); + const gateEvidenceHref = editHref; + const gateDecisionHref = editHref; return ( {CONFORMITY_LABEL[conf]} } - closeHref="/suppliers" + closeHref={backHref} closeLabel={t("close")} footer={ <> {canWrite && ( - )} - + } > @@ -311,12 +313,13 @@ export async function SupplierCreateModal() { /* ─────────────────────── Bearbeiten ─────────────────────── */ -export async function SupplierEditModal({ supplier }: { supplier: SupplierAssetDetail }) { +export async function SupplierEditModal({ supplier, backHref = "/suppliers" }: { supplier: SupplierAssetDetail; backHref?: string }) { const t = await getTranslations("suppliers"); const tc = await getTranslations("common"); const tEKind = await getTranslations("evidenceKind"); const tAType = await getTranslations("assessmentType"); const FORM = "supplier-edit"; + const detailHref = withQuery(backHref, "detail", supplier.id); const disc = "cursor-pointer list-none rounded-lg border border-[var(--panel-brd)] bg-[var(--elevated)] px-3 py-1.5 text-[12.5px] font-semibold text-muted-foreground hover:text-foreground [&::-webkit-details-marker]:hidden"; @@ -328,22 +331,24 @@ export async function SupplierEditModal({ supplier }: { supplier: SupplierAssetD
-
+
} - closeHref={`/suppliers?detail=${supplier.id}`} + closeHref={detailHref} closeLabel={t("close")} footer={ <> - + } > -
+ + +
diff --git a/src/lib/supplier-include.ts b/src/lib/supplier-include.ts new file mode 100644 index 0000000..b0529eb --- /dev/null +++ b/src/lib/supplier-include.ts @@ -0,0 +1,29 @@ +import type { Prisma } from "@prisma/client"; + +/** + * Gemeinsame Prisma-Includes für das Lieferanten- und IT-Service-Cockpit. + * Werden sowohl auf /suppliers als auch auf /assets verwendet, damit die + * Fach-Popups an beiden Stellen mit identischer Datenform gerendert werden. + */ +export const SUPPLIER_INCLUDE = { + supplierProfile: true, + contracts: true, + ndas: true, + evidence: true, + assessments: true, + subcontractors: true, + decisions: true, + maturity: true, + riskAssets: { include: { risk: { select: { id: true, refNo: true, title: true, score: true } } } }, + relationsFrom: { include: { relatedAsset: { select: { name: true, confidentiality: true, integrity: true, availability: true } } } }, + relationsTo: { include: { asset: { select: { name: true, confidentiality: true, integrity: true, availability: true } } } }, +} satisfies Prisma.AssetInclude; + +export const SERVICE_INCLUDE = { + serviceProfile: { include: { provider: { select: { id: true, name: true } } } }, + raci: { orderBy: { controlRef: "asc" } }, + riskAssets: { include: { risk: { select: { id: true, refNo: true, title: true, score: true } } } }, + relationsFrom: { include: { relatedAsset: { select: { id: true, name: true, type: true, confidentiality: true, integrity: true, availability: true } } } }, + relationsTo: { include: { asset: { select: { id: true, name: true } } } }, + processAssets: { include: { process: { select: { id: true, name: true } } } }, +} satisfies Prisma.AssetInclude; diff --git a/src/lib/supplier.ts b/src/lib/supplier.ts index c5ab3d6..3cff9ce 100644 --- a/src/lib/supplier.ts +++ b/src/lib/supplier.ts @@ -11,6 +11,15 @@ export function serviceRef(refNo: number): string { return `S-${String(refNo).padStart(3, "0")}`; } +/** + * Hängt einen Query-Parameter an einen Basis-Pfad an und wählt automatisch + * das richtige Trennzeichen (`?` oder `&`). Erlaubt, Detail-/Bearbeiten-Popups + * sowohl auf /suppliers als auch auf /assets zu öffnen (backHref-Muster). + */ +export function withQuery(base: string, key: string, val: string): string { + return `${base}${base.includes("?") ? "&" : "?"}${key}=${val}`; +} + export const SUPPLIER_LIFECYCLE_TONE = { ACTIVE: "ok", ONBOARDING: "info", diff --git a/src/server/actions/services.ts b/src/server/actions/services.ts index 0b327b2..8b8337a 100644 --- a/src/server/actions/services.ts +++ b/src/server/actions/services.ts @@ -7,6 +7,7 @@ import { requireSession } from "@/server/auth"; import { dbForTenant, prisma } from "@/server/db"; import { requirePermission } from "@/server/rbac"; import { writeAuditLog } from "@/server/audit"; +import { withQuery } from "@/lib/supplier"; const level = z.coerce.number().int().min(1).max(4); @@ -73,10 +74,11 @@ export async function updateService(assetId: string, formData: FormData) { await writeAuditLog({ tenantId: session.user.tenantId, actorId: session.user.id, action: "update", entity: "it_service", entityId: assetId, before, after: { ...asset, ...profile } }); revalidatePath("/suppliers"); revalidatePath("/assets"); - redirect(`/suppliers?tab=services&detail=${assetId}`); + const returnTo = (formData.get("returnTo") as string) || "/suppliers?tab=services"; + redirect(withQuery(returnTo, "detail", assetId)); } -export async function deleteService(assetId: string) { +export async function deleteService(assetId: string, returnTo: string = "/suppliers?tab=services") { const session = await requireSession(); requirePermission(session, "supplier:write"); const db = dbForTenant(session.user.tenantId); @@ -86,7 +88,7 @@ export async function deleteService(assetId: string) { await writeAuditLog({ tenantId: session.user.tenantId, actorId: session.user.id, action: "delete", entity: "it_service", entityId: assetId, before }); revalidatePath("/suppliers"); revalidatePath("/assets"); - redirect("/suppliers?tab=services"); + redirect(returnTo); } export async function addRaci(assetId: string, formData: FormData) { @@ -107,6 +109,7 @@ export async function addRaci(assetId: string, formData: FormData) { }); await writeAuditLog({ tenantId: session.user.tenantId, actorId: session.user.id, action: "create", entity: "service_raci", entityId: assetId }); revalidatePath("/suppliers"); + revalidatePath("/assets"); } /** Verantwortung einer RACI-Zeile per Klick ändern (PROVIDER/US/SHARED durchschalten). */ @@ -120,6 +123,7 @@ export async function cycleRaci(assetId: string, raciId: string) { await db.serviceControlResponsibility.update({ where: { id: raciId }, data: { responsibility: next[row.responsibility] } }); await writeAuditLog({ tenantId: session.user.tenantId, actorId: session.user.id, action: "update", entity: "service_raci", entityId: assetId, after: { responsibility: next[row.responsibility] } }); revalidatePath("/suppliers"); + revalidatePath("/assets"); } export async function deleteRaci(assetId: string, raciId: string) { @@ -129,4 +133,5 @@ export async function deleteRaci(assetId: string, raciId: string) { await db.serviceControlResponsibility.delete({ where: { id: raciId } }); await writeAuditLog({ tenantId: session.user.tenantId, actorId: session.user.id, action: "delete", entity: "service_raci", entityId: assetId }); revalidatePath("/suppliers"); + revalidatePath("/assets"); } diff --git a/src/server/actions/suppliers.ts b/src/server/actions/suppliers.ts index d579f22..3b56b82 100644 --- a/src/server/actions/suppliers.ts +++ b/src/server/actions/suppliers.ts @@ -7,6 +7,7 @@ import { requireSession } from "@/server/auth"; import { dbForTenant, prisma } from "@/server/db"; import { requirePermission } from "@/server/rbac"; import { writeAuditLog } from "@/server/audit"; +import { withQuery } from "@/lib/supplier"; const level = z.coerce.number().int().min(1).max(4); @@ -110,10 +111,11 @@ export async function updateSupplier(assetId: string, formData: FormData) { await writeAuditLog({ tenantId: session.user.tenantId, actorId: session.user.id, action: "update", entity: "supplier", entityId: assetId, before, after: { ...asset, ...profile } }); revalidatePath("/suppliers"); revalidatePath("/assets"); - redirect(`/suppliers?detail=${assetId}`); + const returnTo = (formData.get("returnTo") as string) || "/suppliers"; + redirect(withQuery(returnTo, "detail", assetId)); } -export async function deleteSupplier(assetId: string) { +export async function deleteSupplier(assetId: string, returnTo: string = "/suppliers") { const session = await requireSession(); requirePermission(session, "supplier:write"); const db = dbForTenant(session.user.tenantId); @@ -123,7 +125,7 @@ export async function deleteSupplier(assetId: string) { await writeAuditLog({ tenantId: session.user.tenantId, actorId: session.user.id, action: "delete", entity: "supplier", entityId: assetId, before }); revalidatePath("/suppliers"); revalidatePath("/assets"); - redirect("/suppliers"); + redirect(returnTo); } async function ensure(session: Awaited>, assetId: string) { @@ -154,6 +156,7 @@ export async function addContract(assetId: string, formData: FormData) { }); await writeAuditLog({ tenantId: session.user.tenantId, actorId: session.user.id, action: "create", entity: "contract", entityId: assetId }); revalidatePath("/suppliers"); + revalidatePath("/assets"); } export async function addNda(assetId: string, formData: FormData) { @@ -174,6 +177,7 @@ export async function addNda(assetId: string, formData: FormData) { }); await writeAuditLog({ tenantId: session.user.tenantId, actorId: session.user.id, action: "create", entity: "nda", entityId: assetId }); revalidatePath("/suppliers"); + revalidatePath("/assets"); } export async function addEvidence(assetId: string, formData: FormData) { @@ -193,6 +197,7 @@ export async function addEvidence(assetId: string, formData: FormData) { }); await writeAuditLog({ tenantId: session.user.tenantId, actorId: session.user.id, action: "create", entity: "supplier_evidence", entityId: assetId }); revalidatePath("/suppliers"); + revalidatePath("/assets"); } export async function addAssessment(assetId: string, formData: FormData) { @@ -212,6 +217,7 @@ export async function addAssessment(assetId: string, formData: FormData) { }); await writeAuditLog({ tenantId: session.user.tenantId, actorId: session.user.id, action: "create", entity: "supplier_assessment", entityId: assetId }); revalidatePath("/suppliers"); + revalidatePath("/assets"); } export async function addDecision(assetId: string, formData: FormData) { @@ -230,6 +236,7 @@ export async function addDecision(assetId: string, formData: FormData) { }); await writeAuditLog({ tenantId: session.user.tenantId, actorId: session.user.id, action: "create", entity: "management_decision", entityId: assetId }); revalidatePath("/suppliers"); + revalidatePath("/assets"); } /** Gate-Kompensation: echtes Risiko im Risikomodul anlegen und mit dem Lieferanten verknüpfen. */ @@ -282,6 +289,7 @@ export async function approveMaturity(assetId: string, computed: number, formDat }); await writeAuditLog({ tenantId: session.user.tenantId, actorId: session.user.id, action: "update", entity: "maturity_assessment", entityId: assetId, after: { computed, isbValue, justification } }); revalidatePath("/suppliers"); + revalidatePath("/assets"); } export async function deleteChild( @@ -303,4 +311,5 @@ export async function deleteChild( await map[kind].delete({ where: { id } }); await writeAuditLog({ tenantId: session.user.tenantId, actorId: session.user.id, action: "delete", entity: `supplier_${kind}`, entityId: id }); revalidatePath("/suppliers"); + revalidatePath("/assets"); }