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,
|
||||
cell: ({ row }) => {
|
||||
const MySwal = withReactContent(Swal);
|
||||
const levelNumber = Number(getCookiesDecrypt("ulne")); // 1 = Mabes, 2 = Polda
|
||||
const calendarOwnerLevel = Number(row.original.assignedToLevel); // dari API
|
||||
const calendarOwner = row.original.assignedTo; // ID unit Polda/Mabes dari API
|
||||
const myUnit = getCookiesDecrypt("unitId"); // misal ID unit Polda login
|
||||
const levelNumber = Number(getCookiesDecrypt("ulne"));
|
||||
const userId = Number(getCookiesDecrypt("uie"));
|
||||
|
||||
async function doDelete(id: any) {
|
||||
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 (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
|
|
@ -178,7 +153,7 @@ const useTableColumns = () => {
|
|||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="p-0" align="end">
|
||||
{canView && (
|
||||
|
||||
<Link
|
||||
href={`/contributor/schedule/calendar-polri/detail/${row.original.id}`}
|
||||
>
|
||||
|
|
@ -187,9 +162,9 @@ const useTableColumns = () => {
|
|||
Detail
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
)}
|
||||
|
||||
{canEdit && (
|
||||
|
||||
{row.original.createdById === userId && (
|
||||
<Link
|
||||
href={`/contributor/schedule/calendar-polri/update/${row.original.id}`}
|
||||
>
|
||||
|
|
@ -200,7 +175,7 @@ const useTableColumns = () => {
|
|||
</Link>
|
||||
)}
|
||||
|
||||
{canDelete && (
|
||||
{row.original.createdById === userId && (
|
||||
<DropdownMenuItem
|
||||
onClick={() => handleDeleteCalendars(row.original.id)}
|
||||
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 React, { useState } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import Image from "next/image";
|
||||
|
||||
interface FAQItem {
|
||||
question: string;
|
||||
|
|
@ -41,19 +42,41 @@ const FAQS: React.FC = () => {
|
|||
{/* Header */}
|
||||
<Reveal>
|
||||
<div className="flex items-center justify-center mb-6">
|
||||
<img src="/assets/icons-faqs.png" alt="Faqs" />
|
||||
<h2 className="ml-4 text-lg lg:text-2xl font-bold text-gray-800 dark:text-white">Frequently Asked Questions</h2>
|
||||
<Image
|
||||
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>
|
||||
|
||||
{/* FAQS Items */}
|
||||
<div className="space-y-4">
|
||||
{faqs?.map((faq, index) => (
|
||||
<div key={index} className="border-b border-gray-300 pb-2 cursor-pointer">
|
||||
<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
|
||||
key={index}
|
||||
className="border-b border-gray-300 pb-2 cursor-pointer"
|
||||
>
|
||||
<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>
|
||||
{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>
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import {
|
|||
saveMediaBlastCampaign,
|
||||
} from "@/service/broadcast/broadcast";
|
||||
import { error } from "@/config/swal";
|
||||
import { useRouter } from "@/i18n/routing";
|
||||
import { Link, useRouter } from "@/i18n/routing";
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
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 }) {
|
||||
const editor = useRef(null);
|
||||
const id = useParams()?.id;
|
||||
|
|
@ -85,7 +90,7 @@ export default function ContentBlast(props: { type: string }) {
|
|||
const router = useRouter();
|
||||
const { type } = props;
|
||||
|
||||
const [dataSelectCampaign, setDataSelectCampaign] = useState([]);
|
||||
const [dataSelectCampaign, setDataSelectCampaign] = useState<Campaign[]>([]);
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
|
||||
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”!
|
||||
</div>
|
||||
<DialogFooter className="flex justify-center">
|
||||
<Button type="button" color="success">
|
||||
Menu "Sent"
|
||||
</Button>
|
||||
<Link
|
||||
href={`/admin/broadcast/campaign-list/detail/${
|
||||
form.getValues("selected")[0]?.id
|
||||
}`}
|
||||
>
|
||||
<Button type="button" color="success">
|
||||
Menu "Sent"
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
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
|
||||
const handleFileUnitChange = (
|
||||
fileIndex: number,
|
||||
|
|
@ -219,6 +225,30 @@ export default function FormAudioDetail() {
|
|||
currentSelection.polda = value;
|
||||
currentSelection.polres = 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 {
|
||||
// Validasi khusus untuk POLRES - harus ada POLDA yang ter-checklist
|
||||
if (key === "polres" && value) {
|
||||
|
|
@ -480,10 +510,28 @@ export default function FormAudioDetail() {
|
|||
|
||||
const setupPlacementCheck = (length: number) => {
|
||||
const temp = [];
|
||||
const unitSelections = [];
|
||||
const checkedLevelsArray = [];
|
||||
|
||||
for (let i = 0; i < 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);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -587,8 +635,24 @@ export default function FormAudioDetail() {
|
|||
const temp = [];
|
||||
for (let i = 0; i < filePlacements?.length; i++) {
|
||||
if (filePlacements[i]?.length !== 0) {
|
||||
const now = filePlacements[i]?.filter((a) => a !== "all");
|
||||
const data = { mediaFileId: files[i]?.id, placements: now.join(",") };
|
||||
// const now = filePlacements[i]?.filter((a) => a !== "all");
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -596,13 +660,19 @@ export default function FormAudioDetail() {
|
|||
};
|
||||
|
||||
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 = {
|
||||
mediaUploadId: id,
|
||||
statusId: status,
|
||||
message: description,
|
||||
// files: [],
|
||||
files: isUserMabesApprover ? getPlacement() : [],
|
||||
customLocationPlacement: Array.from(checkedLevels).join(","),
|
||||
};
|
||||
setModalOpen(false);
|
||||
loading();
|
||||
|
|
@ -638,37 +708,129 @@ export default function FormAudioDetail() {
|
|||
setRejectedFiles(rejects);
|
||||
}
|
||||
|
||||
// const setupPlacement = (
|
||||
// index: number,
|
||||
// placement: string,
|
||||
// checked: boolean
|
||||
// ) => {
|
||||
// let temp = [...filePlacements];
|
||||
// if (checked) {
|
||||
// if (placement === "all") {
|
||||
// temp[index] = ["all", "mabes", "polda", "international"];
|
||||
// } else {
|
||||
// const now = temp[index];
|
||||
// now?.push(placement);
|
||||
// if (now?.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);
|
||||
// temp[index] = now;
|
||||
// if (now?.length === 3 && now?.includes("all")) {
|
||||
// const newData = now.filter((b) => b !== "all");
|
||||
// temp[index] = newData;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// setFilePlacements(temp);
|
||||
// };
|
||||
const setupPlacement = (
|
||||
index: number,
|
||||
placement: string,
|
||||
checked: boolean
|
||||
) => {
|
||||
let temp = [...filePlacements];
|
||||
if (checked) {
|
||||
if (placement === "all") {
|
||||
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") {
|
||||
// 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 = (
|
||||
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 = (
|
||||
fileIndex: number,
|
||||
|
|
@ -992,6 +1099,15 @@ export default function FormAudioDetail() {
|
|||
currentSelection.polres = checkedPolresCount > 0;
|
||||
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;
|
||||
return newSelections;
|
||||
});
|
||||
|
|
@ -1303,6 +1419,13 @@ export default function FormAudioDetail() {
|
|||
<p className="text-sm text-gray-600 break-all">
|
||||
{file.fileName}
|
||||
</p>
|
||||
{isUserMabesApprover ? (
|
||||
""
|
||||
) : (
|
||||
<p className="status text-success text-sm mb-0">
|
||||
Selesai
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
|
|
@ -1320,84 +1443,36 @@ export default function FormAudioDetail() {
|
|||
</div>
|
||||
|
||||
{/* Section Pengaturan Distribusi */}
|
||||
<div className="bg-white rounded-md p-4 border">
|
||||
<h5 className="font-medium text-gray-900 mb-4 flex items-center gap-2">
|
||||
<Icon
|
||||
icon="material-symbols:settings-outline"
|
||||
width={18}
|
||||
height={18}
|
||||
/>
|
||||
Pengaturan Distribusi
|
||||
</h5>
|
||||
{isUserMabesApprover ? (
|
||||
<div className="bg-white rounded-md p-4 border">
|
||||
<h5 className="font-medium text-gray-900 mb-4 flex items-center gap-2">
|
||||
<Icon
|
||||
icon="material-symbols:settings-outline"
|
||||
width={18}
|
||||
height={18}
|
||||
/>
|
||||
Pengaturan Distribusi
|
||||
</h5>
|
||||
|
||||
{/* Checkbox Tingkat Utama */}
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-700 mb-3">
|
||||
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:
|
||||
{/* Checkbox Tingkat Utama */}
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-700 mb-3">
|
||||
Tingkat Distribusi:
|
||||
</p>
|
||||
|
||||
{/* Checkbox Sub-kategori dengan tombol Kustom sejajar */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||
{[
|
||||
{ key: "polda", label: "POLDA" },
|
||||
{ key: "polres", label: "POLRES" },
|
||||
{ key: "satker", label: "SATKER" },
|
||||
{ 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-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
|
||||
id={`${item.key}-${index}`}
|
||||
|
|
@ -1427,199 +1502,260 @@ export default function FormAudioDetail() {
|
|||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tombol Kustom sejajar dengan checkbox */}
|
||||
<div className="flex items-center justify-center p-3">
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="gap-2"
|
||||
{/* 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>
|
||||
|
||||
{/* 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
|
||||
icon="material-symbols:tune"
|
||||
width={16}
|
||||
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>
|
||||
{item.label}
|
||||
</Label>
|
||||
</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[
|
||||
index
|
||||
]?.has(
|
||||
Number(sub.id)
|
||||
)
|
||||
);
|
||||
return (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="text-xs h-6 px-2"
|
||||
onClick={() =>
|
||||
handleSelectAllSubItems(
|
||||
index,
|
||||
polda
|
||||
)
|
||||
}
|
||||
>
|
||||
{allSubItemsChecked ? (
|
||||
<>
|
||||
<Icon
|
||||
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>
|
||||
{/* Tombol Kustom sejajar dengan checkbox */}
|
||||
<div className="flex items-center justify-center p-3">
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="gap-2"
|
||||
>
|
||||
<Icon
|
||||
icon="material-symbols:tune"
|
||||
width={16}
|
||||
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
|
||||
);
|
||||
})()}
|
||||
</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={
|
||||
}}
|
||||
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 */}
|
||||
{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[
|
||||
index
|
||||
]?.has(
|
||||
Number(
|
||||
sub.id
|
||||
)
|
||||
) || false
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleFileCheckboxChangePlacement(
|
||||
)
|
||||
);
|
||||
return (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="text-xs h-6 px-2"
|
||||
onClick={() =>
|
||||
handleSelectAllSubItems(
|
||||
index,
|
||||
Number(
|
||||
sub.id
|
||||
)
|
||||
polda
|
||||
)
|
||||
}
|
||||
/>
|
||||
<span className="text-gray-700">
|
||||
{sub.name}
|
||||
</span>
|
||||
</Label>
|
||||
)
|
||||
)}
|
||||
>
|
||||
{allSubItemsChecked ? (
|
||||
<>
|
||||
<Icon
|
||||
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 className="flex justify-end gap-3 border-t border-gray-200 pt-4">
|
||||
<DialogClose asChild>
|
||||
<Button variant="outline">
|
||||
{t("cancel", {
|
||||
defaultValue: "Batal",
|
||||
})}
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<DialogClose asChild>
|
||||
<Button>
|
||||
{t("save", {
|
||||
defaultValue: "Simpan",
|
||||
})}
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
|
||||
<DialogClose asChild>
|
||||
<Button variant="outline">
|
||||
{t("cancel", {
|
||||
defaultValue: "Batal",
|
||||
})}
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<DialogClose asChild>
|
||||
<Button>
|
||||
{/* {t("save", {
|
||||
defaultValue: "Simpan",
|
||||
})} */}
|
||||
Simpan
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</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
|
||||
const handleFileUnitChange = (
|
||||
fileIndex: number,
|
||||
|
|
@ -208,6 +214,30 @@ export default function FormTeksDetail() {
|
|||
currentSelection.polda = value;
|
||||
currentSelection.polres = 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 {
|
||||
// Validasi khusus untuk POLRES - harus ada POLDA yang ter-checklist
|
||||
if (key === "polres" && value) {
|
||||
|
|
@ -440,10 +470,28 @@ export default function FormTeksDetail() {
|
|||
|
||||
const setupPlacementCheck = (length: number) => {
|
||||
const temp = [];
|
||||
const unitSelections = [];
|
||||
const checkedLevelsArray = [];
|
||||
|
||||
for (let i = 0; i < 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);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -460,6 +508,7 @@ export default function FormTeksDetail() {
|
|||
names: details?.files[0]?.fileName,
|
||||
format: details?.files[0]?.format,
|
||||
});
|
||||
setupPlacementCheck(details?.files?.length);
|
||||
|
||||
if (details?.assignedToLevel) {
|
||||
const levels = new Set(
|
||||
|
|
@ -530,12 +579,27 @@ export default function FormTeksDetail() {
|
|||
};
|
||||
|
||||
const getPlacement = () => {
|
||||
// console.log("getPlaa", filePlacements);
|
||||
const temp = [];
|
||||
for (let i = 0; i < filePlacements?.length; i++) {
|
||||
if (filePlacements[i]?.length !== 0) {
|
||||
const now = filePlacements[i]?.filter((a) => a !== "all");
|
||||
const data = { mediaFileId: files[i]?.id, placements: now?.join(",") };
|
||||
// const now = filePlacements[i]?.filter((a) => a !== "all");
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -543,12 +607,19 @@ export default function FormTeksDetail() {
|
|||
};
|
||||
|
||||
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 = {
|
||||
mediaUploadId: id,
|
||||
statusId: status,
|
||||
message: description,
|
||||
files: isUserMabesApprover ? getPlacement() : [],
|
||||
customLocationPlacement: Array.from(checkedLevels).join(","),
|
||||
};
|
||||
setModalOpen(false);
|
||||
|
||||
|
|
@ -653,6 +724,43 @@ export default function FormTeksDetail() {
|
|||
if (checked) {
|
||||
if (placement === "all") {
|
||||
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") {
|
||||
// Ketika satker di-checklist, HANYA tambahkan satker saja
|
||||
// JANGAN otomatis checklist polres
|
||||
|
|
@ -679,6 +787,36 @@ export default function FormTeksDetail() {
|
|||
} 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);
|
||||
|
|
@ -930,6 +1068,15 @@ export default function FormTeksDetail() {
|
|||
currentSelection.polres = checkedPolresCount > 0;
|
||||
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;
|
||||
return newSelections;
|
||||
});
|
||||
|
|
@ -1264,6 +1411,13 @@ export default function FormTeksDetail() {
|
|||
<p className="text-sm text-gray-600 break-all">
|
||||
{file.fileName}
|
||||
</p>
|
||||
{isUserMabesApprover ? (
|
||||
""
|
||||
) : (
|
||||
<p className="status text-success text-sm mb-0">
|
||||
Selesai
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
|
|
@ -1281,84 +1435,36 @@ export default function FormTeksDetail() {
|
|||
</div>
|
||||
|
||||
{/* Section Pengaturan Distribusi */}
|
||||
<div className="bg-white rounded-md p-4 border">
|
||||
<h5 className="font-medium text-gray-900 mb-4 flex items-center gap-2">
|
||||
<Icon
|
||||
icon="material-symbols:settings-outline"
|
||||
width={18}
|
||||
height={18}
|
||||
/>
|
||||
Pengaturan Distribusi
|
||||
</h5>
|
||||
{isUserMabesApprover ? (
|
||||
<div className="bg-white rounded-md p-4 border">
|
||||
<h5 className="font-medium text-gray-900 mb-4 flex items-center gap-2">
|
||||
<Icon
|
||||
icon="material-symbols:settings-outline"
|
||||
width={18}
|
||||
height={18}
|
||||
/>
|
||||
Pengaturan Distribusi
|
||||
</h5>
|
||||
|
||||
{/* Checkbox Tingkat Utama */}
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-700 mb-3">
|
||||
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:
|
||||
{/* Checkbox Tingkat Utama */}
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-700 mb-3">
|
||||
Tingkat Distribusi:
|
||||
</p>
|
||||
|
||||
{/* Checkbox Sub-kategori dengan tombol Kustom sejajar */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||
{[
|
||||
{ key: "polda", label: "POLDA" },
|
||||
{ key: "polres", label: "POLRES" },
|
||||
{ key: "satker", label: "SATKER" },
|
||||
{ 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-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
|
||||
id={`${item.key}-${index}`}
|
||||
|
|
@ -1388,199 +1494,260 @@ export default function FormTeksDetail() {
|
|||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tombol Kustom sejajar dengan checkbox */}
|
||||
<div className="flex items-center justify-center p-3">
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="gap-2"
|
||||
{/* 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>
|
||||
|
||||
{/* 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
|
||||
icon="material-symbols:tune"
|
||||
width={16}
|
||||
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>
|
||||
{item.label}
|
||||
</Label>
|
||||
</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[
|
||||
index
|
||||
]?.has(
|
||||
Number(sub.id)
|
||||
)
|
||||
);
|
||||
return (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="text-xs h-6 px-2"
|
||||
onClick={() =>
|
||||
handleSelectAllSubItems(
|
||||
index,
|
||||
polda
|
||||
)
|
||||
}
|
||||
>
|
||||
{allSubItemsChecked ? (
|
||||
<>
|
||||
<Icon
|
||||
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>
|
||||
{/* Tombol Kustom sejajar dengan checkbox */}
|
||||
<div className="flex items-center justify-center p-3">
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="gap-2"
|
||||
>
|
||||
<Icon
|
||||
icon="material-symbols:tune"
|
||||
width={16}
|
||||
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
|
||||
);
|
||||
})()}
|
||||
</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={
|
||||
}}
|
||||
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 */}
|
||||
{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[
|
||||
index
|
||||
]?.has(
|
||||
Number(
|
||||
sub.id
|
||||
)
|
||||
) || false
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleFileCheckboxChangePlacement(
|
||||
)
|
||||
);
|
||||
return (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="text-xs h-6 px-2"
|
||||
onClick={() =>
|
||||
handleSelectAllSubItems(
|
||||
index,
|
||||
Number(
|
||||
sub.id
|
||||
)
|
||||
polda
|
||||
)
|
||||
}
|
||||
/>
|
||||
<span className="text-gray-700">
|
||||
{sub.name}
|
||||
</span>
|
||||
</Label>
|
||||
)
|
||||
)}
|
||||
>
|
||||
{allSubItemsChecked ? (
|
||||
<>
|
||||
<Icon
|
||||
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 className="flex justify-end gap-3 border-t border-gray-200 pt-4">
|
||||
<DialogClose asChild>
|
||||
<Button variant="outline">
|
||||
{t("cancel", {
|
||||
defaultValue: "Batal",
|
||||
})}
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<DialogClose asChild>
|
||||
<Button>
|
||||
{t("save", {
|
||||
defaultValue: "Simpan",
|
||||
})}
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
|
||||
<DialogClose asChild>
|
||||
<Button variant="outline">
|
||||
{t("cancel", {
|
||||
defaultValue: "Batal",
|
||||
})}
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<DialogClose asChild>
|
||||
<Button>
|
||||
{/* {t("save", {
|
||||
defaultValue: "Simpan",
|
||||
})} */}
|
||||
Simpan
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</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
|
||||
const handleFileUnitChange = (
|
||||
fileIndex: number,
|
||||
|
|
@ -211,6 +217,30 @@ export default function FormVideoDetail() {
|
|||
currentSelection.polda = value;
|
||||
currentSelection.polres = 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 {
|
||||
// Validasi khusus untuk POLRES - harus ada POLDA yang ter-checklist
|
||||
if (key === "polres" && value) {
|
||||
|
|
@ -479,6 +509,31 @@ export default function FormVideoDetail() {
|
|||
);
|
||||
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);
|
||||
setApproval(approvals?.data?.data);
|
||||
}
|
||||
|
|
@ -523,12 +578,19 @@ export default function FormVideoDetail() {
|
|||
};
|
||||
|
||||
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 = {
|
||||
mediaUploadId: id,
|
||||
statusId: status,
|
||||
message: description,
|
||||
files: isUserMabesApprover ? getPlacement() : [],
|
||||
customLocationPlacement: Array.from(checkedLevels).join(","),
|
||||
};
|
||||
setModalOpen(false);
|
||||
loading();
|
||||
|
|
@ -557,12 +619,28 @@ export default function FormVideoDetail() {
|
|||
}
|
||||
|
||||
const getPlacement = () => {
|
||||
// console.log("getPlaa", filePlacements);
|
||||
const temp = [];
|
||||
for (let i = 0; i < filePlacements?.length; i++) {
|
||||
if (filePlacements[i].length !== 0) {
|
||||
const now = filePlacements[i].filter((a) => a !== "all");
|
||||
const data = { mediaFileId: files[i].id, placements: now.join(",") };
|
||||
// const now = filePlacements[i].filter((a) => a !== "all");
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -611,6 +689,43 @@ export default function FormVideoDetail() {
|
|||
if (checked) {
|
||||
if (placement === "all") {
|
||||
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") {
|
||||
// Ketika satker di-checklist, HANYA tambahkan satker saja
|
||||
// JANGAN otomatis checklist polres
|
||||
|
|
@ -637,6 +752,36 @@ export default function FormVideoDetail() {
|
|||
} 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);
|
||||
|
|
@ -925,6 +1070,15 @@ export default function FormVideoDetail() {
|
|||
currentSelection.polres = checkedPolresCount > 0;
|
||||
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;
|
||||
return newSelections;
|
||||
});
|
||||
|
|
@ -1285,84 +1439,36 @@ export default function FormVideoDetail() {
|
|||
</div>
|
||||
|
||||
{/* Section Pengaturan Distribusi */}
|
||||
<div className="bg-white rounded-md p-4 border">
|
||||
<h5 className="font-medium text-gray-900 mb-4 flex items-center gap-2">
|
||||
<Icon
|
||||
icon="material-symbols:settings-outline"
|
||||
width={18}
|
||||
height={18}
|
||||
/>
|
||||
Pengaturan Distribusi
|
||||
</h5>
|
||||
{isUserMabesApprover ? (
|
||||
<div className="bg-white rounded-md p-4 border">
|
||||
<h5 className="font-medium text-gray-900 mb-4 flex items-center gap-2">
|
||||
<Icon
|
||||
icon="material-symbols:settings-outline"
|
||||
width={18}
|
||||
height={18}
|
||||
/>
|
||||
Pengaturan Distribusi
|
||||
</h5>
|
||||
|
||||
{/* Checkbox Tingkat Utama */}
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-700 mb-3">
|
||||
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:
|
||||
{/* Checkbox Tingkat Utama */}
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-700 mb-3">
|
||||
Tingkat Distribusi:
|
||||
</p>
|
||||
|
||||
{/* Checkbox Sub-kategori dengan tombol Kustom sejajar */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||
{[
|
||||
{ key: "polda", label: "POLDA" },
|
||||
{ key: "polres", label: "POLRES" },
|
||||
{ key: "satker", label: "SATKER" },
|
||||
{ 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-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
|
||||
id={`${item.key}-${index}`}
|
||||
|
|
@ -1392,199 +1498,260 @@ export default function FormVideoDetail() {
|
|||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tombol Kustom sejajar dengan checkbox */}
|
||||
<div className="flex items-center justify-center p-3">
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="gap-2"
|
||||
{/* 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>
|
||||
|
||||
{/* 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
|
||||
icon="material-symbols:tune"
|
||||
width={16}
|
||||
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>
|
||||
{item.label}
|
||||
</Label>
|
||||
</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[
|
||||
index
|
||||
]?.has(
|
||||
Number(sub.id)
|
||||
)
|
||||
);
|
||||
return (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="text-xs h-6 px-2"
|
||||
onClick={() =>
|
||||
handleSelectAllSubItems(
|
||||
index,
|
||||
polda
|
||||
)
|
||||
}
|
||||
>
|
||||
{allSubItemsChecked ? (
|
||||
<>
|
||||
<Icon
|
||||
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>
|
||||
{/* Tombol Kustom sejajar dengan checkbox */}
|
||||
<div className="flex items-center justify-center p-3">
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="gap-2"
|
||||
>
|
||||
<Icon
|
||||
icon="material-symbols:tune"
|
||||
width={16}
|
||||
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
|
||||
);
|
||||
})()}
|
||||
</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={
|
||||
}}
|
||||
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 */}
|
||||
{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[
|
||||
index
|
||||
]?.has(
|
||||
Number(
|
||||
sub.id
|
||||
)
|
||||
) || false
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleFileCheckboxChangePlacement(
|
||||
)
|
||||
);
|
||||
return (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="text-xs h-6 px-2"
|
||||
onClick={() =>
|
||||
handleSelectAllSubItems(
|
||||
index,
|
||||
Number(
|
||||
sub.id
|
||||
)
|
||||
polda
|
||||
)
|
||||
}
|
||||
/>
|
||||
<span className="text-gray-700">
|
||||
{sub.name}
|
||||
</span>
|
||||
</Label>
|
||||
)
|
||||
)}
|
||||
>
|
||||
{allSubItemsChecked ? (
|
||||
<>
|
||||
<Icon
|
||||
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 className="flex justify-end gap-3 border-t border-gray-200 pt-4">
|
||||
<DialogClose asChild>
|
||||
<Button variant="outline">
|
||||
{t("cancel", {
|
||||
defaultValue: "Batal",
|
||||
})}
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<DialogClose asChild>
|
||||
<Button>
|
||||
{t("save", {
|
||||
defaultValue: "Simpan",
|
||||
})}
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
|
||||
<DialogClose asChild>
|
||||
<Button variant="outline">
|
||||
{t("cancel", {
|
||||
defaultValue: "Batal",
|
||||
})}
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<DialogClose asChild>
|
||||
<Button>
|
||||
{/* {t("save", {
|
||||
defaultValue: "Simpan",
|
||||
})} */}
|
||||
Simpan
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -95,11 +95,19 @@ export function CalendarPolriAddDetail() {
|
|||
const details = response?.data?.data;
|
||||
|
||||
setDetail(details);
|
||||
if (details) {
|
||||
// if (details) {
|
||||
// setDate({
|
||||
// from: parseISO(details.startDate),
|
||||
// to: parseISO(details.endDate),
|
||||
// });
|
||||
// }
|
||||
if (details?.startDate && details?.endDate) {
|
||||
setDate({
|
||||
from: parseISO(details.startDate),
|
||||
to: parseISO(details.endDate),
|
||||
});
|
||||
} else {
|
||||
// atau biarkan kosong sesuai kebutuhan
|
||||
}
|
||||
|
||||
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 { useEffect, useState } from "react";
|
||||
import * as React from "react";
|
||||
import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog";
|
||||
|
||||
interface Advertisement {
|
||||
id: string;
|
||||
placements: string;
|
||||
imageUrl: string;
|
||||
contentFileUrl?: 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]);
|
||||
}, [props.data, props.loading, props.placement]);
|
||||
|
||||
return (
|
||||
<div
|
||||
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>}
|
||||
|
||||
{ads?.map(
|
||||
(ad) =>
|
||||
ad && (
|
||||
<img
|
||||
key={ad.id}
|
||||
src={ad.contentFileUrl}
|
||||
alt={`Banner ${ad.id}`}
|
||||
className="w-full"
|
||||
/>
|
||||
<Dialog key={ad.id}>
|
||||
<DialogTrigger asChild>
|
||||
<img
|
||||
src={ad.contentFileUrl || ad.imageUrl}
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -533,76 +533,87 @@ const HeroNew = (props: { group?: string }) => {
|
|||
|
||||
<div className="relative w-full">
|
||||
{content && content.length > 0 && (
|
||||
<Swiper
|
||||
modules={[Autoplay, Navigation]}
|
||||
autoplay={{
|
||||
delay: 10000,
|
||||
disableOnInteraction: false,
|
||||
}}
|
||||
loop={true}
|
||||
navigation={{
|
||||
nextEl: ".swiper-button-next",
|
||||
prevEl: ".swiper-button-prev",
|
||||
}}
|
||||
className="w-full"
|
||||
>
|
||||
{content.map((list: any) => (
|
||||
<SwiperSlide key={list?.id}>
|
||||
<div className="relative h-[310px] lg:h-[700px]">
|
||||
{/* Gambar */}
|
||||
<ImageBlurry
|
||||
priority
|
||||
src={list?.smallThumbnailLink}
|
||||
alt="gambar"
|
||||
style={{
|
||||
objectFit: "contain",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
}}
|
||||
/>
|
||||
<>
|
||||
<Swiper
|
||||
modules={[Autoplay, Navigation]}
|
||||
autoplay={{
|
||||
delay: 10000,
|
||||
disableOnInteraction: false,
|
||||
}}
|
||||
loop
|
||||
navigation={{
|
||||
nextEl: ".hero-next",
|
||||
prevEl: ".hero-prev",
|
||||
}}
|
||||
className="w-full"
|
||||
>
|
||||
{content.map((list: any) => (
|
||||
<SwiperSlide key={list?.id}>
|
||||
<div className="relative h-[310px] lg:h-[700px]">
|
||||
{/* Gambar */}
|
||||
<ImageBlurry
|
||||
priority
|
||||
src={list?.smallThumbnailLink}
|
||||
alt="gambar"
|
||||
style={{
|
||||
objectFit: "contain",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Overlay */}
|
||||
<div className="absolute inset-0 bg-black/40 z-10" />
|
||||
{/* Overlay */}
|
||||
<div className="absolute inset-0 bg-black/40 z-10" />
|
||||
|
||||
{/* Judul & Link */}
|
||||
<Link
|
||||
href={
|
||||
Number(list?.fileTypeId) === 1
|
||||
? `${prefixPath}/image/detail/${list?.slug}`
|
||||
: Number(list?.fileTypeId) === 2
|
||||
? `${prefixPath}/video/detail/${list?.slug}`
|
||||
: Number(list?.fileTypeId) === 3
|
||||
? `${prefixPath}/document/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"
|
||||
>
|
||||
<span className="text-red-600 text-[10px] lg:text-lg font-bold uppercase">
|
||||
{list?.categoryName}
|
||||
</span>
|
||||
<h2 className="text-[14px] lg:text-xl font-bold">{list?.title}</h2>
|
||||
<p className="text-[9px] lg:text-sm mt-2">
|
||||
{formatDateToIndonesian(new Date(list?.createdAt))}{" "}
|
||||
{list?.timezone || "WIB"} | 👁 {list?.clickCount}
|
||||
</p>
|
||||
</Link>
|
||||
|
||||
<div className="absolute left-2 top-[45%] z-30 -translate-y-1/2 hover:bg-black/70 text-white p-2 rounded-full">
|
||||
<Icon
|
||||
icon="mdi:chevron-left"
|
||||
className="w-5 lg:w-10 h-5 lg:h-10"
|
||||
/>{" "}
|
||||
{/* Judul & Link */}
|
||||
<Link
|
||||
href={
|
||||
Number(list?.fileTypeId) === 1
|
||||
? `${prefixPath}/image/detail/${list?.slug}`
|
||||
: Number(list?.fileTypeId) === 2
|
||||
? `${prefixPath}/video/detail/${list?.slug}`
|
||||
: Number(list?.fileTypeId) === 3
|
||||
? `${prefixPath}/document/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"
|
||||
>
|
||||
<span className="text-red-600 text-[10px] lg:text-lg font-bold uppercase">
|
||||
{list?.categoryName}
|
||||
</span>
|
||||
<h2 className="text-[14px] lg:text-xl font-bold">
|
||||
{list?.title}
|
||||
</h2>
|
||||
<p className="text-[9px] lg:text-sm mt-2">
|
||||
{formatDateToIndonesian(new Date(list?.createdAt))}{" "}
|
||||
{list?.timezone || "WIB"} | 👁 {list?.clickCount}
|
||||
</p>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="absolute right-2 top-[45%] z-30 -translate-y-1/2 hover:bg-black/70 text-white p-2 rounded-full">
|
||||
<Icon
|
||||
icon="mdi:chevron-right"
|
||||
className="w-5 lg:w-10 h-5 lg:h-10"
|
||||
/>{" "}
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
|
||||
{/* Tombol navigasi — render SEKALI dan beri selector yang di-bind */}
|
||||
<button
|
||||
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"
|
||||
aria-label="Previous"
|
||||
>
|
||||
<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>
|
||||
|
||||
|
|
|
|||
|
|
@ -557,9 +557,9 @@
|
|||
"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.",
|
||||
"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?",
|
||||
"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?",
|
||||
"answer4": "MediaHub is a platform that provides various informative and educational content. Key features include search, download, and personalization of content.",
|
||||
"welcome": "Welcome To",
|
||||
|
|
|
|||
|
|
@ -560,9 +560,9 @@
|
|||
"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.",
|
||||
"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?",
|
||||
"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?",
|
||||
"answer4": "MediaHub adalah platform yang menyediakan berbagai konten informatif dan edukatif. Fitur utama meliputi pencarian, pengunduhan, dan personalisasi konten.",
|
||||
"welcome": "Selamat Datang di",
|
||||
|
|
|
|||
Loading…
Reference in New Issue