fix: add checkbox wilayah in aprove content, add select date in dashboard generate report

This commit is contained in:
Sabda Yagra 2025-08-19 00:07:26 +07:00
parent 5ae245c123
commit 24a5e2a5c1
10 changed files with 1578 additions and 110 deletions

View File

@ -115,7 +115,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
const [selectedCategory, setSelectedCategory] = useState<string[]>([]);
const [selectedEventDate, setSelectedEventDate] = useState<Date | null>(null);
const roleId = Number(getCookiesDecrypt("urie")) || 0;
console.log("DUARR", roleId)
const levelNumber = Number(getCookiesDecrypt("ulne")) || 0;
const userLevelId = Number(getCookiesDecrypt("ulie")) || 0;
const [calendarEvents, setCalendarEvents] = useState<CalendarEvent[]>([]);
const [isLoading, setIsLoading] = useState<boolean>(false);
@ -415,7 +415,9 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
<div className="px-2">
{events.length === 0 ? (
<div className="mt-1 py-2 rounded-lg bg-white border border-black">
<p className="text-center">{t("no-data-yet", { defaultValue: "No Data Yet" })}</p>
<p className="text-center">
{t("no-data-yet", { defaultValue: "No Data Yet" })}
</p>
</div>
) : (
<>
@ -527,7 +529,16 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
<Card className="col-span-12 lg:col-span-4 2xl:col-span-3 pb-5">
<CardContent className="p-0">
<CardHeader className="border-none mb-2 pt-5">
{[3, 11, 2, 12].includes(roleId) && (
{/* {[3, 11, 2, 12].includes(roleId) && (
<Button
onClick={handleDateClick}
className="dark:bg-background dark:text-foreground w-full"
>
<Plus className="w-4 h-4 me-1" />
{t("addEvent", { defaultValue: "Add Event" })}
</Button>
)} */}
{[3, 11, 2, 12].includes(roleId) && levelNumber !== 3 && (
<Button
onClick={handleDateClick}
className="dark:bg-background dark:text-foreground w-full"
@ -542,12 +553,18 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
<DialogTrigger asChild>
<Button className="dark:bg-background dark:text-foreground w-full">
<Book size={15} className="w-4 h-4 mr-3" />
{t("bag-pa-monitoring-results", { defaultValue: "Bag Pa Monitoring Results" })}
{t("bag-pa-monitoring-results", {
defaultValue: "Bag Pa Monitoring Results",
})}
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px] overflow-y-auto max-h-[500px]">
<DialogHeader>
<DialogTitle>{t("monitoring-results", { defaultValue: "Monitoring Results" })}</DialogTitle>
<DialogTitle>
{t("monitoring-results", {
defaultValue: "Monitoring Results",
})}
</DialogTitle>
</DialogHeader>
{getModalContent()}
</DialogContent>

View File

@ -179,7 +179,6 @@ const TableSPIT = () => {
React.useEffect(() => {
fetchData();
getCategories();
console.log("TRIGGERRRR");
}, [statusFilter, page, showData, search, dateFilter]);
async function getCategories() {

View File

@ -67,7 +67,6 @@ import useTableColumns from "./columns";
const TableTeks = () => {
const router = useRouter();
const searchParams = useSearchParams();
const [dataTable, setDataTable] = React.useState<any[]>([]);
const [totalData, setTotalData] = React.useState<number>(1);
const [sorting, setSorting] = React.useState<SortingState>([]);
@ -77,7 +76,7 @@ const TableTeks = () => {
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [showData, setShowData] = React.useState("50");
const [showData, setShowData] = React.useState("10");
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: Number(showData),
@ -88,7 +87,6 @@ const TableTeks = () => {
const [search, setSearch] = React.useState<string>("");
const userId = getCookiesDecrypt("uie");
const userLevelId = getCookiesDecrypt("ulie");
const [categories, setCategories] = React.useState<any[]>([]);
const [selectedCategories, setSelectedCategories] = React.useState<number[]>(
[]

View File

@ -88,7 +88,7 @@ const ReportTable = () => {
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [showData, setShowData] = React.useState("50");
const [showData, setShowData] = React.useState("10");
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: Number(showData),
@ -106,6 +106,8 @@ const ReportTable = () => {
const [statusFilter, setStatusFilter] = React.useState<any[]>([]);
const [openPreview, setOpenPreview] = React.useState(false);
const [previewData, setPreviewData] = React.useState<any>(null);
const [openDateDialog, setOpenDateDialog] = React.useState(false);
const [reportDate, setReportDate] = React.useState("");
const handlePreview = (id: string) => {
const url = `https://new.netidhub.com/api/media/report/view?id=${id}`;
@ -184,8 +186,6 @@ const ReportTable = () => {
? prev.filter((id: any) => id !== categoryId)
: [...prev, categoryId]
);
// Perbarui filter kategori
setCategoryFilter((prev) => {
const updatedCategories = prev.split(",").filter(Boolean).map(Number);
@ -206,22 +206,65 @@ const ReportTable = () => {
}
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
setSearch(e.target.value); // Perbarui state search
table.getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel
setSearch(e.target.value);
table.getColumn("judul")?.setFilterValue(e.target.value);
};
const handleGenerateReport = async () => {
const today = new Date();
const formattedDate = format(today, "dd-MM-yyyy"); // Hasil: 22-04-2025
const title = `Report ${formattedDate}`;
// const handleGenerateReport = async () => {
// const today = new Date();
// const formattedDate = format(today, "dd-MM-yyyy");
// const title = `Report ${formattedDate}`;
// const requestData = {
// title,
// date: reportDate,
// };
// try {
// const response = await saveReport(requestData);
// if (response?.error) {
// MySwal.fire(
// "Error",
// response?.message || "Gagal menyimpan laporan",
// "error"
// );
// return;
// }
// MySwal.fire({
// title: "Sukses",
// text: "Laporan berhasil dibuat.",
// icon: "success",
// confirmButtonColor: "#3085d6",
// confirmButtonText: "OK",
// }).then(() => {
// fetchData();
// });
// } catch (error) {
// console.error("Generate report error:", error);
// MySwal.fire("Error", "Terjadi kesalahan saat membuat laporan", "error");
// }
// };
const handleGenerateReport = async () => {
if (!reportDate) {
MySwal.fire(
"Warning",
"Silakan pilih tanggal laporan terlebih dahulu",
"warning"
);
return;
}
const title = `Report ${format(new Date(reportDate), "dd-MM-yyyy")}`;
const requestData = {
title,
date: reportDate,
};
try {
const response = await saveReport(requestData);
if (response?.error) {
MySwal.fire(
"Error",
@ -238,7 +281,8 @@ const ReportTable = () => {
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then(() => {
fetchData(); // Refresh tabel setelah generate
fetchData();
setReportDate("");
});
} catch (error) {
console.error("Generate report error:", error);
@ -269,10 +313,19 @@ const ReportTable = () => {
<CardTitle>
<div className="flex items-center">
<div className="flex-1 text-xl font-medium text-default-900">
{t("table", { defaultValue: "Table" })} {t("report", { defaultValue: "Report" })}
{t("table", { defaultValue: "Table" })}{" "}
{t("report", { defaultValue: "Report" })}
</div>
<div className="flex-none">
<Button fullWidth color="primary" onClick={handleGenerateReport}>
{/* <Button fullWidth color="primary" onClick={handleGenerateReport}>
<Plus size={18} className=" me-1.5" />
{t("generate-report", { defaultValue: "Generate Report" })}
</Button> */}
<Button
fullWidth
color="primary"
onClick={() => setOpenDateDialog(true)}
>
<Plus size={18} className=" me-1.5" />
{t("generate-report", { defaultValue: "Generate Report" })}
</Button>
@ -472,6 +525,38 @@ const ReportTable = () => {
totalPage={totalPage}
/>
</div>
<Dialog open={openDateDialog} onOpenChange={setOpenDateDialog}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>Pilih Tanggal Laporan</DialogTitle>
</DialogHeader>
<div className="space-y-4">
<Label htmlFor="reportDate">Tanggal</Label>
<Input
id="reportDate"
type="date"
value={reportDate}
onChange={(e) => setReportDate(e.target.value)}
/>
<div className="flex justify-end gap-2">
<Button
variant="outline"
onClick={() => setOpenDateDialog(false)}
>
Batal
</Button>
<Button
onClick={() => {
setOpenDateDialog(false);
handleGenerateReport();
}}
>
Generate
</Button>
</div>
</div>
</DialogContent>
</Dialog>
</div>
);
};

View File

@ -19,7 +19,6 @@ import {
} from "@/components/ui/select";
import { Checkbox } from "@/components/ui/checkbox";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { register } from "module";
import { Switch } from "@/components/ui/switch";
import Cookies from "js-cookie";
@ -35,7 +34,7 @@ import {
getDataApprovalByMediaUpload,
} from "@/service/curated-content/curated-content";
import { Badge } from "@/components/ui/badge";
import { MailIcon, Music } from "lucide-react";
import { ChevronDown, ChevronUp, MailIcon, Music } from "lucide-react";
import { Swiper, SwiperSlide } from "swiper/react";
import "swiper/css";
import "swiper/css/free-mode";
@ -51,6 +50,7 @@ import {
Dialog,
DialogContent,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Textarea } from "@/components/ui/textarea";
import { loading } from "@/config/swal";
@ -66,6 +66,7 @@ import { formatDateToIndonesian } from "@/utils/globals";
import ApprovalHistoryModal from "@/components/modal/approval-history-modal";
import { useDropzone } from "react-dropzone";
import AudioPlayer from "@/components/audio-player";
import { getUserLevelForAssignments } from "@/service/task";
const imageSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }),
@ -115,6 +116,17 @@ const ViewEditor = dynamic(
{ ssr: false }
);
interface Destination {
id: string;
name: string;
subDestination?: SubDestination[];
}
interface SubDestination {
id: string;
name: string;
}
export default function FormAudioDetail() {
const MySwal = withReactContent(Swal);
const router = useRouter();
@ -155,6 +167,135 @@ export default function FormAudioDetail() {
const [wavesurfer, setWavesurfer] = useState<WaveSurfer>();
const [isPlaying, setIsPlaying] = useState(false);
const [approval, setApproval] = useState<any>();
const [expandedPolda, setExpandedPolda] = useState([{}]);
const [unitSelection, setUnitSelection] = useState({
semua: false,
nasional: false,
wilayah: false,
international: false,
polda: false,
polres: false,
satker: false,
});
const [isLoading, setIsLoading] = useState(false);
const [listDest, setListDest] = useState<Destination[]>([]);
const [checkedLevels, setCheckedLevels] = useState<any>(new Set());
const toggleExpand = (id: number) => {
setExpandedPolda((prev) => ({
...prev,
[id]: !prev[id],
}));
};
useEffect(() => {
if (detail?.assignedToTopLevel) {
const outputSet = new Set(
detail.assignedToTopLevel.split(",").map(Number)
);
setUnitSelection({
semua: outputSet.has(0),
nasional: outputSet.has(1),
wilayah: outputSet.has(2),
international: outputSet.has(3),
polda: outputSet.has(4),
polres: outputSet.has(5),
satker: outputSet.has(6),
});
}
}, [detail?.fileTypeOutput]);
useEffect(() => {
async function fetchPoldaPolres() {
setIsLoading(true);
try {
const response = await getUserLevelForAssignments();
setListDest(response?.data?.data.list);
const initialExpandedState = response?.data?.data.list.reduce(
(acc: any, polda: any) => {
acc[polda.id] = false;
return acc;
},
{}
);
setExpandedPolda(initialExpandedState);
console.log("polres", initialExpandedState);
} catch (error) {
console.error("Error fetching Polda/Polres data:", error);
} finally {
setIsLoading(false);
}
}
fetchPoldaPolres();
}, []);
useEffect(() => {
const updated = new Set(checkedLevels);
if (unitSelection.polda) {
listDest.forEach((polda) => {
updated.add(polda.id);
});
}
if (unitSelection.polres) {
listDest.forEach((polda) => {
polda?.subDestination?.forEach((polres: any) => {
updated.add(polres.id);
});
});
}
setCheckedLevels(updated);
}, [unitSelection.polda, unitSelection.polres, listDest]);
const handleUnitChange = (
key: keyof typeof unitSelection,
value: boolean
) => {
if (key === "semua") {
const newState = {
semua: value,
nasional: value,
wilayah: value,
international: value,
polda: value,
polres: value,
satker: value,
};
setUnitSelection(newState);
} else {
const updatedSelection = {
...unitSelection,
[key]: value,
};
const allChecked = [
"nasional",
"wilayah",
"international",
"polda",
"polres",
"satker",
].every((k) => updatedSelection[k as keyof typeof unitSelection]);
updatedSelection.semua = allChecked;
setUnitSelection(updatedSelection);
}
};
const handleCheckboxChangePlacement = (levelId: number) => {
setCheckedLevels((prev: any) => {
const updatedLevels = new Set(prev);
if (updatedLevels.has(levelId)) {
updatedLevels.delete(levelId);
} else {
updatedLevels.add(levelId);
}
return updatedLevels;
});
};
const onDrop = (acceptedFiles: File[]) => {
setUploadedFiles(acceptedFiles);
@ -247,7 +388,7 @@ export default function FormAudioDetail() {
if (findCategory) {
// setValue("categoryId", findCategory.id);
setSelectedCategory(findCategory.id);
setSelectedCategory(findCategory.id);
const response = await getTagsBySubCategoryId(findCategory.id);
setTags(response?.data?.data);
}
@ -270,10 +411,7 @@ export default function FormAudioDetail() {
if (id) {
const response = await detailMedia(id);
const details = response?.data?.data;
console.log("detail", details);
setFiles(details?.files);
console.log("ISI FILES:", details?.files);
setDetail(details);
setMain({
type: details?.fileType.name,
@ -283,6 +421,13 @@ export default function FormAudioDetail() {
});
setupPlacementCheck(details?.files?.length);
if (details?.assignedToLevel) {
const levels = new Set(
details.assignedToLevel.split(",").map(Number)
);
setCheckedLevels(levels);
}
if (details?.publishedForObject) {
const publisherIds = details?.publishedForObject.map(
(obj: any) => obj.id
@ -358,9 +503,9 @@ export default function FormAudioDetail() {
console.log("getPlaa", filePlacements);
const temp = [];
for (let i = 0; i < filePlacements?.length; i++) {
if (filePlacements[i].length !== 0) {
const now = filePlacements[i].filter((a) => a !== "all");
const data = { mediaFileId: files[i].id, placements: now.join(",") };
if (filePlacements[i]?.length !== 0) {
const now = filePlacements[i]?.filter((a) => a !== "all");
const data = { mediaFileId: files[i]?.id, placements: now.join(",") };
temp.push(data);
}
}
@ -374,6 +519,7 @@ export default function FormAudioDetail() {
message: description,
// files: [],
files: isUserMabesApprover ? getPlacement() : [],
customLocationPlacement: Array.from(checkedLevels).join(","),
};
setModalOpen(false);
loading();
@ -420,9 +566,9 @@ export default function FormAudioDetail() {
temp[index] = ["all", "mabes", "polda", "international"];
} else {
const now = temp[index];
now.push(placement);
if (now.length === 3 && !now.includes("all")) {
now.push("all");
now?.push(placement);
if (now?.length === 3 && !now?.includes("all")) {
now?.push("all");
}
temp[index] = now;
}
@ -431,9 +577,8 @@ export default function FormAudioDetail() {
temp[index] = [];
} else {
const now = temp[index].filter((a) => a !== placement);
console.log("now", now);
temp[index] = now;
if (now.length === 3 && now.includes("all")) {
if (now?.length === 3 && now?.includes("all")) {
const newData = now.filter((b) => b !== "all");
temp[index] = newData;
}
@ -734,7 +879,195 @@ export default function FormAudioDetail() {
<Icon icon="humbleicons:times" color="red" />
</a>
</div>
{isUserMabesApprover && (
<div>
{/* --- Checkbox utama --- */}
<div className="flex flex-wrap gap-3 mt-6 lg:pt-7 lg:ml-3">
{[
"semua",
"nasional",
"wilayah",
"international",
].map((key, index) => (
<div
className="flex items-center gap-2"
key={key}
>
<Checkbox
id={key}
checked={
unitSelection[
key as keyof typeof unitSelection
]
}
onCheckedChange={(value) => {
handleUnitChange(
key as keyof typeof unitSelection,
value as boolean
);
setupPlacement(
index,
key,
Boolean(value)
);
}}
/>
<Label htmlFor={key}>
{key.charAt(0).toUpperCase() +
key.slice(1)}
</Label>
</div>
))}
</div>
{/* --- Kalau wilayah dicentang baru tampilkan detail --- */}
{unitSelection.wilayah && (
<div className="mt-6 lg:pt-6 lg:pl-3 space-y-3">
<div className="flex flex-wrap gap-3">
{["polda", "polres", "satker"].map(
(key, index) => (
<div
className="flex items-center gap-2"
key={key}
>
<Checkbox
id={key}
checked={
unitSelection[
key as keyof typeof unitSelection
]
}
onCheckedChange={(value) => {
handleUnitChange(
key as keyof typeof unitSelection,
value as boolean
);
setupPlacement(
index,
key,
Boolean(value)
);
}}
/>
<Label htmlFor={key}>
{key.charAt(0).toUpperCase() +
key.slice(1)}
</Label>
</div>
)
)}
</div>
{/* --- Custom Button + Dialog --- */}
<Dialog>
<DialogTrigger asChild>
<Button
variant="soft"
size="sm"
color="primary"
>
[
{t("custom", {
defaultValue: "Custom",
})}
]
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
<DialogHeader>
<DialogTitle>
Daftar Wilayah Polda dan Polres
</DialogTitle>
</DialogHeader>
<div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
{listDest.map((polda: any) => {
const poldaChecked =
unitSelection.polda;
const polresChecked =
unitSelection.polres;
const isPoldaDisabled = poldaChecked;
const isPolresDisabled =
polresChecked;
return (
<div
key={polda.id}
className="border p-2"
>
<Label className="flex items-center">
<Checkbox
checked={
poldaChecked ||
checkedLevels.has(polda.id)
}
disabled={isPoldaDisabled}
onCheckedChange={() => {
if (isPoldaDisabled) return;
handleCheckboxChangePlacement(
polda.id
);
}}
className="mr-3"
/>
{polda.name}
<button
onClick={() =>
toggleExpand(polda.id)
}
className="ml-2 focus:outline-none"
>
{expandedPolda[polda.id] ? (
<ChevronUp size={16} />
) : (
<ChevronDown size={16} />
)}
</button>
</Label>
{expandedPolda[polda.id] && (
<div className="ml-6 mt-2">
{polda?.subDestination?.map(
(polres: any) => (
<Label
key={polres.id}
className="block mt-1"
>
<Checkbox
checked={
polresChecked ||
checkedLevels.has(
polres.id
)
}
disabled={
isPolresDisabled
}
onCheckedChange={() => {
if (
isPolresDisabled
)
return;
handleCheckboxChangePlacement(
polres.id
);
}}
className="mr-2"
/>
{polres.name}
</Label>
)
)}
</div>
)}
</div>
);
})}
</div>
</DialogContent>
</Dialog>
</div>
)}
</div>
{/* {isUserMabesApprover && (
<div className="flex flex-row gap-2">
<div className="flex items-center space-x-2">
<Checkbox
@ -811,7 +1144,7 @@ export default function FormAudioDetail() {
</label>
</div>
</div>
)}
)} */}
</div>
</div>
))

View File

@ -36,7 +36,7 @@ import {
getDataApprovalByMediaUpload,
} from "@/service/curated-content/curated-content";
import { Badge } from "@/components/ui/badge";
import { MailIcon } from "lucide-react";
import { ChevronDown, ChevronUp, MailIcon } from "lucide-react";
import { Swiper, SwiperSlide } from "swiper/react";
import "swiper/css";
import "swiper/css/free-mode";
@ -67,6 +67,7 @@ import SuggestionModal from "@/components/modal/suggestions-modal";
import { formatDateToIndonesian } from "@/utils/globals";
import ApprovalHistoryModal from "@/components/modal/approval-history-modal";
import Image from "next/image";
import { getUserLevelForAssignments } from "@/service/task";
const imageSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }),
@ -116,6 +117,17 @@ const ViewEditor = dynamic(
{ ssr: false }
);
interface Destination {
id: string;
name: string;
subDestination?: SubDestination[];
}
interface SubDestination {
id: string;
name: string;
}
export default function FormImageDetail() {
const MySwal = withReactContent(Swal);
const router = useRouter();
@ -123,13 +135,13 @@ export default function FormImageDetail() {
const userLevelId = getCookiesDecrypt("ulie");
const userLevelName = Cookies.get("state");
const roleId = getCookiesDecrypt("urie");
const [listDest, setListDest] = useState<Destination[]>([]);
console.log("LALALALA", userLevelName);
const [modalOpen, setModalOpen] = useState(false);
const { id } = useParams() as { id: string };
console.log(id);
const editor = useRef(null);
type ImageSchema = z.infer<typeof imageSchema>;
const t = useTranslations("Form");
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const taskId = Cookies.get("taskId");
@ -149,10 +161,21 @@ export default function FormImageDetail() {
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
const [isUserMabesApprover, setIsUserMabesApprover] = useState(false);
const [approval, setApproval] = useState<any>();
const [unitSelection, setUnitSelection] = useState({
semua: false,
nasional: false,
wilayah: false,
international: false,
polda: false,
polres: false,
satker: false,
});
const [isLoading, setIsLoading] = useState(false);
const [checkedLevels, setCheckedLevels] = useState<any>(new Set());
const [selectedTarget, setSelectedTarget] = useState("");
const [files, setFiles] = useState<FileType[]>([]);
const [rejectedFiles, setRejectedFiles] = useState<number[]>([]);
const [expandedPolda, setExpandedPolda] = useState([{}]);
const [wilayahPublish, setWilayahPublish] = React.useState({
semua: false,
nasional: false,
@ -165,6 +188,117 @@ export default function FormImageDetail() {
let fileTypeId = "1";
const toggleExpand = (id: number) => {
setExpandedPolda((prev) => ({
...prev,
[id]: !prev[id],
}));
};
useEffect(() => {
if (detail?.assignedToTopLevel) {
const outputSet = new Set(
detail.assignedToTopLevel.split(",").map(Number)
);
setUnitSelection({
semua: outputSet.has(0),
nasional: outputSet.has(1),
wilayah: outputSet.has(2),
international: outputSet.has(3),
polda: outputSet.has(4),
polres: outputSet.has(5),
satker: outputSet.has(6),
});
}
}, [detail?.fileTypeOutput]);
// const handlePoldaPolresChange = () => {
// return Array.from(checkedLevels).join(","); // Mengonversi Set ke string
// };
useEffect(() => {
async function fetchPoldaPolres() {
setIsLoading(true);
try {
const response = await getUserLevelForAssignments();
setListDest(response?.data?.data.list);
const initialExpandedState = response?.data?.data.list.reduce(
(acc: any, polda: any) => {
acc[polda.id] = false;
return acc;
},
{}
);
setExpandedPolda(initialExpandedState);
console.log("polres", initialExpandedState);
} catch (error) {
console.error("Error fetching Polda/Polres data:", error);
} finally {
setIsLoading(false);
}
}
fetchPoldaPolres();
}, []);
useEffect(() => {
const updated = new Set(checkedLevels);
if (unitSelection.polda) {
listDest.forEach((polda) => {
updated.add(polda.id); // hanya id polda
});
}
if (unitSelection.polres) {
listDest.forEach((polda) => {
polda?.subDestination?.forEach((polres: any) => {
updated.add(polres.id); // hanya id polres
});
});
}
setCheckedLevels(updated);
}, [unitSelection.polda, unitSelection.polres, listDest]);
const handleUnitChange = (
key: keyof typeof unitSelection,
value: boolean
) => {
if (key === "semua") {
// Jika klik Semua, set semua value ke true/false
const newState = {
semua: value,
nasional: value,
wilayah: value,
international: value,
polda: value,
polres: value,
satker: value,
};
setUnitSelection(newState);
} else {
// Update salah satu saja
const updatedSelection = {
...unitSelection,
[key]: value,
};
// Cek apakah semua selain "semua" sudah dicentang
const allChecked = [
"nasional",
"wilayah",
"international",
"polda",
"polres",
"satker",
].every((k) => updatedSelection[k as keyof typeof unitSelection]);
updatedSelection.semua = allChecked;
setUnitSelection(updatedSelection);
}
};
const {
control,
handleSubmit,
@ -191,6 +325,18 @@ export default function FormImageDetail() {
);
};
const handleCheckboxChangePlacement = (levelId: number) => {
setCheckedLevels((prev: any) => {
const updatedLevels = new Set(prev);
if (updatedLevels.has(levelId)) {
updatedLevels.delete(levelId);
} else {
updatedLevels.add(levelId);
}
return updatedLevels;
});
};
useEffect(() => {
async function initState() {
getCategories();
@ -214,7 +360,7 @@ export default function FormImageDetail() {
if (findCategory) {
// setValue("categoryId", findCategory.id);
setSelectedCategory(findCategory.id); // Set the selected category
setSelectedCategory(findCategory.id);
const response = await getTagsBySubCategoryId(findCategory.id);
setTags(response?.data?.data);
}
@ -248,6 +394,13 @@ export default function FormImageDetail() {
});
setupPlacementCheck(details?.files?.length);
if (details?.assignedToLevel) {
const levels = new Set(
details.assignedToLevel.split(",").map(Number)
);
setCheckedLevels(levels);
}
if (details.publishedForObject) {
const publisherIds = details.publishedForObject.map(
(obj: any) => obj.id
@ -327,8 +480,8 @@ export default function FormImageDetail() {
statusId: status,
message: description,
files: isUserMabesApprover ? getPlacement() : [],
customLocationPlacement: Array.from(checkedLevels).join(","),
};
setModalOpen(false);
loading();
const response = await submitApproval(data);
@ -368,7 +521,7 @@ export default function FormImageDetail() {
temp[index] = ["all", "mabes", "polda", "international"];
} else {
const now = temp[index];
now.push(placement);
now?.push(placement);
if (now.length === 3 && !now.includes("all")) {
now.push("all");
}
@ -397,6 +550,7 @@ export default function FormImageDetail() {
rejects.push(id);
setRejectedFiles(rejects);
}
const handleMain = (
type: string,
url: string,
@ -454,7 +608,9 @@ export default function FormImageDetail() {
<div className="flex flex-col lg:flex-row gap-10">
<Card className="w-full lg:w-8/12">
<div className="px-6 py-6">
<p className="text-lg font-semibold mb-3">{t("form-image", { defaultValue: "Form Image" })}</p>
<p className="text-lg font-semibold mb-3">
{t("form-image", { defaultValue: "Form Image" })}
</p>
<div className="gap-5 mb-5">
{/* Input Title */}
<div className="space-y-2 py-3">
@ -495,16 +651,23 @@ export default function FormImageDetail() {
</SelectTrigger>
<SelectContent>
{/* Show the category from details if it doesn't exist in categories list */}
{detail && !categories.find(cat => String(cat.id) === String(detail.category.id)) && (
<SelectItem
key={String(detail.category.id)}
value={String(detail.category.id)}
>
{detail.category.name}
</SelectItem>
)}
{detail &&
!categories.find(
(cat) =>
String(cat.id) === String(detail.category.id)
) && (
<SelectItem
key={String(detail.category.id)}
value={String(detail.category.id)}
>
{detail.category.name}
</SelectItem>
)}
{categories.map((category) => (
<SelectItem key={String(category.id)} value={String(category.id)}>
<SelectItem
key={String(category.id)}
value={String(category.id)}
>
{category.name}
</SelectItem>
))}
@ -514,7 +677,9 @@ export default function FormImageDetail() {
</div>
<div className="py-3 space-y-2">
<Label>{t("description", { defaultValue: "Description" })}</Label>
<Label>
{t("description", { defaultValue: "Description" })}
</Label>
<Controller
control={control}
name="description"
@ -529,7 +694,9 @@ export default function FormImageDetail() {
)}
</div>
<div className="space-y-2">
<Label className="text-xl ">{t("file-media", { defaultValue: "File Media" })}</Label>
<Label className="text-xl ">
{t("file-media", { defaultValue: "File Media" })}
</Label>
<div className="w-full ">
<Swiper
thumbs={{ swiper: thumbsSwiper }}
@ -628,7 +795,9 @@ export default function FormImageDetail() {
</div>
<div className="px-3 py-3">
<div className="flex flex-col gap-6 space-y-2">
<Label>{t("publish-target", { defaultValue: "Publish Target" })}</Label>
<Label>
{t("publish-target", { defaultValue: "Publish Target" })}
</Label>
<div className="flex gap-2 items-center">
<Checkbox
id="5"
@ -729,7 +898,9 @@ export default function FormImageDetail() {
<Dialog open={modalOpen} onOpenChange={setModalOpen}>
<DialogContent size="md" className="max-h-[600px]">
<DialogHeader>
<DialogTitle>{t("leave-comment", { defaultValue: "Leave Comment" })}</DialogTitle>
<DialogTitle>
{t("leave-comment", { defaultValue: "Leave Comment" })}
</DialogTitle>
</DialogHeader>
<div className="flex flex-col gap-2 max-h-[208px] md:max-h-[312px] overflow-y-auto">
{status == "2"
@ -760,7 +931,199 @@ export default function FormImageDetail() {
<Icon icon="humbleicons:times" color="red" />
</a>
</div>
{isUserMabesApprover && (
<div>
{/* --- Checkbox utama --- */}
<div className="flex flex-wrap gap-3 mt-6 lg:pt-7 lg:ml-3">
{[
"semua",
"nasional",
"wilayah",
"international",
].map((key, index) => (
<div
className="flex items-center gap-2"
key={key}
>
<Checkbox
id={key}
checked={
unitSelection[
key as keyof typeof unitSelection
]
}
onCheckedChange={(value) => {
handleUnitChange(
key as keyof typeof unitSelection,
value as boolean
);
setupPlacement(
index,
key,
Boolean(value)
);
}}
/>
<Label htmlFor={key}>
{key.charAt(0).toUpperCase() +
key.slice(1)}
</Label>
</div>
))}
</div>
{/* --- Kalau wilayah dicentang baru tampilkan detail --- */}
{unitSelection.wilayah && (
<div className="mt-6 lg:pt-6 lg:pl-3 space-y-3">
<div className="flex flex-wrap gap-3">
{["polda", "polres", "satker"].map(
(key, index) => (
<div
className="flex items-center gap-2"
key={key}
>
<Checkbox
id={key}
checked={
unitSelection[
key as keyof typeof unitSelection
]
}
onCheckedChange={(value) => {
handleUnitChange(
key as keyof typeof unitSelection,
value as boolean
);
setupPlacement(
index,
key,
Boolean(value)
);
}}
/>
<Label htmlFor={key}>
{key.charAt(0).toUpperCase() +
key.slice(1)}
</Label>
</div>
)
)}
</div>
{/* --- Custom Button + Dialog --- */}
<Dialog>
<DialogTrigger asChild>
<Button
variant="soft"
size="sm"
color="primary"
>
[
{t("custom", {
defaultValue: "Custom",
})}
]
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
<DialogHeader>
<DialogTitle>
Daftar Wilayah Polda dan Polres
</DialogTitle>
</DialogHeader>
<div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
{listDest.map((polda: any) => {
const poldaChecked =
unitSelection.polda;
const polresChecked =
unitSelection.polres;
const isPoldaDisabled =
poldaChecked;
const isPolresDisabled =
polresChecked;
return (
<div
key={polda.id}
className="border p-2"
>
<Label className="flex items-center">
<Checkbox
checked={
poldaChecked ||
checkedLevels.has(
polda.id
)
}
disabled={isPoldaDisabled}
onCheckedChange={() => {
if (isPoldaDisabled)
return;
handleCheckboxChangePlacement(
polda.id
);
}}
className="mr-3"
/>
{polda.name}
<button
onClick={() =>
toggleExpand(polda.id)
}
className="ml-2 focus:outline-none"
>
{expandedPolda[polda.id] ? (
<ChevronUp size={16} />
) : (
<ChevronDown size={16} />
)}
</button>
</Label>
{expandedPolda[polda.id] && (
<div className="ml-6 mt-2">
{polda?.subDestination?.map(
(polres: any) => (
<Label
key={polres.id}
className="block mt-1"
>
<Checkbox
checked={
polresChecked ||
checkedLevels.has(
polres.id
)
}
disabled={
isPolresDisabled
}
onCheckedChange={() => {
if (
isPolresDisabled
)
return;
handleCheckboxChangePlacement(
polres.id
);
}}
className="mr-2"
/>
{polres.name}
</Label>
)
)}
</div>
)}
</div>
);
})}
</div>
</DialogContent>
</Dialog>
</div>
)}
</div>
{/* {isUserMabesApprover && (
<div className="flex flex-row gap-2">
<div className="flex items-center space-x-2">
<Checkbox
@ -854,7 +1217,7 @@ export default function FormImageDetail() {
</label>
</div>
</div>
)}
)} */}
</div>
</div>
))

View File

@ -35,7 +35,7 @@ import {
getDataApprovalByMediaUpload,
} from "@/service/curated-content/curated-content";
import { Badge } from "@/components/ui/badge";
import { MailIcon } from "lucide-react";
import { ChevronDown, ChevronUp, MailIcon } from "lucide-react";
import { Swiper, SwiperSlide } from "swiper/react";
import "swiper/css";
import "swiper/css/free-mode";
@ -51,6 +51,7 @@ import {
Dialog,
DialogContent,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Textarea } from "@/components/ui/textarea";
import { loading } from "@/config/swal";
@ -64,6 +65,7 @@ import { formatDateToIndonesian } from "@/utils/globals";
import ApprovalHistoryModal from "@/components/modal/approval-history-modal";
import FileTextPreview from "./file-preview-text";
import FileTextThumbnail from "./file-text-thumbnail";
import { getUserLevelForAssignments } from "@/service/task";
const imageSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }),
@ -113,19 +115,28 @@ const ViewEditor = dynamic(
{ ssr: false }
);
interface Destination {
id: string;
name: string;
subDestination?: SubDestination[];
}
interface SubDestination {
id: string;
name: string;
}
export default function FormTeksDetail() {
const MySwal = withReactContent(Swal);
const router = useRouter();
const userId = getCookiesDecrypt("uie");
const userLevelId = getCookiesDecrypt("ulie");
const roleId = getCookiesDecrypt("urie");
const [modalOpen, setModalOpen] = useState(false);
const { id } = useParams() as { id: string };
console.log(id);
const editor = useRef(null);
type ImageSchema = z.infer<typeof imageSchema>;
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const taskId = Cookies.get("taskId");
const scheduleId = Cookies.get("scheduleId");
@ -141,20 +152,146 @@ export default function FormTeksDetail() {
const [main, setMain] = useState<any>([]);
const [detailThumb, setDetailThumb] = useState<any>([]);
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
const t = useTranslations("Form");
const [selectedTarget, setSelectedTarget] = useState("");
const [files, setFiles] = useState<FileType[]>([]);
const [rejectedFiles, setRejectedFiles] = useState<number[]>([]);
const [isMabesApprover, setIsMabesApprover] = useState(false);
const [filePlacements, setFilePlacements] = useState<string[][]>([]);
const [isUserMabesApprover, setIsUserMabesApprover] = useState(false);
const [approval, setApproval] = useState<any>();
const [expandedPolda, setExpandedPolda] = useState([{}]);
const [unitSelection, setUnitSelection] = useState({
semua: false,
nasional: false,
wilayah: false,
international: false,
polda: false,
polres: false,
satker: false,
});
const [isLoading, setIsLoading] = useState(false);
const [listDest, setListDest] = useState<Destination[]>([]);
const [checkedLevels, setCheckedLevels] = useState<any>(new Set());
let fileTypeId = "3";
const toggleExpand = (id: number) => {
setExpandedPolda((prev) => ({
...prev,
[id]: !prev[id],
}));
};
useEffect(() => {
if (detail?.assignedToTopLevel) {
const outputSet = new Set(
detail.assignedToTopLevel.split(",").map(Number)
);
setUnitSelection({
semua: outputSet.has(0),
nasional: outputSet.has(1),
wilayah: outputSet.has(2),
international: outputSet.has(3),
polda: outputSet.has(4),
polres: outputSet.has(5),
satker: outputSet.has(6),
});
}
}, [detail?.fileTypeOutput]);
useEffect(() => {
async function fetchPoldaPolres() {
setIsLoading(true);
try {
const response = await getUserLevelForAssignments();
setListDest(response?.data?.data.list);
const initialExpandedState = response?.data?.data.list.reduce(
(acc: any, polda: any) => {
acc[polda.id] = false;
return acc;
},
{}
);
setExpandedPolda(initialExpandedState);
console.log("polres", initialExpandedState);
} catch (error) {
console.error("Error fetching Polda/Polres data:", error);
} finally {
setIsLoading(false);
}
}
fetchPoldaPolres();
}, []);
useEffect(() => {
const updated = new Set(checkedLevels);
if (unitSelection.polda) {
listDest.forEach((polda) => {
updated.add(polda.id);
});
}
if (unitSelection.polres) {
listDest.forEach((polda) => {
polda?.subDestination?.forEach((polres: any) => {
updated.add(polres.id);
});
});
}
setCheckedLevels(updated);
}, [unitSelection.polda, unitSelection.polres, listDest]);
const handleUnitChange = (
key: keyof typeof unitSelection,
value: boolean
) => {
if (key === "semua") {
const newState = {
semua: value,
nasional: value,
wilayah: value,
international: value,
polda: value,
polres: value,
satker: value,
};
setUnitSelection(newState);
} else {
const updatedSelection = {
...unitSelection,
[key]: value,
};
const allChecked = [
"nasional",
"wilayah",
"international",
"polda",
"polres",
"satker",
].every((k) => updatedSelection[k as keyof typeof unitSelection]);
updatedSelection.semua = allChecked;
setUnitSelection(updatedSelection);
}
};
const handleCheckboxChangePlacement = (levelId: number) => {
setCheckedLevels((prev: any) => {
const updatedLevels = new Set(prev);
if (updatedLevels.has(levelId)) {
updatedLevels.delete(levelId);
} else {
updatedLevels.add(levelId);
}
return updatedLevels;
});
};
const {
control,
handleSubmit,
@ -248,6 +385,13 @@ export default function FormTeksDetail() {
format: details?.files[0]?.format,
});
if (details?.assignedToLevel) {
const levels = new Set(
details.assignedToLevel.split(",").map(Number)
);
setCheckedLevels(levels);
}
if (details.publishedForObject) {
const publisherIds = details.publishedForObject.map(
(obj: any) => obj.id
@ -313,9 +457,9 @@ export default function FormTeksDetail() {
console.log("getPlaa", filePlacements);
const temp = [];
for (let i = 0; i < filePlacements?.length; i++) {
if (filePlacements[i].length !== 0) {
const now = filePlacements[i].filter((a) => a !== "all");
const data = { mediaFileId: files[i].id, placements: now.join(",") };
if (filePlacements[i]?.length !== 0) {
const now = filePlacements[i]?.filter((a) => a !== "all");
const data = { mediaFileId: files[i]?.id, placements: now?.join(",") };
temp.push(data);
}
}
@ -328,6 +472,7 @@ export default function FormTeksDetail() {
statusId: status,
message: description,
files: isUserMabesApprover ? getPlacement() : [],
customLocationPlacement: Array.from(checkedLevels).join(","),
};
setModalOpen(false);
@ -367,9 +512,9 @@ export default function FormTeksDetail() {
temp[index] = ["all", "mabes", "polda", "international"];
} else {
const now = temp[index];
now.push(placement);
if (now.length === 3 && !now.includes("all")) {
now.push("all");
now?.push(placement);
if (now?.length === 3 && !now?.includes("all")) {
now?.push("all");
}
temp[index] = now;
}
@ -377,11 +522,10 @@ export default function FormTeksDetail() {
if (placement === "all") {
temp[index] = [];
} else {
const now = temp[index].filter((a) => a !== placement);
console.log("now", now);
const now = temp[index]?.filter((a) => a !== placement);
temp[index] = now;
if (now.length === 3 && now.includes("all")) {
const newData = now.filter((b) => b !== "all");
if (now?.length === 3 && now?.includes("all")) {
const newData = now?.filter((b) => b !== "all");
temp[index] = newData;
}
}
@ -472,16 +616,23 @@ export default function FormTeksDetail() {
</SelectTrigger>
<SelectContent>
{/* Show the category from details if it doesn't exist in categories list */}
{detail && !categories.find(cat => String(cat.id) === String(detail.category.id)) && (
<SelectItem
key={String(detail.category.id)}
value={String(detail.category.id)}
>
{detail.category.name}
</SelectItem>
)}
{detail &&
!categories.find(
(cat) =>
String(cat.id) === String(detail.category.id)
) && (
<SelectItem
key={String(detail.category.id)}
value={String(detail.category.id)}
>
{detail.category.name}
</SelectItem>
)}
{categories.map((category) => (
<SelectItem key={String(category.id)} value={String(category.id)}>
<SelectItem
key={String(category.id)}
value={String(category.id)}
>
{category.name}
</SelectItem>
))}
@ -696,7 +847,195 @@ export default function FormTeksDetail() {
<Icon icon="humbleicons:times" color="red" />
</a>
</div>
{isUserMabesApprover && (
<div>
{/* --- Checkbox utama --- */}
<div className="flex flex-wrap gap-3 mt-6 lg:pt-7 lg:ml-3">
{[
"semua",
"nasional",
"wilayah",
"international",
].map((key, index) => (
<div
className="flex items-center gap-2"
key={key}
>
<Checkbox
id={key}
checked={
unitSelection[
key as keyof typeof unitSelection
]
}
onCheckedChange={(value) => {
handleUnitChange(
key as keyof typeof unitSelection,
value as boolean
);
setupPlacement(
index,
key,
Boolean(value)
);
}}
/>
<Label htmlFor={key}>
{key.charAt(0).toUpperCase() +
key.slice(1)}
</Label>
</div>
))}
</div>
{/* --- Kalau wilayah dicentang baru tampilkan detail --- */}
{unitSelection.wilayah && (
<div className="mt-6 lg:pt-6 lg:pl-3 space-y-3">
<div className="flex flex-wrap gap-3">
{["polda", "polres", "satker"].map(
(key, index) => (
<div
className="flex items-center gap-2"
key={key}
>
<Checkbox
id={key}
checked={
unitSelection[
key as keyof typeof unitSelection
]
}
onCheckedChange={(value) => {
handleUnitChange(
key as keyof typeof unitSelection,
value as boolean
);
setupPlacement(
index,
key,
Boolean(value)
);
}}
/>
<Label htmlFor={key}>
{key.charAt(0).toUpperCase() +
key.slice(1)}
</Label>
</div>
)
)}
</div>
{/* --- Custom Button + Dialog --- */}
<Dialog>
<DialogTrigger asChild>
<Button
variant="soft"
size="sm"
color="primary"
>
[
{t("custom", {
defaultValue: "Custom",
})}
]
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
<DialogHeader>
<DialogTitle>
Daftar Wilayah Polda dan Polres
</DialogTitle>
</DialogHeader>
<div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
{listDest.map((polda: any) => {
const poldaChecked =
unitSelection.polda;
const polresChecked =
unitSelection.polres;
const isPoldaDisabled = poldaChecked;
const isPolresDisabled =
polresChecked;
return (
<div
key={polda.id}
className="border p-2"
>
<Label className="flex items-center">
<Checkbox
checked={
poldaChecked ||
checkedLevels.has(polda.id)
}
disabled={isPoldaDisabled}
onCheckedChange={() => {
if (isPoldaDisabled) return;
handleCheckboxChangePlacement(
polda.id
);
}}
className="mr-3"
/>
{polda.name}
<button
onClick={() =>
toggleExpand(polda.id)
}
className="ml-2 focus:outline-none"
>
{expandedPolda[polda.id] ? (
<ChevronUp size={16} />
) : (
<ChevronDown size={16} />
)}
</button>
</Label>
{expandedPolda[polda.id] && (
<div className="ml-6 mt-2">
{polda?.subDestination?.map(
(polres: any) => (
<Label
key={polres.id}
className="block mt-1"
>
<Checkbox
checked={
polresChecked ||
checkedLevels.has(
polres.id
)
}
disabled={
isPolresDisabled
}
onCheckedChange={() => {
if (
isPolresDisabled
)
return;
handleCheckboxChangePlacement(
polres.id
);
}}
className="mr-2"
/>
{polres.name}
</Label>
)
)}
</div>
)}
</div>
);
})}
</div>
</DialogContent>
</Dialog>
</div>
)}
</div>
{/* {isUserMabesApprover && (
<div className="flex flex-row gap-2">
<div className="flex items-center space-x-2">
<Checkbox
@ -773,7 +1112,7 @@ export default function FormTeksDetail() {
</label>
</div>
</div>
)}
)} */}
</div>
</div>
))

View File

@ -35,7 +35,7 @@ import {
getDataApprovalByMediaUpload,
} from "@/service/curated-content/curated-content";
import { Badge } from "@/components/ui/badge";
import { MailIcon } from "lucide-react";
import { ChevronDown, ChevronUp, MailIcon } from "lucide-react";
import { Swiper, SwiperSlide } from "swiper/react";
import "swiper/css";
import "swiper/css/free-mode";
@ -51,6 +51,7 @@ import {
Dialog,
DialogContent,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Textarea } from "@/components/ui/textarea";
import { loading } from "@/config/swal";
@ -64,6 +65,7 @@ import SuggestionModal from "@/components/modal/suggestions-modal";
import { formatDateToIndonesian } from "@/utils/globals";
import ApprovalHistoryModal from "@/components/modal/approval-history-modal";
import VideoPlayer from "@/utils/video-player";
import { getUserLevelForAssignments } from "@/service/task";
const imageSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }),
@ -114,19 +116,27 @@ const ViewEditor = dynamic(
{ ssr: false }
);
interface Destination {
id: string;
name: string;
subDestination?: SubDestination[];
}
interface SubDestination {
id: string;
name: string;
}
export default function FormVideoDetail() {
const MySwal = withReactContent(Swal);
const router = useRouter();
const userId = getCookiesDecrypt("uie");
const userLevelId = getCookiesDecrypt("ulie");
const roleId = getCookiesDecrypt("urie");
const [modalOpen, setModalOpen] = useState(false);
const { id } = useParams() as { id: string };
console.log(id);
const editor = useRef(null);
type ImageSchema = z.infer<typeof imageSchema>;
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const taskId = Cookies.get("taskId");
const scheduleId = Cookies.get("scheduleId");
@ -142,7 +152,6 @@ export default function FormVideoDetail() {
const [main, setMain] = useState<any>([]);
const [detailVideo, setDetailVideo] = useState<any>([]);
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
const t = useTranslations("Form");
const [filePlacements, setFilePlacements] = useState<string[][]>([]);
const [selectedTarget, setSelectedTarget] = useState("");
@ -150,9 +159,138 @@ export default function FormVideoDetail() {
const [rejectedFiles, setRejectedFiles] = useState<number[]>([]);
const [isUserMabesApprover, setIsUserMabesApprover] = useState(false);
const [approval, setApproval] = useState<any>();
const [expandedPolda, setExpandedPolda] = useState([{}]);
const [unitSelection, setUnitSelection] = useState({
semua: false,
nasional: false,
wilayah: false,
international: false,
polda: false,
polres: false,
satker: false,
});
const [isLoading, setIsLoading] = useState(false);
const [listDest, setListDest] = useState<Destination[]>([]);
const [checkedLevels, setCheckedLevels] = useState<any>(new Set());
let fileTypeId = "2";
const toggleExpand = (id: number) => {
setExpandedPolda((prev) => ({
...prev,
[id]: !prev[id],
}));
};
useEffect(() => {
if (detail?.assignedToTopLevel) {
const outputSet = new Set(
detail.assignedToTopLevel.split(",").map(Number)
);
setUnitSelection({
semua: outputSet.has(0),
nasional: outputSet.has(1),
wilayah: outputSet.has(2),
international: outputSet.has(3),
polda: outputSet.has(4),
polres: outputSet.has(5),
satker: outputSet.has(6),
});
}
}, [detail?.fileTypeOutput]);
useEffect(() => {
async function fetchPoldaPolres() {
setIsLoading(true);
try {
const response = await getUserLevelForAssignments();
setListDest(response?.data?.data.list);
const initialExpandedState = response?.data?.data.list.reduce(
(acc: any, polda: any) => {
acc[polda.id] = false;
return acc;
},
{}
);
setExpandedPolda(initialExpandedState);
console.log("polres", initialExpandedState);
} catch (error) {
console.error("Error fetching Polda/Polres data:", error);
} finally {
setIsLoading(false);
}
}
fetchPoldaPolres();
}, []);
useEffect(() => {
const updated = new Set(checkedLevels);
if (unitSelection.polda) {
listDest.forEach((polda) => {
updated.add(polda.id);
});
}
if (unitSelection.polres) {
listDest.forEach((polda) => {
polda?.subDestination?.forEach((polres: any) => {
updated.add(polres.id);
});
});
}
setCheckedLevels(updated);
}, [unitSelection.polda, unitSelection.polres, listDest]);
const handleUnitChange = (
key: keyof typeof unitSelection,
value: boolean
) => {
if (key === "semua") {
const newState = {
semua: value,
nasional: value,
wilayah: value,
international: value,
polda: value,
polres: value,
satker: value,
};
setUnitSelection(newState);
} else {
const updatedSelection = {
...unitSelection,
[key]: value,
};
const allChecked = [
"nasional",
"wilayah",
"international",
"polda",
"polres",
"satker",
].every((k) => updatedSelection[k as keyof typeof unitSelection]);
updatedSelection.semua = allChecked;
setUnitSelection(updatedSelection);
}
};
const handleCheckboxChangePlacement = (levelId: number) => {
setCheckedLevels((prev: any) => {
const updatedLevels = new Set(prev);
if (updatedLevels.has(levelId)) {
updatedLevels.delete(levelId);
} else {
updatedLevels.add(levelId);
}
return updatedLevels;
});
};
const {
control,
handleSubmit,
@ -238,6 +376,13 @@ export default function FormVideoDetail() {
format: details?.files[0]?.format,
});
if (details?.assignedToLevel) {
const levels = new Set(
details.assignedToLevel.split(",").map(Number)
);
setCheckedLevels(levels);
}
if (details?.publishedForObject) {
const publisherIds = details.publishedForObject.map(
(obj: any) => obj.id
@ -303,6 +448,7 @@ export default function FormVideoDetail() {
statusId: status,
message: description,
files: isUserMabesApprover ? getPlacement() : [],
customLocationPlacement: Array.from(checkedLevels).join(","),
};
setModalOpen(false);
loading();
@ -354,9 +500,9 @@ export default function FormVideoDetail() {
temp[index] = ["all", "mabes", "polda", "international"];
} else {
const now = temp[index];
now.push(placement);
if (now.length === 3 && !now.includes("all")) {
now.push("all");
now?.push(placement);
if (now?.length === 3 && !now.includes("all")) {
now?.push("all");
}
temp[index] = now;
}
@ -367,7 +513,7 @@ export default function FormVideoDetail() {
const now = temp[index].filter((a) => a !== placement);
console.log("now", now);
temp[index] = now;
if (now.length === 3 && now.includes("all")) {
if (now?.length === 3 && now.includes("all")) {
const newData = now.filter((b) => b !== "all");
temp[index] = newData;
}
@ -715,7 +861,195 @@ export default function FormVideoDetail() {
<Icon icon="humbleicons:times" color="red" />
</a>
</div>
{isUserMabesApprover && (
<div>
{/* --- Checkbox utama --- */}
<div className="flex flex-wrap gap-3 mt-6 lg:pt-7 lg:ml-3">
{[
"semua",
"nasional",
"wilayah",
"international",
].map((key, index) => (
<div
className="flex items-center gap-2"
key={key}
>
<Checkbox
id={key}
checked={
unitSelection[
key as keyof typeof unitSelection
]
}
onCheckedChange={(value) => {
handleUnitChange(
key as keyof typeof unitSelection,
value as boolean
);
setupPlacement(
index,
key,
Boolean(value)
);
}}
/>
<Label htmlFor={key}>
{key.charAt(0).toUpperCase() +
key.slice(1)}
</Label>
</div>
))}
</div>
{/* --- Kalau wilayah dicentang baru tampilkan detail --- */}
{unitSelection.wilayah && (
<div className="mt-6 lg:pt-6 lg:pl-3 space-y-3">
<div className="flex flex-wrap gap-3">
{["polda", "polres", "satker"].map(
(key, index) => (
<div
className="flex items-center gap-2"
key={key}
>
<Checkbox
id={key}
checked={
unitSelection[
key as keyof typeof unitSelection
]
}
onCheckedChange={(value) => {
handleUnitChange(
key as keyof typeof unitSelection,
value as boolean
);
setupPlacement(
index,
key,
Boolean(value)
);
}}
/>
<Label htmlFor={key}>
{key.charAt(0).toUpperCase() +
key.slice(1)}
</Label>
</div>
)
)}
</div>
{/* --- Custom Button + Dialog --- */}
<Dialog>
<DialogTrigger asChild>
<Button
variant="soft"
size="sm"
color="primary"
>
[
{t("custom", {
defaultValue: "Custom",
})}
]
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
<DialogHeader>
<DialogTitle>
Daftar Wilayah Polda dan Polres
</DialogTitle>
</DialogHeader>
<div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
{listDest.map((polda: any) => {
const poldaChecked =
unitSelection.polda;
const polresChecked =
unitSelection.polres;
const isPoldaDisabled = poldaChecked;
const isPolresDisabled =
polresChecked;
return (
<div
key={polda.id}
className="border p-2"
>
<Label className="flex items-center">
<Checkbox
checked={
poldaChecked ||
checkedLevels.has(polda.id)
}
disabled={isPoldaDisabled}
onCheckedChange={() => {
if (isPoldaDisabled) return;
handleCheckboxChangePlacement(
polda.id
);
}}
className="mr-3"
/>
{polda.name}
<button
onClick={() =>
toggleExpand(polda.id)
}
className="ml-2 focus:outline-none"
>
{expandedPolda[polda.id] ? (
<ChevronUp size={16} />
) : (
<ChevronDown size={16} />
)}
</button>
</Label>
{expandedPolda[polda.id] && (
<div className="ml-6 mt-2">
{polda?.subDestination?.map(
(polres: any) => (
<Label
key={polres.id}
className="block mt-1"
>
<Checkbox
checked={
polresChecked ||
checkedLevels.has(
polres.id
)
}
disabled={
isPolresDisabled
}
onCheckedChange={() => {
if (
isPolresDisabled
)
return;
handleCheckboxChangePlacement(
polres.id
);
}}
className="mr-2"
/>
{polres.name}
</Label>
)
)}
</div>
)}
</div>
);
})}
</div>
</DialogContent>
</Dialog>
</div>
)}
</div>
{/* {isUserMabesApprover && (
<div className="flex flex-row gap-2">
<div className="flex items-center space-x-2">
<Checkbox
@ -792,7 +1126,7 @@ export default function FormVideoDetail() {
</label>
</div>
</div>
)}
)} */}
</div>
</div>
))

View File

@ -318,7 +318,7 @@ export default function FormTaskEdit() {
};
const handlePoldaPolresChange = () => {
return Array.from(checkedLevels).join(","); // Mengonversi Set ke string
return Array.from(checkedLevels).join(",");
};
const handleUnitChange = (
@ -471,9 +471,9 @@ export default function FormTaskEdit() {
await uploadResumableFile(
index,
String(id),
item, // Use .file to access the actual File object
item,
"4",
"0" // Optional: Replace with actual duration if available
"0"
);
});
@ -808,11 +808,11 @@ export default function FormTaskEdit() {
</DialogHeader>
<div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
{listDest.map((polda: any) => {
const poldaChecked = unitSelection.polda; // kontrol polda luar
const polresChecked = unitSelection.polres; // kontrol polres luar
const poldaChecked = unitSelection.polda;
const polresChecked = unitSelection.polres;
const isPoldaDisabled = poldaChecked; // lock checkbox polda dialog jika polda luar dicentang
const isPolresDisabled = polresChecked; // lock checkbox polres dialog jika polres luar dicentang
const isPoldaDisabled = poldaChecked;
const isPolresDisabled = polresChecked;
return (
<div key={polda.id} className="border p-2">
@ -820,7 +820,7 @@ export default function FormTaskEdit() {
<Checkbox
checked={
poldaChecked || checkedLevels.has(polda.id)
} // auto-centang jika polda luar dicentang
}
disabled={isPoldaDisabled}
onCheckedChange={() => {
if (isPoldaDisabled) return;
@ -852,7 +852,7 @@ export default function FormTaskEdit() {
checked={
polresChecked ||
checkedLevels.has(polres.id)
} // auto-centang jika polres luar dicentang
}
disabled={isPolresDisabled}
onCheckedChange={() => {
if (isPolresDisabled) return;
@ -876,7 +876,7 @@ export default function FormTaskEdit() {
<div className="mt-5 space-y-2">
<Label>{t("type-task", { defaultValue: "Type Task" })}</Label>
<RadioGroup
defaultValue={detail.assignmentMainType.id.toString()} // State yang dipetakan ke value RadioGroup
defaultValue={detail.assignmentMainType.id.toString()}
onValueChange={(value) => setMainType(value)}
// value={String(mainType)}
// onValueChange={(value) => setMainType(Number(value))}
@ -893,7 +893,7 @@ export default function FormTaskEdit() {
{t("assigment-type", { defaultValue: "Assigment Type" })}{" "}
</Label>
<RadioGroup
value={taskType} // ✅ controlled
value={taskType}
onValueChange={(value) => setTaskType(value)}
className="flex flex-wrap gap-3"
>

View File

@ -63,7 +63,7 @@ export async function listDataImage(
isInt: boolean = false
) {
return await httpGetInterceptor(
`media/list?enablePage=1&size=${size}&sortBy=createdAt&sort=desc&page=${page}&typeId=1&isForSelf=${isForSelf}&isApproval=${isApproval}&categoryId=${categoryFilter}&statusId=${statusFilter}&creatorUserLevelName=${source}&creatorName=${creator}&startDate=${startDate}&endDate=${endDate}&title=${title}&creatorGroupLevelName=${creatorGroup}&needApprovalFromLevel=${needApprovalFromLevel}&isInt=${isInt}`
`media/list?enablePage=1&size=${size}&sortBy=createdAt&sort=desc&page=${page}&typeId=1&isForSelf=${isForSelf}&isApproval=${isApproval}&categoryId=${categoryFilter}&statusId=${statusFilter}&creatorUserLevelName=${source}&creatorName=${creator}&startDate=${startDate}&endDate=${endDate}&title=${title}&creatorGroupLevelName=${creatorGroup}&needApprovalFromLevel=${needApprovalFromLevel}&isInt=${isInt}&isPublish=`
);
}