import Select from "@/components/Select"; import { useFishes } from "@/state/use-fish"; import { zodResolver } from "@hookform/resolvers/zod"; import React from "react"; import { Controller, useFieldArray, useForm } from "react-hook-form"; import { Button, FlatList, Modal, Text, TextInput, View } from "react-native"; import { z } from "zod"; interface CreateOrUpdateHaulModalProps { isVisible: boolean; onClose: () => void; haulData?: Model.FishingLogInfo[] | null; } const UNITS = ["con", "kg", "tấn"] as const; type Unit = (typeof UNITS)[number]; const UNITS_OPTIONS = UNITS.map((unit) => ({ label: unit, value: unit.toString(), })); // Zod schema cho 1 dòng cá const fishItemSchema = z.object({ id: z.number().min(1, "Chọn loài cá"), quantity: z .number({ invalid_type_error: "Số lượng phải là số" }) .positive("Số lượng > 0"), unit: z.enum(UNITS, { required_error: "Chọn đơn vị" }), size: z .number({ invalid_type_error: "Kích thước phải là số" }) .positive("Kích thước > 0") .optional(), }); // Schema tổng: mảng các item const formSchema = z.object({ fish: z.array(fishItemSchema).min(1, "Thêm ít nhất 1 loài cá"), }); type FormValues = z.infer; const defaultItem = (): FormValues["fish"][number] => ({ id: -1, quantity: 1, unit: "con", size: undefined, }); const CreateOrUpdateHaulModal: React.FC = ({ isVisible, onClose, haulData, }) => { const [isCreateMode, setIsCreateMode] = React.useState(!haulData); const { control, handleSubmit, formState, watch, reset } = useForm({ resolver: zodResolver(formSchema), defaultValues: { fish: [defaultItem()], }, mode: "onSubmit", }); const { fishSpecies, getFishSpecies } = useFishes(); const { errors } = formState; if (!fishSpecies) { getFishSpecies(); } const { fields, append, remove } = useFieldArray({ control, name: "fish", keyName: "_id", // tránh đụng key }); const onSubmit = (values: FormValues) => { // Map form values to the FishingLogInfo-like shape the user requested const mapped = values.fish.map((f) => { const meta = fishSpecies!.find((x) => x.id === f.id); return { fish_species_id: f.id, fish_name: meta?.name, catch_number: f.quantity, catch_unit: f.unit, fish_size: f.size, fish_rarity: meta?.rarity_level, fish_condition: "", gear_usage: "", } as unknown; // inferred shape — keep as unknown to avoid relying on global types here }); console.log("SUBMIT (FishingLogInfo[]): ", JSON.stringify(mapped, null, 2)); // close modal after submit (you can change this to pass the payload to a parent via prop) onClose(); }; // Initialize / reset form when modal visibility or haulData changes React.useEffect(() => { if (!isVisible) { // when modal closed, clear form to default reset({ fish: [defaultItem()] }); setIsCreateMode(true); return; } // when modal opened, populate based on haulData if (haulData === null) { // explicit null -> start with a single default item reset({ fish: [defaultItem()] }); setIsCreateMode(true); } else if (Array.isArray(haulData) && haulData.length > 0) { // map FishingLogInfo -> form rows const mapped = haulData.map((h) => ({ id: h.fish_species_id ?? -1, quantity: (h.catch_number as number) ?? 1, unit: (h.catch_unit as Unit) ?? (defaultItem().unit as Unit), size: (h.fish_size as number) ?? undefined, })); reset({ fish: mapped as any }); setIsCreateMode(false); } else { // undefined or empty array -> default reset({ fish: [defaultItem()] }); setIsCreateMode(true); } }, [isVisible, haulData, reset]); const renderRow = ({ item, index }: { item: any; index: number }) => { return ( Loài cá #{index + 1} {/* Species dropdown */} ( Tên cá ({ label: unit.label, value: unit.value, }))} value={value} onChange={onChange} placeholder="Chọn đơn vị" /> {errors.fish?.[index]?.unit && ( {errors.fish[index]?.unit?.message as string} )} )} /> {/* Size (optional) */} ( Kích thước (cm) — tùy chọn onChange(t ? Number(t.replace(/,/g, ".")) : undefined) } style={{ padding: 10, borderWidth: 1, borderRadius: 6 }} /> {errors.fish?.[index]?.size && ( {errors.fish[index]?.size?.message as string} )} )} /> {/* Remove row */}