fix: add checkbox wilayah in aprove content, add select date in dashboard generate report
This commit is contained in:
parent
5ae245c123
commit
24a5e2a5c1
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -179,7 +179,6 @@ const TableSPIT = () => {
|
|||
React.useEffect(() => {
|
||||
fetchData();
|
||||
getCategories();
|
||||
console.log("TRIGGERRRR");
|
||||
}, [statusFilter, page, showData, search, dateFilter]);
|
||||
|
||||
async function getCategories() {
|
||||
|
|
|
|||
|
|
@ -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[]>(
|
||||
[]
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
))
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
))
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
))
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
))
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -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=`
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue