518 lines
17 KiB
TypeScript
518 lines
17 KiB
TypeScript
import { Button } from "@heroui/button";
|
|
import { Input, Textarea } from "@heroui/input";
|
|
import React, { useEffect, useState } from "react";
|
|
import { Controller, useForm } from "react-hook-form";
|
|
import * as z from "zod";
|
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
import {
|
|
deleteArticleComment,
|
|
editArticleComment,
|
|
getArticleComment,
|
|
otpRequest,
|
|
otpValidation,
|
|
postArticleComment,
|
|
} from "@/services/master-user";
|
|
import { error } from "@/config/swal";
|
|
import { UserProfileIcon } from "@/components/icons/globals";
|
|
import { convertDateFormat, getCookiesDecrypt } from "@/utils/global";
|
|
import Cookies from "js-cookie";
|
|
import OTPInput from "react-otp-input";
|
|
import Swal from "sweetalert2";
|
|
import withReactContent from "sweetalert2-react-content";
|
|
import { SendIcon, TimesIcon } from "@/components/icons";
|
|
import { saveActivity } from "@/services/activity-log";
|
|
import { usePathname } from "next/navigation";
|
|
|
|
const commentSchema = z.object({
|
|
name: z.string().min(1, {
|
|
message: "Judul harus diisi",
|
|
}),
|
|
email: z
|
|
.string()
|
|
.email({
|
|
message: "Email tidak valid",
|
|
})
|
|
.min(1, {
|
|
message: "Harus diisi",
|
|
}),
|
|
comment: z.string().min(1, {
|
|
message: "Deskripsi harus diisi",
|
|
}),
|
|
});
|
|
|
|
export default function Comment(props: { id: string | null }) {
|
|
const { id } = props;
|
|
const MySwal = withReactContent(Swal);
|
|
const pathname = usePathname();
|
|
const [needOtp, setNeedOtp] = useState(false);
|
|
const [otpValue, setOtpValue] = useState("");
|
|
const [commentList, setCommentList] = useState<any>([]);
|
|
const [openCommentId, setOpenCommentId] = useState(0);
|
|
const [editCommentId, setEditCommentId] = useState(0);
|
|
const [replyValue, setReplyValue] = useState("");
|
|
const [editValue, setEditValue] = useState("");
|
|
|
|
const [userId, setUserId] = useState<any>();
|
|
const [token, setToken] = useState<any>();
|
|
|
|
useEffect(() => {
|
|
const userId = getCookiesDecrypt("uie");
|
|
const token = Cookies.get("access_token");
|
|
setUserId(userId);
|
|
setToken(token);
|
|
}, []);
|
|
const formOptions = {
|
|
resolver: zodResolver(commentSchema),
|
|
};
|
|
type UserSettingSchema = z.infer<typeof commentSchema>;
|
|
const {
|
|
control,
|
|
handleSubmit,
|
|
formState: { errors },
|
|
setValue,
|
|
reset,
|
|
} = useForm<UserSettingSchema>(formOptions);
|
|
|
|
useEffect(() => {
|
|
fetchData();
|
|
}, [id]);
|
|
|
|
const fetchData = async () => {
|
|
const res = await getArticleComment(String(id));
|
|
setCommentList(res?.data?.data);
|
|
};
|
|
|
|
const onSubmit = async (values: z.infer<typeof commentSchema>) => {
|
|
if (!needOtp) {
|
|
const res = await otpRequest(values.email, values?.name);
|
|
if (res?.error) {
|
|
error(res.message);
|
|
return false;
|
|
}
|
|
setNeedOtp(true);
|
|
} else {
|
|
const validation = await otpValidation(values.email, otpValue);
|
|
if (validation?.error) {
|
|
error("OTP Tidak Sesuai");
|
|
return false;
|
|
}
|
|
|
|
const data = {
|
|
commentFromName: values.name,
|
|
commentFromEmail: values.email,
|
|
articleId: Number(id),
|
|
isPublic: false,
|
|
message: values.comment,
|
|
parentId: 0,
|
|
};
|
|
const res = await postArticleComment(data);
|
|
if (res?.error) {
|
|
error(res?.message);
|
|
return false;
|
|
}
|
|
const req: any = {
|
|
activityTypeId: 5,
|
|
url: "https://new.humas.polri.go.id/" + pathname,
|
|
articleId: Number(id),
|
|
};
|
|
|
|
const resActivity = await saveActivity(req);
|
|
reset();
|
|
fetchData();
|
|
setNeedOtp(false);
|
|
}
|
|
};
|
|
|
|
const handleDelete = async (id: number) => {
|
|
MySwal.fire({
|
|
title: "Delete Comment",
|
|
text: "",
|
|
icon: "warning",
|
|
showCancelButton: true,
|
|
cancelButtonColor: "#d33",
|
|
confirmButtonColor: "#3085d6",
|
|
confirmButtonText: "Simpan",
|
|
}).then((result) => {
|
|
if (result.isConfirmed) {
|
|
doDelete(id);
|
|
}
|
|
});
|
|
};
|
|
|
|
const doDelete = async (id: number) => {
|
|
const res = await deleteArticleComment(id);
|
|
if (res?.error) {
|
|
error(res?.message);
|
|
return false;
|
|
}
|
|
MySwal.fire({
|
|
title: "Sukses",
|
|
icon: "success",
|
|
confirmButtonColor: "#3085d6",
|
|
confirmButtonText: "OK",
|
|
}).then((result) => {
|
|
if (result.isConfirmed) {
|
|
}
|
|
});
|
|
fetchData();
|
|
};
|
|
|
|
const sendComment = async (idComment: number) => {
|
|
const data = {
|
|
articleId: Number(id),
|
|
isPublic: true,
|
|
message: replyValue,
|
|
parentId: idComment,
|
|
};
|
|
|
|
const res = await postArticleComment(data);
|
|
if (res?.error) {
|
|
error(res?.message);
|
|
return false;
|
|
}
|
|
const req: any = {
|
|
activityTypeId: 5,
|
|
url: "https://new.humas.polri.go.id/" + pathname,
|
|
articleId: Number(id),
|
|
userId: Number(userId),
|
|
};
|
|
|
|
const resActivity = await saveActivity(req, token);
|
|
|
|
fetchData();
|
|
};
|
|
|
|
// const sendActivity = async () => {};
|
|
const editComment = async (idComment: number, parentId: number) => {
|
|
const data = {
|
|
articleId: Number(id),
|
|
isPublic: true,
|
|
id: idComment,
|
|
message: editValue,
|
|
parentId: parentId,
|
|
};
|
|
|
|
const res = await editArticleComment(data, idComment);
|
|
if (res?.error) {
|
|
error(res?.message);
|
|
return false;
|
|
}
|
|
setEditCommentId(0);
|
|
fetchData();
|
|
};
|
|
|
|
const childComment = (parentId: number) => {
|
|
const filteredComment = commentList.filter(
|
|
(a: any) => a.parentId === parentId
|
|
);
|
|
|
|
return filteredComment.length > 0 ? (
|
|
<div className="flex flex-col gap-2 ml-4 w-full">
|
|
{filteredComment.map((list: any) => (
|
|
<div className="flex flex-row gap-2 " key={list?.id}>
|
|
<UserProfileIcon size={44} />
|
|
<div className="flex flex-col w-full pr-4">
|
|
<div className="flex justify-between gap-1 w-full">
|
|
<p className="text-sm font-semibold">{list?.commentFromName}</p>
|
|
<p className="text-xs">{convertDateFormat(list?.updatedAt)}</p>
|
|
</div>
|
|
{editCommentId === list?.id ? (
|
|
<div className="flex flex-row gap-2 items-center">
|
|
<Input
|
|
type="text"
|
|
id="editComment"
|
|
placeholder=""
|
|
label=""
|
|
value={editValue}
|
|
onValueChange={setEditValue}
|
|
endContent={
|
|
<div className="flex flex-row gap-2">
|
|
<a
|
|
className="cursor-pointer"
|
|
onClick={() => editComment(list?.id, list?.parentId)}
|
|
>
|
|
<SendIcon />
|
|
</a>
|
|
</div>
|
|
}
|
|
labelPlacement="outside"
|
|
className="w-full "
|
|
classNames={{
|
|
inputWrapper: [
|
|
"border-1 rounded-lg",
|
|
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
|
],
|
|
}}
|
|
variant="bordered"
|
|
/>
|
|
<a
|
|
className="cursor-pointer text-warning"
|
|
onClick={() => setEditCommentId(0)}
|
|
>
|
|
<TimesIcon />
|
|
</a>
|
|
</div>
|
|
) : (
|
|
<div className="flex justify-between gap-1 text-right">
|
|
<p className="text-sm">{list?.message}</p>
|
|
|
|
<div className="flex flex-row gap-2 justify-end">
|
|
{userId === "16" && (
|
|
<a
|
|
className="text-primary cursor-pointer text-xs"
|
|
onClick={() => {
|
|
setEditValue(list?.message);
|
|
setEditCommentId(list?.id);
|
|
}}
|
|
>
|
|
Edit
|
|
</a>
|
|
)}
|
|
{(userId === String(list?.commentFromId) ||
|
|
userId === "16") && (
|
|
<a
|
|
className="text-danger cursor-pointer text-xs"
|
|
onClick={() => handleDelete(list?.id)}
|
|
>
|
|
Hapus
|
|
</a>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : (
|
|
""
|
|
);
|
|
};
|
|
|
|
return (
|
|
<div className="px-0 lg:px-3 flex flex-col gap-3">
|
|
<form
|
|
className="py-3 px-4 flex flex-col gap-3 bg-gray-50 text-black dark:bg-black dark:text-white rounded-lg shadow-md"
|
|
onSubmit={handleSubmit(onSubmit)}
|
|
>
|
|
{!needOtp ? (
|
|
<>
|
|
<b>Tinggalkan balasan</b>
|
|
<p className="text-xs">
|
|
Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib
|
|
ditandai <span className="text-red-600">*</span>
|
|
</p>
|
|
<div className="flex flex-col gap-1">
|
|
<p className="text-sm">Komentar</p>
|
|
<Controller
|
|
control={control}
|
|
name="comment"
|
|
render={({ field: { onChange, value } }) => (
|
|
<Textarea
|
|
type="text"
|
|
id="comment"
|
|
placeholder=""
|
|
label=""
|
|
value={value}
|
|
onChange={onChange}
|
|
labelPlacement="outside"
|
|
className="w-full "
|
|
classNames={{
|
|
inputWrapper: [
|
|
"border-1 rounded-lg",
|
|
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
|
],
|
|
}}
|
|
variant="bordered"
|
|
/>
|
|
)}
|
|
/>
|
|
{errors?.comment && (
|
|
<p className="text-red-400 text-sm mb-3">
|
|
{errors.comment?.message}
|
|
</p>
|
|
)}
|
|
</div>
|
|
<div className="flex flex-col gap-1">
|
|
<p className="text-sm">
|
|
Nama <span className="text-red-600">*</span>
|
|
</p>
|
|
|
|
<Controller
|
|
control={control}
|
|
name="name"
|
|
render={({ field: { onChange, value } }) => (
|
|
<Input
|
|
type="text"
|
|
id="name"
|
|
placeholder=""
|
|
label=""
|
|
value={value}
|
|
onChange={onChange}
|
|
labelPlacement="outside"
|
|
className="w-full "
|
|
classNames={{
|
|
inputWrapper: [
|
|
"border-1 rounded-lg",
|
|
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
|
],
|
|
}}
|
|
variant="bordered"
|
|
/>
|
|
)}
|
|
/>
|
|
{errors?.name && (
|
|
<p className="text-red-400 text-sm mb-3">
|
|
{errors.name?.message}
|
|
</p>
|
|
)}
|
|
</div>
|
|
<div className="flex flex-col gap-1">
|
|
<p className="text-sm">
|
|
Email <span className="text-red-600">*</span>
|
|
</p>
|
|
<Controller
|
|
control={control}
|
|
name="email"
|
|
render={({ field: { onChange, value } }) => (
|
|
<Input
|
|
type="email"
|
|
id="email"
|
|
placeholder=""
|
|
label=""
|
|
value={value}
|
|
onChange={onChange}
|
|
labelPlacement="outside"
|
|
className="w-full "
|
|
classNames={{
|
|
inputWrapper: [
|
|
"border-1 rounded-lg",
|
|
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
|
],
|
|
}}
|
|
variant="bordered"
|
|
/>
|
|
)}
|
|
/>
|
|
{errors?.email && (
|
|
<p className="text-red-400 text-sm mb-3">
|
|
{errors.email?.message}
|
|
</p>
|
|
)}
|
|
</div>
|
|
</>
|
|
) : (
|
|
<div className="flex flex-col gap-1">
|
|
<p className="text-xs">
|
|
Kode verifikasi sudah dikirmkan. Silahkan cek Email Anda!
|
|
</p>
|
|
<p>OTP</p>
|
|
|
|
<OTPInput
|
|
value={otpValue}
|
|
onChange={setOtpValue}
|
|
numInputs={6}
|
|
renderSeparator={<span>-</span>}
|
|
renderInput={(props) => (
|
|
<input
|
|
{...props}
|
|
className="!w-[30px] h-[30px] bg-stone-900 dark:bg-white text-white dark:text-black rounded-sm"
|
|
/>
|
|
)}
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
<Button className="bg-[#DD8306] text-white" radius="sm" type="submit">
|
|
Kirim
|
|
</Button>
|
|
</form>
|
|
<div className="flex flex-col gap-2 text-black">
|
|
{commentList?.map(
|
|
(list: any) =>
|
|
list?.parentId === 0 && (
|
|
<div
|
|
key={list?.id}
|
|
className="flex flex-col gap-3 w-full bg-white text-black dark:bg-stone-900 dark:text-white shadow-md rounded-md border-b-2 py-3 px-4 "
|
|
>
|
|
<div className="flex justify-between items-center">
|
|
<div className="flex flex-row gap-2">
|
|
<UserProfileIcon size={44} />
|
|
<div className="flex flex-col gap-1">
|
|
<p className="text-sm font-semibold">
|
|
{list?.commentFromName}
|
|
</p>
|
|
<p className="text-sm">{list?.message}</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex flex-col gap-1 text-right">
|
|
<p className="text-xs">
|
|
{convertDateFormat(list?.updatedAt)}
|
|
</p>
|
|
<div className="flex flex-row gap-2 justify-end">
|
|
{userId === "16" && (
|
|
<a
|
|
className="text-primary cursor-pointer text-xs"
|
|
onClick={() => setOpenCommentId(list?.id)}
|
|
>
|
|
Balas
|
|
</a>
|
|
)}
|
|
{(userId === String(list?.commentFromId) ||
|
|
userId === "16") && (
|
|
<a
|
|
className="text-danger cursor-pointer text-xs"
|
|
onClick={() => handleDelete(list?.id)}
|
|
>
|
|
Hapus
|
|
</a>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{childComment(list?.id)}
|
|
{openCommentId === list?.id && (
|
|
<div className="flex flex-row gap-2 items-center">
|
|
<Input
|
|
type="text"
|
|
id="comment"
|
|
placeholder=""
|
|
label=""
|
|
value={replyValue}
|
|
onValueChange={setReplyValue}
|
|
endContent={
|
|
<div className="flex flex-row gap-2">
|
|
<a
|
|
className="cursor-pointer"
|
|
onClick={() => sendComment(list?.id)}
|
|
>
|
|
<SendIcon />
|
|
</a>
|
|
</div>
|
|
}
|
|
labelPlacement="outside"
|
|
className="w-full "
|
|
classNames={{
|
|
inputWrapper: [
|
|
"border-1 rounded-lg",
|
|
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
|
],
|
|
}}
|
|
variant="bordered"
|
|
/>
|
|
<a
|
|
className="cursor-pointer text-warning"
|
|
onClick={() => setOpenCommentId(0)}
|
|
>
|
|
<TimesIcon />
|
|
</a>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|