Assets & Prozesse: Detail/Bearbeiten/Anlegen als Popups

- Wiederverwendbares, serverseitig gerendertes Modal (searchParams-
  gesteuert: ?detail= / ?edit= / ?new=1) — Browser-Zurück schließt,
  kein Client-State nötig
- Assets: Read-only-Detail-Popup (Stammdaten, C/I/A, Abhängigkeiten,
  Prozesse, Risiken-Platzhalter), Bearbeiten-Popup (Formular +
  Abhängigkeiten verwalten + Löschen), Anlegen-Popup
- Prozesse: "Bearbeiten" wechselt jetzt innerhalb des Popups in den
  Editiermodus (Stammdaten, Asset-Zuordnung, BIA, Löschen) statt auf
  die alte Detailseite zu springen; Anlegen ebenfalls als Popup
- Server-Actions leiten auf die Popup-URLs zurück (Speichern im
  Bearbeiten-Popup → Detail-Popup; BIA-/Zuordnungs-Änderungen lassen
  das Popup offen); BIA-Formular remountet nach dem Speichern (key)
- Alte Routen (/assets/[id], /assets/new, /processes/[id], …) leiten
  auf die Popup-URLs um

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Martin
2026-07-02 14:59:21 +02:00
co-authored by Claude Fable 5
parent 9997597663
commit c5a1f0095b
13 changed files with 811 additions and 789 deletions
+48 -4
View File
@@ -16,6 +16,11 @@ import {
Pill,
Tag,
} from "@/components/mockup-ui";
import {
AssetCreateModal,
AssetDetailModal,
AssetEditModal,
} from "@/components/asset-modals";
import {
Table,
TableBody,
@@ -32,7 +37,7 @@ const STATUS_TONE = { ACTIVE: "ok", PLANNED: "info", RETIRED: "mut" } as const;
export default async function AssetsPage({
searchParams,
}: {
searchParams: Promise<{ q?: string; type?: string }>;
searchParams: Promise<{ q?: string; type?: string; detail?: string; edit?: string; new?: string }>;
}) {
const session = await requireSession();
requirePermission(session, "asset:read");
@@ -41,7 +46,8 @@ export default async function AssetsPage({
const tStatus = await getTranslations("assetStatus");
const tc = await getTranslations("common");
const { q, type } = await searchParams;
const params = await searchParams;
const { q, type } = params;
const activeType = ASSET_TYPES.includes(type as AssetType) ? (type as AssetType) : null;
const db = dbForTenant(session.user.tenantId);
@@ -76,6 +82,36 @@ export default async function AssetsPage({
const canWrite = hasPermission(session, "asset:write");
// Popup-Zustand aus searchParams: ?detail= (read-only), ?edit=, ?new=1
const modalId = params.edit && canWrite ? params.edit : params.detail;
const modalAsset = modalId
? await db.asset.findUnique({
where: { id: modalId },
include: {
owner: { select: { name: true } },
relationsFrom: { include: { relatedAsset: { select: { id: true, name: true } } } },
relationsTo: { include: { asset: { select: { id: true, name: true } } } },
processAssets: { include: { process: { select: { id: true, name: true } } } },
},
})
: null;
const users =
canWrite && (params.edit || params.new)
? await db.user.findMany({
where: { status: "ACTIVE" },
select: { id: true, name: true },
orderBy: { name: "asc" },
})
: [];
const otherAssets =
canWrite && params.edit && modalAsset
? await db.asset.findMany({
where: { id: { not: modalAsset.id } },
select: { id: true, name: true },
orderBy: { name: "asc" },
})
: [];
const typeHref = (v: string | null) =>
`/assets${v ? `?type=${v}` : ""}${q ? `${v ? "&" : "?"}q=${encodeURIComponent(q)}` : ""}`;
@@ -94,7 +130,7 @@ export default async function AssetsPage({
{t("export")}
</Button>
{canWrite && (
<Button nativeButton={false} render={<Link href="/assets/new" />}>
<Button nativeButton={false} render={<Link href="/assets?new=1" />}>
<Plus className="size-4" /> {t("newAsset")}
</Button>
)}
@@ -167,7 +203,7 @@ export default async function AssetsPage({
{assets.map((asset) => (
<TableRow key={asset.id}>
<TableCell>
<Link href={`/assets/${asset.id}`} className="font-bold hover:underline">
<Link href={`/assets?detail=${asset.id}`} className="font-bold hover:underline">
{asset.name}
</Link>
</TableCell>
@@ -193,6 +229,14 @@ export default async function AssetsPage({
</TableBody>
</Table>
</div>
{modalAsset && params.edit && canWrite ? (
<AssetEditModal asset={modalAsset} users={users} otherAssets={otherAssets} />
) : modalAsset ? (
<AssetDetailModal asset={modalAsset} canWrite={canWrite} />
) : params.new && canWrite ? (
<AssetCreateModal users={users} />
) : null}
</main>
);
}