fix: fixing placement satker and maps

This commit is contained in:
Sabda Yagra 2026-02-11 17:19:20 +07:00
commit 8c98175cdb
9 changed files with 877 additions and 876 deletions

View File

@ -115,7 +115,7 @@ const ViewEditor = dynamic(
() => {
return import("@/components/editor/view-editor");
},
{ ssr: false }
{ ssr: false },
);
interface Destination {
@ -204,6 +204,12 @@ export default function FormAudioDetail() {
satker: boolean;
}>
>([]);
const [creatorLevelNumber, setCreatorLevelNumber] = useState<number | null>(
null,
);
const isContentFromSatker = creatorLevelNumber === 3;
const isContentFromMabesOrPolda =
creatorLevelNumber === 1 || creatorLevelNumber === 2;
useEffect(() => {
if (Number(userLevelId) === 216 && Number(roleId) === 3) {
@ -215,7 +221,7 @@ export default function FormAudioDetail() {
const handleFileUnitChange = (
fileIndex: number,
key: keyof typeof unitSelection,
value: boolean
value: boolean,
) => {
setFileUnitSelections((prev) => {
const newSelections = [...prev];
@ -284,12 +290,12 @@ export default function FormAudioDetail() {
(item: any) =>
item.levelNumber === 2 &&
item.name !== "SATKER POLRI" &&
currentFileCheckedLevels.has(Number(item.id))
currentFileCheckedLevels.has(Number(item.id)),
);
if (!hasSelectedPolda) {
alert(
"Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES."
"Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES.",
);
return prev;
}
@ -410,7 +416,7 @@ export default function FormAudioDetail() {
useEffect(() => {
if (detail?.assignedToTopLevel) {
const outputSet = new Set(
detail.assignedToTopLevel.split(",").map(Number)
detail.assignedToTopLevel.split(",").map(Number),
);
setUnitSelection({
semua: outputSet.has(0),
@ -435,7 +441,7 @@ export default function FormAudioDetail() {
acc[polda.id] = false;
return acc;
},
{}
{},
);
setExpandedPolda(initialExpandedState);
console.log("polres", initialExpandedState);
@ -470,7 +476,7 @@ export default function FormAudioDetail() {
const handleUnitChange = (
key: keyof typeof unitSelection,
value: boolean
value: boolean,
) => {
if (key === "semua") {
const newState = {
@ -580,7 +586,7 @@ export default function FormAudioDetail() {
const handleCheckboxChange = (id: number) => {
setSelectedPublishers((prev) =>
prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id],
);
};
@ -602,7 +608,7 @@ export default function FormAudioDetail() {
if (scheduleId && scheduleType === "3") {
const findCategory = resCategory.find((o) =>
o.name.toLowerCase().includes("pers rilis")
o.name.toLowerCase().includes("pers rilis"),
);
if (findCategory) {
@ -653,6 +659,9 @@ export default function FormAudioDetail() {
// console.log("ISI FILES:", details?.files);
setDetail(details);
if (details?.uploadedBy?.userLevel?.levelNumber) {
setCreatorLevelNumber(details.uploadedBy.userLevel.levelNumber);
}
setMain({
type: details?.fileType.name,
url: details?.files[0]?.url,
@ -663,14 +672,14 @@ export default function FormAudioDetail() {
if (details?.assignedToLevel) {
const levels = new Set(
details.assignedToLevel.split(",").map(Number)
details.assignedToLevel.split(",").map(Number),
);
setCheckedLevels(levels);
}
if (details?.publishedForObject) {
const publisherIds = details?.publishedForObject.map(
(obj: any) => obj.id
(obj: any) => obj.id,
);
setSelectedPublishers(publisherIds);
}
@ -820,7 +829,7 @@ export default function FormAudioDetail() {
const setupPlacement = (
index: number,
placement: string,
checked: boolean
checked: boolean,
) => {
let temp = [...filePlacements];
if (checked) {
@ -838,7 +847,7 @@ export default function FormAudioDetail() {
setFileCheckedLevels((prevLevels) => {
const newArray = [...prevLevels];
const currentFileLevels = new Set<number>(
newArray[index] || new Set()
newArray[index] || new Set(),
);
// Checklist semua item di modal
@ -888,7 +897,7 @@ export default function FormAudioDetail() {
// 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"
(item) => item !== "satker" && item !== "all",
);
if (nonSatkerItems.length === 3 && !now.includes("all")) {
now.push("all");
@ -903,7 +912,7 @@ export default function FormAudioDetail() {
setFileCheckedLevels((prevLevels) => {
const newArray = [...prevLevels];
const currentFileLevels = new Set<number>(
newArray[index] || new Set()
newArray[index] || new Set(),
);
// Unchecklist semua item di modal
@ -937,7 +946,7 @@ export default function FormAudioDetail() {
// Hapus "all" jika tidak semua item ter-checklist
if (now.includes("all")) {
const nonSatkerItems = now.filter(
(item) => item !== "satker" && item !== "all"
(item) => item !== "satker" && item !== "all",
);
if (nonSatkerItems.length < 3) {
const newData = now.filter((b) => b !== "all");
@ -956,7 +965,7 @@ export default function FormAudioDetail() {
type: string,
url: string,
names: string,
format: string
format: string,
) => {
// console.log("Test 3 :", type, url, names, format);
setMain({
@ -991,7 +1000,7 @@ export default function FormAudioDetail() {
const updateModalChecklistLevels = (
fileIndex: number,
placement: string,
checked: boolean
checked: boolean,
) => {
if (!listDest || listDest.length === 0) return;
@ -1024,7 +1033,7 @@ export default function FormAudioDetail() {
} else if (placement === "satker") {
// Checklist SATKER POLRI dan semua sub-item di bawahnya
const satkerItem: any = listDest.find(
(item: any) => item.name === "SATKER POLRI"
(item: any) => item.name === "SATKER POLRI",
);
if (satkerItem) {
currentFileLevels.add(Number(satkerItem.id));
@ -1060,7 +1069,7 @@ export default function FormAudioDetail() {
} else if (placement === "satker") {
// Unchecklist SATKER POLRI dan semua sub-item di bawahnya
const satkerItem: any = listDest.find(
(item: any) => item.name === "SATKER POLRI"
(item: any) => item.name === "SATKER POLRI",
);
if (satkerItem) {
currentFileLevels.delete(Number(satkerItem.id));
@ -1093,7 +1102,7 @@ export default function FormAudioDetail() {
// Fungsi untuk mengupdate checklist levels untuk file tertentu
const handleFileCheckboxChangePlacement = (
fileIndex: number,
levelId: number
levelId: number,
) => {
setFileCheckedLevels((prev) => {
const newArray = [...prev];
@ -1105,7 +1114,7 @@ export default function FormAudioDetail() {
// Jika ini adalah POLDA yang di-unchecklist, unchecklist juga semua polres di bawahnya
const poldaItem = listDest.find(
(item: any) => Number(item.id) === levelId
(item: any) => Number(item.id) === levelId,
) as any;
if (
poldaItem &&
@ -1128,7 +1137,7 @@ export default function FormAudioDetail() {
// 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
(item: any) => Number(item.id) === levelId,
) as any;
if (satkerItem && satkerItem.name === "SATKER POLRI") {
// Checklist semua sub-item di bawah SATKER POLRI (bukan POLRES)
@ -1159,7 +1168,7 @@ export default function FormAudioDetail() {
// Hitung total POLDA yang ada (bukan SATKER POLRI)
const totalPoldaCount = listDest.filter(
(item: any) => item.levelNumber === 2 && item.name !== "SATKER POLRI"
(item: any) => item.levelNumber === 2 && item.name !== "SATKER POLRI",
).length;
// Hitung berapa banyak POLDA yang ter-checklist (bukan SATKER POLRI)
@ -1186,7 +1195,7 @@ export default function FormAudioDetail() {
}
return total;
},
0
0,
);
// Hitung berapa banyak POLRES yang ter-checklist
@ -1196,7 +1205,7 @@ export default function FormAudioDetail() {
return (
total +
item.subDestination.filter((sub: any) =>
currentFileLevels.has(Number(sub.id))
currentFileLevels.has(Number(sub.id)),
).length
);
}
@ -1205,7 +1214,7 @@ export default function FormAudioDetail() {
// Cek apakah SATKER POLRI ter-checklist
const satkerItem = listDest.find(
(item: any) => item.name === "SATKER POLRI"
(item: any) => item.name === "SATKER POLRI",
);
const isSatkerChecked =
satkerItem && currentFileLevels.has(Number(satkerItem.id));
@ -1239,7 +1248,7 @@ export default function FormAudioDetail() {
// Cek apakah semua sub-items sudah ter-checklist
const allSubItemsChecked = polda.subDestination?.every((sub: any) =>
currentFileLevels.has(Number(sub.id))
currentFileLevels.has(Number(sub.id)),
);
if (allSubItemsChecked) {
@ -1322,7 +1331,7 @@ export default function FormAudioDetail() {
{detail &&
!categories.find(
(cat) =>
String(cat.id) === String(detail.category.id)
String(cat.id) === String(detail.category.id),
) && (
<SelectItem
key={String(detail.category.id)}
@ -1582,56 +1591,6 @@ export default function FormAudioDetail() {
Tingkat Distribusi:
</p>
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
{[
{ key: "semua", label: "Semua" },
{ key: "nasional", label: "Nasional" },
{ key: "wilayah", label: "Wilayah" },
{
key: "international",
label: "Internasional",
},
]
.filter(
(item) =>
!(
isUploadedBySatkerLevel3 &&
item.key === "wilayah"
)
)
.map((item) => (
<div
key={item.key}
className="flex items-center gap-2 p-2 border border-gray-200 rounded-md hover:bg-gray-50"
>
<Checkbox
id={`${item.key}-${index}`}
checked={
fileUnitSelections[index]?.[
item.key as keyof typeof unitSelection
] || false
}
onCheckedChange={(value) => {
handleFileUnitChange(
index,
item.key as keyof typeof unitSelection,
value as boolean
);
setupPlacement(
index,
item.key,
Boolean(value)
);
}}
/>
<Label
htmlFor={`${item.key}-${index}`}
className="text-sm font-medium cursor-pointer"
>
{item.label}
</Label>
</div>
))}
{/* {[
{ key: "semua", label: "Semua" },
{ key: "nasional", label: "Nasional" },
@ -1640,6 +1599,39 @@ export default function FormAudioDetail() {
key: "international",
label: "Internasional",
},
] */}
{[
{ key: "semua", label: "Semua" },
...(isContentFromMabesOrPolda
? [
{
key: "nasional",
label: "Nasional",
},
{
key: "international",
label: "Internasional",
},
{
key: "wilayah",
label: "Wilayah",
},
]
: []),
...(isContentFromSatker
? [
{
key: "nasional",
label: "Nasional",
},
{
key: "international",
label: "Internasional",
},
]
: []),
].map((item, idx) => (
<div
key={item.key}
@ -1656,12 +1648,12 @@ export default function FormAudioDetail() {
handleFileUnitChange(
index,
item.key as keyof typeof unitSelection,
value as boolean
value as boolean,
);
setupPlacement(
index,
item.key,
Boolean(value)
Boolean(value),
);
}}
/>
@ -1672,256 +1664,267 @@ export default function FormAudioDetail() {
{item.label}
</Label>
</div>
))} */}
))}
</div>
</div>
{/* Detail Wilayah */}
{fileUnitSelections[index]?.wilayah && (
<div className="border-t border-gray-200 pt-2">
<p className="text-sm font-medium text-gray-700 mb-2">
Detail Wilayah:
</p>
{/* {fileUnitSelections[index]?.wilayah && ( */}
{!isContentFromSatker &&
fileUnitSelections[index]?.wilayah && (
<div className="border-t border-gray-200 pt-2">
<p className="text-sm font-medium text-gray-700 mb-2">
Detail Wilayah:
</p>
{/* Checkbox Sub-kategori dengan tombol Kustom sejajar */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
{[
{ key: "polda", label: "POLDA" },
{ key: "polres", label: "POLRES" },
{ key: "satker", label: "SATKER" },
].map((item, idx) => (
<div
key={item.key}
className="flex items-center gap-2 p-3 border border-gray-200 rounded-md hover:bg-gray-50"
>
<Checkbox
id={`${item.key}-${index}`}
checked={
fileUnitSelections[index]?.[
item.key as keyof typeof unitSelection
] || false
}
onCheckedChange={(value) => {
handleFileUnitChange(
index,
item.key as keyof typeof unitSelection,
value as boolean
);
setupPlacement(
index,
item.key,
Boolean(value)
);
}}
/>
<Label
htmlFor={`${item.key}-${index}`}
className="text-sm font-medium cursor-pointer"
{/* Checkbox Sub-kategori dengan tombol Kustom sejajar */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
{[
{ key: "polda", label: "POLDA" },
{ key: "polres", label: "POLRES" },
{ key: "satker", label: "SATKER" },
].map((item, idx) => (
<div
key={item.key}
className="flex items-center gap-2 p-3 border border-gray-200 rounded-md hover:bg-gray-50"
>
{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"
<Checkbox
id={`${item.key}-${index}`}
checked={
fileUnitSelections[index]?.[
item.key as keyof typeof unitSelection
] || false
}
onCheckedChange={(value) => {
handleFileUnitChange(
index,
item.key as keyof typeof unitSelection,
value as boolean,
);
setupPlacement(
index,
item.key,
Boolean(value),
);
}}
/>
<Label
htmlFor={`${item.key}-${index}`}
className="text-sm font-medium cursor-pointer"
>
<Icon
icon="material-symbols:tune"
width={16}
height={16}
/>
{t("custom", {
defaultValue: "Kustom",
})}
</Button>
</DialogTrigger>
<DialogContent className="max-w-[95vw] lg:max-w-[1400px] max-h-[90vh]">
<DialogHeader className="border-b border-gray-200 pb-4">
<DialogTitle className="text-lg font-semibold">
Daftar Wilayah POLDA dan POLRES
</DialogTitle>
</DialogHeader>
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4 max-h-[70vh] overflow-y-auto p-1">
{listDest.map((polda: any) => (
<div
key={polda.id}
className="border border-gray-200 rounded-lg p-2 bg-white hover:shadow-sm transition-shadow"
>
{/* Header POLDA */}
<div className="flex items-center justify-between">
<Label className="flex items-center gap-3 flex-1 cursor-pointer">
<Checkbox
checked={
fileCheckedLevels[
index
]?.has(
Number(polda.id)
) || false
}
onCheckedChange={() =>
handleFileCheckboxChangePlacement(
index,
Number(polda.id)
)
}
/>
<span className="font-semibold text-gray-900 text-sm">
{polda.name}
</span>
</Label>
{polda.subDestination && (
<button
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
toggleExpand(
polda.id
);
}}
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
>
<Icon
icon={
expandedPolda[
polda.id
]
? "mdi:chevron-up"
: "mdi:chevron-down"
}
width={16}
height={16}
/>
</button>
)}
</div>
{item.label}
</Label>
</div>
))}
{/* Sub-items */}
{polda.subDestination &&
expandedPolda[polda.id] && (
<div className="max-h-[200px] overflow-y-auto border-t border-gray-100 pt-2">
{/* Tombol Pilih Semua untuk sub-items */}
<div className="mb-2 flex justify-start">
{(() => {
const allSubItemsChecked =
polda.subDestination?.every(
(sub: any) =>
fileCheckedLevels[
index
]?.has(
Number(
sub.id
)
)
);
return (
<Button
size="sm"
variant="outline"
className="text-xs h-6 px-2"
onClick={() =>
handleSelectAllSubItems(
index,
polda
)
}
>
{allSubItemsChecked ? (
<>
<Icon
icon="material-symbols:check-indeterminate-small"
width={12}
height={
12
}
className="mr-1"
/>
Batal Semua
</>
) : (
<>
<Icon
icon="material-symbols:check-all"
width={12}
height={
12
}
className="mr-1"
/>
Pilih Semua
</>
)}
</Button>
{/* Tombol Kustom sejajar dengan checkbox */}
<div className="flex items-center justify-center p-3">
<Dialog>
<DialogTrigger asChild>
<Button
variant="outline"
size="sm"
className="gap-2"
>
<Icon
icon="material-symbols:tune"
width={16}
height={16}
/>
{t("custom", {
defaultValue: "Kustom",
})}
</Button>
</DialogTrigger>
<DialogContent className="max-w-[95vw] lg:max-w-[1400px] max-h-[90vh]">
<DialogHeader className="border-b border-gray-200 pb-4">
<DialogTitle className="text-lg font-semibold">
Daftar Wilayah POLDA dan
POLRES
</DialogTitle>
</DialogHeader>
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4 max-h-[70vh] overflow-y-auto p-1">
{listDest.map((polda: any) => (
<div
key={polda.id}
className="border border-gray-200 rounded-lg p-2 bg-white hover:shadow-sm transition-shadow"
>
{/* Header POLDA */}
<div className="flex items-center justify-between">
<Label className="flex items-center gap-3 flex-1 cursor-pointer">
<Checkbox
checked={
fileCheckedLevels[
index
]?.has(
Number(polda.id),
) || false
}
onCheckedChange={() =>
handleFileCheckboxChangePlacement(
index,
Number(polda.id),
)
}
/>
<span className="font-semibold text-gray-900 text-sm">
{polda.name}
</span>
</Label>
{polda.subDestination && (
<button
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
toggleExpand(
polda.id,
);
})()}
</div>
<div className="space-y-1">
{polda.subDestination.map(
(sub: any) => (
<Label
key={sub.id}
className="flex items-center gap-2 p-2 rounded-md hover:bg-gray-50 transition-colors cursor-pointer text-xs"
>
<Checkbox
checked={
}}
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
>
<Icon
icon={
expandedPolda[
polda.id
]
? "mdi:chevron-up"
: "mdi:chevron-down"
}
width={16}
height={16}
/>
</button>
)}
</div>
{/* Sub-items */}
{polda.subDestination &&
expandedPolda[
polda.id
] && (
<div className="max-h-[200px] overflow-y-auto border-t border-gray-100 pt-2">
{/* Tombol Pilih Semua untuk sub-items */}
<div className="mb-2 flex justify-start">
{(() => {
const allSubItemsChecked =
polda.subDestination?.every(
(sub: any) =>
fileCheckedLevels[
index
]?.has(
Number(
sub.id
)
) || false
}
onCheckedChange={() =>
handleFileCheckboxChangePlacement(
sub.id,
),
),
);
return (
<Button
size="sm"
variant="outline"
className="text-xs h-6 px-2"
onClick={() =>
handleSelectAllSubItems(
index,
Number(
sub.id
)
polda,
)
}
/>
<span className="text-gray-700">
{sub.name}
</span>
</Label>
)
)}
>
{allSubItemsChecked ? (
<>
<Icon
icon="material-symbols:check-indeterminate-small"
width={
12
}
height={
12
}
className="mr-1"
/>
Batal
Semua
</>
) : (
<>
<Icon
icon="material-symbols:check-all"
width={
12
}
height={
12
}
className="mr-1"
/>
Pilih
Semua
</>
)}
</Button>
);
})()}
</div>
<div className="space-y-1">
{polda.subDestination.map(
(sub: any) => (
<Label
key={sub.id}
className="flex items-center gap-2 p-2 rounded-md hover:bg-gray-50 transition-colors cursor-pointer text-xs"
>
<Checkbox
checked={
fileCheckedLevels[
index
]?.has(
Number(
sub.id,
),
) || false
}
onCheckedChange={() =>
handleFileCheckboxChangePlacement(
index,
Number(
sub.id,
),
)
}
/>
<span className="text-gray-700">
{sub.name}
</span>
</Label>
),
)}
</div>
</div>
</div>
)}
</div>
))}
</div>
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
<DialogClose asChild>
<Button variant="outline">
{t("cancel", {
defaultValue: "Batal",
})}
</Button>
</DialogClose>
<DialogClose asChild>
<Button>
{/* {t("save", {
)}
</div>
))}
</div>
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
<DialogClose asChild>
<Button variant="outline">
{t("cancel", {
defaultValue: "Batal",
})}
</Button>
</DialogClose>
<DialogClose asChild>
<Button>
{/* {t("save", {
defaultValue: "Simpan",
})} */}
Simpan
</Button>
</DialogClose>
</div>
</DialogContent>
</Dialog>
Simpan
</Button>
</DialogClose>
</div>
</DialogContent>
</Dialog>
</div>
</div>
</div>
</div>
)}
)}
</div>
</div>
) : (
@ -2000,7 +2003,7 @@ export default function FormAudioDetail() {
>
{template}
</Button>
)
),
)}
</div>
</div>

View File

@ -1806,7 +1806,7 @@ export default function FormAudio() {
</Card>
<div className="flex flex-row justify-end gap-3">
<div className="mt-4">
{levelNumber !== "2" && levelNumber !== "3" && (
{levelNumber !== "2" && (
<Button type="submit" color="primary">
{t("submit", { defaultValue: "Submit" })}
</Button>

View File

@ -115,7 +115,7 @@ const ViewEditor = dynamic(
() => {
return import("@/components/editor/view-editor");
},
{ ssr: false }
{ ssr: false },
);
interface Destination {
@ -173,6 +173,12 @@ export default function FormImageDetail() {
satker: boolean;
}>
>([]);
const [creatorLevelNumber, setCreatorLevelNumber] = useState<number | null>(
null,
);
const isContentFromSatker = creatorLevelNumber === 3;
const isContentFromMabesOrPolda =
creatorLevelNumber === 1 || creatorLevelNumber === 2;
useEffect(() => {
if (Number(userLevelId) === 216 && Number(roleId) === 3) {
@ -201,7 +207,7 @@ export default function FormImageDetail() {
const [files, setFiles] = useState<FileType[]>([]);
const [rejectedFiles, setRejectedFiles] = useState<number[]>([]);
const [expandedPolda, setExpandedPolda] = useState<Record<number, boolean>>(
{}
{},
);
// State untuk melacak apakah perubahan berasal dari checkbox utama
@ -232,7 +238,7 @@ export default function FormImageDetail() {
useEffect(() => {
if (detail?.assignedToTopLevel) {
const outputSet = new Set(
detail.assignedToTopLevel.split(",").map(Number)
detail.assignedToTopLevel.split(",").map(Number),
);
setUnitSelection({
semua: outputSet.has(0),
@ -261,7 +267,7 @@ export default function FormImageDetail() {
acc[polda.id] = false;
return acc;
},
{}
{},
);
setExpandedPolda(initialExpandedState);
console.log("polres", initialExpandedState);
@ -300,7 +306,7 @@ export default function FormImageDetail() {
(item: any) =>
item.levelNumber === 2 &&
item.name !== "SATKER POLRI" &&
checkedLevels.has(Number(item.id))
checkedLevels.has(Number(item.id)),
).length;
const checkedPolresCount = listDest.reduce((total: number, item: any) => {
@ -309,7 +315,7 @@ export default function FormImageDetail() {
return (
total +
item.subDestination.filter((sub: any) =>
checkedLevels.has(Number(sub.id))
checkedLevels.has(Number(sub.id)),
).length
);
}
@ -317,12 +323,12 @@ export default function FormImageDetail() {
}, 0);
const satkerItem: any = listDest.find(
(item: any) => item.name === "SATKER POLRI"
(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))
checkedLevels.has(Number(sub.id)),
).length || 0)
: 0;
@ -392,7 +398,7 @@ export default function FormImageDetail() {
} else if (mainCheckboxChangeType === "satker_checked") {
// Checklist satker
const satkerItem: any = listDest.find(
(item: any) => item.name === "SATKER POLRI"
(item: any) => item.name === "SATKER POLRI",
);
if (satkerItem) {
newCheckedLevels.add(Number(satkerItem.id));
@ -440,7 +446,7 @@ export default function FormImageDetail() {
} 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"
(item: any) => item.name === "SATKER POLRI",
);
if (satkerItem) {
newCheckedLevels.delete(Number(satkerItem.id));
@ -467,7 +473,7 @@ export default function FormImageDetail() {
const handleFileUnitChange = (
fileIndex: number,
key: keyof typeof unitSelection,
value: boolean
value: boolean,
) => {
setFileUnitSelections((prev) => {
const newSelections = [...prev];
@ -545,12 +551,12 @@ export default function FormImageDetail() {
(item: any) =>
item.levelNumber === 2 &&
item.name !== "SATKER POLRI" &&
currentFileCheckedLevels.has(Number(item.id))
currentFileCheckedLevels.has(Number(item.id)),
);
if (!hasSelectedPolda) {
alert(
"Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES."
"Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES.",
);
return prev;
}
@ -664,7 +670,7 @@ export default function FormImageDetail() {
// Fungsi lama untuk kompatibilitas (akan dihapus nanti)
const handleUnitChange = (
key: keyof typeof unitSelection,
value: boolean
value: boolean,
) => {
// Set flag bahwa perubahan berasal dari checkbox utama
setIsUpdatingFromMainCheckbox(true);
@ -690,13 +696,13 @@ export default function FormImageDetail() {
(item: any) =>
item.levelNumber === 2 &&
item.name !== "SATKER POLRI" &&
checkedLevels.has(Number(item.id))
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."
"Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES.",
);
// Reset flag karena perubahan dibatalkan
setIsUpdatingFromMainCheckbox(false);
@ -752,14 +758,14 @@ export default function FormImageDetail() {
const handleCheckboxChange = (id: number) => {
setSelectedPublishers((prev) =>
prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id],
);
};
// Fungsi untuk mengupdate checklist levels untuk file tertentu
const handleFileCheckboxChangePlacement = (
fileIndex: number,
levelId: number
levelId: number,
) => {
setFileCheckedLevels((prev) => {
const newArray = [...prev];
@ -771,7 +777,7 @@ export default function FormImageDetail() {
// Jika ini adalah POLDA yang di-unchecklist, unchecklist juga semua polres di bawahnya
const poldaItem = listDest.find(
(item: any) => Number(item.id) === levelId
(item: any) => Number(item.id) === levelId,
) as any;
if (
poldaItem &&
@ -794,7 +800,7 @@ export default function FormImageDetail() {
// 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
(item: any) => Number(item.id) === levelId,
) as any;
if (satkerItem && satkerItem.name === "SATKER POLRI") {
// Checklist semua sub-item di bawah SATKER POLRI (bukan POLRES)
@ -822,7 +828,7 @@ export default function FormImageDetail() {
// Cek apakah semua sub-items sudah ter-checklist
const allSubItemsChecked = polda.subDestination?.every((sub: any) =>
currentFileLevels.has(Number(sub.id))
currentFileLevels.has(Number(sub.id)),
);
if (allSubItemsChecked) {
@ -862,7 +868,7 @@ export default function FormImageDetail() {
// Hitung total POLDA yang ada (bukan SATKER POLRI)
const totalPoldaCount = listDest.filter(
(item: any) => item.levelNumber === 2 && item.name !== "SATKER POLRI"
(item: any) => item.levelNumber === 2 && item.name !== "SATKER POLRI",
).length;
// Hitung berapa banyak POLDA yang ter-checklist (bukan SATKER POLRI)
@ -889,7 +895,7 @@ export default function FormImageDetail() {
}
return total;
},
0
0,
);
// Hitung berapa banyak POLRES yang ter-checklist
@ -899,7 +905,7 @@ export default function FormImageDetail() {
return (
total +
item.subDestination.filter((sub: any) =>
currentFileLevels.has(Number(sub.id))
currentFileLevels.has(Number(sub.id)),
).length
);
}
@ -908,7 +914,7 @@ export default function FormImageDetail() {
// Cek apakah SATKER POLRI ter-checklist
const satkerItem = listDest.find(
(item: any) => item.name === "SATKER POLRI"
(item: any) => item.name === "SATKER POLRI",
);
const isSatkerChecked =
satkerItem && currentFileLevels.has(Number(satkerItem.id));
@ -945,7 +951,7 @@ export default function FormImageDetail() {
// Jika ini adalah POLDA yang di-unchecklist, unchecklist juga semua polres di bawahnya
const poldaItem = listDest.find(
(item: any) => Number(item.id) === levelId
(item: any) => Number(item.id) === levelId,
) as any;
if (
poldaItem &&
@ -968,7 +974,7 @@ export default function FormImageDetail() {
// 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
(item: any) => Number(item.id) === levelId,
) as any;
if (satkerItem && satkerItem.name === "SATKER POLRI") {
// Checklist semua sub-item di bawah SATKER POLRI (bukan POLRES)
@ -999,7 +1005,7 @@ export default function FormImageDetail() {
if (scheduleId && scheduleType === "3") {
const findCategory = resCategory.find((o) =>
o.name.toLowerCase().includes("pers rilis")
o.name.toLowerCase().includes("pers rilis"),
);
if (findCategory) {
@ -1048,6 +1054,11 @@ export default function FormImageDetail() {
// console.log("detail", details);
setFiles(details?.files);
setDetail(details);
if (details?.uploadedBy?.userLevel?.levelNumber) {
setCreatorLevelNumber(details.uploadedBy.userLevel.levelNumber);
}
setMain({
type: details?.fileType.name,
url: details?.files[0]?.url,
@ -1058,14 +1069,14 @@ export default function FormImageDetail() {
if (details?.assignedToLevel) {
const levels = new Set<number>(
details.assignedToLevel.split(",").map(Number)
details.assignedToLevel.split(",").map(Number),
);
setCheckedLevels(levels);
}
if (details?.publishedForObject) {
const publisherIds = details?.publishedForObject?.map(
(obj: any) => obj.id
(obj: any) => obj.id,
);
setSelectedPublishers(publisherIds);
}
@ -1075,7 +1086,7 @@ export default function FormImageDetail() {
const filesData = details.files || [];
const fileUrls = filesData.map((file: { thumbnailFileUrl: string }) =>
file.thumbnailFileUrl ? file.thumbnailFileUrl : "default-image.jpg"
file.thumbnailFileUrl ? file.thumbnailFileUrl : "default-image.jpg",
);
setDetailThumb(fileUrls);
@ -1195,7 +1206,7 @@ export default function FormImageDetail() {
const setupPlacement = (
index: number,
placement: string,
checked: boolean
checked: boolean,
) => {
let temp = [...filePlacements];
if (checked) {
@ -1212,7 +1223,7 @@ export default function FormImageDetail() {
setFileCheckedLevels((prevLevels) => {
const newArray = [...prevLevels];
const currentFileLevels = new Set<number>(
newArray[index] || new Set()
newArray[index] || new Set(),
);
// Checklist semua item di modal
@ -1262,7 +1273,7 @@ export default function FormImageDetail() {
// 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"
(item) => item !== "satker" && item !== "all",
);
if (nonSatkerItems.length === 3 && !now.includes("all")) {
now.push("all");
@ -1277,7 +1288,7 @@ export default function FormImageDetail() {
setFileCheckedLevels((prevLevels) => {
const newArray = [...prevLevels];
const currentFileLevels = new Set<number>(
newArray[index] || new Set()
newArray[index] || new Set(),
);
// Unchecklist semua item di modal
@ -1311,7 +1322,7 @@ export default function FormImageDetail() {
// Hapus "all" jika tidak semua item ter-checklist
if (now.includes("all")) {
const nonSatkerItems = now.filter(
(item) => item !== "satker" && item !== "all"
(item) => item !== "satker" && item !== "all",
);
if (nonSatkerItems.length < 3) {
const newData = now.filter((b) => b !== "all");
@ -1330,7 +1341,7 @@ export default function FormImageDetail() {
const updateModalChecklistLevels = (
fileIndex: number,
placement: string,
checked: boolean
checked: boolean,
) => {
if (!listDest || listDest.length === 0) return;
@ -1363,7 +1374,7 @@ export default function FormImageDetail() {
} else if (placement === "satker") {
// Checklist SATKER POLRI dan semua sub-item di bawahnya
const satkerItem: any = listDest.find(
(item: any) => item.name === "SATKER POLRI"
(item: any) => item.name === "SATKER POLRI",
);
if (satkerItem) {
currentFileLevels.add(Number(satkerItem.id));
@ -1399,7 +1410,7 @@ export default function FormImageDetail() {
} else if (placement === "satker") {
// Unchecklist SATKER POLRI dan semua sub-item di bawahnya
const satkerItem: any = listDest.find(
(item: any) => item.name === "SATKER POLRI"
(item: any) => item.name === "SATKER POLRI",
);
if (satkerItem) {
currentFileLevels.delete(Number(satkerItem.id));
@ -1429,7 +1440,7 @@ export default function FormImageDetail() {
type: string,
url: string,
names: string,
format: string
format: string,
) => {
// console.log("Test 3 :", type, url, names, format);
setMain({
@ -1532,7 +1543,7 @@ export default function FormImageDetail() {
{detail &&
!categories.find(
(cat) =>
String(cat.id) === String(detail.category.id)
String(cat.id) === String(detail.category.id),
) && (
<SelectItem
key={String(detail.category.id)}
@ -1853,56 +1864,6 @@ export default function FormImageDetail() {
Tingkat Distribusi:
</p>
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
{[
{ key: "semua", label: "Semua" },
{ key: "nasional", label: "Nasional" },
{ key: "wilayah", label: "Wilayah" },
{
key: "international",
label: "Internasional",
},
]
.filter(
(item) =>
!(
isUploadedBySatkerLevel3 &&
item.key === "wilayah"
)
)
.map((item) => (
<div
key={item.key}
className="flex items-center gap-2 p-2 border border-gray-200 rounded-md hover:bg-gray-50"
>
<Checkbox
id={`${item.key}-${index}`}
checked={
fileUnitSelections[index]?.[
item.key as keyof typeof unitSelection
] || false
}
onCheckedChange={(value) => {
handleFileUnitChange(
index,
item.key as keyof typeof unitSelection,
value as boolean
);
setupPlacement(
index,
item.key,
Boolean(value)
);
}}
/>
<Label
htmlFor={`${item.key}-${index}`}
className="text-sm font-medium cursor-pointer"
>
{item.label}
</Label>
</div>
))}
{/* {[
{ key: "semua", label: "Semua" },
{ key: "nasional", label: "Nasional" },
@ -1911,6 +1872,39 @@ export default function FormImageDetail() {
key: "international",
label: "Internasional",
},
] */}
{[
{ key: "semua", label: "Semua" },
...(isContentFromMabesOrPolda
? [
{
key: "nasional",
label: "Nasional",
},
{
key: "international",
label: "Internasional",
},
{
key: "wilayah",
label: "Wilayah",
},
]
: []),
...(isContentFromSatker
? [
{
key: "nasional",
label: "Nasional",
},
{
key: "international",
label: "Internasional",
},
]
: []),
].map((item, idx) => (
<div
key={item.key}
@ -1927,12 +1921,12 @@ export default function FormImageDetail() {
handleFileUnitChange(
index,
item.key as keyof typeof unitSelection,
value as boolean
value as boolean,
);
setupPlacement(
index,
item.key,
Boolean(value)
Boolean(value),
);
}}
/>
@ -1943,13 +1937,14 @@ export default function FormImageDetail() {
{item.label}
</Label>
</div>
))} */}
))}
</div>
</div>
{/* Detail Wilayah */}
{fileUnitSelections[index]?.wilayah &&
!isUploadedBySatkerLevel3 && (
{/* {fileUnitSelections[index]?.wilayah && ( */}
{!isContentFromSatker &&
fileUnitSelections[index]?.wilayah && (
<div className="border-t border-gray-200 pt-2">
<p className="text-sm font-medium text-gray-700 mb-2">
Detail Wilayah:
@ -1977,12 +1972,12 @@ export default function FormImageDetail() {
handleFileUnitChange(
index,
item.key as keyof typeof unitSelection,
value as boolean
value as boolean,
);
setupPlacement(
index,
item.key,
Boolean(value)
Boolean(value),
);
}}
/>
@ -2035,13 +2030,13 @@ export default function FormImageDetail() {
fileCheckedLevels[
index
]?.has(
Number(polda.id)
Number(polda.id),
) || false
}
onCheckedChange={() =>
handleFileCheckboxChangePlacement(
index,
Number(polda.id)
Number(polda.id),
)
}
/>
@ -2055,7 +2050,7 @@ export default function FormImageDetail() {
e.preventDefault();
e.stopPropagation();
toggleExpand(
polda.id
polda.id,
);
}}
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
@ -2091,9 +2086,9 @@ export default function FormImageDetail() {
index
]?.has(
Number(
sub.id
)
)
sub.id,
),
),
);
return (
<Button
@ -2103,7 +2098,7 @@ export default function FormImageDetail() {
onClick={() =>
handleSelectAllSubItems(
index,
polda
polda,
)
}
>
@ -2155,16 +2150,16 @@ export default function FormImageDetail() {
index
]?.has(
Number(
sub.id
)
sub.id,
),
) || false
}
onCheckedChange={() =>
handleFileCheckboxChangePlacement(
index,
Number(
sub.id
)
sub.id,
),
)
}
/>
@ -2172,7 +2167,7 @@ export default function FormImageDetail() {
{sub.name}
</span>
</Label>
)
),
)}
</div>
</div>
@ -2276,7 +2271,7 @@ export default function FormImageDetail() {
>
{template}
</Button>
)
),
)}
</div>
</div>

View File

@ -82,7 +82,7 @@ const CustomEditor = dynamic(
() => {
return import("@/components/editor/custom-editor");
},
{ ssr: false }
{ ssr: false },
);
export default function FormImage() {
@ -117,7 +117,7 @@ export default function FormImage() {
const [isGeneratedArticle, setIsGeneratedArticle] = useState(false);
const [articleBody, setArticleBody] = useState<string>("");
const [selectedArticleId, setSelectedArticleId] = useState<string | null>(
null
null,
);
const [selectedMainKeyword, setSelectedMainKeyword] = useState("");
const [selectedWritingStyle, setSelectedWritingStyle] =
@ -183,17 +183,17 @@ export default function FormImage() {
.filter(
(file) =>
["image/jpeg", "image/png", "image/jpg"].includes(file.type) &&
file.size <= MAX_FILE_SIZE
file.size <= MAX_FILE_SIZE,
)
.map((file) =>
Object.assign(file, {
preview: URL.createObjectURL(file),
})
}),
);
if (validFiles.length === 0) {
toast.error(
"File tidak valid. Hanya .jpg, .jpeg, .png maksimal 100MB yang diperbolehkan."
"File tidak valid. Hanya .jpg, .jpeg, .png maksimal 100MB yang diperbolehkan.",
);
return;
}
@ -227,12 +227,12 @@ export default function FormImage() {
files.every(
(file: File) =>
["image/jpeg", "image/png", "image/jpg"].includes(file.type) &&
file.size <= 100 * 1024 * 1024
file.size <= 100 * 1024 * 1024,
),
{
message:
"Hanya file .jpg, .jpeg, .png, maksimal 100MB yang diperbolehkan.",
}
},
),
categoryId: z.string().min(1, { message: "Kategori wajib dipilih." }),
tags: z
@ -454,7 +454,7 @@ export default function FormImage() {
const articleData = await waitForStatusUpdate();
const cleanArticleBody = articleData?.articleBody?.replace(
/<img[^>]*>/g,
""
"",
);
const articleImagesData = articleData?.imagesUrl?.split(",");
setArticleBody(cleanArticleBody || "");
@ -507,7 +507,7 @@ export default function FormImage() {
if (scheduleId && scheduleType === "3") {
const findCategory = resCategory.find((o) =>
o.name.toLowerCase().includes("pers rilis")
o.name.toLowerCase().includes("pers rilis"),
);
if (findCategory) {
@ -530,7 +530,7 @@ export default function FormImage() {
setPublishedFor(
options
.filter((opt: any) => opt.id !== "all")
.map((opt: any) => opt.id)
.map((opt: any) => opt.id),
);
}
} else {
@ -594,11 +594,16 @@ export default function FormImage() {
} else if (data.rewriteDescription && selectedFileType === "rewrite") {
finalDescription = data.rewriteDescription;
console.log("📤 Upload versi rewrite");
} else {
// fallback: gunakan versi Indonesia original
finalDescription = data.descriptionOri ?? "";
console.log("📤 Upload versi Indonesia (original)");
} else if (data.description?.trim()) {
finalDescription = data.description;
} else if (data.descriptionOri?.trim()) {
finalDescription = data.descriptionOri;
}
// else {
// // fallback: gunakan versi Indonesia original
// finalDescription = data.descriptionOri ?? "";
// console.log("📤 Upload versi Indonesia (original)");
// }
if (!finalDescription?.trim()) {
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
@ -690,7 +695,7 @@ export default function FormImage() {
index,
String(id),
item,
fileTypeId == "2" || fileTypeId == "4" ? item.duration : "0"
fileTypeId == "2" || fileTypeId == "4" ? item.duration : "0",
);
});
@ -717,7 +722,7 @@ export default function FormImage() {
idx: number,
id: string,
file: any,
duration: string
duration: string,
) {
// console.log(idx, id, file, duration);
@ -753,7 +758,7 @@ export default function FormImage() {
onChunkComplete: (
chunkSize: any,
bytesAccepted: any,
bytesTotal: any
bytesTotal: any,
) => {
const uploadPersen = Math.floor((bytesAccepted / bytesTotal) * 100);
progressInfo[idx].percentage = uploadPersen;

View File

@ -110,7 +110,7 @@ const ViewEditor = dynamic(
() => {
return import("@/components/editor/view-editor");
},
{ ssr: false }
{ ssr: false },
);
interface Destination {
@ -189,6 +189,12 @@ export default function FormTeksDetail() {
satker: boolean;
}>
>([]);
const [creatorLevelNumber, setCreatorLevelNumber] = useState<number | null>(
null,
);
const isContentFromSatker = creatorLevelNumber === 3;
const isContentFromMabesOrPolda =
creatorLevelNumber === 1 || creatorLevelNumber === 2;
useEffect(() => {
if (Number(userLevelId) === 216 && Number(roleId) === 3) {
@ -200,7 +206,7 @@ export default function FormTeksDetail() {
const handleFileUnitChange = (
fileIndex: number,
key: keyof typeof unitSelection,
value: boolean
value: boolean,
) => {
setFileUnitSelections((prev) => {
const newSelections = [...prev];
@ -269,12 +275,12 @@ export default function FormTeksDetail() {
(item: any) =>
item.levelNumber === 2 &&
item.name !== "SATKER POLRI" &&
currentFileCheckedLevels.has(Number(item.id))
currentFileCheckedLevels.has(Number(item.id)),
);
if (!hasSelectedPolda) {
alert(
"Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES."
"Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES.",
);
return prev;
}
@ -397,7 +403,7 @@ export default function FormTeksDetail() {
useEffect(() => {
if (detail?.assignedToTopLevel) {
const outputSet = new Set(
detail.assignedToTopLevel.split(",").map(Number)
detail.assignedToTopLevel.split(",").map(Number),
);
setUnitSelection({
semua: outputSet.has(0),
@ -422,7 +428,7 @@ export default function FormTeksDetail() {
acc[polda.id] = false;
return acc;
},
{}
{},
);
setExpandedPolda(initialExpandedState);
console.log("polres", initialExpandedState);
@ -457,7 +463,7 @@ export default function FormTeksDetail() {
const handleUnitChange = (
key: keyof typeof unitSelection,
value: boolean
value: boolean,
) => {
if (key === "semua") {
const newState = {
@ -536,7 +542,7 @@ export default function FormTeksDetail() {
const handleCheckboxChange = (id: number) => {
setSelectedPublishers((prev) =>
prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id],
);
};
@ -558,7 +564,7 @@ export default function FormTeksDetail() {
if (scheduleId && scheduleType === "3") {
const findCategory = resCategory.find((o) =>
o.name.toLowerCase().includes("pers rilis")
o.name.toLowerCase().includes("pers rilis"),
);
if (findCategory) {
@ -607,6 +613,9 @@ export default function FormTeksDetail() {
// console.log("detail", details);
setFiles(details?.files);
setDetail(details);
if (details?.uploadedBy?.userLevel?.levelNumber) {
setCreatorLevelNumber(details.uploadedBy.userLevel.levelNumber);
}
setMain({
type: details?.fileType.name,
url: details?.files[0]?.url,
@ -617,14 +626,14 @@ export default function FormTeksDetail() {
if (details?.assignedToLevel) {
const levels = new Set(
details.assignedToLevel.split(",").map(Number)
details.assignedToLevel.split(",").map(Number),
);
setCheckedLevels(levels);
}
if (details?.publishedForObject) {
const publisherIds = details?.publishedForObject?.map(
(obj: any) => obj.id
(obj: any) => obj.id,
);
setSelectedPublishers(publisherIds);
}
@ -796,7 +805,7 @@ export default function FormTeksDetail() {
type: string,
url: string,
names: string,
format: string
format: string,
) => {
// console.log("Test 3 :", type, url, names, format);
setMain({
@ -823,7 +832,7 @@ export default function FormTeksDetail() {
const setupPlacement = (
index: number,
placement: string,
checked: boolean
checked: boolean,
) => {
let temp = [...filePlacements];
if (checked) {
@ -841,7 +850,7 @@ export default function FormTeksDetail() {
setFileCheckedLevels((prevLevels) => {
const newArray = [...prevLevels];
const currentFileLevels = new Set<number>(
newArray[index] || new Set()
newArray[index] || new Set(),
);
// Checklist semua item di modal
@ -891,7 +900,7 @@ export default function FormTeksDetail() {
// 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"
(item) => item !== "satker" && item !== "all",
);
if (nonSatkerItems.length === 3 && !now.includes("all")) {
now.push("all");
@ -906,7 +915,7 @@ export default function FormTeksDetail() {
setFileCheckedLevels((prevLevels) => {
const newArray = [...prevLevels];
const currentFileLevels = new Set<number>(
newArray[index] || new Set()
newArray[index] || new Set(),
);
// Unchecklist semua item di modal
@ -940,7 +949,7 @@ export default function FormTeksDetail() {
// Hapus "all" jika tidak semua item ter-checklist
if (now.includes("all")) {
const nonSatkerItems = now.filter(
(item) => item !== "satker" && item !== "all"
(item) => item !== "satker" && item !== "all",
);
if (nonSatkerItems.length < 3) {
const newData = now.filter((b) => b !== "all");
@ -958,7 +967,7 @@ export default function FormTeksDetail() {
const updateModalChecklistLevels = (
fileIndex: number,
placement: string,
checked: boolean
checked: boolean,
) => {
if (!listDest || listDest.length === 0) return;
@ -991,7 +1000,7 @@ export default function FormTeksDetail() {
} else if (placement === "satker") {
// Checklist SATKER POLRI dan semua sub-item di bawahnya
const satkerItem: any = listDest.find(
(item: any) => item.name === "SATKER POLRI"
(item: any) => item.name === "SATKER POLRI",
);
if (satkerItem) {
currentFileLevels.add(Number(satkerItem.id));
@ -1027,7 +1036,7 @@ export default function FormTeksDetail() {
} else if (placement === "satker") {
// Unchecklist SATKER POLRI dan semua sub-item di bawahnya
const satkerItem: any = listDest.find(
(item: any) => item.name === "SATKER POLRI"
(item: any) => item.name === "SATKER POLRI",
);
if (satkerItem) {
currentFileLevels.delete(Number(satkerItem.id));
@ -1060,7 +1069,7 @@ export default function FormTeksDetail() {
// Fungsi untuk mengupdate checklist levels untuk file tertentu
const handleFileCheckboxChangePlacement = (
fileIndex: number,
levelId: number
levelId: number,
) => {
setFileCheckedLevels((prev) => {
const newArray = [...prev];
@ -1072,7 +1081,7 @@ export default function FormTeksDetail() {
// Jika ini adalah POLDA yang di-unchecklist, unchecklist juga semua polres di bawahnya
const poldaItem = listDest.find(
(item: any) => Number(item.id) === levelId
(item: any) => Number(item.id) === levelId,
) as any;
if (
poldaItem &&
@ -1095,7 +1104,7 @@ export default function FormTeksDetail() {
// 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
(item: any) => Number(item.id) === levelId,
) as any;
if (satkerItem && satkerItem.name === "SATKER POLRI") {
// Checklist semua sub-item di bawah SATKER POLRI (bukan POLRES)
@ -1126,7 +1135,7 @@ export default function FormTeksDetail() {
// Hitung total POLDA yang ada (bukan SATKER POLRI)
const totalPoldaCount = listDest.filter(
(item: any) => item.levelNumber === 2 && item.name !== "SATKER POLRI"
(item: any) => item.levelNumber === 2 && item.name !== "SATKER POLRI",
).length;
// Hitung berapa banyak POLDA yang ter-checklist (bukan SATKER POLRI)
@ -1153,7 +1162,7 @@ export default function FormTeksDetail() {
}
return total;
},
0
0,
);
// Hitung berapa banyak POLRES yang ter-checklist
@ -1163,7 +1172,7 @@ export default function FormTeksDetail() {
return (
total +
item.subDestination.filter((sub: any) =>
currentFileLevels.has(Number(sub.id))
currentFileLevels.has(Number(sub.id)),
).length
);
}
@ -1172,7 +1181,7 @@ export default function FormTeksDetail() {
// Cek apakah SATKER POLRI ter-checklist
const satkerItem = listDest.find(
(item: any) => item.name === "SATKER POLRI"
(item: any) => item.name === "SATKER POLRI",
);
const isSatkerChecked =
satkerItem && currentFileLevels.has(Number(satkerItem.id));
@ -1206,7 +1215,7 @@ export default function FormTeksDetail() {
// Cek apakah semua sub-items sudah ter-checklist
const allSubItemsChecked = polda.subDestination?.every((sub: any) =>
currentFileLevels.has(Number(sub.id))
currentFileLevels.has(Number(sub.id)),
);
if (allSubItemsChecked) {
@ -1292,7 +1301,7 @@ export default function FormTeksDetail() {
{detail &&
!categories.find(
(cat) =>
String(cat.id) === String(detail.category.id)
String(cat.id) === String(detail.category.id),
) && (
<SelectItem
key={String(detail.category.id)}
@ -1575,56 +1584,6 @@ export default function FormTeksDetail() {
Tingkat Distribusi:
</p>
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
{[
{ key: "semua", label: "Semua" },
{ key: "nasional", label: "Nasional" },
{ key: "wilayah", label: "Wilayah" },
{
key: "international",
label: "Internasional",
},
]
.filter(
(item) =>
!(
isUploadedBySatkerLevel3 &&
item.key === "wilayah"
)
)
.map((item) => (
<div
key={item.key}
className="flex items-center gap-2 p-2 border border-gray-200 rounded-md hover:bg-gray-50"
>
<Checkbox
id={`${item.key}-${index}`}
checked={
fileUnitSelections[index]?.[
item.key as keyof typeof unitSelection
] || false
}
onCheckedChange={(value) => {
handleFileUnitChange(
index,
item.key as keyof typeof unitSelection,
value as boolean
);
setupPlacement(
index,
item.key,
Boolean(value)
);
}}
/>
<Label
htmlFor={`${item.key}-${index}`}
className="text-sm font-medium cursor-pointer"
>
{item.label}
</Label>
</div>
))}
{/* {[
{ key: "semua", label: "Semua" },
{ key: "nasional", label: "Nasional" },
@ -1633,6 +1592,39 @@ export default function FormTeksDetail() {
key: "international",
label: "Internasional",
},
] */}
{[
{ key: "semua", label: "Semua" },
...(isContentFromMabesOrPolda
? [
{
key: "nasional",
label: "Nasional",
},
{
key: "international",
label: "Internasional",
},
{
key: "wilayah",
label: "Wilayah",
},
]
: []),
...(isContentFromSatker
? [
{
key: "nasional",
label: "Nasional",
},
{
key: "international",
label: "Internasional",
},
]
: []),
].map((item, idx) => (
<div
key={item.key}
@ -1649,12 +1641,12 @@ export default function FormTeksDetail() {
handleFileUnitChange(
index,
item.key as keyof typeof unitSelection,
value as boolean
value as boolean,
);
setupPlacement(
index,
item.key,
Boolean(value)
Boolean(value),
);
}}
/>
@ -1665,256 +1657,267 @@ export default function FormTeksDetail() {
{item.label}
</Label>
</div>
))} */}
))}
</div>
</div>
{/* Detail Wilayah */}
{fileUnitSelections[index]?.wilayah && (
<div className="border-t border-gray-200 pt-2">
<p className="text-sm font-medium text-gray-700 mb-2">
Detail Wilayah:
</p>
{/* {fileUnitSelections[index]?.wilayah && ( */}
{!isContentFromSatker &&
fileUnitSelections[index]?.wilayah && (
<div className="border-t border-gray-200 pt-2">
<p className="text-sm font-medium text-gray-700 mb-2">
Detail Wilayah:
</p>
{/* Checkbox Sub-kategori dengan tombol Kustom sejajar */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
{[
{ key: "polda", label: "POLDA" },
{ key: "polres", label: "POLRES" },
{ key: "satker", label: "SATKER" },
].map((item, idx) => (
<div
key={item.key}
className="flex items-center gap-2 p-3 border border-gray-200 rounded-md hover:bg-gray-50"
>
<Checkbox
id={`${item.key}-${index}`}
checked={
fileUnitSelections[index]?.[
item.key as keyof typeof unitSelection
] || false
}
onCheckedChange={(value) => {
handleFileUnitChange(
index,
item.key as keyof typeof unitSelection,
value as boolean
);
setupPlacement(
index,
item.key,
Boolean(value)
);
}}
/>
<Label
htmlFor={`${item.key}-${index}`}
className="text-sm font-medium cursor-pointer"
{/* Checkbox Sub-kategori dengan tombol Kustom sejajar */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
{[
{ key: "polda", label: "POLDA" },
{ key: "polres", label: "POLRES" },
{ key: "satker", label: "SATKER" },
].map((item, idx) => (
<div
key={item.key}
className="flex items-center gap-2 p-3 border border-gray-200 rounded-md hover:bg-gray-50"
>
{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"
<Checkbox
id={`${item.key}-${index}`}
checked={
fileUnitSelections[index]?.[
item.key as keyof typeof unitSelection
] || false
}
onCheckedChange={(value) => {
handleFileUnitChange(
index,
item.key as keyof typeof unitSelection,
value as boolean,
);
setupPlacement(
index,
item.key,
Boolean(value),
);
}}
/>
<Label
htmlFor={`${item.key}-${index}`}
className="text-sm font-medium cursor-pointer"
>
<Icon
icon="material-symbols:tune"
width={16}
height={16}
/>
{t("custom", {
defaultValue: "Kustom",
})}
</Button>
</DialogTrigger>
<DialogContent className="max-w-[95vw] lg:max-w-[1400px] max-h-[90vh]">
<DialogHeader className="border-b border-gray-200 pb-4">
<DialogTitle className="text-lg font-semibold">
Daftar Wilayah POLDA dan POLRES
</DialogTitle>
</DialogHeader>
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4 max-h-[70vh] overflow-y-auto p-1">
{listDest.map((polda: any) => (
<div
key={polda.id}
className="border border-gray-200 rounded-lg p-2 bg-white hover:shadow-sm transition-shadow"
>
{/* Header POLDA */}
<div className="flex items-center justify-between">
<Label className="flex items-center gap-3 flex-1 cursor-pointer">
<Checkbox
checked={
fileCheckedLevels[
index
]?.has(
Number(polda.id)
) || false
}
onCheckedChange={() =>
handleFileCheckboxChangePlacement(
index,
Number(polda.id)
)
}
/>
<span className="font-semibold text-gray-900 text-sm">
{polda.name}
</span>
</Label>
{polda.subDestination && (
<button
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
toggleExpand(
polda.id
);
}}
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
>
<Icon
icon={
expandedPolda[
polda.id
]
? "mdi:chevron-up"
: "mdi:chevron-down"
}
width={16}
height={16}
/>
</button>
)}
</div>
{item.label}
</Label>
</div>
))}
{/* Sub-items */}
{polda.subDestination &&
expandedPolda[polda.id] && (
<div className="max-h-[200px] overflow-y-auto border-t border-gray-100 pt-2">
{/* Tombol Pilih Semua untuk sub-items */}
<div className="mb-2 flex justify-start">
{(() => {
const allSubItemsChecked =
polda.subDestination?.every(
(sub: any) =>
fileCheckedLevels[
index
]?.has(
Number(
sub.id
)
)
);
return (
<Button
size="sm"
variant="outline"
className="text-xs h-6 px-2"
onClick={() =>
handleSelectAllSubItems(
index,
polda
)
}
>
{allSubItemsChecked ? (
<>
<Icon
icon="material-symbols:check-indeterminate-small"
width={12}
height={
12
}
className="mr-1"
/>
Batal Semua
</>
) : (
<>
<Icon
icon="material-symbols:check-all"
width={12}
height={
12
}
className="mr-1"
/>
Pilih Semua
</>
)}
</Button>
{/* Tombol Kustom sejajar dengan checkbox */}
<div className="flex items-center justify-center p-3">
<Dialog>
<DialogTrigger asChild>
<Button
variant="outline"
size="sm"
className="gap-2"
>
<Icon
icon="material-symbols:tune"
width={16}
height={16}
/>
{t("custom", {
defaultValue: "Kustom",
})}
</Button>
</DialogTrigger>
<DialogContent className="max-w-[95vw] lg:max-w-[1400px] max-h-[90vh]">
<DialogHeader className="border-b border-gray-200 pb-4">
<DialogTitle className="text-lg font-semibold">
Daftar Wilayah POLDA dan
POLRES
</DialogTitle>
</DialogHeader>
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4 max-h-[70vh] overflow-y-auto p-1">
{listDest.map((polda: any) => (
<div
key={polda.id}
className="border border-gray-200 rounded-lg p-2 bg-white hover:shadow-sm transition-shadow"
>
{/* Header POLDA */}
<div className="flex items-center justify-between">
<Label className="flex items-center gap-3 flex-1 cursor-pointer">
<Checkbox
checked={
fileCheckedLevels[
index
]?.has(
Number(polda.id),
) || false
}
onCheckedChange={() =>
handleFileCheckboxChangePlacement(
index,
Number(polda.id),
)
}
/>
<span className="font-semibold text-gray-900 text-sm">
{polda.name}
</span>
</Label>
{polda.subDestination && (
<button
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
toggleExpand(
polda.id,
);
})()}
</div>
<div className="space-y-1">
{polda.subDestination.map(
(sub: any) => (
<Label
key={sub.id}
className="flex items-center gap-2 p-2 rounded-md hover:bg-gray-50 transition-colors cursor-pointer text-xs"
>
<Checkbox
checked={
}}
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
>
<Icon
icon={
expandedPolda[
polda.id
]
? "mdi:chevron-up"
: "mdi:chevron-down"
}
width={16}
height={16}
/>
</button>
)}
</div>
{/* Sub-items */}
{polda.subDestination &&
expandedPolda[
polda.id
] && (
<div className="max-h-[200px] overflow-y-auto border-t border-gray-100 pt-2">
{/* Tombol Pilih Semua untuk sub-items */}
<div className="mb-2 flex justify-start">
{(() => {
const allSubItemsChecked =
polda.subDestination?.every(
(sub: any) =>
fileCheckedLevels[
index
]?.has(
Number(
sub.id
)
) || false
}
onCheckedChange={() =>
handleFileCheckboxChangePlacement(
sub.id,
),
),
);
return (
<Button
size="sm"
variant="outline"
className="text-xs h-6 px-2"
onClick={() =>
handleSelectAllSubItems(
index,
Number(
sub.id
)
polda,
)
}
/>
<span className="text-gray-700">
{sub.name}
</span>
</Label>
)
)}
>
{allSubItemsChecked ? (
<>
<Icon
icon="material-symbols:check-indeterminate-small"
width={
12
}
height={
12
}
className="mr-1"
/>
Batal
Semua
</>
) : (
<>
<Icon
icon="material-symbols:check-all"
width={
12
}
height={
12
}
className="mr-1"
/>
Pilih
Semua
</>
)}
</Button>
);
})()}
</div>
<div className="space-y-1">
{polda.subDestination.map(
(sub: any) => (
<Label
key={sub.id}
className="flex items-center gap-2 p-2 rounded-md hover:bg-gray-50 transition-colors cursor-pointer text-xs"
>
<Checkbox
checked={
fileCheckedLevels[
index
]?.has(
Number(
sub.id,
),
) || false
}
onCheckedChange={() =>
handleFileCheckboxChangePlacement(
index,
Number(
sub.id,
),
)
}
/>
<span className="text-gray-700">
{sub.name}
</span>
</Label>
),
)}
</div>
</div>
</div>
)}
</div>
))}
</div>
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
<DialogClose asChild>
<Button variant="outline">
{t("cancel", {
defaultValue: "Batal",
})}
</Button>
</DialogClose>
<DialogClose asChild>
<Button>
{/* {t("save", {
)}
</div>
))}
</div>
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
<DialogClose asChild>
<Button variant="outline">
{t("cancel", {
defaultValue: "Batal",
})}
</Button>
</DialogClose>
<DialogClose asChild>
<Button>
{/* {t("save", {
defaultValue: "Simpan",
})} */}
Simpan
</Button>
</DialogClose>
</div>
</DialogContent>
</Dialog>
Simpan
</Button>
</DialogClose>
</div>
</DialogContent>
</Dialog>
</div>
</div>
</div>
</div>
)}
)}
</div>
</div>
) : (
@ -1993,7 +1996,7 @@ export default function FormTeksDetail() {
>
{template}
</Button>
)
),
)}
</div>
</div>

View File

@ -1776,7 +1776,7 @@ export default function FormTeks() {
{/* <Button type="submit" color="primary">
{t("submit", { defaultValue: "Submit" })}
</Button> */}
{levelNumber !== "2" && levelNumber !== "3" && (
{levelNumber !== "2" && (
<Button type="submit" color="primary">
{t("submit", { defaultValue: "Submit" })}
</Button>

View File

@ -115,7 +115,7 @@ const ViewEditor = dynamic(
() => {
return import("@/components/editor/view-editor");
},
{ ssr: false }
{ ssr: false },
);
interface Destination {
@ -192,6 +192,12 @@ export default function FormVideoDetail() {
satker: boolean;
}>
>([]);
const [creatorLevelNumber, setCreatorLevelNumber] = useState<number | null>(
null,
);
const isContentFromSatker = creatorLevelNumber === 3;
const isContentFromMabesOrPolda =
creatorLevelNumber === 1 || creatorLevelNumber === 2;
useEffect(() => {
if (Number(userLevelId) === 216 && Number(roleId) === 3) {
@ -203,7 +209,7 @@ export default function FormVideoDetail() {
const handleFileUnitChange = (
fileIndex: number,
key: keyof typeof unitSelection,
value: boolean
value: boolean,
) => {
setFileUnitSelections((prev) => {
const newSelections = [...prev];
@ -272,12 +278,12 @@ export default function FormVideoDetail() {
(item: any) =>
item.levelNumber === 2 &&
item.name !== "SATKER POLRI" &&
currentFileCheckedLevels.has(Number(item.id))
currentFileCheckedLevels.has(Number(item.id)),
);
if (!hasSelectedPolda) {
alert(
"Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES."
"Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES.",
);
return prev;
}
@ -400,7 +406,7 @@ export default function FormVideoDetail() {
useEffect(() => {
if (detail?.assignedToTopLevel) {
const outputSet = new Set(
detail.assignedToTopLevel.split(",").map(Number)
detail.assignedToTopLevel.split(",").map(Number),
);
setUnitSelection({
semua: outputSet.has(0),
@ -425,7 +431,7 @@ export default function FormVideoDetail() {
acc[polda.id] = false;
return acc;
},
{}
{},
);
setExpandedPolda(initialExpandedState);
console.log("polres", initialExpandedState);
@ -460,7 +466,7 @@ export default function FormVideoDetail() {
const handleUnitChange = (
key: keyof typeof unitSelection,
value: boolean
value: boolean,
) => {
if (key === "semua") {
const newState = {
@ -539,7 +545,7 @@ export default function FormVideoDetail() {
const handleCheckboxChange = (id: number) => {
setSelectedPublishers((prev) =>
prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id],
);
};
@ -561,7 +567,7 @@ export default function FormVideoDetail() {
if (scheduleId && scheduleType === "3") {
const findCategory = resCategory.find((o) =>
o.name.toLowerCase().includes("pers rilis")
o.name.toLowerCase().includes("pers rilis"),
);
if (findCategory) {
@ -584,6 +590,10 @@ export default function FormVideoDetail() {
// console.log("detail", details);
setFiles(details?.files);
setDetail(details);
if (details?.uploadedBy?.userLevel?.levelNumber) {
setCreatorLevelNumber(details.uploadedBy.userLevel.levelNumber);
}
setMain({
type: details?.fileType.name,
url: details?.files[0]?.url,
@ -593,14 +603,14 @@ export default function FormVideoDetail() {
if (details?.assignedToLevel) {
const levels = new Set(
details.assignedToLevel.split(",").map(Number)
details.assignedToLevel.split(",").map(Number),
);
setCheckedLevels(levels);
}
if (details?.publishedForObject) {
const publisherIds = details?.publishedForObject?.map(
(obj: any) => obj.id
(obj: any) => obj.id,
);
setSelectedPublishers(publisherIds);
}
@ -610,7 +620,7 @@ export default function FormVideoDetail() {
const filesData = details?.files || [];
const fileUrls = filesData.map((files: { url: string }) =>
files.url ? files.url : "default-image.jpg"
files.url ? files.url : "default-image.jpg",
);
setDetailVideo(fileUrls);
@ -788,7 +798,7 @@ export default function FormVideoDetail() {
const setupPlacement = (
index: number,
placement: string,
checked: boolean
checked: boolean,
) => {
let temp = [...filePlacements];
if (checked) {
@ -806,7 +816,7 @@ export default function FormVideoDetail() {
setFileCheckedLevels((prevLevels) => {
const newArray = [...prevLevels];
const currentFileLevels = new Set<number>(
newArray[index] || new Set()
newArray[index] || new Set(),
);
// Checklist semua item di modal
@ -856,7 +866,7 @@ export default function FormVideoDetail() {
// 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"
(item) => item !== "satker" && item !== "all",
);
if (nonSatkerItems.length === 3 && !now.includes("all")) {
now.push("all");
@ -871,7 +881,7 @@ export default function FormVideoDetail() {
setFileCheckedLevels((prevLevels) => {
const newArray = [...prevLevels];
const currentFileLevels = new Set<number>(
newArray[index] || new Set()
newArray[index] || new Set(),
);
// Unchecklist semua item di modal
@ -905,7 +915,7 @@ export default function FormVideoDetail() {
// Hapus "all" jika tidak semua item ter-checklist
if (now.includes("all")) {
const nonSatkerItems = now.filter(
(item) => item !== "satker" && item !== "all"
(item) => item !== "satker" && item !== "all",
);
if (nonSatkerItems.length < 3) {
const newData = now.filter((b) => b !== "all");
@ -923,7 +933,7 @@ export default function FormVideoDetail() {
const updateModalChecklistLevels = (
fileIndex: number,
placement: string,
checked: boolean
checked: boolean,
) => {
if (!listDest || listDest.length === 0) return;
@ -956,7 +966,7 @@ export default function FormVideoDetail() {
} else if (placement === "satker") {
// Checklist SATKER POLRI dan semua sub-item di bawahnya
const satkerItem: any = listDest.find(
(item: any) => item.name === "SATKER POLRI"
(item: any) => item.name === "SATKER POLRI",
);
if (satkerItem) {
currentFileLevels.add(Number(satkerItem.id));
@ -992,7 +1002,7 @@ export default function FormVideoDetail() {
} else if (placement === "satker") {
// Unchecklist SATKER POLRI dan semua sub-item di bawahnya
const satkerItem: any = listDest.find(
(item: any) => item.name === "SATKER POLRI"
(item: any) => item.name === "SATKER POLRI",
);
if (satkerItem) {
currentFileLevels.delete(Number(satkerItem.id));
@ -1021,7 +1031,7 @@ export default function FormVideoDetail() {
type: string,
url: string,
names: string,
format: string
format: string,
) => {
// console.log("Test 3 :", type, url, names, format);
setMain({
@ -1062,7 +1072,7 @@ export default function FormVideoDetail() {
// Fungsi untuk mengupdate checklist levels untuk file tertentu
const handleFileCheckboxChangePlacement = (
fileIndex: number,
levelId: number
levelId: number,
) => {
setFileCheckedLevels((prev) => {
const newArray = [...prev];
@ -1074,7 +1084,7 @@ export default function FormVideoDetail() {
// Jika ini adalah POLDA yang di-unchecklist, unchecklist juga semua polres di bawahnya
const poldaItem = listDest.find(
(item: any) => Number(item.id) === levelId
(item: any) => Number(item.id) === levelId,
) as any;
if (
poldaItem &&
@ -1097,7 +1107,7 @@ export default function FormVideoDetail() {
// 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
(item: any) => Number(item.id) === levelId,
) as any;
if (satkerItem && satkerItem.name === "SATKER POLRI") {
// Checklist semua sub-item di bawah SATKER POLRI (bukan POLRES)
@ -1128,7 +1138,7 @@ export default function FormVideoDetail() {
// Hitung total POLDA yang ada (bukan SATKER POLRI)
const totalPoldaCount = listDest.filter(
(item: any) => item.levelNumber === 2 && item.name !== "SATKER POLRI"
(item: any) => item.levelNumber === 2 && item.name !== "SATKER POLRI",
).length;
// Hitung berapa banyak POLDA yang ter-checklist (bukan SATKER POLRI)
@ -1155,7 +1165,7 @@ export default function FormVideoDetail() {
}
return total;
},
0
0,
);
// Hitung berapa banyak POLRES yang ter-checklist
@ -1165,7 +1175,7 @@ export default function FormVideoDetail() {
return (
total +
item.subDestination.filter((sub: any) =>
currentFileLevels.has(Number(sub.id))
currentFileLevels.has(Number(sub.id)),
).length
);
}
@ -1174,7 +1184,7 @@ export default function FormVideoDetail() {
// Cek apakah SATKER POLRI ter-checklist
const satkerItem = listDest.find(
(item: any) => item.name === "SATKER POLRI"
(item: any) => item.name === "SATKER POLRI",
);
const isSatkerChecked =
satkerItem && currentFileLevels.has(Number(satkerItem.id));
@ -1208,7 +1218,7 @@ export default function FormVideoDetail() {
// Cek apakah semua sub-items sudah ter-checklist
const allSubItemsChecked = polda.subDestination?.every((sub: any) =>
currentFileLevels.has(Number(sub.id))
currentFileLevels.has(Number(sub.id)),
);
if (allSubItemsChecked) {
@ -1294,7 +1304,7 @@ export default function FormVideoDetail() {
{detail &&
!categories.find(
(cat) =>
String(cat.id) === String(detail.category.id)
String(cat.id) === String(detail.category.id),
) && (
<SelectItem
key={String(detail.category.id)}
@ -1579,56 +1589,6 @@ export default function FormVideoDetail() {
Tingkat Distribusi:
</p>
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
{[
{ key: "semua", label: "Semua" },
{ key: "nasional", label: "Nasional" },
{ key: "wilayah", label: "Wilayah" },
{
key: "international",
label: "Internasional",
},
]
.filter(
(item) =>
!(
isUploadedBySatkerLevel3 &&
item.key === "wilayah"
)
)
.map((item) => (
<div
key={item.key}
className="flex items-center gap-2 p-2 border border-gray-200 rounded-md hover:bg-gray-50"
>
<Checkbox
id={`${item.key}-${index}`}
checked={
fileUnitSelections[index]?.[
item.key as keyof typeof unitSelection
] || false
}
onCheckedChange={(value) => {
handleFileUnitChange(
index,
item.key as keyof typeof unitSelection,
value as boolean
);
setupPlacement(
index,
item.key,
Boolean(value)
);
}}
/>
<Label
htmlFor={`${item.key}-${index}`}
className="text-sm font-medium cursor-pointer"
>
{item.label}
</Label>
</div>
))}
{/* {[
{ key: "semua", label: "Semua" },
{ key: "nasional", label: "Nasional" },
@ -1637,6 +1597,39 @@ export default function FormVideoDetail() {
key: "international",
label: "Internasional",
},
] */}
{[
{ key: "semua", label: "Semua" },
...(isContentFromMabesOrPolda
? [
{
key: "nasional",
label: "Nasional",
},
{
key: "international",
label: "Internasional",
},
{
key: "wilayah",
label: "Wilayah",
},
]
: []),
...(isContentFromSatker
? [
{
key: "nasional",
label: "Nasional",
},
{
key: "international",
label: "Internasional",
},
]
: []),
].map((item, idx) => (
<div
key={item.key}
@ -1653,12 +1646,12 @@ export default function FormVideoDetail() {
handleFileUnitChange(
index,
item.key as keyof typeof unitSelection,
value as boolean
value as boolean,
);
setupPlacement(
index,
item.key,
Boolean(value)
Boolean(value),
);
}}
/>
@ -1669,13 +1662,14 @@ export default function FormVideoDetail() {
{item.label}
</Label>
</div>
))} */}
))}
</div>
</div>
{/* Detail Wilayah */}
{fileUnitSelections[index]?.wilayah &&
!isUploadedBySatkerLevel3 && (
{/* {fileUnitSelections[index]?.wilayah && ( */}
{!isContentFromSatker &&
fileUnitSelections[index]?.wilayah && (
<div className="border-t border-gray-200 pt-2">
<p className="text-sm font-medium text-gray-700 mb-2">
Detail Wilayah:
@ -1703,12 +1697,12 @@ export default function FormVideoDetail() {
handleFileUnitChange(
index,
item.key as keyof typeof unitSelection,
value as boolean
value as boolean,
);
setupPlacement(
index,
item.key,
Boolean(value)
Boolean(value),
);
}}
/>
@ -1761,13 +1755,13 @@ export default function FormVideoDetail() {
fileCheckedLevels[
index
]?.has(
Number(polda.id)
Number(polda.id),
) || false
}
onCheckedChange={() =>
handleFileCheckboxChangePlacement(
index,
Number(polda.id)
Number(polda.id),
)
}
/>
@ -1781,7 +1775,7 @@ export default function FormVideoDetail() {
e.preventDefault();
e.stopPropagation();
toggleExpand(
polda.id
polda.id,
);
}}
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
@ -1817,9 +1811,9 @@ export default function FormVideoDetail() {
index
]?.has(
Number(
sub.id
)
)
sub.id,
),
),
);
return (
<Button
@ -1829,7 +1823,7 @@ export default function FormVideoDetail() {
onClick={() =>
handleSelectAllSubItems(
index,
polda
polda,
)
}
>
@ -1881,16 +1875,16 @@ export default function FormVideoDetail() {
index
]?.has(
Number(
sub.id
)
sub.id,
),
) || false
}
onCheckedChange={() =>
handleFileCheckboxChangePlacement(
index,
Number(
sub.id
)
sub.id,
),
)
}
/>
@ -1898,7 +1892,7 @@ export default function FormVideoDetail() {
{sub.name}
</span>
</Label>
)
),
)}
</div>
</div>
@ -2007,7 +2001,7 @@ export default function FormVideoDetail() {
>
{template}
</Button>
)
),
)}
</div>
</div>

View File

@ -59,7 +59,7 @@ const CustomEditor = dynamic(
() => {
return import("@/components/editor/custom-editor");
},
{ ssr: false }
{ ssr: false },
);
interface FileWithPreview extends File {
@ -111,11 +111,11 @@ export default function FormVideo() {
const [isGeneratedArticle, setIsGeneratedArticle] = useState(false);
const [articleBody, setArticleBody] = useState<string>("");
const [selectedArticleId, setSelectedArticleId] = useState<string | null>(
null
null,
);
const [selectedMainKeyword, setSelectedMainKeyword] = useState("");
const [publishedForError, setPublishedForError] = useState<string | null>(
null
null,
);
const [selectedSize, setSelectedSize] = useState("");
const [detailData, setDetailData] = useState<any>(null);
@ -184,7 +184,7 @@ export default function FormVideo() {
}
const filesWithPreview = acceptedFiles.map((file) =>
Object.assign(file, { preview: URL.createObjectURL(file) })
Object.assign(file, { preview: URL.createObjectURL(file) }),
);
setFiles((prev) => {
@ -211,11 +211,11 @@ export default function FormVideo() {
.refine(
(files) =>
files.every((file: File) => ACCEPTED_FILE_TYPES.includes(file.type)),
{ message: "File harus berformat mp4 atau mov" }
{ message: "File harus berformat mp4 atau mov" },
)
.refine(
(files) => files.every((file: File) => file.size <= MAX_FILE_SIZE),
{ message: "Ukuran file maksimal 100 MB" }
{ message: "Ukuran file maksimal 100 MB" },
),
publishedFor: z
.array(z.string())
@ -423,7 +423,7 @@ export default function FormVideo() {
const articleData = await waitForStatusUpdate();
const cleanArticleBody = articleData?.articleBody?.replace(
/<img[^>]*>/g,
""
"",
);
const articleImagesData = articleData?.imagesUrl?.split(",");
setValue("description", cleanArticleBody || "");
@ -479,7 +479,7 @@ export default function FormVideo() {
if (scheduleId && scheduleType === "3") {
const findCategory = resCategory.find((o) =>
o.name.toLowerCase().includes("pers rilis")
o.name.toLowerCase().includes("pers rilis"),
);
if (findCategory) {
@ -502,7 +502,7 @@ export default function FormVideo() {
setPublishedFor(
options
.filter((opt: any) => opt.id !== "all")
.map((opt: any) => opt.id)
.map((opt: any) => opt.id),
);
}
} else {
@ -561,8 +561,8 @@ export default function FormVideo() {
translatedTitle && translatedTitle.trim() !== ""
? translatedTitle
: isSwitchOn
? title
: data.title;
? title
: data.title;
// Tentukan deskripsi final:
// Jika ada hasil translate, kirim itu ke backend
@ -570,10 +570,10 @@ export default function FormVideo() {
translatedContent && translatedContent.trim() !== ""
? translatedContent
: isSwitchOn
? data.description
: selectedFileType === "rewrite"
? data.rewriteDescription
: data.descriptionOri;
? data.description
: selectedFileType === "rewrite"
? data.rewriteDescription
: data.descriptionOri;
if (!finalDescription?.trim()) {
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
@ -695,7 +695,7 @@ export default function FormVideo() {
idx: number,
id: string,
file: any,
duration: string
duration: string,
) {
// console.log(idx, id, file, duration);
@ -731,7 +731,7 @@ export default function FormVideo() {
onChunkComplete: (
chunkSize: any,
bytesAccepted: any,
bytesTotal: any
bytesTotal: any,
) => {
const uploadPersen = Math.floor((bytesAccepted / bytesTotal) * 100);
progressInfo[idx].percentage = uploadPersen;
@ -791,8 +791,8 @@ export default function FormVideo() {
const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
setThumbnail(file);
setPreview(URL.createObjectURL(file));
setThumbnail(file);
setPreview(URL.createObjectURL(file));
console.log("Selected Thumbnail:", file);
}
};
@ -823,7 +823,7 @@ export default function FormVideo() {
<div
key={file.name}
className=" flex justify-between border px-3.5 py-3 my-6 rounded-md"
>
>
<div className="flex gap-3 items-center">
{/* <div className="file-preview">{renderFilePreview(file)}</div> */}
<svg
@ -1731,8 +1731,8 @@ export default function FormVideo() {
<button
type="button"
onClick={() => {
const updatedTags = (field.value ?? []).filter(
(_, i) => i !== index
const updatedTags = field.value.filter(
(_, i) => i !== index,
);
field.onChange(updatedTags);
}}
@ -1825,7 +1825,7 @@ export default function FormVideo() {
{/* <Button type="submit" color="primary">
{t("submit", { defaultValue: "Submit" })}
</Button> */}
{levelNumber !== "2" && levelNumber !== "3" && (
{levelNumber !== "2" && (
<Button type="submit" color="primary">
{t("submit", { defaultValue: "Submit" })}
</Button>

View File

@ -1 +1,2 @@
export const GoogleMapsAPI = "AIzaSyCuQHorDceMCzlSgrB9AEY5ns8KeriFsME";
// export const GoogleMapsAPI = "AIzaSyCuQHorDceMCzlSgrB9AEY5ns8KeriFsME";
export const GoogleMapsAPI = "AIzaSyA-Dci9RP4ZjyJCFfy74WvhtMZXSDLTPMQ";