Files
proxdeploy/src/components/formular.tsx
T
florian.wildeandClaude Opus 5.5 86b650e20a feat(server): Verbindungstest mit Fähigkeitserkennung
Der Test liest Version, Cluster, Nodes, Storages, Bridges, VNets und die
effektiven Rechte des Tokens und leitet daraus ab, welcher Vorgang per
API, per SSH oder gar nicht geht – je PVE-Version: Template-Bau per API
erst ab 8.4 mit import-Storage, Snippets und LXC-Exec nur per SSH.

Fehlende Rechte erscheinen einzeln mit Pfad und Zweck; die Rollenliste
in docs/proxmox-rechte.md prüft ein Test gegen den Code. SSH-Host-Keys
werden wie Zertifikate erst nach Bestätigung genutzt, und bestätigt
werden kann nur ein Schlüssel, den der letzte Test gesehen hat.

Statussymbole in der GUI sind jetzt gezeichnet: ◐ und ○ fehlen im
Web-Subset von JetBrains Mono.

Refs #003

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:44:27 +02:00

106 lines
2.7 KiB
TypeScript

import { clsx } from "clsx";
import { StatusSymbol } from "@/components/status";
/**
* Formular-Bausteine: echtes `<label>`, Fehlermeldung per `aria-describedby` verbunden,
* 44 px hoch auf dem Handy.
*/
export const EINGABE_KLASSE =
"bg-panel-hoch border-linie text-text h-11 w-full rounded-lg border px-3 placeholder:text-muted focus:border-akzent disabled:opacity-60 sm:h-10";
export function Feld({
id,
label,
hilfe,
fehler,
children,
className,
}: {
id: string;
label: string;
hilfe?: React.ReactNode;
fehler?: string;
children: React.ReactNode;
className?: string;
}) {
return (
<div className={clsx("flex flex-col gap-1.5", className)}>
<label htmlFor={id} className="text-sm font-medium">
{label}
</label>
{children}
{fehler ? (
<p id={`${id}-fehler`} className="text-fehler text-sm">
<StatusSymbol art="fehler" className="mr-1 inline translate-y-[0.125em]" />
{fehler}
</p>
) : hilfe ? (
<p id={`${id}-hilfe`} className="text-muted text-sm">
{hilfe}
</p>
) : null}
</div>
);
}
/** Verknüpft ein Eingabefeld mit Hilfe- oder Fehlertext. */
export function beschreibung(id: string, fehler?: string, hilfe?: unknown): Record<string, string | boolean> {
if (fehler) return { "aria-describedby": `${id}-fehler`, "aria-invalid": true };
if (hilfe) return { "aria-describedby": `${id}-hilfe` };
return {};
}
export function Schalter({
id,
label,
hilfe,
checked,
onChange,
disabled,
}: {
id: string;
label: string;
hilfe?: React.ReactNode;
checked: boolean;
onChange: (wert: boolean) => void;
disabled?: boolean;
}) {
return (
<div className="flex items-start gap-3">
<input
id={id}
type="checkbox"
checked={checked}
disabled={disabled}
onChange={(ereignis) => onChange(ereignis.target.checked)}
className="accent-akzent mt-0.5 h-5 w-5 shrink-0"
{...(hilfe ? { "aria-describedby": `${id}-hilfe` } : {})}
/>
<div>
<label htmlFor={id} className="text-sm font-medium">
{label}
</label>
{hilfe ? (
<p id={`${id}-hilfe`} className="text-muted text-sm">
{hilfe}
</p>
) : null}
</div>
</div>
);
}
/** Text in JetBrains Mono mit Knopf zum Kopieren (Befehle, Fingerprints, Schlüssel). */
export function Code({ children, className }: { children: string; className?: string }) {
return (
<code
className={clsx(
"bg-grund border-linie block rounded-lg border px-3 py-2 font-mono text-xs break-all sm:text-sm",
className,
)}
>
{children}
</code>
);
}