feat: update placement on media
This commit is contained in:
parent
82e8dfafc9
commit
6991c6d9b9
|
|
@ -762,19 +762,26 @@ export default function FormAudioUpdate() {
|
||||||
const getPlacement = () => {
|
const getPlacement = () => {
|
||||||
const temp = [];
|
const temp = [];
|
||||||
for (let i = 0; i < filePlacements?.length; i++) {
|
for (let i = 0; i < filePlacements?.length; i++) {
|
||||||
if (filePlacements[i]?.length !== 0) {
|
const file = files[i] as any;
|
||||||
const now = filePlacements[i];
|
if (file.id && filePlacements[file.id] && filePlacements[file.id].length > 0) {
|
||||||
let nowArr = now?.join(",")?.replaceAll("nasional", "mabes");
|
const now = filePlacements[file.id];
|
||||||
nowArr = nowArr?.replaceAll("wilayah", "polda");
|
const normalizedNow = now.map((item): PlacementType => {
|
||||||
nowArr = nowArr?.replaceAll("semua", "all");
|
const value = String(item);
|
||||||
|
if (value === "nasional") return "mabes";
|
||||||
|
if (value === "wilayah") return "polda";
|
||||||
|
if (value === "semua") return "all";
|
||||||
|
return item as PlacementType;
|
||||||
|
});
|
||||||
|
const uniqueNow = Array.from(new Set(normalizedNow));
|
||||||
|
const nowArr = uniqueNow.join(",");
|
||||||
|
|
||||||
// Dapatkan checked levels untuk file ini
|
// Dapatkan checked levels untuk file ini
|
||||||
const currentFileCheckedLevels = fileCheckedLevels[i]
|
const currentFileCheckedLevels = fileCheckedLevels[file.id]
|
||||||
? Array.from(fileCheckedLevels[i])
|
? Array.from(fileCheckedLevels[file.id])
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
mediaFileId: files[i]?.id,
|
mediaFileId: file.id,
|
||||||
placements: nowArr,
|
placements: nowArr,
|
||||||
customLocationPlacements: currentFileCheckedLevels.join(","),
|
customLocationPlacements: currentFileCheckedLevels.join(","),
|
||||||
};
|
};
|
||||||
|
|
@ -859,12 +866,20 @@ export default function FormAudioUpdate() {
|
||||||
}
|
}
|
||||||
temp[index] = now;
|
temp[index] = now;
|
||||||
} else {
|
} else {
|
||||||
|
// Handle mapping dari UI key ke backend value
|
||||||
|
let placementToAdd = placement;
|
||||||
|
if (placement === "nasional") {
|
||||||
|
placementToAdd = "mabes";
|
||||||
|
} else if (placement === "semua") {
|
||||||
|
placementToAdd = "all";
|
||||||
|
}
|
||||||
|
|
||||||
const now = temp[index] || [];
|
const now = temp[index] || [];
|
||||||
if (!now.includes(placement)) {
|
if (!now.includes(placementToAdd)) {
|
||||||
now.push(placement);
|
now.push(placementToAdd);
|
||||||
}
|
}
|
||||||
// Auto-checklist "all" jika nasional, wilayah, dan international ter-checklist
|
// Auto-checklist "all" jika nasional, wilayah, dan international ter-checklist
|
||||||
const requiredItems = ["nasional", "wilayah", "international"];
|
const requiredItems = ["mabes", "wilayah", "international"];
|
||||||
const hasAllRequired = requiredItems.every(item => now.includes(item));
|
const hasAllRequired = requiredItems.every(item => now.includes(item));
|
||||||
if (hasAllRequired && !now.includes("all")) {
|
if (hasAllRequired && !now.includes("all")) {
|
||||||
now.push("all");
|
now.push("all");
|
||||||
|
|
@ -921,14 +936,22 @@ export default function FormAudioUpdate() {
|
||||||
const now = temp[index]?.filter((a) => a !== "satker");
|
const now = temp[index]?.filter((a) => a !== "satker");
|
||||||
temp[index] = now;
|
temp[index] = now;
|
||||||
} else {
|
} else {
|
||||||
const now = temp[index]?.filter((a) => a !== placement);
|
// Handle mapping dari UI key ke backend value
|
||||||
|
let placementToRemove = placement;
|
||||||
|
if (placement === "nasional") {
|
||||||
|
placementToRemove = "mabes";
|
||||||
|
} else if (placement === "semua") {
|
||||||
|
placementToRemove = "all";
|
||||||
|
}
|
||||||
|
|
||||||
|
const now = temp[index]?.filter((a) => a !== placementToRemove);
|
||||||
temp[index] = now;
|
temp[index] = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hapus "all" jika tidak semua item ter-checklist
|
// Hapus "all" jika tidak semua item ter-checklist
|
||||||
const currentNow = temp[index] || [];
|
const currentNow = temp[index] || [];
|
||||||
if (currentNow.includes("all")) {
|
if (currentNow.includes("all")) {
|
||||||
const requiredItems = ["nasional", "wilayah", "international"];
|
const requiredItems = ["mabes", "wilayah", "international"];
|
||||||
const hasAllRequired = requiredItems.every(item => currentNow.includes(item));
|
const hasAllRequired = requiredItems.every(item => currentNow.includes(item));
|
||||||
if (!hasAllRequired) {
|
if (!hasAllRequired) {
|
||||||
const newData = currentNow.filter((b) => b !== "all");
|
const newData = currentNow.filter((b) => b !== "all");
|
||||||
|
|
@ -1108,9 +1131,11 @@ export default function FormAudioUpdate() {
|
||||||
}
|
}
|
||||||
if (placements.includes("polda")) {
|
if (placements.includes("polda")) {
|
||||||
selection.polda = true;
|
selection.polda = true;
|
||||||
|
selection.wilayah = true; // Auto-check wilayah when polda is present
|
||||||
}
|
}
|
||||||
if (placements.includes("satker")) {
|
if (placements.includes("satker")) {
|
||||||
selection.satker = true;
|
selection.satker = true;
|
||||||
|
selection.wilayah = true; // Auto-check wilayah when satker is present
|
||||||
}
|
}
|
||||||
if (placements.includes("international")) {
|
if (placements.includes("international")) {
|
||||||
selection.international = true;
|
selection.international = true;
|
||||||
|
|
@ -1608,56 +1633,352 @@ export default function FormAudioUpdate() {
|
||||||
</Label>
|
</Label>
|
||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
{files.map((file: any, index: any) => (
|
{files.map((file: any, index: any) => (
|
||||||
<div
|
<div
|
||||||
key={file.id} // Gunakan ID file sebagai key
|
key={file.id}
|
||||||
className="flex justify-between border px-3.5 py-3 my-6 rounded-md"
|
className="flex items-center border p-2 rounded-md"
|
||||||
>
|
|
||||||
<div className="flex gap-3 items-center">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="48"
|
|
||||||
height="48"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
>
|
>
|
||||||
<path
|
<div className="flex flex-wrap gap-3 items-center ">
|
||||||
fill="currentColor"
|
<div className="flex-grow">
|
||||||
d="M14.702 2.226A1 1 0 0 1 16 3.18v6.027a5.5 5.5 0 0 0-1-.184V6.18L8 8.368V15.5a2.5 2.5 0 1 1-1-2V5.368a1 1 0 0 1 .702-.955zM8 7.32l7-2.187V3.18L8 5.368zM5.5 14a1.5 1.5 0 1 0 0 3a1.5 1.5 0 0 0 0-3m13.5.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0m-2.265-.436l-2.994-1.65a.5.5 0 0 0-.741.438v3.3a.5.5 0 0 0 .741.438l2.994-1.65a.5.5 0 0 0 0-.876"
|
<div>
|
||||||
/>
|
<svg
|
||||||
</svg>{" "}
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<div>
|
width="80"
|
||||||
<div className="text-sm text-card-foreground">
|
height="80"
|
||||||
{file.fileName || file.name}
|
viewBox="0 0 20 20"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M14.702 2.226A1 1 0 0 1 16 3.18v6.027a5.5 5.5 0 0 0-1-.184V6.18L8 8.368V15.5a2.5 2.5 0 1 1-1-2V5.368a1 1 0 0 1 .702-.955zM8 7.32l7-2.187V3.18L8 5.368zM5.5 14a1.5 1.5 0 1 0 0 3a1.5 1.5 0 0 0 0-3m13.5.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0m-2.265-.436l-2.994-1.65a.5.5 0 0 0-.741.438v3.3a.5.5 0 0 0 .741.438l2.994-1.65a.5.5 0 0 0 0-.876"
|
||||||
|
/>
|
||||||
|
</svg>{" "}
|
||||||
|
<p className="font-medium">{file.fileName}</p>
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
href={file.url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-blue-500 text-sm"
|
||||||
|
>
|
||||||
|
{t("view-file", {
|
||||||
|
defaultValue: "View File",
|
||||||
|
})}
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs font-light text-muted-foreground">
|
<div className="bg-white rounded-md p-4 border">
|
||||||
{Math.round(file.size / 100) / 10 > 1000 ? (
|
{/* Checkbox Tingkat Utama */}
|
||||||
<>
|
<div className="space-y-4">
|
||||||
{(
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||||
Math.round(file.size / 100) / 10000
|
{[
|
||||||
).toFixed(1)}
|
{ 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
|
||||||
|
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>
|
||||||
|
|
||||||
|
{/* Detail Wilayah */}
|
||||||
|
{fileUnitSelections[index]?.wilayah && isDetailOfRegionShowed && (
|
||||||
|
<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: "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"
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</Label>
|
||||||
|
</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"
|
||||||
|
>
|
||||||
|
<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 SATKER
|
||||||
|
</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>
|
||||||
|
{/* Tombol expand hanya untuk SATKER POLRI */}
|
||||||
|
{polda.name === "SATKER POLRI" && polda.subDestination && (
|
||||||
|
<button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
toggleExpand(
|
||||||
|
polda.id
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon={
|
||||||
|
expandedPolda[
|
||||||
|
polda.id
|
||||||
|
]
|
||||||
|
? "mdi:chevron-up"
|
||||||
|
: "mdi:chevron-down"
|
||||||
|
}
|
||||||
|
width={16}
|
||||||
|
height={16}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sub-items hanya untuk SATKER POLRI */}
|
||||||
|
{polda.name === "SATKER POLRI" && 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
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{(Math.round(file.size / 100) / 10).toFixed(
|
<Icon
|
||||||
1
|
icon="material-symbols:check-all"
|
||||||
)}
|
width={
|
||||||
|
12
|
||||||
|
}
|
||||||
|
height={
|
||||||
|
12
|
||||||
|
}
|
||||||
|
className="mr-1"
|
||||||
|
/>
|
||||||
|
Pilih
|
||||||
|
Semua
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{" kb"}
|
</Button>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
</div>
|
</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>
|
||||||
|
)}
|
||||||
|
|
||||||
<Button
|
</div>
|
||||||
size="icon"
|
)
|
||||||
color="destructive"
|
)}
|
||||||
variant="outline"
|
</div>
|
||||||
className="border-none rounded-full"
|
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
|
||||||
onClick={() => handleDeleteFile(file.id)} // Kirim ID spesifik
|
<DialogClose asChild>
|
||||||
>
|
<Button variant="outline">
|
||||||
<Icon icon="tabler:x" className="h-5 w-5" />
|
{t("cancel", {
|
||||||
|
defaultValue: "Batal",
|
||||||
|
})}
|
||||||
</Button>
|
</Button>
|
||||||
|
</DialogClose>
|
||||||
|
<DialogClose asChild>
|
||||||
|
<Button>Simpan</Button>
|
||||||
|
</DialogClose>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -489,8 +489,8 @@ export default function FormImageUpdate() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Update salah satu saja
|
// Update salah satu saja
|
||||||
currentSelection[key] = value;
|
currentSelection[key] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cek apakah semua selain "semua" sudah dicentang
|
// Cek apakah semua selain "semua" sudah dicentang
|
||||||
|
|
@ -885,9 +885,11 @@ export default function FormImageUpdate() {
|
||||||
}
|
}
|
||||||
if (placements.includes("polda")) {
|
if (placements.includes("polda")) {
|
||||||
selection.polda = true;
|
selection.polda = true;
|
||||||
|
selection.wilayah = true; // Auto-check wilayah when polda is present
|
||||||
}
|
}
|
||||||
if (placements.includes("satker")) {
|
if (placements.includes("satker")) {
|
||||||
selection.satker = true;
|
selection.satker = true;
|
||||||
|
selection.wilayah = true; // Auto-check wilayah when satker is present
|
||||||
}
|
}
|
||||||
if (placements.includes("international")) {
|
if (placements.includes("international")) {
|
||||||
selection.international = true;
|
selection.international = true;
|
||||||
|
|
@ -934,19 +936,26 @@ export default function FormImageUpdate() {
|
||||||
const getPlacement = () => {
|
const getPlacement = () => {
|
||||||
const temp = [];
|
const temp = [];
|
||||||
for (let i = 0; i < filePlacements?.length; i++) {
|
for (let i = 0; i < filePlacements?.length; i++) {
|
||||||
if (filePlacements[i]?.length !== 0) {
|
const file = files[i] as any;
|
||||||
const now = filePlacements[i];
|
if (file.id && filePlacements[file.id] && filePlacements[file.id].length > 0) {
|
||||||
let nowArr = now?.join(",")?.replaceAll("nasional", "mabes");
|
const now = filePlacements[file.id];
|
||||||
nowArr = nowArr?.replaceAll("wilayah", "polda");
|
const normalizedNow = now.map((item): PlacementType => {
|
||||||
nowArr = nowArr?.replaceAll("semua", "all");
|
const value = String(item);
|
||||||
|
if (value === "nasional") return "mabes";
|
||||||
|
if (value === "wilayah") return "polda";
|
||||||
|
if (value === "semua") return "all";
|
||||||
|
return item as PlacementType;
|
||||||
|
});
|
||||||
|
const uniqueNow = Array.from(new Set(normalizedNow));
|
||||||
|
const nowArr = uniqueNow.join(",");
|
||||||
|
|
||||||
// Dapatkan checked levels untuk file ini
|
// Dapatkan checked levels untuk file ini
|
||||||
const currentFileCheckedLevels = fileCheckedLevels[i]
|
const currentFileCheckedLevels = fileCheckedLevels[file.id]
|
||||||
? Array.from(fileCheckedLevels[i])
|
? Array.from(fileCheckedLevels[file.id])
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
mediaFileId: files[i]?.id,
|
mediaFileId: file.id,
|
||||||
placements: nowArr,
|
placements: nowArr,
|
||||||
customLocationPlacements: currentFileCheckedLevels.join(","),
|
customLocationPlacements: currentFileCheckedLevels.join(","),
|
||||||
};
|
};
|
||||||
|
|
@ -1411,9 +1420,9 @@ export default function FormImageUpdate() {
|
||||||
// Ketika satker di-uncheck, hapus satker dari filePlacements
|
// Ketika satker di-uncheck, hapus satker dari filePlacements
|
||||||
const now = temp[index]?.filter((a) => a !== "satker");
|
const now = temp[index]?.filter((a) => a !== "satker");
|
||||||
temp[index] = now;
|
temp[index] = now;
|
||||||
} else {
|
} else {
|
||||||
const now = temp[index]?.filter((a) => a !== placement);
|
const now = temp[index]?.filter((a) => a !== placement);
|
||||||
temp[index] = now;
|
temp[index] = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hapus "all" jika tidak semua item ter-checklist
|
// Hapus "all" jika tidak semua item ter-checklist
|
||||||
|
|
|
||||||
|
|
@ -804,25 +804,31 @@ export default function FormTeksUpdate() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPlacement = () => {
|
const getPlacement = () => {
|
||||||
const temp = [];
|
const temp: Array<{ mediaFileId: number | string; placements: string; customLocationPlacements: string }> = [];
|
||||||
for (let i = 0; i < filePlacements?.length; i++) {
|
for (let i = 0; i < files.length; i++) {
|
||||||
if (filePlacements[i]?.length !== 0) {
|
const file = files[i] as any;
|
||||||
const now = filePlacements[i];
|
const now = filePlacements[i];
|
||||||
let nowArr = now?.join(",")?.replaceAll("nasional", "mabes");
|
if (file?.id && Array.isArray(now) && now.length > 0) {
|
||||||
nowArr = nowArr?.replaceAll("wilayah", "polda");
|
const normalizedNow = now.map((item): PlacementType => {
|
||||||
nowArr = nowArr?.replaceAll("semua", "all");
|
const value = String(item);
|
||||||
|
if (value === "nasional") return "mabes";
|
||||||
|
if (value === "wilayah") return "polda";
|
||||||
|
if (value === "semua") return "all";
|
||||||
|
return item as PlacementType;
|
||||||
|
});
|
||||||
|
const uniqueNow = Array.from(new Set(normalizedNow));
|
||||||
|
const nowArr = uniqueNow.join(",");
|
||||||
|
|
||||||
// Dapatkan checked levels untuk file ini
|
// Dapatkan checked levels untuk file ini (berbasis index)
|
||||||
const currentFileCheckedLevels = fileCheckedLevels[i]
|
const currentFileCheckedLevels = fileCheckedLevels[i]
|
||||||
? Array.from(fileCheckedLevels[i])
|
? Array.from(fileCheckedLevels[i])
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const data = {
|
temp.push({
|
||||||
mediaFileId: files[i]?.id,
|
mediaFileId: file.id,
|
||||||
placements: nowArr,
|
placements: nowArr,
|
||||||
customLocationPlacements: currentFileCheckedLevels.join(","),
|
customLocationPlacements: currentFileCheckedLevels.join(","),
|
||||||
};
|
});
|
||||||
temp.push(data);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return temp;
|
return temp;
|
||||||
|
|
@ -903,12 +909,20 @@ export default function FormTeksUpdate() {
|
||||||
}
|
}
|
||||||
temp[index] = now;
|
temp[index] = now;
|
||||||
} else {
|
} else {
|
||||||
|
// Handle mapping dari UI key ke backend value
|
||||||
|
let placementToAdd = placement;
|
||||||
|
if (placement === "nasional") {
|
||||||
|
placementToAdd = "mabes";
|
||||||
|
} else if (placement === "semua") {
|
||||||
|
placementToAdd = "all";
|
||||||
|
}
|
||||||
|
|
||||||
const now = temp[index] || [];
|
const now = temp[index] || [];
|
||||||
if (!now.includes(placement)) {
|
if (!now.includes(placementToAdd)) {
|
||||||
now.push(placement);
|
now.push(placementToAdd);
|
||||||
}
|
}
|
||||||
// Auto-checklist "all" jika nasional, wilayah, dan international ter-checklist
|
// Auto-checklist "all" jika nasional, wilayah, dan international ter-checklist
|
||||||
const requiredItems = ["nasional", "wilayah", "international"];
|
const requiredItems = ["mabes", "wilayah", "international"];
|
||||||
const hasAllRequired = requiredItems.every(item => now.includes(item));
|
const hasAllRequired = requiredItems.every(item => now.includes(item));
|
||||||
if (hasAllRequired && !now.includes("all")) {
|
if (hasAllRequired && !now.includes("all")) {
|
||||||
now.push("all");
|
now.push("all");
|
||||||
|
|
@ -965,14 +979,22 @@ export default function FormTeksUpdate() {
|
||||||
const now = temp[index]?.filter((a) => a !== "satker");
|
const now = temp[index]?.filter((a) => a !== "satker");
|
||||||
temp[index] = now;
|
temp[index] = now;
|
||||||
} else {
|
} else {
|
||||||
const now = temp[index]?.filter((a) => a !== placement);
|
// Handle mapping dari UI key ke backend value
|
||||||
|
let placementToRemove = placement;
|
||||||
|
if (placement === "nasional") {
|
||||||
|
placementToRemove = "mabes";
|
||||||
|
} else if (placement === "semua") {
|
||||||
|
placementToRemove = "all";
|
||||||
|
}
|
||||||
|
|
||||||
|
const now = temp[index]?.filter((a) => a !== placementToRemove);
|
||||||
temp[index] = now;
|
temp[index] = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hapus "all" jika tidak semua item ter-checklist
|
// Hapus "all" jika tidak semua item ter-checklist
|
||||||
const currentNow = temp[index] || [];
|
const currentNow = temp[index] || [];
|
||||||
if (currentNow.includes("all")) {
|
if (currentNow.includes("all")) {
|
||||||
const requiredItems = ["nasional", "wilayah", "international"];
|
const requiredItems = ["mabes", "wilayah", "international"];
|
||||||
const hasAllRequired = requiredItems.every(item => currentNow.includes(item));
|
const hasAllRequired = requiredItems.every(item => currentNow.includes(item));
|
||||||
if (!hasAllRequired) {
|
if (!hasAllRequired) {
|
||||||
const newData = currentNow.filter((b) => b !== "all");
|
const newData = currentNow.filter((b) => b !== "all");
|
||||||
|
|
@ -1084,11 +1106,10 @@ export default function FormTeksUpdate() {
|
||||||
}));
|
}));
|
||||||
setFiles(formattedFiles);
|
setFiles(formattedFiles);
|
||||||
|
|
||||||
// Inisialisasi filePlacements dari detail
|
// Inisialisasi filePlacements dari detail (biarkan format backend, normalisasi dilakukan saat submit)
|
||||||
const initialFilePlacements: string[][] = details.files.map((file: any) => {
|
const initialFilePlacements: string[][] = details.files.map((file: any) => {
|
||||||
if (file.placements) {
|
if (file.placements) {
|
||||||
const placements = file.placements.split(",").map((p: string) => p.trim());
|
return file.placements.split(",").map((p: string) => p.trim());
|
||||||
return placements;
|
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
|
|
@ -1123,7 +1144,6 @@ export default function FormTeksUpdate() {
|
||||||
if (file.placements) {
|
if (file.placements) {
|
||||||
const placements = file.placements.split(",").map((p: string) => p.trim());
|
const placements = file.placements.split(",").map((p: string) => p.trim());
|
||||||
|
|
||||||
// Map dari format backend ke checkbox
|
|
||||||
if (placements.includes("all")) {
|
if (placements.includes("all")) {
|
||||||
selection.semua = true;
|
selection.semua = true;
|
||||||
selection.nasional = true;
|
selection.nasional = true;
|
||||||
|
|
@ -1132,23 +1152,21 @@ export default function FormTeksUpdate() {
|
||||||
selection.polda = true;
|
selection.polda = true;
|
||||||
selection.satker = true;
|
selection.satker = true;
|
||||||
} else {
|
} else {
|
||||||
if (placements.includes("mabes")) {
|
if (placements.includes("mabes")) selection.nasional = true;
|
||||||
selection.nasional = true;
|
if (placements.includes("international")) selection.international = true;
|
||||||
}
|
if (placements.includes("polda")) selection.polda = true;
|
||||||
if (placements.includes("wilayah")) {
|
if (placements.includes("satker")) selection.satker = true;
|
||||||
|
// Wilayah aktif jika ada "wilayah" ATAU ada polda/satker
|
||||||
|
if (
|
||||||
|
placements.includes("wilayah") ||
|
||||||
|
placements.includes("polda") ||
|
||||||
|
placements.includes("satker")
|
||||||
|
) {
|
||||||
selection.wilayah = true;
|
selection.wilayah = true;
|
||||||
}
|
}
|
||||||
if (placements.includes("polda")) {
|
|
||||||
selection.polda = true;
|
|
||||||
}
|
|
||||||
if (placements.includes("satker")) {
|
|
||||||
selection.satker = true;
|
|
||||||
}
|
|
||||||
if (placements.includes("international")) {
|
|
||||||
selection.international = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return selection;
|
return selection;
|
||||||
});
|
});
|
||||||
setFileUnitSelections(initialFileUnitSelections);
|
setFileUnitSelections(initialFileUnitSelections);
|
||||||
|
|
@ -1240,6 +1258,16 @@ export default function FormTeksUpdate() {
|
||||||
setIsStartUpload(true);
|
setIsStartUpload(true);
|
||||||
setProgressList(progressInfoArr);
|
setProgressList(progressInfoArr);
|
||||||
|
|
||||||
|
// Update file placements terlebih dahulu (sebelum upload)
|
||||||
|
const placementData = getPlacement();
|
||||||
|
if (placementData.length > 0) {
|
||||||
|
const responseFilePlacements = await updateFilePlacements(placementData);
|
||||||
|
if (responseFilePlacements?.error) {
|
||||||
|
error(responseFilePlacements?.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
close();
|
close();
|
||||||
// showProgress();
|
// showProgress();
|
||||||
files.map(async (item: any, index: number) => {
|
files.map(async (item: any, index: number) => {
|
||||||
|
|
@ -1251,12 +1279,6 @@ export default function FormTeksUpdate() {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update file placements
|
|
||||||
const placementData = getPlacement();
|
|
||||||
if (placementData.length > 0) {
|
|
||||||
await updateFilePlacements(placementData);
|
|
||||||
}
|
|
||||||
|
|
||||||
MySwal.fire({
|
MySwal.fire({
|
||||||
title: "Sukses",
|
title: "Sukses",
|
||||||
text: "Data berhasil disimpan.",
|
text: "Data berhasil disimpan.",
|
||||||
|
|
@ -1594,13 +1616,13 @@ export default function FormTeksUpdate() {
|
||||||
</Fragment>
|
</Fragment>
|
||||||
) : null}
|
) : null}
|
||||||
{files.length > 0 && (
|
{files.length > 0 && (
|
||||||
<div className="mt-4 space-y-2">
|
<div className="mt-4">
|
||||||
<Label className="text-lg font-semibold">
|
<Label className="text-md font-semibold">
|
||||||
{" "}
|
{" "}
|
||||||
{t("file-media", { defaultValue: "File Media" })}
|
{t("file-media", { defaultValue: "File Media" })}
|
||||||
</Label>
|
</Label>
|
||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
{files.map((file: any) => (
|
{files.map((file: any, index: any) => (
|
||||||
<div
|
<div
|
||||||
key={file.id}
|
key={file.id}
|
||||||
className="flex items-center border p-2 rounded-md"
|
className="flex items-center border p-2 rounded-md"
|
||||||
|
|
@ -1624,193 +1646,314 @@ export default function FormTeksUpdate() {
|
||||||
})}
|
})}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="bg-white rounded-md p-4 border">
|
||||||
<Label className="flex items-center space-x-2">
|
{/* Checkbox Tingkat Utama */}
|
||||||
<input
|
<div className="space-y-4">
|
||||||
type="checkbox"
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||||
checked={fileUnitSelections[files.indexOf(file)]?.semua || false}
|
{[
|
||||||
onChange={(e) =>
|
{ key: "semua", label: "Semua" },
|
||||||
handleFileUnitChange(
|
{
|
||||||
files.indexOf(file),
|
key: "nasional",
|
||||||
"semua",
|
label: "Nasional",
|
||||||
e.target.checked
|
},
|
||||||
)
|
{ key: "wilayah", label: "Wilayah" },
|
||||||
}
|
{
|
||||||
className="form-checkbox"
|
key: "international",
|
||||||
/>
|
label: "Internasional",
|
||||||
<span>
|
},
|
||||||
{t("all", { defaultValue: "All" })}
|
].map((item, idx) => (
|
||||||
</span>
|
<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[files.indexOf(file)]?.[
|
||||||
|
item.key as keyof typeof unitSelection
|
||||||
|
] || false
|
||||||
|
}
|
||||||
|
onCheckedChange={(value) => {
|
||||||
|
handleFileUnitChange(
|
||||||
|
files.indexOf(file),
|
||||||
|
item.key as keyof typeof unitSelection,
|
||||||
|
value as boolean
|
||||||
|
);
|
||||||
|
setupPlacement(
|
||||||
|
files.indexOf(file),
|
||||||
|
item.key as keyof typeof unitSelection,
|
||||||
|
Boolean(value)
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Label
|
||||||
|
htmlFor={`${item.key}-${index}`}
|
||||||
|
className="text-sm font-medium cursor-pointer"
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
))}
|
||||||
<Label className="flex items-center space-x-2">
|
</div>
|
||||||
<input
|
|
||||||
type="checkbox"
|
{/* Detail Wilayah */}
|
||||||
checked={fileUnitSelections[files.indexOf(file)]?.nasional || false}
|
{fileUnitSelections[files.indexOf(file)]?.wilayah && isDetailOfRegionShowed && (
|
||||||
onChange={(e) =>
|
<div className="border-t border-gray-200 pt-2">
|
||||||
handleFileUnitChange(
|
<p className="text-sm font-medium text-gray-700 mb-2">
|
||||||
files.indexOf(file),
|
Detail Wilayah:
|
||||||
"nasional",
|
</p>
|
||||||
e.target.checked
|
|
||||||
)
|
{/* Checkbox Sub-kategori dengan tombol Kustom sejajar */}
|
||||||
}
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
|
||||||
className="form-checkbox"
|
{[
|
||||||
/>
|
{ key: "polda", label: "POLDA" },
|
||||||
<span>Nasional</span>
|
{ 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[files.indexOf(file)]?.[
|
||||||
|
item.key as keyof typeof unitSelection
|
||||||
|
] || false
|
||||||
|
}
|
||||||
|
onCheckedChange={(value) => {
|
||||||
|
handleFileUnitChange(
|
||||||
|
files.indexOf(file),
|
||||||
|
item.key as keyof typeof unitSelection,
|
||||||
|
value as boolean
|
||||||
|
);
|
||||||
|
setupPlacement(
|
||||||
|
files.indexOf(file),
|
||||||
|
item.key as keyof typeof unitSelection,
|
||||||
|
Boolean(value)
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Label
|
||||||
|
htmlFor={`${item.key}-${index}`}
|
||||||
|
className="text-sm font-medium cursor-pointer"
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
))}
|
||||||
<Label className="flex items-center space-x-2">
|
|
||||||
<input
|
{/* Tombol Kustom sejajar dengan checkbox */}
|
||||||
type="checkbox"
|
<div className="flex items-center justify-center p-3">
|
||||||
checked={fileUnitSelections[files.indexOf(file)]?.wilayah || false}
|
<Dialog>
|
||||||
onChange={(e) =>
|
<DialogTrigger asChild>
|
||||||
handleFileUnitChange(
|
<Button
|
||||||
files.indexOf(file),
|
variant="outline"
|
||||||
"wilayah",
|
size="sm"
|
||||||
e.target.checked
|
className="gap-2"
|
||||||
)
|
>
|
||||||
}
|
<Icon
|
||||||
className="form-checkbox"
|
icon="material-symbols:tune"
|
||||||
/>
|
width={16}
|
||||||
<span>Wilayah</span>
|
height={16}
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label className="flex items-center space-x-2">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={fileUnitSelections[files.indexOf(file)]?.international || false}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleFileUnitChange(
|
|
||||||
files.indexOf(file),
|
|
||||||
"international",
|
|
||||||
e.target.checked
|
|
||||||
)
|
|
||||||
}
|
|
||||||
className="form-checkbox"
|
|
||||||
/>
|
|
||||||
<span>Internasional</span>
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label className="flex items-center space-x-2">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={fileUnitSelections[files.indexOf(file)]?.polda || false}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleFileUnitChange(
|
|
||||||
files.indexOf(file),
|
|
||||||
"polda",
|
|
||||||
e.target.checked
|
|
||||||
)
|
|
||||||
}
|
|
||||||
className="form-checkbox"
|
|
||||||
/>
|
|
||||||
<span>POLDA</span>
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label className="flex items-center space-x-2">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={fileUnitSelections[files.indexOf(file)]?.satker || false}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleFileUnitChange(
|
|
||||||
files.indexOf(file),
|
|
||||||
"satker",
|
|
||||||
e.target.checked
|
|
||||||
)
|
|
||||||
}
|
|
||||||
className="form-checkbox"
|
|
||||||
/>
|
|
||||||
<span>SATKER</span>
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Dialog>
|
|
||||||
<DialogTrigger asChild>
|
|
||||||
<Button variant="outline" size="sm">
|
|
||||||
Detail Wilayah
|
|
||||||
</Button>
|
|
||||||
</DialogTrigger>
|
|
||||||
<DialogContent className="max-w-2xl">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>Daftar Wilayah</DialogTitle>
|
|
||||||
</DialogHeader>
|
|
||||||
<div className="max-h-96 overflow-y-auto">
|
|
||||||
{isLoading ? (
|
|
||||||
<div className="flex justify-center items-center h-32">
|
|
||||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900"></div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="space-y-2">
|
|
||||||
{listDest.map((polda: any) => (
|
|
||||||
<div key={polda.id} className="space-y-1">
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={fileCheckedLevels[files.indexOf(file)]?.has(Number(polda.id)) || false}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleFileCheckboxChangePlacement(
|
|
||||||
files.indexOf(file),
|
|
||||||
Number(polda.id)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
className="form-checkbox"
|
|
||||||
/>
|
/>
|
||||||
<span className="font-medium">
|
{t("custom", {
|
||||||
{polda.name}
|
defaultValue: "Kustom",
|
||||||
</span>
|
})}
|
||||||
{polda.name === "SATKER POLRI" && polda.subDestination && (
|
</Button>
|
||||||
<Button
|
</DialogTrigger>
|
||||||
variant="outline"
|
<DialogContent className="max-w-[95vw] lg:max-w-[1400px] max-h-[90vh]">
|
||||||
size="sm"
|
<DialogHeader className="border-b border-gray-200 pb-4">
|
||||||
onClick={() => toggleExpand(Number(polda.id))}
|
<DialogTitle className="text-lg font-semibold">
|
||||||
>
|
Daftar Wilayah POLDA dan SATKER
|
||||||
{expandedPolda[Number(polda.id)] ? "Tutup" : "Buka"}
|
</DialogTitle>
|
||||||
</Button>
|
</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[
|
||||||
|
files.indexOf(file)
|
||||||
|
]?.has(
|
||||||
|
Number(
|
||||||
|
polda.id
|
||||||
|
)
|
||||||
|
) || false
|
||||||
|
}
|
||||||
|
onCheckedChange={() =>
|
||||||
|
handleFileCheckboxChangePlacement(
|
||||||
|
files.indexOf(file),
|
||||||
|
Number(polda.id)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<span className="font-semibold text-gray-900 text-sm">
|
||||||
|
{polda.name}
|
||||||
|
</span>
|
||||||
|
</Label>
|
||||||
|
{/* Tombol expand hanya untuk SATKER POLRI */}
|
||||||
|
{polda.name === "SATKER POLRI" && polda.subDestination && (
|
||||||
|
<button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
toggleExpand(
|
||||||
|
Number(polda.id)
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon={
|
||||||
|
expandedPolda[Number(polda.id)]
|
||||||
|
? "mdi:chevron-up"
|
||||||
|
: "mdi:chevron-down"
|
||||||
|
}
|
||||||
|
width={16}
|
||||||
|
height={16}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sub-items hanya untuk SATKER POLRI */}
|
||||||
|
{polda.name === "SATKER POLRI" && polda.subDestination &&
|
||||||
|
expandedPolda[Number(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[
|
||||||
|
files.indexOf(file)
|
||||||
|
]?.has(
|
||||||
|
Number(
|
||||||
|
sub.id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
className="text-xs h-6 px-2"
|
||||||
|
onClick={() =>
|
||||||
|
handleSelectAllSubItems(
|
||||||
|
files.indexOf(file),
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
</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[
|
||||||
|
files.indexOf(file)
|
||||||
|
]?.has(
|
||||||
|
Number(
|
||||||
|
sub.id
|
||||||
|
)
|
||||||
|
) ||
|
||||||
|
false
|
||||||
|
}
|
||||||
|
onCheckedChange={() =>
|
||||||
|
handleFileCheckboxChangePlacement(
|
||||||
|
files.indexOf(file),
|
||||||
|
Number(sub.id)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<span className="text-gray-700">
|
||||||
|
{
|
||||||
|
sub.name
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
</Label>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{polda.name === "SATKER POLRI" && polda.subDestination && expandedPolda[Number(polda.id)] && (
|
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
|
||||||
<div className="ml-6 space-y-1">
|
<DialogClose asChild>
|
||||||
<div className="flex items-center space-x-2">
|
<Button variant="outline">
|
||||||
<Button
|
{t("cancel", {
|
||||||
variant="outline"
|
defaultValue: "Batal",
|
||||||
size="sm"
|
})}
|
||||||
onClick={() => handleSelectAllSubItems(files.indexOf(file), polda)}
|
</Button>
|
||||||
>
|
</DialogClose>
|
||||||
{polda.subDestination.every((sub: any) =>
|
<DialogClose asChild>
|
||||||
fileCheckedLevels[files.indexOf(file)]?.has(Number(sub.id))
|
<Button>Simpan</Button>
|
||||||
) ? "Batal Pilih Semua" : "Pilih Semua"}
|
</DialogClose>
|
||||||
</Button>
|
</div>
|
||||||
</div>
|
</DialogContent>
|
||||||
{polda.subDestination.map((sub: any) => (
|
</Dialog>
|
||||||
<div key={sub.id} className="flex items-center space-x-2 ml-4">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={fileCheckedLevels[files.indexOf(file)]?.has(Number(sub.id)) || false}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleFileCheckboxChangePlacement(
|
|
||||||
files.indexOf(file),
|
|
||||||
Number(sub.id)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
className="form-checkbox"
|
|
||||||
/>
|
|
||||||
<span className="text-sm">{sub.name}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
)}
|
||||||
</Dialog>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import {
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
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 { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
import { Dialog, DialogClose, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
||||||
|
|
||||||
import { register } from "module";
|
import { register } from "module";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
|
|
@ -93,6 +93,7 @@ type Detail = {
|
||||||
};
|
};
|
||||||
|
|
||||||
interface FileWithPreview extends File {
|
interface FileWithPreview extends File {
|
||||||
|
id: string;
|
||||||
preview: string;
|
preview: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -146,6 +147,9 @@ export default function FormVideoUpdate() {
|
||||||
type VideoSchema = z.infer<typeof videoSchema>;
|
type VideoSchema = z.infer<typeof videoSchema>;
|
||||||
let progressInfo: any = [];
|
let progressInfo: any = [];
|
||||||
let counterUpdateProgress = 0;
|
let counterUpdateProgress = 0;
|
||||||
|
|
||||||
|
const isDetailOfRegionShowed = false;
|
||||||
|
|
||||||
const [progressList, setProgressList] = useState<any>([]);
|
const [progressList, setProgressList] = useState<any>([]);
|
||||||
let uploadPersen = 0;
|
let uploadPersen = 0;
|
||||||
const [isStartUpload, setIsStartUpload] = useState(false);
|
const [isStartUpload, setIsStartUpload] = useState(false);
|
||||||
|
|
@ -764,19 +768,36 @@ export default function FormVideoUpdate() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPlacement = (fileId?: string): string => {
|
const getPlacement = () => {
|
||||||
if (fileId && filePlacements[fileId]) {
|
const temp = [];
|
||||||
const placements = filePlacements[fileId];
|
for (let i = 0; i < files.length; i++) {
|
||||||
let nowArr = placements.join(",");
|
const file = files[i] as any;
|
||||||
nowArr = nowArr?.replaceAll("all", "all");
|
if (file.id && filePlacements[file.id] && filePlacements[file.id].length > 0) {
|
||||||
nowArr = nowArr?.replaceAll("mabes", "mabes");
|
const now = filePlacements[file.id];
|
||||||
nowArr = nowArr?.replaceAll("wilayah", "polda");
|
const normalizedNow = now.map((item): PlacementType => {
|
||||||
nowArr = nowArr?.replaceAll("polda", "polda");
|
const value = String(item);
|
||||||
nowArr = nowArr?.replaceAll("satker", "satker");
|
if (value === "nasional") return "mabes";
|
||||||
nowArr = nowArr?.replaceAll("international", "international");
|
if (value === "wilayah") return "polda";
|
||||||
return nowArr;
|
if (value === "semua") return "all";
|
||||||
|
return item as PlacementType;
|
||||||
|
});
|
||||||
|
const uniqueNow = Array.from(new Set(normalizedNow));
|
||||||
|
const nowArr = uniqueNow.join(",");
|
||||||
|
|
||||||
|
// Dapatkan checked levels untuk file ini
|
||||||
|
const currentFileCheckedLevels = fileCheckedLevels[file.id]
|
||||||
|
? Array.from(fileCheckedLevels[file.id])
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
mediaFileId: file.id,
|
||||||
|
placements: nowArr,
|
||||||
|
customLocationPlacements: currentFileCheckedLevels.join(","),
|
||||||
|
};
|
||||||
|
temp.push(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return "";
|
return temp;
|
||||||
};
|
};
|
||||||
|
|
||||||
const setupPlacement = (fileId: string, placement: string, isChecked: boolean) => {
|
const setupPlacement = (fileId: string, placement: string, isChecked: boolean) => {
|
||||||
|
|
@ -926,12 +947,10 @@ export default function FormVideoUpdate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update file placements
|
// Update file placements
|
||||||
if (files.length > 0) {
|
const responseFilePlacements = await updateFilePlacements(getPlacement());
|
||||||
files.forEach((file: any) => {
|
if (responseFilePlacements?.error) {
|
||||||
if (file.id) {
|
error(responseFilePlacements?.message);
|
||||||
updateFilePlacements(getPlacement(file.id));
|
return false;
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedFiles.length > 0) {
|
if (selectedFiles.length > 0) {
|
||||||
|
|
@ -1317,13 +1336,13 @@ export default function FormVideoUpdate() {
|
||||||
</Fragment>
|
</Fragment>
|
||||||
) : null}
|
) : null}
|
||||||
{files.length > 0 && (
|
{files.length > 0 && (
|
||||||
<div className="mt-4 space-y-2">
|
<div className="mt-4">
|
||||||
<Label className="text-lg font-semibold">
|
<Label className="text-md font-semibold">
|
||||||
{" "}
|
{" "}
|
||||||
{t("file-media", { defaultValue: "File Media" })}
|
{t("file-media", { defaultValue: "File Media" })}
|
||||||
</Label>
|
</Label>
|
||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
{files.map((file: any) => (
|
{files.map((file: any, index: any) => (
|
||||||
<div
|
<div
|
||||||
key={file.id}
|
key={file.id}
|
||||||
className="flex items-center border p-2 rounded-md"
|
className="flex items-center border p-2 rounded-md"
|
||||||
|
|
@ -1347,136 +1366,319 @@ export default function FormVideoUpdate() {
|
||||||
})}
|
})}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap gap-3 items-center">
|
<div className="bg-white rounded-md p-4 border">
|
||||||
<div>
|
{/* Checkbox Tingkat Utama */}
|
||||||
<Label className="flex items-center space-x-2">
|
<div className="space-y-4">
|
||||||
<Checkbox
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||||
checked={fileUnitSelections[file.id]?.semua || false}
|
{[
|
||||||
onCheckedChange={(checked) =>
|
{ key: "semua", label: "Semua" },
|
||||||
handleFileUnitChange(file.id, "semua", checked as boolean)
|
{
|
||||||
}
|
key: "nasional",
|
||||||
/>
|
label: "Nasional",
|
||||||
<span>
|
},
|
||||||
{t("all", { defaultValue: "All" })}
|
{ key: "wilayah", label: "Wilayah" },
|
||||||
</span>
|
{
|
||||||
</Label>
|
key: "international",
|
||||||
</div>
|
label: "Internasional",
|
||||||
<div>
|
},
|
||||||
<Label className="flex items-center space-x-2">
|
].map((item, idx) => (
|
||||||
<Checkbox
|
<div
|
||||||
checked={fileUnitSelections[file.id]?.nasional || false}
|
key={item.key}
|
||||||
onCheckedChange={(checked) =>
|
className="flex items-center gap-2 p-2 border border-gray-200 rounded-md hover:bg-gray-50"
|
||||||
handleFileUnitChange(file.id, "nasional", checked as boolean)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<span>Nasional</span>
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label className="flex items-center space-x-2">
|
|
||||||
<Checkbox
|
|
||||||
checked={fileUnitSelections[file.id]?.wilayah || false}
|
|
||||||
onCheckedChange={(checked) =>
|
|
||||||
handleFileUnitChange(file.id, "wilayah", checked as boolean)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<span>Wilayah</span>
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label className="flex items-center space-x-2">
|
|
||||||
<Checkbox
|
|
||||||
checked={fileUnitSelections[file.id]?.international || false}
|
|
||||||
onCheckedChange={(checked) =>
|
|
||||||
handleFileUnitChange(file.id, "international", checked as boolean)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<span>Internasional</span>
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Dialog>
|
|
||||||
<DialogTrigger asChild>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => setTempFile(file)}
|
|
||||||
>
|
>
|
||||||
Detail Wilayah
|
<Checkbox
|
||||||
</Button>
|
// id={`${item.key}-${index}`}
|
||||||
</DialogTrigger>
|
checked={
|
||||||
<DialogContent className="max-w-2xl">
|
fileUnitSelections[file.id]?.[
|
||||||
<DialogHeader>
|
item.key as keyof typeof unitSelection
|
||||||
<DialogTitle>Daftar Wilayah POLDA dan SATKER</DialogTitle>
|
] || false
|
||||||
</DialogHeader>
|
}
|
||||||
<div className="max-h-96 overflow-y-auto">
|
onCheckedChange={(value) => {
|
||||||
{listDest?.filter((item) => item.levelNumber === 2)
|
handleFileUnitChange(
|
||||||
.map((poldaItem) => (
|
file.id,
|
||||||
<div key={poldaItem.id} className="mb-4">
|
item.key,
|
||||||
<div className="flex items-center space-x-2 mb-2">
|
value as boolean
|
||||||
<Checkbox
|
);
|
||||||
checked={fileCheckedLevels[file.id]?.has(poldaItem.id) || false}
|
setupPlacement(
|
||||||
onCheckedChange={(checked) =>
|
file.id,
|
||||||
handleFileCheckboxChangePlacement(file.id, poldaItem, checked as boolean)
|
item.key,
|
||||||
}
|
Boolean(value)
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Label
|
||||||
|
htmlFor={`${item.key}-${index}`}
|
||||||
|
className="text-sm font-medium cursor-pointer"
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Detail Wilayah */}
|
||||||
|
{fileUnitSelections[file.id]?.wilayah && isDetailOfRegionShowed && (
|
||||||
|
<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: "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[file.id]?.[
|
||||||
|
item.key as keyof typeof unitSelection
|
||||||
|
] || false
|
||||||
|
}
|
||||||
|
onCheckedChange={(value) => {
|
||||||
|
handleFileUnitChange(
|
||||||
|
file.id,
|
||||||
|
item.key,
|
||||||
|
value as boolean
|
||||||
|
);
|
||||||
|
setupPlacement(
|
||||||
|
file.id,
|
||||||
|
item.key,
|
||||||
|
Boolean(value)
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Label
|
||||||
|
htmlFor={`${item.key}-${index}`}
|
||||||
|
className="text-sm font-medium cursor-pointer"
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</Label>
|
||||||
|
</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"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon="material-symbols:tune"
|
||||||
|
width={16}
|
||||||
|
height={16}
|
||||||
/>
|
/>
|
||||||
<Label className="font-medium">
|
{t("custom", {
|
||||||
{poldaItem.name}
|
defaultValue: "Kustom",
|
||||||
</Label>
|
})}
|
||||||
{poldaItem.name === "SATKER POLRI" && poldaItem.subDestination.length > 0 && (
|
</Button>
|
||||||
<Button
|
</DialogTrigger>
|
||||||
type="button"
|
<DialogContent className="max-w-[95vw] lg:max-w-[1400px] max-h-[90vh]">
|
||||||
variant="ghost"
|
<DialogHeader className="border-b border-gray-200 pb-4">
|
||||||
size="sm"
|
<DialogTitle className="text-lg font-semibold">
|
||||||
onClick={() => toggleExpand(poldaItem.id)}
|
Daftar Wilayah POLDA dan SATKER
|
||||||
>
|
</DialogTitle>
|
||||||
{expandedPolda.has(poldaItem.id) ? "Tutup" : "Buka"}
|
</DialogHeader>
|
||||||
</Button>
|
<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[
|
||||||
|
file.id
|
||||||
|
]?.has(
|
||||||
|
Number(
|
||||||
|
polda.id
|
||||||
|
)
|
||||||
|
) || false
|
||||||
|
}
|
||||||
|
onCheckedChange={() =>
|
||||||
|
handleFileCheckboxChangePlacement(
|
||||||
|
file.id,
|
||||||
|
polda,
|
||||||
|
!fileCheckedLevels[file.id]?.has(Number(polda.id))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<span className="font-semibold text-gray-900 text-sm">
|
||||||
|
{polda.name}
|
||||||
|
</span>
|
||||||
|
</Label>
|
||||||
|
{/* Tombol expand hanya untuk SATKER POLRI */}
|
||||||
|
{polda.name === "SATKER POLRI" && 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.has(
|
||||||
|
polda.id
|
||||||
|
)
|
||||||
|
? "mdi:chevron-up"
|
||||||
|
: "mdi:chevron-down"
|
||||||
|
}
|
||||||
|
width={16}
|
||||||
|
height={16}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sub-items hanya untuk SATKER POLRI */}
|
||||||
|
{polda.name === "SATKER POLRI" && polda.subDestination &&
|
||||||
|
expandedPolda.has(
|
||||||
|
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[
|
||||||
|
file.id
|
||||||
|
]?.has(
|
||||||
|
Number(
|
||||||
|
sub.id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
className="text-xs h-6 px-2"
|
||||||
|
onClick={() =>
|
||||||
|
handleSelectAllSubItems(
|
||||||
|
file.id,
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
</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[
|
||||||
|
file.id
|
||||||
|
]?.has(
|
||||||
|
Number(
|
||||||
|
sub.id
|
||||||
|
)
|
||||||
|
) ||
|
||||||
|
false
|
||||||
|
}
|
||||||
|
onCheckedChange={() =>
|
||||||
|
handleFileCheckboxChangePlacement(
|
||||||
|
file.id,
|
||||||
|
polda,
|
||||||
|
!fileCheckedLevels[file.id]?.has(Number(sub.id))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<span className="text-gray-700">
|
||||||
|
{
|
||||||
|
sub.name
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
</Label>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{poldaItem.name === "SATKER POLRI" && expandedPolda.has(poldaItem.id) && (
|
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
|
||||||
<div className="ml-6 space-y-2">
|
<DialogClose asChild>
|
||||||
<div className="flex items-center space-x-2">
|
<Button variant="outline">
|
||||||
<Button
|
{t("cancel", {
|
||||||
type="button"
|
defaultValue: "Batal",
|
||||||
variant="outline"
|
})}
|
||||||
size="sm"
|
</Button>
|
||||||
onClick={() => handleSelectAllSubItems(file.id, poldaItem)}
|
</DialogClose>
|
||||||
>
|
<DialogClose asChild>
|
||||||
Pilih Semua
|
<Button>Simpan</Button>
|
||||||
</Button>
|
</DialogClose>
|
||||||
</div>
|
</div>
|
||||||
{poldaItem.subDestination.map((satkerItem) => (
|
</DialogContent>
|
||||||
<div key={satkerItem.id} className="flex items-center space-x-2 ml-4">
|
</Dialog>
|
||||||
<Checkbox
|
</div>
|
||||||
checked={fileCheckedLevels[file.id]?.has(satkerItem.id) || false}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
setFileCheckedLevels(prev => {
|
|
||||||
const currentFileLevels = prev[file.id] || new Set<number>();
|
|
||||||
const newFileLevels = new Set(currentFileLevels);
|
|
||||||
if (checked) {
|
|
||||||
newFileLevels.add(satkerItem.id);
|
|
||||||
} else {
|
|
||||||
newFileLevels.delete(satkerItem.id);
|
|
||||||
}
|
|
||||||
return { ...prev, [file.id]: newFileLevels };
|
|
||||||
});
|
|
||||||
updateMainCheckboxFromModal(file.id);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Label className="text-sm">
|
|
||||||
{satkerItem.name}
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</div>
|
||||||
</Dialog>
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue