Merge branch 'main' of https://gitlab.com/hanifsalafi/mediahub_redesign into prod
This commit is contained in:
commit
ca5a150015
|
|
@ -101,10 +101,8 @@ const useTableColumns = () => {
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const levelNumber = Number(getCookiesDecrypt("ulne")); // 1 = Mabes, 2 = Polda
|
const levelNumber = Number(getCookiesDecrypt("ulne"));
|
||||||
const calendarOwnerLevel = Number(row.original.assignedToLevel); // dari API
|
const userId = Number(getCookiesDecrypt("uie"));
|
||||||
const calendarOwner = row.original.assignedTo; // ID unit Polda/Mabes dari API
|
|
||||||
const myUnit = getCookiesDecrypt("unitId"); // misal ID unit Polda login
|
|
||||||
|
|
||||||
async function doDelete(id: any) {
|
async function doDelete(id: any) {
|
||||||
const response = await deleteCalendar(id);
|
const response = await deleteCalendar(id);
|
||||||
|
|
@ -143,29 +141,6 @@ const useTableColumns = () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// === RULE AKSI ===
|
|
||||||
let canEdit = false;
|
|
||||||
let canDelete = false;
|
|
||||||
const canView = true;
|
|
||||||
|
|
||||||
if (levelNumber === 1) {
|
|
||||||
// Mabes -> bebas
|
|
||||||
canEdit = true;
|
|
||||||
canDelete = true;
|
|
||||||
} else if (levelNumber === 2) {
|
|
||||||
// Polda
|
|
||||||
if (calendarOwnerLevel === 1) {
|
|
||||||
// kalender Mabes -> hanya view
|
|
||||||
canEdit = false;
|
|
||||||
canDelete = false;
|
|
||||||
} else if (calendarOwnerLevel === 2 && calendarOwner === myUnit) {
|
|
||||||
// kalender polda sendiri -> bisa edit/delete
|
|
||||||
canEdit = true;
|
|
||||||
canDelete = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
|
|
@ -178,7 +153,7 @@ const useTableColumns = () => {
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent className="p-0" align="end">
|
<DropdownMenuContent className="p-0" align="end">
|
||||||
{canView && (
|
|
||||||
<Link
|
<Link
|
||||||
href={`/contributor/schedule/calendar-polri/detail/${row.original.id}`}
|
href={`/contributor/schedule/calendar-polri/detail/${row.original.id}`}
|
||||||
>
|
>
|
||||||
|
|
@ -187,9 +162,9 @@ const useTableColumns = () => {
|
||||||
Detail
|
Detail
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
|
||||||
|
|
||||||
{canEdit && (
|
|
||||||
|
{row.original.createdById === userId && (
|
||||||
<Link
|
<Link
|
||||||
href={`/contributor/schedule/calendar-polri/update/${row.original.id}`}
|
href={`/contributor/schedule/calendar-polri/update/${row.original.id}`}
|
||||||
>
|
>
|
||||||
|
|
@ -200,7 +175,7 @@ const useTableColumns = () => {
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{canDelete && (
|
{row.original.createdById === userId && (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => handleDeleteCalendars(row.original.id)}
|
onClick={() => handleDeleteCalendars(row.original.id)}
|
||||||
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
import { Reveal } from "@/components/landing-page/Reveal";
|
import { Reveal } from "@/components/landing-page/Reveal";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
interface FAQItem {
|
interface FAQItem {
|
||||||
question: string;
|
question: string;
|
||||||
|
|
@ -41,19 +42,41 @@ const FAQS: React.FC = () => {
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Reveal>
|
<Reveal>
|
||||||
<div className="flex items-center justify-center mb-6">
|
<div className="flex items-center justify-center mb-6">
|
||||||
<img src="/assets/icons-faqs.png" alt="Faqs" />
|
<Image
|
||||||
<h2 className="ml-4 text-lg lg:text-2xl font-bold text-gray-800 dark:text-white">Frequently Asked Questions</h2>
|
width={1920}
|
||||||
|
height={1080}
|
||||||
|
src="/assets/icons-faqs.png"
|
||||||
|
alt="Faqs"
|
||||||
|
className="h-12 w-12"
|
||||||
|
/>
|
||||||
|
<h2 className="ml-4 text-lg lg:text-2xl font-bold text-gray-800 dark:text-white">
|
||||||
|
Frequently Asked Questions
|
||||||
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* FAQS Items */}
|
{/* FAQS Items */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{faqs?.map((faq, index) => (
|
{faqs?.map((faq, index) => (
|
||||||
<div key={index} className="border-b border-gray-300 pb-2 cursor-pointer">
|
<div
|
||||||
<div className="flex justify-between items-center" onClick={() => toggleFAQ(index)}>
|
key={index}
|
||||||
<h3 className="text-sm font-semibold text-gray-800 dark:text-white">{faq.question}</h3>
|
className="border-b border-gray-300 pb-2 cursor-pointer"
|
||||||
<span className="text-gray-500 dark:text-white text-xl">{openIndex === index ? "−" : "+"}</span>
|
>
|
||||||
|
<div
|
||||||
|
className="flex justify-between items-center"
|
||||||
|
onClick={() => toggleFAQ(index)}
|
||||||
|
>
|
||||||
|
<h3 className="text-sm font-semibold text-gray-800 dark:text-white">
|
||||||
|
{faq.question}
|
||||||
|
</h3>
|
||||||
|
<span className="text-gray-500 dark:text-white text-xl">
|
||||||
|
{openIndex === index ? "−" : "+"}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{openIndex === index && <p className="text-gray-600 dark:text-white mt-2 text-sm">{faq.answer}</p>}
|
{openIndex === index && (
|
||||||
|
<p className="text-gray-600 dark:text-white mt-2 text-sm">
|
||||||
|
{faq.answer}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ import {
|
||||||
saveMediaBlastCampaign,
|
saveMediaBlastCampaign,
|
||||||
} from "@/service/broadcast/broadcast";
|
} from "@/service/broadcast/broadcast";
|
||||||
import { error } from "@/config/swal";
|
import { error } from "@/config/swal";
|
||||||
import { useRouter } from "@/i18n/routing";
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
|
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
|
|
@ -78,6 +78,11 @@ const FormSchema = z.object({
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
interface Campaign {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default function ContentBlast(props: { type: string }) {
|
export default function ContentBlast(props: { type: string }) {
|
||||||
const editor = useRef(null);
|
const editor = useRef(null);
|
||||||
const id = useParams()?.id;
|
const id = useParams()?.id;
|
||||||
|
|
@ -85,7 +90,7 @@ export default function ContentBlast(props: { type: string }) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { type } = props;
|
const { type } = props;
|
||||||
|
|
||||||
const [dataSelectCampaign, setDataSelectCampaign] = useState([]);
|
const [dataSelectCampaign, setDataSelectCampaign] = useState<Campaign[]>([]);
|
||||||
const [openModal, setOpenModal] = useState(false);
|
const [openModal, setOpenModal] = useState(false);
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof FormSchema>>({
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
|
|
@ -295,9 +300,16 @@ export default function ContentBlast(props: { type: string }) {
|
||||||
Untuk melihat Email Terkirim silahkan cek menu “Sent”!
|
Untuk melihat Email Terkirim silahkan cek menu “Sent”!
|
||||||
</div>
|
</div>
|
||||||
<DialogFooter className="flex justify-center">
|
<DialogFooter className="flex justify-center">
|
||||||
<Button type="button" color="success">
|
<Link
|
||||||
Menu "Sent"
|
href={`/admin/broadcast/campaign-list/detail/${
|
||||||
</Button>
|
form.getValues("selected")[0]?.id
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<Button type="button" color="success">
|
||||||
|
Menu "Sent"
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => router.push("/admin/broadcast")}
|
onClick={() => router.push("/admin/broadcast")}
|
||||||
|
|
|
||||||
|
|
@ -200,6 +200,12 @@ export default function FormAudioDetail() {
|
||||||
}>
|
}>
|
||||||
>([]);
|
>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (Number(userLevelId) === 216 && Number(roleId) === 3) {
|
||||||
|
setIsUserMabesApprover(true);
|
||||||
|
}
|
||||||
|
}, [userLevelId, roleId]);
|
||||||
|
|
||||||
// Fungsi untuk mengupdate state individual file
|
// Fungsi untuk mengupdate state individual file
|
||||||
const handleFileUnitChange = (
|
const handleFileUnitChange = (
|
||||||
fileIndex: number,
|
fileIndex: number,
|
||||||
|
|
@ -219,6 +225,30 @@ export default function FormAudioDetail() {
|
||||||
currentSelection.polda = value;
|
currentSelection.polda = value;
|
||||||
currentSelection.polres = value;
|
currentSelection.polres = value;
|
||||||
currentSelection.satker = value;
|
currentSelection.satker = value;
|
||||||
|
|
||||||
|
// Update fileCheckedLevels untuk sinkronisasi dengan modal
|
||||||
|
setFileCheckedLevels((prevLevels) => {
|
||||||
|
const newArray = [...prevLevels];
|
||||||
|
const currentFileLevels = new Set<number>(newArray[fileIndex] || new Set());
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
// Checklist semua item di modal
|
||||||
|
listDest.forEach((item: any) => {
|
||||||
|
currentFileLevels.add(Number(item.id));
|
||||||
|
if (item.subDestination) {
|
||||||
|
item.subDestination.forEach((sub: any) => {
|
||||||
|
currentFileLevels.add(Number(sub.id));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Unchecklist semua item di modal
|
||||||
|
currentFileLevels.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
newArray[fileIndex] = currentFileLevels;
|
||||||
|
return newArray;
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// Validasi khusus untuk POLRES - harus ada POLDA yang ter-checklist
|
// Validasi khusus untuk POLRES - harus ada POLDA yang ter-checklist
|
||||||
if (key === "polres" && value) {
|
if (key === "polres" && value) {
|
||||||
|
|
@ -480,10 +510,28 @@ export default function FormAudioDetail() {
|
||||||
|
|
||||||
const setupPlacementCheck = (length: number) => {
|
const setupPlacementCheck = (length: number) => {
|
||||||
const temp = [];
|
const temp = [];
|
||||||
|
const unitSelections = [];
|
||||||
|
const checkedLevelsArray = [];
|
||||||
|
|
||||||
for (let i = 0; i < length; i++) {
|
for (let i = 0; i < length; i++) {
|
||||||
temp.push([]);
|
temp.push([]);
|
||||||
|
// Inisialisasi state untuk setiap file
|
||||||
|
unitSelections.push({
|
||||||
|
semua: false,
|
||||||
|
nasional: false,
|
||||||
|
wilayah: false,
|
||||||
|
international: false,
|
||||||
|
polda: false,
|
||||||
|
polres: false,
|
||||||
|
satker: false,
|
||||||
|
});
|
||||||
|
// Inisialisasi checkedLevels untuk setiap file
|
||||||
|
checkedLevelsArray.push(new Set<number>());
|
||||||
}
|
}
|
||||||
|
|
||||||
setFilePlacements(temp);
|
setFilePlacements(temp);
|
||||||
|
setFileUnitSelections(unitSelections);
|
||||||
|
setFileCheckedLevels(checkedLevelsArray);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -587,8 +635,24 @@ export default function FormAudioDetail() {
|
||||||
const temp = [];
|
const temp = [];
|
||||||
for (let i = 0; i < filePlacements?.length; i++) {
|
for (let i = 0; i < filePlacements?.length; i++) {
|
||||||
if (filePlacements[i]?.length !== 0) {
|
if (filePlacements[i]?.length !== 0) {
|
||||||
const now = filePlacements[i]?.filter((a) => a !== "all");
|
// const now = filePlacements[i]?.filter((a) => a !== "all");
|
||||||
const data = { mediaFileId: files[i]?.id, placements: now.join(",") };
|
// const data = { mediaFileId: files[i]?.id, placements: now.join(",") };
|
||||||
|
const now = filePlacements[i];
|
||||||
|
let nowArr = now?.join(",")?.replaceAll("wilayah", "polda");
|
||||||
|
nowArr = nowArr?.replaceAll("nasional", "mabes");
|
||||||
|
nowArr = nowArr?.replaceAll("semua", "all");
|
||||||
|
|
||||||
|
// Dapatkan checked levels untuk file ini
|
||||||
|
const currentFileCheckedLevels = fileCheckedLevels[i]
|
||||||
|
? Array.from(fileCheckedLevels[i])
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
mediaFileId: files[i]?.id,
|
||||||
|
placements: nowArr,
|
||||||
|
customLocationPlacements: currentFileCheckedLevels.join(","),
|
||||||
|
};
|
||||||
|
|
||||||
temp.push(data);
|
temp.push(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -596,13 +660,19 @@ export default function FormAudioDetail() {
|
||||||
};
|
};
|
||||||
|
|
||||||
async function save() {
|
async function save() {
|
||||||
|
// Gabungkan semua checkedLevels dari semua file
|
||||||
|
const allCheckedLevels = new Set<number>();
|
||||||
|
fileCheckedLevels.forEach((fileLevels) => {
|
||||||
|
fileLevels.forEach((levelId) => {
|
||||||
|
allCheckedLevels.add(levelId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
mediaUploadId: id,
|
mediaUploadId: id,
|
||||||
statusId: status,
|
statusId: status,
|
||||||
message: description,
|
message: description,
|
||||||
// files: [],
|
|
||||||
files: isUserMabesApprover ? getPlacement() : [],
|
files: isUserMabesApprover ? getPlacement() : [],
|
||||||
customLocationPlacement: Array.from(checkedLevels).join(","),
|
|
||||||
};
|
};
|
||||||
setModalOpen(false);
|
setModalOpen(false);
|
||||||
loading();
|
loading();
|
||||||
|
|
@ -638,37 +708,129 @@ export default function FormAudioDetail() {
|
||||||
setRejectedFiles(rejects);
|
setRejectedFiles(rejects);
|
||||||
}
|
}
|
||||||
|
|
||||||
// const setupPlacement = (
|
const setupPlacement = (
|
||||||
// index: number,
|
index: number,
|
||||||
// placement: string,
|
placement: string,
|
||||||
// checked: boolean
|
checked: boolean
|
||||||
// ) => {
|
) => {
|
||||||
// let temp = [...filePlacements];
|
let temp = [...filePlacements];
|
||||||
// if (checked) {
|
if (checked) {
|
||||||
// if (placement === "all") {
|
if (placement === "all") {
|
||||||
// temp[index] = ["all", "mabes", "polda", "international"];
|
temp[index] = ["all", "mabes", "polda", "international"];
|
||||||
// } else {
|
|
||||||
// const now = temp[index];
|
// Update fileCheckedLevels untuk sinkronisasi dengan modal ketika "all" diklik
|
||||||
// now?.push(placement);
|
setFileCheckedLevels((prevLevels) => {
|
||||||
// if (now?.length === 3 && !now?.includes("all")) {
|
const newArray = [...prevLevels];
|
||||||
// now?.push("all");
|
const currentFileLevels = new Set<number>(newArray[index] || new Set());
|
||||||
// }
|
|
||||||
// temp[index] = now;
|
// Checklist semua item di modal
|
||||||
// }
|
listDest.forEach((item: any) => {
|
||||||
// } else {
|
currentFileLevels.add(Number(item.id));
|
||||||
// if (placement === "all") {
|
if (item.subDestination) {
|
||||||
// temp[index] = [];
|
item.subDestination.forEach((sub: any) => {
|
||||||
// } else {
|
currentFileLevels.add(Number(sub.id));
|
||||||
// const now = temp[index].filter((a) => a !== placement);
|
});
|
||||||
// temp[index] = now;
|
}
|
||||||
// if (now?.length === 3 && now?.includes("all")) {
|
});
|
||||||
// const newData = now.filter((b) => b !== "all");
|
|
||||||
// temp[index] = newData;
|
newArray[index] = currentFileLevels;
|
||||||
// }
|
return newArray;
|
||||||
// }
|
});
|
||||||
// }
|
|
||||||
// setFilePlacements(temp);
|
// Update fileUnitSelections untuk checkbox tingkat utama
|
||||||
// };
|
setFileUnitSelections((prevSelections) => {
|
||||||
|
const newSelections = [...prevSelections];
|
||||||
|
const currentSelection = { ...newSelections[index] };
|
||||||
|
|
||||||
|
// Set semua checkbox tingkat utama ke true
|
||||||
|
currentSelection.nasional = true;
|
||||||
|
currentSelection.wilayah = true;
|
||||||
|
currentSelection.international = true;
|
||||||
|
currentSelection.polda = true;
|
||||||
|
currentSelection.polres = true;
|
||||||
|
currentSelection.satker = true;
|
||||||
|
currentSelection.semua = true;
|
||||||
|
|
||||||
|
newSelections[index] = currentSelection;
|
||||||
|
return newSelections;
|
||||||
|
});
|
||||||
|
} else if (placement === "satker") {
|
||||||
|
// Ketika satker di-checklist, HANYA tambahkan satker saja
|
||||||
|
// JANGAN otomatis checklist polres
|
||||||
|
const now = temp[index] || [];
|
||||||
|
if (!now.includes("satker")) {
|
||||||
|
now.push("satker");
|
||||||
|
}
|
||||||
|
temp[index] = now;
|
||||||
|
} else {
|
||||||
|
const now = temp[index] || [];
|
||||||
|
if (!now.includes(placement)) {
|
||||||
|
now.push(placement);
|
||||||
|
}
|
||||||
|
// Hanya auto-checklist "all" jika polda, polres, dan mabes ter-checklist
|
||||||
|
// JANGAN include satker dalam perhitungan auto "all"
|
||||||
|
const nonSatkerItems = now.filter(
|
||||||
|
(item) => item !== "satker" && item !== "all"
|
||||||
|
);
|
||||||
|
if (nonSatkerItems.length === 3 && !now.includes("all")) {
|
||||||
|
now.push("all");
|
||||||
|
}
|
||||||
|
temp[index] = now;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (placement === "all") {
|
||||||
|
temp[index] = [];
|
||||||
|
|
||||||
|
// Update fileCheckedLevels untuk sinkronisasi dengan modal ketika "all" di-unchecklist
|
||||||
|
setFileCheckedLevels((prevLevels) => {
|
||||||
|
const newArray = [...prevLevels];
|
||||||
|
const currentFileLevels = new Set<number>(newArray[index] || new Set());
|
||||||
|
|
||||||
|
// Unchecklist semua item di modal
|
||||||
|
currentFileLevels.clear();
|
||||||
|
|
||||||
|
newArray[index] = currentFileLevels;
|
||||||
|
return newArray;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update fileUnitSelections untuk checkbox tingkat utama
|
||||||
|
setFileUnitSelections((prevSelections) => {
|
||||||
|
const newSelections = [...prevSelections];
|
||||||
|
const currentSelection = { ...newSelections[index] };
|
||||||
|
|
||||||
|
// Set semua checkbox tingkat utama ke false
|
||||||
|
currentSelection.nasional = false;
|
||||||
|
currentSelection.wilayah = false;
|
||||||
|
currentSelection.international = false;
|
||||||
|
currentSelection.polda = false;
|
||||||
|
currentSelection.polres = false;
|
||||||
|
currentSelection.satker = false;
|
||||||
|
currentSelection.semua = false;
|
||||||
|
|
||||||
|
newSelections[index] = currentSelection;
|
||||||
|
return newSelections;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const now = temp[index]?.filter((a) => a !== placement);
|
||||||
|
console.log("now", now);
|
||||||
|
temp[index] = now;
|
||||||
|
// Hapus "all" jika tidak semua item ter-checklist
|
||||||
|
if (now.includes("all")) {
|
||||||
|
const nonSatkerItems = now.filter(
|
||||||
|
(item) => item !== "satker" && item !== "all"
|
||||||
|
);
|
||||||
|
if (nonSatkerItems.length < 3) {
|
||||||
|
const newData = now.filter((b) => b !== "all");
|
||||||
|
temp[index] = newData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setFilePlacements(temp);
|
||||||
|
|
||||||
|
// Update checklist levels di modal berdasarkan placement yang diubah
|
||||||
|
updateModalChecklistLevels(index, placement, checked);
|
||||||
|
};
|
||||||
|
|
||||||
const handleMain = (
|
const handleMain = (
|
||||||
type: string,
|
type: string,
|
||||||
|
|
@ -706,62 +868,7 @@ export default function FormAudioDetail() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const setupPlacement = (
|
|
||||||
index: number,
|
|
||||||
placement: string,
|
|
||||||
checked: boolean
|
|
||||||
) => {
|
|
||||||
let temp = [...filePlacements];
|
|
||||||
if (checked) {
|
|
||||||
if (placement === "all") {
|
|
||||||
temp[index] = ["all", "mabes", "polda", "international"];
|
|
||||||
} else if (placement === "satker") {
|
|
||||||
// Ketika satker di-checklist, HANYA tambahkan satker saja
|
|
||||||
// JANGAN otomatis checklist polres
|
|
||||||
const now = temp[index] || [];
|
|
||||||
if (!now.includes("satker")) {
|
|
||||||
now.push("satker");
|
|
||||||
}
|
|
||||||
temp[index] = now;
|
|
||||||
} else {
|
|
||||||
const now = temp[index] || [];
|
|
||||||
if (!now.includes(placement)) {
|
|
||||||
now.push(placement);
|
|
||||||
}
|
|
||||||
// Hanya auto-checklist "all" jika polda, polres, dan mabes ter-checklist
|
|
||||||
// JANGAN include satker dalam perhitungan auto "all"
|
|
||||||
const nonSatkerItems = now.filter(
|
|
||||||
(item) => item !== "satker" && item !== "all"
|
|
||||||
);
|
|
||||||
if (nonSatkerItems.length === 3 && !now.includes("all")) {
|
|
||||||
now.push("all");
|
|
||||||
}
|
|
||||||
temp[index] = now;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (placement === "all") {
|
|
||||||
temp[index] = [];
|
|
||||||
} else {
|
|
||||||
const now = temp[index]?.filter((a) => a !== placement);
|
|
||||||
console.log("now", now);
|
|
||||||
temp[index] = now;
|
|
||||||
// Hapus "all" jika tidak semua item ter-checklist
|
|
||||||
if (now.includes("all")) {
|
|
||||||
const nonSatkerItems = now.filter(
|
|
||||||
(item) => item !== "satker" && item !== "all"
|
|
||||||
);
|
|
||||||
if (nonSatkerItems.length < 3) {
|
|
||||||
const newData = now.filter((b) => b !== "all");
|
|
||||||
temp[index] = newData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setFilePlacements(temp);
|
|
||||||
|
|
||||||
// Update checklist levels di modal berdasarkan placement yang diubah
|
|
||||||
updateModalChecklistLevels(index, placement, checked);
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateModalChecklistLevels = (
|
const updateModalChecklistLevels = (
|
||||||
fileIndex: number,
|
fileIndex: number,
|
||||||
|
|
@ -992,6 +1099,15 @@ export default function FormAudioDetail() {
|
||||||
currentSelection.polres = checkedPolresCount > 0;
|
currentSelection.polres = checkedPolresCount > 0;
|
||||||
currentSelection.satker = Boolean(isSatkerChecked);
|
currentSelection.satker = Boolean(isSatkerChecked);
|
||||||
|
|
||||||
|
// Update checkbox "semua" berdasarkan semua checkbox yang aktif
|
||||||
|
currentSelection.semua =
|
||||||
|
currentSelection.nasional &&
|
||||||
|
currentSelection.wilayah &&
|
||||||
|
currentSelection.international &&
|
||||||
|
currentSelection.polda &&
|
||||||
|
currentSelection.polres &&
|
||||||
|
currentSelection.satker;
|
||||||
|
|
||||||
newSelections[fileIndex] = currentSelection;
|
newSelections[fileIndex] = currentSelection;
|
||||||
return newSelections;
|
return newSelections;
|
||||||
});
|
});
|
||||||
|
|
@ -1303,6 +1419,13 @@ export default function FormAudioDetail() {
|
||||||
<p className="text-sm text-gray-600 break-all">
|
<p className="text-sm text-gray-600 break-all">
|
||||||
{file.fileName}
|
{file.fileName}
|
||||||
</p>
|
</p>
|
||||||
|
{isUserMabesApprover ? (
|
||||||
|
""
|
||||||
|
) : (
|
||||||
|
<p className="status text-success text-sm mb-0">
|
||||||
|
Selesai
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
|
@ -1320,84 +1443,36 @@ export default function FormAudioDetail() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Section Pengaturan Distribusi */}
|
{/* Section Pengaturan Distribusi */}
|
||||||
<div className="bg-white rounded-md p-4 border">
|
{isUserMabesApprover ? (
|
||||||
<h5 className="font-medium text-gray-900 mb-4 flex items-center gap-2">
|
<div className="bg-white rounded-md p-4 border">
|
||||||
<Icon
|
<h5 className="font-medium text-gray-900 mb-4 flex items-center gap-2">
|
||||||
icon="material-symbols:settings-outline"
|
<Icon
|
||||||
width={18}
|
icon="material-symbols:settings-outline"
|
||||||
height={18}
|
width={18}
|
||||||
/>
|
height={18}
|
||||||
Pengaturan Distribusi
|
/>
|
||||||
</h5>
|
Pengaturan Distribusi
|
||||||
|
</h5>
|
||||||
|
|
||||||
{/* Checkbox Tingkat Utama */}
|
{/* Checkbox Tingkat Utama */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-gray-700 mb-3">
|
<p className="text-sm font-medium text-gray-700 mb-3">
|
||||||
Tingkat Distribusi:
|
Tingkat Distribusi:
|
||||||
</p>
|
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
|
||||||
{[
|
|
||||||
{ key: "semua", label: "Semua" },
|
|
||||||
{ key: "nasional", label: "Nasional" },
|
|
||||||
{ key: "wilayah", label: "Wilayah" },
|
|
||||||
{
|
|
||||||
key: "international",
|
|
||||||
label: "Internasional",
|
|
||||||
},
|
|
||||||
].map((item, idx) => (
|
|
||||||
<div
|
|
||||||
key={item.key}
|
|
||||||
className="flex items-center gap-2 p-2 border border-gray-200 rounded-md hover:bg-gray-50"
|
|
||||||
>
|
|
||||||
<Checkbox
|
|
||||||
id={`${item.key}-${index}`}
|
|
||||||
checked={
|
|
||||||
fileUnitSelections[index]?.[
|
|
||||||
item.key as keyof typeof unitSelection
|
|
||||||
] || false
|
|
||||||
}
|
|
||||||
onCheckedChange={(value) => {
|
|
||||||
handleFileUnitChange(
|
|
||||||
index,
|
|
||||||
item.key as keyof typeof unitSelection,
|
|
||||||
value as boolean
|
|
||||||
);
|
|
||||||
setupPlacement(
|
|
||||||
index,
|
|
||||||
item.key,
|
|
||||||
Boolean(value)
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Label
|
|
||||||
htmlFor={`${item.key}-${index}`}
|
|
||||||
className="text-sm font-medium cursor-pointer"
|
|
||||||
>
|
|
||||||
{item.label}
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Detail Wilayah */}
|
|
||||||
{fileUnitSelections[index]?.wilayah && (
|
|
||||||
<div className="border-t border-gray-200 pt-2">
|
|
||||||
<p className="text-sm font-medium text-gray-700 mb-2">
|
|
||||||
Detail Wilayah:
|
|
||||||
</p>
|
</p>
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||||
{/* Checkbox Sub-kategori dengan tombol Kustom sejajar */}
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
|
||||||
{[
|
{[
|
||||||
{ key: "polda", label: "POLDA" },
|
{ key: "semua", label: "Semua" },
|
||||||
{ key: "polres", label: "POLRES" },
|
{ key: "nasional", label: "Nasional" },
|
||||||
{ key: "satker", label: "SATKER" },
|
{ key: "wilayah", label: "Wilayah" },
|
||||||
|
{
|
||||||
|
key: "international",
|
||||||
|
label: "Internasional",
|
||||||
|
},
|
||||||
].map((item, idx) => (
|
].map((item, idx) => (
|
||||||
<div
|
<div
|
||||||
key={item.key}
|
key={item.key}
|
||||||
className="flex items-center gap-2 p-3 border border-gray-200 rounded-md hover:bg-gray-50"
|
className="flex items-center gap-2 p-2 border border-gray-200 rounded-md hover:bg-gray-50"
|
||||||
>
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id={`${item.key}-${index}`}
|
id={`${item.key}-${index}`}
|
||||||
|
|
@ -1427,199 +1502,260 @@ export default function FormAudioDetail() {
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Tombol Kustom sejajar dengan checkbox */}
|
{/* Detail Wilayah */}
|
||||||
<div className="flex items-center justify-center p-3">
|
{fileUnitSelections[index]?.wilayah && (
|
||||||
<Dialog>
|
<div className="border-t border-gray-200 pt-2">
|
||||||
<DialogTrigger asChild>
|
<p className="text-sm font-medium text-gray-700 mb-2">
|
||||||
<Button
|
Detail Wilayah:
|
||||||
variant="outline"
|
</p>
|
||||||
size="sm"
|
|
||||||
className="gap-2"
|
{/* Checkbox Sub-kategori dengan tombol Kustom sejajar */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||||
|
{[
|
||||||
|
{ key: "polda", label: "POLDA" },
|
||||||
|
{ key: "polres", label: "POLRES" },
|
||||||
|
{ key: "satker", label: "SATKER" },
|
||||||
|
].map((item, idx) => (
|
||||||
|
<div
|
||||||
|
key={item.key}
|
||||||
|
className="flex items-center gap-2 p-3 border border-gray-200 rounded-md hover:bg-gray-50"
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
id={`${item.key}-${index}`}
|
||||||
|
checked={
|
||||||
|
fileUnitSelections[index]?.[
|
||||||
|
item.key as keyof typeof unitSelection
|
||||||
|
] || false
|
||||||
|
}
|
||||||
|
onCheckedChange={(value) => {
|
||||||
|
handleFileUnitChange(
|
||||||
|
index,
|
||||||
|
item.key as keyof typeof unitSelection,
|
||||||
|
value as boolean
|
||||||
|
);
|
||||||
|
setupPlacement(
|
||||||
|
index,
|
||||||
|
item.key,
|
||||||
|
Boolean(value)
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Label
|
||||||
|
htmlFor={`${item.key}-${index}`}
|
||||||
|
className="text-sm font-medium cursor-pointer"
|
||||||
>
|
>
|
||||||
<Icon
|
{item.label}
|
||||||
icon="material-symbols:tune"
|
</Label>
|
||||||
width={16}
|
</div>
|
||||||
height={16}
|
))}
|
||||||
/>
|
|
||||||
{t("custom", {
|
|
||||||
defaultValue: "Kustom",
|
|
||||||
})}
|
|
||||||
</Button>
|
|
||||||
</DialogTrigger>
|
|
||||||
<DialogContent className="max-w-[95vw] lg:max-w-[1400px] max-h-[90vh]">
|
|
||||||
<DialogHeader className="border-b border-gray-200 pb-4">
|
|
||||||
<DialogTitle className="text-lg font-semibold">
|
|
||||||
Daftar Wilayah POLDA dan POLRES
|
|
||||||
</DialogTitle>
|
|
||||||
</DialogHeader>
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4 max-h-[70vh] overflow-y-auto p-1">
|
|
||||||
{listDest.map((polda: any) => (
|
|
||||||
<div
|
|
||||||
key={polda.id}
|
|
||||||
className="border border-gray-200 rounded-lg p-2 bg-white hover:shadow-sm transition-shadow"
|
|
||||||
>
|
|
||||||
{/* Header POLDA */}
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<Label className="flex items-center gap-3 flex-1 cursor-pointer">
|
|
||||||
<Checkbox
|
|
||||||
checked={
|
|
||||||
fileCheckedLevels[
|
|
||||||
index
|
|
||||||
]?.has(
|
|
||||||
Number(polda.id)
|
|
||||||
) || false
|
|
||||||
}
|
|
||||||
onCheckedChange={() =>
|
|
||||||
handleFileCheckboxChangePlacement(
|
|
||||||
index,
|
|
||||||
Number(polda.id)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<span className="font-semibold text-gray-900 text-sm">
|
|
||||||
{polda.name}
|
|
||||||
</span>
|
|
||||||
</Label>
|
|
||||||
{polda.subDestination && (
|
|
||||||
<button
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
toggleExpand(polda.id);
|
|
||||||
}}
|
|
||||||
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
icon={
|
|
||||||
expandedPolda[
|
|
||||||
polda.id
|
|
||||||
]
|
|
||||||
? "mdi:chevron-up"
|
|
||||||
: "mdi:chevron-down"
|
|
||||||
}
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Sub-items */}
|
{/* Tombol Kustom sejajar dengan checkbox */}
|
||||||
{polda.subDestination &&
|
<div className="flex items-center justify-center p-3">
|
||||||
expandedPolda[polda.id] && (
|
<Dialog>
|
||||||
<div className="max-h-[200px] overflow-y-auto border-t border-gray-100 pt-2">
|
<DialogTrigger asChild>
|
||||||
{/* Tombol Pilih Semua untuk sub-items */}
|
<Button
|
||||||
<div className="mb-2 flex justify-start">
|
variant="outline"
|
||||||
{(() => {
|
size="sm"
|
||||||
const allSubItemsChecked =
|
className="gap-2"
|
||||||
polda.subDestination?.every(
|
>
|
||||||
(sub: any) =>
|
<Icon
|
||||||
fileCheckedLevels[
|
icon="material-symbols:tune"
|
||||||
index
|
width={16}
|
||||||
]?.has(
|
height={16}
|
||||||
Number(sub.id)
|
/>
|
||||||
)
|
{t("custom", {
|
||||||
);
|
defaultValue: "Kustom",
|
||||||
return (
|
})}
|
||||||
<Button
|
</Button>
|
||||||
size="sm"
|
</DialogTrigger>
|
||||||
variant="outline"
|
<DialogContent className="max-w-[95vw] lg:max-w-[1400px] max-h-[90vh]">
|
||||||
className="text-xs h-6 px-2"
|
<DialogHeader className="border-b border-gray-200 pb-4">
|
||||||
onClick={() =>
|
<DialogTitle className="text-lg font-semibold">
|
||||||
handleSelectAllSubItems(
|
Daftar Wilayah POLDA dan POLRES
|
||||||
index,
|
</DialogTitle>
|
||||||
polda
|
</DialogHeader>
|
||||||
)
|
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4 max-h-[70vh] overflow-y-auto p-1">
|
||||||
}
|
{listDest.map((polda: any) => (
|
||||||
>
|
<div
|
||||||
{allSubItemsChecked ? (
|
key={polda.id}
|
||||||
<>
|
className="border border-gray-200 rounded-lg p-2 bg-white hover:shadow-sm transition-shadow"
|
||||||
<Icon
|
>
|
||||||
icon="material-symbols:check-indeterminate-small"
|
{/* Header POLDA */}
|
||||||
width={12}
|
<div className="flex items-center justify-between">
|
||||||
height={12}
|
<Label className="flex items-center gap-3 flex-1 cursor-pointer">
|
||||||
className="mr-1"
|
<Checkbox
|
||||||
/>
|
checked={
|
||||||
Batal Semua
|
fileCheckedLevels[
|
||||||
</>
|
index
|
||||||
) : (
|
]?.has(
|
||||||
<>
|
Number(polda.id)
|
||||||
<Icon
|
) || false
|
||||||
icon="material-symbols:check-all"
|
}
|
||||||
width={12}
|
onCheckedChange={() =>
|
||||||
height={12}
|
handleFileCheckboxChangePlacement(
|
||||||
className="mr-1"
|
index,
|
||||||
/>
|
Number(polda.id)
|
||||||
Pilih Semua
|
)
|
||||||
</>
|
}
|
||||||
)}
|
/>
|
||||||
</Button>
|
<span className="font-semibold text-gray-900 text-sm">
|
||||||
|
{polda.name}
|
||||||
|
</span>
|
||||||
|
</Label>
|
||||||
|
{polda.subDestination && (
|
||||||
|
<button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
toggleExpand(
|
||||||
|
polda.id
|
||||||
);
|
);
|
||||||
})()}
|
}}
|
||||||
</div>
|
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
|
||||||
<div className="space-y-1">
|
>
|
||||||
{polda.subDestination.map(
|
<Icon
|
||||||
(sub: any) => (
|
icon={
|
||||||
<Label
|
expandedPolda[
|
||||||
key={sub.id}
|
polda.id
|
||||||
className="flex items-center gap-2 p-2 rounded-md hover:bg-gray-50 transition-colors cursor-pointer text-xs"
|
]
|
||||||
>
|
? "mdi:chevron-up"
|
||||||
<Checkbox
|
: "mdi:chevron-down"
|
||||||
checked={
|
}
|
||||||
|
width={16}
|
||||||
|
height={16}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sub-items */}
|
||||||
|
{polda.subDestination &&
|
||||||
|
expandedPolda[polda.id] && (
|
||||||
|
<div className="max-h-[200px] overflow-y-auto border-t border-gray-100 pt-2">
|
||||||
|
{/* Tombol Pilih Semua untuk sub-items */}
|
||||||
|
<div className="mb-2 flex justify-start">
|
||||||
|
{(() => {
|
||||||
|
const allSubItemsChecked =
|
||||||
|
polda.subDestination?.every(
|
||||||
|
(sub: any) =>
|
||||||
fileCheckedLevels[
|
fileCheckedLevels[
|
||||||
index
|
index
|
||||||
]?.has(
|
]?.has(
|
||||||
Number(
|
Number(
|
||||||
sub.id
|
sub.id
|
||||||
)
|
)
|
||||||
) || false
|
)
|
||||||
}
|
);
|
||||||
onCheckedChange={() =>
|
return (
|
||||||
handleFileCheckboxChangePlacement(
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
className="text-xs h-6 px-2"
|
||||||
|
onClick={() =>
|
||||||
|
handleSelectAllSubItems(
|
||||||
index,
|
index,
|
||||||
Number(
|
polda
|
||||||
sub.id
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
>
|
||||||
<span className="text-gray-700">
|
{allSubItemsChecked ? (
|
||||||
{sub.name}
|
<>
|
||||||
</span>
|
<Icon
|
||||||
</Label>
|
icon="material-symbols:check-indeterminate-small"
|
||||||
)
|
width={12}
|
||||||
)}
|
height={
|
||||||
|
12
|
||||||
|
}
|
||||||
|
className="mr-1"
|
||||||
|
/>
|
||||||
|
Batal Semua
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Icon
|
||||||
|
icon="material-symbols:check-all"
|
||||||
|
width={12}
|
||||||
|
height={
|
||||||
|
12
|
||||||
|
}
|
||||||
|
className="mr-1"
|
||||||
|
/>
|
||||||
|
Pilih Semua
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
{polda.subDestination.map(
|
||||||
|
(sub: any) => (
|
||||||
|
<Label
|
||||||
|
key={sub.id}
|
||||||
|
className="flex items-center gap-2 p-2 rounded-md hover:bg-gray-50 transition-colors cursor-pointer text-xs"
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
checked={
|
||||||
|
fileCheckedLevels[
|
||||||
|
index
|
||||||
|
]?.has(
|
||||||
|
Number(
|
||||||
|
sub.id
|
||||||
|
)
|
||||||
|
) || false
|
||||||
|
}
|
||||||
|
onCheckedChange={() =>
|
||||||
|
handleFileCheckboxChangePlacement(
|
||||||
|
index,
|
||||||
|
Number(
|
||||||
|
sub.id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<span className="text-gray-700">
|
||||||
|
{sub.name}
|
||||||
|
</span>
|
||||||
|
</Label>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
))}
|
||||||
))}
|
</div>
|
||||||
</div>
|
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
|
||||||
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
|
<DialogClose asChild>
|
||||||
<DialogClose asChild>
|
<Button variant="outline">
|
||||||
<Button variant="outline">
|
{t("cancel", {
|
||||||
{t("cancel", {
|
defaultValue: "Batal",
|
||||||
defaultValue: "Batal",
|
})}
|
||||||
})}
|
</Button>
|
||||||
</Button>
|
</DialogClose>
|
||||||
</DialogClose>
|
<DialogClose asChild>
|
||||||
<DialogClose asChild>
|
<Button>
|
||||||
<Button>
|
{/* {t("save", {
|
||||||
{t("save", {
|
defaultValue: "Simpan",
|
||||||
defaultValue: "Simpan",
|
})} */}
|
||||||
})}
|
Simpan
|
||||||
</Button>
|
</Button>
|
||||||
</DialogClose>
|
</DialogClose>
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -189,6 +189,12 @@ export default function FormTeksDetail() {
|
||||||
}>
|
}>
|
||||||
>([]);
|
>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (Number(userLevelId) === 216 && Number(roleId) === 3) {
|
||||||
|
setIsUserMabesApprover(true);
|
||||||
|
}
|
||||||
|
}, [userLevelId, roleId]);
|
||||||
|
|
||||||
// Fungsi untuk mengupdate state individual file
|
// Fungsi untuk mengupdate state individual file
|
||||||
const handleFileUnitChange = (
|
const handleFileUnitChange = (
|
||||||
fileIndex: number,
|
fileIndex: number,
|
||||||
|
|
@ -208,6 +214,30 @@ export default function FormTeksDetail() {
|
||||||
currentSelection.polda = value;
|
currentSelection.polda = value;
|
||||||
currentSelection.polres = value;
|
currentSelection.polres = value;
|
||||||
currentSelection.satker = value;
|
currentSelection.satker = value;
|
||||||
|
|
||||||
|
// Update fileCheckedLevels untuk sinkronisasi dengan modal
|
||||||
|
setFileCheckedLevels((prevLevels) => {
|
||||||
|
const newArray = [...prevLevels];
|
||||||
|
const currentFileLevels = new Set<number>(newArray[fileIndex] || new Set());
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
// Checklist semua item di modal
|
||||||
|
listDest.forEach((item: any) => {
|
||||||
|
currentFileLevels.add(Number(item.id));
|
||||||
|
if (item.subDestination) {
|
||||||
|
item.subDestination.forEach((sub: any) => {
|
||||||
|
currentFileLevels.add(Number(sub.id));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Unchecklist semua item di modal
|
||||||
|
currentFileLevels.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
newArray[fileIndex] = currentFileLevels;
|
||||||
|
return newArray;
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// Validasi khusus untuk POLRES - harus ada POLDA yang ter-checklist
|
// Validasi khusus untuk POLRES - harus ada POLDA yang ter-checklist
|
||||||
if (key === "polres" && value) {
|
if (key === "polres" && value) {
|
||||||
|
|
@ -440,10 +470,28 @@ export default function FormTeksDetail() {
|
||||||
|
|
||||||
const setupPlacementCheck = (length: number) => {
|
const setupPlacementCheck = (length: number) => {
|
||||||
const temp = [];
|
const temp = [];
|
||||||
|
const unitSelections = [];
|
||||||
|
const checkedLevelsArray = [];
|
||||||
|
|
||||||
for (let i = 0; i < length; i++) {
|
for (let i = 0; i < length; i++) {
|
||||||
temp.push([]);
|
temp.push([]);
|
||||||
|
// Inisialisasi state untuk setiap file
|
||||||
|
unitSelections.push({
|
||||||
|
semua: false,
|
||||||
|
nasional: false,
|
||||||
|
wilayah: false,
|
||||||
|
international: false,
|
||||||
|
polda: false,
|
||||||
|
polres: false,
|
||||||
|
satker: false,
|
||||||
|
});
|
||||||
|
// Inisialisasi checkedLevels untuk setiap file
|
||||||
|
checkedLevelsArray.push(new Set<number>());
|
||||||
}
|
}
|
||||||
|
|
||||||
setFilePlacements(temp);
|
setFilePlacements(temp);
|
||||||
|
setFileUnitSelections(unitSelections);
|
||||||
|
setFileCheckedLevels(checkedLevelsArray);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -460,6 +508,7 @@ export default function FormTeksDetail() {
|
||||||
names: details?.files[0]?.fileName,
|
names: details?.files[0]?.fileName,
|
||||||
format: details?.files[0]?.format,
|
format: details?.files[0]?.format,
|
||||||
});
|
});
|
||||||
|
setupPlacementCheck(details?.files?.length);
|
||||||
|
|
||||||
if (details?.assignedToLevel) {
|
if (details?.assignedToLevel) {
|
||||||
const levels = new Set(
|
const levels = new Set(
|
||||||
|
|
@ -530,12 +579,27 @@ export default function FormTeksDetail() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPlacement = () => {
|
const getPlacement = () => {
|
||||||
// console.log("getPlaa", filePlacements);
|
|
||||||
const temp = [];
|
const temp = [];
|
||||||
for (let i = 0; i < filePlacements?.length; i++) {
|
for (let i = 0; i < filePlacements?.length; i++) {
|
||||||
if (filePlacements[i]?.length !== 0) {
|
if (filePlacements[i]?.length !== 0) {
|
||||||
const now = filePlacements[i]?.filter((a) => a !== "all");
|
// const now = filePlacements[i]?.filter((a) => a !== "all");
|
||||||
const data = { mediaFileId: files[i]?.id, placements: now?.join(",") };
|
// const data = { mediaFileId: files[i]?.id, placements: now?.join(",") };
|
||||||
|
const now = filePlacements[i];
|
||||||
|
let nowArr = now?.join(",")?.replaceAll("wilayah", "polda");
|
||||||
|
nowArr = nowArr?.replaceAll("nasional", "mabes");
|
||||||
|
nowArr = nowArr?.replaceAll("semua", "all");
|
||||||
|
|
||||||
|
// Dapatkan checked levels untuk file ini
|
||||||
|
const currentFileCheckedLevels = fileCheckedLevels[i]
|
||||||
|
? Array.from(fileCheckedLevels[i])
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
mediaFileId: files[i]?.id,
|
||||||
|
placements: nowArr,
|
||||||
|
customLocationPlacements: currentFileCheckedLevels.join(","),
|
||||||
|
};
|
||||||
|
|
||||||
temp.push(data);
|
temp.push(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -543,12 +607,19 @@ export default function FormTeksDetail() {
|
||||||
};
|
};
|
||||||
|
|
||||||
async function save() {
|
async function save() {
|
||||||
|
// Gabungkan semua checkedLevels dari semua file
|
||||||
|
const allCheckedLevels = new Set<number>();
|
||||||
|
fileCheckedLevels.forEach((fileLevels) => {
|
||||||
|
fileLevels.forEach((levelId) => {
|
||||||
|
allCheckedLevels.add(levelId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
mediaUploadId: id,
|
mediaUploadId: id,
|
||||||
statusId: status,
|
statusId: status,
|
||||||
message: description,
|
message: description,
|
||||||
files: isUserMabesApprover ? getPlacement() : [],
|
files: isUserMabesApprover ? getPlacement() : [],
|
||||||
customLocationPlacement: Array.from(checkedLevels).join(","),
|
|
||||||
};
|
};
|
||||||
setModalOpen(false);
|
setModalOpen(false);
|
||||||
|
|
||||||
|
|
@ -653,6 +724,43 @@ export default function FormTeksDetail() {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
if (placement === "all") {
|
if (placement === "all") {
|
||||||
temp[index] = ["all", "mabes", "polda", "international"];
|
temp[index] = ["all", "mabes", "polda", "international"];
|
||||||
|
|
||||||
|
// Update fileCheckedLevels untuk sinkronisasi dengan modal ketika "all" diklik
|
||||||
|
setFileCheckedLevels((prevLevels) => {
|
||||||
|
const newArray = [...prevLevels];
|
||||||
|
const currentFileLevels = new Set<number>(newArray[index] || new Set());
|
||||||
|
|
||||||
|
// Checklist semua item di modal
|
||||||
|
listDest.forEach((item: any) => {
|
||||||
|
currentFileLevels.add(Number(item.id));
|
||||||
|
if (item.subDestination) {
|
||||||
|
item.subDestination.forEach((sub: any) => {
|
||||||
|
currentFileLevels.add(Number(sub.id));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
newArray[index] = currentFileLevels;
|
||||||
|
return newArray;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update fileUnitSelections untuk checkbox tingkat utama
|
||||||
|
setFileUnitSelections((prevSelections) => {
|
||||||
|
const newSelections = [...prevSelections];
|
||||||
|
const currentSelection = { ...newSelections[index] };
|
||||||
|
|
||||||
|
// Set semua checkbox tingkat utama ke true
|
||||||
|
currentSelection.nasional = true;
|
||||||
|
currentSelection.wilayah = true;
|
||||||
|
currentSelection.international = true;
|
||||||
|
currentSelection.polda = true;
|
||||||
|
currentSelection.polres = true;
|
||||||
|
currentSelection.satker = true;
|
||||||
|
currentSelection.semua = true;
|
||||||
|
|
||||||
|
newSelections[index] = currentSelection;
|
||||||
|
return newSelections;
|
||||||
|
});
|
||||||
} else if (placement === "satker") {
|
} else if (placement === "satker") {
|
||||||
// Ketika satker di-checklist, HANYA tambahkan satker saja
|
// Ketika satker di-checklist, HANYA tambahkan satker saja
|
||||||
// JANGAN otomatis checklist polres
|
// JANGAN otomatis checklist polres
|
||||||
|
|
@ -679,6 +787,36 @@ export default function FormTeksDetail() {
|
||||||
} else {
|
} else {
|
||||||
if (placement === "all") {
|
if (placement === "all") {
|
||||||
temp[index] = [];
|
temp[index] = [];
|
||||||
|
|
||||||
|
// Update fileCheckedLevels untuk sinkronisasi dengan modal ketika "all" di-unchecklist
|
||||||
|
setFileCheckedLevels((prevLevels) => {
|
||||||
|
const newArray = [...prevLevels];
|
||||||
|
const currentFileLevels = new Set<number>(newArray[index] || new Set());
|
||||||
|
|
||||||
|
// Unchecklist semua item di modal
|
||||||
|
currentFileLevels.clear();
|
||||||
|
|
||||||
|
newArray[index] = currentFileLevels;
|
||||||
|
return newArray;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update fileUnitSelections untuk checkbox tingkat utama
|
||||||
|
setFileUnitSelections((prevSelections) => {
|
||||||
|
const newSelections = [...prevSelections];
|
||||||
|
const currentSelection = { ...newSelections[index] };
|
||||||
|
|
||||||
|
// Set semua checkbox tingkat utama ke false
|
||||||
|
currentSelection.nasional = false;
|
||||||
|
currentSelection.wilayah = false;
|
||||||
|
currentSelection.international = false;
|
||||||
|
currentSelection.polda = false;
|
||||||
|
currentSelection.polres = false;
|
||||||
|
currentSelection.satker = false;
|
||||||
|
currentSelection.semua = false;
|
||||||
|
|
||||||
|
newSelections[index] = currentSelection;
|
||||||
|
return newSelections;
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
const now = temp[index]?.filter((a) => a !== placement);
|
const now = temp[index]?.filter((a) => a !== placement);
|
||||||
console.log("now", now);
|
console.log("now", now);
|
||||||
|
|
@ -930,6 +1068,15 @@ export default function FormTeksDetail() {
|
||||||
currentSelection.polres = checkedPolresCount > 0;
|
currentSelection.polres = checkedPolresCount > 0;
|
||||||
currentSelection.satker = Boolean(isSatkerChecked);
|
currentSelection.satker = Boolean(isSatkerChecked);
|
||||||
|
|
||||||
|
// Update checkbox "semua" berdasarkan semua checkbox yang aktif
|
||||||
|
currentSelection.semua =
|
||||||
|
currentSelection.nasional &&
|
||||||
|
currentSelection.wilayah &&
|
||||||
|
currentSelection.international &&
|
||||||
|
currentSelection.polda &&
|
||||||
|
currentSelection.polres &&
|
||||||
|
currentSelection.satker;
|
||||||
|
|
||||||
newSelections[fileIndex] = currentSelection;
|
newSelections[fileIndex] = currentSelection;
|
||||||
return newSelections;
|
return newSelections;
|
||||||
});
|
});
|
||||||
|
|
@ -1264,6 +1411,13 @@ export default function FormTeksDetail() {
|
||||||
<p className="text-sm text-gray-600 break-all">
|
<p className="text-sm text-gray-600 break-all">
|
||||||
{file.fileName}
|
{file.fileName}
|
||||||
</p>
|
</p>
|
||||||
|
{isUserMabesApprover ? (
|
||||||
|
""
|
||||||
|
) : (
|
||||||
|
<p className="status text-success text-sm mb-0">
|
||||||
|
Selesai
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
|
@ -1281,84 +1435,36 @@ export default function FormTeksDetail() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Section Pengaturan Distribusi */}
|
{/* Section Pengaturan Distribusi */}
|
||||||
<div className="bg-white rounded-md p-4 border">
|
{isUserMabesApprover ? (
|
||||||
<h5 className="font-medium text-gray-900 mb-4 flex items-center gap-2">
|
<div className="bg-white rounded-md p-4 border">
|
||||||
<Icon
|
<h5 className="font-medium text-gray-900 mb-4 flex items-center gap-2">
|
||||||
icon="material-symbols:settings-outline"
|
<Icon
|
||||||
width={18}
|
icon="material-symbols:settings-outline"
|
||||||
height={18}
|
width={18}
|
||||||
/>
|
height={18}
|
||||||
Pengaturan Distribusi
|
/>
|
||||||
</h5>
|
Pengaturan Distribusi
|
||||||
|
</h5>
|
||||||
|
|
||||||
{/* Checkbox Tingkat Utama */}
|
{/* Checkbox Tingkat Utama */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-gray-700 mb-3">
|
<p className="text-sm font-medium text-gray-700 mb-3">
|
||||||
Tingkat Distribusi:
|
Tingkat Distribusi:
|
||||||
</p>
|
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
|
||||||
{[
|
|
||||||
{ key: "semua", label: "Semua" },
|
|
||||||
{ key: "nasional", label: "Nasional" },
|
|
||||||
{ key: "wilayah", label: "Wilayah" },
|
|
||||||
{
|
|
||||||
key: "international",
|
|
||||||
label: "Internasional",
|
|
||||||
},
|
|
||||||
].map((item, idx) => (
|
|
||||||
<div
|
|
||||||
key={item.key}
|
|
||||||
className="flex items-center gap-2 p-2 border border-gray-200 rounded-md hover:bg-gray-50"
|
|
||||||
>
|
|
||||||
<Checkbox
|
|
||||||
id={`${item.key}-${index}`}
|
|
||||||
checked={
|
|
||||||
fileUnitSelections[index]?.[
|
|
||||||
item.key as keyof typeof unitSelection
|
|
||||||
] || false
|
|
||||||
}
|
|
||||||
onCheckedChange={(value) => {
|
|
||||||
handleFileUnitChange(
|
|
||||||
index,
|
|
||||||
item.key as keyof typeof unitSelection,
|
|
||||||
value as boolean
|
|
||||||
);
|
|
||||||
setupPlacement(
|
|
||||||
index,
|
|
||||||
item.key,
|
|
||||||
Boolean(value)
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Label
|
|
||||||
htmlFor={`${item.key}-${index}`}
|
|
||||||
className="text-sm font-medium cursor-pointer"
|
|
||||||
>
|
|
||||||
{item.label}
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Detail Wilayah */}
|
|
||||||
{fileUnitSelections[index]?.wilayah && (
|
|
||||||
<div className="border-t border-gray-200 pt-2">
|
|
||||||
<p className="text-sm font-medium text-gray-700 mb-2">
|
|
||||||
Detail Wilayah:
|
|
||||||
</p>
|
</p>
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||||
{/* Checkbox Sub-kategori dengan tombol Kustom sejajar */}
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
|
||||||
{[
|
{[
|
||||||
{ key: "polda", label: "POLDA" },
|
{ key: "semua", label: "Semua" },
|
||||||
{ key: "polres", label: "POLRES" },
|
{ key: "nasional", label: "Nasional" },
|
||||||
{ key: "satker", label: "SATKER" },
|
{ key: "wilayah", label: "Wilayah" },
|
||||||
|
{
|
||||||
|
key: "international",
|
||||||
|
label: "Internasional",
|
||||||
|
},
|
||||||
].map((item, idx) => (
|
].map((item, idx) => (
|
||||||
<div
|
<div
|
||||||
key={item.key}
|
key={item.key}
|
||||||
className="flex items-center gap-2 p-3 border border-gray-200 rounded-md hover:bg-gray-50"
|
className="flex items-center gap-2 p-2 border border-gray-200 rounded-md hover:bg-gray-50"
|
||||||
>
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id={`${item.key}-${index}`}
|
id={`${item.key}-${index}`}
|
||||||
|
|
@ -1388,199 +1494,260 @@ export default function FormTeksDetail() {
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Tombol Kustom sejajar dengan checkbox */}
|
{/* Detail Wilayah */}
|
||||||
<div className="flex items-center justify-center p-3">
|
{fileUnitSelections[index]?.wilayah && (
|
||||||
<Dialog>
|
<div className="border-t border-gray-200 pt-2">
|
||||||
<DialogTrigger asChild>
|
<p className="text-sm font-medium text-gray-700 mb-2">
|
||||||
<Button
|
Detail Wilayah:
|
||||||
variant="outline"
|
</p>
|
||||||
size="sm"
|
|
||||||
className="gap-2"
|
{/* Checkbox Sub-kategori dengan tombol Kustom sejajar */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||||
|
{[
|
||||||
|
{ key: "polda", label: "POLDA" },
|
||||||
|
{ key: "polres", label: "POLRES" },
|
||||||
|
{ key: "satker", label: "SATKER" },
|
||||||
|
].map((item, idx) => (
|
||||||
|
<div
|
||||||
|
key={item.key}
|
||||||
|
className="flex items-center gap-2 p-3 border border-gray-200 rounded-md hover:bg-gray-50"
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
id={`${item.key}-${index}`}
|
||||||
|
checked={
|
||||||
|
fileUnitSelections[index]?.[
|
||||||
|
item.key as keyof typeof unitSelection
|
||||||
|
] || false
|
||||||
|
}
|
||||||
|
onCheckedChange={(value) => {
|
||||||
|
handleFileUnitChange(
|
||||||
|
index,
|
||||||
|
item.key as keyof typeof unitSelection,
|
||||||
|
value as boolean
|
||||||
|
);
|
||||||
|
setupPlacement(
|
||||||
|
index,
|
||||||
|
item.key,
|
||||||
|
Boolean(value)
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Label
|
||||||
|
htmlFor={`${item.key}-${index}`}
|
||||||
|
className="text-sm font-medium cursor-pointer"
|
||||||
>
|
>
|
||||||
<Icon
|
{item.label}
|
||||||
icon="material-symbols:tune"
|
</Label>
|
||||||
width={16}
|
</div>
|
||||||
height={16}
|
))}
|
||||||
/>
|
|
||||||
{t("custom", {
|
|
||||||
defaultValue: "Kustom",
|
|
||||||
})}
|
|
||||||
</Button>
|
|
||||||
</DialogTrigger>
|
|
||||||
<DialogContent className="max-w-[95vw] lg:max-w-[1400px] max-h-[90vh]">
|
|
||||||
<DialogHeader className="border-b border-gray-200 pb-4">
|
|
||||||
<DialogTitle className="text-lg font-semibold">
|
|
||||||
Daftar Wilayah POLDA dan POLRES
|
|
||||||
</DialogTitle>
|
|
||||||
</DialogHeader>
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4 max-h-[70vh] overflow-y-auto p-1">
|
|
||||||
{listDest.map((polda: any) => (
|
|
||||||
<div
|
|
||||||
key={polda.id}
|
|
||||||
className="border border-gray-200 rounded-lg p-2 bg-white hover:shadow-sm transition-shadow"
|
|
||||||
>
|
|
||||||
{/* Header POLDA */}
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<Label className="flex items-center gap-3 flex-1 cursor-pointer">
|
|
||||||
<Checkbox
|
|
||||||
checked={
|
|
||||||
fileCheckedLevels[
|
|
||||||
index
|
|
||||||
]?.has(
|
|
||||||
Number(polda.id)
|
|
||||||
) || false
|
|
||||||
}
|
|
||||||
onCheckedChange={() =>
|
|
||||||
handleFileCheckboxChangePlacement(
|
|
||||||
index,
|
|
||||||
Number(polda.id)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<span className="font-semibold text-gray-900 text-sm">
|
|
||||||
{polda.name}
|
|
||||||
</span>
|
|
||||||
</Label>
|
|
||||||
{polda.subDestination && (
|
|
||||||
<button
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
toggleExpand(polda.id);
|
|
||||||
}}
|
|
||||||
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
icon={
|
|
||||||
expandedPolda[
|
|
||||||
polda.id
|
|
||||||
]
|
|
||||||
? "mdi:chevron-up"
|
|
||||||
: "mdi:chevron-down"
|
|
||||||
}
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Sub-items */}
|
{/* Tombol Kustom sejajar dengan checkbox */}
|
||||||
{polda.subDestination &&
|
<div className="flex items-center justify-center p-3">
|
||||||
expandedPolda[polda.id] && (
|
<Dialog>
|
||||||
<div className="max-h-[200px] overflow-y-auto border-t border-gray-100 pt-2">
|
<DialogTrigger asChild>
|
||||||
{/* Tombol Pilih Semua untuk sub-items */}
|
<Button
|
||||||
<div className="mb-2 flex justify-start">
|
variant="outline"
|
||||||
{(() => {
|
size="sm"
|
||||||
const allSubItemsChecked =
|
className="gap-2"
|
||||||
polda.subDestination?.every(
|
>
|
||||||
(sub: any) =>
|
<Icon
|
||||||
fileCheckedLevels[
|
icon="material-symbols:tune"
|
||||||
index
|
width={16}
|
||||||
]?.has(
|
height={16}
|
||||||
Number(sub.id)
|
/>
|
||||||
)
|
{t("custom", {
|
||||||
);
|
defaultValue: "Kustom",
|
||||||
return (
|
})}
|
||||||
<Button
|
</Button>
|
||||||
size="sm"
|
</DialogTrigger>
|
||||||
variant="outline"
|
<DialogContent className="max-w-[95vw] lg:max-w-[1400px] max-h-[90vh]">
|
||||||
className="text-xs h-6 px-2"
|
<DialogHeader className="border-b border-gray-200 pb-4">
|
||||||
onClick={() =>
|
<DialogTitle className="text-lg font-semibold">
|
||||||
handleSelectAllSubItems(
|
Daftar Wilayah POLDA dan POLRES
|
||||||
index,
|
</DialogTitle>
|
||||||
polda
|
</DialogHeader>
|
||||||
)
|
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4 max-h-[70vh] overflow-y-auto p-1">
|
||||||
}
|
{listDest.map((polda: any) => (
|
||||||
>
|
<div
|
||||||
{allSubItemsChecked ? (
|
key={polda.id}
|
||||||
<>
|
className="border border-gray-200 rounded-lg p-2 bg-white hover:shadow-sm transition-shadow"
|
||||||
<Icon
|
>
|
||||||
icon="material-symbols:check-indeterminate-small"
|
{/* Header POLDA */}
|
||||||
width={12}
|
<div className="flex items-center justify-between">
|
||||||
height={12}
|
<Label className="flex items-center gap-3 flex-1 cursor-pointer">
|
||||||
className="mr-1"
|
<Checkbox
|
||||||
/>
|
checked={
|
||||||
Batal Semua
|
fileCheckedLevels[
|
||||||
</>
|
index
|
||||||
) : (
|
]?.has(
|
||||||
<>
|
Number(polda.id)
|
||||||
<Icon
|
) || false
|
||||||
icon="material-symbols:check-all"
|
}
|
||||||
width={12}
|
onCheckedChange={() =>
|
||||||
height={12}
|
handleFileCheckboxChangePlacement(
|
||||||
className="mr-1"
|
index,
|
||||||
/>
|
Number(polda.id)
|
||||||
Pilih Semua
|
)
|
||||||
</>
|
}
|
||||||
)}
|
/>
|
||||||
</Button>
|
<span className="font-semibold text-gray-900 text-sm">
|
||||||
|
{polda.name}
|
||||||
|
</span>
|
||||||
|
</Label>
|
||||||
|
{polda.subDestination && (
|
||||||
|
<button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
toggleExpand(
|
||||||
|
polda.id
|
||||||
);
|
);
|
||||||
})()}
|
}}
|
||||||
</div>
|
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
|
||||||
<div className="space-y-1">
|
>
|
||||||
{polda.subDestination.map(
|
<Icon
|
||||||
(sub: any) => (
|
icon={
|
||||||
<Label
|
expandedPolda[
|
||||||
key={sub.id}
|
polda.id
|
||||||
className="flex items-center gap-2 p-2 rounded-md hover:bg-gray-50 transition-colors cursor-pointer text-xs"
|
]
|
||||||
>
|
? "mdi:chevron-up"
|
||||||
<Checkbox
|
: "mdi:chevron-down"
|
||||||
checked={
|
}
|
||||||
|
width={16}
|
||||||
|
height={16}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sub-items */}
|
||||||
|
{polda.subDestination &&
|
||||||
|
expandedPolda[polda.id] && (
|
||||||
|
<div className="max-h-[200px] overflow-y-auto border-t border-gray-100 pt-2">
|
||||||
|
{/* Tombol Pilih Semua untuk sub-items */}
|
||||||
|
<div className="mb-2 flex justify-start">
|
||||||
|
{(() => {
|
||||||
|
const allSubItemsChecked =
|
||||||
|
polda.subDestination?.every(
|
||||||
|
(sub: any) =>
|
||||||
fileCheckedLevels[
|
fileCheckedLevels[
|
||||||
index
|
index
|
||||||
]?.has(
|
]?.has(
|
||||||
Number(
|
Number(
|
||||||
sub.id
|
sub.id
|
||||||
)
|
)
|
||||||
) || false
|
)
|
||||||
}
|
);
|
||||||
onCheckedChange={() =>
|
return (
|
||||||
handleFileCheckboxChangePlacement(
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
className="text-xs h-6 px-2"
|
||||||
|
onClick={() =>
|
||||||
|
handleSelectAllSubItems(
|
||||||
index,
|
index,
|
||||||
Number(
|
polda
|
||||||
sub.id
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
>
|
||||||
<span className="text-gray-700">
|
{allSubItemsChecked ? (
|
||||||
{sub.name}
|
<>
|
||||||
</span>
|
<Icon
|
||||||
</Label>
|
icon="material-symbols:check-indeterminate-small"
|
||||||
)
|
width={12}
|
||||||
)}
|
height={
|
||||||
|
12
|
||||||
|
}
|
||||||
|
className="mr-1"
|
||||||
|
/>
|
||||||
|
Batal Semua
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Icon
|
||||||
|
icon="material-symbols:check-all"
|
||||||
|
width={12}
|
||||||
|
height={
|
||||||
|
12
|
||||||
|
}
|
||||||
|
className="mr-1"
|
||||||
|
/>
|
||||||
|
Pilih Semua
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
{polda.subDestination.map(
|
||||||
|
(sub: any) => (
|
||||||
|
<Label
|
||||||
|
key={sub.id}
|
||||||
|
className="flex items-center gap-2 p-2 rounded-md hover:bg-gray-50 transition-colors cursor-pointer text-xs"
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
checked={
|
||||||
|
fileCheckedLevels[
|
||||||
|
index
|
||||||
|
]?.has(
|
||||||
|
Number(
|
||||||
|
sub.id
|
||||||
|
)
|
||||||
|
) || false
|
||||||
|
}
|
||||||
|
onCheckedChange={() =>
|
||||||
|
handleFileCheckboxChangePlacement(
|
||||||
|
index,
|
||||||
|
Number(
|
||||||
|
sub.id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<span className="text-gray-700">
|
||||||
|
{sub.name}
|
||||||
|
</span>
|
||||||
|
</Label>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
))}
|
||||||
))}
|
</div>
|
||||||
</div>
|
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
|
||||||
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
|
<DialogClose asChild>
|
||||||
<DialogClose asChild>
|
<Button variant="outline">
|
||||||
<Button variant="outline">
|
{t("cancel", {
|
||||||
{t("cancel", {
|
defaultValue: "Batal",
|
||||||
defaultValue: "Batal",
|
})}
|
||||||
})}
|
</Button>
|
||||||
</Button>
|
</DialogClose>
|
||||||
</DialogClose>
|
<DialogClose asChild>
|
||||||
<DialogClose asChild>
|
<Button>
|
||||||
<Button>
|
{/* {t("save", {
|
||||||
{t("save", {
|
defaultValue: "Simpan",
|
||||||
defaultValue: "Simpan",
|
})} */}
|
||||||
})}
|
Simpan
|
||||||
</Button>
|
</Button>
|
||||||
</DialogClose>
|
</DialogClose>
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -192,6 +192,12 @@ export default function FormVideoDetail() {
|
||||||
}>
|
}>
|
||||||
>([]);
|
>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (Number(userLevelId) === 216 && Number(roleId) === 3) {
|
||||||
|
setIsUserMabesApprover(true);
|
||||||
|
}
|
||||||
|
}, [userLevelId, roleId]);
|
||||||
|
|
||||||
// Fungsi untuk mengupdate state individual file
|
// Fungsi untuk mengupdate state individual file
|
||||||
const handleFileUnitChange = (
|
const handleFileUnitChange = (
|
||||||
fileIndex: number,
|
fileIndex: number,
|
||||||
|
|
@ -211,6 +217,30 @@ export default function FormVideoDetail() {
|
||||||
currentSelection.polda = value;
|
currentSelection.polda = value;
|
||||||
currentSelection.polres = value;
|
currentSelection.polres = value;
|
||||||
currentSelection.satker = value;
|
currentSelection.satker = value;
|
||||||
|
|
||||||
|
// Update fileCheckedLevels untuk sinkronisasi dengan modal
|
||||||
|
setFileCheckedLevels((prevLevels) => {
|
||||||
|
const newArray = [...prevLevels];
|
||||||
|
const currentFileLevels = new Set<number>(newArray[fileIndex] || new Set());
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
// Checklist semua item di modal
|
||||||
|
listDest.forEach((item: any) => {
|
||||||
|
currentFileLevels.add(Number(item.id));
|
||||||
|
if (item.subDestination) {
|
||||||
|
item.subDestination.forEach((sub: any) => {
|
||||||
|
currentFileLevels.add(Number(sub.id));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Unchecklist semua item di modal
|
||||||
|
currentFileLevels.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
newArray[fileIndex] = currentFileLevels;
|
||||||
|
return newArray;
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// Validasi khusus untuk POLRES - harus ada POLDA yang ter-checklist
|
// Validasi khusus untuk POLRES - harus ada POLDA yang ter-checklist
|
||||||
if (key === "polres" && value) {
|
if (key === "polres" && value) {
|
||||||
|
|
@ -479,6 +509,31 @@ export default function FormVideoDetail() {
|
||||||
);
|
);
|
||||||
setDetailVideo(fileUrls);
|
setDetailVideo(fileUrls);
|
||||||
|
|
||||||
|
// Setup placement check untuk setiap file
|
||||||
|
const temp = [];
|
||||||
|
const unitSelections = [];
|
||||||
|
const checkedLevelsArray = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < details?.files?.length; i++) {
|
||||||
|
temp.push([]);
|
||||||
|
// Inisialisasi state untuk setiap file
|
||||||
|
unitSelections.push({
|
||||||
|
semua: false,
|
||||||
|
nasional: false,
|
||||||
|
wilayah: false,
|
||||||
|
international: false,
|
||||||
|
polda: false,
|
||||||
|
polres: false,
|
||||||
|
satker: false,
|
||||||
|
});
|
||||||
|
// Inisialisasi checkedLevels untuk setiap file
|
||||||
|
checkedLevelsArray.push(new Set<number>());
|
||||||
|
}
|
||||||
|
|
||||||
|
setFilePlacements(temp);
|
||||||
|
setFileUnitSelections(unitSelections);
|
||||||
|
setFileCheckedLevels(checkedLevelsArray);
|
||||||
|
|
||||||
const approvals = await getDataApprovalByMediaUpload(details?.id);
|
const approvals = await getDataApprovalByMediaUpload(details?.id);
|
||||||
setApproval(approvals?.data?.data);
|
setApproval(approvals?.data?.data);
|
||||||
}
|
}
|
||||||
|
|
@ -523,12 +578,19 @@ export default function FormVideoDetail() {
|
||||||
};
|
};
|
||||||
|
|
||||||
async function save() {
|
async function save() {
|
||||||
|
// Gabungkan semua checkedLevels dari semua file
|
||||||
|
const allCheckedLevels = new Set<number>();
|
||||||
|
fileCheckedLevels.forEach((fileLevels) => {
|
||||||
|
fileLevels.forEach((levelId) => {
|
||||||
|
allCheckedLevels.add(levelId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
mediaUploadId: id,
|
mediaUploadId: id,
|
||||||
statusId: status,
|
statusId: status,
|
||||||
message: description,
|
message: description,
|
||||||
files: isUserMabesApprover ? getPlacement() : [],
|
files: isUserMabesApprover ? getPlacement() : [],
|
||||||
customLocationPlacement: Array.from(checkedLevels).join(","),
|
|
||||||
};
|
};
|
||||||
setModalOpen(false);
|
setModalOpen(false);
|
||||||
loading();
|
loading();
|
||||||
|
|
@ -557,12 +619,28 @@ export default function FormVideoDetail() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const getPlacement = () => {
|
const getPlacement = () => {
|
||||||
// console.log("getPlaa", filePlacements);
|
|
||||||
const temp = [];
|
const temp = [];
|
||||||
for (let i = 0; i < filePlacements?.length; i++) {
|
for (let i = 0; i < filePlacements?.length; i++) {
|
||||||
if (filePlacements[i].length !== 0) {
|
if (filePlacements[i].length !== 0) {
|
||||||
const now = filePlacements[i].filter((a) => a !== "all");
|
// const now = filePlacements[i].filter((a) => a !== "all");
|
||||||
const data = { mediaFileId: files[i].id, placements: now.join(",") };
|
// const data = { mediaFileId: files[i].id, placements: now.join(",") };
|
||||||
|
// const now = filePlacements[i]?.filter((a) => a !== "semua");
|
||||||
|
const now = filePlacements[i];
|
||||||
|
let nowArr = now?.join(",")?.replaceAll("wilayah", "polda");
|
||||||
|
nowArr = nowArr?.replaceAll("nasional", "mabes");
|
||||||
|
nowArr = nowArr?.replaceAll("semua", "all");
|
||||||
|
|
||||||
|
// Dapatkan checked levels untuk file ini
|
||||||
|
const currentFileCheckedLevels = fileCheckedLevels[i]
|
||||||
|
? Array.from(fileCheckedLevels[i])
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
mediaFileId: files[i]?.id,
|
||||||
|
placements: nowArr,
|
||||||
|
customLocationPlacements: currentFileCheckedLevels.join(","),
|
||||||
|
};
|
||||||
|
|
||||||
temp.push(data);
|
temp.push(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -611,6 +689,43 @@ export default function FormVideoDetail() {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
if (placement === "all") {
|
if (placement === "all") {
|
||||||
temp[index] = ["all", "mabes", "polda", "international"];
|
temp[index] = ["all", "mabes", "polda", "international"];
|
||||||
|
|
||||||
|
// Update fileCheckedLevels untuk sinkronisasi dengan modal ketika "all" diklik
|
||||||
|
setFileCheckedLevels((prevLevels) => {
|
||||||
|
const newArray = [...prevLevels];
|
||||||
|
const currentFileLevels = new Set<number>(newArray[index] || new Set());
|
||||||
|
|
||||||
|
// Checklist semua item di modal
|
||||||
|
listDest.forEach((item: any) => {
|
||||||
|
currentFileLevels.add(Number(item.id));
|
||||||
|
if (item.subDestination) {
|
||||||
|
item.subDestination.forEach((sub: any) => {
|
||||||
|
currentFileLevels.add(Number(sub.id));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
newArray[index] = currentFileLevels;
|
||||||
|
return newArray;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update fileUnitSelections untuk checkbox tingkat utama
|
||||||
|
setFileUnitSelections((prevSelections) => {
|
||||||
|
const newSelections = [...prevSelections];
|
||||||
|
const currentSelection = { ...newSelections[index] };
|
||||||
|
|
||||||
|
// Set semua checkbox tingkat utama ke true
|
||||||
|
currentSelection.nasional = true;
|
||||||
|
currentSelection.wilayah = true;
|
||||||
|
currentSelection.international = true;
|
||||||
|
currentSelection.polda = true;
|
||||||
|
currentSelection.polres = true;
|
||||||
|
currentSelection.satker = true;
|
||||||
|
currentSelection.semua = true;
|
||||||
|
|
||||||
|
newSelections[index] = currentSelection;
|
||||||
|
return newSelections;
|
||||||
|
});
|
||||||
} else if (placement === "satker") {
|
} else if (placement === "satker") {
|
||||||
// Ketika satker di-checklist, HANYA tambahkan satker saja
|
// Ketika satker di-checklist, HANYA tambahkan satker saja
|
||||||
// JANGAN otomatis checklist polres
|
// JANGAN otomatis checklist polres
|
||||||
|
|
@ -637,6 +752,36 @@ export default function FormVideoDetail() {
|
||||||
} else {
|
} else {
|
||||||
if (placement === "all") {
|
if (placement === "all") {
|
||||||
temp[index] = [];
|
temp[index] = [];
|
||||||
|
|
||||||
|
// Update fileCheckedLevels untuk sinkronisasi dengan modal ketika "all" di-unchecklist
|
||||||
|
setFileCheckedLevels((prevLevels) => {
|
||||||
|
const newArray = [...prevLevels];
|
||||||
|
const currentFileLevels = new Set<number>(newArray[index] || new Set());
|
||||||
|
|
||||||
|
// Unchecklist semua item di modal
|
||||||
|
currentFileLevels.clear();
|
||||||
|
|
||||||
|
newArray[index] = currentFileLevels;
|
||||||
|
return newArray;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update fileUnitSelections untuk checkbox tingkat utama
|
||||||
|
setFileUnitSelections((prevSelections) => {
|
||||||
|
const newSelections = [...prevSelections];
|
||||||
|
const currentSelection = { ...newSelections[index] };
|
||||||
|
|
||||||
|
// Set semua checkbox tingkat utama ke false
|
||||||
|
currentSelection.nasional = false;
|
||||||
|
currentSelection.wilayah = false;
|
||||||
|
currentSelection.international = false;
|
||||||
|
currentSelection.polda = false;
|
||||||
|
currentSelection.polres = false;
|
||||||
|
currentSelection.satker = false;
|
||||||
|
currentSelection.semua = false;
|
||||||
|
|
||||||
|
newSelections[index] = currentSelection;
|
||||||
|
return newSelections;
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
const now = temp[index].filter((a) => a !== placement);
|
const now = temp[index].filter((a) => a !== placement);
|
||||||
// console.log("now", now);
|
// console.log("now", now);
|
||||||
|
|
@ -925,6 +1070,15 @@ export default function FormVideoDetail() {
|
||||||
currentSelection.polres = checkedPolresCount > 0;
|
currentSelection.polres = checkedPolresCount > 0;
|
||||||
currentSelection.satker = Boolean(isSatkerChecked);
|
currentSelection.satker = Boolean(isSatkerChecked);
|
||||||
|
|
||||||
|
// Update checkbox "semua" berdasarkan semua checkbox yang aktif
|
||||||
|
currentSelection.semua =
|
||||||
|
currentSelection.nasional &&
|
||||||
|
currentSelection.wilayah &&
|
||||||
|
currentSelection.international &&
|
||||||
|
currentSelection.polda &&
|
||||||
|
currentSelection.polres &&
|
||||||
|
currentSelection.satker;
|
||||||
|
|
||||||
newSelections[fileIndex] = currentSelection;
|
newSelections[fileIndex] = currentSelection;
|
||||||
return newSelections;
|
return newSelections;
|
||||||
});
|
});
|
||||||
|
|
@ -1285,84 +1439,36 @@ export default function FormVideoDetail() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Section Pengaturan Distribusi */}
|
{/* Section Pengaturan Distribusi */}
|
||||||
<div className="bg-white rounded-md p-4 border">
|
{isUserMabesApprover ? (
|
||||||
<h5 className="font-medium text-gray-900 mb-4 flex items-center gap-2">
|
<div className="bg-white rounded-md p-4 border">
|
||||||
<Icon
|
<h5 className="font-medium text-gray-900 mb-4 flex items-center gap-2">
|
||||||
icon="material-symbols:settings-outline"
|
<Icon
|
||||||
width={18}
|
icon="material-symbols:settings-outline"
|
||||||
height={18}
|
width={18}
|
||||||
/>
|
height={18}
|
||||||
Pengaturan Distribusi
|
/>
|
||||||
</h5>
|
Pengaturan Distribusi
|
||||||
|
</h5>
|
||||||
|
|
||||||
{/* Checkbox Tingkat Utama */}
|
{/* Checkbox Tingkat Utama */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-gray-700 mb-3">
|
<p className="text-sm font-medium text-gray-700 mb-3">
|
||||||
Tingkat Distribusi:
|
Tingkat Distribusi:
|
||||||
</p>
|
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
|
||||||
{[
|
|
||||||
{ key: "semua", label: "Semua" },
|
|
||||||
{ key: "nasional", label: "Nasional" },
|
|
||||||
{ key: "wilayah", label: "Wilayah" },
|
|
||||||
{
|
|
||||||
key: "international",
|
|
||||||
label: "Internasional",
|
|
||||||
},
|
|
||||||
].map((item, idx) => (
|
|
||||||
<div
|
|
||||||
key={item.key}
|
|
||||||
className="flex items-center gap-2 p-2 border border-gray-200 rounded-md hover:bg-gray-50"
|
|
||||||
>
|
|
||||||
<Checkbox
|
|
||||||
id={`${item.key}-${index}`}
|
|
||||||
checked={
|
|
||||||
fileUnitSelections[index]?.[
|
|
||||||
item.key as keyof typeof unitSelection
|
|
||||||
] || false
|
|
||||||
}
|
|
||||||
onCheckedChange={(value) => {
|
|
||||||
handleFileUnitChange(
|
|
||||||
index,
|
|
||||||
item.key as keyof typeof unitSelection,
|
|
||||||
value as boolean
|
|
||||||
);
|
|
||||||
setupPlacement(
|
|
||||||
index,
|
|
||||||
item.key,
|
|
||||||
Boolean(value)
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Label
|
|
||||||
htmlFor={`${item.key}-${index}`}
|
|
||||||
className="text-sm font-medium cursor-pointer"
|
|
||||||
>
|
|
||||||
{item.label}
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Detail Wilayah */}
|
|
||||||
{fileUnitSelections[index]?.wilayah && (
|
|
||||||
<div className="border-t border-gray-200 pt-2">
|
|
||||||
<p className="text-sm font-medium text-gray-700 mb-2">
|
|
||||||
Detail Wilayah:
|
|
||||||
</p>
|
</p>
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||||
{/* Checkbox Sub-kategori dengan tombol Kustom sejajar */}
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
|
||||||
{[
|
{[
|
||||||
{ key: "polda", label: "POLDA" },
|
{ key: "semua", label: "Semua" },
|
||||||
{ key: "polres", label: "POLRES" },
|
{ key: "nasional", label: "Nasional" },
|
||||||
{ key: "satker", label: "SATKER" },
|
{ key: "wilayah", label: "Wilayah" },
|
||||||
|
{
|
||||||
|
key: "international",
|
||||||
|
label: "Internasional",
|
||||||
|
},
|
||||||
].map((item, idx) => (
|
].map((item, idx) => (
|
||||||
<div
|
<div
|
||||||
key={item.key}
|
key={item.key}
|
||||||
className="flex items-center gap-2 p-3 border border-gray-200 rounded-md hover:bg-gray-50"
|
className="flex items-center gap-2 p-2 border border-gray-200 rounded-md hover:bg-gray-50"
|
||||||
>
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id={`${item.key}-${index}`}
|
id={`${item.key}-${index}`}
|
||||||
|
|
@ -1392,199 +1498,260 @@ export default function FormVideoDetail() {
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Tombol Kustom sejajar dengan checkbox */}
|
{/* Detail Wilayah */}
|
||||||
<div className="flex items-center justify-center p-3">
|
{fileUnitSelections[index]?.wilayah && (
|
||||||
<Dialog>
|
<div className="border-t border-gray-200 pt-2">
|
||||||
<DialogTrigger asChild>
|
<p className="text-sm font-medium text-gray-700 mb-2">
|
||||||
<Button
|
Detail Wilayah:
|
||||||
variant="outline"
|
</p>
|
||||||
size="sm"
|
|
||||||
className="gap-2"
|
{/* Checkbox Sub-kategori dengan tombol Kustom sejajar */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||||
|
{[
|
||||||
|
{ key: "polda", label: "POLDA" },
|
||||||
|
{ key: "polres", label: "POLRES" },
|
||||||
|
{ key: "satker", label: "SATKER" },
|
||||||
|
].map((item, idx) => (
|
||||||
|
<div
|
||||||
|
key={item.key}
|
||||||
|
className="flex items-center gap-2 p-3 border border-gray-200 rounded-md hover:bg-gray-50"
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
id={`${item.key}-${index}`}
|
||||||
|
checked={
|
||||||
|
fileUnitSelections[index]?.[
|
||||||
|
item.key as keyof typeof unitSelection
|
||||||
|
] || false
|
||||||
|
}
|
||||||
|
onCheckedChange={(value) => {
|
||||||
|
handleFileUnitChange(
|
||||||
|
index,
|
||||||
|
item.key as keyof typeof unitSelection,
|
||||||
|
value as boolean
|
||||||
|
);
|
||||||
|
setupPlacement(
|
||||||
|
index,
|
||||||
|
item.key,
|
||||||
|
Boolean(value)
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Label
|
||||||
|
htmlFor={`${item.key}-${index}`}
|
||||||
|
className="text-sm font-medium cursor-pointer"
|
||||||
>
|
>
|
||||||
<Icon
|
{item.label}
|
||||||
icon="material-symbols:tune"
|
</Label>
|
||||||
width={16}
|
</div>
|
||||||
height={16}
|
))}
|
||||||
/>
|
|
||||||
{t("custom", {
|
|
||||||
defaultValue: "Kustom",
|
|
||||||
})}
|
|
||||||
</Button>
|
|
||||||
</DialogTrigger>
|
|
||||||
<DialogContent className="max-w-[95vw] lg:max-w-[1400px] max-h-[90vh]">
|
|
||||||
<DialogHeader className="border-b border-gray-200 pb-4">
|
|
||||||
<DialogTitle className="text-lg font-semibold">
|
|
||||||
Daftar Wilayah POLDA dan POLRES
|
|
||||||
</DialogTitle>
|
|
||||||
</DialogHeader>
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4 max-h-[70vh] overflow-y-auto p-1">
|
|
||||||
{listDest.map((polda: any) => (
|
|
||||||
<div
|
|
||||||
key={polda.id}
|
|
||||||
className="border border-gray-200 rounded-lg p-2 bg-white hover:shadow-sm transition-shadow"
|
|
||||||
>
|
|
||||||
{/* Header POLDA */}
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<Label className="flex items-center gap-3 flex-1 cursor-pointer">
|
|
||||||
<Checkbox
|
|
||||||
checked={
|
|
||||||
fileCheckedLevels[
|
|
||||||
index
|
|
||||||
]?.has(
|
|
||||||
Number(polda.id)
|
|
||||||
) || false
|
|
||||||
}
|
|
||||||
onCheckedChange={() =>
|
|
||||||
handleFileCheckboxChangePlacement(
|
|
||||||
index,
|
|
||||||
Number(polda.id)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<span className="font-semibold text-gray-900 text-sm">
|
|
||||||
{polda.name}
|
|
||||||
</span>
|
|
||||||
</Label>
|
|
||||||
{polda.subDestination && (
|
|
||||||
<button
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
toggleExpand(polda.id);
|
|
||||||
}}
|
|
||||||
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
icon={
|
|
||||||
expandedPolda[
|
|
||||||
polda.id
|
|
||||||
]
|
|
||||||
? "mdi:chevron-up"
|
|
||||||
: "mdi:chevron-down"
|
|
||||||
}
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Sub-items */}
|
{/* Tombol Kustom sejajar dengan checkbox */}
|
||||||
{polda.subDestination &&
|
<div className="flex items-center justify-center p-3">
|
||||||
expandedPolda[polda.id] && (
|
<Dialog>
|
||||||
<div className="max-h-[200px] overflow-y-auto border-t border-gray-100 pt-2">
|
<DialogTrigger asChild>
|
||||||
{/* Tombol Pilih Semua untuk sub-items */}
|
<Button
|
||||||
<div className="mb-2 flex justify-start">
|
variant="outline"
|
||||||
{(() => {
|
size="sm"
|
||||||
const allSubItemsChecked =
|
className="gap-2"
|
||||||
polda.subDestination?.every(
|
>
|
||||||
(sub: any) =>
|
<Icon
|
||||||
fileCheckedLevels[
|
icon="material-symbols:tune"
|
||||||
index
|
width={16}
|
||||||
]?.has(
|
height={16}
|
||||||
Number(sub.id)
|
/>
|
||||||
)
|
{t("custom", {
|
||||||
);
|
defaultValue: "Kustom",
|
||||||
return (
|
})}
|
||||||
<Button
|
</Button>
|
||||||
size="sm"
|
</DialogTrigger>
|
||||||
variant="outline"
|
<DialogContent className="max-w-[95vw] lg:max-w-[1400px] max-h-[90vh]">
|
||||||
className="text-xs h-6 px-2"
|
<DialogHeader className="border-b border-gray-200 pb-4">
|
||||||
onClick={() =>
|
<DialogTitle className="text-lg font-semibold">
|
||||||
handleSelectAllSubItems(
|
Daftar Wilayah POLDA dan POLRES
|
||||||
index,
|
</DialogTitle>
|
||||||
polda
|
</DialogHeader>
|
||||||
)
|
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4 max-h-[70vh] overflow-y-auto p-1">
|
||||||
}
|
{listDest.map((polda: any) => (
|
||||||
>
|
<div
|
||||||
{allSubItemsChecked ? (
|
key={polda.id}
|
||||||
<>
|
className="border border-gray-200 rounded-lg p-2 bg-white hover:shadow-sm transition-shadow"
|
||||||
<Icon
|
>
|
||||||
icon="material-symbols:check-indeterminate-small"
|
{/* Header POLDA */}
|
||||||
width={12}
|
<div className="flex items-center justify-between">
|
||||||
height={12}
|
<Label className="flex items-center gap-3 flex-1 cursor-pointer">
|
||||||
className="mr-1"
|
<Checkbox
|
||||||
/>
|
checked={
|
||||||
Batal Semua
|
fileCheckedLevels[
|
||||||
</>
|
index
|
||||||
) : (
|
]?.has(
|
||||||
<>
|
Number(polda.id)
|
||||||
<Icon
|
) || false
|
||||||
icon="material-symbols:check-all"
|
}
|
||||||
width={12}
|
onCheckedChange={() =>
|
||||||
height={12}
|
handleFileCheckboxChangePlacement(
|
||||||
className="mr-1"
|
index,
|
||||||
/>
|
Number(polda.id)
|
||||||
Pilih Semua
|
)
|
||||||
</>
|
}
|
||||||
)}
|
/>
|
||||||
</Button>
|
<span className="font-semibold text-gray-900 text-sm">
|
||||||
|
{polda.name}
|
||||||
|
</span>
|
||||||
|
</Label>
|
||||||
|
{polda.subDestination && (
|
||||||
|
<button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
toggleExpand(
|
||||||
|
polda.id
|
||||||
);
|
);
|
||||||
})()}
|
}}
|
||||||
</div>
|
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
|
||||||
<div className="space-y-1">
|
>
|
||||||
{polda.subDestination.map(
|
<Icon
|
||||||
(sub: any) => (
|
icon={
|
||||||
<Label
|
expandedPolda[
|
||||||
key={sub.id}
|
polda.id
|
||||||
className="flex items-center gap-2 p-2 rounded-md hover:bg-gray-50 transition-colors cursor-pointer text-xs"
|
]
|
||||||
>
|
? "mdi:chevron-up"
|
||||||
<Checkbox
|
: "mdi:chevron-down"
|
||||||
checked={
|
}
|
||||||
|
width={16}
|
||||||
|
height={16}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sub-items */}
|
||||||
|
{polda.subDestination &&
|
||||||
|
expandedPolda[polda.id] && (
|
||||||
|
<div className="max-h-[200px] overflow-y-auto border-t border-gray-100 pt-2">
|
||||||
|
{/* Tombol Pilih Semua untuk sub-items */}
|
||||||
|
<div className="mb-2 flex justify-start">
|
||||||
|
{(() => {
|
||||||
|
const allSubItemsChecked =
|
||||||
|
polda.subDestination?.every(
|
||||||
|
(sub: any) =>
|
||||||
fileCheckedLevels[
|
fileCheckedLevels[
|
||||||
index
|
index
|
||||||
]?.has(
|
]?.has(
|
||||||
Number(
|
Number(
|
||||||
sub.id
|
sub.id
|
||||||
)
|
)
|
||||||
) || false
|
)
|
||||||
}
|
);
|
||||||
onCheckedChange={() =>
|
return (
|
||||||
handleFileCheckboxChangePlacement(
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
className="text-xs h-6 px-2"
|
||||||
|
onClick={() =>
|
||||||
|
handleSelectAllSubItems(
|
||||||
index,
|
index,
|
||||||
Number(
|
polda
|
||||||
sub.id
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
>
|
||||||
<span className="text-gray-700">
|
{allSubItemsChecked ? (
|
||||||
{sub.name}
|
<>
|
||||||
</span>
|
<Icon
|
||||||
</Label>
|
icon="material-symbols:check-indeterminate-small"
|
||||||
)
|
width={12}
|
||||||
)}
|
height={
|
||||||
|
12
|
||||||
|
}
|
||||||
|
className="mr-1"
|
||||||
|
/>
|
||||||
|
Batal Semua
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Icon
|
||||||
|
icon="material-symbols:check-all"
|
||||||
|
width={12}
|
||||||
|
height={
|
||||||
|
12
|
||||||
|
}
|
||||||
|
className="mr-1"
|
||||||
|
/>
|
||||||
|
Pilih Semua
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
{polda.subDestination.map(
|
||||||
|
(sub: any) => (
|
||||||
|
<Label
|
||||||
|
key={sub.id}
|
||||||
|
className="flex items-center gap-2 p-2 rounded-md hover:bg-gray-50 transition-colors cursor-pointer text-xs"
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
checked={
|
||||||
|
fileCheckedLevels[
|
||||||
|
index
|
||||||
|
]?.has(
|
||||||
|
Number(
|
||||||
|
sub.id
|
||||||
|
)
|
||||||
|
) || false
|
||||||
|
}
|
||||||
|
onCheckedChange={() =>
|
||||||
|
handleFileCheckboxChangePlacement(
|
||||||
|
index,
|
||||||
|
Number(
|
||||||
|
sub.id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<span className="text-gray-700">
|
||||||
|
{sub.name}
|
||||||
|
</span>
|
||||||
|
</Label>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
))}
|
||||||
))}
|
</div>
|
||||||
</div>
|
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
|
||||||
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
|
<DialogClose asChild>
|
||||||
<DialogClose asChild>
|
<Button variant="outline">
|
||||||
<Button variant="outline">
|
{t("cancel", {
|
||||||
{t("cancel", {
|
defaultValue: "Batal",
|
||||||
defaultValue: "Batal",
|
})}
|
||||||
})}
|
</Button>
|
||||||
</Button>
|
</DialogClose>
|
||||||
</DialogClose>
|
<DialogClose asChild>
|
||||||
<DialogClose asChild>
|
<Button>
|
||||||
<Button>
|
{/* {t("save", {
|
||||||
{t("save", {
|
defaultValue: "Simpan",
|
||||||
defaultValue: "Simpan",
|
})} */}
|
||||||
})}
|
Simpan
|
||||||
</Button>
|
</Button>
|
||||||
</DialogClose>
|
</DialogClose>
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -95,11 +95,19 @@ export function CalendarPolriAddDetail() {
|
||||||
const details = response?.data?.data;
|
const details = response?.data?.data;
|
||||||
|
|
||||||
setDetail(details);
|
setDetail(details);
|
||||||
if (details) {
|
// if (details) {
|
||||||
|
// setDate({
|
||||||
|
// from: parseISO(details.startDate),
|
||||||
|
// to: parseISO(details.endDate),
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
if (details?.startDate && details?.endDate) {
|
||||||
setDate({
|
setDate({
|
||||||
from: parseISO(details.startDate),
|
from: parseISO(details.startDate),
|
||||||
to: parseISO(details.endDate),
|
to: parseISO(details.endDate),
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// atau biarkan kosong sesuai kebutuhan
|
||||||
}
|
}
|
||||||
|
|
||||||
if (details?.assignedTo) {
|
if (details?.assignedTo) {
|
||||||
|
|
|
||||||
|
|
@ -1,78 +1,145 @@
|
||||||
|
// import { listDataAdvertisements } from "@/service/broadcast/broadcast";
|
||||||
|
// import { useEffect, useState } from "react";
|
||||||
|
// import * as React from "react";
|
||||||
|
|
||||||
|
// interface Advertisement {
|
||||||
|
// id: string;
|
||||||
|
// placements: string;
|
||||||
|
// imageUrl: string;
|
||||||
|
// [key: string]: any;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // // Simulasi fungsi API (replace dengan yang asli)
|
||||||
|
// // async function listDataAdvertisements(
|
||||||
|
// // page: number,
|
||||||
|
// // size: number,
|
||||||
|
// // search: string,
|
||||||
|
// // category: string,
|
||||||
|
// // status: string
|
||||||
|
// // ) {
|
||||||
|
// // // contoh struktur response dummy
|
||||||
|
// // return {
|
||||||
|
// // data: {
|
||||||
|
// // data: {
|
||||||
|
// // content: [
|
||||||
|
// // { id: "1", imageUrl: "/images/all-img/kiri1.png" },
|
||||||
|
// // { id: "2", imageUrl: "/images/all-img/kiri2.png" },
|
||||||
|
// // ],
|
||||||
|
// // totalElements: 2,
|
||||||
|
// // totalPages: 1,
|
||||||
|
// // },
|
||||||
|
// // },
|
||||||
|
// // };
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// const AdvertisementPlacements = (props: {
|
||||||
|
// placement: string;
|
||||||
|
// data: Advertisement[];
|
||||||
|
// loading: boolean;
|
||||||
|
// }) => {
|
||||||
|
// const [ads, setAds] = useState<Advertisement[] | undefined[]>([]);
|
||||||
|
// useEffect(() => {
|
||||||
|
// if (!props.loading && props.data.length > 0) {
|
||||||
|
// console.log(
|
||||||
|
// "RRRRRR",
|
||||||
|
// props.data[0].placements.includes(props.placement),
|
||||||
|
// props.placement
|
||||||
|
// );
|
||||||
|
|
||||||
|
// const filtered = props.data.filter((a) =>
|
||||||
|
// a.placements.includes(props.placement)
|
||||||
|
// );
|
||||||
|
// let temps: Advertisement[] | undefined[] = [];
|
||||||
|
// temps[0] = filtered.find((b) => b.placements.includes("top"));
|
||||||
|
// temps[1] = filtered.find((b) => b.placements.includes("bottom"));
|
||||||
|
// setAds(temps);
|
||||||
|
// console.log("PPPPPP", filtered);
|
||||||
|
// }
|
||||||
|
// }, [props.data, props.loading]);
|
||||||
|
|
||||||
|
// return (
|
||||||
|
// <div
|
||||||
|
// className={`sticky top-0 space-y-4 ${
|
||||||
|
// props.placement == "left" ? "ml-14" : "mr-14"
|
||||||
|
// }`}
|
||||||
|
// >
|
||||||
|
// {props.loading && <p className="text-sm text-gray-500">Loading...</p>}
|
||||||
|
// {ads?.map(
|
||||||
|
// (ad) =>
|
||||||
|
// ad && (
|
||||||
|
// <img
|
||||||
|
// key={ad.id}
|
||||||
|
// src={ad.contentFileUrl}
|
||||||
|
// alt={`Banner ${ad.id}`}
|
||||||
|
// className="w-full"
|
||||||
|
// />
|
||||||
|
// )
|
||||||
|
// )}
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
|
||||||
|
// export default AdvertisementPlacements;
|
||||||
import { listDataAdvertisements } from "@/service/broadcast/broadcast";
|
import { listDataAdvertisements } from "@/service/broadcast/broadcast";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog";
|
||||||
|
|
||||||
interface Advertisement {
|
interface Advertisement {
|
||||||
id: string;
|
id: string;
|
||||||
placements: string;
|
placements: string;
|
||||||
imageUrl: string;
|
imageUrl: string;
|
||||||
|
contentFileUrl?: string;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
// // Simulasi fungsi API (replace dengan yang asli)
|
|
||||||
// async function listDataAdvertisements(
|
|
||||||
// page: number,
|
|
||||||
// size: number,
|
|
||||||
// search: string,
|
|
||||||
// category: string,
|
|
||||||
// status: string
|
|
||||||
// ) {
|
|
||||||
// // contoh struktur response dummy
|
|
||||||
// return {
|
|
||||||
// data: {
|
|
||||||
// data: {
|
|
||||||
// content: [
|
|
||||||
// { id: "1", imageUrl: "/images/all-img/kiri1.png" },
|
|
||||||
// { id: "2", imageUrl: "/images/all-img/kiri2.png" },
|
|
||||||
// ],
|
|
||||||
// totalElements: 2,
|
|
||||||
// totalPages: 1,
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
const AdvertisementPlacements = (props: {
|
const AdvertisementPlacements = (props: {
|
||||||
placement: string;
|
placement: string;
|
||||||
data: Advertisement[];
|
data: Advertisement[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const [ads, setAds] = useState<Advertisement[] | undefined[]>([]);
|
const [ads, setAds] = useState<Advertisement[] | undefined[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!props.loading && props.data.length > 0) {
|
if (!props.loading && props.data.length > 0) {
|
||||||
console.log(
|
|
||||||
"RRRRRR",
|
|
||||||
props.data[0].placements.includes(props.placement),
|
|
||||||
props.placement
|
|
||||||
);
|
|
||||||
|
|
||||||
const filtered = props.data.filter((a) =>
|
const filtered = props.data.filter((a) =>
|
||||||
a.placements.includes(props.placement)
|
a.placements.includes(props.placement)
|
||||||
);
|
);
|
||||||
|
|
||||||
let temps: Advertisement[] | undefined[] = [];
|
let temps: Advertisement[] | undefined[] = [];
|
||||||
temps[0] = filtered.find((b) => b.placements.includes("top"));
|
temps[0] = filtered.find((b) => b.placements.includes("top"));
|
||||||
temps[1] = filtered.find((b) => b.placements.includes("bottom"));
|
temps[1] = filtered.find((b) => b.placements.includes("bottom"));
|
||||||
setAds(temps);
|
setAds(temps);
|
||||||
console.log("PPPPPP", filtered);
|
|
||||||
}
|
}
|
||||||
}, [props.data, props.loading]);
|
}, [props.data, props.loading, props.placement]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`sticky top-0 space-y-4 ${
|
className={`sticky top-0 space-y-4 ${
|
||||||
props.placement == "left" ? "ml-14" : "mr-14"
|
props.placement === "left" ? "ml-14" : "mr-14"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{props.loading && <p className="text-sm text-gray-500">Loading...</p>}
|
{props.loading && <p className="text-sm text-gray-500">Loading...</p>}
|
||||||
|
|
||||||
{ads?.map(
|
{ads?.map(
|
||||||
(ad) =>
|
(ad) =>
|
||||||
ad && (
|
ad && (
|
||||||
<img
|
<Dialog key={ad.id}>
|
||||||
key={ad.id}
|
<DialogTrigger asChild>
|
||||||
src={ad.contentFileUrl}
|
<img
|
||||||
alt={`Banner ${ad.id}`}
|
src={ad.contentFileUrl || ad.imageUrl}
|
||||||
className="w-full"
|
alt={`Banner ${ad.id}`}
|
||||||
/>
|
className="w-full cursor-pointer rounded-lg shadow-md hover:opacity-90 transition"
|
||||||
|
/>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent className="max-w-4xl p-0 bg-transparent border-0 shadow-none">
|
||||||
|
<img
|
||||||
|
src={ad.contentFileUrl || ad.imageUrl}
|
||||||
|
alt={`Banner Besar ${ad.id}`}
|
||||||
|
className="w-full h-auto rounded-lg"
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -533,76 +533,87 @@ const HeroNew = (props: { group?: string }) => {
|
||||||
|
|
||||||
<div className="relative w-full">
|
<div className="relative w-full">
|
||||||
{content && content.length > 0 && (
|
{content && content.length > 0 && (
|
||||||
<Swiper
|
<>
|
||||||
modules={[Autoplay, Navigation]}
|
<Swiper
|
||||||
autoplay={{
|
modules={[Autoplay, Navigation]}
|
||||||
delay: 10000,
|
autoplay={{
|
||||||
disableOnInteraction: false,
|
delay: 10000,
|
||||||
}}
|
disableOnInteraction: false,
|
||||||
loop={true}
|
}}
|
||||||
navigation={{
|
loop
|
||||||
nextEl: ".swiper-button-next",
|
navigation={{
|
||||||
prevEl: ".swiper-button-prev",
|
nextEl: ".hero-next",
|
||||||
}}
|
prevEl: ".hero-prev",
|
||||||
className="w-full"
|
}}
|
||||||
>
|
className="w-full"
|
||||||
{content.map((list: any) => (
|
>
|
||||||
<SwiperSlide key={list?.id}>
|
{content.map((list: any) => (
|
||||||
<div className="relative h-[310px] lg:h-[700px]">
|
<SwiperSlide key={list?.id}>
|
||||||
{/* Gambar */}
|
<div className="relative h-[310px] lg:h-[700px]">
|
||||||
<ImageBlurry
|
{/* Gambar */}
|
||||||
priority
|
<ImageBlurry
|
||||||
src={list?.smallThumbnailLink}
|
priority
|
||||||
alt="gambar"
|
src={list?.smallThumbnailLink}
|
||||||
style={{
|
alt="gambar"
|
||||||
objectFit: "contain",
|
style={{
|
||||||
width: "100%",
|
objectFit: "contain",
|
||||||
height: "100%",
|
width: "100%",
|
||||||
}}
|
height: "100%",
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Overlay */}
|
{/* Overlay */}
|
||||||
<div className="absolute inset-0 bg-black/40 z-10" />
|
<div className="absolute inset-0 bg-black/40 z-10" />
|
||||||
|
|
||||||
{/* Judul & Link */}
|
{/* Judul & Link */}
|
||||||
<Link
|
<Link
|
||||||
href={
|
href={
|
||||||
Number(list?.fileTypeId) === 1
|
Number(list?.fileTypeId) === 1
|
||||||
? `${prefixPath}/image/detail/${list?.slug}`
|
? `${prefixPath}/image/detail/${list?.slug}`
|
||||||
: Number(list?.fileTypeId) === 2
|
: Number(list?.fileTypeId) === 2
|
||||||
? `${prefixPath}/video/detail/${list?.slug}`
|
? `${prefixPath}/video/detail/${list?.slug}`
|
||||||
: Number(list?.fileTypeId) === 3
|
: Number(list?.fileTypeId) === 3
|
||||||
? `${prefixPath}/document/detail/${list?.slug}`
|
? `${prefixPath}/document/detail/${list?.slug}`
|
||||||
: `${prefixPath}/audio/detail/${list?.slug}`
|
: `${prefixPath}/audio/detail/${list?.slug}`
|
||||||
}
|
}
|
||||||
className="absolute bottom-10 lg:bottom-20 left-8 lg:left-32 z-20 text-white w-[85%] lg:w-[45%] cursor-pointer"
|
className="absolute bottom-10 lg:bottom-20 left-8 lg:left-32 z-20 text-white w-[85%] lg:w-[45%] cursor-pointer"
|
||||||
>
|
>
|
||||||
<span className="text-red-600 text-[10px] lg:text-lg font-bold uppercase">
|
<span className="text-red-600 text-[10px] lg:text-lg font-bold uppercase">
|
||||||
{list?.categoryName}
|
{list?.categoryName}
|
||||||
</span>
|
</span>
|
||||||
<h2 className="text-[14px] lg:text-xl font-bold">{list?.title}</h2>
|
<h2 className="text-[14px] lg:text-xl font-bold">
|
||||||
<p className="text-[9px] lg:text-sm mt-2">
|
{list?.title}
|
||||||
{formatDateToIndonesian(new Date(list?.createdAt))}{" "}
|
</h2>
|
||||||
{list?.timezone || "WIB"} | 👁 {list?.clickCount}
|
<p className="text-[9px] lg:text-sm mt-2">
|
||||||
</p>
|
{formatDateToIndonesian(new Date(list?.createdAt))}{" "}
|
||||||
</Link>
|
{list?.timezone || "WIB"} | 👁 {list?.clickCount}
|
||||||
|
</p>
|
||||||
<div className="absolute left-2 top-[45%] z-30 -translate-y-1/2 hover:bg-black/70 text-white p-2 rounded-full">
|
</Link>
|
||||||
<Icon
|
|
||||||
icon="mdi:chevron-left"
|
|
||||||
className="w-5 lg:w-10 h-5 lg:h-10"
|
|
||||||
/>{" "}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute right-2 top-[45%] z-30 -translate-y-1/2 hover:bg-black/70 text-white p-2 rounded-full">
|
</SwiperSlide>
|
||||||
<Icon
|
))}
|
||||||
icon="mdi:chevron-right"
|
</Swiper>
|
||||||
className="w-5 lg:w-10 h-5 lg:h-10"
|
|
||||||
/>{" "}
|
{/* Tombol navigasi — render SEKALI dan beri selector yang di-bind */}
|
||||||
</div>
|
<button
|
||||||
</div>
|
className="hero-prev absolute left-2 top-1/2 z-30 -translate-y-1/2 hover:bg-black/70 text-white p-2 rounded-full"
|
||||||
</SwiperSlide>
|
aria-label="Previous"
|
||||||
))}
|
>
|
||||||
</Swiper>
|
<Icon
|
||||||
|
icon="mdi:chevron-left"
|
||||||
|
className="w-5 lg:w-10 h-5 lg:h-10"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="hero-next absolute right-2 top-1/2 z-30 -translate-y-1/2 hover:bg-black/70 text-white p-2 rounded-full"
|
||||||
|
aria-label="Next"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon="mdi:chevron-right"
|
||||||
|
className="w-5 lg:w-10 h-5 lg:h-10"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -557,9 +557,9 @@
|
||||||
"question1": "WHAT CONTENT IS ON MEDIAHUB AND WHAT CATEGORIES ARE THERE IN IT?",
|
"question1": "WHAT CONTENT IS ON MEDIAHUB AND WHAT CATEGORIES ARE THERE IN IT?",
|
||||||
"answer1": "MediaHub has a variety of content such as news, videos, and documents categorized into topics such as education, entertainment, and current affairs.",
|
"answer1": "MediaHub has a variety of content such as news, videos, and documents categorized into topics such as education, entertainment, and current affairs.",
|
||||||
"question2": "HOW CAN MEDIAHUB CONTENT BE DOWNLOADED?",
|
"question2": "HOW CAN MEDIAHUB CONTENT BE DOWNLOADED?",
|
||||||
"answer2": "You can download content by clicking the download button available on each content on the MediaHub page.",
|
"answer2": "A=) Login first to be able to download B=) Select the photo size you want C=) Click the download button available on each content on the MediaHub page.",
|
||||||
"question3": "WHO CAN REGISTER AS A MEDIAHUB USER?",
|
"question3": "WHO CAN REGISTER AS A MEDIAHUB USER?",
|
||||||
"answer3": "Anyone who has an interest in the content on MediaHub can register as a user, either for personal or professional purposes.",
|
"answer3": "Anyone interested in MediaHub content can register as a user, including individuals and professionals within the General Public, Police, Journalists, and Presidential Staff Office (KSP) categories.",
|
||||||
"question4": "WHAT IS MEDIAHUB? AND WHAT ARE THE FEATURES IN IT?",
|
"question4": "WHAT IS MEDIAHUB? AND WHAT ARE THE FEATURES IN IT?",
|
||||||
"answer4": "MediaHub is a platform that provides various informative and educational content. Key features include search, download, and personalization of content.",
|
"answer4": "MediaHub is a platform that provides various informative and educational content. Key features include search, download, and personalization of content.",
|
||||||
"welcome": "Welcome To",
|
"welcome": "Welcome To",
|
||||||
|
|
|
||||||
|
|
@ -560,9 +560,9 @@
|
||||||
"question1": "APA SAJA KONTEN-KONTEN YANG ADA DI MEDIAHUB DAN KATEGORI DI DALAMNYA?",
|
"question1": "APA SAJA KONTEN-KONTEN YANG ADA DI MEDIAHUB DAN KATEGORI DI DALAMNYA?",
|
||||||
"answer1": "MediaHub memiliki beragam konten seperti berita, video, dan dokumen yang dikategorikan dalam topik seperti edukasi, hiburan, dan informasi terkini.",
|
"answer1": "MediaHub memiliki beragam konten seperti berita, video, dan dokumen yang dikategorikan dalam topik seperti edukasi, hiburan, dan informasi terkini.",
|
||||||
"question2": "BAGAIMANA KONTEN DARI MEDIAHUB DAPAT DIUNDUH?",
|
"question2": "BAGAIMANA KONTEN DARI MEDIAHUB DAPAT DIUNDUH?",
|
||||||
"answer2": "Anda dapat mengunduh konten dengan klik tombol unduh yang tersedia pada setiap konten di halaman MediaHub.",
|
"answer2": "A=) Login terlebih dahulu untuk dapat mengunduh. B=) Pilih size foto yang anda inginkan. C=) Klik tombol unduh yang tersedia pada setiap konten di halaman MediaHub.",
|
||||||
"question3": "SIAPA SAJA YANG DAPAT MENDAFTARKAN DIRI SEBAGAI PENGGUNA MEDIAHUB?",
|
"question3": "SIAPA SAJA YANG DAPAT MENDAFTARKAN DIRI SEBAGAI PENGGUNA MEDIAHUB?",
|
||||||
"answer3": "Semua orang yang memiliki minat terhadap konten di MediaHub dapat mendaftar sebagai pengguna, baik untuk personal maupun profesional.",
|
"answer3": "Semua orang yang memiliki minat terhadap konten di MediaHub dapat mendaftar sebagai pengguna, baik untuk personal maupun profesional yang tergabung dalam kategpri Masyarakat Umum. Anggota Polri, Jurnalis serta KSP",
|
||||||
"question4": "APA ITU MEDIAHUB? DAN APA SAJA FITUR YANG ADA DI DALAMNYA?",
|
"question4": "APA ITU MEDIAHUB? DAN APA SAJA FITUR YANG ADA DI DALAMNYA?",
|
||||||
"answer4": "MediaHub adalah platform yang menyediakan berbagai konten informatif dan edukatif. Fitur utama meliputi pencarian, pengunduhan, dan personalisasi konten.",
|
"answer4": "MediaHub adalah platform yang menyediakan berbagai konten informatif dan edukatif. Fitur utama meliputi pencarian, pengunduhan, dan personalisasi konten.",
|
||||||
"welcome": "Selamat Datang di",
|
"welcome": "Selamat Datang di",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue