Files
craftvia/src/components/asset-form.tsx
T
msolarczekandClaude Opus 5 c8e6f30a27
CI / build-and-check (push) Canceled after 0s
CI / audit (push) Canceled after 0s
CI / sbom (push) Canceled after 0s
Basis: Certvia dev@a48c5fb als Fundament für Craftvia
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>
2026-09-14 11:05:39 +02:00

130 lines
4.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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>
);
}