This commit is contained in:
Sabda Yagra 2025-08-23 22:04:54 +07:00
parent e5a2fc2bf1
commit 285bcad1d8
5 changed files with 1561 additions and 1215 deletions

View File

@ -198,6 +198,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,
@ -1298,6 +1304,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
@ -1315,6 +1328,7 @@ export default function FormAudioDetail() {
</div> </div>
{/* Section Pengaturan Distribusi */} {/* Section Pengaturan Distribusi */}
{isUserMabesApprover ? (
<div className="bg-white rounded-md p-4 border"> <div className="bg-white rounded-md p-4 border">
<h5 className="font-medium text-gray-900 mb-4 flex items-center gap-2"> <h5 className="font-medium text-gray-900 mb-4 flex items-center gap-2">
<Icon <Icon
@ -1481,7 +1495,9 @@ export default function FormAudioDetail() {
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
toggleExpand(polda.id); toggleExpand(
polda.id
);
}} }}
className="p-1 hover:bg-gray-100 rounded-md transition-colors" className="p-1 hover:bg-gray-100 rounded-md transition-colors"
> >
@ -1513,7 +1529,9 @@ export default function FormAudioDetail() {
fileCheckedLevels[ fileCheckedLevels[
index index
]?.has( ]?.has(
Number(sub.id) Number(
sub.id
)
) )
); );
return ( return (
@ -1533,7 +1551,9 @@ export default function FormAudioDetail() {
<Icon <Icon
icon="material-symbols:check-indeterminate-small" icon="material-symbols:check-indeterminate-small"
width={12} width={12}
height={12} height={
12
}
className="mr-1" className="mr-1"
/> />
Batal Semua Batal Semua
@ -1543,7 +1563,9 @@ export default function FormAudioDetail() {
<Icon <Icon
icon="material-symbols:check-all" icon="material-symbols:check-all"
width={12} width={12}
height={12} height={
12
}
className="mr-1" className="mr-1"
/> />
Pilih Semua Pilih Semua
@ -1615,6 +1637,9 @@ export default function FormAudioDetail() {
)} )}
</div> </div>
</div> </div>
) : (
""
)}
</div> </div>
))} ))}
</div> </div>

View File

@ -20,7 +20,6 @@ import {
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { import {
@ -163,7 +162,8 @@ export default function FormImageDetail() {
const [isUserMabesApprover, setIsUserMabesApprover] = useState(false); const [isUserMabesApprover, setIsUserMabesApprover] = useState(false);
const [approval, setApproval] = useState<any>(); const [approval, setApproval] = useState<any>();
// State untuk setiap file secara individual // State untuk setiap file secara individual
const [fileUnitSelections, setFileUnitSelections] = useState<Array<{ const [fileUnitSelections, setFileUnitSelections] = useState<
Array<{
semua: boolean; semua: boolean;
nasional: boolean; nasional: boolean;
wilayah: boolean; wilayah: boolean;
@ -171,10 +171,19 @@ export default function FormImageDetail() {
polda: boolean; polda: boolean;
polres: boolean; polres: boolean;
satker: boolean; satker: boolean;
}>>([]); }>
>([]);
useEffect(() => {
if (Number(userLevelId) === 216 && Number(roleId) === 3) {
setIsUserMabesApprover(true);
}
}, [userLevelId, roleId]);
// State untuk setiap file secara individual - checklist levels // State untuk setiap file secara individual - checklist levels
const [fileCheckedLevels, setFileCheckedLevels] = useState<Array<Set<number>>>([]); const [fileCheckedLevels, setFileCheckedLevels] = useState<
Array<Set<number>>
>([]);
// State global untuk kompatibilitas (akan dihapus nanti) // State global untuk kompatibilitas (akan dihapus nanti)
const [unitSelection, setUnitSelection] = useState({ const [unitSelection, setUnitSelection] = useState({
@ -191,12 +200,16 @@ export default function FormImageDetail() {
const [selectedTarget, setSelectedTarget] = useState(""); const [selectedTarget, setSelectedTarget] = useState("");
const [files, setFiles] = useState<FileType[]>([]); const [files, setFiles] = useState<FileType[]>([]);
const [rejectedFiles, setRejectedFiles] = useState<number[]>([]); const [rejectedFiles, setRejectedFiles] = useState<number[]>([]);
const [expandedPolda, setExpandedPolda] = useState<Record<number, boolean>>({}); const [expandedPolda, setExpandedPolda] = useState<Record<number, boolean>>(
{}
);
// State untuk melacak apakah perubahan berasal dari checkbox utama // State untuk melacak apakah perubahan berasal dari checkbox utama
const [isUpdatingFromMainCheckbox, setIsUpdatingFromMainCheckbox] = useState(false); const [isUpdatingFromMainCheckbox, setIsUpdatingFromMainCheckbox] =
useState(false);
// State untuk melacak jenis perubahan spesifik // State untuk melacak jenis perubahan spesifik
const [mainCheckboxChangeType, setMainCheckboxChangeType] = useState<string>(""); const [mainCheckboxChangeType, setMainCheckboxChangeType] =
useState<string>("");
const [wilayahPublish, setWilayahPublish] = React.useState({ const [wilayahPublish, setWilayahPublish] = React.useState({
semua: false, semua: false,
nasional: false, nasional: false,
@ -263,7 +276,11 @@ export default function FormImageDetail() {
// useEffect untuk sinkronisasi checkbox modal dengan checkbox utama // useEffect untuk sinkronisasi checkbox modal dengan checkbox utama
useEffect(() => { useEffect(() => {
if (listDest.length > 0 && isUpdatingFromMainCheckbox && mainCheckboxChangeType) { if (
listDest.length > 0 &&
isUpdatingFromMainCheckbox &&
mainCheckboxChangeType
) {
syncModalWithMainCheckbox(); syncModalWithMainCheckbox();
} }
}, [isUpdatingFromMainCheckbox, mainCheckboxChangeType]); }, [isUpdatingFromMainCheckbox, mainCheckboxChangeType]);
@ -279,7 +296,8 @@ export default function FormImageDetail() {
const updateMainCheckboxFromModalLegacy = () => { const updateMainCheckboxFromModalLegacy = () => {
if (!isUpdatingFromMainCheckbox && listDest.length > 0) { if (!isUpdatingFromMainCheckbox && listDest.length > 0) {
// Hitung item yang dipilih berdasarkan checkedLevels // Hitung item yang dipilih berdasarkan checkedLevels
const checkedPoldaCount = listDest.filter((item: any) => const checkedPoldaCount = listDest.filter(
(item: any) =>
item.levelNumber === 2 && item.levelNumber === 2 &&
item.name !== "SATKER POLRI" && item.name !== "SATKER POLRI" &&
checkedLevels.has(Number(item.id)) checkedLevels.has(Number(item.id))
@ -288,16 +306,25 @@ export default function FormImageDetail() {
const checkedPolresCount = listDest.reduce((total: number, item: any) => { const checkedPolresCount = listDest.reduce((total: number, item: any) => {
if (item.subDestination && item.name !== "SATKER POLRI") { if (item.subDestination && item.name !== "SATKER POLRI") {
// Hanya hitung sub-item dari POLDA (bukan dari SATKER POLRI) // Hanya hitung sub-item dari POLDA (bukan dari SATKER POLRI)
return total + item.subDestination.filter((sub: any) => checkedLevels.has(Number(sub.id))).length; return (
total +
item.subDestination.filter((sub: any) =>
checkedLevels.has(Number(sub.id))
).length
);
} }
return total; return total;
}, 0); }, 0);
const satkerItem: any = listDest.find((item: any) => item.name === "SATKER POLRI"); const satkerItem: any = listDest.find(
const checkedSatkerCount = satkerItem ? ( (item: any) => item.name === "SATKER POLRI"
(checkedLevels.has(Number(satkerItem.id)) ? 1 : 0) + );
(satkerItem.subDestination?.filter((sub: any) => checkedLevels.has(Number(sub.id))).length || 0) const checkedSatkerCount = satkerItem
) : 0; ? (checkedLevels.has(Number(satkerItem.id)) ? 1 : 0) +
(satkerItem.subDestination?.filter((sub: any) =>
checkedLevels.has(Number(sub.id))
).length || 0)
: 0;
// Checkbox aktif jika ADA item yang dipilih dalam kategori tersebut // Checkbox aktif jika ADA item yang dipilih dalam kategori tersebut
const hasSelectedPolda = checkedPoldaCount > 0; const hasSelectedPolda = checkedPoldaCount > 0;
@ -305,7 +332,7 @@ export default function FormImageDetail() {
const hasSelectedSatker = checkedSatkerCount > 0; const hasSelectedSatker = checkedSatkerCount > 0;
// Update unitSelection berdasarkan yang dipilih di modal // Update unitSelection berdasarkan yang dipilih di modal
setUnitSelection(prev => { setUnitSelection((prev) => {
const newState = { ...prev }; const newState = { ...prev };
// Update individual checkboxes // Update individual checkboxes
@ -314,7 +341,13 @@ export default function FormImageDetail() {
newState.satker = hasSelectedSatker; newState.satker = hasSelectedSatker;
// Update checkbox "semua" berdasarkan semua checkbox yang aktif // Update checkbox "semua" berdasarkan semua checkbox yang aktif
newState.semua = newState.nasional && newState.wilayah && newState.international && hasSelectedPolda && hasSelectedPolres && hasSelectedSatker; newState.semua =
newState.nasional &&
newState.wilayah &&
newState.international &&
hasSelectedPolda &&
hasSelectedPolres &&
hasSelectedSatker;
return newState; return newState;
}); });
@ -338,7 +371,11 @@ export default function FormImageDetail() {
// Checklist semua polres, tapi hanya yang poldanya sudah di-checklist // Checklist semua polres, tapi hanya yang poldanya sudah di-checklist
// Jangan checklist sub-item SATKER POLRI // Jangan checklist sub-item SATKER POLRI
listDest.forEach((item: any) => { listDest.forEach((item: any) => {
if (item.levelNumber === 2 && item.name !== "SATKER POLRI" && newCheckedLevels.has(Number(item.id))) { if (
item.levelNumber === 2 &&
item.name !== "SATKER POLRI" &&
newCheckedLevels.has(Number(item.id))
) {
if (item.subDestination) { if (item.subDestination) {
item.subDestination.forEach((polres: any) => { item.subDestination.forEach((polres: any) => {
newCheckedLevels.add(Number(polres.id)); newCheckedLevels.add(Number(polres.id));
@ -354,7 +391,9 @@ export default function FormImageDetail() {
// - POLRES: unit-unit seperti POLRES METRO JAKARTA PUSAT, dll. // - POLRES: unit-unit seperti POLRES METRO JAKARTA PUSAT, dll.
} else if (mainCheckboxChangeType === "satker_checked") { } else if (mainCheckboxChangeType === "satker_checked") {
// Checklist satker // Checklist satker
const satkerItem: any = listDest.find((item: any) => item.name === "SATKER POLRI"); const satkerItem: any = listDest.find(
(item: any) => item.name === "SATKER POLRI"
);
if (satkerItem) { if (satkerItem) {
newCheckedLevels.add(Number(satkerItem.id)); newCheckedLevels.add(Number(satkerItem.id));
// Checklist semua sub-item yang ada di bawah SATKER (bukan POLRES) // Checklist semua sub-item yang ada di bawah SATKER (bukan POLRES)
@ -400,7 +439,9 @@ export default function FormImageDetail() {
}); });
} else if (mainCheckboxChangeType === "satker_unchecked") { } else if (mainCheckboxChangeType === "satker_unchecked") {
// Clear satker dan semua sub-item di bawahnya dari checkedLevels // Clear satker dan semua sub-item di bawahnya dari checkedLevels
const satkerItem: any = listDest.find((item: any) => item.name === "SATKER POLRI"); const satkerItem: any = listDest.find(
(item: any) => item.name === "SATKER POLRI"
);
if (satkerItem) { if (satkerItem) {
newCheckedLevels.delete(Number(satkerItem.id)); newCheckedLevels.delete(Number(satkerItem.id));
if (satkerItem.subDestination) { if (satkerItem.subDestination) {
@ -428,7 +469,7 @@ export default function FormImageDetail() {
key: keyof typeof unitSelection, key: keyof typeof unitSelection,
value: boolean value: boolean
) => { ) => {
setFileUnitSelections(prev => { setFileUnitSelections((prev) => {
const newSelections = [...prev]; const newSelections = [...prev];
const currentSelection = { ...newSelections[fileIndex] }; const currentSelection = { ...newSelections[fileIndex] };
@ -445,14 +486,19 @@ export default function FormImageDetail() {
// 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) {
const currentFileCheckedLevels = fileCheckedLevels[fileIndex]; const currentFileCheckedLevels = fileCheckedLevels[fileIndex];
const hasSelectedPolda = currentFileCheckedLevels && listDest.some((item: any) => const hasSelectedPolda =
currentFileCheckedLevels &&
listDest.some(
(item: any) =>
item.levelNumber === 2 && item.levelNumber === 2 &&
item.name !== "SATKER POLRI" && item.name !== "SATKER POLRI" &&
currentFileCheckedLevels.has(Number(item.id)) currentFileCheckedLevels.has(Number(item.id))
); );
if (!hasSelectedPolda) { if (!hasSelectedPolda) {
alert("Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES."); alert(
"Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES."
);
return prev; // Batalkan perubahan return prev; // Batalkan perubahan
} }
} }
@ -503,7 +549,8 @@ export default function FormImageDetail() {
// Validasi khusus untuk POLRES // Validasi khusus untuk POLRES
if (key === "polres" && value) { if (key === "polres" && value) {
// Cek apakah ada POLDA yang sudah dipilih di modal // Cek apakah ada POLDA yang sudah dipilih di modal
const hasSelectedPolda = listDest.some((item: any) => const hasSelectedPolda = listDest.some(
(item: any) =>
item.levelNumber === 2 && item.levelNumber === 2 &&
item.name !== "SATKER POLRI" && item.name !== "SATKER POLRI" &&
checkedLevels.has(Number(item.id)) checkedLevels.has(Number(item.id))
@ -511,7 +558,9 @@ export default function FormImageDetail() {
if (!hasSelectedPolda) { if (!hasSelectedPolda) {
// Jika tidak ada POLDA yang dipilih, tampilkan peringatan dan batalkan // Jika tidak ada POLDA yang dipilih, tampilkan peringatan dan batalkan
alert("Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES."); alert(
"Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES."
);
// Reset flag karena perubahan dibatalkan // Reset flag karena perubahan dibatalkan
setIsUpdatingFromMainCheckbox(false); setIsUpdatingFromMainCheckbox(false);
setMainCheckboxChangeType(""); setMainCheckboxChangeType("");
@ -571,7 +620,10 @@ export default function FormImageDetail() {
}; };
// Fungsi untuk mengupdate checklist levels untuk file tertentu // Fungsi untuk mengupdate checklist levels untuk file tertentu
const handleFileCheckboxChangePlacement = (fileIndex: number, levelId: number) => { const handleFileCheckboxChangePlacement = (
fileIndex: number,
levelId: number
) => {
setFileCheckedLevels((prev) => { setFileCheckedLevels((prev) => {
const newArray = [...prev]; const newArray = [...prev];
const currentFileLevels = new Set<number>(newArray[fileIndex]); const currentFileLevels = new Set<number>(newArray[fileIndex]);
@ -581,8 +633,14 @@ export default function FormImageDetail() {
currentFileLevels.delete(levelId); currentFileLevels.delete(levelId);
// Jika ini adalah POLDA yang di-unchecklist, unchecklist juga semua polres di bawahnya // Jika ini adalah POLDA yang di-unchecklist, unchecklist juga semua polres di bawahnya
const poldaItem = listDest.find((item: any) => Number(item.id) === levelId) as any; const poldaItem = listDest.find(
if (poldaItem && poldaItem.subDestination && poldaItem.name !== "SATKER POLRI") { (item: any) => Number(item.id) === levelId
) as any;
if (
poldaItem &&
poldaItem.subDestination &&
poldaItem.name !== "SATKER POLRI"
) {
poldaItem.subDestination.forEach((polres: any) => { poldaItem.subDestination.forEach((polres: any) => {
currentFileLevels.delete(Number(polres.id)); currentFileLevels.delete(Number(polres.id));
}); });
@ -598,7 +656,9 @@ export default function FormImageDetail() {
currentFileLevels.add(levelId); currentFileLevels.add(levelId);
// Jika ini adalah SATKER POLRI yang di-checklist, checklist juga semua sub-item di bawahnya // Jika ini adalah SATKER POLRI yang di-checklist, checklist juga semua sub-item di bawahnya
const satkerItem = listDest.find((item: any) => Number(item.id) === levelId) as any; const satkerItem = listDest.find(
(item: any) => Number(item.id) === levelId
) as any;
if (satkerItem && satkerItem.name === "SATKER POLRI") { if (satkerItem && satkerItem.name === "SATKER POLRI") {
// Checklist semua sub-item di bawah SATKER POLRI (bukan POLRES) // Checklist semua sub-item di bawah SATKER POLRI (bukan POLRES)
satkerItem.subDestination?.forEach((subItem: any) => { satkerItem.subDestination?.forEach((subItem: any) => {
@ -664,38 +724,57 @@ export default function FormImageDetail() {
if (!currentFileLevels) return prev; if (!currentFileLevels) return prev;
// Hitung total POLDA yang ada (bukan SATKER POLRI) // Hitung total POLDA yang ada (bukan SATKER POLRI)
const totalPoldaCount = listDest.filter((item: any) => const totalPoldaCount = listDest.filter(
item.levelNumber === 2 && item.name !== "SATKER POLRI" (item: any) => item.levelNumber === 2 && item.name !== "SATKER POLRI"
).length; ).length;
// Hitung berapa banyak POLDA yang ter-checklist (bukan SATKER POLRI) // Hitung berapa banyak POLDA yang ter-checklist (bukan SATKER POLRI)
const checkedPoldaCount = listDest.reduce((total: number, item: any) => { const checkedPoldaCount = listDest.reduce((total: number, item: any) => {
if (item.levelNumber === 2 && item.name !== "SATKER POLRI" && currentFileLevels.has(Number(item.id))) { if (
item.levelNumber === 2 &&
item.name !== "SATKER POLRI" &&
currentFileLevels.has(Number(item.id))
) {
return total + 1; return total + 1;
} }
return total; return total;
}, 0); }, 0);
// Hitung total POLRES yang ada dari POLDA yang ter-checklist // Hitung total POLRES yang ada dari POLDA yang ter-checklist
const totalPolresFromCheckedPolda = listDest.reduce((total: number, item: any) => { const totalPolresFromCheckedPolda = listDest.reduce(
if (item.subDestination && item.name !== "SATKER POLRI" && currentFileLevels.has(Number(item.id))) { (total: number, item: any) => {
if (
item.subDestination &&
item.name !== "SATKER POLRI" &&
currentFileLevels.has(Number(item.id))
) {
return total + item.subDestination.length; return total + item.subDestination.length;
} }
return total; return total;
}, 0); },
0
);
// Hitung berapa banyak POLRES yang ter-checklist // Hitung berapa banyak POLRES yang ter-checklist
const checkedPolresCount = listDest.reduce((total: number, item: any) => { const checkedPolresCount = listDest.reduce((total: number, item: any) => {
if (item.subDestination && item.name !== "SATKER POLRI") { if (item.subDestination && item.name !== "SATKER POLRI") {
// Hanya hitung sub-item dari POLDA (bukan dari SATKER POLRI) // Hanya hitung sub-item dari POLDA (bukan dari SATKER POLRI)
return total + item.subDestination.filter((sub: any) => currentFileLevels.has(Number(sub.id))).length; return (
total +
item.subDestination.filter((sub: any) =>
currentFileLevels.has(Number(sub.id))
).length
);
} }
return total; return total;
}, 0); }, 0);
// Cek apakah SATKER POLRI ter-checklist // Cek apakah SATKER POLRI ter-checklist
const satkerItem = listDest.find((item: any) => item.name === "SATKER POLRI"); const satkerItem = listDest.find(
const isSatkerChecked = satkerItem && currentFileLevels.has(Number(satkerItem.id)); (item: any) => item.name === "SATKER POLRI"
);
const isSatkerChecked =
satkerItem && currentFileLevels.has(Number(satkerItem.id));
// Update checkbox berdasarkan kondisi // Update checkbox berdasarkan kondisi
// POLDA aktif jika ada minimal 1 POLDA ter-checklist // POLDA aktif jika ada minimal 1 POLDA ter-checklist
@ -719,8 +798,14 @@ export default function FormImageDetail() {
updatedLevels.delete(levelId); updatedLevels.delete(levelId);
// Jika ini adalah POLDA yang di-unchecklist, unchecklist juga semua polres di bawahnya // Jika ini adalah POLDA yang di-unchecklist, unchecklist juga semua polres di bawahnya
const poldaItem = listDest.find((item: any) => Number(item.id) === levelId) as any; const poldaItem = listDest.find(
if (poldaItem && poldaItem.subDestination && poldaItem.name !== "SATKER POLRI") { (item: any) => Number(item.id) === levelId
) as any;
if (
poldaItem &&
poldaItem.subDestination &&
poldaItem.name !== "SATKER POLRI"
) {
poldaItem.subDestination.forEach((polres: any) => { poldaItem.subDestination.forEach((polres: any) => {
updatedLevels.delete(Number(polres.id)); updatedLevels.delete(Number(polres.id));
}); });
@ -736,7 +821,9 @@ export default function FormImageDetail() {
updatedLevels.add(levelId); updatedLevels.add(levelId);
// Jika ini adalah SATKER POLRI yang di-checklist, checklist juga semua sub-item di bawahnya // Jika ini adalah SATKER POLRI yang di-checklist, checklist juga semua sub-item di bawahnya
const satkerItem = listDest.find((item: any) => Number(item.id) === levelId) as any; const satkerItem = listDest.find(
(item: any) => Number(item.id) === levelId
) as any;
if (satkerItem && satkerItem.name === "SATKER POLRI") { if (satkerItem && satkerItem.name === "SATKER POLRI") {
// Checklist semua sub-item di bawah SATKER POLRI (bukan POLRES) // Checklist semua sub-item di bawah SATKER POLRI (bukan POLRES)
satkerItem.subDestination?.forEach((subItem: any) => { satkerItem.subDestination?.forEach((subItem: any) => {
@ -971,7 +1058,9 @@ export default function FormImageDetail() {
} }
// Hanya auto-checklist "all" jika polda, polres, dan mabes ter-checklist // Hanya auto-checklist "all" jika polda, polres, dan mabes ter-checklist
// JANGAN include satker dalam perhitungan auto "all" // JANGAN include satker dalam perhitungan auto "all"
const nonSatkerItems = now.filter(item => item !== "satker" && item !== "all"); const nonSatkerItems = now.filter(
(item) => item !== "satker" && item !== "all"
);
if (nonSatkerItems.length === 3 && !now.includes("all")) { if (nonSatkerItems.length === 3 && !now.includes("all")) {
now.push("all"); now.push("all");
} }
@ -986,7 +1075,9 @@ export default function FormImageDetail() {
temp[index] = now; temp[index] = now;
// Hapus "all" jika tidak semua item ter-checklist // Hapus "all" jika tidak semua item ter-checklist
if (now.includes("all")) { if (now.includes("all")) {
const nonSatkerItems = now.filter(item => item !== "satker" && item !== "all"); const nonSatkerItems = now.filter(
(item) => item !== "satker" && item !== "all"
);
if (nonSatkerItems.length < 3) { if (nonSatkerItems.length < 3) {
const newData = now.filter((b) => b !== "all"); const newData = now.filter((b) => b !== "all");
temp[index] = newData; temp[index] = newData;
@ -1001,7 +1092,11 @@ export default function FormImageDetail() {
}; };
// Fungsi untuk mengupdate checklist levels di modal berdasarkan placement // Fungsi untuk mengupdate checklist levels di modal berdasarkan placement
const updateModalChecklistLevels = (fileIndex: number, placement: string, checked: boolean) => { const updateModalChecklistLevels = (
fileIndex: number,
placement: string,
checked: boolean
) => {
if (!listDest || listDest.length === 0) return; if (!listDest || listDest.length === 0) return;
setFileCheckedLevels((prev) => { setFileCheckedLevels((prev) => {
@ -1032,7 +1127,9 @@ export default function FormImageDetail() {
}); });
} else if (placement === "satker") { } else if (placement === "satker") {
// Checklist SATKER POLRI dan semua sub-item di bawahnya // Checklist SATKER POLRI dan semua sub-item di bawahnya
const satkerItem: any = listDest.find((item: any) => item.name === "SATKER POLRI"); const satkerItem: any = listDest.find(
(item: any) => item.name === "SATKER POLRI"
);
if (satkerItem) { if (satkerItem) {
currentFileLevels.add(Number(satkerItem.id)); currentFileLevels.add(Number(satkerItem.id));
if (satkerItem.subDestination) { if (satkerItem.subDestination) {
@ -1066,7 +1163,9 @@ export default function FormImageDetail() {
}); });
} else if (placement === "satker") { } else if (placement === "satker") {
// Unchecklist SATKER POLRI dan semua sub-item di bawahnya // Unchecklist SATKER POLRI dan semua sub-item di bawahnya
const satkerItem: any = listDest.find((item: any) => item.name === "SATKER POLRI"); const satkerItem: any = listDest.find(
(item: any) => item.name === "SATKER POLRI"
);
if (satkerItem) { if (satkerItem) {
currentFileLevels.delete(Number(satkerItem.id)); currentFileLevels.delete(Number(satkerItem.id));
if (satkerItem.subDestination) { if (satkerItem.subDestination) {
@ -1439,13 +1538,19 @@ export default function FormImageDetail() {
<DialogContent className="max-h-[90vh] max-w-[95vw] lg:max-w-[1200px]"> <DialogContent className="max-h-[90vh] max-w-[95vw] lg:max-w-[1200px]">
<DialogHeader className="border-b border-gray-200 pb-4"> <DialogHeader className="border-b border-gray-200 pb-4">
<DialogTitle className="text-xl font-semibold"> <DialogTitle className="text-xl font-semibold">
{t("leave-comment", { defaultValue: "Kelola Persetujuan Konten" })} {t("leave-comment", {
defaultValue: "Kelola Persetujuan Konten",
})}
</DialogTitle> </DialogTitle>
</DialogHeader> </DialogHeader>
<div className="flex flex-col gap-4 max-h-[40vh] overflow-y-auto p-1"> <div className="flex flex-col gap-4 max-h-[40vh] overflow-y-auto p-1">
{status == "2" && files?.map((file, index) => ( {status == "2" &&
<div key={file.id} className="border border-gray-200 rounded-lg p-4 bg-gray-50"> files?.map((file, index) => (
<div
key={file.id}
className="border border-gray-200 rounded-lg p-4 bg-gray-50"
>
{/* Header File */} {/* Header File */}
<div className="flex items-center justify-between mb-4 bg-white p-3 rounded-md border"> <div className="flex items-center justify-between mb-4 bg-white p-3 rounded-md border">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
@ -1461,8 +1566,19 @@ export default function FormImageDetail() {
/> />
</div> </div>
<div> <div>
<h4 className="font-medium text-gray-900 mb-1">File {index + 1}</h4> <h4 className="font-medium text-gray-900 mb-1">
<p className="text-sm text-gray-600 break-all">{file.fileName}</p> File {index + 1}
</h4>
<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>
</div> </div>
<button <button
@ -1470,42 +1586,71 @@ export default function FormImageDetail() {
className="p-2 hover:bg-red-50 rounded-full transition-colors" className="p-2 hover:bg-red-50 rounded-full transition-colors"
title="Hapus file" title="Hapus file"
> >
<Icon icon="humbleicons:times" color="red" width={20} height={20} /> <Icon
icon="humbleicons:times"
color="red"
width={20}
height={20}
/>
</button> </button>
</div> </div>
{/* Section Pengaturan Distribusi */} {/* Section Pengaturan Distribusi */}
{isUserMabesApprover ? (
<div className="bg-white rounded-md p-4 border"> <div className="bg-white rounded-md p-4 border">
<h5 className="font-medium text-gray-900 mb-4 flex items-center gap-2"> <h5 className="font-medium text-gray-900 mb-4 flex items-center gap-2">
<Icon icon="material-symbols:settings-outline" width={18} height={18} /> <Icon
icon="material-symbols:settings-outline"
width={18}
height={18}
/>
Pengaturan Distribusi Pengaturan Distribusi
</h5> </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">Tingkat Distribusi:</p> <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"> <div className="grid grid-cols-2 md:grid-cols-4 gap-3">
{[ {[
{ key: "semua", label: "Semua" }, { key: "semua", label: "Semua" },
{ key: "nasional", label: "Nasional" }, { key: "nasional", label: "Nasional" },
{ key: "wilayah", label: "Wilayah" }, { key: "wilayah", label: "Wilayah" },
{ key: "international", label: "Internasional" }, {
key: "international",
label: "Internasional",
},
].map((item, idx) => ( ].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"> <div
key={item.key}
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}`}
checked={fileUnitSelections[index]?.[item.key as keyof typeof unitSelection] || false} checked={
fileUnitSelections[index]?.[
item.key as keyof typeof unitSelection
] || false
}
onCheckedChange={(value) => { onCheckedChange={(value) => {
handleFileUnitChange( handleFileUnitChange(
index, index,
item.key as keyof typeof unitSelection, item.key as keyof typeof unitSelection,
value as boolean value as boolean
); );
setupPlacement(index, item.key, Boolean(value)); setupPlacement(
index,
item.key,
Boolean(value)
);
}} }}
/> />
<Label htmlFor={`${item.key}-${index}`} className="text-sm font-medium cursor-pointer"> <Label
htmlFor={`${item.key}-${index}`}
className="text-sm font-medium cursor-pointer"
>
{item.label} {item.label}
</Label> </Label>
</div> </div>
@ -1516,7 +1661,9 @@ export default function FormImageDetail() {
{/* Detail Wilayah */} {/* Detail Wilayah */}
{fileUnitSelections[index]?.wilayah && ( {fileUnitSelections[index]?.wilayah && (
<div className="border-t border-gray-200 pt-2"> <div className="border-t border-gray-200 pt-2">
<p className="text-sm font-medium text-gray-700 mb-2">Detail Wilayah:</p> <p className="text-sm font-medium text-gray-700 mb-2">
Detail Wilayah:
</p>
{/* Checkbox Sub-kategori dengan tombol Kustom sejajar */} {/* Checkbox Sub-kategori dengan tombol Kustom sejajar */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-3"> <div className="grid grid-cols-1 md:grid-cols-4 gap-3">
@ -1525,20 +1672,34 @@ export default function FormImageDetail() {
{ key: "polres", label: "POLRES" }, { key: "polres", label: "POLRES" },
{ key: "satker", label: "SATKER" }, { key: "satker", label: "SATKER" },
].map((item, idx) => ( ].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"> <div
key={item.key}
className="flex items-center gap-2 p-3 border border-gray-200 rounded-md hover:bg-gray-50"
>
<Checkbox <Checkbox
id={`${item.key}-${index}`} id={`${item.key}-${index}`}
checked={fileUnitSelections[index]?.[item.key as keyof typeof unitSelection] || false} checked={
fileUnitSelections[index]?.[
item.key as keyof typeof unitSelection
] || false
}
onCheckedChange={(value) => { onCheckedChange={(value) => {
handleFileUnitChange( handleFileUnitChange(
index, index,
item.key as keyof typeof unitSelection, item.key as keyof typeof unitSelection,
value as boolean value as boolean
); );
setupPlacement(index, item.key, Boolean(value)); setupPlacement(
index,
item.key,
Boolean(value)
);
}} }}
/> />
<Label htmlFor={`${item.key}-${index}`} className="text-sm font-medium cursor-pointer"> <Label
htmlFor={`${item.key}-${index}`}
className="text-sm font-medium cursor-pointer"
>
{item.label} {item.label}
</Label> </Label>
</div> </div>
@ -1548,9 +1709,19 @@ export default function FormImageDetail() {
<div className="flex items-center justify-center p-3"> <div className="flex items-center justify-center p-3">
<Dialog> <Dialog>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button variant="outline" size="sm" className="gap-2"> <Button
<Icon icon="material-symbols:tune" width={16} height={16} /> variant="outline"
{t("custom", { defaultValue: "Kustom" })} size="sm"
className="gap-2"
>
<Icon
icon="material-symbols:tune"
width={16}
height={16}
/>
{t("custom", {
defaultValue: "Kustom",
})}
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent className="max-w-[95vw] lg:max-w-[1400px] max-h-[90vh]"> <DialogContent className="max-w-[95vw] lg:max-w-[1400px] max-h-[90vh]">
@ -1561,57 +1732,110 @@ export default function FormImageDetail() {
</DialogHeader> </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"> <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) => ( {listDest.map((polda: any) => (
<div key={polda.id} className="border border-gray-200 rounded-lg p-2 bg-white hover:shadow-sm transition-shadow"> <div
key={polda.id}
className="border border-gray-200 rounded-lg p-2 bg-white hover:shadow-sm transition-shadow"
>
{/* Header POLDA */} {/* Header POLDA */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<Label className="flex items-center gap-3 flex-1 cursor-pointer"> <Label className="flex items-center gap-3 flex-1 cursor-pointer">
<Checkbox <Checkbox
checked={fileCheckedLevels[index]?.has(Number(polda.id)) || false} checked={
onCheckedChange={() => handleFileCheckboxChangePlacement(index, Number(polda.id))} 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> <span className="font-semibold text-gray-900 text-sm">
{polda.name}
</span>
</Label> </Label>
{polda.subDestination && ( {polda.subDestination && (
<button <button
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
toggleExpand(polda.id); toggleExpand(
polda.id
);
}} }}
className="p-1 hover:bg-gray-100 rounded-md transition-colors" className="p-1 hover:bg-gray-100 rounded-md transition-colors"
> >
<Icon <Icon
icon={expandedPolda[polda.id] ? "mdi:chevron-up" : "mdi:chevron-down"} icon={
width={16} height={16} expandedPolda[
polda.id
]
? "mdi:chevron-up"
: "mdi:chevron-down"
}
width={16}
height={16}
/> />
</button> </button>
)} )}
</div> </div>
{/* Sub-items */} {/* Sub-items */}
{polda.subDestination && expandedPolda[polda.id] && ( {polda.subDestination &&
expandedPolda[polda.id] && (
<div className="max-h-[200px] overflow-y-auto border-t border-gray-100 pt-2"> <div className="max-h-[200px] overflow-y-auto border-t border-gray-100 pt-2">
{/* Tombol Pilih Semua untuk sub-items */} {/* Tombol Pilih Semua untuk sub-items */}
<div className="mb-2 flex justify-start"> <div className="mb-2 flex justify-start">
{(() => { {(() => {
const allSubItemsChecked = polda.subDestination?.every((sub: any) => const allSubItemsChecked =
fileCheckedLevels[index]?.has(Number(sub.id)) polda.subDestination?.every(
(sub: any) =>
fileCheckedLevels[
index
]?.has(
Number(
sub.id
)
)
); );
return ( return (
<Button <Button
size="sm" size="sm"
variant="outline" variant="outline"
className="text-xs h-6 px-2" className="text-xs h-6 px-2"
onClick={() => handleSelectAllSubItems(index, polda)} onClick={() =>
handleSelectAllSubItems(
index,
polda
)
}
> >
{allSubItemsChecked ? ( {allSubItemsChecked ? (
<> <>
<Icon icon="material-symbols:check-indeterminate-small" width={12} height={12} className="mr-1" /> <Icon
icon="material-symbols:check-indeterminate-small"
width={12}
height={
12
}
className="mr-1"
/>
Batal Semua Batal Semua
</> </>
) : ( ) : (
<> <>
<Icon icon="material-symbols:check-all" width={12} height={12} className="mr-1" /> <Icon
icon="material-symbols:check-all"
width={12}
height={
12
}
className="mr-1"
/>
Pilih Semua Pilih Semua
</> </>
)} )}
@ -1620,15 +1844,37 @@ export default function FormImageDetail() {
})()} })()}
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
{polda.subDestination.map((sub: any) => ( {polda.subDestination.map(
<Label key={sub.id} className="flex items-center gap-2 p-2 rounded-md hover:bg-gray-50 transition-colors cursor-pointer text-xs"> (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 <Checkbox
checked={fileCheckedLevels[index]?.has(Number(sub.id)) || false} checked={
onCheckedChange={() => handleFileCheckboxChangePlacement(index, Number(sub.id))} fileCheckedLevels[
index
]?.has(
Number(
sub.id
)
) || false
}
onCheckedChange={() =>
handleFileCheckboxChangePlacement(
index,
Number(
sub.id
)
)
}
/> />
<span className="text-gray-700">{sub.name}</span> <span className="text-gray-700">
{sub.name}
</span>
</Label> </Label>
))} )
)}
</div> </div>
</div> </div>
)} )}
@ -1638,12 +1884,16 @@ export default function FormImageDetail() {
<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", { defaultValue: "Batal" })} {t("cancel", {
defaultValue: "Batal",
})}
</Button> </Button>
</DialogClose> </DialogClose>
<DialogClose asChild> <DialogClose asChild>
<Button> <Button>
{t("save", { defaultValue: "Simpan" })} {t("save", {
defaultValue: "Simpan",
})}
</Button> </Button>
</DialogClose> </DialogClose>
</div> </div>
@ -1655,6 +1905,9 @@ export default function FormImageDetail() {
)} )}
</div> </div>
</div> </div>
) : (
""
)}
</div> </div>
))} ))}
</div> </div>
@ -1663,7 +1916,10 @@ export default function FormImageDetail() {
<div className="border-t border-gray-200 pt-6"> <div className="border-t border-gray-200 pt-6">
<div className="space-y-4"> <div className="space-y-4">
<div> <div>
<Label htmlFor="comment" className="text-sm font-medium text-gray-900 mb-2 block"> <Label
htmlFor="comment"
className="text-sm font-medium text-gray-900 mb-2 block"
>
Komentar / Catatan: Komentar / Catatan:
</Label> </Label>
<Textarea <Textarea
@ -1679,17 +1935,23 @@ export default function FormImageDetail() {
<div className="space-y-3"> <div className="space-y-3">
{status == "3" || status == "4" ? ( {status == "3" || status == "4" ? (
<div> <div>
<p className="text-sm font-medium text-gray-700 mb-2">Template Komentar Penolakan:</p> <p className="text-sm font-medium text-gray-700 mb-2">
Template Komentar Penolakan:
</p>
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
{[ {[
"Kualitas media kurang baik", "Kualitas media kurang baik",
"Deskripsi kurang lengkap", "Deskripsi kurang lengkap",
"Judul kurang tepat" "Judul kurang tepat",
].map((template) => ( ].map((template) => (
<Button <Button
key={template} key={template}
size="sm" size="sm"
variant={description === template ? "default" : "outline"} variant={
description === template
? "default"
: "outline"
}
className="text-xs" className="text-xs"
onClick={() => setDescription(template)} onClick={() => setDescription(template)}
> >
@ -1700,22 +1962,27 @@ export default function FormImageDetail() {
</div> </div>
) : ( ) : (
<div> <div>
<p className="text-sm font-medium text-gray-700 mb-2">Template Komentar Persetujuan:</p> <p className="text-sm font-medium text-gray-700 mb-2">
Template Komentar Persetujuan:
</p>
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
{[ {["Konten sangat bagus", "Konten menarik"].map(
"Konten sangat bagus", (template) => (
"Konten menarik"
].map((template) => (
<Button <Button
key={template} key={template}
size="sm" size="sm"
variant={description === template ? "default" : "outline"} variant={
description === template
? "default"
: "outline"
}
className="text-xs" className="text-xs"
onClick={() => setDescription(template)} onClick={() => setDescription(template)}
> >
{template} {template}
</Button> </Button>
))} )
)}
</div> </div>
</div> </div>
)} )}

View File

@ -191,6 +191,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,
@ -1266,6 +1272,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
@ -1283,6 +1296,7 @@ export default function FormTeksDetail() {
</div> </div>
{/* Section Pengaturan Distribusi */} {/* Section Pengaturan Distribusi */}
{isUserMabesApprover ? (
<div className="bg-white rounded-md p-4 border"> <div className="bg-white rounded-md p-4 border">
<h5 className="font-medium text-gray-900 mb-4 flex items-center gap-2"> <h5 className="font-medium text-gray-900 mb-4 flex items-center gap-2">
<Icon <Icon
@ -1449,7 +1463,9 @@ export default function FormTeksDetail() {
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
toggleExpand(polda.id); toggleExpand(
polda.id
);
}} }}
className="p-1 hover:bg-gray-100 rounded-md transition-colors" className="p-1 hover:bg-gray-100 rounded-md transition-colors"
> >
@ -1481,7 +1497,9 @@ export default function FormTeksDetail() {
fileCheckedLevels[ fileCheckedLevels[
index index
]?.has( ]?.has(
Number(sub.id) Number(
sub.id
)
) )
); );
return ( return (
@ -1501,7 +1519,9 @@ export default function FormTeksDetail() {
<Icon <Icon
icon="material-symbols:check-indeterminate-small" icon="material-symbols:check-indeterminate-small"
width={12} width={12}
height={12} height={
12
}
className="mr-1" className="mr-1"
/> />
Batal Semua Batal Semua
@ -1511,7 +1531,9 @@ export default function FormTeksDetail() {
<Icon <Icon
icon="material-symbols:check-all" icon="material-symbols:check-all"
width={12} width={12}
height={12} height={
12
}
className="mr-1" className="mr-1"
/> />
Pilih Semua Pilih Semua
@ -1583,6 +1605,9 @@ export default function FormTeksDetail() {
)} )}
</div> </div>
</div> </div>
) : (
""
)}
</div> </div>
))} ))}
</div> </div>

View File

@ -190,6 +190,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,
@ -1283,6 +1289,7 @@ export default function FormVideoDetail() {
</div> </div>
{/* Section Pengaturan Distribusi */} {/* Section Pengaturan Distribusi */}
{isUserMabesApprover ? (
<div className="bg-white rounded-md p-4 border"> <div className="bg-white rounded-md p-4 border">
<h5 className="font-medium text-gray-900 mb-4 flex items-center gap-2"> <h5 className="font-medium text-gray-900 mb-4 flex items-center gap-2">
<Icon <Icon
@ -1449,7 +1456,9 @@ export default function FormVideoDetail() {
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
toggleExpand(polda.id); toggleExpand(
polda.id
);
}} }}
className="p-1 hover:bg-gray-100 rounded-md transition-colors" className="p-1 hover:bg-gray-100 rounded-md transition-colors"
> >
@ -1481,7 +1490,9 @@ export default function FormVideoDetail() {
fileCheckedLevels[ fileCheckedLevels[
index index
]?.has( ]?.has(
Number(sub.id) Number(
sub.id
)
) )
); );
return ( return (
@ -1501,7 +1512,9 @@ export default function FormVideoDetail() {
<Icon <Icon
icon="material-symbols:check-indeterminate-small" icon="material-symbols:check-indeterminate-small"
width={12} width={12}
height={12} height={
12
}
className="mr-1" className="mr-1"
/> />
Batal Semua Batal Semua
@ -1511,7 +1524,9 @@ export default function FormVideoDetail() {
<Icon <Icon
icon="material-symbols:check-all" icon="material-symbols:check-all"
width={12} width={12}
height={12} height={
12
}
className="mr-1" className="mr-1"
/> />
Pilih Semua Pilih Semua
@ -1583,6 +1598,9 @@ export default function FormVideoDetail() {
)} )}
</div> </div>
</div> </div>
) : (
""
)}
</div> </div>
))} ))}
</div> </div>

View File

@ -533,16 +533,17 @@ const HeroNew = (props: { group?: string }) => {
<div className="relative w-full"> <div className="relative w-full">
{content && content.length > 0 && ( {content && content.length > 0 && (
<>
<Swiper <Swiper
modules={[Autoplay, Navigation]} modules={[Autoplay, Navigation]}
autoplay={{ autoplay={{
delay: 10000, delay: 10000,
disableOnInteraction: false, disableOnInteraction: false,
}} }}
loop={true} loop
navigation={{ navigation={{
nextEl: ".swiper-button-next", nextEl: ".hero-next",
prevEl: ".swiper-button-prev", prevEl: ".hero-prev",
}} }}
className="w-full" className="w-full"
> >
@ -580,29 +581,39 @@ const HeroNew = (props: { group?: string }) => {
<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">
{list?.title}
</h2>
<p className="text-[9px] lg:text-sm mt-2"> <p className="text-[9px] lg:text-sm mt-2">
{formatDateToIndonesian(new Date(list?.createdAt))}{" "} {formatDateToIndonesian(new Date(list?.createdAt))}{" "}
{list?.timezone || "WIB"} | 👁 {list?.clickCount} {list?.timezone || "WIB"} | 👁 {list?.clickCount}
</p> </p>
</Link> </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"
/>{" "}
</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> </div>
</SwiperSlide> </SwiperSlide>
))} ))}
</Swiper> </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> </div>