import { useForm } from "@tanstack/react-form"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; import { Label } from "@/components/ui/label"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Info } from "lucide-react"; import { useState } from "react"; import { toast } from "sonner"; import { z } from "zod"; import { CommandType } from "@/types/command-registry"; interface CommandRegistryFormProps { onSubmit: (data: CommandRegistryFormData) => Promise; closeDialog?: () => void; initialData?: Partial; title?: string; } export interface CommandRegistryFormData { commandName: string; commandType: CommandType; description?: string; commandContent: string; qos: 0 | 1 | 2; isRetained: boolean; } // Zod validation schema const commandRegistrySchema = z.object({ commandName: z .string() .min(1, "Tên lệnh không được để trống") .min(3, "Tên lệnh phải có ít nhất 3 ký tự") .max(100, "Tên lệnh tối đa 100 ký tự") .trim(), commandType: z.nativeEnum(CommandType, { errorMap: () => ({ message: "Loại lệnh không hợp lệ" }), }), description: z.string().max(500, "Mô tả tối đa 500 ký tự").optional(), commandContent: z .string() .min(1, "Nội dung lệnh không được để trống") .min(5, "Nội dung lệnh phải có ít nhất 5 ký tự") .trim(), qos: z.union([z.literal(0), z.literal(1), z.literal(2)]), isRetained: z.boolean(), }); const QoSLevels = [ { level: 0, name: "At Most Once (Fire and Forget)", description: "Gửi lệnh một lần mà không đảm bảo. Nếu broker hoặc client bị ngắt kết nối, lệnh có thể bị mất. Tốc độ nhanh nhất, tiêu tốn ít tài nguyên.", useCase: "Các lệnh không quan trọng, có thể mất mà không ảnh hưởng", }, { level: 1, name: "At Least Once", description: "Đảm bảo lệnh sẽ được nhận ít nhất một lần. Có thể gửi lại nếu chưa nhận được ACK. Lệnh có thể được nhận nhiều lần.", useCase: "Hầu hết các lệnh bình thường cần đảm bảo gửi thành công", }, { level: 2, name: "Exactly Once", description: "Đảm bảo lệnh được nhận chính xác một lần. Sử dụng bắt tay 4 chiều để đảm bảo độ tin cậy cao nhất. Tốc độ chậm hơn, tiêu tốn nhiều tài nguyên.", useCase: "Các lệnh quan trọng như xóa dữ liệu, thay đổi cấu hình", }, ]; export function CommandRegistryForm({ onSubmit, closeDialog, initialData, title = "Đăng ký Lệnh Mới", }: CommandRegistryFormProps) { const [selectedQoS, setSelectedQoS] = useState( initialData?.qos ?? 0 ); const [isSubmitting, setIsSubmitting] = useState(false); const form = useForm({ defaultValues: { commandName: initialData?.commandName || "", commandType: initialData?.commandType || CommandType.RESTART, description: initialData?.description || "", commandContent: initialData?.commandContent || "", qos: (initialData?.qos || 0) as 0 | 1 | 2, isRetained: initialData?.isRetained || false, }, onSubmit: async ({ value }) => { try { // Validate using Zod const validatedData = commandRegistrySchema.parse(value); setIsSubmitting(true); await onSubmit(validatedData as CommandRegistryFormData); toast.success("Lưu lệnh thành công!"); if (closeDialog) { closeDialog(); } } catch (error: any) { if (error.errors?.length > 0) { toast.error(error.errors[0].message); } else { console.error("Submit error:", error); toast.error("Có lỗi xảy ra khi lưu lệnh!"); } } finally { setIsSubmitting(false); } }, }); return (
{title} Tạo và cấu hình lệnh MQTT mới để điều khiển thiết bị
{ e.preventDefault(); form.handleSubmit(); }} > {/* Tên lệnh */} {(field: any) => (
field.handleChange(e.target.value)} onBlur={field.handleBlur} disabled={isSubmitting} /> {field.state.meta.errors?.length > 0 && (

{String(field.state.meta.errors[0])}

)}

Tên định danh duy nhất cho lệnh này

)}
{/* Loại lệnh */} {(field: any) => (
{field.state.meta.errors?.length > 0 && (

{String(field.state.meta.errors[0])}

)}

Phân loại lệnh để dễ dàng quản lý và tổ chức

)}
{/* Mô tả */} {(field: any) => (