feat:curated-content, komen detail curated-content,detail tanggapan task

This commit is contained in:
Anang Yusman 2025-01-10 01:59:20 +08:00
parent fdf810d197
commit d0c5f7d42f
8 changed files with 693 additions and 195 deletions

View File

@ -23,8 +23,23 @@ const columns: ColumnDef<any>[] = [
{ {
accessorKey: "title", accessorKey: "title",
header: "Title", header: "Title",
cell: ({ row }) => <span>{row.getValue("title")}</span>, cell: ({ row }) => (
<div>
<span>{row.getValue("title")}</span>
{row.original.isForward && (
<Button
variant={"outline"}
color="primary"
size="sm"
className="ml-3 rounded-xl"
>
Forward
</Button>
)}
</div>
),
}, },
{ {
accessorKey: "uniqueCode", accessorKey: "uniqueCode",
header: "Code", header: "Code",

View File

@ -21,8 +21,19 @@ import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { postBlog } from "@/service/blog/blog"; import { postBlog } from "@/service/blog/blog";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { DotSquare, InboxIcon, PaperclipIcon, SmileIcon } from "lucide-react"; import {
import { detailMedia } from "@/service/curated-content/curated-content"; DotSquare,
InboxIcon,
PaperclipIcon,
SmileIcon,
TrashIcon,
} from "lucide-react";
import {
deleteMediaCurationMessage,
detailMedia,
getMediaCurationMessage,
saveMediaCurationMessage,
} from "@/service/curated-content/curated-content";
import { Swiper, SwiperSlide } from "swiper/react"; import { Swiper, SwiperSlide } from "swiper/react";
import "swiper/css"; import "swiper/css";
import "swiper/css/free-mode"; import "swiper/css/free-mode";
@ -34,6 +45,14 @@ import "swiper/css/navigation";
import { FreeMode, Navigation, Pagination, Thumbs } from "swiper/modules"; import { FreeMode, Navigation, Pagination, Thumbs } from "swiper/modules";
import { Avatar, AvatarImage } from "@/components/ui/avatar"; import { Avatar, AvatarImage } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { listData } from "@/service/landing/landing";
import {
createAssignmentResponse,
deleteAssignmentResponse,
getAssignmentResponseList,
} from "@/service/task";
import { getCookiesDecrypt } from "@/lib/utils";
import { close, loading } from "@/lib/swal";
const detailSchema = z.object({ const detailSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }), title: z.string().min(1, { message: "Judul diperlukan" }),
@ -50,6 +69,24 @@ type Category = {
categoryName: string; categoryName: string;
}; };
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} `;
};
export type curationDetail = { export type curationDetail = {
id: number; id: number;
title: string; title: string;
@ -108,22 +145,21 @@ export default function DetailImage() {
console.log(id); console.log(id);
const editor = useRef(null); const editor = useRef(null);
type DetailSchema = z.infer<typeof detailSchema>; type DetailSchema = z.infer<typeof detailSchema>;
const userLevelNumber = getCookiesDecrypt("ulne");
const userId = getCookiesDecrypt("uie");
const [selectedFiles, setSelectedFiles] = useState<File[]>([]); const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const taskId = Cookies.get("taskId"); const taskId = Cookies.get("taskId");
const scheduleId = Cookies.get("scheduleId"); const scheduleId = Cookies.get("scheduleId");
const scheduleType = Cookies.get("scheduleType"); const scheduleType = Cookies.get("scheduleType");
const [selectedTarget, setSelectedTarget] = useState(""); const [selectedTarget, setSelectedTarget] = useState("");
// const [detail, setDetail] = useState({
// title: null,
// tags: null,
// files: [],
// fileType: null,
// });
const [detail, setDetail] = useState<curationDetail>(); const [detail, setDetail] = useState<curationDetail>();
const [refresh] = useState(false); const [refresh] = useState(false);
const [detailThumb, setDetailThumb] = useState<any>([]); const [detailThumb, setDetailThumb] = useState<any>([]);
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null); const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
const [showInput, setShowInput] = useState<boolean>(false);
const [selectedFileId, setSelectedFileId] = useState(null);
const [listData, setListData] = useState([]);
const [message, setMessage] = useState("");
const { const {
control, control,
@ -142,31 +178,93 @@ export default function DetailImage() {
setReplyingTo(commentId); setReplyingTo(commentId);
}; };
const addReply = (commentId: number) => { const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
if (replyText.trim()) { setMessage(e.target.value);
const newCommentData = commentsData.map((comment: any) => {
if (comment.id === commentId) {
return {
...comment,
replies: [
...comment.replies,
{
text: replyText,
username: "You",
date: new Date().toLocaleString(),
},
],
}; };
}
return comment;
});
setCommentsData(newCommentData); useEffect(() => {
setReplyText(""); async function initState() {
// loading();
const response = await getMediaCurationMessage(selectedFileId);
console.log("data", response?.data?.data);
console.log("userLvl", userLevelNumber);
setListData(response?.data?.data);
close();
}
initState();
}, [selectedFileId]);
// const postData = () => {
// sendSuggestionParent();
// };
const postData = async () => {
if (message?.length > 1 && selectedFileId) {
try {
const data = {
mediaUploadFileId: selectedFileId,
message,
parentId: null,
};
const response = await saveMediaCurationMessage(data);
console.log("Komentar terkirim:", response);
const responseGet = await getMediaCurationMessage(selectedFileId);
setListData(responseGet?.data?.data);
setMessage("");
} catch (error) {
console.error("Error posting comment:", error);
}
} else {
console.log("Pesan atau file ID tidak valid.");
}
};
const sendReplyData = async (parentId: number) => {
const inputElement = document.querySelector(
`#input-comment-${parentId}`
) as HTMLTextAreaElement;
if (inputElement?.value?.length > 1 && selectedFileId) {
loading();
const data = {
mediaUploadFileId: selectedFileId,
message: inputElement.value,
parentId,
};
console.log("Sending reply:", data);
const response = await saveMediaCurationMessage(data);
console.log(response);
const responseGet = await getMediaCurationMessage(selectedFileId);
console.log("Updated comments:", responseGet?.data?.data);
setListData(responseGet?.data?.data);
inputElement.value = "";
close();
setReplyingTo(null); setReplyingTo(null);
} }
}; };
async function deleteDataSuggestion(dataId: any) {
loading();
const response = await deleteMediaCurationMessage(dataId);
console.log(response);
const responseGet = await getMediaCurationMessage(selectedFileId);
console.log(responseGet?.data?.data);
setListData(responseGet?.data?.data);
close();
}
const deleteData = (dataId: any) => {
deleteDataSuggestion(dataId);
console.log(dataId);
};
useEffect(() => { useEffect(() => {
async function initState() { async function initState() {
if (id) { if (id) {
@ -174,16 +272,29 @@ export default function DetailImage() {
const details = response?.data?.data; const details = response?.data?.data;
setDetail(details); setDetail(details);
setSelectedFileId(details?.files[0]?.id);
const filesData = details.files || []; const filesData = details.files || [];
const fileUrls = filesData.map((file: { thumbnailFileUrl: string }) => const fileUrls = filesData.map((file: any) => ({
file.thumbnailFileUrl ? file.thumbnailFileUrl : "default-image.jpg" id: file.id,
); thumbnailFileUrl: file.thumbnailFileUrl || "default-image.jpg",
}));
setDetailThumb(fileUrls); setDetailThumb(fileUrls);
} }
} }
initState(); initState();
}, [id, refresh]); }, [id, refresh]);
const handleFileClick = async (fileId: any) => {
setSelectedFileId(fileId);
try {
const response = await getMediaCurationMessage(fileId);
console.log("Data komentar:", response?.data?.data);
setListData(response?.data?.data);
} catch (error) {
console.error("Error fetching comments:", error);
}
};
return ( return (
<div className="flex gap-10"> <div className="flex gap-10">
{detail !== undefined ? ( {detail !== undefined ? (
@ -336,11 +447,14 @@ export default function DetailImage() {
className="w-full" className="w-full"
> >
{detailThumb?.map((data: any) => ( {detailThumb?.map((data: any) => (
<SwiperSlide key={data.id}> <SwiperSlide
key={data.id}
onClick={() => handleFileClick(data.id)}
>
<img <img
className="object-fill h-full w-full" className="object-fill h-full w-full"
src={data} src={data.thumbnailFileUrl}
alt={` ${data.id}`} alt={`File ID: ${data.id}`}
/> />
</SwiperSlide> </SwiperSlide>
))} ))}
@ -357,11 +471,14 @@ export default function DetailImage() {
// className="mySwiper2" // className="mySwiper2"
> >
{detailThumb?.map((data: any) => ( {detailThumb?.map((data: any) => (
<SwiperSlide key={data.id}> <SwiperSlide
key={data.id}
onClick={() => handleFileClick(data.id)}
>
<img <img
className="object-cover h-[60px] w-[80px]" className="object-fill h-full w-full"
src={data} src={data.thumbnailFileUrl}
alt={` ${data.id}`} alt={`File ID: ${data.id}`}
/> />
</SwiperSlide> </SwiperSlide>
))} ))}
@ -432,112 +549,186 @@ export default function DetailImage() {
</CardContent> </CardContent>
<CardContent> <CardContent>
<div className="gap-5 mb-5"> <div className="gap-5 mb-5">
<div className="mt-5">
<Label className="text-xl text-black">Berikan Komentar</Label>
<div className="flex items-start gap-3">
<Avatar>
<AvatarImage
src="/images/avatar/avatar-1.png"
alt="@shadcn"
/>
</Avatar>
<textarea
className="flex-grow p-2 border rounded-lg focus:outline-none focus:border-yellow-400"
placeholder="Tuliskan komentar Anda di sini.."
></textarea>
</div>
<div className="flex items-center mt-2 gap-3">
<button className="flex items-center text-gray-600 hover:text-gray-800">
<PaperclipIcon className="w-5 h-5" />
Lampirkan
</button>
<button className="flex items-center text-gray-600 hover:text-gray-800">
<SmileIcon className="w-5 h-5" />
Emoticon
</button>
<button className="ml-auto px-4 py-1 bg-yellow-500 text-white rounded-lg hover:bg-yellow-600">
Kirim
</button>
</div>
</div>
<div className="mt-5"> <div className="mt-5">
<Label className="text-xl text-black">Komentar</Label> <Label className="text-xl text-black">Komentar</Label>
{commentsData.map((comment) => ( <div className="mt-4 border p-4 rounded bg-gray-50">
<div <Textarea
key={comment.id} placeholder="Tulis tanggapan Anda di sini..."
className="flex items-start gap-3 mt-2" value={message}
onChange={handleInputChange}
/>
<div className="flex justify-end mt-3">
<Button
color="primary"
onClick={() => postData()}
type="button"
> >
<Avatar> Kirim Komentar
</Button>
</div>
</div>
{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 <AvatarImage
src={comment.avatar} src={"/assets/avatar-profile.png"}
alt={`@${comment.username}`} alt={`@${item.username}`}
/> />
</Avatar> </Avatar>
<div className="flex flex-col"> <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"> <span className="text-gray-700 font-semibold">
{comment.username} {item.messageFrom.fullname}
</span> </span>
<span className="text-gray-500 text-sm"> <span className="text-gray-500 text-sm">
{comment.date} {formatDate(item.createdAt)}
</span> </span>
<p className="text-gray-800 mt-1">{comment.text}</p> </div>
<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" className="flex items-center mt-1 text-blue-500 cursor-pointer"
onClick={() => handleReply(comment.id)} onClick={() => handleReply(item.id)}
> >
<DotSquare className="w-4 h-4" /> <DotSquare className="w-4 h-4" />
<span className="ml-1">Balas</span> <span className="ml-1">Balas</span>
</div> </div> */}
{comment.replies.length > 0 && (
<div className="ml-8 mt-2">
{comment.replies.map((reply: any, index: any) => (
<div <div
key={index} className="flex items-center mt-1 text-red-500 cursor-pointer"
className="flex items-start gap-3 mt-1" onClick={() => deleteData(item.id)}
> >
<Avatar> <TrashIcon className="w-4 h-4" />
<AvatarImage <span className="ml-1">Delete</span>
src="https://github.com/shadcn.png"
alt={`@${reply.username}`}
/>
</Avatar>
<div className="flex flex-col">
<span className="text-gray-700 font-semibold">
{reply.username}
</span>
<span className="text-gray-500 text-sm">
{reply.date}
</span>
<p className="text-gray-800 mt-1">
{reply.text}
</p>
</div> </div>
</div> </div>
))}
</div>
)}
</div> </div>
</div> </div>
))} {replyingTo === item.id && (
{replyingTo !== null && ( <div className="ml-10 mt-2">
<div className="mt-4">
<textarea <textarea
className="w-full p-2 border rounded-md" id={`input-comment-${item.id}`}
rows={3} className="w-full p-2 border rounded"
placeholder="Tulis balasan..." placeholder="Masukkan tanggapan anda"
value={replyText} />
onChange={(e) => setReplyText(e.target.value)}
></textarea>
<button <button
className="mt-2 bg-yellow-500 text-white px-4 py-2 rounded-md" className="mt-2 px-4 py-2 bg-blue-500 text-white rounded"
onClick={() => addReply(replyingTo)} onClick={() => sendReplyData(item.id)}
> >
Kirim Kirim
</button> </button>
</div> </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">
{item.messageFrom.fullname}
</span>
<span className="text-gray-500 text-sm">
{formatDate(item.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">
{item.messageFrom.fullname}
</span>
<span className="text-gray-500 text-sm">
{formatDate(item.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>
))}
</div> </div>
</div> </div>
</CardContent> </CardContent>

View File

@ -8,7 +8,8 @@ import {
CarouselNext, CarouselNext,
CarouselPrevious, CarouselPrevious,
} from "@/components/ui/carousel"; } from "@/components/ui/carousel";
import { getListContent } from "@/service/landing/landing"; import { listCuratedContent } from "@/service/curated-content/curated-content";
import { import {
formatDateToIndonesian, formatDateToIndonesian,
generateLocalizedPath, generateLocalizedPath,
@ -35,10 +36,12 @@ const ImageSliderPage = () => {
const [imageData, setImageData] = useState<any>(); const [imageData, setImageData] = useState<any>();
const [displayImage, setDisplayImage] = useState<any[]>([]); const [displayImage, setDisplayImage] = useState<any[]>([]);
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const [limit, setLimit] = React.useState(10);
const [search, setSearch] = React.useState("");
useEffect(() => { useEffect(() => {
fetchData(); fetchData();
}, [page]); }, [page, limit, search]);
useEffect(() => { useEffect(() => {
if (imageData?.length > 0) { if (imageData?.length > 0) {
@ -49,12 +52,7 @@ const ImageSliderPage = () => {
}, [imageData]); }, [imageData]);
const fetchData = async () => { const fetchData = async () => {
const response = await getListContent({ const response = await listCuratedContent(search, limit, page - 1, 1, "1");
page: page - 1,
size: 6,
sortBy: "createdAt",
contentTypeId: "1",
});
console.log(response); console.log(response);
const data = response?.data?.data; const data = response?.data?.data;

View File

@ -1,6 +1,7 @@
"use client"; "use client";
import { Link } from "@/components/navigation"; import { Link } from "@/components/navigation";
import { Card, CardContent } from "@/components/ui/card"; import { Card, CardContent } from "@/components/ui/card";
import { listCuratedContent } from "@/service/curated-content/curated-content";
import { getListContent } from "@/service/landing/landing"; import { getListContent } from "@/service/landing/landing";
import { formatDateToIndonesian } from "@/utils/globals"; import { formatDateToIndonesian } from "@/utils/globals";
import { Icon } from "@iconify/react/dist/iconify.js"; import { Icon } from "@iconify/react/dist/iconify.js";
@ -11,10 +12,12 @@ const VideoSliderPage = () => {
const [allVideoData, setAllVideoData] = useState<any[]>([]); const [allVideoData, setAllVideoData] = useState<any[]>([]);
const [displayVideos, setDisplayVideos] = useState<any[]>([]); const [displayVideos, setDisplayVideos] = useState<any[]>([]);
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const [limit, setLimit] = React.useState(10);
const [search, setSearch] = React.useState("");
useEffect(() => { useEffect(() => {
initFetch(); fetchData();
}, []); }, [page, limit, search]);
useEffect(() => { useEffect(() => {
if (allVideoData?.length > 0) { if (allVideoData?.length > 0) {
@ -24,14 +27,13 @@ const VideoSliderPage = () => {
} }
}, [allVideoData]); }, [allVideoData]);
const initFetch = async () => { const fetchData = async () => {
const response = await getListContent({ const response = await listCuratedContent(search, limit, page - 1, 1, "2");
page: page - 1, console.log(response);
size: 12,
sortBy: "createdAt", const data = response?.data?.data;
contentTypeId: "2", const contentData = data?.content;
}); setAllVideoData(contentData);
setAllVideoData(response?.data?.data?.content || []);
}; };
const shuffleAndSetVideos = () => { const shuffleAndSetVideos = () => {

View File

@ -23,6 +23,8 @@ import JoditEditor from "jodit-react";
import { import {
createAssignmentResponse, createAssignmentResponse,
createTask, createTask,
deleteAssignmentResponse,
getAcceptance,
getAssignmentResponseList, getAssignmentResponseList,
getTask, getTask,
getUserLevelForAssignments, getUserLevelForAssignments,
@ -34,12 +36,13 @@ import {
DialogTitle, DialogTitle,
DialogTrigger, DialogTrigger,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { ChevronDown, ChevronUp } from "lucide-react"; import { ChevronDown, ChevronUp, DotSquare, TrashIcon } from "lucide-react";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import { Link } from "@/components/navigation"; import { Link } from "@/components/navigation";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { loading } from "@/lib/swal"; import { close, loading } from "@/lib/swal";
import { getCookiesDecrypt } from "@/lib/utils"; import { getCookiesDecrypt } from "@/lib/utils";
import { Avatar, AvatarImage } from "@/components/ui/avatar";
const taskSchema = z.object({ const taskSchema = z.object({
uniqueCode: z.string().min(1, { message: "Judul diperlukan" }), uniqueCode: z.string().min(1, { message: "Judul diperlukan" }),
@ -85,6 +88,24 @@ const ViewEditor = dynamic(
{ ssr: false } { 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} `;
};
export default function FormTaskDetail() { export default function FormTaskDetail() {
const MySwal = withReactContent(Swal); const MySwal = withReactContent(Swal);
const router = useRouter(); const router = useRouter();
@ -123,6 +144,10 @@ export default function FormTaskDetail() {
const [showInput, setShowInput] = useState<boolean>(false); const [showInput, setShowInput] = useState<boolean>(false);
const [listData, setListData] = useState([]); const [listData, setListData] = useState([]);
const [message, setMessage] = useState<string>(""); const [message, setMessage] = useState<string>("");
const [sentAcceptance, setSentAcceptance] = useState([]);
const [acceptAcceptance, setAcceptAcceptance] = useState([]);
const [refreshAcceptance, setRefreshAcceptance] = useState(false);
const [replyingTo, setReplyingTo] = useState<number | null>(null);
const [platformTypeVisible, setPlatformTypeVisible] = useState(false); const [platformTypeVisible, setPlatformTypeVisible] = useState(false);
const [unitSelection, setUnitSelection] = useState({ const [unitSelection, setUnitSelection] = useState({
@ -307,10 +332,6 @@ export default function FormTaskDetail() {
})); }));
}; };
// const handleToggleInput = () => {
// setShowInput((prev: any) => !prev); // Toggle visibility of input field
// };
useEffect(() => { useEffect(() => {
async function initState() { async function initState() {
// loading(); // loading();
@ -324,11 +345,6 @@ export default function FormTaskDetail() {
initState(); initState();
}, []); }, []);
// const handleSubmitResponse = () => {
// console.log("Response Submitted:", response);
// setShowInput(false); // Optionally hide the input after submission
// };
const handleToggleInput = (): void => { const handleToggleInput = (): void => {
setShowInput(!showInput); setShowInput(!showInput);
}; };
@ -356,12 +372,39 @@ export default function FormTaskDetail() {
sendSuggestionParent(); 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);
}
};
async function sendSuggestionParent() { async function sendSuggestionParent() {
if (message?.length > 1) { if (message?.length > 1) {
loading(); loading();
const data = { const data = {
assignmentId: id, assignmentId: id,
message, // Gunakan response di sini message,
parentId: null, parentId: null,
}; };
@ -377,11 +420,93 @@ export default function FormTaskDetail() {
} }
} }
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);
};
// async function sendSuggestionChild(parentId: any) {
// const msg = document.querySelectorAll(`#input-comment-${parentId}`)[0]
// .value;
// if (msg?.length > 1) {
// loading();
// const data = {
// assignmentId: id,
// message: msg,
// 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);
// // $(":input").val("");
// close();
// }
// }
async function getListAcceptance() {
const isAccept = true;
const resSent = await getAcceptance(id, !isAccept);
setSentAcceptance(resSent?.data?.data);
const resAccept = await getAcceptance(id, isAccept);
const acceptanceSort = resAccept?.data?.data?.sort((a: any, b: any) => {
const dateA = new Date(a.acceptAt).getTime();
const dateB = new Date(b.acceptAt).getTime();
return dateA - dateB;
});
console.log("Data sort :", acceptanceSort);
setAcceptAcceptance(acceptanceSort);
}
useEffect(() => {
async function initState() {
getListAcceptance();
}
initState();
}, [refreshAcceptance]);
const setFinishAcceptance = async (id: any, isFinish: any) => {
if (!isFinish) {
loading();
setRefreshAcceptance(!refreshAcceptance);
close();
}
};
const handleReply = (id: any) => {
setReplyingTo(id);
};
return ( return (
<Card> <Card>
<div className="px-6 py-6">
<p className="text-lg font-semibold mb-3">Form Penugasan</p>
{detail !== undefined ? ( {detail !== undefined ? (
<div className="px-6 py-6">
<div className="flex flex-row justify-between">
<p className="text-lg font-semibold mb-3">Detail Penugasan</p>
<div className="flex gap-3">
<Button color="primary">2 Terkirim</Button>
<Button color="warning">0 Diterima</Button>
</div>
</div>
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<div className="gap-5 mb-5"> <div className="gap-5 mb-5">
<div className="space-y-2"> <div className="space-y-2">
@ -546,7 +671,7 @@ export default function FormTaskDetail() {
<div className="mt-6"> <div className="mt-6">
<Label>Tipe Penugasan</Label> <Label>Tipe Penugasan</Label>
<RadioGroup <RadioGroup
value={detail.assignmentMainType.id.toString()} // State yang dipetakan ke value RadioGroup value={detail.assignmentMainType.id.toString()}
onValueChange={(value) => setMainType(value)} onValueChange={(value) => setMainType(value)}
// value={String(mainType)} // value={String(mainType)}
// onValueChange={(value) => setMainType(Number(value))} // onValueChange={(value) => setMainType(Number(value))}
@ -575,8 +700,8 @@ export default function FormTaskDetail() {
<div className="mt-6"> <div className="mt-6">
<Label>Jenis Penugasan</Label> <Label>Jenis Penugasan</Label>
<RadioGroup <RadioGroup
value={detail.assignmentType.id.toString()} // State yang dipetakan ke value RadioGroup value={detail.assignmentType.id.toString()}
onValueChange={(value) => setType(value)} // Mengubah nilai state ketika pilihan berubah onValueChange={(value) => setType(value)}
className="flex flex-wrap gap-3" className="flex flex-wrap gap-3"
> >
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@ -690,27 +815,163 @@ export default function FormTaskDetail() {
</div> </div>
)} )}
<div className="mt-6"> <div className="mt-6">
<h3 className="text-lg font-bold">Tanggapan</h3> <h3 className="text-lg font-bold text-center">Tanggapan</h3>
{listData?.map((item: any) => ( {listData?.map((item: any) => (
<div <div key={item.id} className="flex flex-col gap-3 mt-2 ">
key={item.id} <div className="flex flex-row gap-3">
className="border p-4 mt-2 rounded bg-gray-100 flex flex-col" <Avatar className="mt-2">
> <AvatarImage
<div className="flex justify-between items-center"> src={"/assets/avatar-profile.png"}
<span className="font-medium">{item.name}</span> alt={`@${item.username}`}
<span className="text-sm text-gray-500"> />
{item.timestamp} </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> </span>
</div> </div>
<p className="mt-2">{item.content}</p> <p className="text-gray-800 mt-1">{item.message}</p>
<div className="flex gap-2 mt-2"> <div className="flex flex-row gap-2">
<button className="text-blue-500 hover:underline"> <div
Balas className="flex items-center mt-1 text-blue-500 cursor-pointer"
</button> onClick={() => handleReply(item.id)}
<button className="text-red-500 hover:underline"> >
Hapus <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> </button>
</div> </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> </div>
))} ))}
</div> </div>
@ -726,10 +987,10 @@ export default function FormTaskDetail() {
)} )}
</div> </div>
</form> </form>
</div>
) : ( ) : (
"" ""
)} )}
</div>
</Card> </Card>
); );
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -1,6 +1,37 @@
import { httpGetInterceptor } from "../http-config/http-interceptor-service"; import {
httpDeleteInterceptor,
httpGetInterceptor,
httpPostInterceptor,
} from "../http-config/http-interceptor-service";
export async function detailMedia(id: any) { export async function detailMedia(id: any) {
const url = `media?id=${id}`; const url = `media?id=${id}`;
return httpGetInterceptor(url); return httpGetInterceptor(url);
} }
export async function listCuratedContent(
title: string = "",
limit: any,
page: any,
typeId: any,
curationType: any
// categoryId = ""
) {
const url = `media/curation/list?title=${title}&enablePage=1&sortBy=createdAt&sort=desc&size=${limit}&page=${page}&typeId=${typeId}&curationType=${curationType}`;
return httpGetInterceptor(url);
}
export async function getMediaCurationMessage(id: any) {
const url = `media/curation/message/list?mediaFileId=${id}`;
return httpGetInterceptor(url);
}
export async function saveMediaCurationMessage(data: any) {
const url = "media/curation/message";
return httpPostInterceptor(url, data);
}
export async function deleteMediaCurationMessage(id: any) {
const url = `media/curation/message?id=${id}`;
return httpDeleteInterceptor(url);
}

View File

@ -58,7 +58,7 @@ export async function getUserLevelForAssignments() {
export async function getAssignmentResponseList(id: any) { export async function getAssignmentResponseList(id: any) {
const url = `assignment/response?assignmentId=${id}`; const url = `assignment/response?assignmentId=${id}`;
return httpGetInterceptor({ url }); return httpGetInterceptor(url);
} }
export async function createAssignmentResponse(data: any) { export async function createAssignmentResponse(data: any) {