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 [selectedCategory, setSelectedCategory] = useState<string[]>([]);
|
||||||
const [selectedEventDate, setSelectedEventDate] = useState<Date | null>(null);
|
const [selectedEventDate, setSelectedEventDate] = useState<Date | null>(null);
|
||||||
const roleId = Number(getCookiesDecrypt("urie")) || 0;
|
const roleId = Number(getCookiesDecrypt("urie")) || 0;
|
||||||
console.log("DUARR", roleId)
|
const levelNumber = Number(getCookiesDecrypt("ulne")) || 0;
|
||||||
const userLevelId = Number(getCookiesDecrypt("ulie")) || 0;
|
const userLevelId = Number(getCookiesDecrypt("ulie")) || 0;
|
||||||
const [calendarEvents, setCalendarEvents] = useState<CalendarEvent[]>([]);
|
const [calendarEvents, setCalendarEvents] = useState<CalendarEvent[]>([]);
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
|
@ -415,7 +415,9 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
<div className="px-2">
|
<div className="px-2">
|
||||||
{events.length === 0 ? (
|
{events.length === 0 ? (
|
||||||
<div className="mt-1 py-2 rounded-lg bg-white border border-black">
|
<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>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|
@ -527,7 +529,16 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
<Card className="col-span-12 lg:col-span-4 2xl:col-span-3 pb-5">
|
<Card className="col-span-12 lg:col-span-4 2xl:col-span-3 pb-5">
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
<CardHeader className="border-none mb-2 pt-5">
|
<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
|
<Button
|
||||||
onClick={handleDateClick}
|
onClick={handleDateClick}
|
||||||
className="dark:bg-background dark:text-foreground w-full"
|
className="dark:bg-background dark:text-foreground w-full"
|
||||||
|
|
@ -542,12 +553,18 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button className="dark:bg-background dark:text-foreground w-full">
|
<Button className="dark:bg-background dark:text-foreground w-full">
|
||||||
<Book size={15} className="w-4 h-4 mr-3" />
|
<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>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px] overflow-y-auto max-h-[500px]">
|
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px] overflow-y-auto max-h-[500px]">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>{t("monitoring-results", { defaultValue: "Monitoring Results" })}</DialogTitle>
|
<DialogTitle>
|
||||||
|
{t("monitoring-results", {
|
||||||
|
defaultValue: "Monitoring Results",
|
||||||
|
})}
|
||||||
|
</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{getModalContent()}
|
{getModalContent()}
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,6 @@ const TableSPIT = () => {
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
getCategories();
|
getCategories();
|
||||||
console.log("TRIGGERRRR");
|
|
||||||
}, [statusFilter, page, showData, search, dateFilter]);
|
}, [statusFilter, page, showData, search, dateFilter]);
|
||||||
|
|
||||||
async function getCategories() {
|
async function getCategories() {
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,6 @@ import useTableColumns from "./columns";
|
||||||
const TableTeks = () => {
|
const TableTeks = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||||
const [totalData, setTotalData] = React.useState<number>(1);
|
const [totalData, setTotalData] = React.useState<number>(1);
|
||||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
|
|
@ -77,7 +76,7 @@ const TableTeks = () => {
|
||||||
const [columnVisibility, setColumnVisibility] =
|
const [columnVisibility, setColumnVisibility] =
|
||||||
React.useState<VisibilityState>({});
|
React.useState<VisibilityState>({});
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
const [showData, setShowData] = React.useState("50");
|
const [showData, setShowData] = React.useState("10");
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: Number(showData),
|
pageSize: Number(showData),
|
||||||
|
|
@ -88,7 +87,6 @@ const TableTeks = () => {
|
||||||
const [search, setSearch] = React.useState<string>("");
|
const [search, setSearch] = React.useState<string>("");
|
||||||
const userId = getCookiesDecrypt("uie");
|
const userId = getCookiesDecrypt("uie");
|
||||||
const userLevelId = getCookiesDecrypt("ulie");
|
const userLevelId = getCookiesDecrypt("ulie");
|
||||||
|
|
||||||
const [categories, setCategories] = React.useState<any[]>([]);
|
const [categories, setCategories] = React.useState<any[]>([]);
|
||||||
const [selectedCategories, setSelectedCategories] = React.useState<number[]>(
|
const [selectedCategories, setSelectedCategories] = React.useState<number[]>(
|
||||||
[]
|
[]
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ const ReportTable = () => {
|
||||||
const [columnVisibility, setColumnVisibility] =
|
const [columnVisibility, setColumnVisibility] =
|
||||||
React.useState<VisibilityState>({});
|
React.useState<VisibilityState>({});
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
const [showData, setShowData] = React.useState("50");
|
const [showData, setShowData] = React.useState("10");
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: Number(showData),
|
pageSize: Number(showData),
|
||||||
|
|
@ -106,6 +106,8 @@ const ReportTable = () => {
|
||||||
const [statusFilter, setStatusFilter] = React.useState<any[]>([]);
|
const [statusFilter, setStatusFilter] = React.useState<any[]>([]);
|
||||||
const [openPreview, setOpenPreview] = React.useState(false);
|
const [openPreview, setOpenPreview] = React.useState(false);
|
||||||
const [previewData, setPreviewData] = React.useState<any>(null);
|
const [previewData, setPreviewData] = React.useState<any>(null);
|
||||||
|
const [openDateDialog, setOpenDateDialog] = React.useState(false);
|
||||||
|
const [reportDate, setReportDate] = React.useState("");
|
||||||
|
|
||||||
const handlePreview = (id: string) => {
|
const handlePreview = (id: string) => {
|
||||||
const url = `https://new.netidhub.com/api/media/report/view?id=${id}`;
|
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.filter((id: any) => id !== categoryId)
|
||||||
: [...prev, categoryId]
|
: [...prev, categoryId]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Perbarui filter kategori
|
|
||||||
setCategoryFilter((prev) => {
|
setCategoryFilter((prev) => {
|
||||||
const updatedCategories = prev.split(",").filter(Boolean).map(Number);
|
const updatedCategories = prev.split(",").filter(Boolean).map(Number);
|
||||||
|
|
||||||
|
|
@ -206,22 +206,65 @@ const ReportTable = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setSearch(e.target.value); // Perbarui state search
|
setSearch(e.target.value);
|
||||||
table.getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel
|
table.getColumn("judul")?.setFilterValue(e.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleGenerateReport = async () => {
|
// const handleGenerateReport = async () => {
|
||||||
const today = new Date();
|
// const today = new Date();
|
||||||
const formattedDate = format(today, "dd-MM-yyyy"); // Hasil: 22-04-2025
|
// const formattedDate = format(today, "dd-MM-yyyy");
|
||||||
const title = `Report ${formattedDate}`;
|
// 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 = {
|
const requestData = {
|
||||||
title,
|
title,
|
||||||
|
date: reportDate,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await saveReport(requestData);
|
const response = await saveReport(requestData);
|
||||||
|
|
||||||
if (response?.error) {
|
if (response?.error) {
|
||||||
MySwal.fire(
|
MySwal.fire(
|
||||||
"Error",
|
"Error",
|
||||||
|
|
@ -238,7 +281,8 @@ const ReportTable = () => {
|
||||||
confirmButtonColor: "#3085d6",
|
confirmButtonColor: "#3085d6",
|
||||||
confirmButtonText: "OK",
|
confirmButtonText: "OK",
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
fetchData(); // Refresh tabel setelah generate
|
fetchData();
|
||||||
|
setReportDate("");
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Generate report error:", error);
|
console.error("Generate report error:", error);
|
||||||
|
|
@ -269,10 +313,19 @@ const ReportTable = () => {
|
||||||
<CardTitle>
|
<CardTitle>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="flex-1 text-xl font-medium text-default-900">
|
<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>
|
||||||
<div className="flex-none">
|
<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" />
|
<Plus size={18} className=" me-1.5" />
|
||||||
{t("generate-report", { defaultValue: "Generate Report" })}
|
{t("generate-report", { defaultValue: "Generate Report" })}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -472,6 +525,38 @@ const ReportTable = () => {
|
||||||
totalPage={totalPage}
|
totalPage={totalPage}
|
||||||
/>
|
/>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ import {
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||||
|
|
||||||
import { register } from "module";
|
import { register } from "module";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import Cookies from "js-cookie";
|
import Cookies from "js-cookie";
|
||||||
|
|
@ -35,7 +34,7 @@ import {
|
||||||
getDataApprovalByMediaUpload,
|
getDataApprovalByMediaUpload,
|
||||||
} from "@/service/curated-content/curated-content";
|
} from "@/service/curated-content/curated-content";
|
||||||
import { Badge } from "@/components/ui/badge";
|
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, SwiperSlide } from "swiper/react";
|
||||||
import "swiper/css";
|
import "swiper/css";
|
||||||
import "swiper/css/free-mode";
|
import "swiper/css/free-mode";
|
||||||
|
|
@ -51,6 +50,7 @@ import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { loading } from "@/config/swal";
|
import { loading } from "@/config/swal";
|
||||||
|
|
@ -66,6 +66,7 @@ import { formatDateToIndonesian } from "@/utils/globals";
|
||||||
import ApprovalHistoryModal from "@/components/modal/approval-history-modal";
|
import ApprovalHistoryModal from "@/components/modal/approval-history-modal";
|
||||||
import { useDropzone } from "react-dropzone";
|
import { useDropzone } from "react-dropzone";
|
||||||
import AudioPlayer from "@/components/audio-player";
|
import AudioPlayer from "@/components/audio-player";
|
||||||
|
import { getUserLevelForAssignments } from "@/service/task";
|
||||||
|
|
||||||
const imageSchema = z.object({
|
const imageSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -115,6 +116,17 @@ const ViewEditor = dynamic(
|
||||||
{ ssr: false }
|
{ ssr: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
interface Destination {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
subDestination?: SubDestination[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SubDestination {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default function FormAudioDetail() {
|
export default function FormAudioDetail() {
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -155,6 +167,135 @@ export default function FormAudioDetail() {
|
||||||
const [wavesurfer, setWavesurfer] = useState<WaveSurfer>();
|
const [wavesurfer, setWavesurfer] = useState<WaveSurfer>();
|
||||||
const [isPlaying, setIsPlaying] = useState(false);
|
const [isPlaying, setIsPlaying] = useState(false);
|
||||||
const [approval, setApproval] = useState<any>();
|
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[]) => {
|
const onDrop = (acceptedFiles: File[]) => {
|
||||||
setUploadedFiles(acceptedFiles);
|
setUploadedFiles(acceptedFiles);
|
||||||
|
|
@ -270,10 +411,7 @@ export default function FormAudioDetail() {
|
||||||
if (id) {
|
if (id) {
|
||||||
const response = await detailMedia(id);
|
const response = await detailMedia(id);
|
||||||
const details = response?.data?.data;
|
const details = response?.data?.data;
|
||||||
console.log("detail", details);
|
|
||||||
setFiles(details?.files);
|
setFiles(details?.files);
|
||||||
console.log("ISI FILES:", details?.files);
|
|
||||||
|
|
||||||
setDetail(details);
|
setDetail(details);
|
||||||
setMain({
|
setMain({
|
||||||
type: details?.fileType.name,
|
type: details?.fileType.name,
|
||||||
|
|
@ -283,6 +421,13 @@ export default function FormAudioDetail() {
|
||||||
});
|
});
|
||||||
setupPlacementCheck(details?.files?.length);
|
setupPlacementCheck(details?.files?.length);
|
||||||
|
|
||||||
|
if (details?.assignedToLevel) {
|
||||||
|
const levels = new Set(
|
||||||
|
details.assignedToLevel.split(",").map(Number)
|
||||||
|
);
|
||||||
|
setCheckedLevels(levels);
|
||||||
|
}
|
||||||
|
|
||||||
if (details?.publishedForObject) {
|
if (details?.publishedForObject) {
|
||||||
const publisherIds = details?.publishedForObject.map(
|
const publisherIds = details?.publishedForObject.map(
|
||||||
(obj: any) => obj.id
|
(obj: any) => obj.id
|
||||||
|
|
@ -358,9 +503,9 @@ export default function FormAudioDetail() {
|
||||||
console.log("getPlaa", filePlacements);
|
console.log("getPlaa", filePlacements);
|
||||||
const temp = [];
|
const temp = [];
|
||||||
for (let i = 0; i < filePlacements?.length; i++) {
|
for (let i = 0; i < filePlacements?.length; i++) {
|
||||||
if (filePlacements[i].length !== 0) {
|
if (filePlacements[i]?.length !== 0) {
|
||||||
const now = filePlacements[i].filter((a) => a !== "all");
|
const now = filePlacements[i]?.filter((a) => a !== "all");
|
||||||
const data = { mediaFileId: files[i].id, placements: now.join(",") };
|
const data = { mediaFileId: files[i]?.id, placements: now.join(",") };
|
||||||
temp.push(data);
|
temp.push(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -374,6 +519,7 @@ export default function FormAudioDetail() {
|
||||||
message: description,
|
message: description,
|
||||||
// files: [],
|
// files: [],
|
||||||
files: isUserMabesApprover ? getPlacement() : [],
|
files: isUserMabesApprover ? getPlacement() : [],
|
||||||
|
customLocationPlacement: Array.from(checkedLevels).join(","),
|
||||||
};
|
};
|
||||||
setModalOpen(false);
|
setModalOpen(false);
|
||||||
loading();
|
loading();
|
||||||
|
|
@ -420,9 +566,9 @@ export default function FormAudioDetail() {
|
||||||
temp[index] = ["all", "mabes", "polda", "international"];
|
temp[index] = ["all", "mabes", "polda", "international"];
|
||||||
} else {
|
} else {
|
||||||
const now = temp[index];
|
const now = temp[index];
|
||||||
now.push(placement);
|
now?.push(placement);
|
||||||
if (now.length === 3 && !now.includes("all")) {
|
if (now?.length === 3 && !now?.includes("all")) {
|
||||||
now.push("all");
|
now?.push("all");
|
||||||
}
|
}
|
||||||
temp[index] = now;
|
temp[index] = now;
|
||||||
}
|
}
|
||||||
|
|
@ -431,9 +577,8 @@ export default function FormAudioDetail() {
|
||||||
temp[index] = [];
|
temp[index] = [];
|
||||||
} else {
|
} else {
|
||||||
const now = temp[index].filter((a) => a !== placement);
|
const now = temp[index].filter((a) => a !== placement);
|
||||||
console.log("now", now);
|
|
||||||
temp[index] = 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");
|
const newData = now.filter((b) => b !== "all");
|
||||||
temp[index] = newData;
|
temp[index] = newData;
|
||||||
}
|
}
|
||||||
|
|
@ -734,7 +879,195 @@ export default function FormAudioDetail() {
|
||||||
<Icon icon="humbleicons:times" color="red" />
|
<Icon icon="humbleicons:times" color="red" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</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 flex-row gap-2">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|
@ -811,7 +1144,7 @@ export default function FormAudioDetail() {
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)} */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ import {
|
||||||
getDataApprovalByMediaUpload,
|
getDataApprovalByMediaUpload,
|
||||||
} from "@/service/curated-content/curated-content";
|
} from "@/service/curated-content/curated-content";
|
||||||
import { Badge } from "@/components/ui/badge";
|
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, SwiperSlide } from "swiper/react";
|
||||||
import "swiper/css";
|
import "swiper/css";
|
||||||
import "swiper/css/free-mode";
|
import "swiper/css/free-mode";
|
||||||
|
|
@ -67,6 +67,7 @@ import SuggestionModal from "@/components/modal/suggestions-modal";
|
||||||
import { formatDateToIndonesian } from "@/utils/globals";
|
import { formatDateToIndonesian } from "@/utils/globals";
|
||||||
import ApprovalHistoryModal from "@/components/modal/approval-history-modal";
|
import ApprovalHistoryModal from "@/components/modal/approval-history-modal";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
import { getUserLevelForAssignments } from "@/service/task";
|
||||||
|
|
||||||
const imageSchema = z.object({
|
const imageSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -116,6 +117,17 @@ const ViewEditor = dynamic(
|
||||||
{ ssr: false }
|
{ ssr: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
interface Destination {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
subDestination?: SubDestination[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SubDestination {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default function FormImageDetail() {
|
export default function FormImageDetail() {
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -123,13 +135,13 @@ export default function FormImageDetail() {
|
||||||
const userLevelId = getCookiesDecrypt("ulie");
|
const userLevelId = getCookiesDecrypt("ulie");
|
||||||
const userLevelName = Cookies.get("state");
|
const userLevelName = Cookies.get("state");
|
||||||
const roleId = getCookiesDecrypt("urie");
|
const roleId = getCookiesDecrypt("urie");
|
||||||
|
const [listDest, setListDest] = useState<Destination[]>([]);
|
||||||
console.log("LALALALA", userLevelName);
|
console.log("LALALALA", userLevelName);
|
||||||
const [modalOpen, setModalOpen] = useState(false);
|
const [modalOpen, setModalOpen] = useState(false);
|
||||||
const { id } = useParams() as { id: string };
|
const { id } = useParams() as { id: string };
|
||||||
console.log(id);
|
console.log(id);
|
||||||
const editor = useRef(null);
|
const editor = useRef(null);
|
||||||
type ImageSchema = z.infer<typeof imageSchema>;
|
type ImageSchema = z.infer<typeof imageSchema>;
|
||||||
|
|
||||||
const t = useTranslations("Form");
|
const t = useTranslations("Form");
|
||||||
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
||||||
const taskId = Cookies.get("taskId");
|
const taskId = Cookies.get("taskId");
|
||||||
|
|
@ -149,10 +161,21 @@ export default function FormImageDetail() {
|
||||||
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
|
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
|
||||||
const [isUserMabesApprover, setIsUserMabesApprover] = useState(false);
|
const [isUserMabesApprover, setIsUserMabesApprover] = useState(false);
|
||||||
const [approval, setApproval] = useState<any>();
|
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 [selectedTarget, setSelectedTarget] = useState("");
|
||||||
const [files, setFiles] = useState<FileType[]>([]);
|
const [files, setFiles] = useState<FileType[]>([]);
|
||||||
const [rejectedFiles, setRejectedFiles] = useState<number[]>([]);
|
const [rejectedFiles, setRejectedFiles] = useState<number[]>([]);
|
||||||
|
const [expandedPolda, setExpandedPolda] = useState([{}]);
|
||||||
const [wilayahPublish, setWilayahPublish] = React.useState({
|
const [wilayahPublish, setWilayahPublish] = React.useState({
|
||||||
semua: false,
|
semua: false,
|
||||||
nasional: false,
|
nasional: false,
|
||||||
|
|
@ -165,6 +188,117 @@ export default function FormImageDetail() {
|
||||||
|
|
||||||
let fileTypeId = "1";
|
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 {
|
const {
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
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(() => {
|
useEffect(() => {
|
||||||
async function initState() {
|
async function initState() {
|
||||||
getCategories();
|
getCategories();
|
||||||
|
|
@ -214,7 +360,7 @@ export default function FormImageDetail() {
|
||||||
|
|
||||||
if (findCategory) {
|
if (findCategory) {
|
||||||
// setValue("categoryId", findCategory.id);
|
// setValue("categoryId", findCategory.id);
|
||||||
setSelectedCategory(findCategory.id); // Set the selected category
|
setSelectedCategory(findCategory.id);
|
||||||
const response = await getTagsBySubCategoryId(findCategory.id);
|
const response = await getTagsBySubCategoryId(findCategory.id);
|
||||||
setTags(response?.data?.data);
|
setTags(response?.data?.data);
|
||||||
}
|
}
|
||||||
|
|
@ -248,6 +394,13 @@ export default function FormImageDetail() {
|
||||||
});
|
});
|
||||||
setupPlacementCheck(details?.files?.length);
|
setupPlacementCheck(details?.files?.length);
|
||||||
|
|
||||||
|
if (details?.assignedToLevel) {
|
||||||
|
const levels = new Set(
|
||||||
|
details.assignedToLevel.split(",").map(Number)
|
||||||
|
);
|
||||||
|
setCheckedLevels(levels);
|
||||||
|
}
|
||||||
|
|
||||||
if (details.publishedForObject) {
|
if (details.publishedForObject) {
|
||||||
const publisherIds = details.publishedForObject.map(
|
const publisherIds = details.publishedForObject.map(
|
||||||
(obj: any) => obj.id
|
(obj: any) => obj.id
|
||||||
|
|
@ -327,8 +480,8 @@ export default function FormImageDetail() {
|
||||||
statusId: status,
|
statusId: status,
|
||||||
message: description,
|
message: description,
|
||||||
files: isUserMabesApprover ? getPlacement() : [],
|
files: isUserMabesApprover ? getPlacement() : [],
|
||||||
|
customLocationPlacement: Array.from(checkedLevels).join(","),
|
||||||
};
|
};
|
||||||
|
|
||||||
setModalOpen(false);
|
setModalOpen(false);
|
||||||
loading();
|
loading();
|
||||||
const response = await submitApproval(data);
|
const response = await submitApproval(data);
|
||||||
|
|
@ -368,7 +521,7 @@ export default function FormImageDetail() {
|
||||||
temp[index] = ["all", "mabes", "polda", "international"];
|
temp[index] = ["all", "mabes", "polda", "international"];
|
||||||
} else {
|
} else {
|
||||||
const now = temp[index];
|
const now = temp[index];
|
||||||
now.push(placement);
|
now?.push(placement);
|
||||||
if (now.length === 3 && !now.includes("all")) {
|
if (now.length === 3 && !now.includes("all")) {
|
||||||
now.push("all");
|
now.push("all");
|
||||||
}
|
}
|
||||||
|
|
@ -397,6 +550,7 @@ export default function FormImageDetail() {
|
||||||
rejects.push(id);
|
rejects.push(id);
|
||||||
setRejectedFiles(rejects);
|
setRejectedFiles(rejects);
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleMain = (
|
const handleMain = (
|
||||||
type: string,
|
type: string,
|
||||||
url: string,
|
url: string,
|
||||||
|
|
@ -454,7 +608,9 @@ export default function FormImageDetail() {
|
||||||
<div className="flex flex-col lg:flex-row gap-10">
|
<div className="flex flex-col lg:flex-row gap-10">
|
||||||
<Card className="w-full lg:w-8/12">
|
<Card className="w-full lg:w-8/12">
|
||||||
<div className="px-6 py-6">
|
<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">
|
<div className="gap-5 mb-5">
|
||||||
{/* Input Title */}
|
{/* Input Title */}
|
||||||
<div className="space-y-2 py-3">
|
<div className="space-y-2 py-3">
|
||||||
|
|
@ -495,16 +651,23 @@ export default function FormImageDetail() {
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{/* Show the category from details if it doesn't exist in categories list */}
|
{/* Show the category from details if it doesn't exist in categories list */}
|
||||||
{detail && !categories.find(cat => String(cat.id) === String(detail.category.id)) && (
|
{detail &&
|
||||||
<SelectItem
|
!categories.find(
|
||||||
key={String(detail.category.id)}
|
(cat) =>
|
||||||
value={String(detail.category.id)}
|
String(cat.id) === String(detail.category.id)
|
||||||
>
|
) && (
|
||||||
{detail.category.name}
|
<SelectItem
|
||||||
</SelectItem>
|
key={String(detail.category.id)}
|
||||||
)}
|
value={String(detail.category.id)}
|
||||||
|
>
|
||||||
|
{detail.category.name}
|
||||||
|
</SelectItem>
|
||||||
|
)}
|
||||||
{categories.map((category) => (
|
{categories.map((category) => (
|
||||||
<SelectItem key={String(category.id)} value={String(category.id)}>
|
<SelectItem
|
||||||
|
key={String(category.id)}
|
||||||
|
value={String(category.id)}
|
||||||
|
>
|
||||||
{category.name}
|
{category.name}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
|
|
@ -514,7 +677,9 @@ export default function FormImageDetail() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="py-3 space-y-2">
|
<div className="py-3 space-y-2">
|
||||||
<Label>{t("description", { defaultValue: "Description" })}</Label>
|
<Label>
|
||||||
|
{t("description", { defaultValue: "Description" })}
|
||||||
|
</Label>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="description"
|
name="description"
|
||||||
|
|
@ -529,7 +694,9 @@ export default function FormImageDetail() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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 ">
|
<div className="w-full ">
|
||||||
<Swiper
|
<Swiper
|
||||||
thumbs={{ swiper: thumbsSwiper }}
|
thumbs={{ swiper: thumbsSwiper }}
|
||||||
|
|
@ -628,7 +795,9 @@ export default function FormImageDetail() {
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-3">
|
<div className="px-3 py-3">
|
||||||
<div className="flex flex-col gap-6 space-y-2">
|
<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">
|
<div className="flex gap-2 items-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id="5"
|
id="5"
|
||||||
|
|
@ -729,7 +898,9 @@ export default function FormImageDetail() {
|
||||||
<Dialog open={modalOpen} onOpenChange={setModalOpen}>
|
<Dialog open={modalOpen} onOpenChange={setModalOpen}>
|
||||||
<DialogContent size="md" className="max-h-[600px]">
|
<DialogContent size="md" className="max-h-[600px]">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>{t("leave-comment", { defaultValue: "Leave Comment" })}</DialogTitle>
|
<DialogTitle>
|
||||||
|
{t("leave-comment", { defaultValue: "Leave Comment" })}
|
||||||
|
</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="flex flex-col gap-2 max-h-[208px] md:max-h-[312px] overflow-y-auto">
|
<div className="flex flex-col gap-2 max-h-[208px] md:max-h-[312px] overflow-y-auto">
|
||||||
{status == "2"
|
{status == "2"
|
||||||
|
|
@ -760,7 +931,199 @@ export default function FormImageDetail() {
|
||||||
<Icon icon="humbleicons:times" color="red" />
|
<Icon icon="humbleicons:times" color="red" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</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 flex-row gap-2">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|
@ -854,7 +1217,7 @@ export default function FormImageDetail() {
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)} */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ import {
|
||||||
getDataApprovalByMediaUpload,
|
getDataApprovalByMediaUpload,
|
||||||
} from "@/service/curated-content/curated-content";
|
} from "@/service/curated-content/curated-content";
|
||||||
import { Badge } from "@/components/ui/badge";
|
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, SwiperSlide } from "swiper/react";
|
||||||
import "swiper/css";
|
import "swiper/css";
|
||||||
import "swiper/css/free-mode";
|
import "swiper/css/free-mode";
|
||||||
|
|
@ -51,6 +51,7 @@ import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { loading } from "@/config/swal";
|
import { loading } from "@/config/swal";
|
||||||
|
|
@ -64,6 +65,7 @@ import { formatDateToIndonesian } from "@/utils/globals";
|
||||||
import ApprovalHistoryModal from "@/components/modal/approval-history-modal";
|
import ApprovalHistoryModal from "@/components/modal/approval-history-modal";
|
||||||
import FileTextPreview from "./file-preview-text";
|
import FileTextPreview from "./file-preview-text";
|
||||||
import FileTextThumbnail from "./file-text-thumbnail";
|
import FileTextThumbnail from "./file-text-thumbnail";
|
||||||
|
import { getUserLevelForAssignments } from "@/service/task";
|
||||||
|
|
||||||
const imageSchema = z.object({
|
const imageSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -113,19 +115,28 @@ const ViewEditor = dynamic(
|
||||||
{ ssr: false }
|
{ ssr: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
interface Destination {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
subDestination?: SubDestination[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SubDestination {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default function FormTeksDetail() {
|
export default function FormTeksDetail() {
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const userId = getCookiesDecrypt("uie");
|
const userId = getCookiesDecrypt("uie");
|
||||||
const userLevelId = getCookiesDecrypt("ulie");
|
const userLevelId = getCookiesDecrypt("ulie");
|
||||||
const roleId = getCookiesDecrypt("urie");
|
const roleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
const [modalOpen, setModalOpen] = useState(false);
|
const [modalOpen, setModalOpen] = useState(false);
|
||||||
const { id } = useParams() as { id: string };
|
const { id } = useParams() as { id: string };
|
||||||
console.log(id);
|
console.log(id);
|
||||||
const editor = useRef(null);
|
const editor = useRef(null);
|
||||||
type ImageSchema = z.infer<typeof imageSchema>;
|
type ImageSchema = z.infer<typeof imageSchema>;
|
||||||
|
|
||||||
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
||||||
const taskId = Cookies.get("taskId");
|
const taskId = Cookies.get("taskId");
|
||||||
const scheduleId = Cookies.get("scheduleId");
|
const scheduleId = Cookies.get("scheduleId");
|
||||||
|
|
@ -141,20 +152,146 @@ export default function FormTeksDetail() {
|
||||||
const [main, setMain] = useState<any>([]);
|
const [main, setMain] = useState<any>([]);
|
||||||
const [detailThumb, setDetailThumb] = useState<any>([]);
|
const [detailThumb, setDetailThumb] = useState<any>([]);
|
||||||
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
|
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
|
||||||
|
|
||||||
const t = useTranslations("Form");
|
const t = useTranslations("Form");
|
||||||
const [selectedTarget, setSelectedTarget] = useState("");
|
const [selectedTarget, setSelectedTarget] = useState("");
|
||||||
const [files, setFiles] = useState<FileType[]>([]);
|
const [files, setFiles] = useState<FileType[]>([]);
|
||||||
const [rejectedFiles, setRejectedFiles] = useState<number[]>([]);
|
const [rejectedFiles, setRejectedFiles] = useState<number[]>([]);
|
||||||
const [isMabesApprover, setIsMabesApprover] = useState(false);
|
const [isMabesApprover, setIsMabesApprover] = useState(false);
|
||||||
|
|
||||||
const [filePlacements, setFilePlacements] = useState<string[][]>([]);
|
const [filePlacements, setFilePlacements] = useState<string[][]>([]);
|
||||||
const [isUserMabesApprover, setIsUserMabesApprover] = useState(false);
|
const [isUserMabesApprover, setIsUserMabesApprover] = useState(false);
|
||||||
|
|
||||||
const [approval, setApproval] = useState<any>();
|
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";
|
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 {
|
const {
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
|
|
@ -248,6 +385,13 @@ export default function FormTeksDetail() {
|
||||||
format: details?.files[0]?.format,
|
format: details?.files[0]?.format,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (details?.assignedToLevel) {
|
||||||
|
const levels = new Set(
|
||||||
|
details.assignedToLevel.split(",").map(Number)
|
||||||
|
);
|
||||||
|
setCheckedLevels(levels);
|
||||||
|
}
|
||||||
|
|
||||||
if (details.publishedForObject) {
|
if (details.publishedForObject) {
|
||||||
const publisherIds = details.publishedForObject.map(
|
const publisherIds = details.publishedForObject.map(
|
||||||
(obj: any) => obj.id
|
(obj: any) => obj.id
|
||||||
|
|
@ -313,9 +457,9 @@ export default function FormTeksDetail() {
|
||||||
console.log("getPlaa", filePlacements);
|
console.log("getPlaa", filePlacements);
|
||||||
const temp = [];
|
const temp = [];
|
||||||
for (let i = 0; i < filePlacements?.length; i++) {
|
for (let i = 0; i < filePlacements?.length; i++) {
|
||||||
if (filePlacements[i].length !== 0) {
|
if (filePlacements[i]?.length !== 0) {
|
||||||
const now = filePlacements[i].filter((a) => a !== "all");
|
const now = filePlacements[i]?.filter((a) => a !== "all");
|
||||||
const data = { mediaFileId: files[i].id, placements: now.join(",") };
|
const data = { mediaFileId: files[i]?.id, placements: now?.join(",") };
|
||||||
temp.push(data);
|
temp.push(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -328,6 +472,7 @@ export default function FormTeksDetail() {
|
||||||
statusId: status,
|
statusId: status,
|
||||||
message: description,
|
message: description,
|
||||||
files: isUserMabesApprover ? getPlacement() : [],
|
files: isUserMabesApprover ? getPlacement() : [],
|
||||||
|
customLocationPlacement: Array.from(checkedLevels).join(","),
|
||||||
};
|
};
|
||||||
setModalOpen(false);
|
setModalOpen(false);
|
||||||
|
|
||||||
|
|
@ -367,9 +512,9 @@ export default function FormTeksDetail() {
|
||||||
temp[index] = ["all", "mabes", "polda", "international"];
|
temp[index] = ["all", "mabes", "polda", "international"];
|
||||||
} else {
|
} else {
|
||||||
const now = temp[index];
|
const now = temp[index];
|
||||||
now.push(placement);
|
now?.push(placement);
|
||||||
if (now.length === 3 && !now.includes("all")) {
|
if (now?.length === 3 && !now?.includes("all")) {
|
||||||
now.push("all");
|
now?.push("all");
|
||||||
}
|
}
|
||||||
temp[index] = now;
|
temp[index] = now;
|
||||||
}
|
}
|
||||||
|
|
@ -377,11 +522,10 @@ export default function FormTeksDetail() {
|
||||||
if (placement === "all") {
|
if (placement === "all") {
|
||||||
temp[index] = [];
|
temp[index] = [];
|
||||||
} else {
|
} else {
|
||||||
const now = temp[index].filter((a) => a !== placement);
|
const now = temp[index]?.filter((a) => a !== placement);
|
||||||
console.log("now", now);
|
|
||||||
temp[index] = 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");
|
const newData = now?.filter((b) => b !== "all");
|
||||||
temp[index] = newData;
|
temp[index] = newData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -472,16 +616,23 @@ export default function FormTeksDetail() {
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{/* Show the category from details if it doesn't exist in categories list */}
|
{/* Show the category from details if it doesn't exist in categories list */}
|
||||||
{detail && !categories.find(cat => String(cat.id) === String(detail.category.id)) && (
|
{detail &&
|
||||||
<SelectItem
|
!categories.find(
|
||||||
key={String(detail.category.id)}
|
(cat) =>
|
||||||
value={String(detail.category.id)}
|
String(cat.id) === String(detail.category.id)
|
||||||
>
|
) && (
|
||||||
{detail.category.name}
|
<SelectItem
|
||||||
</SelectItem>
|
key={String(detail.category.id)}
|
||||||
)}
|
value={String(detail.category.id)}
|
||||||
|
>
|
||||||
|
{detail.category.name}
|
||||||
|
</SelectItem>
|
||||||
|
)}
|
||||||
{categories.map((category) => (
|
{categories.map((category) => (
|
||||||
<SelectItem key={String(category.id)} value={String(category.id)}>
|
<SelectItem
|
||||||
|
key={String(category.id)}
|
||||||
|
value={String(category.id)}
|
||||||
|
>
|
||||||
{category.name}
|
{category.name}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
|
|
@ -696,7 +847,195 @@ export default function FormTeksDetail() {
|
||||||
<Icon icon="humbleicons:times" color="red" />
|
<Icon icon="humbleicons:times" color="red" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</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 flex-row gap-2">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|
@ -773,7 +1112,7 @@ export default function FormTeksDetail() {
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)} */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ import {
|
||||||
getDataApprovalByMediaUpload,
|
getDataApprovalByMediaUpload,
|
||||||
} from "@/service/curated-content/curated-content";
|
} from "@/service/curated-content/curated-content";
|
||||||
import { Badge } from "@/components/ui/badge";
|
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, SwiperSlide } from "swiper/react";
|
||||||
import "swiper/css";
|
import "swiper/css";
|
||||||
import "swiper/css/free-mode";
|
import "swiper/css/free-mode";
|
||||||
|
|
@ -51,6 +51,7 @@ import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { loading } from "@/config/swal";
|
import { loading } from "@/config/swal";
|
||||||
|
|
@ -64,6 +65,7 @@ import SuggestionModal from "@/components/modal/suggestions-modal";
|
||||||
import { formatDateToIndonesian } from "@/utils/globals";
|
import { formatDateToIndonesian } from "@/utils/globals";
|
||||||
import ApprovalHistoryModal from "@/components/modal/approval-history-modal";
|
import ApprovalHistoryModal from "@/components/modal/approval-history-modal";
|
||||||
import VideoPlayer from "@/utils/video-player";
|
import VideoPlayer from "@/utils/video-player";
|
||||||
|
import { getUserLevelForAssignments } from "@/service/task";
|
||||||
|
|
||||||
const imageSchema = z.object({
|
const imageSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -114,19 +116,27 @@ const ViewEditor = dynamic(
|
||||||
{ ssr: false }
|
{ ssr: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
interface Destination {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
subDestination?: SubDestination[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SubDestination {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default function FormVideoDetail() {
|
export default function FormVideoDetail() {
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const userId = getCookiesDecrypt("uie");
|
const userId = getCookiesDecrypt("uie");
|
||||||
const userLevelId = getCookiesDecrypt("ulie");
|
const userLevelId = getCookiesDecrypt("ulie");
|
||||||
const roleId = getCookiesDecrypt("urie");
|
const roleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
const [modalOpen, setModalOpen] = useState(false);
|
const [modalOpen, setModalOpen] = useState(false);
|
||||||
const { id } = useParams() as { id: string };
|
const { id } = useParams() as { id: string };
|
||||||
console.log(id);
|
|
||||||
const editor = useRef(null);
|
const editor = useRef(null);
|
||||||
type ImageSchema = z.infer<typeof imageSchema>;
|
type ImageSchema = z.infer<typeof imageSchema>;
|
||||||
|
|
||||||
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
||||||
const taskId = Cookies.get("taskId");
|
const taskId = Cookies.get("taskId");
|
||||||
const scheduleId = Cookies.get("scheduleId");
|
const scheduleId = Cookies.get("scheduleId");
|
||||||
|
|
@ -142,7 +152,6 @@ export default function FormVideoDetail() {
|
||||||
const [main, setMain] = useState<any>([]);
|
const [main, setMain] = useState<any>([]);
|
||||||
const [detailVideo, setDetailVideo] = useState<any>([]);
|
const [detailVideo, setDetailVideo] = useState<any>([]);
|
||||||
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
|
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
|
||||||
|
|
||||||
const t = useTranslations("Form");
|
const t = useTranslations("Form");
|
||||||
const [filePlacements, setFilePlacements] = useState<string[][]>([]);
|
const [filePlacements, setFilePlacements] = useState<string[][]>([]);
|
||||||
const [selectedTarget, setSelectedTarget] = useState("");
|
const [selectedTarget, setSelectedTarget] = useState("");
|
||||||
|
|
@ -150,9 +159,138 @@ export default function FormVideoDetail() {
|
||||||
const [rejectedFiles, setRejectedFiles] = useState<number[]>([]);
|
const [rejectedFiles, setRejectedFiles] = useState<number[]>([]);
|
||||||
const [isUserMabesApprover, setIsUserMabesApprover] = useState(false);
|
const [isUserMabesApprover, setIsUserMabesApprover] = useState(false);
|
||||||
const [approval, setApproval] = useState<any>();
|
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";
|
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 {
|
const {
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
|
|
@ -238,6 +376,13 @@ export default function FormVideoDetail() {
|
||||||
format: details?.files[0]?.format,
|
format: details?.files[0]?.format,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (details?.assignedToLevel) {
|
||||||
|
const levels = new Set(
|
||||||
|
details.assignedToLevel.split(",").map(Number)
|
||||||
|
);
|
||||||
|
setCheckedLevels(levels);
|
||||||
|
}
|
||||||
|
|
||||||
if (details?.publishedForObject) {
|
if (details?.publishedForObject) {
|
||||||
const publisherIds = details.publishedForObject.map(
|
const publisherIds = details.publishedForObject.map(
|
||||||
(obj: any) => obj.id
|
(obj: any) => obj.id
|
||||||
|
|
@ -303,6 +448,7 @@ export default function FormVideoDetail() {
|
||||||
statusId: status,
|
statusId: status,
|
||||||
message: description,
|
message: description,
|
||||||
files: isUserMabesApprover ? getPlacement() : [],
|
files: isUserMabesApprover ? getPlacement() : [],
|
||||||
|
customLocationPlacement: Array.from(checkedLevels).join(","),
|
||||||
};
|
};
|
||||||
setModalOpen(false);
|
setModalOpen(false);
|
||||||
loading();
|
loading();
|
||||||
|
|
@ -354,9 +500,9 @@ export default function FormVideoDetail() {
|
||||||
temp[index] = ["all", "mabes", "polda", "international"];
|
temp[index] = ["all", "mabes", "polda", "international"];
|
||||||
} else {
|
} else {
|
||||||
const now = temp[index];
|
const now = temp[index];
|
||||||
now.push(placement);
|
now?.push(placement);
|
||||||
if (now.length === 3 && !now.includes("all")) {
|
if (now?.length === 3 && !now.includes("all")) {
|
||||||
now.push("all");
|
now?.push("all");
|
||||||
}
|
}
|
||||||
temp[index] = now;
|
temp[index] = now;
|
||||||
}
|
}
|
||||||
|
|
@ -367,7 +513,7 @@ export default function FormVideoDetail() {
|
||||||
const now = temp[index].filter((a) => a !== placement);
|
const now = temp[index].filter((a) => a !== placement);
|
||||||
console.log("now", now);
|
console.log("now", now);
|
||||||
temp[index] = now;
|
temp[index] = now;
|
||||||
if (now.length === 3 && now.includes("all")) {
|
if (now?.length === 3 && now.includes("all")) {
|
||||||
const newData = now.filter((b) => b !== "all");
|
const newData = now.filter((b) => b !== "all");
|
||||||
temp[index] = newData;
|
temp[index] = newData;
|
||||||
}
|
}
|
||||||
|
|
@ -715,7 +861,195 @@ export default function FormVideoDetail() {
|
||||||
<Icon icon="humbleicons:times" color="red" />
|
<Icon icon="humbleicons:times" color="red" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</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 flex-row gap-2">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|
@ -792,7 +1126,7 @@ export default function FormVideoDetail() {
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)} */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
|
|
|
||||||
|
|
@ -318,7 +318,7 @@ export default function FormTaskEdit() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePoldaPolresChange = () => {
|
const handlePoldaPolresChange = () => {
|
||||||
return Array.from(checkedLevels).join(","); // Mengonversi Set ke string
|
return Array.from(checkedLevels).join(",");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUnitChange = (
|
const handleUnitChange = (
|
||||||
|
|
@ -471,9 +471,9 @@ export default function FormTaskEdit() {
|
||||||
await uploadResumableFile(
|
await uploadResumableFile(
|
||||||
index,
|
index,
|
||||||
String(id),
|
String(id),
|
||||||
item, // Use .file to access the actual File object
|
item,
|
||||||
"4",
|
"4",
|
||||||
"0" // Optional: Replace with actual duration if available
|
"0"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -808,11 +808,11 @@ export default function FormTaskEdit() {
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
|
<div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
|
||||||
{listDest.map((polda: any) => {
|
{listDest.map((polda: any) => {
|
||||||
const poldaChecked = unitSelection.polda; // kontrol polda luar
|
const poldaChecked = unitSelection.polda;
|
||||||
const polresChecked = unitSelection.polres; // kontrol polres luar
|
const polresChecked = unitSelection.polres;
|
||||||
|
|
||||||
const isPoldaDisabled = poldaChecked; // lock checkbox polda dialog jika polda luar dicentang
|
const isPoldaDisabled = poldaChecked;
|
||||||
const isPolresDisabled = polresChecked; // lock checkbox polres dialog jika polres luar dicentang
|
const isPolresDisabled = polresChecked;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={polda.id} className="border p-2">
|
<div key={polda.id} className="border p-2">
|
||||||
|
|
@ -820,7 +820,7 @@ export default function FormTaskEdit() {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={
|
checked={
|
||||||
poldaChecked || checkedLevels.has(polda.id)
|
poldaChecked || checkedLevels.has(polda.id)
|
||||||
} // auto-centang jika polda luar dicentang
|
}
|
||||||
disabled={isPoldaDisabled}
|
disabled={isPoldaDisabled}
|
||||||
onCheckedChange={() => {
|
onCheckedChange={() => {
|
||||||
if (isPoldaDisabled) return;
|
if (isPoldaDisabled) return;
|
||||||
|
|
@ -852,7 +852,7 @@ export default function FormTaskEdit() {
|
||||||
checked={
|
checked={
|
||||||
polresChecked ||
|
polresChecked ||
|
||||||
checkedLevels.has(polres.id)
|
checkedLevels.has(polres.id)
|
||||||
} // auto-centang jika polres luar dicentang
|
}
|
||||||
disabled={isPolresDisabled}
|
disabled={isPolresDisabled}
|
||||||
onCheckedChange={() => {
|
onCheckedChange={() => {
|
||||||
if (isPolresDisabled) return;
|
if (isPolresDisabled) return;
|
||||||
|
|
@ -876,7 +876,7 @@ export default function FormTaskEdit() {
|
||||||
<div className="mt-5 space-y-2">
|
<div className="mt-5 space-y-2">
|
||||||
<Label>{t("type-task", { defaultValue: "Type Task" })}</Label>
|
<Label>{t("type-task", { defaultValue: "Type Task" })}</Label>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
defaultValue={detail.assignmentMainType.id.toString()} // State yang dipetakan ke value RadioGroup
|
defaultValue={detail.assignmentMainType.id.toString()}
|
||||||
onValueChange={(value) => setMainType(value)}
|
onValueChange={(value) => setMainType(value)}
|
||||||
// value={String(mainType)}
|
// value={String(mainType)}
|
||||||
// onValueChange={(value) => setMainType(Number(value))}
|
// onValueChange={(value) => setMainType(Number(value))}
|
||||||
|
|
@ -893,7 +893,7 @@ export default function FormTaskEdit() {
|
||||||
{t("assigment-type", { defaultValue: "Assigment Type" })}{" "}
|
{t("assigment-type", { defaultValue: "Assigment Type" })}{" "}
|
||||||
</Label>
|
</Label>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
value={taskType} // ✅ controlled
|
value={taskType}
|
||||||
onValueChange={(value) => setTaskType(value)}
|
onValueChange={(value) => setTaskType(value)}
|
||||||
className="flex flex-wrap gap-3"
|
className="flex flex-wrap gap-3"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ export async function listDataImage(
|
||||||
isInt: boolean = false
|
isInt: boolean = false
|
||||||
) {
|
) {
|
||||||
return await httpGetInterceptor(
|
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