fix: button and content curation in polres
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Sabda Yagra 2026-04-06 21:35:20 +07:00
parent c32223e4a2
commit 02f23edbeb
10 changed files with 280 additions and 86 deletions

View File

@ -24,7 +24,7 @@ import ReportTable from "../contributor/report/components/report-table";
const DashboardPage = () => {
const t = useTranslations("AnalyticsDashboard");
const roleId = getCookiesDecrypt("urie");
const levelNumber = Number(getCookiesDecrypt("ulne"));
const levelNumber = getCookiesDecrypt("ulne");
return Number(roleId) == 2 || Number(roleId) == 11 || Number(roleId) == 12 ? (
<div>
@ -55,7 +55,7 @@ const DashboardPage = () => {
>
{t("schedule", { defaultValue: "Schedule" })}
</TabsTrigger>
{levelNumber !== 3 && (
{levelNumber !== "2" && (
<>
<TabsTrigger
value="indeks"
@ -65,12 +65,20 @@ const DashboardPage = () => {
</TabsTrigger>
</>
)}
{levelNumber !== "3" && (
<TabsTrigger
value="report"
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground rounded-md px-6"
>
{t("report", { defaultValue: "Report" })}
</TabsTrigger>
)}
{/* <TabsTrigger
value="report"
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground rounded-md px-6"
>
{t("report", { defaultValue: "Report" })}
</TabsTrigger> */}
</TabsList>
</Card>
<TabsContent value="routine-task">

View File

@ -19,6 +19,8 @@ import { Icon } from "@iconify/react/dist/iconify.js";
import { hasData } from "jquery";
import { useParams, usePathname, useRouter } from "next/navigation";
import React, { Component, useEffect, useState } from "react";
import { listDataTracking } from "@/service/media-tracking/media-tracking"; // sesuaikan path
import { getCookiesDecrypt } from "@/lib/utils";
const AudioSliderPage = () => {
const [audioData, setAudioData] = useState<any>();
@ -31,14 +33,47 @@ const AudioSliderPage = () => {
initFetch();
}, [page, limit, search]);
const initFetch = async () => {
const response = await listCuratedContent(search, limit, page - 1, 4, "1");
// console.log(response);
// const initFetch = async () => {
// const response = await listCuratedContent(search, limit, page - 1, 4, "1");
// // console.log(response);
const data = response?.data?.data;
const contentData = data?.content;
setHasData(displayAudio && displayAudio.length > 0);
setDisplayAudio(contentData);
// const data = response?.data?.data;
// const contentData = data?.content;
// setHasData(displayAudio && displayAudio.length > 0);
// setDisplayAudio(contentData);
// };
const initFetch = async () => {
try {
const levelName = (getCookiesDecrypt("levelName") || "").toUpperCase();
const res = await listDataTracking(
limit,
page - 1,
search,
"", // category
"", // status
levelName,
"4", // ✅ AUDIO
);
const data = res?.data?.data?.content || [];
console.log("AUDIO DATA:", data);
const mapped = data.map((item: any) => ({
id: item.id,
title: item.title,
createdAt: item.createdAt,
timezone: item.timezone,
clickCount: item.clickCount || 0,
}));
setDisplayAudio(mapped);
setHasData(mapped.length > 0);
} catch (error) {
console.error("Error fetching audio:", error);
}
};
const shuffleArray = (array: any[]) => {

View File

@ -18,6 +18,8 @@ import {
import { Icon } from "@iconify/react/dist/iconify.js";
import { useParams, usePathname, useRouter } from "next/navigation";
import React, { Component, useEffect, useState } from "react";
import { listDataTracking } from "@/service/media-tracking/media-tracking"; // sesuaikan path
import { getCookiesDecrypt } from "@/lib/utils";
const TeksSliderPage = () => {
const [documentData, setDocumentData] = useState<any>();
@ -31,14 +33,47 @@ const TeksSliderPage = () => {
initFetch();
}, [page, limit, search]);
const initFetch = async () => {
const response = await listCuratedContent(search, limit, page - 1, 3, "1");
// console.log(response);
// const initFetch = async () => {
// const response = await listCuratedContent(search, limit, page - 1, 3, "1");
// // console.log(response);
const data = response?.data?.data;
const contentData = data?.content;
setHasData(displayDocument && displayDocument.length > 0);
setDisplayDocument(contentData);
// const data = response?.data?.data;
// const contentData = data?.content;
// setHasData(displayDocument && displayDocument.length > 0);
// setDisplayDocument(contentData);
// };
const initFetch = async () => {
try {
const levelName = (getCookiesDecrypt("levelName") || "").toUpperCase();
const res = await listDataTracking(
limit,
page - 1,
search,
"", // category
"", // status
levelName,
"3", // ✅ TEXT / DOCUMENT
);
const data = res?.data?.data?.content || [];
console.log("TEXT DATA:", data);
const mapped = data.map((item: any) => ({
id: item.id,
title: item.title,
createdAt: item.createdAt,
timezone: item.timezone,
clickCount: item.clickCount || 0,
}));
setDisplayDocument(mapped);
setHasData(mapped.length > 0);
} catch (error) {
console.error("Error fetching text:", error);
}
};
return (
@ -83,7 +118,7 @@ const TeksSliderPage = () => {
<div className="flex flex-col flex-1">
<div className="text-gray-500 dark:text-gray-400 flex flex-row text-sm">
{formatDateToIndonesian(
new Date(document?.createdAt)
new Date(document?.createdAt),
)}{" "}
{document?.timezone ? document?.timezone : "WIB"} |{" "}
<Icon icon="formkit:eye" width="15" height="15" />{" "}

View File

@ -16,6 +16,11 @@ import { formatDateToIndonesian } from "@/utils/globals";
import { Icon } from "@iconify/react/dist/iconify.js";
import { useRouter } from "next/navigation";
import React, { useEffect, useState } from "react";
import {
listDataAllNonPagination,
listDataTracking,
mediaTrackingSave,
} from "@/service/media-tracking/media-tracking";
type ImageData = {
id: string;
@ -33,14 +38,48 @@ const ImageSliderPage = () => {
const [page, setPage] = useState(1);
const [limit] = useState(10);
const ALLOWED_LEVEL = ["POLDA", "POLRES", "SATKER", "MABES"];
useEffect(() => {
fetchData();
}, [page]);
// const fetchData = async () => {
// const response = await listCuratedContent("", limit, page - 1, 1, "1");
// const data = response?.data?.data?.content || [];
// setImageData(data);
// };
const fetchData = async () => {
const response = await listCuratedContent("", limit, page - 1, 1, "1");
const data = response?.data?.data?.content || [];
setImageData(data);
try {
const levelName = getCookiesDecrypt("levelName") || "";
const res = await listDataTracking(
limit,
page - 1,
"",
"",
"",
levelName
);
const data = res?.data?.data?.content || [];
console.log("RAW DATA:", data);
const mapped = data.map((item: any) => ({
id: item.id,
title: item.title,
createdAt: item.createdAt,
timezone: item.timezone,
thumbnailLink: item.thumbnailLink,
clickCount: item.clickCount || 0,
}));
setImageData(mapped);
} catch (error) {
console.error(error);
}
};
return (
@ -78,7 +117,7 @@ const ImageSliderPage = () => {
<div className="p-3">
<p className="text-xs text-gray-500">
{formatDateToIndonesian(
new Date(image?.createdAt)
new Date(image?.createdAt),
)}{" "}
{image?.timezone || "WIB"} |
<Icon

View File

@ -16,6 +16,8 @@ import { formatDateToIndonesian } from "@/utils/globals";
import { Icon } from "@iconify/react/dist/iconify.js";
import image from "next/image";
import React, { useEffect, useState } from "react";
import { listDataTracking } from "@/service/media-tracking/media-tracking"; // sesuaikan path
import { getCookiesDecrypt } from "@/lib/utils";
type VideoData = {
id: string;
@ -37,13 +39,46 @@ const VideoSliderPage = () => {
fetchData();
}, [page, limit, search]);
const fetchData = async () => {
const response = await listCuratedContent(search, limit, page - 1, 2, "1");
// console.log(response);
// const fetchData = async () => {
// const response = await listCuratedContent(search, limit, page - 1, 2, "1");
// // console.log(response);
const data = response?.data?.data;
const contentData = data?.content;
setVideoData(contentData);
// const data = response?.data?.data;
// const contentData = data?.content;
// setVideoData(contentData);
// };
const fetchData = async () => {
try {
const levelName = (getCookiesDecrypt("levelName") || "").toUpperCase();
const res = await listDataTracking(
limit,
page - 1,
search,
"", // category
"", // status
levelName,
"2",
);
const data = res?.data?.data?.content || [];
console.log("VIDEO DATA:", data);
const mapped = data.map((item: any) => ({
id: item.id,
title: item.title,
createdAt: item.createdAt,
timezone: item.timezone,
thumbnailLink: item.thumbnailLink, // ✅ penting (bukan thumbnailUrl)
clickCount: item.clickCount || 0,
}));
setVideoData(mapped);
} catch (error) {
console.error("Error fetching video:", error);
}
};
return (
@ -77,7 +112,7 @@ const VideoSliderPage = () => {
<div className="p-3">
<p className="text-xs text-gray-500">
{formatDateToIndonesian(
new Date(video?.createdAt)
new Date(video?.createdAt),
)}{" "}
{video?.timezone || "WIB"} |
<Icon

View File

@ -79,7 +79,7 @@ const CustomEditor = dynamic(
() => {
return import("@/components/editor/custom-editor");
},
{ ssr: false }
{ ssr: false },
);
export default function FormAudio() {
@ -117,7 +117,7 @@ export default function FormAudio() {
const [isGeneratedArticle, setIsGeneratedArticle] = useState(false);
const [articleBody, setArticleBody] = useState<string>("");
const [selectedArticleId, setSelectedArticleId] = useState<string | null>(
null
null,
);
const [selectedMainKeyword, setSelectedMainKeyword] = useState("");
const [selectedSize, setSelectedSize] = useState("");
@ -185,7 +185,7 @@ export default function FormAudio() {
}
const filesWithPreview = acceptedFiles.map((file) =>
Object.assign(file, { preview: URL.createObjectURL(file) })
Object.assign(file, { preview: URL.createObjectURL(file) }),
);
setFiles((prevFiles) => [...prevFiles, ...filesWithPreview]);
@ -420,7 +420,7 @@ export default function FormAudio() {
const articleData = await waitForStatusUpdate();
const cleanArticleBody = articleData?.articleBody?.replace(
/<img[^>]*>/g,
""
"",
);
const articleImagesData = articleData?.imagesUrl?.split(",");
setValue("description", cleanArticleBody || "");
@ -474,7 +474,7 @@ export default function FormAudio() {
if (scheduleId && scheduleType === "3") {
const findCategory = resCategory.find((o) =>
o.name.toLowerCase().includes("pers rilis")
o.name.toLowerCase().includes("pers rilis"),
);
if (findCategory) {
@ -499,7 +499,7 @@ export default function FormAudio() {
setPublishedFor(
options
.filter((opt: any) => opt.id !== "all")
.map((opt: any) => opt.id)
.map((opt: any) => opt.id),
);
}
} else {
@ -654,7 +654,7 @@ export default function FormAudio() {
data.descriptionOri = translatedContent;
console.log(
"🌍 Translate dimasukkan ke descriptionOri:",
translatedContent
translatedContent,
);
}
@ -762,7 +762,7 @@ export default function FormAudio() {
idx: number,
id: string,
file: any,
duration: string
duration: string,
) {
// console.log(idx, id, file, duration);
@ -799,7 +799,7 @@ export default function FormAudio() {
onChunkComplete: (
chunkSize: any,
bytesAccepted: any,
bytesTotal: any
bytesTotal: any,
) => {
const uploadPersen = Math.floor((bytesAccepted / bytesTotal) * 100);
progressInfo[idx].percentage = uploadPersen;
@ -1754,7 +1754,7 @@ export default function FormAudio() {
const isChecked =
option.id === "all"
? isAllChecked
: field.value?.includes(option.id) ?? false;
: (field.value?.includes(option.id) ?? false);
const handleChange = () => {
let updated: string[] = [];
@ -1767,7 +1767,9 @@ export default function FormAudio() {
.map((opt: any) => opt.id);
} else {
updated = isChecked
? (field.value ?? []).filter((val) => val !== option.id)
? (field.value ?? []).filter(
(val) => val !== option.id,
)
: [...(field.value ?? []), option.id];
if (isAllChecked && option.id !== "all") {
@ -1806,16 +1808,24 @@ export default function FormAudio() {
</Card>
<div className="flex flex-row justify-end gap-3">
<div className="mt-4">
{levelNumber !== "2" && (
{levelNumber !== "2" && levelNumber !== "3" && (
<Button type="submit" color="primary">
{t("submit", { defaultValue: "Submit" })}
</Button>
)}
</div>
<div className="mt-4">
<Button type="submit" color="primary" variant="outline">
<Button
type="button"
color="primary"
variant="outline"
onClick={() => router.back()}
>
{t("cancel", { defaultValue: "Cancel" })}
</Button>
{/* <Button type="submit" color="primary" variant="outline">
{t("cancel", { defaultValue: "Cancel" })}
</Button> */}
</div>
</div>
</div>

View File

@ -1684,7 +1684,7 @@ export default function FormImage() {
const isChecked =
option.id === "all"
? isAllChecked
: field.value?.includes(option.id) ?? false;
: (field.value?.includes(option.id) ?? false);
const handleChange = () => {
let updated: string[] = [];
@ -1697,7 +1697,9 @@ export default function FormImage() {
.map((opt: any) => opt.id);
} else {
updated = isChecked
? (field.value ?? []).filter((val) => val !== option.id)
? (field.value ?? []).filter(
(val) => val !== option.id,
)
: [...(field.value ?? []), option.id];
if (isAllChecked && option.id !== "all") {
@ -1741,16 +1743,24 @@ export default function FormImage() {
{/* <Button type="submit" color="primary">
{t("submit", { defaultValue: "Submit" })}
</Button> */}
{levelNumber !== "2" && (
{levelNumber !== "2" && levelNumber !== "3" && (
<Button type="submit" color="primary">
{t("submit", { defaultValue: "Submit" })}
</Button>
)}
</div>
<div className="mt-4">
<Button type="submit" color="primary" variant="outline">
<Button
type="button"
color="primary"
variant="outline"
onClick={() => router.back()}
>
{t("cancel", { defaultValue: "Cancel" })}
</Button>
{/* <Button type="submit" color="primary" variant="outline">
{t("cancel", { defaultValue: "Cancel" })}
</Button> */}
</div>
</div>
</div>

View File

@ -71,7 +71,7 @@ const CustomEditor = dynamic(
() => {
return import("@/components/editor/custom-editor");
},
{ ssr: false }
{ ssr: false },
);
type Option = {
@ -113,7 +113,7 @@ export default function FormTeks() {
const [isGeneratedArticle, setIsGeneratedArticle] = useState(false);
const [articleBody, setArticleBody] = useState<string>("");
const [selectedArticleId, setSelectedArticleId] = useState<string | null>(
null
null,
);
const [isContentRewriteClicked, setIsContentRewriteClicked] = useState(false);
const [showRewriteEditor, setShowRewriteEditor] = useState(false);
@ -174,13 +174,13 @@ export default function FormTeks() {
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.ms-powerpoint",
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
].includes(file.type) && file.size <= 20 * 1024 * 1024
].includes(file.type) && file.size <= 20 * 1024 * 1024,
);
const filesWithPreview = filtered.map((file) =>
Object.assign(file, {
preview: URL.createObjectURL(file),
})
}),
);
setFiles(filesWithPreview);
@ -214,8 +214,8 @@ export default function FormTeks() {
"application/vnd.ms-powerpoint",
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
].includes(file.type),
{ message: "Format file tidak didukung" }
)
{ message: "Format file tidak didukung" },
),
)
.min(1, { message: "File wajib diunggah" }),
description: z.string().optional(),
@ -430,7 +430,7 @@ export default function FormTeks() {
const articleData = await waitForStatusUpdate();
const cleanArticleBody = articleData?.articleBody?.replace(
/<img[^>]*>/g,
""
"",
);
const articleImagesData = articleData?.imagesUrl?.split(",");
setValue("description", cleanArticleBody || "");
@ -490,7 +490,7 @@ export default function FormTeks() {
if (scheduleId && scheduleType === "3") {
const findCategory = resCategory.find((o) =>
o.name.toLowerCase().includes("pers rilis")
o.name.toLowerCase().includes("pers rilis"),
);
if (findCategory) {
@ -515,7 +515,7 @@ export default function FormTeks() {
setPublishedFor(
options
.filter((opt: any) => opt.id !== "all")
.map((opt: any) => opt.id)
.map((opt: any) => opt.id),
);
}
} else {
@ -684,7 +684,7 @@ export default function FormTeks() {
data.descriptionOri = translatedContent;
console.log(
"🌍 Translate dimasukkan ke descriptionOri:",
translatedContent
translatedContent,
);
}
@ -755,7 +755,7 @@ export default function FormTeks() {
index,
String(id),
item,
fileTypeId == "2" || fileTypeId == "4" ? item.duration : "0"
fileTypeId == "2" || fileTypeId == "4" ? item.duration : "0",
);
});
@ -782,7 +782,7 @@ export default function FormTeks() {
idx: number,
id: string,
file: any,
duration: string
duration: string,
) {
// console.log(idx, id, file, duration);
@ -819,7 +819,7 @@ export default function FormTeks() {
onChunkComplete: (
chunkSize: any,
bytesAccepted: any,
bytesTotal: any
bytesTotal: any,
) => {
const uploadPersen = Math.floor((bytesAccepted / bytesTotal) * 100);
progressInfo[idx].percentage = uploadPersen;
@ -1721,7 +1721,7 @@ export default function FormTeks() {
const isChecked =
option.id === "all"
? isAllChecked
: field.value?.includes(option.id) ?? false;
: (field.value?.includes(option.id) ?? false);
const handleChange = () => {
let updated: string[] = [];
@ -1734,7 +1734,9 @@ export default function FormTeks() {
.map((opt: any) => opt.id);
} else {
updated = isChecked
? (field.value ?? []).filter((val) => val !== option.id)
? (field.value ?? []).filter(
(val) => val !== option.id,
)
: [...(field.value ?? []), option.id];
if (isAllChecked && option.id !== "all") {
@ -1776,16 +1778,24 @@ export default function FormTeks() {
{/* <Button type="submit" color="primary">
{t("submit", { defaultValue: "Submit" })}
</Button> */}
{levelNumber !== "2" && (
{levelNumber !== "2" && levelNumber !== "3" && (
<Button type="submit" color="primary">
{t("submit", { defaultValue: "Submit" })}
</Button>
)}
</div>
<div className="mt-4">
<Button type="submit" color="primary" variant="outline">
<Button
type="button"
color="primary"
variant="outline"
onClick={() => router.back()}
>
{t("cancel", { defaultValue: "Cancel" })}
</Button>
{/* <Button type="submit" color="primary" variant="outline">
{t("cancel", { defaultValue: "Cancel" })}
</Button> */}
</div>
</div>
</div>

View File

@ -1770,7 +1770,7 @@ export default function FormVideo() {
const isChecked =
option.id === "all"
? isAllChecked
: field.value?.includes(option.id) ?? false;
: (field.value?.includes(option.id) ?? false);
const handleChange = () => {
let updated: string[] = [];
@ -1783,7 +1783,9 @@ export default function FormVideo() {
.map((opt: any) => opt.id);
} else {
updated = isChecked
? (field.value ?? []).filter((val) => val !== option.id)
? (field.value ?? []).filter(
(val) => val !== option.id,
)
: [...(field.value ?? []), option.id];
if (isAllChecked && option.id !== "all") {
@ -1825,16 +1827,24 @@ export default function FormVideo() {
{/* <Button type="submit" color="primary">
{t("submit", { defaultValue: "Submit" })}
</Button> */}
{levelNumber !== "2" && (
{levelNumber !== "2" && levelNumber !== "3" && (
<Button type="submit" color="primary">
{t("submit", { defaultValue: "Submit" })}
</Button>
)}
</div>
<div className="mt-4">
<Button type="submit" color="primary" variant="outline">
<Button
type="button"
color="primary"
variant="outline"
onClick={() => router.back()}
>
{t("cancel", { defaultValue: "Cancel" })}
</Button>
{/* <Button type="submit" color="primary" variant="outline">
{t("cancel", { defaultValue: "Cancel" })}
</Button> */}
</div>
</div>
</div>

View File

@ -57,10 +57,12 @@ export async function listDataTracking(
page: number,
keyword = "",
categoryFilter = "",
statusFilter = ""
statusFilter = "",
creatorGroupLevelName = "",
typeId = ""
) {
return await httpGetInterceptor(
`media/list?isForAdmin=true&title=${keyword}&enablePage=1&sortBy=createdAt&sort=desc&size=${size}&page=${page}&categoryId=${categoryFilter}&statusId=${statusFilter}`
`media/list?isForAdmin=true&title=${keyword}&enablePage=1&sortBy=createdAt&sort=desc&size=${size}&page=${page}&categoryId=${categoryFilter}&statusId=${statusFilter}&creatorGroupLevelName=${creatorGroupLevelName}&typeId=${typeId}`
);
}