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",
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",
header: "Code",

View File

@ -21,8 +21,19 @@ import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import Cookies from "js-cookie";
import { postBlog } from "@/service/blog/blog";
import { Textarea } from "@/components/ui/textarea";
import { DotSquare, InboxIcon, PaperclipIcon, SmileIcon } from "lucide-react";
import { detailMedia } from "@/service/curated-content/curated-content";
import {
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/css";
import "swiper/css/free-mode";
@ -34,6 +45,14 @@ import "swiper/css/navigation";
import { FreeMode, Navigation, Pagination, Thumbs } from "swiper/modules";
import { Avatar, AvatarImage } from "@/components/ui/avatar";
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({
title: z.string().min(1, { message: "Judul diperlukan" }),
@ -50,6 +69,24 @@ type Category = {
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 = {
id: number;
title: string;
@ -108,22 +145,21 @@ export default function DetailImage() {
console.log(id);
const editor = useRef(null);
type DetailSchema = z.infer<typeof detailSchema>;
const userLevelNumber = getCookiesDecrypt("ulne");
const userId = getCookiesDecrypt("uie");
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const taskId = Cookies.get("taskId");
const scheduleId = Cookies.get("scheduleId");
const scheduleType = Cookies.get("scheduleType");
const [selectedTarget, setSelectedTarget] = useState("");
// const [detail, setDetail] = useState({
// title: null,
// tags: null,
// files: [],
// fileType: null,
// });
const [detail, setDetail] = useState<curationDetail>();
const [refresh] = useState(false);
const [detailThumb, setDetailThumb] = useState<any>([]);
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 {
control,
@ -142,31 +178,93 @@ export default function DetailImage() {
setReplyingTo(commentId);
};
const addReply = (commentId: number) => {
if (replyText.trim()) {
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;
});
const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
setMessage(e.target.value);
};
setCommentsData(newCommentData);
setReplyText("");
useEffect(() => {
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);
}
};
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(() => {
async function initState() {
if (id) {
@ -174,16 +272,29 @@ export default function DetailImage() {
const details = response?.data?.data;
setDetail(details);
setSelectedFileId(details?.files[0]?.id);
const filesData = details.files || [];
const fileUrls = filesData.map((file: { thumbnailFileUrl: string }) =>
file.thumbnailFileUrl ? file.thumbnailFileUrl : "default-image.jpg"
);
const fileUrls = filesData.map((file: any) => ({
id: file.id,
thumbnailFileUrl: file.thumbnailFileUrl || "default-image.jpg",
}));
setDetailThumb(fileUrls);
}
}
initState();
}, [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 (
<div className="flex gap-10">
{detail !== undefined ? (
@ -336,11 +447,14 @@ export default function DetailImage() {
className="w-full"
>
{detailThumb?.map((data: any) => (
<SwiperSlide key={data.id}>
<SwiperSlide
key={data.id}
onClick={() => handleFileClick(data.id)}
>
<img
className="object-fill h-full w-full"
src={data}
alt={` ${data.id}`}
src={data.thumbnailFileUrl}
alt={`File ID: ${data.id}`}
/>
</SwiperSlide>
))}
@ -357,11 +471,14 @@ export default function DetailImage() {
// className="mySwiper2"
>
{detailThumb?.map((data: any) => (
<SwiperSlide key={data.id}>
<SwiperSlide
key={data.id}
onClick={() => handleFileClick(data.id)}
>
<img
className="object-cover h-[60px] w-[80px]"
src={data}
alt={` ${data.id}`}
className="object-fill h-full w-full"
src={data.thumbnailFileUrl}
alt={`File ID: ${data.id}`}
/>
</SwiperSlide>
))}
@ -432,112 +549,186 @@ export default function DetailImage() {
</CardContent>
<CardContent>
<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">
<Label className="text-xl text-black">Komentar</Label>
{commentsData.map((comment) => (
<div
key={comment.id}
className="flex items-start gap-3 mt-2"
>
<Avatar>
<AvatarImage
src={comment.avatar}
alt={`@${comment.username}`}
/>
</Avatar>
<div className="flex flex-col">
<span className="text-gray-700 font-semibold">
{comment.username}
</span>
<span className="text-gray-500 text-sm">
{comment.date}
</span>
<p className="text-gray-800 mt-1">{comment.text}</p>
<div
className="flex items-center mt-1 text-blue-500 cursor-pointer"
onClick={() => handleReply(comment.id)}
>
<DotSquare className="w-4 h-4" />
<span className="ml-1">Balas</span>
<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 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
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.messageFrom.fullname}
</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>
{comment.replies.length > 0 && (
<div className="ml-8 mt-2">
{comment.replies.map((reply: any, index: any) => (
<div
key={index}
className="flex items-start gap-3 mt-1"
>
<Avatar>
</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="https://github.com/shadcn.png"
alt={`@${reply.username}`}
src={"/assets/avatar-profile.png"}
alt={`@${child.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>
<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">
{reply.text}
{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>
))}
</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>
))}
{replyingTo !== null && (
<div className="mt-4">
<textarea
className="w-full p-2 border rounded-md"
rows={3}
placeholder="Tulis balasan..."
value={replyText}
onChange={(e) => setReplyText(e.target.value)}
></textarea>
<button
className="mt-2 bg-yellow-500 text-white px-4 py-2 rounded-md"
onClick={() => addReply(replyingTo)}
>
Kirim
</button>
</div>
)}
</div>
</div>
</CardContent>

View File

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

View File

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

View File

@ -23,6 +23,8 @@ import JoditEditor from "jodit-react";
import {
createAssignmentResponse,
createTask,
deleteAssignmentResponse,
getAcceptance,
getAssignmentResponseList,
getTask,
getUserLevelForAssignments,
@ -34,12 +36,13 @@ import {
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { ChevronDown, ChevronUp } from "lucide-react";
import { ChevronDown, ChevronUp, DotSquare, TrashIcon } from "lucide-react";
import dynamic from "next/dynamic";
import { Link } from "@/components/navigation";
import { Textarea } from "@/components/ui/textarea";
import { loading } from "@/lib/swal";
import { close, loading } from "@/lib/swal";
import { getCookiesDecrypt } from "@/lib/utils";
import { Avatar, AvatarImage } from "@/components/ui/avatar";
const taskSchema = z.object({
uniqueCode: z.string().min(1, { message: "Judul diperlukan" }),
@ -85,6 +88,24 @@ const ViewEditor = dynamic(
{ 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() {
const MySwal = withReactContent(Swal);
const router = useRouter();
@ -123,6 +144,10 @@ export default function FormTaskDetail() {
const [showInput, setShowInput] = useState<boolean>(false);
const [listData, setListData] = useState([]);
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 [unitSelection, setUnitSelection] = useState({
@ -307,10 +332,6 @@ export default function FormTaskDetail() {
}));
};
// const handleToggleInput = () => {
// setShowInput((prev: any) => !prev); // Toggle visibility of input field
// };
useEffect(() => {
async function initState() {
// loading();
@ -324,11 +345,6 @@ export default function FormTaskDetail() {
initState();
}, []);
// const handleSubmitResponse = () => {
// console.log("Response Submitted:", response);
// setShowInput(false); // Optionally hide the input after submission
// };
const handleToggleInput = (): void => {
setShowInput(!showInput);
};
@ -356,12 +372,39 @@ export default function FormTaskDetail() {
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() {
if (message?.length > 1) {
loading();
const data = {
assignmentId: id,
message, // Gunakan response di sini
message,
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 (
<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)}>
<div className="gap-5 mb-5">
<div className="space-y-2">
@ -546,7 +671,7 @@ export default function FormTaskDetail() {
<div className="mt-6">
<Label>Tipe Penugasan</Label>
<RadioGroup
value={detail.assignmentMainType.id.toString()} // State yang dipetakan ke value RadioGroup
value={detail.assignmentMainType.id.toString()}
onValueChange={(value) => setMainType(value)}
// value={String(mainType)}
// onValueChange={(value) => setMainType(Number(value))}
@ -575,8 +700,8 @@ export default function FormTaskDetail() {
<div className="mt-6">
<Label>Jenis Penugasan</Label>
<RadioGroup
value={detail.assignmentType.id.toString()} // State yang dipetakan ke value RadioGroup
onValueChange={(value) => setType(value)} // Mengubah nilai state ketika pilihan berubah
value={detail.assignmentType.id.toString()}
onValueChange={(value) => setType(value)}
className="flex flex-wrap gap-3"
>
<div className="flex items-center gap-2">
@ -690,27 +815,163 @@ export default function FormTaskDetail() {
</div>
)}
<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) => (
<div
key={item.id}
className="border p-4 mt-2 rounded bg-gray-100 flex flex-col"
>
<div className="flex justify-between items-center">
<span className="font-medium">{item.name}</span>
<span className="text-sm text-gray-500">
{item.timestamp}
</span>
</div>
<p className="mt-2">{item.content}</p>
<div className="flex gap-2 mt-2">
<button className="text-blue-500 hover:underline">
Balas
</button>
<button className="text-red-500 hover:underline">
Hapus
</button>
<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>
))}
</div>
@ -726,10 +987,10 @@ export default function FormTaskDetail() {
)}
</div>
</form>
) : (
""
)}
</div>
</div>
) : (
""
)}
</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) {
const url = `media?id=${id}`;
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) {
const url = `assignment/response?assignmentId=${id}`;
return httpGetInterceptor({ url });
return httpGetInterceptor(url);
}
export async function createAssignmentResponse(data: any) {