mediahub-fe/components/form/communication/escalation-forward-form.tsx

478 lines
15 KiB
TypeScript
Raw Normal View History

2025-05-19 19:39:21 +00:00
"use client";
"use client";
import React, { useEffect, useState } from "react";
import { useForm, Controller } from "react-hook-form";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { Card } from "@/components/ui/card";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { useParams } from "next/navigation";
import {
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Avatar, AvatarImage } from "@/components/ui/avatar";
import {
2025-05-26 10:48:12 +00:00
getCuratorUser,
2025-05-19 19:39:21 +00:00
getEscalationDiscussion,
getQuestionTicket,
getTicketingDetail,
getTicketingInternalDetail,
getTicketingInternalDiscussion,
saveTicketInternalReply,
saveTicketsQuestion,
} from "@/service/communication/communication";
import { Textarea } from "@/components/ui/textarea";
import { Icon } from "@iconify/react/dist/iconify.js";
import { Link } from "@/i18n/routing";
import { loading } from "@/lib/swal";
import { id } from "date-fns/locale";
import { DetailTicket } from "../ticketing/info-lainnya-types";
import { Description } from "@radix-ui/react-toast";
2025-05-26 10:48:12 +00:00
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { ChevronDownIcon } from "lucide-react";
import { getOperatorUser } from "@/service/management-user/management-user";
import makeAnimated from "react-select/animated";
2025-06-13 08:21:08 +00:00
import Select, { ActionMeta, MultiValue } from "react-select";
2025-08-28 13:59:12 +00:00
import { Checkbox } from "@/components/ui/checkbox";
2025-05-26 10:48:12 +00:00
interface Option {
id: string;
label: string;
value: string;
fullname: string;
userLevel: string;
userLevelId: string;
}
2025-05-19 19:39:21 +00:00
2025-08-21 17:12:01 +00:00
// const taskSchema = z.object({
// title: z.string().optional(),
// description: z.string().min(2, {
// message: "Narasi Penugasan harus lebih dari 2 karakter.",
// }),
// });
2025-05-19 19:39:21 +00:00
const taskSchema = z.object({
2025-08-21 17:12:01 +00:00
title: z.string().optional(),
2025-05-19 19:39:21 +00:00
description: z.string().min(2, {
message: "Narasi Penugasan harus lebih dari 2 karakter.",
}),
});
export type replyDetail = {
id: number;
message: string;
createdAt: string;
messageFrom: {
id: number;
fullname: string;
};
messageTo: {
id: number;
fullname: string;
};
};
2025-05-26 10:48:12 +00:00
const optionsData = [
{ value: "1", label: "Low" },
{ value: "2", label: "Medium" },
{ value: "3", label: "High" },
];
type OptionType = {
value: string;
label: string;
};
2025-05-19 19:39:21 +00:00
export default function FormQuestionsForward() {
const MySwal = withReactContent(Swal);
const { id } = useParams() as { id: string };
const [detail, setDetail] = useState<any>();
const [ticketReply, setTicketReply] = useState<replyDetail[]>([]);
const [replyVisible, setReplyVisible] = useState(false);
const [listDiscussion, setListDiscussion] = useState();
const [message, setMessage] = useState("");
const [detailTickets, setDetailTickets] = useState<DetailTicket | null>(null);
2025-05-26 10:48:12 +00:00
// const [selectedPriority, setSelectedPriority] = useState("");
const [selectedPriority, setSelectedPriority] = useState<OptionType | null>(
null
);
2025-05-19 19:39:21 +00:00
const [replyMessage, setReplyMessage] = useState("");
const [selectedStatus, setSelectedStatus] = useState("");
2025-05-26 10:48:12 +00:00
const [operatorOpt, setOperatorOpt] = useState<
{ id: string; label: string; value: string }[]
>([]);
const [selectedOperator, setSelectedOperator] = useState<string[]>([]);
const [options, setOptions] = useState<Option[]>([]);
const animatedComponent = makeAnimated();
2025-08-28 13:59:12 +00:00
const [isCollaboration, setIsCollaboration] = useState(false);
2025-06-13 08:21:08 +00:00
const [selectedOption, setSelectedOption] = useState<Option[]>([]);
2025-05-19 19:39:21 +00:00
const [replies, setReplies] = useState([
{
id: 1,
name: "Mabes Polri - Approver",
message: "test",
timestamp: "2024-12-20 00:56:10",
},
{
id: 2,
name: "Mabes Polri - Approver",
message: "balas",
timestamp: "2025-01-18 17:42:48",
},
]);
const {
control,
handleSubmit,
reset,
formState: { errors },
} = useForm({
resolver: zodResolver(taskSchema),
});
useEffect(() => {
async function initState() {
2025-05-26 10:48:12 +00:00
const response = await getQuestionTicket(id);
setDetail(response?.data?.data);
if (response?.data !== null) {
setDetailTickets(response?.data?.data);
}
if (detailTickets?.emergencyIssue) {
reset({
title: detailTickets.emergencyIssue.title || "",
description: detailTickets.emergencyIssue.description || "",
});
// setSelectedPriority(String(detailTickets.emergencyIssue.urgencyId));
// setSelectedStatus(String(detailTickets.statusId)); // jika ada
2025-05-19 19:39:21 +00:00
}
}
initState();
getTicketReply();
2025-05-26 10:48:12 +00:00
getUser();
}, [id, reset]);
useEffect(() => {
async function getOperator() {
const res = await getOperatorUser(detailTickets?.typeId);
if (res?.data !== null) {
const rawUser = res?.data?.data;
const optionArr = rawUser?.map((option: any) => ({
id: option.id,
label: option.fullName,
value: option.id,
}));
setOperatorOpt(optionArr);
}
}
getOperator();
}, [detailTickets]);
2025-05-19 19:39:21 +00:00
async function getTicketReply() {
const res = await getTicketingInternalDiscussion(id);
if (res?.data !== null) {
setTicketReply(res?.data?.data);
}
}
const onSubmit = async (data: any) => {
try {
const payload = {
id,
title: data.title,
description: data.description,
2025-05-26 10:48:12 +00:00
priorityId: selectedPriority?.value,
2025-05-19 19:39:21 +00:00
statusId: selectedStatus,
typeId: detailTickets?.typeId,
parentCommentId: detailTickets?.feedId,
2025-05-26 10:48:12 +00:00
operatorTeam: selectedOperator.join(","),
isEscalation: true,
2025-06-13 08:21:08 +00:00
communicationTeam: selectedOption.map((item) => item.id).join(","),
2025-08-28 13:59:12 +00:00
isCollaboration: isCollaboration,
2025-05-19 19:39:21 +00:00
};
const response = await saveTicketsQuestion(payload);
MySwal.fire({
title: "Sukses",
text: "Data berhasil diperbarui.",
icon: "success",
2025-08-21 17:12:01 +00:00
}).then(() => {
window.location.href = "/in/supervisor/communications/questions/all";
2025-05-19 19:39:21 +00:00
});
getTicketReply();
} catch (error) {
console.error("Gagal update:", error);
MySwal.fire({
title: "Error",
text: "Terjadi kesalahan saat memperbarui.",
icon: "error",
});
}
};
2025-06-13 08:21:08 +00:00
const handleChange = (
selected: MultiValue<Option>,
_actionMeta: ActionMeta<Option>
) => {
setSelectedOption([...selected]);
2025-05-26 10:48:12 +00:00
};
const formatOptionLabel = (option: Option) => (
<>
<div className="row">
<div className="col">
{option.value} | {option.fullname}
</div>
</div>
<div className="row">
<div className="col">
<b>{option.userLevel}</b>
</div>
</div>
</>
);
async function getUser() {
const res = await getCuratorUser();
if (res?.data !== null) {
const rawUser = res?.data?.data?.content;
console.log("raw user", rawUser);
const optionArr: Option[] = rawUser.map((option: any) => ({
id: option?.id,
label: option?.username + option?.fullname + option?.userLevel?.name,
value: option?.username,
fullname: option?.fullname,
userLevel: option?.userLevel?.name,
userLevelId: option?.userLevel?.id,
}));
setOptions(optionArr);
}
}
2025-05-19 19:39:21 +00:00
const handleSendReply = () => {
if (replyMessage.trim() === "") return;
const newReply = {
id: replies.length + 1,
2025-08-21 17:12:01 +00:00
name: "Mabes Polri - Approver",
2025-05-19 19:39:21 +00:00
message: replyMessage,
timestamp: new Date().toISOString().slice(0, 19).replace("T", " "),
};
setReplies([...replies, newReply]);
setReplyMessage("");
};
return (
<div>
<div className="flex">
<div className="flex flex-col mt-6 w-full mb-3">
{detail !== undefined && (
<div key={detail?.id} className="bg-slate-300 rounded-md">
<p className="p-5 bg-slate-300 rounded-md text-lg font-semibold">
Ticket #{detail.id}
</p>
<div className="flex flex-row gap-3 bg-sky-100 p-5 items-center">
<Icon icon="qlementine-icons:user-16" width={36} />
<div>
<p>
<span className="font-bold">
{detail?.commentFromUserName}
</span>
{` `}
mengirimkan pesan untuk{` `}
<Link
href={
detail?.feed
? detail?.feed?.permalink_url == undefined
? detail?.feedUrl
: detail?.feed?.permalink_url
: ""
}
target="_blank"
className="font-bold"
>
{detail?.message}
</Link>
</p>
<p className="text-xs">
{`${new Date(detail?.createdAt).getDate()}-${
new Date(detail?.createdAt).getMonth() + 1
}-${new Date(detail?.createdAt).getFullYear()} ${new Date(
detail?.createdAt
).getHours()}:${new Date(detail?.createdAt).getMinutes()}`}
</p>
</div>
</div>
<p className="p-5 bg-white">{detail.message}</p>
</div>
)}
</div>
</div>
2025-05-26 10:48:12 +00:00
{detailTickets && (
<form onSubmit={handleSubmit(onSubmit)}>
<div className="gap-5 mb-5 w-[100%] lg:w-auto border bg-white rounded-md">
<p className="mx-3 mt-3">Properties</p>
<div className="space-y-2 px-3">
<Label>Judul</Label>
<Controller
control={control}
name="title"
render={({ field }) => (
<Input
size="md"
defaultValue={detailTickets?.message}
type="text"
{...field}
placeholder="Masukkan judul"
/>
)}
/>
</div>
<div className="mt-5 px-3 space-y-3">
<Label>Prioritas</Label>
<Select
options={optionsData}
value={selectedPriority}
onChange={(option) => setSelectedPriority(option)}
placeholder="Pilih Prioritas"
/>
</div>
<div className="mt-5 px-3 mb-3 flex flex-col gap-y-3">
<Label>Operator</Label>
{selectedOperator.length > 0 && (
2025-06-13 08:21:08 +00:00
<div className="flex flex-row gap-2 mb-2">
2025-05-26 10:48:12 +00:00
{selectedOperator.map((id) => {
const label = operatorOpt.find(
(op: any) => op.value === id
)?.label;
return (
<div
key={id}
className="flex items-center justify-between bg-gray-200 px-3 py-1 rounded"
>
<span>{label}</span>
<button
type="button"
onClick={() =>
setSelectedOperator((prev) =>
prev.filter((val) => val !== id)
)
}
className="ml-2 text-gray-500 hover:text-red-600"
>
×
</button>
</div>
);
})}
</div>
)}
2025-05-26 10:48:12 +00:00
{/* Popover Checkbox Dropdown */}
<Popover>
<PopoverTrigger asChild>
<Button variant="outline" className="w-3/12 justify-between">
Pilih Operator
<ChevronDownIcon className="ml-2 h-4 w-4" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-full max-h-60 overflow-auto">
<div className="flex flex-col gap-1">
{operatorOpt.map((op: any) => (
<label
key={op.id}
className="flex space-x-2 cursor-pointer px-2 py-1 hover:bg-gray-100 rounded"
>
<input
type="checkbox"
checked={selectedOperator.includes(op.value)}
onChange={(e) => {
if (e.target.checked) {
setSelectedOperator((prev) => [
...prev,
op.value,
]);
} else {
setSelectedOperator((prev) =>
prev.filter((val) => val !== op.value)
);
}
}}
/>
<span>{op.label}</span>
</label>
))}
</div>
</PopoverContent>
</Popover>
</div>
<div className="space-y-2 px-3 py-3">
<div className="">
<Label>
Eskalasi Untuk <span className="text-red-500">*</span>
</Label>
<Select
options={options}
className="w-100"
closeMenuOnSelect={false}
components={animatedComponent}
onChange={handleChange}
formatOptionLabel={formatOptionLabel}
2025-06-13 08:21:08 +00:00
isMulti={true}
/>
2025-05-26 10:48:12 +00:00
</div>
</div>
<div className="space-y-2 px-3 py-3">
<Label>Deskripsi</Label>
<Controller
control={control}
name="description"
render={({ field }) => (
<Textarea {...field} placeholder="Masukkan description" />
)}
/>
2025-05-19 19:39:21 +00:00
</div>
2025-08-28 13:59:12 +00:00
<div className="flex flex-row gap-2 px-3 py-3">
<Label className="">Bagikan Untuk Kolaborasi</Label>
<Checkbox
checked={isCollaboration}
onCheckedChange={(e: boolean) => setIsCollaboration(e)}
/>
</div>
2025-05-26 10:48:12 +00:00
<div className="flex justify-end mt-3 mr-3 py-3">
<Button type="submit" color="primary">
Simpan
</Button>
2025-05-19 19:39:21 +00:00
</div>
</div>
2025-05-26 10:48:12 +00:00
</form>
2025-05-19 19:39:21 +00:00
)}
</div>
);
}