TTMT.ManageWebGUI/src/components/forms/command-form.tsx

165 lines
5.0 KiB
TypeScript
Raw Normal View History

2025-09-24 16:13:57 +07:00
"use client";
import { useForm } from "@tanstack/react-form";
import { z } from "zod";
import { Textarea } from "@/components/ui/textarea";
2025-12-11 14:29:06 +07:00
import { Label } from "@/components/ui/label";
import { Checkbox } from "@/components/ui/checkbox";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Info } from "lucide-react";
import { useState } from "react";
export interface ShellCommandData {
command: string;
qos: 0 | 1 | 2;
isRetained: boolean;
}
2025-09-24 16:13:57 +07:00
interface ShellCommandFormProps {
2025-09-26 17:56:55 +07:00
command: string;
onCommandChange: (value: string) => void;
2025-12-11 14:29:06 +07:00
qos?: 0 | 1 | 2;
onQoSChange?: (value: 0 | 1 | 2) => void;
isRetained?: boolean;
onIsRetainedChange?: (value: boolean) => void;
2025-09-26 17:56:55 +07:00
disabled?: boolean;
2025-09-24 16:13:57 +07:00
}
2025-12-11 14:29:06 +07:00
const QoSDescriptions = {
0: {
name: "At Most Once (Fire and Forget)",
description:
"Gửi lệnh một lần mà không đảm bảo. Nhanh nhất, tiêu tốn ít tài nguyên.",
},
1: {
name: "At Least Once",
description:
"Đảm bảo lệnh sẽ được nhận ít nhất một lần. Cân bằng giữa tốc độ và độ tin cậy.",
},
2: {
name: "Exactly Once",
description:
"Đảm bảo lệnh được nhận chính xác một lần. Chậm nhất nhưng đáng tin cậy nhất.",
},
};
2025-09-26 17:56:55 +07:00
export function ShellCommandForm({
command,
onCommandChange,
2025-12-11 14:29:06 +07:00
qos = 0,
onQoSChange,
isRetained = false,
onIsRetainedChange,
2025-09-26 17:56:55 +07:00
disabled,
}: ShellCommandFormProps) {
2025-12-11 14:29:06 +07:00
const [selectedQoS, setSelectedQoS] = useState<0 | 1 | 2>(qos);
2025-09-24 16:13:57 +07:00
const form = useForm({
2025-09-26 17:56:55 +07:00
defaultValues: { command },
onSubmit: () => {},
2025-09-24 16:13:57 +07:00
});
2025-12-11 14:29:06 +07:00
const handleQoSChange = (value: string) => {
const newQoS = Number(value) as 0 | 1 | 2;
setSelectedQoS(newQoS);
onQoSChange?.(newQoS);
};
const handleRetainedChange = (checked: boolean) => {
onIsRetainedChange?.(checked);
};
2025-09-24 16:13:57 +07:00
return (
2025-09-26 17:56:55 +07:00
<form
onSubmit={(e) => {
e.preventDefault();
form.handleSubmit();
}}
className="space-y-5"
>
2025-12-11 14:29:06 +07:00
{/* Command Input */}
2025-09-26 17:56:55 +07:00
<form.Field
name="command"
validators={{
onChange: ({ value }: { value: string }) => {
const schema = z
.string()
.min(1, "Nhập command để thực thi")
.max(500, "Command quá dài");
const result = schema.safeParse(value);
if (!result.success) {
return result.error.issues.map((i) => i.message);
}
return [];
},
}}
children={(field) => (
2025-12-11 14:29:06 +07:00
<div className="w-full space-y-2">
<Label>Nội Dung Lệnh *</Label>
2025-09-26 17:56:55 +07:00
<Textarea
2025-12-11 14:29:06 +07:00
className="w-full h-[20vh] font-mono"
placeholder="VD: shutdown /s /t 60 /c 'Máy sẽ tắt trong 60 giây'"
2025-09-26 17:56:55 +07:00
value={field.state.value}
onChange={(e) => {
field.handleChange(e.target.value);
onCommandChange(e.target.value);
2025-09-24 16:13:57 +07:00
}}
2025-09-26 17:56:55 +07:00
disabled={disabled}
2025-09-24 16:13:57 +07:00
/>
2025-09-26 17:56:55 +07:00
{field.state.meta.errors?.length > 0 && (
<p className="text-sm text-red-500">
{String(field.state.meta.errors[0])}
</p>
)}
</div>
)}
/>
2025-12-11 14:29:06 +07:00
{/* QoS Selection */}
<div className="space-y-2">
<Label>QoS (Quality of Service) *</Label>
<select
value={selectedQoS}
onChange={(e) => handleQoSChange(e.target.value)}
disabled={disabled}
className="w-full h-10 rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary"
>
<option value="0">QoS 0 - At Most Once (Tốc đ cao)</option>
<option value="1">QoS 1 - At Least Once (Cân bằng)</option>
<option value="2">QoS 2 - Exactly Once (Đ tin cậy cao)</option>
</select>
{/* QoS Description */}
<Alert className="border-l-4 border-l-blue-500 bg-blue-50 mt-2">
<Info className="h-4 w-4 text-blue-600" />
<AlertDescription className="text-sm text-blue-800 mt-1">
<div className="font-semibold">
{QoSDescriptions[selectedQoS].name}
</div>
<div className="mt-1">{QoSDescriptions[selectedQoS].description}</div>
</AlertDescription>
</Alert>
</div>
{/* Retained Checkbox */}
<div className="flex items-center gap-3 rounded-lg border p-4">
<Checkbox
id="retained"
checked={isRetained}
onCheckedChange={handleRetainedChange}
disabled={disabled}
/>
<div className="flex-1">
<Label htmlFor="retained" className="text-base cursor-pointer">
Lưu giữ lệnh (Retained)
</Label>
<p className="text-sm text-muted-foreground mt-1">
Broker MQTT sẽ lưu lệnh này gửi cho client mới khi kết nối. Hữu ích
cho các lệnh cấu hình cần duy trì trạng thái.
</p>
</div>
</div>
2025-09-26 17:56:55 +07:00
</form>
2025-09-24 16:13:57 +07:00
);
}