feat:fix spit,ui executive

This commit is contained in:
Anang Yusman 2025-06-07 20:35:53 +08:00
parent 43ae5c480a
commit 8980d8d77d
6 changed files with 718 additions and 203 deletions

View File

@ -0,0 +1,543 @@
"use client";
import SiteBreadcrumb from "@/components/site-breadcrumb";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { getCookiesDecrypt } from "@/lib/utils";
import { generateTicket } from "@/service/tableau/tableau-service";
import { LucideBoxSelect, UploadIcon } from "lucide-react";
import { useEffect, useState } from "react";
import Cookies from "js-cookie";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { useTranslations } from "next-intl";
import DashboardDropdown from "@/components/dashboard-dropdown";
import RecentActivity from "../routine-task/components/recent-activity";
import ContentTable from "../routine-task/components/content-table";
import { Link } from "@/i18n/routing";
import TaskTable from "../../contributor/task/components/task-table";
import PressConferenceTable from "../../contributor/schedule/press-conference/components/presscon-table";
export default function ExecutiveDataDashboard() {
const [startDate, setStartDate] = useState<any>(new Date());
const [endDate, setEndDate] = useState<any>(new Date());
const [hasMounted, setHasMounted] = useState(false);
// const t = useTranslations("AnalyticsDashboard");
const levelName = getCookiesDecrypt("ulnae");
const levelNumber = getCookiesDecrypt("ulne");
const state = Cookies.get("state");
const provState = Cookies.get("state-prov");
const t = useTranslations("AnalyticsDashboard");
const [ticket1, setTicket1] = useState("");
const [ticket2, setTicket2] = useState("");
const [ticket3, setTicket3] = useState("");
const [ticket4, setTicket4] = useState("");
const [ticket5, setTicket5] = useState("");
const [ticket6, setTicket6] = useState("");
const [isInternational, setIsInternational] = useState([false, false, false]);
const baseUrl = "https://analytic.sitani.info/";
const url = "https://analytic.sitani.info/trusted/";
const safeLevelName = levelNumber ?? "";
const view1 =
levelName == "MABES POLRI"
? isInternational[0]
? "views/2023_08_MediaHUB-KtnMgt_Rev100/db-emg-issue-executive?"
: "views/2023_08_MediaHUB-KtnMgt_Rev100/db-emg-issue-executive?"
: safeLevelName.includes("POLDA")
? `views/2023_08_MediaHUB-KtnMgt_Rev100/db-emg-issue?provinsi-polda=${state}&`
: `views/2023_08_MediaHUB-KtnMgt_Rev100/db-emg-issue?provinsi-polda=${state}&`;
const view2 =
levelName == "MABES POLRI"
? isInternational[1]
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-published-produksi?"
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-published-produksi-executive?"
: safeLevelName.includes("POLDA")
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-published-produksi-polda-executive?polda-selected=${state}&`
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-published-produksi-polda-executive?polda-selected=${state}&`;
const view3 =
levelName == "MABES POLRI"
? isInternational[2]
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-waktu-akses-pengguna?"
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-waktu-akses-pengguna-executive?"
: safeLevelName.includes("POLDA")
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-waktu-akses-pengguna-polda-executive?polda-selected=${state}&`
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-waktu-akses-pengguna-polda-executive?polda-selected=${state}&`;
const view4 =
levelName == "MABES POLRI"
? isInternational[1]
? "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polda?"
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polda?"
: safeLevelName.includes("POLDA")
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polda?polda-selected=${state}&`
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polda?polda-selected=${state}&`;
const view5 =
levelName == "MABES POLRI"
? isInternational[1]
? "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polres?"
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polres?"
: safeLevelName.includes("POLDA")
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polres?provinsi-polda=${state}&`
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polres?provinsi-polda=${state}&`;
const view6 =
levelName == "MABES POLRI"
? isInternational[1]
? "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-satker?"
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-satker?"
: safeLevelName.includes("POLDA")
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-satker?satker-selected=${state}&`
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-satker?satker-selected=${state}&`;
const view7 =
levelName == "MABES POLRI"
? isInternational[2]
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-penugasan?"
: "views/2023_09_db-penugasan_rev100/db-penugasan?"
: safeLevelName.includes("POLDA")
? `views/2023_09_db-penugasan_rev100/db-penugasan?polda-selected=${state}&`
: `views/2023_09_db-penugasan_rev100/db-penugasan?polda-selected=${state}&`;
const view8 =
levelName == "MABES POLRI"
? isInternational[2]
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-konten-kategori-top10?"
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-kategori-top10?"
: safeLevelName.includes("POLDA")
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-kategori-top10-polda?polda-selected=${state}&`
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-kategori-top10-polda?polda-selected=${state}&`;
const view9 =
levelName == "MABES POLRI"
? isInternational[3]
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-konten-kategori?"
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-kategori?"
: safeLevelName.includes("POLDA")
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-kategori-polda?polda-selected=${state}&`
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-kategori-polda?polda-selected=${state}&`;
const param = ":embed=yes&:toolbar=no&:iframeSizedToWindow=true";
useEffect(() => {
async function initState() {
const response1 = await generateTicket();
setTicket1(response1?.data?.data);
const response2 = await generateTicket();
setTicket2(response2?.data?.data);
const response3 = await generateTicket();
setTicket3(response3?.data?.data);
const response4 = await generateTicket();
setTicket4(response4?.data?.data);
const response5 = await generateTicket();
setTicket5(response5?.data?.data);
const response6 = await generateTicket();
setTicket6(response6?.data?.data);
}
initState();
}, [isInternational]);
// Hooks
useEffect(() => {
setHasMounted(true);
}, []);
// Render
if (!hasMounted) return null;
const handleInternational = (index: number, val: boolean) => {
const updatedIsInternational = [...isInternational];
updatedIsInternational[index] = val;
setIsInternational(updatedIsInternational);
};
return (
<div>
<SiteBreadcrumb />
<div>
<div className="my-3">
<Tabs defaultValue="content-publish" className="w-full">
<TabsList className="flex-wrap bg-black">
<TabsTrigger
value="content-publish"
className="data-[state=active]:bg-black data-[state=active]:text-primary-foreground rounded-md px-6"
>
Publish Konten
</TabsTrigger>
<TabsTrigger
value="content-category"
className="data-[state=active]:bg-black data-[state=active]:text-primary-foreground rounded-md px-6"
>
Kategori Konten
</TabsTrigger>
<TabsTrigger
value="popular-content"
className="data-[state=active]:bg-black data-[state=active]:text-primary-foreground rounded-md px-6"
>
Konten Terpopuler
</TabsTrigger>
<TabsTrigger
value="heatmap"
className="data-[state=active]:bg-black data-[state=active]:text-primary-foreground rounded-md px-6"
>
Heatmap
</TabsTrigger>
<TabsTrigger
value="task"
className="data-[state=active]:bg-black data-[state=active]:text-primary-foreground rounded-md px-6"
>
Penugasan
</TabsTrigger>
</TabsList>
<TabsContent value="content-publish">
<div className="mt-3 flex gap-2 flex-col justify-center">
{/* Polda */}
{(levelNumber === "1" || levelNumber === "2") && (
<Card
className={`rounded-sm p-3 ${
levelNumber === "2" ? "w-full" : "w-full"
}`}
>
<div className="flex flex-row justify-between">
<p className="text-base font-semibold">
Upload konten hari ini Polda
</p>
</div>
<div className="my-5">
{ticket1 == "w-full" ? (
<iframe
src={`${baseUrl + view4 + param}`}
width="100%"
height="750"
className="w-full"
frameBorder="0"
/>
) : (
<iframe
src={`${url + ticket1}/${view4}${param}`}
width="100%"
height="750"
frameBorder="0"
/>
)}
</div>
</Card>
)}
{/* Satker */}
{(levelNumber === "1" || levelNumber === "3") && (
<Card
className={`rounded-sm p-3 ${
levelNumber === "3" ? "w-full" : "w-full"
}`}
>
<div className="flex flex-row justify-between">
<p className="text-base font-semibold">
Upload konten hari ini Satker
</p>
</div>
<div className="my-5">
{ticket2 == "" ? (
<iframe
src={`${baseUrl + view6 + param}`}
width="100%"
height="750"
frameBorder="0"
/>
) : (
<iframe
src={`${url + ticket2}/${view6}${param}`}
width="100%"
height="750"
frameBorder="0"
/>
)}
</div>
</Card>
)}
{/* Polres */}
{(levelNumber === "1" || levelNumber === "2") && (
<Card
className={`rounded-sm p-3 ${
levelNumber === "2" ? "w-full" : "w-full"
}`}
>
<div className="flex flex-row justify-between">
<p className="text-base font-semibold">
Upload konten hari ini Polres
</p>
</div>
<div className="my-5">
{ticket3 == "" ? (
<iframe
src={`${baseUrl + view5 + param}`}
width="100%"
height="750"
frameBorder="0"
/>
) : (
<iframe
src={`${url + ticket3}/${view5}${param}`}
width="100%"
height="750"
frameBorder="0"
/>
)}
</div>
</Card>
)}
</div>
</TabsContent>
<TabsContent value="content-category">
<Card className="px-3 py-3">
<p className="text-lg">
<b>
{isInternational[2]
? "INTERACTION OF THE MOST POPULAR CATEGORIES AND TITLES"
: "INTERAKSI KATEGORI DAN JUDUL TERPOPULER"}
</b>
</p>
{levelName === "MABES POLRI" ? (
<div className="flex flex-col gap-1">
<p>{t("choose_category")}</p>
<div className="flex flex-row gap-1 border-2 w-fit">
<Button
onClick={() => handleInternational(2, false)}
className={` hover:text-white rounded-none
${
isInternational[2]
? "bg-white text-black "
: "bg-black text-white "
}`}
>
Indonesia
</Button>
<Button
onClick={() => handleInternational(2, true)}
className={`hover:text-white rounded-none ${
isInternational[1]
? "bg-black text-white "
: "bg-white text-black "
}
`}
>
{t("international")}
</Button>
</div>
</div>
) : (
""
)}
<div className="my-5">
{ticket3 == "" ? (
<iframe
src={`${baseUrl + view8 + param}`}
width="100%"
height="750"
frameBorder="0"
/>
) : (
<iframe
src={`${`${url + ticket3}/${view8}${param}`}`}
width="100%"
height="750"
frameBorder="0"
/>
)}
</div>
<p className="text-lg">
<b>
{isInternational[3]
? "INTERACTIONS OF CATEGORY AND ACCESS TIME"
: "INTERAKSI KATEGORI DAN WAKTU AKSES"}
</b>
</p>
{levelName === "MABES POLRI" ? (
<div className="flex flex-col gap-1">
<p>{t("choose_category")}</p>
<div className="flex flex-row gap-1 border-2 w-fit">
<Button
onClick={() => handleInternational(3, false)}
className={` hover:text-white rounded-none
${
isInternational[2]
? "bg-white text-black "
: "bg-black text-white "
}`}
>
Indonesia
</Button>
<Button
onClick={() => handleInternational(3, true)}
className={`hover:text-white rounded-none ${
isInternational[1]
? "bg-black text-white "
: "bg-white text-black "
}
`}
>
{t("international")}
</Button>
</div>
</div>
) : (
""
)}
<div className="my-5">
{ticket4 == "" ? (
<iframe
src={`${baseUrl + view9 + param}`}
width="100%"
height="750"
frameBorder="0"
/>
) : (
<iframe
src={`${`${url + ticket4}/${view9}${param}`}`}
width="100%"
height="750"
frameBorder="0"
/>
)}
</div>
</Card>
</TabsContent>
<TabsContent value="popular-content">
<Card className="rounded-sm p-3 h-[750px]">
<div className="flex flex-row justify-between">
<p className="text-base font-semibold">
Konten Paling Populer
</p>
</div>
<div className="my-5">
{ticket4 == "" ? (
<iframe
src={`${baseUrl + view2 + param}`}
width="100%"
height="650"
frameBorder="0"
/>
) : (
<iframe
src={`${`${url + ticket4}/${view2}${param}`}`}
width="100%"
height="650"
frameBorder="0"
/>
)}
</div>
</Card>
</TabsContent>
<TabsContent value="heatmap">
<Card className="rounded-sm p-3 h-[750px]">
<div className="flex flex-row justify-between mx-3">
<p className="text-base font-semibold">
Heatmap Konten dan Kategori dengan Interaksi
</p>
</div>
<div className="my-5">
{ticket5 == "" ? (
<iframe
src={`${baseUrl + view3 + param}`}
width="100%"
height="600"
frameBorder="0"
/>
) : (
<iframe
src={`${`${url + ticket5}/${view3}${param}`}`}
width="100%"
height="600"
frameBorder="0"
/>
)}
</div>
</Card>
</TabsContent>
<TabsContent value="task">
<div className="grid grid-cols-12 gap-5">
<div className="lg:col-span-12 col-span-12">
<Card className="px-3 py-3">
{levelName === "MABES POLRI" ? (
<div className="flex flex-col gap-1">
<p>{t("choose_category")}</p>
<div className="flex flex-row gap-1 border-2 w-fit">
<Button
onClick={() => handleInternational(2, false)}
className={` hover:text-white rounded-none
${
isInternational[2]
? "bg-white text-black "
: "bg-black text-white "
}`}
>
Indonesia
</Button>
<Button
onClick={() => handleInternational(2, true)}
className={`hover:text-white rounded-none ${
isInternational[1]
? "bg-black text-white "
: "bg-white text-black "
}
`}
>
{t("international")}
</Button>
</div>
</div>
) : (
""
)}
<div className="my-5">
{ticket3 == "" ? (
<iframe
src={`${baseUrl + view7 + param}`}
width="100%"
height="750"
frameBorder="0"
/>
) : (
<iframe
src={`${`${url + ticket3}/${view7}${param}`}`}
width="100%"
height="750"
frameBorder="0"
/>
)}
</div>
</Card>
</div>
</div>
</TabsContent>
</Tabs>
</div>
</div>
</div>
);
}

View File

@ -57,18 +57,6 @@ import { useTranslations } from "next-intl";
import { contextType } from "cleave.js/react"; import { contextType } from "cleave.js/react";
import { Form } from "@/components/ui/form"; import { Form } from "@/components/ui/form";
const imageSchema = z.object({
contentTitle: z.string().min(1, { message: "Judul diperlukan" }),
contentDescription: z
.string()
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }),
contentCreator: z.string().min(1, { message: "Creator diperlukan" }),
// tags: z.string().min(1, { message: "Judul diperlukan" }),
contentRewriteDescription: z
.string()
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }),
});
type Category = { type Category = {
id: number; id: number;
name: string; name: string;
@ -126,7 +114,7 @@ export default function FormConvertSPIT() {
const { id } = useParams() as { id: string }; const { id } = useParams() as { id: string };
console.log(id); console.log(id);
const editor = useRef(null); const editor = useRef(null);
type ImageSchema = z.infer<typeof imageSchema>; // type ImageSchema = z.infer<typeof imageSchema>;
const [selectedFiles, setSelectedFiles] = useState<File[]>([]); const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const taskId = Cookies.get("taskId"); const taskId = Cookies.get("taskId");
@ -151,6 +139,7 @@ export default function FormConvertSPIT() {
const [selectedArticleId, setSelectedArticleId] = useState<string | null>( const [selectedArticleId, setSelectedArticleId] = useState<string | null>(
null null
); );
const [isContentRewriteClicked, setIsContentRewriteClicked] = useState(false);
const t = useTranslations("Form"); const t = useTranslations("Form");
const [detailData, setDetailData] = useState<any>(null); const [detailData, setDetailData] = useState<any>(null);
const [selectedFileType, setSelectedFileType] = useState("original"); const [selectedFileType, setSelectedFileType] = useState("original");
@ -171,7 +160,30 @@ export default function FormConvertSPIT() {
const [isUserMabesApprover, setIsUserMabesApprover] = useState(false); const [isUserMabesApprover, setIsUserMabesApprover] = useState(false);
const [files, setFiles] = useState<FileType[]>([]); const [files, setFiles] = useState<FileType[]>([]);
const [selectedWritingStyle, setSelectedWritingStyle] = const [selectedWritingStyle, setSelectedWritingStyle] =
useState("profesional"); useState("Professional");
const imageSchema = z
.object({
contentTitle: z.string().min(1, { message: "Judul diperlukan" }),
contentDescription: z
.string()
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }),
contentCreator: z.string().min(1, { message: "Creator diperlukan" }),
contentRewriteDescription: z.string().optional(),
})
.refine(
(data) => {
// Jika tombol rewrite diklik, pastikan field-nya tidak kosong
if (isContentRewriteClicked) {
return detail?.contentRewriteDescription?.trim().length > 0;
}
return true; // tidak diklik => tidak wajib
},
{
path: ["contentRewriteDescription"],
message: "File hasil rewrite wajib diisi",
}
);
const options: Option[] = [ const options: Option[] = [
{ id: "all", label: "SEMUA" }, { id: "all", label: "SEMUA" },
@ -458,7 +470,7 @@ export default function FormConvertSPIT() {
const save = async (data: { const save = async (data: {
contentTitle: string; contentTitle: string;
contentDescription: string; contentDescription: string;
contentRewriteDescription: string; contentRewriteDescription?: string;
contentCreator: string; contentCreator: string;
}): Promise<void> => { }): Promise<void> => {
const temp = []; const temp = [];
@ -498,26 +510,19 @@ export default function FormConvertSPIT() {
}; };
const onSubmit = async (data: z.infer<typeof imageSchema>) => { const onSubmit = async (data: z.infer<typeof imageSchema>) => {
if (form.getValues("contentTitle") == "") { MySwal.fire({
form.setError("contentTitle", { title: "Simpan Data",
type: "manual", text: "Apakah Anda yakin ingin menyimpan data ini?",
message: "Required", icon: "warning",
}); showCancelButton: true,
} else { cancelButtonColor: "#d33",
MySwal.fire({ confirmButtonColor: "#3085d6",
title: "Simpan Data", confirmButtonText: "Simpan",
text: "Apakah Anda yakin ingin menyimpan data ini?", }).then((result) => {
icon: "warning", if (result.isConfirmed) {
showCancelButton: true, save(data);
cancelButtonColor: "#d33", }
confirmButtonColor: "#3085d6", });
confirmButtonText: "Simpan",
}).then((result) => {
if (result.isConfirmed) {
save(data);
}
});
}
}; };
const [showRewriteEditor, setShowRewriteEditor] = useState(false); const [showRewriteEditor, setShowRewriteEditor] = useState(false);
@ -527,6 +532,7 @@ export default function FormConvertSPIT() {
// }; // };
const handleRewriteClick = async () => { const handleRewriteClick = async () => {
setIsContentRewriteClicked(true);
const request = { const request = {
style: selectedWritingStyle, style: selectedWritingStyle,
lang: "id", lang: "id",
@ -741,7 +747,7 @@ export default function FormConvertSPIT() {
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="friendly">Friendly</SelectItem> <SelectItem value="friendly">Friendly</SelectItem>
<SelectItem value="profesional"> <SelectItem value="professional">
Profesional Profesional
</SelectItem> </SelectItem>
<SelectItem value="informational"> <SelectItem value="informational">

View File

@ -83,9 +83,7 @@ export function CalendarPolriAdd() {
FileUploaded[] FileUploaded[]
>([]); >([]);
const [imageFiles, setImageFiles] = React.useState<FileWithPreview[]>([]); const [imageFiles, setImageFiles] = React.useState<FileWithPreview[]>([]);
const [date, setDate] = React.useState<DateRange | undefined>({ const [date, setDate] = React.useState<DateRange | undefined>();
from: new Date(2025, 0, 1),
});
const [unitSelection, setUnitSelection] = React.useState({ const [unitSelection, setUnitSelection] = React.useState({
semua: false, semua: false,

View File

@ -40,11 +40,13 @@ import { Icon } from "@/components/ui/icon";
const calendarSchema = z.object({ const calendarSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }), title: z.string().min(1, { message: "Judul diperlukan" }),
description: z.string().min(1, { message: "Judul diperlukan" }), description: z.string().min(1, { message: "Judul diperlukan" }),
redirectLink: z.string().min(1, { message: "Judul diperlukan" }),
}); });
interface FileWithPreview extends File { type FileWithPreview = {
file: File;
preview: string; preview: string;
} };
interface FileUploaded { interface FileUploaded {
id: number; id: number;
@ -181,113 +183,46 @@ export function TambahIklanModal() {
satker: "4", satker: "4",
internasional: "5", internasional: "5",
}; };
const assignmentToString = Object.keys(unitSelection) const assignmentToString = Object.keys(unitSelection)
.filter((key) => unitSelection[key as keyof typeof unitSelection]) .filter((key) => unitSelection[key as keyof typeof unitSelection])
.map((key) => unitMapping[key as keyof typeof unitMapping]) .map((key) => unitMapping[key as keyof typeof unitMapping])
.join(","); .join(",");
const requestData = {
title: data.title,
placements: selectedPlacement,
description: data.description,
redirectLink: "https://new.netidhub.com",
createdBy: assignmentToString,
assignedToLevel: handlePoldaPolresChange(),
};
console.log("Form Data Submitted:", requestData); const assignedLevel = handlePoldaPolresChange();
const response = await postAdvertisements(requestData); const formData = new FormData();
if (response?.error) { formData.append("title", data.title);
error(response?.message); formData.append("placements", selectedPlacement);
return false; formData.append("description", data.description);
formData.append("redirectLink", data.redirectLink);
formData.append("createdBy", assignmentToString);
formData.append("assignedToLevel", assignedLevel);
// Append each actual File instance
if (imageFiles.length > 0) {
formData.append("files", imageFiles[0].file);
} }
Cookies.set("scheduleId", response?.data?.data.id, { try {
expires: 1, const response = await postAdvertisements(formData);
});
loading(); if (response?.error) {
if (imageFiles?.length === 0) { error(response?.message);
return false;
}
Cookies.set("scheduleId", response?.data?.data.id, {
expires: 1,
});
loading();
setIsImageUploadFinish(true); setIsImageUploadFinish(true);
} catch (e: any) {
error(e.message || "Terjadi kesalahan saat menyimpan data");
} }
imageFiles?.map(async (item: any, index: number) => {
await uploadResumableFile(index, String(id), item, "1", "0");
});
}; };
async function uploadResumableFile(
idx: number,
id: string,
file: any,
fileTypeId: string,
duration: string
) {
console.log(idx, id, file, fileTypeId, duration);
const resCsrf = await getCsrfToken();
const csrfToken = resCsrf?.data?.token;
console.log("CSRF TOKEN : ", csrfToken);
const headers = {
"X-XSRF-TOKEN": csrfToken,
};
const upload = new Upload(file, {
endpoint: `${process.env.NEXT_PUBLIC_API}/advertisment/file/upload`,
headers: headers,
retryDelays: [0, 3000, 6000, 12_000, 24_000],
chunkSize: 20_000,
metadata: {
advertisementsId: id,
filename: file.name,
contentType: file.type,
fileTypeId: fileTypeId,
duration,
},
onBeforeRequest: function (req) {
var xhr = req.getUnderlyingObject();
xhr.withCredentials = true;
},
onError: async (e: any) => {
console.log("Error upload :", e);
error(e);
},
onChunkComplete: (
chunkSize: any,
bytesAccepted: any,
bytesTotal: any
) => {
// const uploadPersen = Math.floor((bytesAccepted / bytesTotal) * 100);
// progressInfo[idx].percentage = uploadPersen;
// counterUpdateProgress++;
// console.log(counterUpdateProgress);
// setProgressList(progressInfo);
// setCounterProgress(counterUpdateProgress);
},
onSuccess: async () => {
// uploadPersen = 100;
// progressInfo[idx].percentage = 100;
// counterUpdateProgress++;
// setCounterProgress(counterUpdateProgress);
successTodo();
if (fileTypeId == "1") {
setIsImageUploadFinish(true);
}
},
});
upload.start();
}
React.useEffect(() => {
successTodo();
}, [isImageUploadFinish]);
function successTodo() {
if (isImageUploadFinish) {
successSubmit("/in/admin/settings/iklan");
}
}
const successSubmit = (redirect: string) => { const successSubmit = (redirect: string) => {
MySwal.fire({ MySwal.fire({
title: "Sukses", title: "Sukses",
@ -336,7 +271,7 @@ export function TambahIklanModal() {
<div className="space-y-4"> <div className="space-y-4">
<div> <div>
<p className="font-medium">Target Area</p> <p className="font-medium">Target Area</p>
<div className="flex flex-wrap gap-4 mt-2"> <div className="flex flex-wrap gap-4 mt-2 ml-3">
{[ {[
{ label: "Kiri - 1", value: "left-top" }, { label: "Kiri - 1", value: "left-top" },
{ label: "Kiri - 2", value: "left-bottom" }, { label: "Kiri - 2", value: "left-bottom" },
@ -484,52 +419,50 @@ export function TambahIklanModal() {
<p className="text-red-400 text-sm">{errors.title.message}</p> <p className="text-red-400 text-sm">{errors.title.message}</p>
)} )}
</div> </div>
<div>
<p className="font-medium">Link</p>
<Controller
control={control}
name="redirectLink"
render={({ field }) => (
<Input
size={"md"}
type="text"
value={field.value}
onChange={field.onChange}
placeholder="Masukan Link"
/>
)}
/>
{errors.title?.message && (
<p className="text-red-400 text-sm">{errors.title.message}</p>
)}
</div>
<div> <div>
<Label>Foto</Label> <p className="font-medium">Foto</p>
<FileUploader <input
accept={{ type="file"
"image/*": [], accept="image/*"
multiple
onChange={(e) => {
if (e.target.files) {
const files = Array.from(e.target.files).map((file) => ({
file: file,
preview: URL.createObjectURL(file),
}));
setImageFiles(files); // ✅ simpan objek yang punya properti .file dan .preview
}
}} }}
maxSize={100}
label="Upload file dengan format .png, .jpg, atau .jpeg."
onDrop={(files) => setImageFiles(files)}
/> />
{imageUploadedFiles?.map((file: any, index: number) => ( {imageFiles.map(({ preview }, index) => (
<div> <Card key={index} className="mt-2">
<Card className="mt-2"> <img
<img src={preview}
src={file.url} alt={`Preview ${index}`}
alt="Thumbnail Gambar Utama" className="w-full h-auto rounded-md"
className="w-full h-auto rounded-md" />
/> </Card>
</Card>
<div
key={index}
className=" flex justify-between border px-3.5 py-3 my-6 rounded-md"
>
<div className="flex gap-3 items-center">
<div className="file-preview">
{renderFilePreview(file.url)}
</div>
<div>
<div className=" text-sm text-card-foreground">
{file.fileName}
</div>
</div>
</div>
<Button
size="icon"
color="destructive"
variant="outline"
className=" border-none rounded-full"
onClick={() => handleRemoveFile(file)}
>
<Icon icon="tabler:x" className=" h-5 w-5" />
</Button>
</div>
</div>
))} ))}
</div> </div>

View File

@ -531,10 +531,10 @@ const LoginForm = () => {
</div> </div>
<Button <Button
type="button" type="submit"
fullWidth fullWidth
onClick={handleEmailValidation} // onClick={handleEmailValidation}
disabled={isPending} // disabled={isPending}
> >
Selanjutnya Selanjutnya
</Button> </Button>

View File

@ -1815,25 +1815,60 @@ export function getMenuList(pathname: string, t: any): Group[] {
active: pathname.includes("/schedule"), active: pathname.includes("/schedule"),
icon: "uil:schedule", icon: "uil:schedule",
submenus: [ 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: [],
// },
{ {
href: "/contributor/schedule/press-conference", href: "/contributor/schedule/live-report",
label: t("press-conference"), label: t("live-report"),
active: pathname.includes("/schedule/press-conference"), active: pathname.includes("/schedule/live-report"),
icon: "heroicons:arrow-trending-up", icon: "heroicons:arrow-trending-up",
children: [], children: [],
}, },
// {
// 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: "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: [],
// },
{ {
href: "/contributor/schedule/event", href: "/contributor/schedule/calendar-polri",
label: t("event"), label: t("calendar-polri"),
active: pathname.includes("/schedule/event"), active: pathname.includes("/schedule/calendar-polri"),
icon: "heroicons:shopping-cart", icon: "heroicons:arrow-trending-up",
children: [],
},
{
href: "/contributor/schedule/press-release",
label: t("press-release"),
active: pathname.includes("/schedule/press-release"),
icon: "heroicons:shopping-cart",
children: [], children: [],
}, },
], ],
@ -2946,9 +2981,9 @@ export function getMenuList(pathname: string, t: any): Group[] {
icon: "material-symbols:dashboard", icon: "material-symbols:dashboard",
submenus: [ submenus: [
{ {
href: "/dashboard/executive", href: "/dashboard/executive-data",
label: "Executive", label: "Executive",
active: pathname === "/dashboard/executive", active: pathname === "/dashboard/executive-data",
icon: "heroicons:arrow-trending-up", icon: "heroicons:arrow-trending-up",
children: [], children: [],
}, },
@ -3277,9 +3312,9 @@ export function getMenuList(pathname: string, t: any): Group[] {
icon: "material-symbols:dashboard", icon: "material-symbols:dashboard",
submenus: [ submenus: [
{ {
href: "/dashboard/executive", href: "/dashboard/executive-data",
label: "Executive", label: "Executive",
active: pathname === "/dashboard/executive", active: pathname === "/dashboard/executive-data",
icon: "heroicons:arrow-trending-up", icon: "heroicons:arrow-trending-up",
children: [], children: [],
}, },
@ -3704,15 +3739,15 @@ export function getMenuList(pathname: string, t: any): Group[] {
menus: [ menus: [
{ {
id: "dashboard", id: "dashboard",
href: "/dashboard", href: "/dashboard/executive-data",
label: t("dashboard"), label: t("dashboard"),
active: pathname.includes("/dashboard"), active: pathname.includes("/dashboard/executive-data"),
icon: "material-symbols:dashboard", icon: "material-symbols:dashboard",
submenus: [ submenus: [
{ {
href: "/dashboard/executive", href: "/dashboard/executive-data",
label: "Executive", label: "Executive",
active: pathname === "/dashboard/executive", active: pathname === "/dashboard/executive-data",
icon: "heroicons:arrow-trending-up", icon: "heroicons:arrow-trending-up",
children: [], children: [],
}, },