fixing conflict

This commit is contained in:
Sabda Yagra 2025-08-22 18:43:35 +07:00
commit 134a2532ee
19 changed files with 9011 additions and 1067 deletions

View File

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

View File

@ -108,8 +108,8 @@ const EventModal = ({
const router = useRouter();
const pathname = usePathname();
const [isLoading, setIsLoading] = useState(false);
const [checkedLevels, setCheckedLevels] = useState(new Set());
const [expandedPolda, setExpandedPolda] = useState([{}]);
const [checkedLevels, setCheckedLevels] = useState<Set<number>>(new Set());
const [expandedPolda, setExpandedPolda] = useState<Record<number, boolean>>({});
const [audioFile, setAudioFile] = useState<File | null>(null);
const [isRecording, setIsRecording] = useState(false);
const [timer, setTimer] = useState<number>(120);
@ -151,7 +151,13 @@ const EventModal = ({
satker: false,
international: false,
});
const levelNumber = getCookiesDecrypt("ulne");
// State untuk melacak apakah perubahan berasal dari checkbox Jenis Agenda
const [isUpdatingFromJenisAgenda, setIsUpdatingFromJenisAgenda] = useState(false);
// State untuk melacak jenis perubahan spesifik
const [jenisAgendaChangeType, setJenisAgendaChangeType] = useState<string>("");
const levelNumber = Number(getCookiesDecrypt("ulne")) || 0;
const userLevelId = getCookiesDecrypt("ulie");
const poldaState = Cookies.get("state");
const [agendaType, setAgendaType] = React.useState("");
@ -253,15 +259,242 @@ const EventModal = ({
fetchDetailData();
}, [event, setValue]);
// useEffect untuk sinkronisasi checkbox modal dengan Jenis Agenda
useEffect(() => {
if (listDest.length > 0 && isUpdatingFromJenisAgenda && jenisAgendaChangeType) {
syncModalWithJenisAgenda();
}
}, [isUpdatingFromJenisAgenda, jenisAgendaChangeType]);
// useEffect untuk update wilayahPublish ketika pilihan modal berubah
useEffect(() => {
if (!isUpdatingFromJenisAgenda && listDest.length > 0) {
updateWilayahPublishFromModal();
}
}, [checkedLevels, isUpdatingFromJenisAgenda]);
// Fungsi untuk update wilayahPublish berdasarkan checkbox modal
const updateWilayahPublishFromModal = () => {
// Hanya update jika tidak sedang dalam proses update dari Jenis Agenda
if (!isUpdatingFromJenisAgenda && listDest.length > 0) {
// Hitung item yang dipilih berdasarkan checkedLevels
const checkedPoldaCount = listDest.filter((item: any) =>
item.levelNumber === 2 &&
item.name !== "SATKER POLRI" &&
checkedLevels.has(Number(item.id))
).length;
const checkedPolresCount = listDest.reduce((total: number, item: any) => {
if (item.subDestination) {
return total + item.subDestination.filter((sub: any) => checkedLevels.has(Number(sub.id))).length;
}
return total;
}, 0);
const satkerItem: any = listDest.find((item: any) => item.name === "SATKER POLRI");
const checkedSatkerCount = satkerItem ? (
(checkedLevels.has(Number(satkerItem.id)) ? 1 : 0) +
(satkerItem.subDestination?.filter((sub: any) => checkedLevels.has(Number(sub.id))).length || 0)
) : 0;
// Checkbox aktif jika ADA item yang dipilih dalam kategori tersebut
const hasSelectedPolda = checkedPoldaCount > 0;
const hasSelectedPolres = checkedPolresCount > 0;
const hasSelectedSatker = checkedSatkerCount > 0;
// Update arrays untuk backend
const newSelectedPolda = listDest
.filter((item: any) =>
item.levelNumber === 2 &&
item.name !== "SATKER POLRI" &&
checkedLevels.has(Number(item.id))
)
.map((item: any) => String(item.id));
const newSelectedPolres: string[] = [];
listDest.forEach((item: any) => {
if (item.subDestination) {
item.subDestination.forEach((sub: any) => {
if (checkedLevels.has(Number(sub.id))) {
newSelectedPolres.push(String(sub.id));
}
});
}
});
const newSelectedSatker: string[] = [];
if (satkerItem) {
if (checkedLevels.has(Number(satkerItem.id))) {
newSelectedSatker.push(String(satkerItem.id));
}
if (satkerItem.subDestination) {
satkerItem.subDestination.forEach((sub: any) => {
if (checkedLevels.has(Number(sub.id))) {
newSelectedSatker.push(String(sub.id));
}
});
}
}
// Update state arrays
setSelectedPolda(newSelectedPolda);
setSelectedPolres(newSelectedPolres);
setSelectedSatker(newSelectedSatker);
// Update wilayahPublish berdasarkan yang dipilih di modal
setWilayahPublish(prev => {
const newState = { ...prev };
// Update individual checkboxes
newState.polda = hasSelectedPolda;
newState.polres = hasSelectedPolres;
newState.satker = hasSelectedSatker;
// Update checkbox "semua" berdasarkan level user
if (levelNumber === 1) {
// Level 1: semua checkbox harus aktif (nasional, polda, polres, satker, international)
newState.semua = newState.nasional && hasSelectedPolda && hasSelectedPolres && hasSelectedSatker && newState.international;
} else if (levelNumber === 2) {
// Level 2: hanya polres yang perlu aktif
newState.semua = hasSelectedPolres;
} else {
newState.semua = false;
}
return newState;
});
// Update agendaType berdasarkan checkbox yang aktif
const selectedKeys = [];
if (hasSelectedPolda) selectedKeys.push(wilayahValueMap.polda);
if (hasSelectedPolres) selectedKeys.push(wilayahValueMap.polres);
if (hasSelectedSatker) selectedKeys.push(wilayahValueMap.satker);
setAgendaType(selectedKeys.join(","));
}
};
// Fungsi untuk sinkronisasi checkbox modal dengan Jenis Agenda
const syncModalWithJenisAgenda = () => {
// Hanya jalankan sinkronisasi jika perubahan berasal dari checkbox Jenis Agenda
if (isUpdatingFromJenisAgenda) {
const newCheckedLevels = new Set(checkedLevels);
// Handle checklist actions - menambahkan semua item ke modal
if (jenisAgendaChangeType === "polda_checked") {
// Checklist semua polda
listDest.forEach((item: any) => {
if (item.levelNumber === 2 && item.name !== "SATKER POLRI") {
newCheckedLevels.add(Number(item.id));
}
});
} else if (jenisAgendaChangeType === "polres_checked") {
// Checklist semua polres, tapi hanya yang poldanya sudah di-checklist
listDest.forEach((item: any) => {
if (item.levelNumber === 2 && item.name !== "SATKER POLRI" && newCheckedLevels.has(Number(item.id))) {
if (item.subDestination) {
item.subDestination.forEach((polres: any) => {
newCheckedLevels.add(Number(polres.id));
});
}
}
});
} else if (jenisAgendaChangeType === "satker_checked") {
// Checklist satker
const satkerItem: any = listDest.find((item: any) => item.name === "SATKER POLRI");
if (satkerItem) {
newCheckedLevels.add(Number(satkerItem.id));
if (satkerItem.subDestination) {
satkerItem.subDestination.forEach((sub: any) => {
newCheckedLevels.add(Number(sub.id));
});
}
}
}
// Handle unchecklist actions - menghapus item dari modal
else if (jenisAgendaChangeType === "polres_unchecked") {
// Clear polres dari checkedLevels
listDest.forEach((item: any) => {
if (item.subDestination) {
item.subDestination.forEach((polres: any) => {
newCheckedLevels.delete(Number(polres.id));
});
}
});
} else if (jenisAgendaChangeType === "polda_unchecked") {
// Clear polda dan polres dari checkedLevels
listDest.forEach((item: any) => {
if (item.levelNumber === 2 && item.name !== "SATKER POLRI") {
newCheckedLevels.delete(Number(item.id));
// Juga clear polres dari polda ini
if (item.subDestination) {
item.subDestination.forEach((polres: any) => {
newCheckedLevels.delete(Number(polres.id));
});
}
}
});
setWilayahPublish(prev => ({ ...prev, polres: false }));
} else if (jenisAgendaChangeType === "satker_unchecked") {
// Clear satker dari checkedLevels
const satkerItem: any = listDest.find((item: any) => item.name === "SATKER POLRI");
if (satkerItem) {
newCheckedLevels.delete(Number(satkerItem.id));
if (satkerItem.subDestination) {
satkerItem.subDestination.forEach((sub: any) => {
newCheckedLevels.delete(Number(sub.id));
});
}
}
}
setCheckedLevels(newCheckedLevels);
// Reset flag setelah sinkronisasi selesai
setIsUpdatingFromJenisAgenda(false);
setJenisAgendaChangeType("");
}
};
useEffect(() => {
setIsDatePickerOpen(false);
}, [onClose]);
useEffect(() => {
async function fetchPoldaPolres() {
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);
} catch (error) {
console.error("Error fetching Polda/Polres data:", error);
}
}
fetchPoldaPolres();
}, []);
const handleCheckboxChange = (levelId: number) => {
setCheckedLevels((prev) => {
const updatedLevels = new Set(prev);
if (updatedLevels.has(levelId)) {
const isCurrentlyChecked = updatedLevels.has(levelId);
if (isCurrentlyChecked) {
updatedLevels.delete(levelId);
// Jika ini adalah POLDA yang di-unchecklist, unchecklist juga semua polres di bawahnya
const poldaItem = listDest.find((item: any) => Number(item.id) === levelId) as any;
if (poldaItem && poldaItem.subDestination) {
poldaItem.subDestination.forEach((polres: any) => {
updatedLevels.delete(Number(polres.id));
});
}
} else {
updatedLevels.add(levelId);
}
@ -279,6 +512,10 @@ const EventModal = ({
};
const toggleWilayah = (key: string) => {
// Set flag bahwa perubahan berasal dari checkbox Jenis Agenda
setIsUpdatingFromJenisAgenda(true);
setJenisAgendaChangeType(key + (wilayahPublish[key as keyof typeof wilayahPublish] ? "_unchecked" : "_checked"));
setWilayahPublish((prev: any) => {
let newState = { ...prev };
if (key === "semua") {
@ -294,15 +531,96 @@ const EventModal = ({
if (newChecked) {
setAgendaType("0,1,2,3,4,5");
// Checklist semua item di modal ketika "semua" di-checklist
const allCheckedLevels = new Set<number>();
listDest.forEach((item: any) => {
allCheckedLevels.add(Number(item.id));
if (item.subDestination) {
item.subDestination.forEach((sub: any) => {
allCheckedLevels.add(Number(sub.id));
});
}
});
setCheckedLevels(allCheckedLevels);
} else {
setAgendaType("");
// Clear semua pilihan modal ketika "semua" di-unchecklist
setCheckedLevels(new Set());
}
return newState;
}
// Validasi khusus untuk POLRES
if (key === "polres" && !prev[key]) {
// Cek apakah ada POLDA yang sudah dipilih di modal
const hasSelectedPolda = listDest.some((item: any) =>
item.levelNumber === 2 &&
item.name !== "SATKER POLRI" &&
checkedLevels.has(Number(item.id))
);
if (!hasSelectedPolda) {
// Jika tidak ada POLDA yang dipilih, tampilkan peringatan dan batalkan
alert("Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES.");
// Reset flag karena perubahan dibatalkan
setIsUpdatingFromJenisAgenda(false);
setJenisAgendaChangeType("");
return prev; // Batalkan perubahan
}
}
newState[key] = !prev[key];
newState.semua = false;
// Jika checkbox di-unchecklist, clear pilihan modal yang sesuai
if (prev[key]) {
const newCheckedLevels = new Set(checkedLevels);
if (key === "polda") {
// Clear polda dan polres
listDest.forEach((item: any) => {
if (item.levelNumber === 2 && item.name !== "SATKER POLRI") {
newCheckedLevels.delete(Number(item.id));
if (item.subDestination) {
item.subDestination.forEach((polres: any) => {
newCheckedLevels.delete(Number(polres.id));
});
}
}
});
} else if (key === "polres") {
// Clear polres
listDest.forEach((item: any) => {
if (item.subDestination) {
item.subDestination.forEach((polres: any) => {
newCheckedLevels.delete(Number(polres.id));
});
}
});
} else if (key === "satker") {
// Clear satker
const satkerItem: any = listDest.find((item: any) => item.name === "SATKER POLRI");
if (satkerItem) {
newCheckedLevels.delete(Number(satkerItem.id));
if (satkerItem.subDestination) {
satkerItem.subDestination.forEach((sub: any) => {
newCheckedLevels.delete(Number(sub.id));
});
}
}
}
setCheckedLevels(newCheckedLevels);
}
// Update checkbox "semua" berdasarkan status semua checkbox lainnya
// Untuk level 1: semua, nasional, polda, polres, satker, international harus aktif
// Untuk level 2: semua, polres harus aktif
if (levelNumber === 1) {
newState.semua = newState.nasional && newState.polda && newState.polres && newState.satker && newState.international;
} else if (levelNumber === 2) {
newState.semua = newState.polres;
} else {
newState.semua = false;
}
const selectedKeys = Object.entries(newState)
.filter(([k, v]) => v && k !== "semua")
@ -449,7 +767,7 @@ const EventModal = ({
const onDeleteEventAction = async () => {
try {
} catch (error) {}
} catch (error) { }
};
const handleOpenDeleteModal = (eventId: string) => {
@ -629,7 +947,7 @@ const EventModal = ({
);
};
const handleRemoveFile = (id: number) => {};
const handleRemoveFile = (id: number) => { };
async function doDelete(id: any) {
loading();
@ -797,7 +1115,8 @@ const EventModal = ({
</label>
</div>
{roleId === 1 && (
{levelNumber === 1 && (
<>
<div>
<Checkbox
id="nasional"
@ -808,8 +1127,6 @@ const EventModal = ({
Nasional
</label>
</div>
)}
<div>
<Checkbox
id="polda"
@ -819,18 +1136,11 @@ const EventModal = ({
<label htmlFor="polda" className="mx-2 text-sm mr-2">
Polda
</label>
{wilayahPublish.polda && (
<UnitMapping
unit="Polda"
isDetail={isDetailMode}
initData={selectedPolda}
sendDataToParent={(data: any) =>
setSelectedPolda(data)
}
/>
)}
</div>
{(roleId === 1 || roleId === 4 || roleId === 3) && (
</>
)}
{(levelNumber === 1 || levelNumber === 2) && (
<div>
<Checkbox
id="polres"
@ -840,19 +1150,11 @@ const EventModal = ({
<label htmlFor="polres" className="ml-2 text-sm mr-2">
Polres
</label>
{wilayahPublish.polres && (
<UnitMapping
unit="Polres"
isDetail={isDetailMode}
initData={selectedPolres}
sendDataToParent={(data: any) =>
setSelectedPolres(data)
}
/>
)}
</div>
)}
{(roleId === 1 || roleId === 2) && (
{levelNumber === 1 && (
<>
<div>
<Checkbox
id="satker"
@ -862,19 +1164,7 @@ const EventModal = ({
<label htmlFor="satker" className="mx-2 text-sm mr-2">
Satker
</label>
{wilayahPublish.satker && (
<UnitMapping
unit="Satker"
isDetail={isDetailMode}
initData={selectedSatker}
sendDataToParent={(data: any) =>
setSelectedSatker(data)
}
/>
)}
</div>
)}
{roleId === 1 && (
<div>
<Checkbox
id="international"
@ -888,7 +1178,97 @@ const EventModal = ({
Internasional
</label>
</div>
</>
)}
<div className="pl-1">
<Dialog>
<DialogTrigger asChild>
<Button variant="soft" size="sm" color="primary">
[Kustom]
</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) => (
<div key={polda.id} className="border p-2">
<Label className="flex items-center">
<Checkbox
checked={checkedLevels.has(Number(polda.id))}
onCheckedChange={() =>
handleCheckboxChange(Number(polda.id))
}
className="mr-3"
/>
{polda.name}
<button
type="button"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
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">
<Label className="block">
<Checkbox
checked={polda?.subDestination?.every(
(polres: any) =>
checkedLevels.has(Number(polres.id))
)}
onCheckedChange={(isChecked) => {
const updatedLevels = new Set(
checkedLevels
);
polda?.subDestination?.forEach(
(polres: any) => {
if (isChecked) {
updatedLevels.add(Number(polres.id));
} else {
updatedLevels.delete(Number(polres.id));
}
}
);
setCheckedLevels(updatedLevels);
}}
className="mr-2"
/>
Pilih Semua
</Label>
{polda?.subDestination?.map((polres: any) => (
<Label key={polres.id} className="block mt-1">
<Checkbox
checked={checkedLevels.has(Number(polres.id))}
onCheckedChange={() =>
handleCheckboxChange(Number(polres.id))
}
className="mr-2"
/>
{polres.name}
</Label>
))}
</div>
)}
</div>
))}
</div>
</DialogContent>
</Dialog>
</div>
</div>
</div>
@ -1099,8 +1479,7 @@ const EventModal = ({
type="button"
onClick={onPlayPause}
disabled={isPlaying}
className={`flex items-center gap-2 ${
isPlaying
className={`flex items-center gap-2 ${isPlaying
? "bg-gray-300 cursor-not-allowed"
: "bg-primary text-white"
} p-2 rounded`}

View File

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

View File

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

View File

@ -2,7 +2,7 @@ import * as React from "react";
import { ColumnDef } from "@tanstack/react-table";
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
import { cn } from "@/lib/utils";
import { cn, getCookiesDecrypt } from "@/lib/utils";
import {
DropdownMenu,
DropdownMenuContent,
@ -17,9 +17,10 @@ import withReactContent from "sweetalert2-react-content";
import Swal from "sweetalert2";
import { error } from "@/lib/swal";
import { deleteCalendar } from "@/service/schedule/schedule";
import { loading, success } from "@/config/swal";
const useTableColumns = () => {
const t = useTranslations("Table"); // Panggil di dalam hook
const t = useTranslations("Table");
const columns: ColumnDef<any>[] = [
{
@ -100,15 +101,13 @@ const useTableColumns = () => {
enableHiding: false,
cell: ({ row }) => {
const MySwal = withReactContent(Swal);
const levelNumber = Number(getCookiesDecrypt("ulne")); // 1 = Mabes, 2 = Polda
const calendarOwnerLevel = Number(row.original.assignedToLevel); // dari API
const calendarOwner = row.original.assignedTo; // ID unit Polda/Mabes dari API
const myUnit = getCookiesDecrypt("unitId"); // misal ID unit Polda login
async function doDelete(id: any) {
// loading();
const data = {
id,
};
const response = await deleteCalendar(id);
if (response?.error) {
error(response.message);
return false;
@ -144,6 +143,29 @@ const useTableColumns = () => {
}
});
};
// === RULE AKSI ===
let canEdit = false;
let canDelete = false;
const canView = true;
if (levelNumber === 1) {
// Mabes -> bebas
canEdit = true;
canDelete = true;
} else if (levelNumber === 2) {
// Polda
if (calendarOwnerLevel === 1) {
// kalender Mabes -> hanya view
canEdit = false;
canDelete = false;
} else if (calendarOwnerLevel === 2 && calendarOwner === myUnit) {
// kalender polda sendiri -> bisa edit/delete
canEdit = true;
canDelete = true;
}
}
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
@ -156,34 +178,134 @@ const useTableColumns = () => {
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<Link
href={`/contributor/schedule/calendar-polri/detail/${row.original.id}`}
>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Eye className="w-4 h-4 me-1.5" />
Detail
{canView && (
<Link
href={`/contributor/schedule/calendar-polri/detail/${row.original.id}`}
>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<Eye className="w-4 h-4 me-1.5" />
Detail
</DropdownMenuItem>
</Link>
)}
{canEdit && (
<Link
href={`/contributor/schedule/calendar-polri/update/${row.original.id}`}
>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<SquarePen className="w-4 h-4 me-1.5" />
Edit
</DropdownMenuItem>
</Link>
)}
{canDelete && (
<DropdownMenuItem
onClick={() => handleDeleteCalendars(row.original.id)}
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
>
<Trash2 className="w-4 h-4 me-1.5" />
Delete
</DropdownMenuItem>
</Link>
<Link
href={`/contributor/schedule/calendar-polri/update/${row.original.id}`}
>
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
<SquarePen className="w-4 h-4 me-1.5" />
Edit
</DropdownMenuItem>
</Link>
<DropdownMenuItem
onClick={() => handleDeleteCalendars(row.original.id)}
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
>
<Trash2 className="w-4 h-4 me-1.5" />
Delete
</DropdownMenuItem>
)}
</DropdownMenuContent>
</DropdownMenu>
);
},
},
// {
// id: "actions",
// accessorKey: "action",
// header: t("action", { defaultValue: "Action" }),
// enableHiding: false,
// cell: ({ row }) => {
// const MySwal = withReactContent(Swal);
// async function doDelete(id: any) {
// // loading();
// const data = {
// id,
// };
// const response = await deleteCalendar(id);
// if (response?.error) {
// error(response.message);
// return false;
// }
// success();
// }
// function success() {
// MySwal.fire({
// title: "Sukses",
// icon: "success",
// confirmButtonColor: "#3085d6",
// confirmButtonText: "OK",
// }).then((result) => {
// if (result.isConfirmed) {
// window.location.reload();
// }
// });
// }
// const handleDeleteCalendars = (id: any) => {
// MySwal.fire({
// title: "Hapus Data",
// text: "",
// icon: "warning",
// showCancelButton: true,
// cancelButtonColor: "#3085d6",
// confirmButtonColor: "#d33",
// confirmButtonText: "Hapus",
// }).then((result) => {
// if (result.isConfirmed) {
// doDelete(id);
// }
// });
// };
// return (
// <DropdownMenu>
// <DropdownMenuTrigger asChild>
// <Button
// size="icon"
// className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
// >
// <span className="sr-only">Open menu</span>
// <MoreVertical className="h-4 w-4 text-default-800" />
// </Button>
// </DropdownMenuTrigger>
// <DropdownMenuContent className="p-0" align="end">
// <Link
// href={`/contributor/schedule/calendar-polri/detail/${row.original.id}`}
// >
// <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
// <Eye className="w-4 h-4 me-1.5" />
// Detail
// </DropdownMenuItem>
// </Link>
// <Link
// href={`/contributor/schedule/calendar-polri/update/${row.original.id}`}
// >
// <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
// <SquarePen className="w-4 h-4 me-1.5" />
// Edit
// </DropdownMenuItem>
// </Link>
// <DropdownMenuItem
// onClick={() => handleDeleteCalendars(row.original.id)}
// className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
// >
// <Trash2 className="w-4 h-4 me-1.5" />
// Delete
// </DropdownMenuItem>
// </DropdownMenuContent>
// </DropdownMenu>
// );
// },
// },
];
return columns;

View File

@ -54,8 +54,15 @@ interface Option {
userLevelId: string;
}
// const taskSchema = z.object({
// title: z.string().optional(),
// description: z.string().min(2, {
// message: "Narasi Penugasan harus lebih dari 2 karakter.",
// }),
// });
const taskSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }),
title: z.string().optional(),
description: z.string().min(2, {
message: "Narasi Penugasan harus lebih dari 2 karakter.",
}),
@ -89,13 +96,11 @@ type OptionType = {
export default function FormQuestionsForward() {
const MySwal = withReactContent(Swal);
const { id } = useParams() as { id: string };
const [detail, setDetail] = useState<any>();
const [ticketReply, setTicketReply] = useState<replyDetail[]>([]);
const [replyVisible, setReplyVisible] = useState(false);
const [listDiscussion, setListDiscussion] = useState();
const [message, setMessage] = useState("");
const [detailTickets, setDetailTickets] = useState<DetailTicket | null>(null);
// const [selectedPriority, setSelectedPriority] = useState("");
const [selectedPriority, setSelectedPriority] = useState<OptionType | null>(
@ -202,6 +207,8 @@ export default function FormQuestionsForward() {
title: "Sukses",
text: "Data berhasil diperbarui.",
icon: "success",
}).then(() => {
window.location.href = "/in/supervisor/communications/questions/all";
});
getTicketReply();
@ -262,7 +269,7 @@ export default function FormQuestionsForward() {
const newReply = {
id: replies.length + 1,
name: "Mabes Polri - Approver", // Sesuaikan dengan data dinamis jika ada
name: "Mabes Polri - Approver",
message: replyMessage,
timestamp: new Date().toISOString().slice(0, 19).replace("T", " "),
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -170,7 +170,6 @@ export default function FormConvertSPIT() {
const { id } = useParams() as { id: string };
const [isAlreadySaved, setIsAlreadySaved] = useState(false);
// Form state
const {
control,
handleSubmit,
@ -586,10 +585,10 @@ export default function FormConvertSPIT() {
return false;
}
if (!checkPlacement(filePlacements)) {
error("Select File Placement");
return false;
}
// if (!checkPlacement(filePlacements)) {
// error("Select File Placement");
// return false;
// }
if (!selectedCategoryId) {
error("Select a category");

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -118,9 +118,9 @@ export default function FormContestDetail() {
const [detail, setDetail] = useState<any>();
const [refresh] = useState(false);
const [date, setDate] = useState<DateRange | undefined>();
const [listDest, setListDest] = useState([]);
const [checkedLevels, setCheckedLevels] = useState(new Set());
const [expandedPolda, setExpandedPolda] = useState([{}]);
const [listDest, setListDest] = useState<any[]>([]);
const [checkedLevels, setCheckedLevels] = useState<Set<number>>(new Set());
const [expandedPolda, setExpandedPolda] = useState<Record<number, boolean>>({});
const [isLoading, setIsLoading] = useState(false);
const [audioFile, setAudioFile] = useState<File | null>(null);
const [imageFiles, setImageFiles] = useState<FileWithPreview[]>([]);
@ -153,6 +153,11 @@ export default function FormContestDetail() {
satker: false,
});
// State untuk melacak apakah perubahan berasal dari checkbox Pelaksana Tugas
const [isUpdatingFromPelaksana, setIsUpdatingFromPelaksana] = useState(false);
// State untuk melacak jenis perubahan spesifik
const [pelaksanaChangeType, setPelaksanaChangeType] = useState<string>("");
const {
control,
handleSubmit,
@ -192,6 +197,16 @@ export default function FormContestDetail() {
fetchPoldaPolres();
}, []);
// useEffect untuk sinkronisasi checkbox modal dengan Pelaksana Tugas
// Ketika unitSelection berubah dari checkbox Pelaksana Tugas:
// - Jika di-checklist: checklist semua item sesuai kategori di modal
// - Jika di-unchecklist: unchecklist semua item di modal
useEffect(() => {
if (listDest.length > 0) {
syncModalWithUnitSelection();
}
}, [unitSelection, listDest]);
useEffect(() => {
async function initState() {
if (id) {
@ -237,16 +252,160 @@ export default function FormContestDetail() {
}
}, [detail?.targetOutput]);
// Fungsi untuk update unitSelection berdasarkan checkbox modal
// Checkbox di Pelaksana Tugas hanya akan aktif jika SEMUA item dalam kategori tersebut dichecklist
const updateUnitSelectionFromModal = (levelId: number) => {
setTimeout(() => {
// Hitung total item yang tersedia untuk setiap kategori
const totalPolda = listDest.filter((item: any) =>
item.levelNumber === 2 && item.name !== "SATKER POLRI"
).length;
const totalPolres = listDest.reduce((total: number, item: any) => {
if (item.subDestination) {
return total + item.subDestination.length;
}
return total;
}, 0);
const satkerItem = listDest.find((item: any) => item.name === "SATKER POLRI");
const totalSatker = satkerItem ? (1 + (satkerItem.subDestination?.length || 0)) : 0;
// Hitung item yang dichecklist untuk setiap kategori
const checkedPoldaCount = listDest.filter((item: any) =>
item.levelNumber === 2 &&
item.name !== "SATKER POLRI" &&
checkedLevels.has(item.id)
).length;
const checkedPolresCount = listDest.reduce((total: number, item: any) => {
if (item.subDestination) {
return total + item.subDestination.filter((sub: any) => checkedLevels.has(sub.id)).length;
}
return total;
}, 0);
const checkedSatkerCount = satkerItem ? (
(checkedLevels.has(satkerItem.id) ? 1 : 0) +
(satkerItem.subDestination?.filter((sub: any) => checkedLevels.has(sub.id)).length || 0)
) : 0;
// Checkbox hanya aktif jika SEMUA item dalam kategori tersebut dichecklist
const hasCheckedPolda = totalPolda > 0 && checkedPoldaCount === totalPolda;
const hasCheckedPolres = totalPolres > 0 && checkedPolresCount === totalPolres;
const hasCheckedSatker = totalSatker > 0 && checkedSatkerCount === totalSatker;
// Update unitSelection berdasarkan checkbox yang aktif di modal
setUnitSelection(prev => ({
...prev,
polda: hasCheckedPolda,
polres: hasCheckedPolres,
satker: hasCheckedSatker,
// allUnit hanya true jika semua kategori terpenuhi
allUnit: hasCheckedPolda && hasCheckedPolres && hasCheckedSatker
}));
}, 0);
};
const handleCheckboxChange = (levelId: number) => {
setCheckedLevels((prev) => {
const updatedLevels = new Set(prev);
if (updatedLevels.has(levelId)) {
const isCurrentlyChecked = updatedLevels.has(levelId);
if (isCurrentlyChecked) {
updatedLevels.delete(levelId);
// Jika ini adalah POLDA yang di-unchecklist, unchecklist juga semua polres di bawahnya
const poldaItem = listDest.find((item: any) => item.id === levelId);
if (poldaItem && poldaItem.subDestination) {
poldaItem.subDestination.forEach((polres: any) => {
updatedLevels.delete(polres.id);
});
}
} else {
updatedLevels.add(levelId);
}
return updatedLevels;
});
// Update unitSelection berdasarkan perubahan di modal
updateUnitSelectionFromModal(levelId);
};
// Fungsi untuk sinkronisasi checkbox modal dengan Pelaksana Tugas
const syncModalWithUnitSelection = () => {
// Hanya jalankan sinkronisasi jika perubahan berasal dari checkbox Pelaksana Tugas
if (isUpdatingFromPelaksana) {
// Khusus untuk unchecklist POLRES: hanya unchecklist polres, pertahankan polda
if (pelaksanaChangeType === "polres_unchecked") {
const newCheckedLevels = new Set<number>(checkedLevels);
// Hapus semua polres dari modal, tapi pertahankan polda
listDest.forEach((item: any) => {
if (item.subDestination && item.levelNumber === 2 && item.name !== "SATKER POLRI") {
item.subDestination.forEach((polres: any) => {
newCheckedLevels.delete(polres.id);
});
}
});
setCheckedLevels(newCheckedLevels);
}
// Untuk perubahan lainnya, jalankan logika normal
else if (unitSelection.polda || unitSelection.polres || unitSelection.satker) {
// Mulai dengan checkbox yang sudah ada untuk mempertahankan pilihan manual user
const newCheckedLevels = new Set<number>(checkedLevels);
listDest.forEach((item: any) => {
// Jika polda dichecklist, checklist semua polda (levelNumber 2, bukan SATKER POLRI)
if (unitSelection.polda && item.levelNumber === 2 && item.name !== "SATKER POLRI") {
newCheckedLevels.add(item.id);
}
// Jika satker dichecklist, checklist SATKER POLRI dan sub-itemnya
if (unitSelection.satker && item.name === "SATKER POLRI") {
newCheckedLevels.add(item.id);
if (item.subDestination) {
item.subDestination.forEach((sub: any) => {
newCheckedLevels.add(sub.id);
});
}
}
// Jika polres dichecklist
if (unitSelection.polres && item.subDestination) {
// Jika checkbox POLDA di Pelaksana Tugas juga aktif, checklist semua polres
if (unitSelection.polda && item.levelNumber === 2 && item.name !== "SATKER POLRI") {
item.subDestination.forEach((polres: any) => {
newCheckedLevels.add(polres.id);
});
}
// Jika checkbox POLDA di Pelaksana Tugas tidak aktif, tapi ada POLDA yang dichecklist di modal
else if (!unitSelection.polda && item.levelNumber === 2 && item.name !== "SATKER POLRI") {
// Cek apakah POLDA ini sudah dichecklist di modal
if (checkedLevels.has(item.id)) {
// Jika ya, checklist semua polres dari POLDA ini
item.subDestination.forEach((polres: any) => {
newCheckedLevels.add(polres.id);
});
}
}
}
});
setCheckedLevels(newCheckedLevels);
} else {
// Jika tidak ada unitSelection yang aktif, unchecklist semua item di modal
// Setelah itu user bisa checklist secara manual
setCheckedLevels(new Set<number>());
}
// Reset flag setelah sinkronisasi selesai
setTimeout(() => {
setIsUpdatingFromPelaksana(false);
setPelaksanaChangeType("");
}, 100);
}
};
const handlePoldaPolresChange = () => {
@ -691,7 +850,7 @@ export default function FormContestDetail() {
}}
/>
<Label htmlFor={key}>
{key.charAt(0).toUpperCase() + key.slice(1)}
{key === "allUnit" ? "Semua Unit" : key.charAt(0).toUpperCase() + key.slice(1)}
</Label>
</div>
))}
@ -706,6 +865,10 @@ export default function FormContestDetail() {
id={key}
checked={unitSelection[key as keyof typeof unitSelection]}
onCheckedChange={(value) => {
// Set flag bahwa perubahan berasal dari checkbox Pelaksana Tugas
setIsUpdatingFromPelaksana(true);
setPelaksanaChangeType(key + (value ? "_checked" : "_unchecked"));
if (key === "allUnit") {
const newValue = Boolean(value);
setUnitSelection({
@ -716,6 +879,22 @@ export default function FormContestDetail() {
satker: newValue,
});
} else {
// Validasi khusus untuk POLRES
if (key === "polres" && value) {
// Cek apakah ada POLDA yang sudah dichecklist di modal
const hasCheckedPolda = listDest.some((item: any) =>
item.levelNumber === 2 &&
item.name !== "SATKER POLRI" &&
checkedLevels.has(item.id)
);
if (!hasCheckedPolda) {
// Jika tidak ada POLDA yang dichecklist di modal, tampilkan peringatan dan batalkan
alert("Harap pilih POLDA di Modal List terlebih dahulu sebelum mengaktifkan checkbox POLRES.");
return; // Batalkan perubahan
}
}
setUnitSelection((prev) => {
const updated = { ...prev, [key]: Boolean(value) };
// Update 'allUnit' jika semua sub-checkbox true
@ -728,7 +907,7 @@ export default function FormContestDetail() {
}}
/>
<Label htmlFor={key}>
{key.charAt(0).toUpperCase() + key.slice(1)}
{key === "allUnit" ? "Semua Unit" : key.charAt(0).toUpperCase() + key.slice(1)}
</Label>
</div>
))}
@ -758,7 +937,12 @@ export default function FormContestDetail() {
/>
{polda.name}
<button
onClick={() => toggleExpand(polda.id)}
type="button"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
toggleExpand(polda.id);
}}
className="ml-2 focus:outline-none"
>
{expandedPolda[polda.id] ? (
@ -790,10 +974,60 @@ export default function FormContestDetail() {
}
);
setCheckedLevels(updatedLevels);
// Update unitSelection berdasarkan perubahan
setTimeout(() => {
// Hitung total item yang tersedia untuk setiap kategori
const totalPolda = listDest.filter((item: any) =>
item.levelNumber === 2 && item.name !== "SATKER POLRI"
).length;
const totalPolres = listDest.reduce((total: number, item: any) => {
if (item.subDestination) {
return total + item.subDestination.length;
}
return total;
}, 0);
const satkerItem = listDest.find((item: any) => item.name === "SATKER POLRI");
const totalSatker = satkerItem ? (1 + (satkerItem.subDestination?.length || 0)) : 0;
// Hitung item yang dichecklist untuk setiap kategori
const checkedPoldaCount = listDest.filter((item: any) =>
item.levelNumber === 2 &&
item.name !== "SATKER POLRI" &&
updatedLevels.has(item.id)
).length;
const checkedPolresCount = listDest.reduce((total: number, item: any) => {
if (item.subDestination) {
return total + item.subDestination.filter((sub: any) => updatedLevels.has(sub.id)).length;
}
return total;
}, 0);
const checkedSatkerCount = satkerItem ? (
(updatedLevels.has(satkerItem.id) ? 1 : 0) +
(satkerItem.subDestination?.filter((sub: any) => updatedLevels.has(sub.id)).length || 0)
) : 0;
// Checkbox hanya aktif jika SEMUA item dalam kategori tersebut dichecklist
const hasCheckedPolda = totalPolda > 0 && checkedPoldaCount === totalPolda;
const hasCheckedPolres = totalPolres > 0 && checkedPolresCount === totalPolres;
const hasCheckedSatker = totalSatker > 0 && checkedSatkerCount === totalSatker;
setUnitSelection(prev => ({
...prev,
polda: hasCheckedPolda,
polres: hasCheckedPolres,
satker: hasCheckedSatker,
allUnit: hasCheckedPolda && hasCheckedPolres && hasCheckedSatker
}));
}, 0);
}}
className="mr-2"
/>
Pilih Semua Polres
Pilih Semua
</Label>
{polda?.subDestination?.map((polres: any) => (
<Label key={polres.id} className="block mt-1">

View File

@ -41,7 +41,7 @@ import {
} from "@/components/ui/popover";
import { Calendar } from "@/components/ui/calendar";
import { format } from "date-fns";
import { cn } from "@/lib/utils";
import { cn, getCookiesDecrypt } from "@/lib/utils";
import { getUserLevelForAssignments } from "@/service/task";
import { Card } from "@/components/ui/card";
import { useDropzone } from "react-dropzone";
@ -84,6 +84,8 @@ export function CalendarPolriAdd() {
>([]);
const [imageFiles, setImageFiles] = React.useState<FileWithPreview[]>([]);
const [date, setDate] = React.useState<DateRange | undefined>();
const levelNumber = Number(getCookiesDecrypt("ulne")) || 0;
const roleId = Number(getCookiesDecrypt("urie")) || 0;
const [unitSelection, setUnitSelection] = React.useState({
semua: false,
@ -143,7 +145,7 @@ export function CalendarPolriAdd() {
};
const handlePoldaPolresChange = () => {
return Array.from(checkedLevels).join(","); // Mengonversi Set ke string
return Array.from(checkedLevels).join(",");
};
const handleUnitChange = (
@ -233,7 +235,6 @@ export function CalendarPolriAdd() {
const resCsrf = await getCsrfToken();
const csrfToken = resCsrf?.data?.token;
console.log("CSRF TOKEN : ", csrfToken);
const headers = {
"X-XSRF-TOKEN": csrfToken,
};
@ -387,7 +388,7 @@ export function CalendarPolriAdd() {
<div>
<p className="font-medium">Publish Area</p>
<div className="flex flex-row">
<div className="flex flex-wrap gap-3 lg:ml-3 ">
{/* <div className="flex flex-wrap gap-3 lg:ml-3 ">
{Object.keys(unitSelection).map((key) => (
<div className="flex items-center gap-2" key={key}>
<Checkbox
@ -407,8 +408,128 @@ export function CalendarPolriAdd() {
</Label>
</div>
))}
</div> */}
<div className="flex flex-wrap gap-3 lg:ml-3 ">
{Object.keys(unitSelection)
.filter((key) => {
// Jika login sebagai polda (2), hanya tampilkan polda
if (levelNumber === 2) return key === "polda";
// Jika login sebagai satker (4), hanya tampilkan satker
if (levelNumber === 4) return key === "satker";
// Selain itu tampilkan semua
return true;
})
.map((key) => (
<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
)
}
/>
<Label htmlFor={key}>
{key.charAt(0).toUpperCase() + key.slice(1)}
</Label>
</div>
))}
</div>
<div className=" lg:pl-3">
{levelNumber !== 2 && levelNumber !== 4 && (
<div className="lg:pl-3">
<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) => (
<div key={polda.id} className="border p-2">
<Label className="flex items-center">
<Checkbox
checked={checkedLevels.has(polda.id)}
onCheckedChange={() =>
handleCheckboxChange(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">
<Label className="block">
<Checkbox
checked={polda?.subDestination?.every(
(polres: any) =>
checkedLevels.has(polres.id)
)}
onCheckedChange={(isChecked) => {
const updatedLevels = new Set(
checkedLevels
);
polda?.subDestination?.forEach(
(polres: any) => {
if (isChecked) {
updatedLevels.add(polres.id);
} else {
updatedLevels.delete(polres.id);
}
}
);
setCheckedLevels(updatedLevels);
}}
className="mr-2"
/>
Pilih Semua Polres
</Label>
{polda?.subDestination?.map((polres: any) => (
<Label
key={polres.id}
className="block mt-1"
>
<Checkbox
checked={checkedLevels.has(polres.id)}
onCheckedChange={() =>
handleCheckboxChange(polres.id)
}
className="mr-2"
/>
{polres.name}
</Label>
))}
</div>
)}
</div>
))}
</div>
</DialogContent>
</Dialog>
</div>
)}
{/* <div className=" lg:pl-3">
<Dialog>
<DialogTrigger asChild>
<Button variant="soft" size="sm" color="primary">
@ -490,7 +611,7 @@ export function CalendarPolriAdd() {
</div>
</DialogContent>
</Dialog>
</div>
</div> */}
</div>
</div>

View File

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

View File

@ -122,7 +122,7 @@ export default function FormTask() {
const [detail, setDetail] = useState<taskDetail>();
const [refresh] = useState(false);
// const [listDest, setListDest] = useState([]);
const [checkedLevels, setCheckedLevels] = useState(new Set());
const [checkedLevels, setCheckedLevels] = useState<Set<number>>(new Set());
const [expandedPolda, setExpandedPolda] = useState([{}]);
const [isLoading, setIsLoading] = useState(false);
const [audioFile, setAudioFile] = useState<File | null>(null);
@ -146,6 +146,12 @@ export default function FormTask() {
polres: false,
satker: false,
});
// State untuk melacak apakah perubahan berasal dari checkbox Penerima Tugas
const [isUpdatingFromPenerimaTugas, setIsUpdatingFromPenerimaTugas] = useState(false);
// State untuk melacak jenis perubahan spesifik
const [penerimaTugasChangeType, setPenerimaTugasChangeType] = useState<string>("");
const [links, setLinks] = useState<string[]>([""]);
const {
register,
@ -194,13 +200,26 @@ export default function FormTask() {
const handleCheckboxChange = (levelId: number) => {
setCheckedLevels((prev) => {
const updatedLevels = new Set(prev);
if (updatedLevels.has(levelId)) {
const isCurrentlyChecked = updatedLevels.has(levelId);
if (isCurrentlyChecked) {
updatedLevels.delete(levelId);
// Jika ini adalah POLDA yang di-unchecklist, unchecklist juga semua polres di bawahnya
const poldaItem = listDest.find((item: any) => Number(item.id) === levelId);
if (poldaItem && poldaItem.subDestination) {
poldaItem.subDestination.forEach((polres: any) => {
updatedLevels.delete(Number(polres.id));
});
}
} else {
updatedLevels.add(levelId);
}
return updatedLevels;
});
// Update unitSelection berdasarkan perubahan di modal
updateUnitSelectionFromModal();
};
const handlePoldaPolresChange = () => {
@ -211,6 +230,10 @@ export default function FormTask() {
key: keyof typeof unitSelection,
value: boolean
) => {
// Set flag bahwa perubahan berasal dari checkbox Penerima Tugas
setIsUpdatingFromPenerimaTugas(true);
setPenerimaTugasChangeType(key + (value ? "_checked" : "_unchecked"));
if (key === "allUnit") {
const newState = {
allUnit: value,
@ -220,8 +243,23 @@ export default function FormTask() {
satker: value,
};
setUnitSelection(newState);
console.log("QQQ", newState);
} else {
// Validasi khusus untuk POLRES
if (key === "polres" && value) {
// Cek apakah ada POLDA yang sudah dichecklist di modal
const hasCheckedPolda = listDest.some((item: any) =>
item.levelNumber === 2 &&
item.name !== "SATKER POLRI" &&
checkedLevels.has(Number(item.id))
);
if (!hasCheckedPolda) {
// Jika tidak ada POLDA yang dichecklist di modal, tampilkan peringatan dan batalkan
alert("Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES.");
return; // Batalkan perubahan
}
}
const updatedSelection = {
...unitSelection,
[key]: value,
@ -234,7 +272,6 @@ export default function FormTask() {
updatedSelection.allUnit = allChecked;
setUnitSelection(updatedSelection);
console.log("AAA", updatedSelection);
}
};
@ -348,25 +385,141 @@ export default function FormTask() {
}
};
// useEffect untuk sinkronisasi checkbox modal dengan Penerima Tugas
useEffect(() => {
const updated = new Set(checkedLevels);
if (unitSelection.polda) {
listDest.forEach((polda) => {
updated.add(polda.id); // hanya id polda
});
if (listDest.length > 0) {
syncModalWithPenerimaTugas();
}
}, [unitSelection, listDest]);
if (unitSelection.polres) {
listDest.forEach((polda) => {
polda?.subDestination?.forEach((polres: any) => {
updated.add(polres.id); // hanya id polres
// Fungsi untuk update unitSelection berdasarkan checkbox modal
const updateUnitSelectionFromModal = () => {
setTimeout(() => {
// Hitung total item yang tersedia untuk setiap kategori
const totalPolda = listDest.filter((item: any) =>
item.levelNumber === 2 && item.name !== "SATKER POLRI"
).length;
const totalPolres = listDest.reduce((total: number, item: any) => {
if (item.subDestination) {
return total + item.subDestination.length;
}
return total;
}, 0);
const satkerItem = listDest.find((item: any) => item.name === "SATKER POLRI");
const totalSatker = satkerItem ? (1 + (satkerItem.subDestination?.length || 0)) : 0;
// Hitung item yang dichecklist untuk setiap kategori
const checkedPoldaCount = listDest.filter((item: any) =>
item.levelNumber === 2 &&
item.name !== "SATKER POLRI" &&
checkedLevels.has(Number(item.id))
).length;
const checkedPolresCount = listDest.reduce((total: number, item: any) => {
if (item.subDestination) {
return total + item.subDestination.filter((sub: any) => checkedLevels.has(Number(sub.id))).length;
}
return total;
}, 0);
const checkedSatkerCount = satkerItem ? (
(checkedLevels.has(Number(satkerItem.id)) ? 1 : 0) +
(satkerItem.subDestination?.filter((sub: any) => checkedLevels.has(Number(sub.id))).length || 0)
) : 0;
// Checkbox hanya aktif jika SEMUA item dalam kategori tersebut dichecklist
const hasCheckedPolda = totalPolda > 0 && checkedPoldaCount === totalPolda;
const hasCheckedPolres = totalPolres > 0 && checkedPolresCount === totalPolres;
const hasCheckedSatker = totalSatker > 0 && checkedSatkerCount === totalSatker;
// Update unitSelection berdasarkan checkbox yang aktif di modal
setUnitSelection(prev => ({
...prev,
polda: hasCheckedPolda,
polres: hasCheckedPolres,
satker: hasCheckedSatker,
// allUnit hanya true jika semua kategori terpenuhi
allUnit: hasCheckedPolda && hasCheckedPolres && hasCheckedSatker
}));
}, 0);
};
// Fungsi untuk sinkronisasi checkbox modal dengan Penerima Tugas
const syncModalWithPenerimaTugas = () => {
// Hanya jalankan sinkronisasi jika perubahan berasal dari checkbox Penerima Tugas
if (isUpdatingFromPenerimaTugas) {
// Khusus untuk unchecklist POLRES: hanya unchecklist polres, pertahankan polda
if (penerimaTugasChangeType === "polres_unchecked") {
const newCheckedLevels = new Set<number>(checkedLevels);
// Hapus semua polres dari modal, tapi pertahankan polda
listDest.forEach((item: any) => {
if (item.subDestination && item.levelNumber === 2 && item.name !== "SATKER POLRI") {
item.subDestination.forEach((polres: any) => {
newCheckedLevels.delete(Number(polres.id));
});
}
});
});
setCheckedLevels(newCheckedLevels);
}
// Untuk perubahan lainnya, jalankan logika normal
else if (unitSelection.polda || unitSelection.polres || unitSelection.satker) {
// Mulai dengan checkbox yang sudah ada untuk mempertahankan pilihan manual user
const newCheckedLevels = new Set<number>(checkedLevels);
listDest.forEach((item: any) => {
// Jika polda dichecklist, checklist semua polda (levelNumber 2, bukan SATKER POLRI)
if (unitSelection.polda && item.levelNumber === 2 && item.name !== "SATKER POLRI") {
newCheckedLevels.add(Number(item.id));
}
// Jika satker dichecklist, checklist SATKER POLRI dan sub-itemnya
if (unitSelection.satker && item.name === "SATKER POLRI") {
newCheckedLevels.add(Number(item.id));
if (item.subDestination) {
item.subDestination.forEach((sub: any) => {
newCheckedLevels.add(Number(sub.id));
});
}
}
// Jika polres dichecklist
if (unitSelection.polres && item.subDestination) {
// Jika checkbox POLDA di Penerima Tugas juga aktif, checklist semua polres
if (unitSelection.polda && item.levelNumber === 2 && item.name !== "SATKER POLRI") {
item.subDestination.forEach((polres: any) => {
newCheckedLevels.add(Number(polres.id));
});
}
// Jika checkbox POLDA di Penerima Tugas tidak aktif, tapi ada POLDA yang dichecklist di modal
else if (!unitSelection.polda && item.levelNumber === 2 && item.name !== "SATKER POLRI") {
// Cek apakah POLDA ini sudah dichecklist di modal
if (checkedLevels.has(Number(item.id))) {
// Jika ya, checklist semua polres dari POLDA ini
item.subDestination.forEach((polres: any) => {
newCheckedLevels.add(Number(polres.id));
});
}
}
}
});
setCheckedLevels(newCheckedLevels);
} else {
// Jika tidak ada unitSelection yang aktif, unchecklist semua item di modal
setCheckedLevels(new Set<number>());
}
// Reset flag setelah sinkronisasi selesai
setTimeout(() => {
setIsUpdatingFromPenerimaTugas(false);
setPenerimaTugasChangeType("");
}, 100);
}
setCheckedLevels(updated);
}, [unitSelection.polda, unitSelection.polres, listDest]);
};
const onSubmit = (data: TaskSchema) => {
MySwal.fire({
@ -604,7 +757,6 @@ export default function FormTask() {
</div>
<div className="flex flex-wrap gap-3 mt-5 lg:pt-7 lg:ml-3 ">
{Object.keys(unitSelection).map((key) => {
const isDisabled = key === "polres" && !unitSelection.polda;
return (
<div className="flex items-center gap-2" key={key}>
<Checkbox
@ -612,7 +764,6 @@ export default function FormTask() {
checked={
unitSelection[key as keyof typeof unitSelection]
}
disabled={isDisabled}
onCheckedChange={(value) =>
handleUnitChange(
key as keyof typeof unitSelection,
@ -621,7 +772,7 @@ export default function FormTask() {
}
/>
<Label htmlFor={key}>
{key.charAt(0).toUpperCase() + key.slice(1)}
{key === "allUnit" ? "Semua Unit" : key.charAt(0).toUpperCase() + key.slice(1)}
</Label>
</div>
);
@ -639,30 +790,22 @@ export default function FormTask() {
<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; // kontrol polda luar
const polresChecked = unitSelection.polres; // kontrol polres luar
const isPoldaDisabled = poldaChecked; // lock checkbox polda dialog jika polda luar dicentang
const isPolresDisabled = polresChecked; // lock checkbox polres dialog jika polres luar dicentang
return (
{listDest.map((polda: any) => (
<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;
handleCheckboxChange(polda.id);
}}
checked={checkedLevels.has(Number(polda.id))}
onCheckedChange={() => handleCheckboxChange(Number(polda.id))}
className="mr-3"
/>
{polda.name}
<button
onClick={() => toggleExpand(polda.id)}
type="button"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
toggleExpand(polda.id);
}}
className="ml-2 focus:outline-none"
>
{expandedPolda[polda.id] ? (
@ -672,21 +815,43 @@ export default function FormTask() {
)}
</button>
</Label>
{expandedPolda[polda.id] && (
<div className="ml-6 mt-2">
<Label className="block">
<Checkbox
checked={polda?.subDestination?.every(
(polres: any) =>
checkedLevels.has(Number(polres.id))
)}
onCheckedChange={(isChecked) => {
const updatedLevels = new Set(
checkedLevels
);
polda?.subDestination?.forEach(
(polres: any) => {
if (isChecked) {
updatedLevels.add(Number(polres.id));
} else {
updatedLevels.delete(Number(polres.id));
}
}
);
setCheckedLevels(updatedLevels);
// Update unitSelection berdasarkan perubahan
updateUnitSelectionFromModal();
}}
className="mr-2"
/>
Pilih Semua
</Label>
{polda?.subDestination?.map((polres: any) => (
<Label key={polres.id} className="block mt-1">
<Checkbox
checked={
polresChecked ||
checkedLevels.has(polres.id)
checked={checkedLevels.has(Number(polres.id))}
onCheckedChange={() =>
handleCheckboxChange(Number(polres.id))
}
disabled={isPolresDisabled}
onCheckedChange={() => {
if (isPolresDisabled) return;
handleCheckboxChange(polres.id);
}}
className="mr-2"
/>
{polres.name}
@ -695,8 +860,7 @@ export default function FormTask() {
</div>
)}
</div>
);
})}
))}
</div>
</DialogContent>
</Dialog>

View File

@ -536,7 +536,7 @@ const HeroNew = (props: { group?: string }) => {
<Swiper
modules={[Autoplay, Navigation]}
autoplay={{
delay: 3000,
delay: 10000,
disableOnInteraction: false,
}}
loop={true}

File diff suppressed because it is too large Load Diff

View File

@ -2151,6 +2151,20 @@ export function getMenuList(pathname: string, t: any): Group[] {
},
],
},
{
groupLabel: "",
id: "curatedcontent",
menus: [
{
id: "curatedcontent",
href: "/shared/curated-content",
label: t("curated-content"),
active: pathname.includes("/curated-content"),
icon: "pixelarticons:calendar-text",
submenus: [],
},
],
},
{
groupLabel: "",
id: "communication",
@ -2996,25 +3010,39 @@ export function getMenuList(pathname: string, t: any): Group[] {
active: pathname.includes("/schedule"),
icon: "uil:schedule",
submenus: [
// {
// href: "/contributor/schedule/press-conference",
// label: t("press-conference"),
// active: pathname.includes("/schedule/press-conference"),
// icon: "heroicons:arrow-trending-up",
// children: [],
// },
// {
// href: "/contributor/schedule/event",
// label: t("event"),
// active: pathname.includes("/schedule/event"),
// icon: "heroicons:shopping-cart",
// children: [],
// },
// {
// href: "/contributor/schedule/press-release",
// label: t("press-release"),
// active: pathname.includes("/schedule/press-release"),
// icon: "heroicons:shopping-cart",
// children: [],
// },
{
href: "/contributor/schedule/press-conference",
label: t("press-conference"),
active: pathname.includes("/schedule/press-conference"),
href: "/contributor/schedule/live-report",
label: t("live-report"),
active: pathname.includes("/schedule/live-report"),
icon: "heroicons:arrow-trending-up",
children: [],
},
{
href: "/contributor/schedule/event",
label: t("event"),
active: pathname.includes("/schedule/event"),
icon: "heroicons:shopping-cart",
children: [],
},
{
href: "/contributor/schedule/press-release",
label: t("press-release"),
active: pathname.includes("/schedule/press-release"),
icon: "heroicons:shopping-cart",
href: "/contributor/schedule/calendar-polri",
label: t("calendar-polri"),
active: pathname.includes("/schedule/calendar-polri"),
icon: "heroicons:arrow-trending-up",
children: [],
},
],

View File

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