Basis: Certvia dev@a48c5fb als Fundament für Craftvia
CI / build-and-check (push) Canceled after 0s
CI / audit (push) Canceled after 0s
CI / sbom (push) Canceled after 0s

Unveränderter Stand von certvia/dev (a48c5fb) plus Craftvia-Spezifikation
und Brandbook unter docs/craftvia/. ISMS-Module werden im Folgecommit entfernt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-14 11:05:39 +02:00
co-authored by Claude Opus 5
commit c8e6f30a27
720 changed files with 140143 additions and 0 deletions
+129
View File
@@ -0,0 +1,129 @@
import Link from "next/link";
import { getTranslations } from "next-intl/server";
import type { Asset, AssetType } from "@prisma/client";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { SegmentedRating } from "@/components/segmented-rating";
const ASSET_TYPES = ["INFORMATION", "SYSTEM", "APPLICATION", "LOCATION", "SUPPLIER", "IT_SERVICE", "PERSON", "DATA"] as const;
const ASSET_STATUS = ["ACTIVE", "PLANNED", "RETIRED"] as const;
/** Formular für Anlegen/Bearbeiten eines Assets (Server-Action wird übergeben). */
export async function AssetForm({
action,
asset,
users,
cancelHref,
defaultType = "SYSTEM",
}: {
action: (formData: FormData) => Promise<void>;
asset?: Asset;
users: { id: string; name: string }[];
cancelHref: string;
/** Vorbelegter Typ beim Anlegen (z. B. INFORMATION im BIA-Popup Schritt 1). */
defaultType?: AssetType;
}) {
const t = await getTranslations("assets");
const tType = await getTranslations("assetType");
const tStatus = await getTranslations("assetStatus");
const tLevel = await getTranslations("protectionLevel");
const tc = await getTranslations("common");
const selectClass =
"h-9 w-full rounded-md border border-input bg-transparent px-3 text-sm";
return (
<form action={action} className="max-w-2xl space-y-4">
<div>
<Label htmlFor="name">{t("name")}</Label>
<Input id="name" name="name" required defaultValue={asset?.name} className="mt-1" />
</div>
<div>
<Label htmlFor="description">{t("description")}</Label>
<Textarea
id="description"
name="description"
rows={3}
defaultValue={asset?.description ?? ""}
className="mt-1"
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<Label htmlFor="type">{t("type")}</Label>
<select id="type" name="type" defaultValue={asset?.type ?? defaultType} className={`${selectClass} mt-1`}>
{ASSET_TYPES.map((v) => (
<option key={v} value={v}>
{tType(v)}
</option>
))}
</select>
</div>
<div>
<Label htmlFor="status">{t("status")}</Label>
<select id="status" name="status" defaultValue={asset?.status ?? "ACTIVE"} className={`${selectClass} mt-1`}>
{ASSET_STATUS.map((v) => (
<option key={v} value={v}>
{tStatus(v)}
</option>
))}
</select>
</div>
<div>
<Label htmlFor="ownerId">{t("owner")}</Label>
<select id="ownerId" name="ownerId" defaultValue={asset?.ownerId ?? ""} className={`${selectClass} mt-1`}>
<option value="">{tc("none")}</option>
{users.map((u) => (
<option key={u.id} value={u.id}>
{u.name}
</option>
))}
</select>
</div>
<div>
<Label htmlFor="location">{t("location")}</Label>
<Input id="location" name="location" defaultValue={asset?.location ?? ""} className="mt-1" />
</div>
</div>
<fieldset>
<legend className="text-sm font-medium">{t("protection")} (1–4)</legend>
<div className="mt-2 grid grid-cols-3 gap-4">
{(
[
["confidentiality", t("confidentiality"), asset?.confidentiality],
["integrity", t("integrity"), asset?.integrity],
["availability", t("availability"), asset?.availability],
] as const
).map(([name, label, value]) => (
<div key={name}>
<Label>{label}</Label>
<div className="mt-1">
<SegmentedRating
name={name}
defaultValue={value ?? 1}
low={tLevel("1")}
high={tLevel("4")}
/>
</div>
</div>
))}
</div>
</fieldset>
<div>
<Label htmlFor="tags">{t("tags")}</Label>
<Input id="tags" name="tags" defaultValue={asset?.tags.join(", ") ?? ""} className="mt-1" />
</div>
<div className="flex gap-2 pt-2">
<Button type="submit">{tc("save")}</Button>
<Button variant="outline" nativeButton={false} render={<Link href={cancelHref} />}>
{tc("cancel")}
</Button>
</div>
</form>
);
}