"use client"; import { useCallback, useMemo, useState } from "react"; import { CalendarClock, Camera } from "lucide-react"; import { BrapiEntityPage, type BrapiFormField } from "@/components/brapi/BrapiEntityPage"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { createEventRow, createImageRow, deleteEventRow, deleteImageRow, fetchEventImageOptions, fetchEventRows, fetchImageRows, updateEventRow, updateImageRow, } from "./api"; import { ImageFormUpload } from "./components/ImageFormUpload"; import { NONE_SELECT_VALUE, type SelectOption } from "./types"; const eventTypeOptions: SelectOption[] = [ { value: "observation", label: "observation / 观察测量" }, { value: "planting", label: "planting / 播种" }, { value: "fertilizer", label: "fertilizer / 施肥" }, { value: "irrigation", label: "irrigation / 灌溉" }, { value: "tillage", label: "tillage / 耕作整地" }, { value: "chemicals", label: "chemicals / 药剂处理" }, { value: "weeding", label: "weeding / 除草" }, { value: "harvest", label: "harvest / 收获" }, { value: "other", label: "other / 其他" }, ]; const mimeTypeOptions: SelectOption[] = [ { value: "image/jpeg", label: "image/jpeg" }, { value: "image/jpg", label: "image/jpg" }, { value: "image/png", label: "image/png" }, { value: "image/gif", label: "image/gif" }, { value: "image/webp", label: "image/webp" }, { value: "image/bmp", label: "image/bmp" }, ]; const formatDateRange = (start: unknown, end: unknown) => { const startText = String(start ?? "").trim(); const endText = String(end ?? "").trim(); if (!startText && !endText) return "N/A"; if (startText && endText) return `${startText} ~ ${endText}`; return startText || endText; }; const eventTypeLabel = (value: unknown) => { const text = String(value ?? "").trim(); const match = eventTypeOptions.find((option) => option.value === text); return match?.label || text || "N/A"; }; export default function EventImagePage() { const [studyOptions, setStudyOptions] = useState([]); const [observationUnitOptions, setObservationUnitOptions] = useState([]); const [observationOptions, setObservationOptions] = useState([]); const loadOptions = useCallback(async () => { const options = await fetchEventImageOptions(); setStudyOptions(options.studies); setObservationUnitOptions(options.observationUnits); setObservationOptions(options.observations); }, []); const loadEvents = useCallback(async () => { const [, rows] = await Promise.all([loadOptions(), fetchEventRows()]); return rows as unknown as Record[]; }, [loadOptions]); const loadImages = useCallback(async () => { const [, rows] = await Promise.all([loadOptions(), fetchImageRows()]); return rows as unknown as Record[]; }, [loadOptions]); const eventFields = useMemo(() => [ { key: "id", label: "Event ID", type: "text", required: true, placeholder: "event-001" }, { key: "event_type", label: "Event Type", type: "select", required: true, options: eventTypeOptions }, { key: "event_type_db_id", label: "Event Type DbId", type: "text", placeholder: "CO:0000000" }, { key: "study_db_id", label: "Study", type: "select", options: [{ value: NONE_SELECT_VALUE, label: "No study" }, ...studyOptions], }, { key: "start_date", label: "Start Date", type: "date" }, { key: "end_date", label: "End Date", type: "date" }, { key: "observation_unit_db_ids", label: "ObservationUnit IDs", type: "text", placeholder: "ou-plot-001, ou-plot-002", colSpan: 2, }, { key: "event_description", label: "Description", type: "textarea", colSpan: 2 }, ], [studyOptions]); const imageFields = useMemo(() => [ { key: "id", label: "Image ID", type: "text", required: true, readOnly: true, placeholder: "上传图片后自动生成" }, { key: "image_name", label: "Image Name", type: "text", required: true, readOnly: true, placeholder: "上传图片后自动填写" }, { key: "image_file_name", label: "File Name", type: "text", readOnly: true, placeholder: "上传图片后自动填写" }, { key: "image_url", label: "Image URL", type: "text", required: true, readOnly: true, placeholder: "上传图片后自动填写", colSpan: 2 }, { key: "mime_type", label: "MIME Type", type: "select", readOnly: true, options: mimeTypeOptions }, { key: "image_time_stamp", label: "Image Date", type: "date", readOnly: true }, { key: "study_db_id", label: "Study", type: "select", options: [{ value: NONE_SELECT_VALUE, label: "No study" }, ...studyOptions], }, { key: "observation_unit_db_id", label: "ObservationUnit", type: "select", options: [{ value: NONE_SELECT_VALUE, label: "No observation unit" }, ...observationUnitOptions], }, { key: "observation_db_ids", label: "Observation IDs", type: "text", placeholder: observationOptions.slice(0, 3).map((option) => option.value).join(", ") || "obs-001, obs-002", colSpan: 2, }, { key: "description", label: "Description", type: "textarea", colSpan: 2 }, ], [observationOptions, observationUnitOptions, studyOptions]); const validateImageForm = useCallback((payload: Record) => { const imageUrl = String(payload.image_url ?? "").trim(); if (!imageUrl) { return "请先上传图片,上传成功后系统会自动填充带 * 的图片字段。"; } return null; }, []); return ( Events Images formatDateRange(row.start_date, row.end_date), }, { key: "event_description", label: "Description" }, { key: "observation_unit_db_ids", label: "Observation Units" }, ]} fields={eventFields} data={[]} stats={[{ label: "/brapi/v2/events", value: "BrAPI", className: "bg-fuchsia-50 text-fuchsia-700 dark:bg-fuchsia-400/10 dark:text-fuchsia-200" }]} loadData={loadEvents} createRecord={(payload) => createEventRow(payload) as unknown as Promise>} updateRecord={(id, payload) => updateEventRow(id, payload) as unknown as Promise>} deleteRecord={deleteEventRow} /> } validateForm={validateImageForm} createRecord={(payload) => createImageRow(payload) as unknown as Promise>} updateRecord={(id, payload) => updateImageRow(id, payload) as unknown as Promise>} deleteRecord={deleteImageRow} /> ); }