update : image detail approval

This commit is contained in:
hanif salafi 2025-08-21 10:38:38 +07:00
parent 9fc973d151
commit 82bf3a8709
1 changed files with 313 additions and 83 deletions

View File

@ -171,11 +171,16 @@ export default function FormImageDetail() {
satker: false, satker: false,
}); });
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [checkedLevels, setCheckedLevels] = useState<any>(new Set()); const [checkedLevels, setCheckedLevels] = useState<Set<number>>(new Set());
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([{}]); const [expandedPolda, setExpandedPolda] = useState<Record<number, boolean>>({});
// State untuk melacak apakah perubahan berasal dari checkbox utama
const [isUpdatingFromMainCheckbox, setIsUpdatingFromMainCheckbox] = useState(false);
// State untuk melacak jenis perubahan spesifik
const [mainCheckboxChangeType, setMainCheckboxChangeType] = useState<string>("");
const [wilayahPublish, setWilayahPublish] = React.useState({ const [wilayahPublish, setWilayahPublish] = React.useState({
semua: false, semua: false,
nasional: false, nasional: false,
@ -240,30 +245,175 @@ export default function FormImageDetail() {
fetchPoldaPolres(); fetchPoldaPolres();
}, []); }, []);
// useEffect untuk sinkronisasi checkbox modal dengan checkbox utama
useEffect(() => { useEffect(() => {
const updated = new Set(checkedLevels); if (listDest.length > 0 && isUpdatingFromMainCheckbox && mainCheckboxChangeType) {
syncModalWithMainCheckbox();
}
}, [isUpdatingFromMainCheckbox, mainCheckboxChangeType]);
if (unitSelection.polda) { // useEffect untuk update checkbox utama ketika pilihan modal berubah
listDest.forEach((polda) => { useEffect(() => {
updated.add(polda.id); // hanya id polda if (!isUpdatingFromMainCheckbox && listDest.length > 0) {
updateMainCheckboxFromModal();
}
}, [checkedLevels, isUpdatingFromMainCheckbox]);
// Fungsi untuk update checkbox utama berdasarkan checkbox modal
const updateMainCheckboxFromModal = () => {
if (!isUpdatingFromMainCheckbox && listDest.length > 0) {
// Hitung item yang dipilih berdasarkan checkedLevels
const checkedPoldaCount = listDest.filter((item: any) =>
item.levelNumber === 2 &&
item.name !== "SATKER POLRI" &&
checkedLevels.has(Number(item.id))
).length;
const checkedPolresCount = listDest.reduce((total: number, item: any) => {
if (item.subDestination && item.name !== "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;
}, 0);
const satkerItem: any = listDest.find((item: any) => item.name === "SATKER POLRI");
const checkedSatkerCount = satkerItem ? (
(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
const hasSelectedPolda = checkedPoldaCount > 0;
const hasSelectedPolres = checkedPolresCount > 0;
const hasSelectedSatker = checkedSatkerCount > 0;
// Update unitSelection berdasarkan yang dipilih di modal
setUnitSelection(prev => {
const newState = { ...prev };
// Update individual checkboxes
newState.polda = hasSelectedPolda;
newState.polres = hasSelectedPolres;
newState.satker = hasSelectedSatker;
// Update checkbox "semua" berdasarkan semua checkbox yang aktif
newState.semua = newState.nasional && newState.wilayah && newState.international && hasSelectedPolda && hasSelectedPolres && hasSelectedSatker;
return newState;
}); });
} }
};
if (unitSelection.polres) { // Fungsi untuk sinkronisasi checkbox modal dengan checkbox utama
listDest.forEach((polda) => { const syncModalWithMainCheckbox = () => {
polda?.subDestination?.forEach((polres: any) => { if (isUpdatingFromMainCheckbox) {
updated.add(polres.id); // hanya id polres const newCheckedLevels = new Set(checkedLevels);
// Handle checklist actions - menambahkan semua item ke modal
if (mainCheckboxChangeType === "polda_checked") {
// Checklist semua polda
listDest.forEach((item: any) => {
if (item.levelNumber === 2 && item.name !== "SATKER POLRI") {
newCheckedLevels.add(Number(item.id));
}
}); });
}); } else if (mainCheckboxChangeType === "polres_checked") {
} // Checklist semua polres, tapi hanya yang poldanya sudah di-checklist
// Jangan checklist sub-item SATKER POLRI
listDest.forEach((item: any) => {
if (item.levelNumber === 2 && item.name !== "SATKER POLRI" && newCheckedLevels.has(Number(item.id))) {
if (item.subDestination) {
item.subDestination.forEach((polres: any) => {
newCheckedLevels.add(Number(polres.id));
});
}
}
});
setCheckedLevels(updated); // Tidak perlu menghapus SATKER ketika POLRES di-checklist
}, [unitSelection.polda, unitSelection.polres, listDest]); // Biarkan keduanya bisa aktif bersamaan
// SATKER dan POLRES adalah konsep yang berbeda:
// - SATKER: unit-unit seperti ITWASUM, BAINTELKAM, dll.
// - POLRES: unit-unit seperti POLRES METRO JAKARTA PUSAT, dll.
} else if (mainCheckboxChangeType === "satker_checked") {
// Checklist satker
const satkerItem: any = listDest.find((item: any) => item.name === "SATKER POLRI");
if (satkerItem) {
newCheckedLevels.add(Number(satkerItem.id));
// Checklist semua sub-item yang ada di bawah SATKER (bukan POLRES)
if (satkerItem.subDestination) {
satkerItem.subDestination.forEach((sub: any) => {
newCheckedLevels.add(Number(sub.id));
});
}
}
} else if (mainCheckboxChangeType === "semua_checked") {
// Checklist semua item
listDest.forEach((item: any) => {
newCheckedLevels.add(Number(item.id));
// Checklist semua sub-item di bawah setiap item
if (item.subDestination) {
item.subDestination.forEach((sub: any) => {
newCheckedLevels.add(Number(sub.id));
});
}
});
}
// Handle unchecklist actions - menghapus item dari modal
else if (mainCheckboxChangeType === "polres_unchecked") {
// Clear polres dari checkedLevels, tapi jangan hapus sub-item SATKER POLRI
listDest.forEach((item: any) => {
if (item.subDestination && item.name !== "SATKER POLRI") {
item.subDestination.forEach((polres: any) => {
newCheckedLevels.delete(Number(polres.id));
});
}
});
} else if (mainCheckboxChangeType === "polda_unchecked") {
// Clear polda dan polres dari checkedLevels, tapi jangan hapus SATKER POLRI
listDest.forEach((item: any) => {
if (item.levelNumber === 2 && item.name !== "SATKER POLRI") {
newCheckedLevels.delete(Number(item.id));
if (item.subDestination) {
item.subDestination.forEach((polres: any) => {
newCheckedLevels.delete(Number(polres.id));
});
}
}
});
} else if (mainCheckboxChangeType === "satker_unchecked") {
// Clear satker dan semua sub-item di bawahnya dari checkedLevels
const satkerItem: any = listDest.find((item: any) => item.name === "SATKER POLRI");
if (satkerItem) {
newCheckedLevels.delete(Number(satkerItem.id));
if (satkerItem.subDestination) {
satkerItem.subDestination.forEach((sub: any) => {
newCheckedLevels.delete(Number(sub.id));
});
}
}
} else if (mainCheckboxChangeType === "semua_unchecked") {
// Clear semua
newCheckedLevels.clear();
}
setCheckedLevels(newCheckedLevels);
// Reset flag setelah sinkronisasi selesai
setIsUpdatingFromMainCheckbox(false);
setMainCheckboxChangeType("");
}
};
const handleUnitChange = ( const handleUnitChange = (
key: keyof typeof unitSelection, key: keyof typeof unitSelection,
value: boolean value: boolean
) => { ) => {
// Set flag bahwa perubahan berasal dari checkbox utama
setIsUpdatingFromMainCheckbox(true);
setMainCheckboxChangeType(key + (value ? "_checked" : "_unchecked"));
if (key === "semua") { if (key === "semua") {
// Jika klik Semua, set semua value ke true/false // Jika klik Semua, set semua value ke true/false
const newState = { const newState = {
@ -277,6 +427,28 @@ export default function FormImageDetail() {
}; };
setUnitSelection(newState); setUnitSelection(newState);
} else { } else {
// Validasi khusus untuk POLRES
if (key === "polres" && value) {
// Cek apakah ada POLDA yang sudah dipilih di modal
const hasSelectedPolda = listDest.some((item: any) =>
item.levelNumber === 2 &&
item.name !== "SATKER POLRI" &&
checkedLevels.has(Number(item.id))
);
if (!hasSelectedPolda) {
// Jika tidak ada POLDA yang dipilih, tampilkan peringatan dan batalkan
alert("Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES.");
// Reset flag karena perubahan dibatalkan
setIsUpdatingFromMainCheckbox(false);
setMainCheckboxChangeType("");
return; // Batalkan perubahan
}
}
// Tidak ada validasi khusus untuk SATKER dan POLRES
// Keduanya bisa aktif bersamaan
// Update salah satu saja // Update salah satu saja
const updatedSelection = { const updatedSelection = {
...unitSelection, ...unitSelection,
@ -326,12 +498,38 @@ export default function FormImageDetail() {
}; };
const handleCheckboxChangePlacement = (levelId: number) => { const handleCheckboxChangePlacement = (levelId: number) => {
setCheckedLevels((prev: any) => { setCheckedLevels((prev: Set<number>) => {
const updatedLevels = new Set(prev); const updatedLevels = new Set<number>(prev);
if (updatedLevels.has(levelId)) { const isCurrentlyChecked = updatedLevels.has(levelId);
if (isCurrentlyChecked) {
updatedLevels.delete(levelId); updatedLevels.delete(levelId);
// 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;
if (poldaItem && poldaItem.subDestination && poldaItem.name !== "SATKER POLRI") {
poldaItem.subDestination.forEach((polres: any) => {
updatedLevels.delete(Number(polres.id));
});
}
// Jika ini adalah SATKER POLRI yang di-unchecklist, unchecklist juga semua sub-item di bawahnya
if (poldaItem && poldaItem.name === "SATKER POLRI") {
poldaItem.subDestination?.forEach((subItem: any) => {
updatedLevels.delete(Number(subItem.id));
});
}
} else { } else {
updatedLevels.add(levelId); updatedLevels.add(levelId);
// 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;
if (satkerItem && satkerItem.name === "SATKER POLRI") {
// Checklist semua sub-item di bawah SATKER POLRI (bukan POLRES)
satkerItem.subDestination?.forEach((subItem: any) => {
updatedLevels.add(Number(subItem.id));
});
}
} }
return updatedLevels; return updatedLevels;
}); });
@ -395,7 +593,7 @@ export default function FormImageDetail() {
setupPlacementCheck(details?.files?.length); setupPlacementCheck(details?.files?.length);
if (details?.assignedToLevel) { if (details?.assignedToLevel) {
const levels = new Set( const levels = new Set<number>(
details.assignedToLevel.split(",").map(Number) details.assignedToLevel.split(",").map(Number)
); );
setCheckedLevels(levels); setCheckedLevels(levels);
@ -519,10 +717,23 @@ export default function FormImageDetail() {
if (checked) { if (checked) {
if (placement === "all") { if (placement === "all") {
temp[index] = ["all", "mabes", "polda", "international"]; 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 { } else {
const now = temp[index]; const now = temp[index] || [];
now?.push(placement); if (!now.includes(placement)) {
if (now.length === 3 && !now.includes("all")) { 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"); now.push("all");
} }
temp[index] = now; temp[index] = now;
@ -534,9 +745,13 @@ export default function FormImageDetail() {
const now = temp[index].filter((a) => a !== placement); const now = temp[index].filter((a) => a !== placement);
console.log("now", now); console.log("now", now);
temp[index] = now; temp[index] = now;
if (now.length === 3 && now.includes("all")) { // Hapus "all" jika tidak semua item ter-checklist
const newData = now.filter((b) => b !== "all"); if (now.includes("all")) {
temp[index] = newData; const nonSatkerItems = now.filter(item => item !== "satker" && item !== "all");
if (nonSatkerItems.length < 3) {
const newData = now.filter((b) => b !== "all");
temp[index] = newData;
}
} }
} }
} }
@ -1031,45 +1246,30 @@ export default function FormImageDetail() {
</DialogTitle> </DialogTitle>
</DialogHeader> </DialogHeader>
<div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto"> <div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
{listDest.map((polda: any) => { {listDest.map((polda: any) => (
const poldaChecked =
unitSelection.polda;
const polresChecked =
unitSelection.polres;
const isPoldaDisabled =
poldaChecked;
const isPolresDisabled =
polresChecked;
return (
<div <div
key={polda.id} key={polda.id}
className="border p-2" className="border p-2"
> >
<Label className="flex items-center"> <div className="flex items-center">
<Checkbox <Label className="flex items-center flex-1">
checked={ <Checkbox
poldaChecked || checked={checkedLevels.has(Number(polda.id))}
checkedLevels.has( onCheckedChange={() =>
polda.id handleCheckboxChangePlacement(Number(polda.id))
) }
} className="mr-3"
disabled={isPoldaDisabled} />
onCheckedChange={() => { {polda.name}
if (isPoldaDisabled) </Label>
return;
handleCheckboxChangePlacement(
polda.id
);
}}
className="mr-3"
/>
{polda.name}
<button <button
onClick={() => onClick={(e) => {
toggleExpand(polda.id) e.preventDefault();
} e.stopPropagation();
className="ml-2 focus:outline-none" toggleExpand(polda.id);
}}
className="ml-2 focus:outline-none p-1 hover:bg-gray-100 rounded"
type="button"
> >
{expandedPolda[polda.id] ? ( {expandedPolda[polda.id] ? (
<ChevronUp size={16} /> <ChevronUp size={16} />
@ -1077,46 +1277,76 @@ export default function FormImageDetail() {
<ChevronDown size={16} /> <ChevronDown size={16} />
)} )}
</button> </button>
</Label> </div>
{expandedPolda[polda.id] && ( {expandedPolda[polda.id] && (
<div className="ml-6 mt-2"> <div className="ml-6 mt-2">
<Label className="block">
<Checkbox
checked={polda?.subDestination?.every(
(polres: any) =>
checkedLevels.has(Number(polres.id))
)}
onCheckedChange={(isChecked) => {
const updatedLevels = new Set<number>(
checkedLevels
);
// Jika ini adalah SATKER POLRI, checklist juga sub-item di bawahnya
if (polda.name === "SATKER POLRI") {
if (isChecked) {
updatedLevels.add(Number(polda.id));
// Checklist semua sub-item di bawah SATKER POLRI (bukan POLRES)
polda?.subDestination?.forEach((subItem: any) => {
updatedLevels.add(Number(subItem.id));
});
} else {
updatedLevels.delete(Number(polda.id));
// Unchecklist semua sub-item di bawah SATKER POLRI
polda?.subDestination?.forEach((subItem: any) => {
updatedLevels.delete(Number(subItem.id));
});
}
} else {
// Untuk POLDA biasa, checklist/unchecklist polres
polda?.subDestination?.forEach(
(polres: any) => {
if (isChecked) {
updatedLevels.add(Number(polres.id));
} else {
updatedLevels.delete(Number(polres.id));
}
}
);
}
setCheckedLevels(updatedLevels);
}}
className="mr-2"
/>
{polda.name === "SATKER POLRI" ? "Pilih SATKER" : "Pilih Semua"}
</Label>
{polda?.subDestination?.map( {polda?.subDestination?.map(
(polres: any) => ( (polres: any) => (
<Label <Label
key={polres.id} key={polres.id}
className="block mt-1" className="block mt-1"
> >
<Checkbox <Checkbox
checked={ checked={checkedLevels.has(Number(polres.id))}
polresChecked || onCheckedChange={() =>
checkedLevels.has( handleCheckboxChangePlacement(Number(polres.id))
polres.id }
) className="mr-2"
} />
disabled={ {polres.name}
isPolresDisabled
}
onCheckedChange={() => {
if (
isPolresDisabled
)
return;
handleCheckboxChangePlacement(
polres.id
);
}}
className="mr-2"
/>
{polres.name}
</Label> </Label>
) )
)} )}
</div> </div>
)} )}
</div> </div>
); ))}
})}
</div> </div>
</DialogContent> </DialogContent>
</Dialog> </Dialog>