mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-02 13:53:47 +00:00
changes
This commit is contained in:
@@ -1,27 +1,18 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Loader2 } from "lucide-react";
|
||||
|
||||
import {
|
||||
Modal,
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
Field,
|
||||
FieldContent,
|
||||
FieldLabel,
|
||||
Input,
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
Separator,
|
||||
Switch,
|
||||
TextInput,
|
||||
Textarea,
|
||||
} from "@edr/ui-common";
|
||||
Select,
|
||||
Switch,
|
||||
Stack,
|
||||
Group,
|
||||
Divider,
|
||||
Text,
|
||||
Box,
|
||||
} from "@mantine/core";
|
||||
|
||||
import {
|
||||
RULE_ENGINE_SELECT_NONE,
|
||||
@@ -29,8 +20,6 @@ import {
|
||||
} from "@/pages/ruleEngine/config/resources";
|
||||
import type { RuleEngineRecord } from "@/types/rule-engine";
|
||||
|
||||
import { ruleEngineField, ruleEngineSurface } from "./ruleEngineStyles";
|
||||
|
||||
export interface RuleEngineFormDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
@@ -142,133 +131,174 @@ const RuleEngineFormDialog = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className={ruleEngineSurface.dialog}>
|
||||
<DialogHeader className="space-y-1">
|
||||
<DialogTitle className="text-lg font-semibold">{title}</DialogTitle>
|
||||
<DialogDescription>{description}</DialogDescription>
|
||||
</DialogHeader>
|
||||
<Modal
|
||||
opened={open}
|
||||
onClose={() => onOpenChange(false)}
|
||||
title={title}
|
||||
centered
|
||||
size="md"
|
||||
radius="lg"
|
||||
styles={{
|
||||
header: {
|
||||
paddingBottom: "20px",
|
||||
borderBottom: "1px solid var(--mantine-color-gray-2)",
|
||||
},
|
||||
body: {
|
||||
paddingTop: "24px",
|
||||
paddingBottom: "24px",
|
||||
},
|
||||
title: {
|
||||
fontSize: "1.25rem",
|
||||
fontWeight: 700,
|
||||
color: "var(--mantine-color-gray-9)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Stack gap="lg">
|
||||
<Text size="sm" c="dimmed">
|
||||
{description}
|
||||
</Text>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-1">
|
||||
<div className="max-h-[min(60vh,28rem)] space-y-4 overflow-y-auto pr-1">
|
||||
{fields.map((field) => (
|
||||
<Field key={field.name} orientation="vertical" className="gap-1.5">
|
||||
{field.type === "boolean" ? (
|
||||
<div className={ruleEngineField.switchRow}>
|
||||
<div className="min-w-0">
|
||||
<FieldLabel htmlFor={field.name} className={ruleEngineField.label}>
|
||||
{field.label}
|
||||
</FieldLabel>
|
||||
<p className={ruleEngineField.switchHint}>
|
||||
{Boolean(values[field.name]) ? "Enabled" : "Disabled"}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
id={field.name}
|
||||
checked={Boolean(values[field.name])}
|
||||
onCheckedChange={(checked) => setField(field.name, checked)}
|
||||
<div style={{ maxHeight: "calc(60vh - 150px)", overflowY: "auto", paddingRight: "12px" }}>
|
||||
<Stack gap="lg">
|
||||
{fields.map((field) => (
|
||||
<Box key={field.name}>
|
||||
{field.type === "boolean" ? (
|
||||
<Group
|
||||
justify="space-between"
|
||||
align="center"
|
||||
p="lg"
|
||||
style={{
|
||||
background: "linear-gradient(135deg, var(--mantine-color-blue-0) 0%, var(--mantine-color-cyan-0) 100%)",
|
||||
borderRadius: "12px",
|
||||
border: "1px solid var(--mantine-color-blue-2)",
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<Text size="sm" fw={600} mb="4px">
|
||||
{field.label}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed">
|
||||
{Boolean(values[field.name]) ? "✓ Enabled" : "○ Disabled"}
|
||||
</Text>
|
||||
</div>
|
||||
<Switch
|
||||
checked={Boolean(values[field.name])}
|
||||
onChange={(e) => setField(field.name, e.currentTarget.checked)}
|
||||
size="lg"
|
||||
/>
|
||||
</Group>
|
||||
) : field.type === "select" ? (
|
||||
<Select
|
||||
label={
|
||||
<Group gap="4px">
|
||||
<span>{field.label}</span>
|
||||
{field.required && <span style={{ color: "var(--mantine-color-red-6)" }}>*</span>}
|
||||
</Group>
|
||||
}
|
||||
placeholder={
|
||||
selectOptionsLoading ? "Loading options..." : (field.placeholder ?? "Select an option")
|
||||
}
|
||||
value={resolveSelectValue(field, values)}
|
||||
onChange={(v) =>
|
||||
setField(field.name, v === RULE_ENGINE_SELECT_NONE ? "" : v)
|
||||
}
|
||||
disabled={selectOptionsLoading}
|
||||
data={(field.options ?? []).filter((opt) => opt.value !== "").map((opt) => ({
|
||||
label: opt.label,
|
||||
value: opt.value,
|
||||
}))}
|
||||
searchable
|
||||
clearable
|
||||
size="md"
|
||||
radius="lg"
|
||||
styles={{
|
||||
input: {
|
||||
borderColor: "var(--mantine-color-gray-3)",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<FieldLabel htmlFor={field.name} className={ruleEngineField.label}>
|
||||
{field.label}
|
||||
{field.required ? (
|
||||
<span className={ruleEngineField.requiredMark}> *</span>
|
||||
) : null}
|
||||
</FieldLabel>
|
||||
<FieldContent>
|
||||
{field.type === "select" ? (
|
||||
<Select
|
||||
value={resolveSelectValue(field, values)}
|
||||
onValueChange={(v) =>
|
||||
setField(
|
||||
field.name,
|
||||
v === RULE_ENGINE_SELECT_NONE ? "" : v,
|
||||
)
|
||||
}
|
||||
disabled={selectOptionsLoading}
|
||||
>
|
||||
<SelectTrigger
|
||||
id={field.name}
|
||||
className={ruleEngineField.selectTrigger}
|
||||
>
|
||||
<SelectValue
|
||||
placeholder={
|
||||
selectOptionsLoading
|
||||
? "Loading options..."
|
||||
: (field.placeholder ?? "Select an option")
|
||||
}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent className={ruleEngineField.selectContent}>
|
||||
{(field.options ?? [])
|
||||
.filter((opt) => opt.value !== "")
|
||||
.map((opt) => (
|
||||
<SelectItem key={opt.value} value={opt.value}>
|
||||
{opt.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
) : field.type === "textarea" ? (
|
||||
<Textarea
|
||||
id={field.name}
|
||||
value={String(values[field.name] ?? "")}
|
||||
onChange={(e) => setField(field.name, e.target.value)}
|
||||
placeholder={field.placeholder}
|
||||
className={ruleEngineField.textarea}
|
||||
/>
|
||||
) : (
|
||||
<Input
|
||||
id={field.name}
|
||||
type={
|
||||
field.type === "number"
|
||||
? "number"
|
||||
: field.type === "date"
|
||||
? "date"
|
||||
: "text"
|
||||
}
|
||||
value={String(values[field.name] ?? "")}
|
||||
onChange={(e) => setField(field.name, e.target.value)}
|
||||
placeholder={field.placeholder}
|
||||
className={ruleEngineField.input}
|
||||
required={field.required}
|
||||
/>
|
||||
)}
|
||||
</FieldContent>
|
||||
</>
|
||||
)}
|
||||
</Field>
|
||||
))}
|
||||
) : field.type === "textarea" ? (
|
||||
<Textarea
|
||||
label={
|
||||
<Group gap="4px">
|
||||
<span>{field.label}</span>
|
||||
{field.required && <span style={{ color: "var(--mantine-color-red-6)" }}>*</span>}
|
||||
</Group>
|
||||
}
|
||||
value={String(values[field.name] ?? "")}
|
||||
onChange={(e) => setField(field.name, e.currentTarget.value)}
|
||||
placeholder={field.placeholder}
|
||||
required={field.required}
|
||||
minRows={5}
|
||||
size="md"
|
||||
radius="lg"
|
||||
styles={{
|
||||
input: {
|
||||
borderColor: "var(--mantine-color-gray-3)",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<TextInput
|
||||
label={
|
||||
<Group gap="4px">
|
||||
<span>{field.label}</span>
|
||||
{field.required && <span style={{ color: "var(--mantine-color-red-6)" }}>*</span>}
|
||||
</Group>
|
||||
}
|
||||
type={
|
||||
field.type === "number"
|
||||
? "number"
|
||||
: field.type === "date"
|
||||
? "date"
|
||||
: "text"
|
||||
}
|
||||
value={String(values[field.name] ?? "")}
|
||||
onChange={(e) => setField(field.name, e.currentTarget.value)}
|
||||
placeholder={field.placeholder}
|
||||
required={field.required}
|
||||
size="md"
|
||||
radius="lg"
|
||||
styles={{
|
||||
input: {
|
||||
borderColor: "var(--mantine-color-gray-3)",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
))}
|
||||
</Stack>
|
||||
</div>
|
||||
|
||||
<Separator className="my-4" />
|
||||
<Divider />
|
||||
|
||||
<DialogFooter className="gap-2 sm:gap-2">
|
||||
<Group justify="flex-end" gap="sm">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
className="rounded-md"
|
||||
variant="light"
|
||||
onClick={() => onOpenChange(false)}
|
||||
disabled={isSubmitting}
|
||||
radius="lg"
|
||||
size="md"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="submit" className="rounded-md" disabled={isSubmitting}>
|
||||
{isSubmitting ? (
|
||||
<>
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
Saving...
|
||||
</>
|
||||
) : (
|
||||
"Save"
|
||||
)}
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={isSubmitting}
|
||||
leftSection={isSubmitting && <Loader2 size={18} style={{ animation: "spin 1s linear infinite" }} />}
|
||||
radius="lg"
|
||||
color="blue"
|
||||
size="md"
|
||||
>
|
||||
{isSubmitting ? "Saving..." : "Save"}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</Group>
|
||||
</Stack>
|
||||
</form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user