mediahub-fe/components/form/task/task-detail-form.tsx

1817 lines
67 KiB
TypeScript
Raw Normal View History

"use client";
import React, { useEffect, useRef, useState } from "react";
import { useForm, Controller } from "react-hook-form";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
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 {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Checkbox } from "@/components/ui/checkbox";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
2024-12-21 06:25:07 +00:00
import {
2025-01-11 14:30:35 +00:00
acceptAssignment,
2025-01-08 20:01:32 +00:00
createAssignmentResponse,
2024-12-21 06:25:07 +00:00
createTask,
deleteAssignmentResponse,
2025-01-11 14:30:35 +00:00
deleteTask,
finishTask,
getAcceptance,
2025-01-11 14:30:35 +00:00
getAcceptanceAssignmentStatus,
2025-01-08 20:01:32 +00:00
getAssignmentResponseList,
getMediaUpload,
2024-12-21 06:25:07 +00:00
getTask,
getUserLevelForAssignments,
} from "@/service/task";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
ChevronDown,
ChevronUp,
Dock,
DotSquare,
ImageIcon,
Music,
Search,
TrashIcon,
VideoIcon,
} from "lucide-react";
2025-01-07 18:04:41 +00:00
import dynamic from "next/dynamic";
import { Link } from "@/components/navigation";
2025-01-08 20:01:32 +00:00
import { Textarea } from "@/components/ui/textarea";
2025-01-11 14:30:35 +00:00
import { close, error, loading } from "@/lib/swal";
2025-01-08 20:01:32 +00:00
import { getCookiesDecrypt } from "@/lib/utils";
import { Avatar, AvatarImage } from "@/components/ui/avatar";
2025-01-11 14:30:35 +00:00
import { successCallback } from "@/config/swal";
import FileUploader from "../shared/file-uploader";
import { AudioRecorder } from "react-audio-voice-recorder";
import Image from "next/image";
import { Icon } from "@iconify/react/dist/iconify.js";
import WavesurferPlayer from "@wavesurfer/react";
import WaveSurfer from "wavesurfer.js";
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
2025-04-03 15:43:04 +00:00
import { useTranslations } from "next-intl";
const taskSchema = z.object({
uniqueCode: z.string().min(1, { message: "Judul diperlukan" }),
title: z.string().min(1, { message: "Judul diperlukan" }),
naration: z.string().min(2, {
message: "Narasi Penugasan harus lebih dari 2 karakter.",
}),
});
export type taskDetail = {
id: number;
uniqueCode: string;
title: string;
fileTypeOutput: string;
assignedToRole: string;
assignedToTopLevel: string;
assignmentType: {
id: number;
name: string;
};
assignmentMainType: {
id: number;
name: string;
};
2025-01-11 14:30:35 +00:00
createdBy: {
id: number;
fullname: string;
username: string | null;
email: string;
isActive: boolean;
isDefault: boolean;
isInternational: boolean;
userLevel: {
id: number;
name: string;
aliasName: string;
userGroupId: number;
};
};
taskType: string;
broadcastType: string;
narration: string;
is_active: string;
2025-05-13 06:05:42 +00:00
isAssignmentAccepted: boolean;
2025-01-08 20:01:32 +00:00
isDone: any;
};
2025-01-08 20:01:32 +00:00
interface ListData {
id: number;
name: string;
content: string;
timestamp: string;
}
2025-01-07 18:04:41 +00:00
const ViewEditor = dynamic(
() => {
return import("@/components/editor/view-editor");
},
{ ssr: false }
);
const formatDate = (dateString: string): string => {
const date = new Date(dateString);
// Pastikan validitas tanggal
if (isNaN(date.getTime())) {
throw new Error("Invalid date format");
}
// Format tanggal
const day = date.getDate().toString().padStart(2, "0");
const month = (date.getMonth() + 1).toString().padStart(2, "0");
const year = date.getFullYear();
// const hours = date.getHours().toString().padStart(2, "0");
// Gabungkan hasil format
return `${day}-${month}-${year} `;
};
2025-01-11 14:30:35 +00:00
interface AcceptanceData {
id: number;
acceptAt: string;
// Tambahkan properti lain sesuai dengan struktur data Anda
}
interface AcceptanceData {
id: number;
userLevelId: number;
sentAt: string;
isAccept: boolean;
isSent: boolean;
userLevels: {
id: number;
name: string;
aliasName: string;
};
}
interface FileWithPreview extends File {
preview: string;
}
2025-01-12 17:38:34 +00:00
interface UploadResult {
id: number;
title: string;
description: string;
createdAt: string;
2025-02-05 09:02:13 +00:00
creatorGroupLevelName: string;
2025-01-12 17:38:34 +00:00
category: { name: string };
fileType: { name: string };
uploadStatus: { name: string };
creatorName: string;
2025-02-04 08:04:57 +00:00
creatorGroup: string;
2025-01-12 17:38:34 +00:00
}
interface FileUploaded {
id: number;
url: string;
}
export default function FormTaskDetail() {
const MySwal = withReactContent(Swal);
const router = useRouter();
const editor = useRef(null);
type TaskSchema = z.infer<typeof taskSchema>;
const { id } = useParams() as { id: string };
console.log(id);
2025-01-08 20:01:32 +00:00
const userLevelNumber = getCookiesDecrypt("ulne");
const userId = getCookiesDecrypt("uie");
2025-02-08 03:44:09 +00:00
const userLevelId = "";
2025-01-08 20:01:32 +00:00
// State for various form fields
const [taskOutput, setTaskOutput] = useState({
all: false,
video: false,
audio: false,
image: false,
text: false,
});
2025-04-03 15:43:04 +00:00
const t = useTranslations("Form");
2025-01-12 17:38:34 +00:00
const [uploadResults, setUploadResults] = useState<UploadResult[]>([]);
const [isTableResult, setIsTableResult] = useState(false);
const [isSentResult] = useState(false);
const [mainType, setMainType] = useState<string>("1");
const [taskType, setTaskType] = useState<string>("atensi-khusus");
const [broadcastType, setBroadcastType] = useState<string>(""); // untuk Tipe Penugasan
const [type, setType] = useState<string>("1");
const [selectedTarget, setSelectedTarget] = useState("all");
const [detail, setDetail] = useState<taskDetail>();
const [urlInputs, setUrlInputs] = useState<string[]>([]);
const [refresh] = useState(false);
2024-12-21 06:25:07 +00:00
const [listDest, setListDest] = useState([]); // Data Polda dan Polres
const [checkedLevels, setCheckedLevels] = useState(new Set());
const [expandedPolda, setExpandedPolda] = useState([{}]);
const [isLoading, setIsLoading] = useState(false);
2025-01-08 20:01:32 +00:00
const [responses, setResponses] = useState<Response[]>([]);
const [response, setResponse] = useState<string>("");
const [showInput, setShowInput] = useState<boolean>(false);
const [listData, setListData] = useState([]);
const [message, setMessage] = useState<string>("");
2025-01-11 14:30:35 +00:00
const [sentAcceptance, setSentAcceptance] = useState<AcceptanceData[]>([]);
const [acceptAcceptance, setAcceptAcceptance] = useState<AcceptanceData[]>(
[]
);
const [totalPage, setTotalPage] = React.useState(1);
const [dataTable, setDataTable] = React.useState<any[]>([]);
const [totalData, setTotalData] = React.useState<number>(1);
const [imageFiles, setImageFiles] = useState<FileWithPreview[]>([]);
const [videoFiles, setVideoFiles] = useState<FileWithPreview[]>([]);
const [textFiles, setTextFiles] = useState<FileWithPreview[]>([]);
const [audioFiles, setAudioFiles] = useState<FileWithPreview[]>([]);
const [isImageUploadFinish, setIsImageUploadFinish] = useState(false);
const [isVideoUploadFinish, setIsVideoUploadFinish] = useState(false);
const [isTextUploadFinish, setIsTextUploadFinish] = useState(false);
const [isAudioUploadFinish, setIsAudioUploadFinish] = useState(false);
const [voiceNoteLink, setVoiceNoteLink] = useState("");
2025-01-11 14:30:35 +00:00
const [statusAcceptance, setStatusAcceptance] = useState();
const [modalType, setModalType] = useState<"terkirim" | "diterima" | "">("");
const [Isloading, setLoading] = useState<boolean>(false);
const [refreshAcceptance, setRefreshAcceptance] = useState<boolean>(false);
const [replyingTo, setReplyingTo] = useState<number | null>(null);
const [audioFile, setAudioFile] = useState<File | null>(null);
const [isRecording, setIsRecording] = useState(false);
const [timer, setTimer] = useState<number>(120);
const [search, setSearch] = React.useState<string>("");
const [wavesurfer, setWavesurfer] = useState<WaveSurfer>();
const [isPlaying, setIsPlaying] = useState(false);
const [imageUploadedFiles, setImageUploadedFiles] = useState<FileUploaded[]>(
[]
);
const [selectedImage, setSelectedImage] = useState<string | null>(
imageUploadedFiles?.[0]?.url || null
);
const [videoUploadedFiles, setVideoUploadedFiles] = useState<FileUploaded[]>(
[]
);
const [selectedVideo, setSelectedVideo] = useState<string | null>(
videoUploadedFiles?.[0]?.url || null
);
const [textUploadedFiles, setTextUploadedFiles] = useState<FileUploaded[]>(
[]
);
const [selectedText, setSelectedText] = useState<string | null>(
textUploadedFiles?.[0]?.url || null
);
const [audioUploadedFiles, setAudioUploadedFiles] = useState<FileUploaded[]>(
[]
);
const [selectedAudio, setSelectedAudio] = useState<string | null>(
audioUploadedFiles?.[0]?.url || null
);
const [platformTypeVisible, setPlatformTypeVisible] = useState(false);
const [unitSelection, setUnitSelection] = useState({
allUnit: false,
mabes: false,
polda: false,
2025-01-18 15:13:47 +00:00
satker: false,
polres: false,
});
const {
control,
handleSubmit,
formState: { errors },
} = useForm<TaskSchema>({
resolver: zodResolver(taskSchema),
});
// const handleRadioChange = (event: React.ChangeEvent<HTMLInputElement>) => {
// const selectedValue = Number(event.target.value);
// setMainType(selectedValue);
// setPlatformTypeVisible(selectedValue === 2);
// };
2024-12-21 06:25:07 +00:00
useEffect(() => {
async function fetchPoldaPolres() {
setIsLoading(true);
try {
const response = await getUserLevelForAssignments();
2025-01-01 17:48:57 +00:00
setListDest(response?.data?.data.list);
const initialExpandedState = response?.data?.data.list.reduce(
2024-12-21 06:25:07 +00:00
(acc: any, polda: any) => {
acc[polda.id] = false;
return acc;
},
{}
);
setExpandedPolda(initialExpandedState);
console.log("polres", initialExpandedState);
} catch (error) {
console.error("Error fetching Polda/Polres data:", error);
} finally {
setIsLoading(false);
}
}
fetchPoldaPolres();
}, []);
2025-02-05 09:02:13 +00:00
const fetchAllData = async () => {
try {
const response = await getMediaUpload(id, userLevelId);
setUploadResults(response?.data?.data || []);
} catch (error) {
console.error("Error fetching all data:", error);
}
};
const fetchFilteredData = async (selectedLevels: any[]) => {
try {
2025-02-05 09:02:13 +00:00
if (selectedLevels.length === 0) {
fetchAllData(); // Jika tidak ada filter, panggil semua data
return;
}
2025-02-05 09:02:13 +00:00
const levels = selectedLevels.join(",");
const response = await getMediaUpload(id, levels);
setUploadResults(response?.data?.data || []);
} catch (error) {
console.error("Error fetching filtered data:", error);
}
};
useEffect(() => {
async function initState() {
if (id) {
const response = await getTask(id);
2025-01-05 00:44:55 +00:00
const details = response?.data?.data;
setDetail(details);
2024-12-21 06:25:07 +00:00
// if (details) {
// setUploadResults(details.uploadResults || []);
// }
2025-01-12 17:38:34 +00:00
if (details?.urls) {
// Extract attachmentUrl from each object in urls
const urls = details.urls.map(
(urlObj: any) => urlObj.attachmentUrl || ""
);
setUrlInputs(urls); // Save the URLs to state
}
2024-12-21 06:25:07 +00:00
if (details?.assignedToLevel) {
const levels = new Set(
details.assignedToLevel.split(",").map(Number)
);
setCheckedLevels(levels);
}
const attachment = details?.files;
setImageUploadedFiles(
attachment?.filter((file: any) => file.fileTypeId == 1)
);
setVideoUploadedFiles(
attachment?.filter((file: any) => file.fileTypeId == 2)
);
setTextUploadedFiles(
attachment?.filter((file: any) => file.fileTypeId == 3)
);
setAudioUploadedFiles(
attachment?.filter((file: any) => file.fileTypeId == 4)
);
}
}
initState();
2025-05-13 06:05:42 +00:00
getDataAcceptance();
}, [id, refresh]);
const handleUrlChange = (index: number, newUrl: string) => {
setUrlInputs((prev: any) =>
prev.map((url: any, idx: any) => (idx === index ? newUrl : url))
);
};
useEffect(() => {
if (detail?.broadcastType) {
setBroadcastType(detail.broadcastType);
}
}, [detail?.broadcastType]);
useEffect(() => {
if (detail?.fileTypeOutput) {
const outputSet = new Set(detail.fileTypeOutput.split(",").map(Number));
setTaskOutput({
all: outputSet.has(1),
video: outputSet.has(2),
audio: outputSet.has(4),
image: outputSet.has(3),
text: outputSet.has(5),
});
}
}, [detail?.fileTypeOutput]);
useEffect(() => {
if (detail?.assignedToTopLevel) {
const outputSet = new Set(
detail.assignedToTopLevel.split(",").map(Number)
);
setUnitSelection({
allUnit: outputSet.has(0),
mabes: outputSet.has(1),
polda: outputSet.has(2),
polres: outputSet.has(3),
2025-01-18 15:13:47 +00:00
satker: outputSet.has(4),
});
}
}, [detail?.fileTypeOutput]);
2025-01-11 14:30:35 +00:00
const successConfirm = () => {
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
router.push("/en/contributor/task");
}
});
};
2024-12-21 06:25:07 +00:00
const handleCheckboxChange = (levelId: any) => {
setCheckedLevels((prev: any) => {
const updatedLevels = new Set(prev);
if (updatedLevels.has(levelId)) {
updatedLevels.delete(levelId);
} else {
updatedLevels.add(levelId);
}
return updatedLevels;
});
};
const handleCheckboxChangeFilter = async (id: number) => {
setCheckedLevels((prev: any) => {
const updatedLevels = new Set(prev);
if (updatedLevels.has(id)) {
updatedLevels.delete(id);
} else {
updatedLevels.add(id);
}
console.log("Checked Levels:", Array.from(updatedLevels));
fetchFilteredData(Array.from(updatedLevels));
return updatedLevels;
});
};
2024-12-21 06:25:07 +00:00
const toggleExpand = (poldaId: any) => {
setExpandedPolda((prev: any) => ({
...prev,
[poldaId]: !prev[poldaId],
}));
};
2025-01-08 20:01:32 +00:00
useEffect(() => {
async function initState() {
// loading();
const response = await getAssignmentResponseList(id);
console.log("data", response?.data?.data);
console.log("userLvl", userLevelNumber);
setListData(response?.data?.data);
close();
}
initState();
}, []);
const handleToggleInput = (): void => {
setShowInput(!showInput);
};
const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
setMessage(e.target.value);
};
const postData = () => {
sendSuggestionParent();
};
const sendReplyData = async (parentId: number) => {
const inputElement = document.querySelector(
`#input-comment-${parentId}`
) as HTMLTextAreaElement;
if (inputElement?.value?.length > 1) {
loading();
const data = {
assignmentId: id,
message: inputElement.value,
parentId,
};
console.log(data);
const response = await createAssignmentResponse(data);
console.log(response);
const responseGet = await getAssignmentResponseList(id);
console.log(responseGet?.data?.data);
setListData(responseGet?.data?.data);
inputElement.value = "";
close();
setReplyingTo(null);
}
};
2025-01-08 20:01:32 +00:00
async function sendSuggestionParent() {
if (message?.length > 1) {
loading();
const data = {
assignmentId: id,
message,
2025-01-08 20:01:32 +00:00
parentId: null,
};
const response = await createAssignmentResponse(data);
console.log(response);
setMessage("");
const responseGet = await getAssignmentResponseList(id);
console.log(responseGet?.data?.data);
setListData(responseGet?.data?.data);
close();
}
}
async function deleteDataSuggestion(dataId: any) {
loading();
const response = await deleteAssignmentResponse(dataId);
console.log(response);
const responseGet = await getAssignmentResponseList(id);
console.log(responseGet?.data?.data);
setListData(responseGet?.data?.data);
close();
}
const deleteData = (dataId: any) => {
deleteDataSuggestion(dataId);
console.log(dataId);
};
2025-01-11 14:30:35 +00:00
async function getDataAcceptance() {
2025-05-13 06:05:42 +00:00
console.log("Get Acceptance Status >> ");
2025-01-11 14:30:35 +00:00
const response = await getAcceptanceAssignmentStatus(id);
setStatusAcceptance(response?.data?.data?.isAccept);
2025-05-13 06:05:42 +00:00
console.log("Get Acceptance Status : ", response);
2025-01-11 14:30:35 +00:00
}
const handleAcceptAcceptance = async () => {
const isAccept = true;
2025-01-11 14:30:35 +00:00
loading();
console.log("Id user :", userId);
2025-05-13 06:05:42 +00:00
const response = await acceptAssignment(id);
2025-01-11 14:30:35 +00:00
if (response?.error) {
error(response?.message);
return false;
}
2025-01-11 14:30:35 +00:00
successCallback();
getDataAcceptance();
return false;
};
async function getListAcceptance(): Promise<void> {
const isAccept = true;
try {
const resSent = await getAcceptance(id, !isAccept);
setSentAcceptance(resSent?.data?.data);
const resAccept = await getAcceptance(id, isAccept);
const acceptanceSort = resAccept?.data?.data?.sort(
(a: AcceptanceData, b: AcceptanceData) =>
new Date(a.acceptAt).getTime() - new Date(b.acceptAt).getTime()
);
console.log("Data sort:", acceptanceSort);
setAcceptAcceptance(acceptanceSort);
} catch (error) {
console.error("Error fetching acceptance data:", error);
}
}
useEffect(() => {
2025-01-11 14:30:35 +00:00
async function initState(): Promise<void> {
await getListAcceptance();
}
initState();
}, [refreshAcceptance]);
2025-01-11 14:30:35 +00:00
const setFinishAcceptance = async (
id: number,
isFinish: boolean
): Promise<void> => {
if (!isFinish) {
loading();
2025-01-11 14:30:35 +00:00
setRefreshAcceptance((prev) => !prev);
close();
}
};
const handleReply = (id: any) => {
setReplyingTo(id);
};
2025-01-11 14:30:35 +00:00
const getModalContent = (type: "terkirim" | "diterima") => (
<div className="overflow-x-auto overflow-y-auto ">
2025-01-11 14:30:35 +00:00
{Isloading ? (
<p>Loading...</p>
) : (
<table className="w-full border-collapse border border-gray-300">
<thead>
<tr className="bg-gray-100 border-b">
<th className="px-4 py-2 text-left">Waktu</th>
<th className="px-4 py-2 text-left">Unit</th>
<th className="px-4 py-2 text-left">Status</th>
</tr>
</thead>
<tbody>
{(type === "terkirim" ? sentAcceptance : acceptAcceptance).map(
(item) => (
<tr key={item.id} className="border-b">
<td className="px-4 py-2">
{new Date(item.sentAt).toLocaleString()}
</td>
<td className="px-4 py-2">{item.userLevels.name}</td>
<td className="px-4 py-2">
{type === "terkirim" ? "Terkirim" : "Diterima"}
</td>
</tr>
)
)}
</tbody>
</table>
)}
</div>
);
async function finishAssignment() {
const response = finishTask(id);
// if (response.error) {
// error(response.message);
// return false;
// }
successConfirm();
}
async function deleteAssignment() {
const response = deleteTask(id);
// if (response.error) {
// error(response.message);
// return false;
// }
successConfirm();
}
function handleForward() {
router.push(`/en/contributor/task/forward/${id}`);
}
async function handleDeleteAssignment() {
MySwal.fire({
title: "Apakah Anda yakin ingin menghapus data?",
text: "",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#d33",
confirmButtonColor: "#3085d6",
confirmButtonText: "Hapus",
}).then((result) => {
if (result.isConfirmed) {
deleteAssignment();
}
});
}
async function handleAssignmentDone() {
MySwal.fire({
title: "Apakah tugas sudah selesai?",
text: "",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#d33",
confirmButtonColor: "#3085d6",
confirmButtonText: "Ya",
cancelButtonText: "Tidak",
}).then((result) => {
if (result.isConfirmed) {
finishAssignment();
}
});
}
const addAudioElement = (blob: Blob) => {
const url = URL.createObjectURL(blob);
const audio = document.createElement("audio");
audio.src = url;
audio.controls = true;
document.body.appendChild(audio);
// Convert Blob to File
const file = new File([blob], "voiceNote.webm", { type: "audio/webm" });
setAudioFile(file);
};
const handleDeleteAudio = () => {
// Remove the audio file by setting state to null
setAudioFile(null);
const audioElements = document.querySelectorAll("audio");
audioElements.forEach((audio) => audio.remove());
};
const onRecordingStart = () => {
setIsRecording(true);
const countdown = setInterval(() => {
setTimer((prevTimer) => {
if (prevTimer <= 1) {
clearInterval(countdown);
return 0;
}
return prevTimer - 1;
});
}, 1000);
setTimeout(() => {
if (isRecording) {
handleStopRecording();
}
}, 120000);
};
const handleStopRecording = () => {
setIsRecording(false);
setTimer(120); // Reset the timer to 2 minutes for the next recording
};
const renderFilePreview = (url: string) => {
return (
<Image
width={48}
height={48}
alt={"file preview"}
src={url}
className=" rounded border p-0.5"
/>
);
};
const onReady = (ws: any) => {
setWavesurfer(ws);
setIsPlaying(false);
};
const onPlayPause = () => {
wavesurfer && wavesurfer.playPause();
};
const handleRemoveFile = (id: number) => {};
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
setSearch(e.target.value); // Perbarui state search
// getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel
};
return (
<Card>
{detail !== undefined ? (
<div className="px-6 py-6">
<div className="flex flex-col sm:flex-row lg:flex-row justify-between">
<p className="text-lg font-semibold mb-3">{t("detail-task", { defaultValue: "Detail Task" })}</p>
2025-01-11 14:30:35 +00:00
<div
className="flex gap-3"
2025-01-11 14:30:35 +00:00
style={
detail?.createdBy?.id === Number(userId)
? {}
: {
display: "none",
}
}
>
<div>
<Dialog>
<DialogTrigger asChild>
<Button
color="primary"
onClick={() => setModalType("terkirim")}
>
{sentAcceptance?.length} {t("sent", { defaultValue: "Sent" })}
2025-01-11 14:30:35 +00:00
</Button>
</DialogTrigger>
<DialogTrigger asChild>
<Button
color="warning"
onClick={() => setModalType("diterima")}
className="ml-3"
>
{acceptAcceptance?.length} {t("accepted", { defaultValue: "Accepted" })}
2025-01-11 14:30:35 +00:00
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px] overflow-y-auto max-h-[500px]">
2025-01-11 14:30:35 +00:00
<DialogHeader>
2025-04-03 15:43:04 +00:00
<DialogTitle>
{t("assignment-status-details", { defaultValue: "Assignment Status Details" })}
2025-04-03 15:43:04 +00:00
</DialogTitle>
2025-01-11 14:30:35 +00:00
</DialogHeader>
{modalType === "terkirim" && getModalContent("terkirim")}
{modalType === "diterima" && getModalContent("diterima")}
</DialogContent>
</Dialog>
</div>
</div>
</div>
<form>
<div className="gap-5 mb-5">
<div className="space-y-2">
<Label>{t("unique-code", { defaultValue: "Unique Code" })}</Label>
<Controller
control={control}
name="uniqueCode"
render={({ field }) => (
<Input
size="md"
type="text"
value={detail?.uniqueCode}
onChange={field.onChange}
placeholder="Enter uniqueCode"
readOnly
/>
)}
/>
</div>
<div className="space-y-2 mt-6">
<Label>{t("title", { defaultValue: "Title" })}</Label>
<Controller
control={control}
name="title"
render={({ field }) => (
<Input
size="md"
type="text"
value={detail?.title}
onChange={field.onChange}
placeholder="Enter Title"
/>
)}
/>
{errors.title?.message && (
<p className="text-red-400 text-sm">{errors.title.message}</p>
)}
</div>
<div className="flex flex-col sm:flex-row lg:flex-row sm:items-center lg:items-center">
2025-04-03 15:43:04 +00:00
<div className="mt-6 space-y-2">
<Label>{t("assignment-selection", { defaultValue: "Assignment Selection" })}</Label>
<Select
onValueChange={setSelectedTarget}
value={detail.assignedToRole}
>
<SelectTrigger size="md">
<SelectValue placeholder="Pilih" />
</SelectTrigger>
<SelectContent>
<SelectItem value="3,4">Semua Pengguna</SelectItem>
<SelectItem value="4">Kontributor</SelectItem>
<SelectItem value="3">Approver</SelectItem>
</SelectContent>
</Select>
</div>
<div className="flex flex-wrap gap-3 mt-6 lg:pt-5 lg:ml-3">
{Object.keys(unitSelection).map((key) => (
<div className="flex items-center gap-2" key={key}>
<Checkbox
id={key}
2025-01-07 18:04:41 +00:00
disabled
checked={
unitSelection[key as keyof typeof unitSelection]
}
onCheckedChange={(value) =>
setUnitSelection({ ...unitSelection, [key]: value })
}
/>
<Label htmlFor={key}>
{key.charAt(0).toUpperCase() + key.slice(1)}
</Label>
</div>
))}
</div>
<div className="mt-6 lg:pt-5 lg:pl-3">
2024-12-21 06:25:07 +00:00
<Dialog>
<DialogTrigger asChild>
<Button variant="soft" size="sm" color="primary">
[{t("custom", { defaultValue: "Custom" })}]
2024-12-21 06:25:07 +00:00
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
<DialogHeader>
<DialogTitle>
Daftar Wilayah Polda dan Polres
</DialogTitle>
</DialogHeader>
<div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
{listDest.map((polda: any) => (
<div key={polda.id} className="border p-2">
<Label className="flex items-center">
<Checkbox
2025-01-07 18:04:41 +00:00
disabled
2024-12-21 06:25:07 +00:00
checked={checkedLevels.has(polda.id)}
onCheckedChange={() =>
handleCheckboxChange(polda.id)
}
className="mr-3"
/>
{polda.name}
<button
onClick={() => toggleExpand(polda.id)}
className="ml-2 focus:outline-none"
>
{expandedPolda[polda.id] ? (
<ChevronUp size={16} />
) : (
<ChevronDown size={16} />
)}
</button>
</Label>
{expandedPolda[polda.id] && (
<div className="ml-6 mt-2">
<Label className="block">
<Checkbox
2025-01-07 18:04:41 +00:00
disabled
2024-12-21 06:25:07 +00:00
checked={polda?.subDestination?.every(
(polres: any) =>
checkedLevels.has(polres.id)
)}
onCheckedChange={(isChecked) => {
const updatedLevels = new Set(
checkedLevels
);
polda?.subDestination?.forEach(
(polres: any) => {
if (isChecked) {
updatedLevels.add(polres.id);
} else {
updatedLevels.delete(polres.id);
}
}
);
setCheckedLevels(updatedLevels);
}}
className="mr-2"
/>
Pilih Semua Polres
</Label>
{polda?.subDestination?.map((polres: any) => (
<Label key={polres.id} className="block mt-1">
<Checkbox
2025-01-07 18:04:41 +00:00
disabled
2024-12-21 06:25:07 +00:00
checked={checkedLevels.has(polres.id)}
onCheckedChange={() =>
handleCheckboxChange(polres.id)
}
className="mr-2"
/>
{polres.name}
</Label>
))}
</div>
)}
</div>
))}
</div>
</DialogContent>
</Dialog>
</div>
</div>
2025-04-03 15:43:04 +00:00
<div className="mt-6 space-y-2">
<Label>{t("type-task", { defaultValue: "Type Task" })}</Label>
<RadioGroup
value={detail?.assignmentMainType?.id?.toString()}
onValueChange={(value) => setMainType(value)}
// value={String(mainType)}
// onValueChange={(value) => setMainType(Number(value))}
className="flex flex-wrap gap-3"
>
<RadioGroupItem value="1" id="mediahub" />
<Label htmlFor="mediahub">Mediahub</Label>
<RadioGroupItem value="2" id="medsos-mediahub" />
<Label htmlFor="medsos-mediahub">Medsos Mediahub</Label>
</RadioGroup>
</div>
2025-04-03 15:43:04 +00:00
<div className="mt-6 space-y-2">
<Label>{t("assigment-type", { defaultValue: "Assigment Type" })} </Label>
<RadioGroup
value={detail.taskType.toString()}
onValueChange={(value) => setTaskType(String(value))}
className="flex flex-wrap gap-3"
>
<RadioGroupItem value="atensi-khusus" id="khusus" />
<Label htmlFor="atensi-khusus">Atensi Khusus</Label>
<RadioGroupItem value="tugas-harian" id="harian" />
<Label htmlFor="tugas-harian">Tugas Harian</Label>
</RadioGroup>
</div>
{/* RadioGroup Assignment Category */}
2025-04-03 15:43:04 +00:00
<div className="mt-6 space-y-2">
<Label>{t("type-of-task", { defaultValue: "Type Of Task" })}</Label>
<RadioGroup
value={detail.assignmentType.id.toString()}
onValueChange={(value) => setType(value)}
className="flex flex-wrap gap-3"
>
<div className="flex items-center gap-2">
<RadioGroupItem value="1" id="publication" />
<Label htmlFor="publication">Publikasi</Label>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="2" id="amplification" />
<Label htmlFor="amplification">Amplifikasi</Label>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="3" id="contra" />
<Label htmlFor="contra">Kontra</Label>
</div>
</RadioGroup>
</div>
2025-04-03 15:43:04 +00:00
<div className="mt-6 space-y-2">
<Label>{t("output-task", { defaultValue: "Output Task" })}</Label>
<div className="flex flex-wrap gap-3">
{Object.keys(taskOutput).map((key) => (
<div className="flex items-center gap-2" key={key}>
<Checkbox
id={key}
2025-01-07 18:04:41 +00:00
disabled
checked={taskOutput[key as keyof typeof taskOutput]}
onCheckedChange={(value) =>
setTaskOutput({ ...taskOutput, [key]: value })
}
/>
<Label htmlFor={key}>
{key.charAt(0).toUpperCase() + key.slice(1)}
</Label>
</div>
))}
</div>
</div>
2025-04-03 15:43:04 +00:00
<div className="mt-6 space-y-2">
<Label>{t("description", { defaultValue: "Description" })}</Label>
<Controller
control={control}
name="naration"
render={({ field: { onChange, value } }) => (
2025-01-07 18:04:41 +00:00
<ViewEditor initialData={detail?.narration} />
)}
/>
{/* {errors.naration?.message && (
<p className="text-red-400 text-sm">
{errors.naration.message}
</p>
)} */}
</div>
<div className=" mt-5 space-y-2">
<Label htmlFor="attachment">{t("attachment", { defaultValue: "Attachment" })}</Label>
<div className="space-y-3">
<div>
2025-04-03 15:43:04 +00:00
{videoUploadedFiles?.length > 0 && (
<Label>{t("audio-visual", { defaultValue: "Audio Visual" })}</Label>
2025-04-03 15:43:04 +00:00
)}
<div>
{selectedVideo && (
<Card className="mt-2">
<video
className="object-fill h-full w-full rounded-md"
src={selectedVideo}
controls
title={`Video`} // Mengganti alt dengan title
/>
</Card>
)}
{videoUploadedFiles?.map((file: any, index: number) => (
<div
key={index}
className="flex justify-between border px-3.5 py-3 my-6 rounded-md"
>
<div
className="flex gap-3 items-center cursor-pointer"
onClick={() => setSelectedVideo(file.url)}
>
<div className="file-preview">
<VideoIcon />
</div>
<div>
<div className="text-sm text-card-foreground">
{file.fileName}
</div>
</div>
</div>
<Button
size="icon"
color="destructive"
variant="outline"
className="border-none rounded-full"
onClick={() => handleRemoveFile(file)}
>
<Icon icon="tabler:x" className="h-5 w-5" />
</Button>
</div>
))}
</div>
</div>
<div>
2025-04-03 15:43:04 +00:00
{imageUploadedFiles?.length > 0 && (
<Label>{t("image", { defaultValue: "Image" })}</Label>
2025-04-03 15:43:04 +00:00
)}
<div>
{selectedImage && (
<Card className="mt-2">
<img
src={selectedImage}
alt="Thumbnail Gambar Utama"
className="w-full h-auto rounded-md"
/>
</Card>
)}
{imageUploadedFiles?.map((file: any, index: number) => (
<div
key={index}
className="flex justify-between border px-3.5 py-3 my-6 rounded-md"
>
<div
className="flex gap-3 items-center cursor-pointer"
onClick={() => setSelectedImage(file.url)}
>
<div className="file-preview">
<ImageIcon />
</div>
<div>
<div className="text-sm text-card-foreground">
{file.fileName}
</div>
</div>
</div>
<Button
2025-01-20 06:45:54 +00:00
type="button"
size="icon"
color="destructive"
variant="outline"
className="border-none rounded-full"
onClick={() => handleRemoveFile(file)}
>
<Icon icon="tabler:x" className="h-5 w-5" />
</Button>
</div>
))}
</div>
</div>
<div>
2025-04-03 15:43:04 +00:00
{textUploadedFiles?.length > 0 && (
<Label>{t("text", { defaultValue: "Text" })}</Label>
2025-04-03 15:43:04 +00:00
)}
<div>
{selectedText && (
<Card className="mt-2">
<iframe
className="w-full h-96 rounded-md"
src={`https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(
selectedText
)}`}
title={"Document"}
/>
</Card>
)}
{textUploadedFiles?.map((file: any, index: number) => (
<div
key={index}
className="flex justify-between border px-3.5 py-3 my-6 rounded-md"
>
<div
className="flex gap-3 items-center cursor-pointer"
onClick={() => setSelectedText(file.url)}
>
<div className="file-preview">
<Dock />
</div>
<div>
<div className="text-sm text-card-foreground">
{file.fileName}
</div>
</div>
</div>
<Button
size="icon"
color="destructive"
variant="outline"
className="border-none rounded-full"
onClick={() => handleRemoveFile(file)}
>
<Icon icon="tabler:x" className="h-5 w-5" />
</Button>
</div>
))}
</div>
</div>
<div>
2025-04-03 15:43:04 +00:00
{audioUploadedFiles?.length > 0 && (
<Label>{t("audio", { defaultValue: "Audio" })}</Label>
2025-04-03 15:43:04 +00:00
)}
<div>
{selectedAudio && (
<Card className="mt-2">
<div key={selectedAudio}>
<WavesurferPlayer
height={500}
waveColor="red"
url={selectedAudio}
onReady={onReady}
onPlay={() => setIsPlaying(true)}
onPause={() => setIsPlaying(false)}
/>
</div>
<Button
size="sm"
type="button"
onClick={onPlayPause}
disabled={isPlaying}
className={`flex items-center gap-2 ${
isPlaying
? "bg-gray-300 cursor-not-allowed"
: "bg-primary text-white"
} p-2 rounded`}
>
{isPlaying ? "Pause" : "Play"}
<Icon
icon={
isPlaying
? "carbon:pause-outline"
: "famicons:play-sharp"
}
className="h-5 w-5"
/>
</Button>
</Card>
)}
{audioUploadedFiles?.map((file: any, index: number) => (
<div
key={index}
className="flex justify-between border px-3.5 py-3 my-6 rounded-md"
>
<div
className="flex gap-3 items-center cursor-pointer"
onClick={() => setSelectedAudio(file.url)}
>
<div className="file-preview">
<Music />
</div>
<div>
<div className="text-sm text-card-foreground">
{file.fileName}
</div>
</div>
</div>
<Button
size="icon"
color="destructive"
variant="outline"
className="border-none rounded-full"
onClick={() => handleRemoveFile(file)}
>
<Icon icon="tabler:x" className="h-5 w-5" />
</Button>
</div>
))}
</div>
2025-01-20 06:45:54 +00:00
{/* {audioUploadedFiles?.length > 0 && (
<AudioRecorder
onRecordingComplete={addAudioElement}
audioTrackConstraints={{
noiseSuppression: true,
echoCancellation: true,
}}
downloadOnSavePress={true}
downloadFileExtension="webm"
/>
2025-01-20 06:45:54 +00:00
)} */}
</div>
{audioFile && (
<div className="flex flex-row justify-between items-center">
<p>Voice note ready to submit: {audioFile.name}</p>
<Button
type="button"
onClick={handleDeleteAudio}
size="sm"
color="destructive"
>
X
</Button>
</div>
)}
{isRecording && <p>Recording... {timer} seconds remaining</p>}{" "}
{/* Display remaining time */}
<div className="mt-4">
2025-01-20 06:45:54 +00:00
<Label>Link Url</Label>
{urlInputs.map((url: any, index: any) => (
2025-06-09 07:16:37 +00:00
<Link
key={url.id}
2025-06-09 07:16:37 +00:00
href={url}
target="_blank"
className="flex items-center gap-2 mt-2"
>
<input
type="url"
2025-06-09 07:16:37 +00:00
className="border rounded p-2 w-full cursor-pointer"
value={url}
// onChange={(e) =>
// handleLinkChange(index, e.target.value)
// }
placeholder={`Masukkan link berita ${index + 1}`}
/>
2025-06-09 07:16:37 +00:00
</Link>
))}
</div>
</div>
</div>
</div>
<div className="flex flex-wrap lg:flex-row justify-between gap-3 my-3">
<div className="lg:px-1">
{detail?.isDone !== true &&
(Number(userLevelNumber) !== 3 ||
Number(userLevelNumber) == 2) ? (
<Button
color="primary"
variant={"outline"}
type="button"
className="btn btn-outline-primary lg:ml-3"
onClick={() => handleForward()}
>
Forward
</Button>
) : (
""
)}
</div>
<div className="flex flex-wrap gap-2">
<div className="">
<Button
2025-01-23 12:16:41 +00:00
type="button"
color="primary"
variant={"outline"}
onClick={handleToggleInput}
>
Beri Tanggapan
</Button>
</div>
<div className="">
<Button
2025-01-23 12:16:41 +00:00
type="button"
className="btn btn-primary lg:mx-3"
style={
statusAcceptance ||
2025-05-13 06:05:42 +00:00
detail?.isAssignmentAccepted == true ||
detail?.createdBy?.id == Number(userId)
? {
display: "none",
}
: {}
}
onClick={() => handleAcceptAcceptance()}
>
Terima Tugas
</Button>
</div>
<div
className="task-response w-100 lg:px-3 "
// style={
// Number(detail?.createdBy?.id) == Number(userId)
// ? {}
// : {
// display: "none",
// }
// }
2025-01-12 17:38:34 +00:00
>
<Button
type="button"
color="primary"
variant={"default"}
2025-02-05 09:02:13 +00:00
onClick={() => {
setIsTableResult(!isTableResult);
if (!isTableResult) fetchAllData(); // Panggil API saat tombol diklik
}}
>
2025-05-13 06:05:42 +00:00
Hasil Upload
</Button>
</div>
2025-01-08 20:01:32 +00:00
</div>
</div>
2025-01-12 17:38:34 +00:00
{isTableResult && (
<div>
<div className="flex flex-row items-center gap-2 my-2">
<div className="mb-3 sm:mb-0 lg-mb-0">
<InputGroup merged>
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
<Search className=" h-4 w-4 dark:text-white" />
</InputGroupText>
<Input
type="text"
placeholder="Search Judul..."
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white w-full"
value={search}
onChange={handleSearch}
/>
</InputGroup>
</div>
<Dialog>
<DialogTrigger asChild>
<Button variant="outline" size="md" color="primary">
Filter Polda/Polres
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
<DialogHeader>
<DialogTitle>
Daftar Wilayah Polda dan Polres
</DialogTitle>
</DialogHeader>
<div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
{listDest.map((polda: any) => (
<div key={polda.id} className="border p-2">
<Label className="flex items-center">
<Checkbox
2025-02-11 05:54:31 +00:00
// checked={checkedLevels.has(polda.id)}
onCheckedChange={() =>
handleCheckboxChangeFilter(polda.id)
}
className="mr-3"
/>
{polda.name}
<button
onClick={() => toggleExpand(polda.id)}
className="ml-2 focus:outline-none"
>
{expandedPolda[polda.id] ? (
<ChevronUp size={16} />
) : (
<ChevronDown size={16} />
)}
</button>
</Label>
{expandedPolda[polda.id] && (
<div className="ml-6 mt-2">
<Label className="block">
<Checkbox
2025-02-11 05:54:31 +00:00
// checked={polda?.subDestination?.every(
// (polres: any) =>
// checkedLevels.has(polres.id)
// )}
onCheckedChange={(isChecked) => {
const updatedLevels = new Set(
checkedLevels
);
polda?.subDestination?.forEach(
(polres: any) => {
if (isChecked) {
updatedLevels.add(polres.id);
} else {
updatedLevels.delete(polres.id);
}
}
);
setCheckedLevels(updatedLevels);
}}
className="mr-2"
/>
Pilih Semua Polres
</Label>
{polda?.subDestination?.map((polres: any) => (
<Label key={polres.id} className="block mt-1">
<Checkbox
2025-02-11 05:54:31 +00:00
// checked={checkedLevels.has(polres.id)}
onCheckedChange={() =>
handleCheckboxChangeFilter(polres.id)
}
className="mr-2"
/>
{polres.name}
</Label>
))}
</div>
)}
</div>
))}
</div>
</DialogContent>
</Dialog>
</div>
<div className="overflow-x-auto">
<table className="min-w-full border-collapse border border-gray-300">
<thead>
<tr className="bg-gray-100 border-b">
<th className="px-4 py-2 text-left">Judul</th>
<th className="px-4 py-2 text-left">Konten</th>
<th className="px-4 py-2 text-left">Kategory</th>
<th className="px-4 py-2 text-left">Kreator</th>
<th className="px-4 py-2 text-left">Diupload Oleh</th>
</tr>
</thead>
<tbody>
{uploadResults.map((item: any) => (
<tr key={item.id} className="border-b">
<td className="px-4 py-2 text-blue-500">
<Link
href={
Number(item?.fileType?.id) == 1
? `/contributor/content/image/detail/${item?.id}`
: Number(item?.fileType?.id) == 2
? `/contributor/content/video/detail/${item?.id}`
: Number(item?.fileType?.id) == 3
? `/contributor/content/teks/detail/${item?.id}`
: `/contributor/content/audio/detail/${item?.id}`
}
>
{item.title}
</Link>
</td>
<td className="px-4 py-2">{item.fileType.name}</td>
<td className="px-4 py-2">{item.category.name}</td>
<td className="px-4 py-2">
{item.creatorGroupLevelName}
</td>
<td className="px-4 py-2">{item.creatorName}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
2025-01-12 17:38:34 +00:00
)}
2025-01-08 20:01:32 +00:00
{showInput && (
<>
<div className="mt-4 border p-4 rounded bg-gray-50">
<Textarea
placeholder="Tulis tanggapan Anda di sini..."
value={message}
onChange={handleInputChange}
/>
<div className="flex justify-end mt-3">
<Button
color="primary"
onClick={() => postData()}
type="button"
>
Kirim Tanggapan
</Button>
</div>
2025-01-08 20:01:32 +00:00
</div>
<div className="mt-6">
<h3 className="text-lg font-bold text-center">Tanggapan</h3>
{listData?.map((item: any) => (
<div key={item.id} className="flex flex-col gap-3 mt-2 ">
<div className="flex flex-row gap-3">
<Avatar className="mt-2">
<AvatarImage
src={"/assets/avatar-profile.png"}
alt={`@${item.username}`}
/>
</Avatar>
<div className="flex flex-col bg-slate-200 w-full px-2 py-2 rounded-md">
<div className="flex items-center justify-between">
<span className="text-gray-700 font-semibold">
{item.suggestionFrom.username}
</span>
<span className="text-gray-500 text-sm">
{formatDate(item.createdAt)}
</span>
</div>
<p className="text-gray-800 mt-1">{item.message}</p>
<div className="flex flex-row gap-2">
<div
className="flex items-center mt-1 text-blue-500 cursor-pointer"
onClick={() => handleReply(item.id)}
>
<DotSquare className="w-4 h-4" />
<span className="ml-1">Balas</span>
</div>
<div
className="flex items-center mt-1 text-red-500 cursor-pointer"
onClick={() => deleteData(item.id)}
>
<TrashIcon className="w-4 h-4" />
<span className="ml-1">Delete</span>
</div>
</div>
</div>
</div>
{replyingTo === item.id && (
<div className="ml-10 mt-2">
<textarea
id={`input-comment-${item.id}`}
className="w-full p-2 border rounded"
placeholder="Masukkan tanggapan anda"
/>
<button
className="mt-2 px-4 py-2 bg-blue-500 text-white rounded"
onClick={() => sendReplyData(item.id)}
>
Kirim
</button>
</div>
)}
{item.children?.length > 0 && (
<div className="ml-10 mt-2 flex flex-col">
{item.children.map((child: any) => (
<div
key={child.id}
className="flex flex-col gap-3 mt-2"
>
<div className="flex flex-row gap-3">
<Avatar className="mt-2">
<AvatarImage
src={"/assets/avatar-profile.png"}
alt={`@${child.username}`}
/>
</Avatar>
<div className="flex flex-col bg-slate-200 w-full px-2 py-2 rounded-md">
<div className="flex items-center justify-between">
<span className="text-gray-700 font-semibold">
{child.suggestionFrom.username}
</span>
<span className="text-gray-500 text-sm">
{formatDate(child.createdAt)}
</span>
</div>
<p className="text-gray-800 mt-1">
{child.message}
</p>
<div className="flex flex-row gap-2">
<div
className="flex items-center mt-1 text-blue-500 cursor-pointer"
onClick={() => handleReply(child.id)}
>
<DotSquare className="w-4 h-4" />
<span className="ml-1">Balas</span>
</div>
<div
className="flex items-center mt-1 text-red-500 cursor-pointer"
onClick={() => deleteData(child.id)}
>
<TrashIcon className="w-4 h-4" />
<span className="ml-1">Delete</span>
</div>
</div>
</div>
</div>
{replyingTo === child.id && (
<div className="ml-10 mt-2">
<textarea
id={`input-comment-${child.id}`}
className="w-full p-2 border rounded"
placeholder="Masukkan tanggapan anda"
/>
<button
className="mt-2 px-4 py-2 bg-blue-500 text-white rounded"
onClick={() => sendReplyData(child.id)}
>
Kirim
</button>
</div>
)}
{child.children?.length > 0 && (
<div className="ml-10 mt-2 flex flex-col mb-3">
{child.children.map((child2: any) => (
<div
key={child2.id}
className="flex flex-col gap-3 mt-2"
>
<div className="flex flex-row gap-3 ">
<Avatar className="mt-2">
<AvatarImage
src={"/assets/avatar-profile.png"}
alt={`@${child2.username}`}
/>
</Avatar>
<div className="flex flex-col bg-slate-200 w-full px-2 py-2 rounded-md">
<div className="flex items-center justify-between">
<span className="text-gray-700 font-semibold">
{child2.suggestionFrom.username}
</span>
<span className="text-gray-500 text-sm">
{formatDate(child2.createdAt)}
</span>
</div>
<p className="text-gray-800 mt-1">
{child2.message}
</p>
<div className="flex flex-row gap-2">
<div
className="flex items-center mt-1 text-red-500 cursor-pointer"
onClick={() =>
deleteData(child2.id)
}
>
<TrashIcon className="w-4 h-4" />
<span className="ml-1">
Delete
</span>
</div>
</div>
</div>
</div>
</div>
))}
</div>
)}
</div>
))}
</div>
)}
</div>
))}
2025-01-08 20:01:32 +00:00
</div>
</>
)}
<div className="flex lg:justify-center mt-4">
2025-01-11 14:30:35 +00:00
{detail?.createdBy?.id == Number(userId) &&
detail?.isDone !== true ? (
<>
<Button
type="button"
color="warning"
variant={"outline"}
className="btn btn-outline-danger"
onClick={() => handleDeleteAssignment()}
>
Hapus Tugas
</Button>
<Button
color="primary"
type="button"
variant={"default"}
className="btn btn-primary ml-3"
onClick={() => handleAssignmentDone()}
>
Tugas Selesai
</Button>
</>
) : (
""
)}
</div>
</form>
</div>
) : (
""
)}
</Card>
);
}