/** * CreateJailDialog — dialog for creating a new jail configuration file. * * Asks for a config file name and calls ``POST /api/config/jail-files`` on * confirmation, seeding the file with a minimal comment header. */ import { useCallback, useEffect, useState } from "react"; import { Button, Dialog, DialogActions, DialogBody, DialogContent, DialogSurface, DialogTitle, Field, Input, MessageBar, MessageBarBody, Spinner, Text, tokens, } from "@fluentui/react-components"; import { createJailConfigFile } from "../../api/config"; import type { ConfFileCreateRequest } from "../../types/config"; import { ApiError } from "../../api/client"; // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- export interface CreateJailDialogProps { /** Whether the dialog is currently open. */ open: boolean; /** Called when the dialog should close without taking action. */ onClose: () => void; /** Called after the jail config file has been successfully created. */ onCreated: () => void; } // --------------------------------------------------------------------------- // Component // --------------------------------------------------------------------------- /** * Dialog for creating a new jail configuration file at * ``jail.d/{name}.conf``. * * The name field accepts a plain base name (e.g. ``my-custom-jail``); the * backend appends the ``.conf`` extension. * * @param props - Component props. * @returns JSX element. */ export function CreateJailDialog({ open, onClose, onCreated, }: CreateJailDialogProps): React.JSX.Element { const [name, setName] = useState(""); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); // Reset form when the dialog opens. useEffect(() => { if (open) { setName(""); setError(null); } }, [open]); const handleClose = useCallback((): void => { if (submitting) return; onClose(); }, [submitting, onClose]); const handleConfirm = useCallback((): void => { const trimmedName = name.trim(); if (!trimmedName || submitting) return; const req: ConfFileCreateRequest = { name: trimmedName, content: `# ${trimmedName}\n`, }; setSubmitting(true); setError(null); createJailConfigFile(req) .then(() => { onCreated(); }) .catch((err: unknown) => { setError( err instanceof ApiError ? err.message : "Failed to create jail config.", ); }) .finally(() => { setSubmitting(false); }); }, [name, submitting, onCreated]); const canConfirm = name.trim() !== "" && !submitting; return ( { if (!data.open) handleClose(); }}> Create Config Creates a new jail configuration file at{" "} jail.d/<name>.conf. {error !== null && ( {error} )} { setName(d.value); }} placeholder="my-custom-jail" disabled={submitting} /> ); }