feat: Mantine design system with dark mode
This commit is contained in:
parent
6e12720ad6
commit
38d4899b07
18 changed files with 1170 additions and 322 deletions
|
|
@ -1,9 +1,22 @@
|
|||
import { useCallback, useEffect, useState } from "react";
|
||||
import Form from "@rjsf/core";
|
||||
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 { useAuth } from "../auth";
|
||||
import StatusBadge from "../components/StatusBadge";
|
||||
|
||||
export default function WorkflowsPage() {
|
||||
const { me } = useAuth();
|
||||
|
|
@ -70,68 +83,107 @@ export default function WorkflowsPage() {
|
|||
|
||||
return (
|
||||
<div>
|
||||
<h2>Workflows</h2>
|
||||
{!me && <p>Sie sind nicht angemeldet — Läufe nutzen den Standard-Mandanten.</p>}
|
||||
{error && <p style={{ color: "crimson" }}>{error}</p>}
|
||||
<div style={{ display: "flex", gap: 8, marginBottom: 12 }}>
|
||||
<select value={flowId} onChange={(e) => setFlowId(e.target.value)}>
|
||||
<option value="">— Flow wählen —</option>
|
||||
{flows.map((f) => (
|
||||
<option key={f.id} value={f.id}>
|
||||
{f.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<Title order={2} mb="md">
|
||||
Workflows
|
||||
</Title>
|
||||
{!me && (
|
||||
<Alert color="blue" mb="md">
|
||||
Sie sind nicht angemeldet — Läufe nutzen den Standard-Mandanten.
|
||||
</Alert>
|
||||
)}
|
||||
{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 && (
|
||||
<div>
|
||||
<textarea
|
||||
<Card withBorder padding="lg" mb="md">
|
||||
<Textarea
|
||||
label="Freitext-Eingabe"
|
||||
rows={6}
|
||||
style={{ width: "100%", fontFamily: "monospace" }}
|
||||
value={freeText}
|
||||
onChange={(e) => setFreeText(e.target.value)}
|
||||
placeholder="Freitext-Eingabe"
|
||||
mb="sm"
|
||||
/>
|
||||
<button
|
||||
<Button
|
||||
leftSection={<IconPlayerPlay size={16} />}
|
||||
loading={busy}
|
||||
disabled={!flowId}
|
||||
onClick={() => void onRun({ text: freeText })}
|
||||
disabled={!flowId || busy}
|
||||
>
|
||||
{busy ? "Läuft …" : "Starten"}
|
||||
</button>
|
||||
</div>
|
||||
Starten
|
||||
</Button>
|
||||
</Card>
|
||||
)}
|
||||
{schema && !schema.fallback && (
|
||||
<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" disabled={busy}>
|
||||
{busy ? "Läuft …" : "Starten"}
|
||||
</button>
|
||||
</Form>
|
||||
<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 && (
|
||||
<div>
|
||||
<h3>Ergebnis</h3>
|
||||
<pre style={{ whiteSpace: "pre-wrap" }}>{result}</pre>
|
||||
</div>
|
||||
<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>
|
||||
)}
|
||||
<h3>Letzte Läufe</h3>
|
||||
{runs.length === 0 && <p>Noch keine Läufe.</p>}
|
||||
<ul>
|
||||
{runs.map((r) => (
|
||||
<li key={r.id}>
|
||||
<strong>{r.status}</strong> · {r.tenant_id} ·{" "}
|
||||
<code>{r.output?.result ?? r.output?.error ?? "–"}</code>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue