"use client"; import React, { useEffect, useRef, useState } from "react"; import { useForm } from "react-hook-form"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; import { zodResolver } from "@hookform/resolvers/zod"; import * as z from "zod"; import Swal from "sweetalert2"; import withReactContent from "sweetalert2-react-content"; import { useParams, useRouter } from "next/navigation"; import { Input } from "@/components/ui/input"; import { Checkbox } from "@/components/ui/checkbox"; import { Label } from "@/components/ui/label"; // Import new reusable form components import { FormField, FormSelect, FormCheckbox, FormRadio, FormSection, FormGrid, FormGridItem, SelectOption, CheckboxOption, RadioOption, } from "@/components/form/shared"; import { createTask, getTask, getUserLevelForAssignments, } from "@/service/task"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { ChevronDown, ChevronUp, Trash2 } from "lucide-react"; import { AudioRecorder } from "react-audio-voice-recorder"; import FileUploader from "@/components/form/shared/file-uploader"; import { Upload } from "tus-js-client"; import { close, error } from "@/config/swal"; import { getCsrfToken } from "@/service/auth"; import { loading } from "@/lib/swal"; import { useTranslations } from "next-intl"; import dynamic from "next/dynamic"; // ============================================================================= // SCHEMA // ============================================================================= const taskSchema = z.object({ title: z.string().min(1, { message: "Judul diperlukan" }), naration: z.string().min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter.", }), assignmentSelection: z.string().min(1, { message: "Assignment selection is required" }), mainType: z.string().min(1, { message: "Main type is required" }), taskType: z.string().min(1, { message: "Task type is required" }), type: z.string().min(1, { message: "Type is required" }), taskOutput: z.array(z.string()).min(1, { message: "At least one output is required" }), }); // ============================================================================= // TYPES // ============================================================================= interface FileWithPreview extends File { preview: string; } export type taskDetail = { id: number; title: string; fileTypeOutput: string; assignedToTopLevel: string; assignedToLevel: string; assignmentType: { id: number; name: string; }; assignmentMainType: { id: number; name: string; }; attachmentUrl: string; taskType: string; broadcastType: string; narration: string; is_active: string; }; const CustomEditor = dynamic( () => { return import("@/components/editor/custom-editor"); }, { ssr: false } ); // ============================================================================= // OPTIONS DATA // ============================================================================= const assignmentSelectionOptions: SelectOption[] = [ { value: "3,4", label: "Semua Pengguna" }, { value: "4", label: "Kontributor" }, { value: "3", label: "Approver" }, ]; const mainTypeOptions: RadioOption[] = [ { value: "1", label: "Mediahub" }, { value: "2", label: "Medsos Mediahub" }, ]; const taskTypeOptions: RadioOption[] = [ { value: "atensi-khusus", label: "Atensi Khusus" }, { value: "tugas-harian", label: "Tugas Harian" }, ]; const typeOptions: RadioOption[] = [ { value: "1", label: "Publikasi" }, { value: "2", label: "Amplifikasi" }, { value: "3", label: "Kontra" }, ]; const taskOutputOptions: CheckboxOption[] = [ { value: "all", label: "All" }, { value: "video", label: "Video" }, { value: "audio", label: "Audio" }, { value: "image", label: "Image" }, { value: "text", label: "Text" }, ]; const unitSelectionOptions: CheckboxOption[] = [ { value: "allUnit", label: "All Unit" }, { value: "mabes", label: "Mabes" }, { value: "polda", label: "Polda" }, { value: "polres", label: "Polres" }, { value: "satker", label: "Satker" }, ]; // ============================================================================= // COMPONENT // ============================================================================= export default function FormTaskRefactored() { const MySwal = withReactContent(Swal); const router = useRouter(); const editor = useRef(null); type TaskSchema = z.infer; const { id } = useParams() as { id: string }; const t = useTranslations("Form"); // ============================================================================= // STATE // ============================================================================= const [detail, setDetail] = useState(); const [listDest, setListDest] = useState([]); const [checkedLevels, setCheckedLevels] = useState(new Set()); const [expandedPolda, setExpandedPolda] = useState([{}]); const [isLoading, setIsLoading] = useState(false); const [audioFile, setAudioFile] = useState(null); const [isRecording, setIsRecording] = useState(false); const [timer, setTimer] = useState(120); const [imageFiles, setImageFiles] = useState([]); const [videoFiles, setVideoFiles] = useState([]); const [textFiles, setTextFiles] = useState([]); const [audioFiles, setAudioFiles] = useState([]); const [isImageUploadFinish, setIsImageUploadFinish] = useState(false); const [isVideoUploadFinish, setIsVideoUploadFinish] = useState(false); const [isTextUploadFinish, setIsTextUploadFinish] = useState(false); const [isAudioUploadFinish, setIsAudioUploadFinish] = useState(false); const [voiceNoteLink, setVoiceNoteLink] = useState(""); const [links, setLinks] = useState([""]); // ============================================================================= // FORM SETUP // ============================================================================= const form = useForm({ resolver: zodResolver(taskSchema), mode: "all", defaultValues: { title: detail?.title || "", naration: detail?.narration || "", assignmentSelection: "3,4", mainType: "1", taskType: "atensi-khusus", type: "1", taskOutput: [], }, }); // ============================================================================= // EFFECTS // ============================================================================= useEffect(() => { async function fetchPoldaPolres() { setIsLoading(true); try { const response = await getUserLevelForAssignments(); setListDest(response?.data?.data.list); const initialExpandedState = response?.data?.data.list.reduce( (acc: any, polda: any) => { acc[polda.id] = false; return acc; }, {} ); setExpandedPolda(initialExpandedState); } catch (error) { console.error("Error fetching Polda/Polres data:", error); } finally { setIsLoading(false); } } fetchPoldaPolres(); }, []); // ============================================================================= // HANDLERS // ============================================================================= const handleCheckboxChange = (levelId: number) => { setCheckedLevels((prev) => { const updatedLevels = new Set(prev); if (updatedLevels.has(levelId)) { updatedLevels.delete(levelId); } else { updatedLevels.add(levelId); } return updatedLevels; }); }; const handlePoldaPolresChange = () => { return Array.from(checkedLevels).join(","); }; const toggleExpand = (poldaId: any) => { setExpandedPolda((prev) => ({ ...prev, [poldaId]: !prev[poldaId], })); }; const onRecordingStart = () => { setIsRecording(true); setTimer(120); const interval = setInterval(() => { setTimer((prev) => { if (prev <= 1) { clearInterval(interval); setIsRecording(false); return 0; } return prev - 1; }); }, 1000); }; const handleStopRecording = () => { setIsRecording(false); }; const addAudioElement = (blob: Blob) => { const url = URL.createObjectURL(blob); const audio = new Audio(url); const file = new File([blob], "voice-note.webm", { type: "audio/webm" }); setAudioFile(file); setVoiceNoteLink(url); }; const handleDeleteAudio = (index: number) => { setAudioFiles((prev) => prev.filter((_, i) => i !== index)); }; const handleLinkChange = (index: number, value: string) => { const newLinks = [...links]; newLinks[index] = value; setLinks(newLinks); }; const handleAddRow = () => { setLinks([...links, ""]); }; const handleRemoveRow = (index: number) => { setLinks(links.filter((_, i) => i !== index)); }; // ============================================================================= // SUBMISSION // ============================================================================= const save = async (data: TaskSchema) => { try { const csrfToken = await getCsrfToken(); const formData = new FormData(); formData.append("title", data.title); formData.append("narration", data.naration); formData.append("assignmentSelection", data.assignmentSelection); formData.append("mainType", data.mainType); formData.append("taskType", data.taskType); formData.append("type", data.type); formData.append("taskOutput", data.taskOutput.join(",")); formData.append("selectedTarget", handlePoldaPolresChange()); // Add file uploads if (videoFiles.length > 0) { videoFiles.forEach((file) => { formData.append("videoFiles", file); }); } if (imageFiles.length > 0) { imageFiles.forEach((file) => { formData.append("imageFiles", file); }); } if (textFiles.length > 0) { textFiles.forEach((file) => { formData.append("textFiles", file); }); } if (audioFiles.length > 0) { audioFiles.forEach((file) => { formData.append("audioFiles", file); }); } if (audioFile) { formData.append("audioFile", audioFile); } // Add links formData.append("links", JSON.stringify(links.filter(link => link.trim() !== ""))); const response = await createTask(formData, csrfToken); if (response?.data?.success) { successSubmit("/task"); } else { error("Failed to create task"); } } catch (error) { console.error("Error saving task:", error); error("An error occurred while saving the task"); } }; const onSubmit = (data: TaskSchema) => { MySwal.fire({ title: "Simpan Data", text: "Apakah Anda yakin ingin menyimpan data ini?", icon: "warning", showCancelButton: true, cancelButtonColor: "#d33", confirmButtonColor: "#3085d6", confirmButtonText: "Simpan", }).then((result) => { if (result.isConfirmed) { save(data); } }); }; const successSubmit = (redirect: string) => { MySwal.fire({ title: "Berhasil!", text: "Data berhasil disimpan", icon: "success", timer: 2000, showConfirmButton: false, }).then(() => { router.push(redirect); }); }; // ============================================================================= // RENDER // ============================================================================= return (
{/* Header */}

{t("form-task", { defaultValue: "Form Task" })} (Refactored)

Create and manage task assignments with detailed configuration

{/* Basic Information */}

Basic Information

Enter the basic details for your task

{form.formState.errors.title && (

{form.formState.errors.title.message}

)}
{/* Assignment Configuration */}

Assignment Configuration

Configure assignment settings and target audience

{form.formState.errors.assignmentSelection && (

{form.formState.errors.assignmentSelection.message}

)}
{unitSelectionOptions.map((option) => (
))}
{/* Custom Assignment Dialog */}
Daftar Wilayah Polda dan Polres
{listDest.map((polda: any) => (
{expandedPolda[polda.id] && (
{polda?.subDestination?.map((polres: any) => ( ))}
)}
))}
{/* Task Configuration */}

Task Configuration

Configure task type and output settings

{mainTypeOptions.map((option) => (
))}
{form.formState.errors.mainType && (

{form.formState.errors.mainType.message}

)}
{taskTypeOptions.map((option) => (
))}
{form.formState.errors.taskType && (

{form.formState.errors.taskType.message}

)}
{typeOptions.map((option) => (
))}
{form.formState.errors.type && (

{form.formState.errors.type.message}

)}
{taskOutputOptions.map((option) => (
))}
{form.formState.errors.taskOutput && (

{form.formState.errors.taskOutput.message}

)}
{/* Task Description */}

Task Description

Provide detailed description of the task

{form.formState.errors.naration && (

{form.formState.errors.naration.message}

)}
{/* Attachments */}

Attachments

Upload files and add links for the task

{/* File Uploaders */}
setVideoFiles(files)} />
setImageFiles(files)} />
setTextFiles(files)} />
setAudioFiles((prev) => [...prev, ...files])} />
{/* Audio Files List */} {audioFiles?.length > 0 && (
{audioFiles.map((audio: any, idx: any) => (
{t("voice-note", { defaultValue: "Voice Note" })} {idx + 1}
))}
)} {isRecording && (

Recording... {timer} seconds remaining

)} {/* News Links */}
{links.map((link, index) => (
handleLinkChange(index, e.target.value)} /> {links.length > 1 && ( )}
))}
{/* Submit Button */}
); }