feat:curated-content, komen detail curated-content,detail tanggapan task
This commit is contained in:
parent
fdf810d197
commit
d0c5f7d42f
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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 = () => {
|
||||||
|
|
|
||||||
|
|
@ -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 |
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue