diff --git a/frontend/src/pages/Actions.tsx b/frontend/src/pages/Actions.tsx index 48025c3..b44a473 100644 --- a/frontend/src/pages/Actions.tsx +++ b/frontend/src/pages/Actions.tsx @@ -1,26 +1,6 @@ +import type { ReactNode } from "react"; import { useMemo, useState } from "react"; -import { - Alert, - Box, - Button, - Card, - CardContent, - Chip, - Dialog, - DialogContent, - DialogTitle, - Divider, - FormControl, - InputLabel, - MenuItem, - Select, - Stack, - Tab, - Tabs, - TextField, - Typography, -} from "@mui/material"; -import type { MonitoringMachine, SavedTaskInput } from "../types"; +import type { MonitoringMachine, SavedTask, SavedTaskInput } from "../types"; import { useDeleteTask, useMonitoringSettings, @@ -31,10 +11,63 @@ import { } from "../hooks/useSettings"; import { DialogFooter } from "../components/DialogFooter"; import { HoverEditButton } from "../components/HoverEditButton"; +import { SectionCard } from "../components/SectionCard"; import { SelectionRailCard } from "../components/SelectionRailCard"; +import { Alert, AlertDescription } from "@/components/ui/alert"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent } from "@/components/ui/card"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Separator } from "@/components/ui/separator"; +import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Textarea } from "@/components/ui/textarea"; + +// Radix Select disallows empty-string item values; the "None" option maps to +// this sentinel and converts back to "" at the draft boundary. +const NONE = "__none__"; type ActionTab = "new" | string; +/** Small labeled-field wrapper replacing the MUI `` shell. */ +function FormField({ + label, + htmlFor, + helperText, + children, +}: { + label: string; + htmlFor?: string; + helperText?: string; + children: ReactNode; +}) { + return ( +
+ + {children} + {helperText ? ( +

{helperText}

+ ) : null} +
+ ); +} + function emptyTask(): SavedTaskInput { return { id: null, @@ -59,6 +92,18 @@ function sameTask(a: SavedTaskInput, b: SavedTaskInput) { ); } +function initialFromTask(task: SavedTask): SavedTaskInput { + return { + id: task.id, + name: task.name, + task_type: task.task_type, + content: task.content, + enabled: task.enabled, + default_machine_id: task.default_machine_id, + notes: task.notes, + }; +} + function TaskEditor({ task, machines, @@ -72,101 +117,98 @@ function TaskEditor({ (machine) => machine.id === task.default_machine_id, ); return ( - - - +
+
+

{task.id ? "Edit action" : "New action"} - - - +

+ {task.task_type} + {task.enabled ? "enabled" : "disabled"} {selectedMachine && ( - + {`default: ${selectedMachine.name}`} )} - +
- - onChange({ ...task, name: e.target.value })} - /> - - - Type - - - - Default machine - - - - onChange({ ...task, notes: e.target.value })} - /> - + + onChange({ ...task, name: e.target.value })} + /> + +
+
+ + + +
+
+ + + +
+
+ + onChange({ ...task, notes: e.target.value })} + /> + + onChange({ ...task, content: e.target.value })} helperText={ task.task_type === "python" ? "Python is run as `python3 -c`." : "Shell commands are run through `/bin/sh -c`." } - /> -
- + > +