feat:escalation,video

This commit is contained in:
Anang Yusman 2025-06-13 16:21:08 +08:00
parent cfbc6349cc
commit 45a7ec116a
6 changed files with 316 additions and 70 deletions

View File

@ -103,20 +103,6 @@ export default function FormQuestionsDetail() {
const [selectedOperatorEscalation, setSelectedOperatorEscalation] = useState< const [selectedOperatorEscalation, setSelectedOperatorEscalation] = useState<
string[] string[]
>([]); >([]);
// 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 { const {
control, control,
@ -156,12 +142,11 @@ export default function FormQuestionsDetail() {
setDetail(detail); setDetail(detail);
setDetailTickets(detail); setDetailTickets(detail);
// Ambil escalationTeams seperti ":891:"
if (detail?.escalationTeams) { if (detail?.escalationTeams) {
const teamIds = detail.escalationTeams const teamIds = detail.escalationTeams
.split(":") .split(":")
.filter((id: string) => id); // hapus string kosong .filter((id: string) => id);
setSelectedOperatorEscalation(teamIds); // set ke state setSelectedOperatorEscalation(teamIds);
} }
getTicketReply(); getTicketReply();
@ -409,7 +394,7 @@ export default function FormQuestionsDetail() {
{/* Tag yang ditampilkan secara kolom */} {/* Tag yang ditampilkan secara kolom */}
{selectedOperator.length > 0 && ( {selectedOperator.length > 0 && (
<div className="flex flex-col gap-2 mb-2"> <div className="flex flex-row gap-2 mb-2">
{selectedOperator.map((id) => { {selectedOperator.map((id) => {
const label = operatorOpt.find( const label = operatorOpt.find(
(op: any) => op.value === id (op: any) => op.value === id
@ -453,6 +438,7 @@ export default function FormQuestionsDetail() {
className="flex items-center space-x-2 cursor-pointer px-2 py-1 hover:bg-gray-100 rounded" className="flex items-center space-x-2 cursor-pointer px-2 py-1 hover:bg-gray-100 rounded"
> >
<input <input
disabled
type="checkbox" type="checkbox"
checked={selectedOperator.includes(op.value)} checked={selectedOperator.includes(op.value)}
onChange={(e) => { onChange={(e) => {
@ -495,7 +481,7 @@ export default function FormQuestionsDetail() {
<Label>Eskalasi Untuk</Label> <Label>Eskalasi Untuk</Label>
{selectedOperatorEscalation.length > 0 && ( {selectedOperatorEscalation.length > 0 && (
<div className="flex flex-col gap-2 mb-2"> <div className="flex flex-row gap-2 mb-2">
{selectedOperatorEscalation.map((id) => { {selectedOperatorEscalation.map((id) => {
const operator = curatorOpt.find( const operator = curatorOpt.find(
(op: any) => op.value === id (op: any) => op.value === id

View File

@ -43,7 +43,7 @@ import {
import { ChevronDownIcon } from "lucide-react"; import { ChevronDownIcon } from "lucide-react";
import { getOperatorUser } from "@/service/management-user/management-user"; import { getOperatorUser } from "@/service/management-user/management-user";
import makeAnimated from "react-select/animated"; import makeAnimated from "react-select/animated";
import Select from "react-select"; import Select, { ActionMeta, MultiValue } from "react-select";
interface Option { interface Option {
id: string; id: string;
@ -109,9 +109,7 @@ export default function FormQuestionsForward() {
const [selectedOperator, setSelectedOperator] = useState<string[]>([]); const [selectedOperator, setSelectedOperator] = useState<string[]>([]);
const [options, setOptions] = useState<Option[]>([]); const [options, setOptions] = useState<Option[]>([]);
const animatedComponent = makeAnimated(); const animatedComponent = makeAnimated();
const [selectedOption, setSelectedOption] = useState<Option | undefined>( const [selectedOption, setSelectedOption] = useState<Option[]>([]);
undefined
);
const [replies, setReplies] = useState([ const [replies, setReplies] = useState([
{ {
id: 1, id: 1,
@ -195,7 +193,7 @@ export default function FormQuestionsForward() {
parentCommentId: detailTickets?.feedId, parentCommentId: detailTickets?.feedId,
operatorTeam: selectedOperator.join(","), operatorTeam: selectedOperator.join(","),
isEscalation: true, isEscalation: true,
communicationTeam: selectedOption?.id, communicationTeam: selectedOption.map((item) => item.id).join(","),
}; };
const response = await saveTicketsQuestion(payload); const response = await saveTicketsQuestion(payload);
@ -206,7 +204,6 @@ export default function FormQuestionsForward() {
icon: "success", icon: "success",
}); });
// Refresh data jika perlu
getTicketReply(); getTicketReply();
} catch (error) { } catch (error) {
console.error("Gagal update:", error); console.error("Gagal update:", error);
@ -218,9 +215,11 @@ export default function FormQuestionsForward() {
} }
}; };
const handleChange = (e: any) => { const handleChange = (
const selected = e; selected: MultiValue<Option>,
setSelectedOption(selected); _actionMeta: ActionMeta<Option>
) => {
setSelectedOption([...selected]);
}; };
const formatOptionLabel = (option: Option) => ( const formatOptionLabel = (option: Option) => (
@ -353,9 +352,8 @@ export default function FormQuestionsForward() {
<div className="mt-5 px-3 mb-3 flex flex-col gap-y-3"> <div className="mt-5 px-3 mb-3 flex flex-col gap-y-3">
<Label>Operator</Label> <Label>Operator</Label>
{/* Tag yang ditampilkan secara kolom */}
{selectedOperator.length > 0 && ( {selectedOperator.length > 0 && (
<div className="flex flex-col gap-2 mb-2"> <div className="flex flex-row gap-2 mb-2">
{selectedOperator.map((id) => { {selectedOperator.map((id) => {
const label = operatorOpt.find( const label = operatorOpt.find(
(op: any) => op.value === id (op: any) => op.value === id
@ -433,7 +431,7 @@ export default function FormQuestionsForward() {
components={animatedComponent} components={animatedComponent}
onChange={handleChange} onChange={handleChange}
formatOptionLabel={formatOptionLabel} formatOptionLabel={formatOptionLabel}
isMulti={false} isMulti={true}
/> />
</div> </div>
</div> </div>

View File

@ -125,7 +125,6 @@ export default function FormConvertSPIT() {
const [selectedCategoryId, setSelectedCategoryId] = useState<number | null>( const [selectedCategoryId, setSelectedCategoryId] = useState<number | null>(
null null
); );
const [tags, setTags] = useState<any[]>([]);
const [detail, setDetail] = useState<any>(); const [detail, setDetail] = useState<any>();
const [refresh, setRefresh] = useState(false); const [refresh, setRefresh] = useState(false);
const [selectedPublishers, setSelectedPublishers] = useState<number[]>([]); const [selectedPublishers, setSelectedPublishers] = useState<number[]>([]);
@ -159,6 +158,8 @@ export default function FormConvertSPIT() {
const [filePlacements, setFilePlacements] = useState<string[][]>([]); const [filePlacements, setFilePlacements] = useState<string[][]>([]);
const [isUserMabesApprover, setIsUserMabesApprover] = useState(false); const [isUserMabesApprover, setIsUserMabesApprover] = useState(false);
const [files, setFiles] = useState<FileType[]>([]); const [files, setFiles] = useState<FileType[]>([]);
const [tags, setTags] = useState<any[]>([]);
const inputRef = useRef<HTMLInputElement>(null);
const [selectedWritingStyle, setSelectedWritingStyle] = const [selectedWritingStyle, setSelectedWritingStyle] =
useState("Professional"); useState("Professional");
@ -367,6 +368,13 @@ export default function FormConvertSPIT() {
setSelectedTarget(matchingCategory.name); setSelectedTarget(matchingCategory.name);
} }
if (details?.contentTag) {
const initialTags = details.contentTag
.split(",")
.map((tag: string) => tag.trim());
setTags(initialTags);
}
// setSelectedTarget(details.categoryId); // Untuk dropdown // setSelectedTarget(details.categoryId); // Untuk dropdown
} }
} }
@ -477,13 +485,14 @@ export default function FormConvertSPIT() {
selectedFileType === "original" selectedFileType === "original"
? data.contentDescription ? data.contentDescription
: data.contentRewriteDescription; : data.contentRewriteDescription;
const finalTags = tags.join(", ");
const requestData = { const requestData = {
spitId: id, spitId: id,
title: data.contentTitle, title: data.contentTitle,
description, description,
htmlDescription: description, htmlDescription: description,
tags: "siap", tags: finalTags,
categoryId: selectedCategoryId, categoryId: selectedCategoryId,
publishedFor: publishedFor.join(","), publishedFor: publishedFor.join(","),
creator: data.contentCreator, creator: data.contentCreator,
@ -647,6 +656,19 @@ export default function FormConvertSPIT() {
}); });
} }
const handleAddTag = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter" && inputRef.current?.value.trim()) {
e.preventDefault();
const newTag = inputRef.current.value.trim();
if (!tags.includes(newTag)) {
setTags((prevTags) => [...prevTags, newTag]);
}
inputRef.current.value = "";
}
};
return ( return (
<> <>
<Form {...form}> <Form {...form}>
@ -1054,17 +1076,22 @@ export default function FormConvertSPIT() {
<div className="px-3 py-3"> <div className="px-3 py-3">
<div className="space-y-2"> <div className="space-y-2">
<Label>{t("tags")}</Label> <Label>{t("tags")}</Label>
<Input
type="text"
id="tags"
placeholder="Add a tag and press Enter"
onKeyDown={handleAddTag}
ref={inputRef}
/>
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
{detail?.contentTag {tags.map((tag, index) => (
?.split(",") <Badge
.map((tag: any, index: any) => ( key={index}
<Badge className="border rounded-md px-2 py-2"
key={index} >
className="border rounded-md px-2 py-2" {tag}
> </Badge>
{tag.trim()} ))}
</Badge>
))}
</div> </div>
</div> </div>
</div> </div>

View File

@ -481,7 +481,7 @@ export default function FormVideo() {
let id = Cookies.get("idCreate"); let id = Cookies.get("idCreate");
if (scheduleId !== undefined) { if (scheduleId !== undefined) {
requestData.attachFromScheduleId = Number(scheduleId); // ✅ Tambahkan nilai ini requestData.attachFromScheduleId = Number(scheduleId);
} }
if (id == undefined) { if (id == undefined) {
@ -495,14 +495,14 @@ export default function FormVideo() {
Cookies.set("idCreate", response?.data?.data, { expires: 1 }); Cookies.set("idCreate", response?.data?.data, { expires: 1 });
id = response?.data?.data; id = response?.data?.data;
// Upload Thumbnail if (thumbnail) {
const formMedia = new FormData(); const formMedia = new FormData();
const thumbnail = files[0]; formMedia.append("file", thumbnail);
formMedia.append("file", thumbnail); const responseThumbnail = await uploadThumbnail(formMedia, id);
const responseThumbnail = await uploadThumbnail(id, formMedia); if (responseThumbnail?.error) {
if (responseThumbnail?.error == true) { error(responseThumbnail.message);
error(responseThumbnail?.message); return false;
return false; }
} }
} }
@ -526,6 +526,14 @@ export default function FormVideo() {
// MySwal.fire("Sukses", "Data berhasil disimpan.", "success"); // MySwal.fire("Sukses", "Data berhasil disimpan.", "success");
}; };
useEffect(() => {
return () => {
if (preview) {
URL.revokeObjectURL(preview);
}
};
}, [preview]);
const onSubmit = (data: VideoSchema) => { const onSubmit = (data: VideoSchema) => {
MySwal.fire({ MySwal.fire({
title: "Simpan Data", title: "Simpan Data",
@ -633,12 +641,10 @@ export default function FormVideo() {
const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0]; const file = e.target.files?.[0];
if (file) { if (file) {
setThumbnail(file); setThumbnail(file); // Simpan file asli tanpa dimodifikasi
setPreview(URL.createObjectURL(file)); // Simpan preview string terpisah
console.log("Selected Thumbnail:", file); console.log("Selected Thumbnail:", file);
} }
if (file) {
setPreview(URL.createObjectURL(file));
}
}; };
const renderFilePreview = (file: FileWithPreview) => { const renderFilePreview = (file: FileWithPreview) => {
@ -1075,10 +1081,10 @@ export default function FormVideo() {
)} )}
</div> </div>
</div> </div>
{/* <div className="px-3 py-3"> <div className="px-3 py-3">
<Label htmlFor="fileInput">Gambar Utama</Label> <Label htmlFor="fileInput">Gambar Utama</Label>
<Input id="fileInput" type="file" onChange={handleImageChange} /> <Input id="fileInput" type="file" onChange={handleImageChange} />
</div> */} </div>
{preview && ( {preview && (
<div className="mt-3 px-3"> <div className="mt-3 px-3">
<img <img

View File

@ -38,7 +38,10 @@ export function getMenuList(pathname: string, t: any): Group[] {
let menusSelected = <any>[]; let menusSelected = <any>[];
if ((Number(roleId) == 3 || Number(roleId) == 14) && Number(levelNumber) == 1) { if (
(Number(roleId) == 3 || Number(roleId) == 14) &&
Number(levelNumber) == 1
) {
menusSelected = [ menusSelected = [
{ {
groupLabel: t("apps"), groupLabel: t("apps"),
@ -1531,13 +1534,236 @@ export function getMenuList(pathname: string, t: any): Group[] {
icon: "heroicons:share", icon: "heroicons:share",
children: [], children: [],
}, },
// {
// href: "/contributor/content/spit",
// label: "spit",
// active: pathname.includes("/content/spit"),
// icon: "heroicons:credit-card",
// children: [],
// },
// {
// href: "/contributor/content/nulis-ai",
// label: "nulis ai",
// active: pathname.includes("/content/nulisai"),
// icon: "heroicons:credit-card",
// children: [],
// },
],
},
],
},
{
groupLabel: "",
id: "agenda-setting",
menus: [
{
id: "agenda-setting",
href: "/contributor/agenda-setting",
label: t("agenda-setting"),
active: pathname.includes("/agenda-setting"),
icon: "iconoir:journal-page",
submenus: [],
},
],
},
{
groupLabel: "",
id: "planning",
menus: [
{
id: "planning",
href: "/contributor/planning",
label: t("planning"),
active: pathname.includes("/planning"),
icon: "pajamas:planning",
submenus: [
{ {
href: "/contributor/content/spit", href: "/contributor/planning/mediahub",
label: "spit", label: "mediaHub",
active: pathname.includes("/content/spit"), active: pathname.includes("/planning/mediahub"),
icon: "heroicons:credit-card", icon: "heroicons:arrow-trending-up",
children: [], children: [],
}, },
{
href: "/contributor/planning/medsos-mediahub",
label: "medsos mediahub",
active: pathname.includes("/planning/medsos-mediahub"),
icon: "heroicons:shopping-cart",
children: [],
},
],
},
],
},
{
groupLabel: "",
id: "task",
menus: [
{
id: "task",
href: "/contributor/task",
label: t("task"),
active: pathname.includes("/task"),
icon: "fluent:clipboard-task-add-24-regular",
submenus: [],
},
],
},
{
groupLabel: "",
id: "schedule",
menus: [
{
id: "schedule",
href: "/contributor/schedule",
label: t("schedule"),
active: pathname.includes("/schedule"),
icon: "uil:schedule",
submenus: [
{
href: "/contributor/schedule/press-conference",
label: t("press-conference"),
active: pathname.includes("/schedule/press-conference"),
icon: "heroicons:arrow-trending-up",
children: [],
},
{
href: "/contributor/schedule/event",
label: t("event"),
active: pathname.includes("/schedule/event"),
icon: "heroicons:shopping-cart",
children: [],
},
{
href: "/contributor/schedule/press-release",
label: t("press-release"),
active: pathname.includes("/schedule/press-release"),
icon: "heroicons:shopping-cart",
children: [],
},
],
},
],
},
{
groupLabel: "",
id: "blog",
menus: [
{
id: "blog",
href: "/contributor/blog",
label: t("blog"),
active: pathname.includes("/blog"),
icon: "fluent:clipboard-text-32-regular",
submenus: [],
},
],
},
{
groupLabel: "",
id: "curatedcontent",
menus: [
{
id: "curatedcontent",
href: "/shared/curated-content",
label: t("curated-content"),
active: pathname.includes("/curated-content"),
icon: "pixelarticons:calendar-text",
submenus: [],
},
],
},
{
groupLabel: "",
id: "communication",
menus: [
{
id: "communication",
href: "/shared/communication",
label: t("communication"),
active: pathname.includes("/communication"),
icon: "token:chat",
submenus: [],
},
],
},
{
groupLabel: "",
id: "contest",
menus: [
{
id: "contest",
href: "/shared/contest",
label: t("contest"),
active: pathname.includes("/contest"),
icon: "ic:outline-emoji-events",
submenus: [],
},
],
},
];
} else if (Number(roleId) == 4 && Number(levelNumber) == 2) {
menusSelected = [
{
groupLabel: t("apps"),
id: "dashboard",
menus: [
{
id: "dashboard",
href: "/dashboard",
label: t("dashboard"),
active: pathname.includes("/dashboard"),
icon: "material-symbols:dashboard",
submenus: [],
},
],
},
{
groupLabel: "",
id: "content",
menus: [
{
id: "content",
href: "/contributor/content/image",
label: t("content"),
active: pathname.includes("/content"),
icon: "line-md:youtube",
submenus: [
{
href: "/contributor/content/image",
label: t("image"),
active: pathname.includes("/content/image"),
icon: "ic:outline-image",
children: [],
},
{
href: "/contributor/content/video",
label: t("video"),
active: pathname.includes("/content/video"),
icon: "line-md:youtube",
children: [],
},
{
href: "/contributor/content/teks",
label: t("text"),
active: pathname.includes("/content/teks"),
icon: "heroicons:document",
children: [],
},
{
href: "/contributor/content/audio",
label: t("audio"),
active: pathname.includes("/content/audio"),
icon: "heroicons:share",
children: [],
},
// {
// href: "/contributor/content/spit",
// label: "spit",
// active: pathname.includes("/content/spit"),
// icon: "heroicons:credit-card",
// children: [],
// },
// { // {
// href: "/contributor/content/nulis-ai", // href: "/contributor/content/nulis-ai",
// label: "nulis ai", // label: "nulis ai",
@ -1700,7 +1926,7 @@ export function getMenuList(pathname: string, t: any): Group[] {
}, },
]; ];
} else if ( } else if (
(Number(roleId) == 3 || Number(roleId) == 4 || Number(roleId) == 14 || Number(roleId) == 15) && (Number(roleId) == 3 || Number(roleId) == 14 || Number(roleId) == 15) &&
Number(levelNumber) == 2 Number(levelNumber) == 2
) { ) {
if (Number(userLevelId) != 761) { if (Number(userLevelId) != 761) {
@ -2158,7 +2384,10 @@ export function getMenuList(pathname: string, t: any): Group[] {
]; ];
} }
} else if ( } else if (
(Number(roleId) == 3 || Number(roleId) == 4 || Number(roleId) == 14 || Number(roleId) == 15) && (Number(roleId) == 3 ||
Number(roleId) == 4 ||
Number(roleId) == 14 ||
Number(roleId) == 15) &&
Number(levelNumber) == 3 Number(levelNumber) == 3
) { ) {
if (Number(userParentLevelId) != 761) { if (Number(userParentLevelId) != 761) {

View File

@ -173,10 +173,10 @@ export async function createMedia(data: any) {
export async function uploadThumbnail(id: any, data: any) { export async function uploadThumbnail(id: any, data: any) {
const url = `media/upload?id=${id}&operation=thumbnail`; const url = `media/upload?id=${id}&operation=thumbnail`;
const headers = { // const headers = {
"Content-Type": "multipart/form-data", // "Content-Type": "multipart/form-data",
}; // };
return httpPostInterceptor(url, data, headers); return httpPostInterceptor(url, data);
} }
export async function detailSPIT(id: any) { export async function detailSPIT(id: any) {
@ -234,4 +234,4 @@ export async function postActivityLog(data: any) {
"content-type": "application/json", "content-type": "application/json",
}; };
return httpPost(url, headers, data); return httpPost(url, headers, data);
} }