kirche-einfach/frontend/src/pages/WorkflowsPage.tsx

182 lines
5.1 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 { useCallback, useEffect, useState } from "react";
import Form from "@rjsf/mantine";
import validator from "@rjsf/validator-ajv8";
import type { RJSFSchema, UiSchema } from "@rjsf/utils";
import {
Alert,
Button,
Card,
Code,
Select,
Table,
Text,
Textarea,
Title,
} from "@mantine/core";
import { IconAlertCircle, IconPlayerPlay } from "@tabler/icons-react";
import { ApiError, api, type FlowDefinition, type InputSchema, type Run } from "../api";
import StatusBadge from "../components/StatusBadge";
export default function WorkflowsPage() {
const [flows, setFlows] = useState<FlowDefinition[]>([]);
const [flowId, setFlowId] = useState("");
const [schema, setSchema] = useState<InputSchema | null>(null);
const [formData, setFormData] = useState<Record<string, unknown>>({});
const [freeText, setFreeText] = useState("");
const [runs, setRuns] = useState<Run[]>([]);
const [result, setResult] = useState("");
const [error, setError] = useState("");
const [busy, setBusy] = useState(false);
const fail = useCallback((e: unknown) => {
setError(e instanceof ApiError ? `${e.status}: ${e.message}` : String(e));
}, []);
useEffect(() => {
api
.listFlows()
.then((list) => {
setFlows(list);
const first = list[0];
if (first) setFlowId((prev) => prev || first.id);
})
.catch(fail);
}, [fail]);
useEffect(() => {
if (!flowId) return;
setError("");
setResult("");
Promise.all([api.getInputSchema(flowId), api.listRuns(flowId)])
.then(([s, r]) => {
setSchema(s);
setFormData({});
setRuns(r);
})
.catch((e: unknown) => {
// Flows without a deployed version have no schema yet.
if (e instanceof ApiError && e.status === 404) {
setSchema(null);
setRuns([]);
setError("Für diesen Flow ist noch keine Version deployed.");
} else fail(e);
});
}, [flowId, fail]);
async function onRun(input: unknown) {
if (!flowId) return;
setBusy(true);
setError("");
setResult("");
try {
const run = await api.runFlow(flowId, input);
setRuns((prev) => [run, ...prev]);
setResult(run.output?.result ?? run.output?.error ?? run.status);
} catch (e) {
fail(e);
} finally {
setBusy(false);
}
}
return (
<div>
<Title order={2} mb="md">
Workflows
</Title>
{error && (
<Alert color="red" icon={<IconAlertCircle size={18} />} mb="md">
{error}
</Alert>
)}
<Select
label="Flow"
placeholder="— Flow wählen —"
value={flowId}
onChange={(v) => setFlowId(v ?? "")}
data={flows.map((f) => ({ value: f.id, label: f.name }))}
mb="md"
maw={480}
/>
{schema && schema.fallback && (
<Card withBorder padding="lg" mb="md">
<Textarea
label="Freitext-Eingabe"
rows={6}
value={freeText}
onChange={(e) => setFreeText(e.target.value)}
mb="sm"
/>
<Button
leftSection={<IconPlayerPlay size={16} />}
loading={busy}
disabled={!flowId}
onClick={() => void onRun({ text: freeText })}
>
Starten
</Button>
</Card>
)}
{schema && !schema.fallback && (
<Card withBorder padding="lg" mb="md">
<Form
schema={schema.json_schema as RJSFSchema}
uiSchema={schema.ui_schema as UiSchema}
validator={validator}
formData={formData}
onChange={(e) =>
setFormData((e.formData as Record<string, unknown>) ?? {})
}
onSubmit={(e) => void onRun(e.formData)}
>
<Button
type="submit"
leftSection={<IconPlayerPlay size={16} />}
loading={busy}
mt="sm"
>
Starten
</Button>
</Form>
</Card>
)}
{result && (
<Card withBorder padding="lg" mb="md">
<Title order={3} mb="xs">
Ergebnis
</Title>
<Code block>{result}</Code>
</Card>
)}
<Title order={3} mb="xs">
Letzte Läufe
</Title>
{runs.length === 0 ? (
<Text c="dimmed">Noch keine Läufe.</Text>
) : (
<Table striped highlightOnHover>
<Table.Thead>
<Table.Tr>
<Table.Th>Status</Table.Th>
<Table.Th>Mandant</Table.Th>
<Table.Th>Ergebnis</Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{runs.map((r) => (
<Table.Tr key={r.id}>
<Table.Td>
<StatusBadge status={r.status} />
</Table.Td>
<Table.Td>{r.tenant_id}</Table.Td>
<Table.Td>
<Code>{r.output?.result ?? r.output?.error ?? "–"}</Code>
</Table.Td>
</Table.Tr>
))}
</Table.Tbody>
</Table>
)}
</div>
);
}