Merge branch 'dev-sabda-v2' of https://gitlab.com/hanifsalafi/mediahub_redesign
This commit is contained in:
commit
e873af1f56
|
|
@ -303,7 +303,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
const firstType = typeSplit[0] as EventType;
|
const firstType = typeSplit[0] as EventType;
|
||||||
|
|
||||||
const colors: Record<EventType, string> = {
|
const colors: Record<EventType, string> = {
|
||||||
"0": "bg-black",
|
"0": "bg-gray-500",
|
||||||
"1": "bg-yellow-500",
|
"1": "bg-yellow-500",
|
||||||
"2": "bg-blue-400",
|
"2": "bg-blue-400",
|
||||||
"3": "bg-slate-400",
|
"3": "bg-slate-400",
|
||||||
|
|
@ -408,13 +408,13 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
const hasMoreEvents = events.length > 3;
|
const hasMoreEvents = events.length > 3;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 bg-white rounded-lg shadow-sm border border-gray-200 pb-3 mr-1">
|
<div className="flex-1 bg-white dark:bg-black rounded-lg shadow-sm border border-gray-200 pb-3 mr-1">
|
||||||
<div className="py-3">
|
<div className="py-3">
|
||||||
<h4 className="font-bold text-center">{label}</h4>
|
<h4 className="font-bold text-center">{label}</h4>
|
||||||
</div>
|
</div>
|
||||||
<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 dark:bg-black border border-black dark:border-gray-500">
|
||||||
<p className="text-center">
|
<p className="text-center">
|
||||||
{t("no-data-yet", { defaultValue: "No Data Yet" })}
|
{t("no-data-yet", { defaultValue: "No Data Yet" })}
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
|
|
@ -226,12 +226,12 @@ const LiveReportTable = () => {
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
<div className="border border-black rounded-md w-full md:w-fit">
|
<div className="border dark:bg-transparent border-black dark:border dark:border-white rounded-md w-full md:w-fit">
|
||||||
<Select
|
<Select
|
||||||
value={selectedType}
|
value={selectedType}
|
||||||
onValueChange={(value) => setSelectedType(value)}
|
onValueChange={(value) => setSelectedType(value)}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="w-full md:w-[150px] text-black">
|
<SelectTrigger className="w-full md:w-[150px] text-black dark:text-white">
|
||||||
<SelectValue placeholder="Tipe" />
|
<SelectValue placeholder="Tipe" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,9 @@ const TaskTaTable = () => {
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: Number(showData),
|
pageSize: Number(showData),
|
||||||
});
|
});
|
||||||
|
const [activeTab, setActiveTab] = React.useState<"ta" | "daily" | "special">(
|
||||||
|
"ta"
|
||||||
|
);
|
||||||
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
|
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
|
||||||
const [dateFilter, setDateFilter] = React.useState("");
|
const [dateFilter, setDateFilter] = React.useState("");
|
||||||
const [endDate, setEndDate] = React.useState("");
|
const [endDate, setEndDate] = React.useState("");
|
||||||
|
|
@ -126,51 +129,56 @@ const TaskTaTable = () => {
|
||||||
dateFilter,
|
dateFilter,
|
||||||
filterByCode,
|
filterByCode,
|
||||||
statusFilter,
|
statusFilter,
|
||||||
|
activeTab,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
const formattedStartDate = dateFilter
|
const formattedStartDate = dateFilter
|
||||||
? format(new Date(dateFilter), "yyyy-MM-dd")
|
? format(new Date(dateFilter), "yyyy-MM-dd")
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = isSpecificAttention
|
let res;
|
||||||
? await listTaskTa(
|
|
||||||
page - 1,
|
if (activeTab === "ta") {
|
||||||
search,
|
res = await listTaskTa(
|
||||||
showData,
|
page - 1,
|
||||||
filterByCode,
|
search,
|
||||||
formattedStartDate,
|
showData,
|
||||||
"atensi-khusus",
|
filterByCode,
|
||||||
statusFilter
|
formattedStartDate,
|
||||||
)
|
"atensi-khusus",
|
||||||
: await listTask(
|
statusFilter
|
||||||
page - 1,
|
);
|
||||||
search,
|
} else if (activeTab === "daily") {
|
||||||
showData,
|
res = await listTaskTa(
|
||||||
filterByCode,
|
page - 1,
|
||||||
formattedStartDate,
|
search,
|
||||||
"atensi-khusus",
|
showData,
|
||||||
statusFilter
|
filterByCode,
|
||||||
);
|
formattedStartDate,
|
||||||
|
"tugas-harian",
|
||||||
|
statusFilter
|
||||||
|
);
|
||||||
|
} else if (activeTab === "special") {
|
||||||
|
res = await listTask(
|
||||||
|
page - 1,
|
||||||
|
search,
|
||||||
|
showData,
|
||||||
|
filterByCode,
|
||||||
|
formattedStartDate,
|
||||||
|
"atensi-khusus",
|
||||||
|
statusFilter
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const data = res?.data?.data;
|
const data = res?.data?.data;
|
||||||
const contentData = data?.content;
|
const contentData = data?.content || [];
|
||||||
|
|
||||||
// let contentDataFilter = res?.data?.data?.content || [];
|
|
||||||
|
|
||||||
// Filter berdasarkan status
|
|
||||||
// contentDataFilter = contentDataFilter.filter((item: any) => {
|
|
||||||
// const isSelesai = statusFilter.includes(1) ? item.isDone : true;
|
|
||||||
// const isAktif = statusFilter.includes(2) ? item.isActive : true;
|
|
||||||
// return isSelesai && isAktif;
|
|
||||||
// });
|
|
||||||
|
|
||||||
contentData.forEach((item: any, index: number) => {
|
contentData.forEach((item: any, index: number) => {
|
||||||
item.no = (page - 1) * Number(showData) + index + 1;
|
item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("contentData : ", contentData);
|
|
||||||
|
|
||||||
setDataTable(contentData);
|
setDataTable(contentData);
|
||||||
setTotalData(data?.totalElements);
|
setTotalData(data?.totalElements);
|
||||||
setTotalPage(data?.totalPages);
|
setTotalPage(data?.totalPages);
|
||||||
|
|
@ -179,6 +187,57 @@ const TaskTaTable = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// async function fetchData() {
|
||||||
|
// const formattedStartDate = dateFilter
|
||||||
|
// ? format(new Date(dateFilter), "yyyy-MM-dd")
|
||||||
|
// : "";
|
||||||
|
// try {
|
||||||
|
// const res = isSpecificAttention
|
||||||
|
// ? await listTaskTa(
|
||||||
|
// page - 1,
|
||||||
|
// search,
|
||||||
|
// showData,
|
||||||
|
// filterByCode,
|
||||||
|
// formattedStartDate,
|
||||||
|
// "atensi-khusus",
|
||||||
|
// statusFilter
|
||||||
|
// )
|
||||||
|
// : await listTask(
|
||||||
|
// page - 1,
|
||||||
|
// search,
|
||||||
|
// showData,
|
||||||
|
// filterByCode,
|
||||||
|
// formattedStartDate,
|
||||||
|
// "atensi-khusus",
|
||||||
|
// statusFilter
|
||||||
|
// );
|
||||||
|
|
||||||
|
// const data = res?.data?.data;
|
||||||
|
// const contentData = data?.content;
|
||||||
|
|
||||||
|
// // let contentDataFilter = res?.data?.data?.content || [];
|
||||||
|
|
||||||
|
// // Filter berdasarkan status
|
||||||
|
// // contentDataFilter = contentDataFilter.filter((item: any) => {
|
||||||
|
// // const isSelesai = statusFilter.includes(1) ? item.isDone : true;
|
||||||
|
// // const isAktif = statusFilter.includes(2) ? item.isActive : true;
|
||||||
|
// // return isSelesai && isAktif;
|
||||||
|
// // });
|
||||||
|
|
||||||
|
// contentData.forEach((item: any, index: number) => {
|
||||||
|
// item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
|
// });
|
||||||
|
|
||||||
|
// console.log("contentData : ", contentData);
|
||||||
|
|
||||||
|
// setDataTable(contentData);
|
||||||
|
// setTotalData(data?.totalElements);
|
||||||
|
// setTotalPage(data?.totalPages);
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error("Error fetching tasks:", error);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setFilterByCode(e.target.value);
|
setFilterByCode(e.target.value);
|
||||||
setSearch(e.target.value);
|
setSearch(e.target.value);
|
||||||
|
|
@ -212,7 +271,44 @@ const TaskTaTable = () => {
|
||||||
onChange={() => setIsSpecificAttention(!isSpecificAttention)}
|
onChange={() => setIsSpecificAttention(!isSpecificAttention)}
|
||||||
hidden
|
hidden
|
||||||
/>
|
/>
|
||||||
<span
|
<div className="flex mb-6">
|
||||||
|
<button
|
||||||
|
onClick={() => setActiveTab("ta")}
|
||||||
|
className={`px-4 py-1 rounded transition ${
|
||||||
|
activeTab === "ta"
|
||||||
|
? "bg-default-900 text-white"
|
||||||
|
: "border dark:text-default-700"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
Atensi Khusus TA
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={() => setActiveTab("daily")}
|
||||||
|
className={`px-4 py-1 rounded transition ${
|
||||||
|
activeTab === "daily"
|
||||||
|
? "bg-default-900 text-white"
|
||||||
|
: "border dark:text-default-700"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{t("daily-tasks", { defaultValue: "Daily Tasks" })}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={() => setActiveTab("special")}
|
||||||
|
className={`px-4 py-1 rounded transition ${
|
||||||
|
activeTab === "special"
|
||||||
|
? "bg-default-900 text-white"
|
||||||
|
: "border dark:text-default-700"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{t("special-attention", {
|
||||||
|
defaultValue: "Special Attention",
|
||||||
|
})}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* <span
|
||||||
className={` ${
|
className={` ${
|
||||||
isSpecificAttention
|
isSpecificAttention
|
||||||
? "bg-default-900 text-white"
|
? "bg-default-900 text-white"
|
||||||
|
|
@ -224,6 +320,18 @@ const TaskTaTable = () => {
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className={`
|
className={`
|
||||||
|
${
|
||||||
|
!isSpecificAttention
|
||||||
|
? "bg-default-900 text-white dark:text-black"
|
||||||
|
: " dark:text-default-700 border-2 dark:border dark:border-gray-500"
|
||||||
|
}
|
||||||
|
px-[18px] py-1 transition duration-100 rounded
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{t("daily-tasks", { defaultValue: "Daily Tasks" })}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className={`
|
||||||
${
|
${
|
||||||
!isSpecificAttention
|
!isSpecificAttention
|
||||||
? "bg-default-900 text-white"
|
? "bg-default-900 text-white"
|
||||||
|
|
@ -235,7 +343,7 @@ const TaskTaTable = () => {
|
||||||
{t("special-attention", {
|
{t("special-attention", {
|
||||||
defaultValue: "Special Attention",
|
defaultValue: "Special Attention",
|
||||||
})}{" "}
|
})}{" "}
|
||||||
</span>
|
</span> */}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,7 @@ const TaskTable = () => {
|
||||||
className={` ${
|
className={` ${
|
||||||
isSpecificAttention
|
isSpecificAttention
|
||||||
? "bg-default-900 text-white dark:text-black"
|
? "bg-default-900 text-white dark:text-black"
|
||||||
: "dark:text-default-700 border-2"
|
: "dark:text-default-700 border-2 dark:border dark:border-gray-500"
|
||||||
}
|
}
|
||||||
px-[18px] py-1 transition duration-100 rounded`}
|
px-[18px] py-1 transition duration-100 rounded`}
|
||||||
>
|
>
|
||||||
|
|
@ -218,7 +218,7 @@ const TaskTable = () => {
|
||||||
${
|
${
|
||||||
!isSpecificAttention
|
!isSpecificAttention
|
||||||
? "bg-default-900 text-white dark:text-black"
|
? "bg-default-900 text-white dark:text-black"
|
||||||
: " dark:text-default-700 border-2"
|
: " dark:text-default-700 border-2 dark:border dark:border-gray-500"
|
||||||
}
|
}
|
||||||
px-[18px] py-1 transition duration-100 rounded
|
px-[18px] py-1 transition duration-100 rounded
|
||||||
`}
|
`}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ const CommunicationPage = () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
<div className="w-full overflow-x-auto bg-white dark:bg-black p-4 rounded-sm space-y-3">
|
||||||
<div className="flex justify-between py-3">
|
<div className="flex justify-between py-3">
|
||||||
<p className="text-lg">{tab}</p>
|
<p className="text-lg">{tab}</p>
|
||||||
{tab === "Pertanyaan Internal" && (
|
{tab === "Pertanyaan Internal" && (
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { CKEditor } from "@ckeditor/ckeditor5-react";
|
||||||
import Editor from "ckeditor5-custom-build";
|
import Editor from "ckeditor5-custom-build";
|
||||||
|
|
||||||
function CustomEditor(props) {
|
function CustomEditor(props) {
|
||||||
const maxHeight = props.maxHeight || 600; // Default max height 600px
|
const maxHeight = props.maxHeight || 600;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ckeditor-wrapper">
|
<div className="ckeditor-wrapper">
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import Editor from "ckeditor5-custom-build";
|
||||||
|
|
||||||
function ViewEditor(props) {
|
function ViewEditor(props) {
|
||||||
const maxHeight = props.maxHeight || 600; // Default max height 600px
|
const maxHeight = props.maxHeight || 600; // Default max height 600px
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ckeditor-view-wrapper">
|
<div className="ckeditor-view-wrapper">
|
||||||
<CKEditor
|
<CKEditor
|
||||||
|
|
@ -12,15 +12,14 @@ function ViewEditor(props) {
|
||||||
data={props.initialData}
|
data={props.initialData}
|
||||||
disabled={true}
|
disabled={true}
|
||||||
config={{
|
config={{
|
||||||
// toolbar: [],
|
|
||||||
isReadOnly: true,
|
isReadOnly: true,
|
||||||
// Add content styling configuration for read-only mode
|
|
||||||
content_style: `
|
content_style: `
|
||||||
body {
|
body {
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
color: #333;
|
color: #111;
|
||||||
|
background: #fff;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
@ -41,66 +40,105 @@ function ViewEditor(props) {
|
||||||
background-color: #f9fafb;
|
background-color: #f9fafb;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
// Editor appearance settings
|
|
||||||
height: props.height || 400,
|
height: props.height || 400,
|
||||||
removePlugins: ['Title'],
|
removePlugins: ["Title"],
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<style jsx>{`
|
<style jsx>{`
|
||||||
.ckeditor-view-wrapper {
|
.ckeditor-view-wrapper {
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1),
|
||||||
|
0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ckeditor-view-wrapper :global(.ck.ck-editor__main) {
|
.ckeditor-view-wrapper :global(.ck.ck-editor__main) {
|
||||||
min-height: ${props.height || 400}px;
|
min-height: ${props.height || 400}px;
|
||||||
max-height: ${maxHeight}px;
|
max-height: ${maxHeight}px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ckeditor-view-wrapper :global(.ck.ck-editor__editable) {
|
.ckeditor-view-wrapper :global(.ck.ck-editor__editable) {
|
||||||
min-height: ${(props.height || 400) - 50}px;
|
min-height: ${(props.height || 400) - 50}px;
|
||||||
max-height: ${maxHeight - 50}px;
|
max-height: ${maxHeight - 50}px;
|
||||||
overflow-y: auto !important;
|
overflow-y: auto !important;
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: #cbd5e1 #f1f5f9;
|
scrollbar-color: #cbd5e1 #f1f5f9;
|
||||||
background-color:rgb(253, 253, 253);
|
background-color: #fdfdfd;
|
||||||
border: 1px solid #d1d5db;
|
border: 1px solid #d1d5db;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
|
color: #111;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Custom scrollbar styling for webkit browsers */
|
/* 🌙 Dark mode support */
|
||||||
.ckeditor-view-wrapper :global(.ck.ck-editor__editable::-webkit-scrollbar) {
|
:global(.dark) .ckeditor-view-wrapper :global(.ck.ck-editor__editable) {
|
||||||
|
background-color: #111 !important;
|
||||||
|
color: #f9fafb !important;
|
||||||
|
border-color: #374151;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.dark) .ckeditor-view-wrapper h1,
|
||||||
|
:global(.dark) .ckeditor-view-wrapper h2,
|
||||||
|
:global(.dark) .ckeditor-view-wrapper h3,
|
||||||
|
:global(.dark) .ckeditor-view-wrapper h4,
|
||||||
|
:global(.dark) .ckeditor-view-wrapper h5,
|
||||||
|
:global(.dark) .ckeditor-view-wrapper h6 {
|
||||||
|
color: #f9fafb !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.dark) .ckeditor-view-wrapper blockquote {
|
||||||
|
background-color: #1f2937 !important;
|
||||||
|
border-left: 4px solid #374151 !important;
|
||||||
|
color: #f3f4f6 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom scrollbar styling */
|
||||||
|
.ckeditor-view-wrapper
|
||||||
|
:global(.ck.ck-editor__editable::-webkit-scrollbar) {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ckeditor-view-wrapper :global(.ck.ck-editor__editable::-webkit-scrollbar-track) {
|
.ckeditor-view-wrapper
|
||||||
|
:global(.ck.ck-editor__editable::-webkit-scrollbar-track) {
|
||||||
background: #f1f5f9;
|
background: #f1f5f9;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ckeditor-view-wrapper :global(.ck.ck-editor__editable::-webkit-scrollbar-thumb) {
|
.ckeditor-view-wrapper
|
||||||
|
:global(.ck.ck-editor__editable::-webkit-scrollbar-thumb) {
|
||||||
background: #cbd5e1;
|
background: #cbd5e1;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ckeditor-view-wrapper :global(.ck.ck-editor__editable::-webkit-scrollbar-thumb:hover) {
|
.ckeditor-view-wrapper
|
||||||
|
:global(.ck.ck-editor__editable::-webkit-scrollbar-thumb:hover) {
|
||||||
background: #94a3b8;
|
background: #94a3b8;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ensure content doesn't overflow */
|
/* 🌙 Dark mode scrollbar */
|
||||||
.ckeditor-view-wrapper :global(.ck.ck-editor__editable .ck-content) {
|
:global(.dark)
|
||||||
overflow: hidden;
|
.ckeditor-view-wrapper
|
||||||
|
:global(.ck.ck-editor__editable::-webkit-scrollbar-track) {
|
||||||
|
background: #1f2937;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:global(.dark)
|
||||||
|
.ckeditor-view-wrapper
|
||||||
|
:global(.ck.ck-editor__editable::-webkit-scrollbar-thumb) {
|
||||||
|
background: #4b5563;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.dark)
|
||||||
|
.ckeditor-view-wrapper
|
||||||
|
:global(.ck.ck-editor__editable::-webkit-scrollbar-thumb:hover) {
|
||||||
|
background: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
/* Read-only specific styling */
|
/* Read-only specific styling */
|
||||||
.ckeditor-view-wrapper :global(.ck.ck-editor__editable.ck-read-only) {
|
.ckeditor-view-wrapper :global(.ck.ck-editor__editable.ck-read-only) {
|
||||||
background-color: #f8fafc;
|
|
||||||
color: #4b5563;
|
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hide toolbar for view-only mode */
|
/* Hide toolbar */
|
||||||
.ckeditor-view-wrapper :global(.ck.ck-toolbar) {
|
.ckeditor-view-wrapper :global(.ck.ck-toolbar) {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
@ -110,3 +148,116 @@ function ViewEditor(props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ViewEditor;
|
export default ViewEditor;
|
||||||
|
|
||||||
|
// import React from "react";
|
||||||
|
// import { CKEditor } from "@ckeditor/ckeditor5-react";
|
||||||
|
// import Editor from "ckeditor5-custom-build";
|
||||||
|
|
||||||
|
// function ViewEditor(props) {
|
||||||
|
// const maxHeight = props.maxHeight || 600;
|
||||||
|
|
||||||
|
// return (
|
||||||
|
// <div className="ckeditor-view-wrapper">
|
||||||
|
// <CKEditor
|
||||||
|
// editor={Editor}
|
||||||
|
// data={props.initialData}
|
||||||
|
// disabled={true}
|
||||||
|
// config={{
|
||||||
|
// // toolbar: [],
|
||||||
|
// isReadOnly: true,
|
||||||
|
// // Add content styling configuration for read-only mode
|
||||||
|
// content_style: `
|
||||||
|
// body {
|
||||||
|
// font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
// font-size: 14px;
|
||||||
|
// line-height: 1.6;
|
||||||
|
// color: #333;
|
||||||
|
// margin: 0;
|
||||||
|
// padding: 0;
|
||||||
|
// }
|
||||||
|
// p {
|
||||||
|
// margin: 0.5em 0;
|
||||||
|
// }
|
||||||
|
// h1, h2, h3, h4, h5, h6 {
|
||||||
|
// margin: 1em 0 0.5em 0;
|
||||||
|
// }
|
||||||
|
// ul, ol {
|
||||||
|
// margin: 0.5em 0;
|
||||||
|
// padding-left: 2em;
|
||||||
|
// }
|
||||||
|
// blockquote {
|
||||||
|
// margin: 1em 0;
|
||||||
|
// padding: 0.5em 1em;
|
||||||
|
// border-left: 4px solid #d1d5db;
|
||||||
|
// background-color: #f9fafb;
|
||||||
|
// }
|
||||||
|
// `,
|
||||||
|
// // Editor appearance settings
|
||||||
|
// height: props.height || 400,
|
||||||
|
// removePlugins: ['Title'],
|
||||||
|
// }}
|
||||||
|
// />
|
||||||
|
// <style jsx>{`
|
||||||
|
// .ckeditor-view-wrapper {
|
||||||
|
// border-radius: 6px;
|
||||||
|
// overflow: hidden;
|
||||||
|
// box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// .ckeditor-view-wrapper :global(.ck.ck-editor__main) {
|
||||||
|
// min-height: ${props.height || 400}px;
|
||||||
|
// max-height: ${maxHeight}px;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// .ckeditor-view-wrapper :global(.ck.ck-editor__editable) {
|
||||||
|
// min-height: ${(props.height || 400) - 50}px;
|
||||||
|
// max-height: ${maxHeight - 50}px;
|
||||||
|
// overflow-y: auto !important;
|
||||||
|
// scrollbar-width: thin;
|
||||||
|
// scrollbar-color: #cbd5e1 #f1f5f9;
|
||||||
|
// background-color:rgb(253, 253, 253);
|
||||||
|
// border: 1px solid #d1d5db;
|
||||||
|
// border-radius: 6px;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /* Custom scrollbar styling for webkit browsers */
|
||||||
|
// .ckeditor-view-wrapper :global(.ck.ck-editor__editable::-webkit-scrollbar) {
|
||||||
|
// width: 8px;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// .ckeditor-view-wrapper :global(.ck.ck-editor__editable::-webkit-scrollbar-track) {
|
||||||
|
// background: #f1f5f9;
|
||||||
|
// border-radius: 4px;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// .ckeditor-view-wrapper :global(.ck.ck-editor__editable::-webkit-scrollbar-thumb) {
|
||||||
|
// background: #cbd5e1;
|
||||||
|
// border-radius: 4px;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// .ckeditor-view-wrapper :global(.ck.ck-editor__editable::-webkit-scrollbar-thumb:hover) {
|
||||||
|
// background: #94a3b8;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /* Ensure content doesn't overflow */
|
||||||
|
// .ckeditor-view-wrapper :global(.ck.ck-editor__editable .ck-content) {
|
||||||
|
// overflow: hidden;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /* Read-only specific styling */
|
||||||
|
// .ckeditor-view-wrapper :global(.ck.ck-editor__editable.ck-read-only) {
|
||||||
|
// background-color: #f8fafc;
|
||||||
|
// color: #4b5563;
|
||||||
|
// cursor: default;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /* Hide toolbar for view-only mode */
|
||||||
|
// .ckeditor-view-wrapper :global(.ck.ck-toolbar) {
|
||||||
|
// display: none !important;
|
||||||
|
// }
|
||||||
|
// `}</style>
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export default ViewEditor;
|
||||||
|
|
|
||||||
|
|
@ -729,7 +729,7 @@ export default function FormConvertSPIT() {
|
||||||
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
|
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
<div className="lg:col-span-2 space-y-6">
|
<div className="lg:col-span-3 space-y-6">
|
||||||
{/* Basic Information */}
|
{/* Basic Information */}
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
|
|
@ -797,138 +797,40 @@ export default function FormConvertSPIT() {
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-8">
|
<CardContent className="space-y-8">
|
||||||
{/* Original Content */}
|
{/* Pilih Upload Type */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label className="text-lg text-black">Original Content</Label>
|
<Label>Upload Type</Label>
|
||||||
<Controller
|
<RadioGroup
|
||||||
control={control}
|
value={selectedFileType}
|
||||||
name="contentDescription"
|
onValueChange={(value: "original" | "rewrite") =>
|
||||||
render={({ field }) => (
|
setSelectedFileType(value)
|
||||||
<CustomEditor
|
}
|
||||||
onChange={field.onChange}
|
className="grid grid-cols-2 gap-4"
|
||||||
initialData={field.value}
|
>
|
||||||
/>
|
<div className="flex items-center space-x-2">
|
||||||
)}
|
<RadioGroupItem value="original" id="original" />
|
||||||
/>
|
<Label htmlFor="original">Original Content</Label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<RadioGroupItem value="rewrite" id="rewrite" />
|
||||||
|
<Label htmlFor="rewrite">Rewritten Content</Label>
|
||||||
|
</div>
|
||||||
|
</RadioGroup>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content Rewrite */}
|
{/* Tampilkan keduanya berdampingan */}
|
||||||
<div className="space-y-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<Label className="text-lg text-black">
|
{/* Original Content */}
|
||||||
Rewritten Content
|
<div
|
||||||
</Label>
|
className={`space-y-2 p-4 rounded-lg border ${
|
||||||
|
selectedFileType === "original"
|
||||||
<div className="flex items-center justify-between">
|
? "border-blue-600"
|
||||||
<div className="space-y-2">
|
: "border-gray-300"
|
||||||
<Label>Writing Style</Label>
|
}`}
|
||||||
<Select
|
>
|
||||||
value={selectedWritingStyle}
|
<Label className="text-lg text-black">
|
||||||
onValueChange={setSelectedWritingStyle}
|
Original Content
|
||||||
>
|
</Label>
|
||||||
<SelectTrigger className="w-48">
|
|
||||||
<SelectValue placeholder="Select style" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
{WRITING_STYLES.map((style) => (
|
|
||||||
<SelectItem key={style.value} value={style.value}>
|
|
||||||
{style.label}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
onClick={handleRewriteClick}
|
|
||||||
disabled={
|
|
||||||
isGeneratingRewrite || !detail?.contentDescription
|
|
||||||
}
|
|
||||||
className="bg-blue-600 hover:bg-blue-700"
|
|
||||||
>
|
|
||||||
{isGeneratingRewrite ? (
|
|
||||||
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
|
|
||||||
) : (
|
|
||||||
<Edit3 className="h-4 w-4 mr-2" />
|
|
||||||
)}
|
|
||||||
Generate Rewrite
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{showRewriteEditor && (
|
|
||||||
<div className="space-y-4">
|
|
||||||
{articleIds.length > 0 && (
|
|
||||||
<div className="flex gap-2">
|
|
||||||
{articleIds.map((articleId, index) => (
|
|
||||||
<Button
|
|
||||||
key={articleId}
|
|
||||||
type="button"
|
|
||||||
variant={
|
|
||||||
selectedArticleId === articleId
|
|
||||||
? "default"
|
|
||||||
: "outline"
|
|
||||||
}
|
|
||||||
size="sm"
|
|
||||||
onClick={() => handleArticleSelect(articleId)}
|
|
||||||
disabled={isLoadingRewrite}
|
|
||||||
>
|
|
||||||
{isLoadingRewrite &&
|
|
||||||
selectedArticleId === articleId && (
|
|
||||||
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
|
|
||||||
)}
|
|
||||||
Narrative {index + 1}
|
|
||||||
</Button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label>Rewritten Content</Label>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="contentRewriteDescription"
|
|
||||||
render={({ field }) => (
|
|
||||||
<CustomEditor
|
|
||||||
onChange={field.onChange}
|
|
||||||
initialData={articleBody || field.value}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* <Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="flex items-center gap-2">
|
|
||||||
<Edit3 className="h-5 w-5" />
|
|
||||||
Content Editor
|
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="space-y-4">
|
|
||||||
<RadioGroup
|
|
||||||
value={selectedFileType}
|
|
||||||
onValueChange={(value: "original" | "rewrite") =>
|
|
||||||
setSelectedFileType(value)
|
|
||||||
}
|
|
||||||
className="grid grid-cols-2 gap-4"
|
|
||||||
>
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<RadioGroupItem value="original" id="original" />
|
|
||||||
<Label htmlFor="original">Original Content</Label>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<RadioGroupItem value="rewrite" id="rewrite" />
|
|
||||||
<Label htmlFor="rewrite">Rewritten Content</Label>
|
|
||||||
</div>
|
|
||||||
</RadioGroup>
|
|
||||||
|
|
||||||
{/* Original Content */}
|
|
||||||
{/* {selectedFileType === "original" && (
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label>Content Description</Label>
|
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="contentDescription"
|
name="contentDescription"
|
||||||
|
|
@ -940,11 +842,19 @@ export default function FormConvertSPIT() {
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)} */}
|
|
||||||
|
|
||||||
{/* Content Rewrite */}
|
{/* Rewrite Content */}
|
||||||
{/* {selectedFileType === "rewrite" && (
|
<div
|
||||||
<div className="space-y-4">
|
className={`space-y-4 p-4 rounded-lg border ${
|
||||||
|
selectedFileType === "rewrite"
|
||||||
|
? "border-blue-600"
|
||||||
|
: "border-gray-300"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<Label className="text-lg text-black">
|
||||||
|
Rewritten Content
|
||||||
|
</Label>
|
||||||
|
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Writing Style</Label>
|
<Label>Writing Style</Label>
|
||||||
|
|
@ -953,7 +863,7 @@ export default function FormConvertSPIT() {
|
||||||
onValueChange={setSelectedWritingStyle}
|
onValueChange={setSelectedWritingStyle}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="w-48">
|
<SelectTrigger className="w-48">
|
||||||
<SelectValue />
|
<SelectValue placeholder="Select style" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{WRITING_STYLES.map((style) => (
|
{WRITING_STYLES.map((style) => (
|
||||||
|
|
@ -1024,359 +934,367 @@ export default function FormConvertSPIT() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card> */}
|
</Card>
|
||||||
|
|
||||||
{/* Media Files */}
|
{/* Media Files */}
|
||||||
{detailThumb.length > 0 && (
|
<div className="flex flex-col lg:flex-row gap-6">
|
||||||
<Card>
|
<div className="flex-1 space-y-6">
|
||||||
<CardHeader>
|
{detailThumb.length > 0 && (
|
||||||
<CardTitle className="flex items-center gap-2">
|
<Card>
|
||||||
<Image className="h-5 w-5" />
|
<CardHeader>
|
||||||
Media Files
|
<CardTitle className="flex items-center gap-2">
|
||||||
</CardTitle>
|
<Image className="h-5 w-5" />
|
||||||
</CardHeader>
|
Media Files
|
||||||
<CardContent className="space-y-4">
|
</CardTitle>
|
||||||
<div className="space-y-4">
|
</CardHeader>
|
||||||
<Swiper
|
<CardContent className="space-y-4">
|
||||||
thumbs={{ swiper: thumbsSwiper }}
|
<div className="space-y-4">
|
||||||
modules={[FreeMode, Navigation, Thumbs]}
|
<Swiper
|
||||||
navigation={true}
|
thumbs={{ swiper: thumbsSwiper }}
|
||||||
className="w-full h-96"
|
modules={[FreeMode, Navigation, Thumbs]}
|
||||||
>
|
navigation={true}
|
||||||
{detailThumb.map((item) => (
|
className="w-full h-96"
|
||||||
<SwiperSlide key={item.contentId}>
|
>
|
||||||
{item.contentType === "VIDEO" ? (
|
{detailThumb.map((item) => (
|
||||||
<div className="relative max-h-screen overflow-hidden">
|
<SwiperSlide key={item.contentId}>
|
||||||
<div className="w-full max-h-screen aspect-video">
|
{item.contentType === "VIDEO" ? (
|
||||||
<div className="w-full h-full object-contain">
|
<div className="relative max-h-screen overflow-hidden">
|
||||||
{/* main video player */}
|
<div className="w-full max-h-screen aspect-video">
|
||||||
<video
|
<div className="w-full h-full object-contain">
|
||||||
className="object-contain h-full w-full rounded-lg"
|
{/* main video player */}
|
||||||
src={item.contentFile}
|
<video
|
||||||
controls
|
className="object-contain h-full w-full rounded-lg"
|
||||||
// playsInline to better on mobile
|
src={item.contentFile}
|
||||||
playsInline
|
controls
|
||||||
// you can set poster if available: poster={item.thumbnailFileUrl}
|
// playsInline to better on mobile
|
||||||
title={`Video ${item.contentId}`}
|
playsInline
|
||||||
/>
|
// you can set poster if available: poster={item.thumbnailFileUrl}
|
||||||
|
title={`Video ${item.contentId}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
) : (
|
||||||
</div>
|
<img
|
||||||
) : (
|
src={item.contentFile}
|
||||||
<img
|
alt={`Media ${item.contentId}`}
|
||||||
src={item.contentFile}
|
className="w-full h-full object-cover rounded-lg"
|
||||||
alt={`Media ${item.contentId}`}
|
/>
|
||||||
className="w-full h-full object-cover rounded-lg"
|
)}
|
||||||
/>
|
</SwiperSlide>
|
||||||
)}
|
))}
|
||||||
</SwiperSlide>
|
</Swiper>
|
||||||
))}
|
|
||||||
</Swiper>
|
|
||||||
|
|
||||||
<Swiper
|
<Swiper
|
||||||
onSwiper={setThumbsSwiper}
|
onSwiper={setThumbsSwiper}
|
||||||
slidesPerView={8}
|
slidesPerView={8}
|
||||||
spaceBetween={8}
|
spaceBetween={8}
|
||||||
modules={[Pagination, Thumbs]}
|
modules={[Pagination, Thumbs]}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
>
|
>
|
||||||
{detailThumb.map((item) => (
|
{detailThumb.map((item) => (
|
||||||
<SwiperSlide key={`thumb-${item.contentId}`}>
|
<SwiperSlide key={`thumb-${item.contentId}`}>
|
||||||
{item.contentType === "VIDEO" ? (
|
{item.contentType === "VIDEO" ? (
|
||||||
<div className="relative w-full h-16 rounded cursor-pointer overflow-hidden">
|
<div className="relative w-full h-16 rounded cursor-pointer overflow-hidden">
|
||||||
{/* use preload metadata so browser doesn't download full video */}
|
{/* use preload metadata so browser doesn't download full video */}
|
||||||
|
<video
|
||||||
|
src={item.contentFile}
|
||||||
|
className="w-full h-16 object-cover"
|
||||||
|
muted
|
||||||
|
preload="metadata"
|
||||||
|
playsInline
|
||||||
|
// no controls in thumbnail
|
||||||
|
tabIndex={-1}
|
||||||
|
/>
|
||||||
|
<div className="absolute inset-0 flex items-center justify-center bg-black/30 pointer-events-none">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
className="w-6 h-6 text-white"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path d="M8 5v14l11-7z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<img
|
||||||
|
src={item.contentFile}
|
||||||
|
alt={`Thumbnail ${item.contentId}`}
|
||||||
|
className="w-full h-16 object-cover rounded cursor-pointer"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</SwiperSlide>
|
||||||
|
))}
|
||||||
|
</Swiper>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* File Placement */}
|
||||||
|
{files.length > 0 && isUserMabesApprover && (
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="flex items-center gap-2">
|
||||||
|
<Globe className="h-5 w-5" />
|
||||||
|
File Placement
|
||||||
|
</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-4">
|
||||||
|
{files.length > 1 && (
|
||||||
|
<div className="flex flex-wrap gap-4 p-4 bg-muted/50 rounded-lg">
|
||||||
|
{PLACEMENT_OPTIONS.map((option) => (
|
||||||
|
<div
|
||||||
|
key={option.value}
|
||||||
|
className="flex items-center space-x-2"
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
id={`select-all-${option.value}`}
|
||||||
|
onCheckedChange={(checked) =>
|
||||||
|
handleSelectAllPlacements(
|
||||||
|
option.value,
|
||||||
|
Boolean(checked)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Label
|
||||||
|
htmlFor={`select-all-${option.value}`}
|
||||||
|
className="text-sm"
|
||||||
|
>
|
||||||
|
All {option.label}
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="space-y-4">
|
||||||
|
{files.map((file, index) => (
|
||||||
|
<div
|
||||||
|
key={file.contentId}
|
||||||
|
className="flex gap-4 p-4 border rounded-lg"
|
||||||
|
>
|
||||||
|
{/* show thumbnail or video preview */}
|
||||||
|
{file.contentType === "VIDEO" ? (
|
||||||
<video
|
<video
|
||||||
src={item.contentFile}
|
src={file.contentFile}
|
||||||
className="w-full h-16 object-cover"
|
className="w-32 h-24 object-cover rounded"
|
||||||
muted
|
muted
|
||||||
preload="metadata"
|
preload="metadata"
|
||||||
playsInline
|
playsInline
|
||||||
// no controls in thumbnail
|
|
||||||
tabIndex={-1}
|
|
||||||
/>
|
/>
|
||||||
<div className="absolute inset-0 flex items-center justify-center bg-black/30 pointer-events-none">
|
) : (
|
||||||
<svg
|
<img
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
src={file.contentFile}
|
||||||
className="w-6 h-6 text-white"
|
alt={file.fileName || `file-${file.contentId}`}
|
||||||
fill="currentColor"
|
className="w-32 h-24 object-cover rounded"
|
||||||
viewBox="0 0 24 24"
|
/>
|
||||||
>
|
)}
|
||||||
<path d="M8 5v14l11-7z" />
|
|
||||||
</svg>
|
<div className="flex-1 space-y-3">
|
||||||
|
<p className="font-medium text-sm">
|
||||||
|
{file.fileName ||
|
||||||
|
file.contentFileName ||
|
||||||
|
`File ${file.contentId}`}
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-wrap gap-3">
|
||||||
|
{PLACEMENT_OPTIONS.map((option) => (
|
||||||
|
<div
|
||||||
|
key={option.value}
|
||||||
|
className="flex items-center space-x-2"
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
id={`${file.contentId}-${option.value}`}
|
||||||
|
checked={filePlacements[index]?.includes(
|
||||||
|
option.value
|
||||||
|
)}
|
||||||
|
onCheckedChange={(checked) =>
|
||||||
|
handleFilePlacementChange(
|
||||||
|
index,
|
||||||
|
option.value,
|
||||||
|
Boolean(checked)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Label
|
||||||
|
htmlFor={`${file.contentId}-${option.value}`}
|
||||||
|
className="text-sm"
|
||||||
|
>
|
||||||
|
{option.label}
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
</div>
|
||||||
<img
|
))}
|
||||||
src={item.contentFile}
|
</div>
|
||||||
alt={`Thumbnail ${item.contentId}`}
|
</CardContent>
|
||||||
className="w-full h-16 object-cover rounded cursor-pointer"
|
</Card>
|
||||||
/>
|
)}
|
||||||
)}
|
</div>
|
||||||
</SwiperSlide>
|
|
||||||
))}
|
|
||||||
</Swiper>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* File Placement */}
|
{/* Sidebar */}
|
||||||
{files.length > 0 && isUserMabesApprover && (
|
<div className="w-full lg:w-[30%] space-y-6">
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="flex items-center gap-2">
|
<CardTitle className="flex items-center gap-2">
|
||||||
<Globe className="h-5 w-5" />
|
<Users className="h-5 w-5" />
|
||||||
File Placement
|
Creator Information
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4">
|
<CardContent className="space-y-4">
|
||||||
{files.length > 1 && (
|
<div className="space-y-2">
|
||||||
<div className="flex flex-wrap gap-4 p-4 bg-muted/50 rounded-lg">
|
<Label htmlFor="creator">Creator *</Label>
|
||||||
{PLACEMENT_OPTIONS.map((option) => (
|
<Controller
|
||||||
<div
|
control={control}
|
||||||
key={option.value}
|
name="contentCreator"
|
||||||
className="flex items-center space-x-2"
|
render={({ field }) => (
|
||||||
>
|
<Input
|
||||||
<Checkbox
|
id="creator"
|
||||||
id={`select-all-${option.value}`}
|
placeholder="Enter creator name"
|
||||||
onCheckedChange={(checked) =>
|
{...field}
|
||||||
handleSelectAllPlacements(
|
|
||||||
option.value,
|
|
||||||
Boolean(checked)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Label
|
|
||||||
htmlFor={`select-all-${option.value}`}
|
|
||||||
className="text-sm"
|
|
||||||
>
|
|
||||||
All {option.label}
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="space-y-4">
|
|
||||||
{files.map((file, index) => (
|
|
||||||
<div
|
|
||||||
key={file.contentId}
|
|
||||||
className="flex gap-4 p-4 border rounded-lg"
|
|
||||||
>
|
|
||||||
{/* show thumbnail or video preview */}
|
|
||||||
{file.contentType === "VIDEO" ? (
|
|
||||||
<video
|
|
||||||
src={file.contentFile}
|
|
||||||
className="w-32 h-24 object-cover rounded"
|
|
||||||
muted
|
|
||||||
preload="metadata"
|
|
||||||
playsInline
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<img
|
|
||||||
src={file.contentFile}
|
|
||||||
alt={file.fileName || `file-${file.contentId}`}
|
|
||||||
className="w-32 h-24 object-cover rounded"
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
/>
|
||||||
|
{errors.contentCreator && (
|
||||||
|
<Alert variant="soft">
|
||||||
|
<AlertCircle className="h-4 w-4" />
|
||||||
|
<AlertDescription>
|
||||||
|
{errors.contentCreator.message}
|
||||||
|
</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
<div className="flex-1 space-y-3">
|
{/* Preview */}
|
||||||
<p className="font-medium text-sm">
|
{detail?.contentThumbnail && (
|
||||||
{file.fileName ||
|
<Card>
|
||||||
file.contentFileName ||
|
<CardHeader>
|
||||||
`File ${file.contentId}`}
|
<CardTitle className="flex items-center gap-2">
|
||||||
</p>
|
<Eye className="h-5 w-5" />
|
||||||
<div className="flex flex-wrap gap-3">
|
Preview
|
||||||
{PLACEMENT_OPTIONS.map((option) => (
|
</CardTitle>
|
||||||
<div
|
</CardHeader>
|
||||||
key={option.value}
|
<CardContent>
|
||||||
className="flex items-center space-x-2"
|
<img
|
||||||
>
|
src={detail.contentThumbnail}
|
||||||
<Checkbox
|
alt="Content thumbnail"
|
||||||
id={`${file.contentId}-${option.value}`}
|
className="w-full h-auto rounded-lg"
|
||||||
checked={filePlacements[index]?.includes(
|
/>
|
||||||
option.value
|
</CardContent>
|
||||||
)}
|
</Card>
|
||||||
onCheckedChange={(checked) =>
|
)}
|
||||||
handleFilePlacementChange(
|
|
||||||
index,
|
{/* Tags */}
|
||||||
option.value,
|
<Card>
|
||||||
Boolean(checked)
|
<CardHeader>
|
||||||
)
|
<CardTitle className="flex items-center gap-2">
|
||||||
}
|
<Tag className="h-5 w-5" />
|
||||||
/>
|
Tags
|
||||||
<Label
|
</CardTitle>
|
||||||
htmlFor={`${file.contentId}-${option.value}`}
|
</CardHeader>
|
||||||
className="text-sm"
|
<CardContent className="space-y-4">
|
||||||
>
|
<div className="space-y-2">
|
||||||
{option.label}
|
<Label htmlFor="tag-input">Add Tags</Label>
|
||||||
</Label>
|
<Input
|
||||||
</div>
|
id="tag-input"
|
||||||
))}
|
placeholder="Type a tag and press Enter"
|
||||||
</div>
|
onKeyDown={handleAddTag}
|
||||||
</div>
|
ref={inputRef}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{tags.length > 0 && (
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
{tags.map((tag, index) => (
|
||||||
|
<Badge
|
||||||
|
key={index}
|
||||||
|
className="cursor-pointer hover:bg-destructive hover:text-destructive-foreground"
|
||||||
|
onClick={() => handleRemoveTag(index)}
|
||||||
|
>
|
||||||
|
{tag}
|
||||||
|
<XCircle className="h-3 w-3 ml-1" />
|
||||||
|
</Badge>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Publish Targets */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="flex items-center gap-2">
|
||||||
|
<Settings className="h-5 w-5" />
|
||||||
|
Publish Targets
|
||||||
|
</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-3">
|
||||||
|
{PUBLISH_OPTIONS.map((option) => (
|
||||||
|
<div
|
||||||
|
key={option.id}
|
||||||
|
className="flex items-center space-x-2"
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
id={option.id}
|
||||||
|
checked={
|
||||||
|
option.id === "all"
|
||||||
|
? publishedFor.length ===
|
||||||
|
PUBLISH_OPTIONS.filter(
|
||||||
|
(opt) => opt.id !== "all"
|
||||||
|
).length
|
||||||
|
: publishedFor.includes(option.id)
|
||||||
|
}
|
||||||
|
onCheckedChange={() =>
|
||||||
|
handlePublishTargetChange(option.id)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Label htmlFor={option.id} className="text-sm">
|
||||||
|
{option.label}
|
||||||
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</CardContent>
|
||||||
</CardContent>
|
</Card>
|
||||||
</Card>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Sidebar */}
|
{/* Submit Button */}
|
||||||
<div className="space-y-6">
|
<Card>
|
||||||
{/* Creator Information */}
|
<CardContent className="pt-6">
|
||||||
<Card>
|
<Button
|
||||||
<CardHeader>
|
type="submit"
|
||||||
<CardTitle className="flex items-center gap-2">
|
className="w-full mb-4"
|
||||||
<Users className="h-5 w-5" />
|
disabled={isSubmitting || isSaving || isAlreadySaved}
|
||||||
Creator Information
|
>
|
||||||
</CardTitle>
|
{isSubmitting || isSaving ? (
|
||||||
</CardHeader>
|
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
|
||||||
<CardContent className="space-y-4">
|
) : (
|
||||||
<div className="space-y-2">
|
<Save className="h-4 w-4 mr-2" />
|
||||||
<Label htmlFor="creator">Creator *</Label>
|
)}
|
||||||
<Controller
|
{isAlreadySaved
|
||||||
control={control}
|
? "Already Saved"
|
||||||
name="contentCreator"
|
: isSubmitting || isSaving
|
||||||
render={({ field }) => (
|
? "Saving..."
|
||||||
<Input
|
: "Save Changes"}
|
||||||
id="creator"
|
</Button>
|
||||||
placeholder="Enter creator name"
|
|
||||||
{...field}
|
{isAlreadySaved && (
|
||||||
/>
|
<Alert variant="soft">
|
||||||
|
<CheckCircle className="h-4 w-4 text-red-500" />
|
||||||
|
<AlertDescription className="text-red-500">
|
||||||
|
Konten sudah disimpan. Anda tidak dapat menyimpan
|
||||||
|
ulang.
|
||||||
|
</AlertDescription>
|
||||||
|
</Alert>
|
||||||
)}
|
)}
|
||||||
/>
|
</CardContent>
|
||||||
{errors.contentCreator && (
|
</Card>
|
||||||
<Alert variant="soft">
|
</div>
|
||||||
<AlertCircle className="h-4 w-4" />
|
</div>
|
||||||
<AlertDescription>
|
|
||||||
{errors.contentCreator.message}
|
|
||||||
</AlertDescription>
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* Preview */}
|
|
||||||
{detail?.contentThumbnail && (
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="flex items-center gap-2">
|
|
||||||
<Eye className="h-5 w-5" />
|
|
||||||
Preview
|
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<img
|
|
||||||
src={detail.contentThumbnail}
|
|
||||||
alt="Content thumbnail"
|
|
||||||
className="w-full h-auto rounded-lg"
|
|
||||||
/>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Tags */}
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="flex items-center gap-2">
|
|
||||||
<Tag className="h-5 w-5" />
|
|
||||||
Tags
|
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="space-y-4">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="tag-input">Add Tags</Label>
|
|
||||||
<Input
|
|
||||||
id="tag-input"
|
|
||||||
placeholder="Type a tag and press Enter"
|
|
||||||
onKeyDown={handleAddTag}
|
|
||||||
ref={inputRef}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{tags.length > 0 && (
|
|
||||||
<div className="flex flex-wrap gap-2">
|
|
||||||
{tags.map((tag, index) => (
|
|
||||||
<Badge
|
|
||||||
key={index}
|
|
||||||
className="cursor-pointer hover:bg-destructive hover:text-destructive-foreground"
|
|
||||||
onClick={() => handleRemoveTag(index)}
|
|
||||||
>
|
|
||||||
{tag}
|
|
||||||
<XCircle className="h-3 w-3 ml-1" />
|
|
||||||
</Badge>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* Publish Targets */}
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="flex items-center gap-2">
|
|
||||||
<Settings className="h-5 w-5" />
|
|
||||||
Publish Targets
|
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="space-y-3">
|
|
||||||
{PUBLISH_OPTIONS.map((option) => (
|
|
||||||
<div key={option.id} className="flex items-center space-x-2">
|
|
||||||
<Checkbox
|
|
||||||
id={option.id}
|
|
||||||
checked={
|
|
||||||
option.id === "all"
|
|
||||||
? publishedFor.length ===
|
|
||||||
PUBLISH_OPTIONS.filter((opt) => opt.id !== "all")
|
|
||||||
.length
|
|
||||||
: publishedFor.includes(option.id)
|
|
||||||
}
|
|
||||||
onCheckedChange={() =>
|
|
||||||
handlePublishTargetChange(option.id)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Label htmlFor={option.id} className="text-sm">
|
|
||||||
{option.label}
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* Submit Button */}
|
|
||||||
<Card>
|
|
||||||
<CardContent className="pt-6">
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
className="w-full mb-4"
|
|
||||||
disabled={isSubmitting || isSaving || isAlreadySaved}
|
|
||||||
>
|
|
||||||
{isSubmitting || isSaving ? (
|
|
||||||
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
|
|
||||||
) : (
|
|
||||||
<Save className="h-4 w-4 mr-2" />
|
|
||||||
)}
|
|
||||||
{isAlreadySaved
|
|
||||||
? "Already Saved"
|
|
||||||
: isSubmitting || isSaving
|
|
||||||
? "Saving..."
|
|
||||||
: "Save Changes"}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
{isAlreadySaved && (
|
|
||||||
<Alert variant="soft">
|
|
||||||
<CheckCircle className="h-4 w-4 text-red-500" />
|
|
||||||
<AlertDescription className="text-red-500">
|
|
||||||
Konten sudah disimpan. Anda tidak dapat menyimpan ulang.
|
|
||||||
</AlertDescription>
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
||||||
|
|
@ -87,13 +87,13 @@ export default function PublishMediahub() {
|
||||||
const t = useTranslations("Form");
|
const t = useTranslations("Form");
|
||||||
const [mainType, setMainType] = useState<number>(1);
|
const [mainType, setMainType] = useState<number>(1);
|
||||||
const [taskType, setTaskType] = useState<string>("atensi-khusus");
|
const [taskType, setTaskType] = useState<string>("atensi-khusus");
|
||||||
const [broadcastType, setBroadcastType] = useState<string>("all"); // untuk Tipe Penugasan
|
const [broadcastType, setBroadcastType] = useState<string>("all");
|
||||||
const [type, setType] = useState<string>("1");
|
const [type, setType] = useState<string>("1");
|
||||||
const [selectedTarget, setSelectedTarget] = useState("all");
|
const [selectedTarget, setSelectedTarget] = useState("all");
|
||||||
const [startDate, setStartDate] = useState<Date>(new Date());
|
const [startDate, setStartDate] = useState<Date>(new Date());
|
||||||
const [detail, setDetail] = useState<mediahubDetail>();
|
const [detail, setDetail] = useState<mediahubDetail>();
|
||||||
const [refresh] = useState(false);
|
const [refresh] = useState(false);
|
||||||
const [listDest, setListDest] = useState([]); // Data Polda dan Polres
|
const [listDest, setListDest] = useState([]);
|
||||||
const [checkedLevels, setCheckedLevels] = useState(new Set());
|
const [checkedLevels, setCheckedLevels] = useState(new Set());
|
||||||
const [expandedPolda, setExpandedPolda] = useState([{}]);
|
const [expandedPolda, setExpandedPolda] = useState([{}]);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
@ -267,7 +267,7 @@ export default function PublishMediahub() {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (id) {
|
if (id) {
|
||||||
requestData.id = parseInt(id, 10); // Ensure id is a number
|
requestData.id = parseInt(id, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Form Data Submitted:", requestData);
|
console.log("Form Data Submitted:", requestData);
|
||||||
|
|
@ -491,7 +491,7 @@ export default function PublishMediahub() {
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="md"
|
size="md"
|
||||||
className={cn(
|
className={cn(
|
||||||
" justify-between text-left font-normal border-default-200 text-default-600 md:px-4 w-3/12",
|
" justify-between text-left font-normal border-default-200 text-default-600 md:px-4 w-3/12 dark:border dark:border-gray-500",
|
||||||
!startDate && "text-muted-foreground"
|
!startDate && "text-muted-foreground"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -994,7 +994,7 @@ export default function FormTaskEdit() {
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className=" flex justify-between border px-3.5 py-3 my-6 rounded-md"
|
className=" flex justify-between border px-3.5 py-3 my-6 rounded-md dark:border dark:border-gray-500"
|
||||||
>
|
>
|
||||||
<div className="flex gap-3 items-center">
|
<div className="flex gap-3 items-center">
|
||||||
<div className="file-preview">
|
<div className="file-preview">
|
||||||
|
|
@ -1034,7 +1034,7 @@ export default function FormTaskEdit() {
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className=" flex justify-between border px-3.5 py-3 my-6 rounded-md"
|
className=" flex justify-between border px-3.5 py-3 my-6 rounded-md dark:border dark:border-gray-500"
|
||||||
>
|
>
|
||||||
<div className="flex gap-3 items-center">
|
<div className="flex gap-3 items-center">
|
||||||
<div className="file-preview">
|
<div className="file-preview">
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ import { loading } from "@/lib/swal";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import UpdateSection from "@/app/[locale]/(public)/inbox/update/page";
|
import UpdateSection from "@/app/[locale]/(public)/inbox/update/page";
|
||||||
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
|
|
||||||
const taskSchema = z.object({
|
const taskSchema = z.object({
|
||||||
title: z.string().min(1, { message: "Judul diperlukan" }),
|
title: z.string().min(1, { message: "Judul diperlukan" }),
|
||||||
|
|
@ -98,12 +99,11 @@ export default function FormTask() {
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const editor = useRef(null);
|
const editor = useRef(null);
|
||||||
|
const levelNumber = Number(getCookiesDecrypt("ulne")) || 0;
|
||||||
type TaskSchema = z.infer<typeof taskSchema>;
|
type TaskSchema = z.infer<typeof taskSchema>;
|
||||||
const { id } = useParams() as { id: string };
|
const { id } = useParams() as { id: string };
|
||||||
console.log(id);
|
console.log(id);
|
||||||
const [listDest, setListDest] = useState<Destination[]>([]);
|
const [listDest, setListDest] = useState<Destination[]>([]);
|
||||||
|
|
||||||
// State for various form fields
|
// State for various form fields
|
||||||
const [taskOutput, setTaskOutput] = useState({
|
const [taskOutput, setTaskOutput] = useState({
|
||||||
all: false,
|
all: false,
|
||||||
|
|
@ -146,12 +146,14 @@ export default function FormTask() {
|
||||||
polres: false,
|
polres: false,
|
||||||
satker: false,
|
satker: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// State untuk melacak apakah perubahan berasal dari checkbox Penerima Tugas
|
// State untuk melacak apakah perubahan berasal dari checkbox Penerima Tugas
|
||||||
const [isUpdatingFromPenerimaTugas, setIsUpdatingFromPenerimaTugas] = useState(false);
|
const [isUpdatingFromPenerimaTugas, setIsUpdatingFromPenerimaTugas] =
|
||||||
|
useState(false);
|
||||||
// State untuk melacak jenis perubahan spesifik
|
// State untuk melacak jenis perubahan spesifik
|
||||||
const [penerimaTugasChangeType, setPenerimaTugasChangeType] = useState<string>("");
|
const [penerimaTugasChangeType, setPenerimaTugasChangeType] =
|
||||||
|
useState<string>("");
|
||||||
|
|
||||||
const [links, setLinks] = useState<string[]>([""]);
|
const [links, setLinks] = useState<string[]>([""]);
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
|
|
@ -201,12 +203,14 @@ export default function FormTask() {
|
||||||
setCheckedLevels((prev) => {
|
setCheckedLevels((prev) => {
|
||||||
const updatedLevels = new Set(prev);
|
const updatedLevels = new Set(prev);
|
||||||
const isCurrentlyChecked = updatedLevels.has(levelId);
|
const isCurrentlyChecked = updatedLevels.has(levelId);
|
||||||
|
|
||||||
if (isCurrentlyChecked) {
|
if (isCurrentlyChecked) {
|
||||||
updatedLevels.delete(levelId);
|
updatedLevels.delete(levelId);
|
||||||
|
|
||||||
// Jika ini adalah POLDA yang di-unchecklist, unchecklist juga semua polres di bawahnya
|
// Jika ini adalah POLDA yang di-unchecklist, unchecklist juga semua polres di bawahnya
|
||||||
const poldaItem = listDest.find((item: any) => Number(item.id) === levelId);
|
const poldaItem = listDest.find(
|
||||||
|
(item: any) => Number(item.id) === levelId
|
||||||
|
);
|
||||||
if (poldaItem && poldaItem.subDestination) {
|
if (poldaItem && poldaItem.subDestination) {
|
||||||
poldaItem.subDestination.forEach((polres: any) => {
|
poldaItem.subDestination.forEach((polres: any) => {
|
||||||
updatedLevels.delete(Number(polres.id));
|
updatedLevels.delete(Number(polres.id));
|
||||||
|
|
@ -217,7 +221,7 @@ export default function FormTask() {
|
||||||
}
|
}
|
||||||
return updatedLevels;
|
return updatedLevels;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update unitSelection berdasarkan perubahan di modal
|
// Update unitSelection berdasarkan perubahan di modal
|
||||||
updateUnitSelectionFromModal();
|
updateUnitSelectionFromModal();
|
||||||
};
|
};
|
||||||
|
|
@ -233,7 +237,7 @@ export default function FormTask() {
|
||||||
// Set flag bahwa perubahan berasal dari checkbox Penerima Tugas
|
// Set flag bahwa perubahan berasal dari checkbox Penerima Tugas
|
||||||
setIsUpdatingFromPenerimaTugas(true);
|
setIsUpdatingFromPenerimaTugas(true);
|
||||||
setPenerimaTugasChangeType(key + (value ? "_checked" : "_unchecked"));
|
setPenerimaTugasChangeType(key + (value ? "_checked" : "_unchecked"));
|
||||||
|
|
||||||
if (key === "allUnit") {
|
if (key === "allUnit") {
|
||||||
const newState = {
|
const newState = {
|
||||||
allUnit: value,
|
allUnit: value,
|
||||||
|
|
@ -247,19 +251,22 @@ export default function FormTask() {
|
||||||
// Validasi khusus untuk POLRES
|
// Validasi khusus untuk POLRES
|
||||||
if (key === "polres" && value) {
|
if (key === "polres" && value) {
|
||||||
// Cek apakah ada POLDA yang sudah dichecklist di modal
|
// Cek apakah ada POLDA yang sudah dichecklist di modal
|
||||||
const hasCheckedPolda = listDest.some((item: any) =>
|
const hasCheckedPolda = listDest.some(
|
||||||
item.levelNumber === 2 &&
|
(item: any) =>
|
||||||
item.name !== "SATKER POLRI" &&
|
item.levelNumber === 2 &&
|
||||||
checkedLevels.has(Number(item.id))
|
item.name !== "SATKER POLRI" &&
|
||||||
|
checkedLevels.has(Number(item.id))
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!hasCheckedPolda) {
|
if (!hasCheckedPolda) {
|
||||||
// Jika tidak ada POLDA yang dichecklist di modal, tampilkan peringatan dan batalkan
|
// Jika tidak ada POLDA yang dichecklist di modal, tampilkan peringatan dan batalkan
|
||||||
alert("Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES.");
|
alert(
|
||||||
|
"Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES."
|
||||||
|
);
|
||||||
return; // Batalkan perubahan
|
return; // Batalkan perubahan
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedSelection = {
|
const updatedSelection = {
|
||||||
...unitSelection,
|
...unitSelection,
|
||||||
[key]: value,
|
[key]: value,
|
||||||
|
|
@ -396,52 +403,67 @@ export default function FormTask() {
|
||||||
const updateUnitSelectionFromModal = () => {
|
const updateUnitSelectionFromModal = () => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// Hitung total item yang tersedia untuk setiap kategori
|
// Hitung total item yang tersedia untuk setiap kategori
|
||||||
const totalPolda = listDest.filter((item: any) =>
|
const totalPolda = listDest.filter(
|
||||||
item.levelNumber === 2 && item.name !== "SATKER POLRI"
|
(item: any) => item.levelNumber === 2 && item.name !== "SATKER POLRI"
|
||||||
).length;
|
).length;
|
||||||
|
|
||||||
const totalPolres = listDest.reduce((total: number, item: any) => {
|
const totalPolres = listDest.reduce((total: number, item: any) => {
|
||||||
if (item.subDestination) {
|
if (item.subDestination) {
|
||||||
return total + item.subDestination.length;
|
return total + item.subDestination.length;
|
||||||
}
|
}
|
||||||
return total;
|
return total;
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
const satkerItem = listDest.find((item: any) => item.name === "SATKER POLRI");
|
const satkerItem = listDest.find(
|
||||||
const totalSatker = satkerItem ? (1 + (satkerItem.subDestination?.length || 0)) : 0;
|
(item: any) => item.name === "SATKER POLRI"
|
||||||
|
);
|
||||||
|
const totalSatker = satkerItem
|
||||||
|
? 1 + (satkerItem.subDestination?.length || 0)
|
||||||
|
: 0;
|
||||||
|
|
||||||
// Hitung item yang dichecklist untuk setiap kategori
|
// Hitung item yang dichecklist untuk setiap kategori
|
||||||
const checkedPoldaCount = listDest.filter((item: any) =>
|
const checkedPoldaCount = listDest.filter(
|
||||||
item.levelNumber === 2 &&
|
(item: any) =>
|
||||||
item.name !== "SATKER POLRI" &&
|
item.levelNumber === 2 &&
|
||||||
checkedLevels.has(Number(item.id))
|
item.name !== "SATKER POLRI" &&
|
||||||
|
checkedLevels.has(Number(item.id))
|
||||||
).length;
|
).length;
|
||||||
|
|
||||||
const checkedPolresCount = listDest.reduce((total: number, item: any) => {
|
const checkedPolresCount = listDest.reduce((total: number, item: any) => {
|
||||||
if (item.subDestination) {
|
if (item.subDestination) {
|
||||||
return total + item.subDestination.filter((sub: any) => checkedLevels.has(Number(sub.id))).length;
|
return (
|
||||||
|
total +
|
||||||
|
item.subDestination.filter((sub: any) =>
|
||||||
|
checkedLevels.has(Number(sub.id))
|
||||||
|
).length
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return total;
|
return total;
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
const checkedSatkerCount = satkerItem ? (
|
const checkedSatkerCount = satkerItem
|
||||||
(checkedLevels.has(Number(satkerItem.id)) ? 1 : 0) +
|
? (checkedLevels.has(Number(satkerItem.id)) ? 1 : 0) +
|
||||||
(satkerItem.subDestination?.filter((sub: any) => checkedLevels.has(Number(sub.id))).length || 0)
|
(satkerItem.subDestination?.filter((sub: any) =>
|
||||||
) : 0;
|
checkedLevels.has(Number(sub.id))
|
||||||
|
).length || 0)
|
||||||
|
: 0;
|
||||||
|
|
||||||
// Checkbox hanya aktif jika SEMUA item dalam kategori tersebut dichecklist
|
// Checkbox hanya aktif jika SEMUA item dalam kategori tersebut dichecklist
|
||||||
const hasCheckedPolda = totalPolda > 0 && checkedPoldaCount === totalPolda;
|
const hasCheckedPolda =
|
||||||
const hasCheckedPolres = totalPolres > 0 && checkedPolresCount === totalPolres;
|
totalPolda > 0 && checkedPoldaCount === totalPolda;
|
||||||
const hasCheckedSatker = totalSatker > 0 && checkedSatkerCount === totalSatker;
|
const hasCheckedPolres =
|
||||||
|
totalPolres > 0 && checkedPolresCount === totalPolres;
|
||||||
|
const hasCheckedSatker =
|
||||||
|
totalSatker > 0 && checkedSatkerCount === totalSatker;
|
||||||
|
|
||||||
// Update unitSelection berdasarkan checkbox yang aktif di modal
|
// Update unitSelection berdasarkan checkbox yang aktif di modal
|
||||||
setUnitSelection(prev => ({
|
setUnitSelection((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
polda: hasCheckedPolda,
|
polda: hasCheckedPolda,
|
||||||
polres: hasCheckedPolres,
|
polres: hasCheckedPolres,
|
||||||
satker: hasCheckedSatker,
|
satker: hasCheckedSatker,
|
||||||
// allUnit hanya true jika semua kategori terpenuhi
|
// allUnit hanya true jika semua kategori terpenuhi
|
||||||
allUnit: hasCheckedPolda && hasCheckedPolres && hasCheckedSatker
|
allUnit: hasCheckedPolda && hasCheckedPolres && hasCheckedSatker,
|
||||||
}));
|
}));
|
||||||
}, 0);
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
@ -453,29 +475,41 @@ export default function FormTask() {
|
||||||
// Khusus untuk unchecklist POLRES: hanya unchecklist polres, pertahankan polda
|
// Khusus untuk unchecklist POLRES: hanya unchecklist polres, pertahankan polda
|
||||||
if (penerimaTugasChangeType === "polres_unchecked") {
|
if (penerimaTugasChangeType === "polres_unchecked") {
|
||||||
const newCheckedLevels = new Set<number>(checkedLevels);
|
const newCheckedLevels = new Set<number>(checkedLevels);
|
||||||
|
|
||||||
// Hapus semua polres dari modal, tapi pertahankan polda
|
// Hapus semua polres dari modal, tapi pertahankan polda
|
||||||
listDest.forEach((item: any) => {
|
listDest.forEach((item: any) => {
|
||||||
if (item.subDestination && item.levelNumber === 2 && item.name !== "SATKER POLRI") {
|
if (
|
||||||
|
item.subDestination &&
|
||||||
|
item.levelNumber === 2 &&
|
||||||
|
item.name !== "SATKER POLRI"
|
||||||
|
) {
|
||||||
item.subDestination.forEach((polres: any) => {
|
item.subDestination.forEach((polres: any) => {
|
||||||
newCheckedLevels.delete(Number(polres.id));
|
newCheckedLevels.delete(Number(polres.id));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
setCheckedLevels(newCheckedLevels);
|
setCheckedLevels(newCheckedLevels);
|
||||||
}
|
}
|
||||||
// Untuk perubahan lainnya, jalankan logika normal
|
// Untuk perubahan lainnya, jalankan logika normal
|
||||||
else if (unitSelection.polda || unitSelection.polres || unitSelection.satker) {
|
else if (
|
||||||
|
unitSelection.polda ||
|
||||||
|
unitSelection.polres ||
|
||||||
|
unitSelection.satker
|
||||||
|
) {
|
||||||
// Mulai dengan checkbox yang sudah ada untuk mempertahankan pilihan manual user
|
// Mulai dengan checkbox yang sudah ada untuk mempertahankan pilihan manual user
|
||||||
const newCheckedLevels = new Set<number>(checkedLevels);
|
const newCheckedLevels = new Set<number>(checkedLevels);
|
||||||
|
|
||||||
listDest.forEach((item: any) => {
|
listDest.forEach((item: any) => {
|
||||||
// Jika polda dichecklist, checklist semua polda (levelNumber 2, bukan SATKER POLRI)
|
// Jika polda dichecklist, checklist semua polda (levelNumber 2, bukan SATKER POLRI)
|
||||||
if (unitSelection.polda && item.levelNumber === 2 && item.name !== "SATKER POLRI") {
|
if (
|
||||||
|
unitSelection.polda &&
|
||||||
|
item.levelNumber === 2 &&
|
||||||
|
item.name !== "SATKER POLRI"
|
||||||
|
) {
|
||||||
newCheckedLevels.add(Number(item.id));
|
newCheckedLevels.add(Number(item.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jika satker dichecklist, checklist SATKER POLRI dan sub-itemnya
|
// Jika satker dichecklist, checklist SATKER POLRI dan sub-itemnya
|
||||||
if (unitSelection.satker && item.name === "SATKER POLRI") {
|
if (unitSelection.satker && item.name === "SATKER POLRI") {
|
||||||
newCheckedLevels.add(Number(item.id));
|
newCheckedLevels.add(Number(item.id));
|
||||||
|
|
@ -485,17 +519,25 @@ export default function FormTask() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jika polres dichecklist
|
// Jika polres dichecklist
|
||||||
if (unitSelection.polres && item.subDestination) {
|
if (unitSelection.polres && item.subDestination) {
|
||||||
// Jika checkbox POLDA di Penerima Tugas juga aktif, checklist semua polres
|
// Jika checkbox POLDA di Penerima Tugas juga aktif, checklist semua polres
|
||||||
if (unitSelection.polda && item.levelNumber === 2 && item.name !== "SATKER POLRI") {
|
if (
|
||||||
|
unitSelection.polda &&
|
||||||
|
item.levelNumber === 2 &&
|
||||||
|
item.name !== "SATKER POLRI"
|
||||||
|
) {
|
||||||
item.subDestination.forEach((polres: any) => {
|
item.subDestination.forEach((polres: any) => {
|
||||||
newCheckedLevels.add(Number(polres.id));
|
newCheckedLevels.add(Number(polres.id));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Jika checkbox POLDA di Penerima Tugas tidak aktif, tapi ada POLDA yang dichecklist di modal
|
// 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") {
|
else if (
|
||||||
|
!unitSelection.polda &&
|
||||||
|
item.levelNumber === 2 &&
|
||||||
|
item.name !== "SATKER POLRI"
|
||||||
|
) {
|
||||||
// Cek apakah POLDA ini sudah dichecklist di modal
|
// Cek apakah POLDA ini sudah dichecklist di modal
|
||||||
if (checkedLevels.has(Number(item.id))) {
|
if (checkedLevels.has(Number(item.id))) {
|
||||||
// Jika ya, checklist semua polres dari POLDA ini
|
// Jika ya, checklist semua polres dari POLDA ini
|
||||||
|
|
@ -506,13 +548,13 @@ export default function FormTask() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
setCheckedLevels(newCheckedLevels);
|
setCheckedLevels(newCheckedLevels);
|
||||||
} else {
|
} else {
|
||||||
// Jika tidak ada unitSelection yang aktif, unchecklist semua item di modal
|
// Jika tidak ada unitSelection yang aktif, unchecklist semua item di modal
|
||||||
setCheckedLevels(new Set<number>());
|
setCheckedLevels(new Set<number>());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset flag setelah sinkronisasi selesai
|
// Reset flag setelah sinkronisasi selesai
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setIsUpdatingFromPenerimaTugas(false);
|
setIsUpdatingFromPenerimaTugas(false);
|
||||||
|
|
@ -756,7 +798,7 @@ export default function FormTask() {
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap gap-3 mt-5 lg:pt-7 lg:ml-3 ">
|
<div className="flex flex-wrap gap-3 mt-5 lg:pt-7 lg:ml-3 ">
|
||||||
{Object.keys(unitSelection).map((key) => {
|
{/* {Object.keys(unitSelection).map((key) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2" key={key}>
|
<div className="flex items-center gap-2" key={key}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|
@ -776,7 +818,37 @@ export default function FormTask() {
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})} */}
|
||||||
|
{Object.keys(unitSelection)
|
||||||
|
.filter((key) => {
|
||||||
|
if (levelNumber === 2) {
|
||||||
|
return key === "polda" || key === "polres";
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.map((key) => {
|
||||||
|
return (
|
||||||
|
<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 === "allUnit"
|
||||||
|
? "Semua Unit"
|
||||||
|
: key.charAt(0).toUpperCase() + key.slice(1)}
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 lg:pt-6 lg:pl-3">
|
<div className="mt-6 lg:pt-6 lg:pl-3">
|
||||||
<Dialog>
|
<Dialog>
|
||||||
|
|
@ -790,77 +862,83 @@ export default function FormTask() {
|
||||||
<DialogTitle>Daftar Wilayah Polda dan Polres</DialogTitle>
|
<DialogTitle>Daftar Wilayah Polda dan Polres</DialogTitle>
|
||||||
</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) => (
|
||||||
<div key={polda.id} className="border p-2">
|
<div key={polda.id} className="border p-2">
|
||||||
<Label className="flex items-center">
|
<Label className="flex items-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={checkedLevels.has(Number(polda.id))}
|
checked={checkedLevels.has(Number(polda.id))}
|
||||||
onCheckedChange={() => handleCheckboxChange(Number(polda.id))}
|
onCheckedChange={() =>
|
||||||
className="mr-3"
|
handleCheckboxChange(Number(polda.id))
|
||||||
/>
|
}
|
||||||
{polda.name}
|
className="mr-3"
|
||||||
<button
|
/>
|
||||||
type="button"
|
{polda.name}
|
||||||
onClick={(e) => {
|
<button
|
||||||
e.preventDefault();
|
type="button"
|
||||||
e.stopPropagation();
|
onClick={(e) => {
|
||||||
toggleExpand(polda.id);
|
e.preventDefault();
|
||||||
}}
|
e.stopPropagation();
|
||||||
className="ml-2 focus:outline-none"
|
toggleExpand(polda.id);
|
||||||
>
|
}}
|
||||||
{expandedPolda[polda.id] ? (
|
className="ml-2 focus:outline-none"
|
||||||
<ChevronUp size={16} />
|
>
|
||||||
) : (
|
{expandedPolda[polda.id] ? (
|
||||||
<ChevronDown size={16} />
|
<ChevronUp size={16} />
|
||||||
)}
|
) : (
|
||||||
</button>
|
<ChevronDown size={16} />
|
||||||
</Label>
|
)}
|
||||||
{expandedPolda[polda.id] && (
|
</button>
|
||||||
<div className="ml-6 mt-2">
|
</Label>
|
||||||
<Label className="block">
|
{expandedPolda[polda.id] && (
|
||||||
<Checkbox
|
<div className="ml-6 mt-2">
|
||||||
checked={polda?.subDestination?.every(
|
<Label className="block">
|
||||||
(polres: any) =>
|
<Checkbox
|
||||||
checkedLevels.has(Number(polres.id))
|
checked={polda?.subDestination?.every(
|
||||||
)}
|
(polres: any) =>
|
||||||
onCheckedChange={(isChecked) => {
|
checkedLevels.has(Number(polres.id))
|
||||||
const updatedLevels = new Set(
|
)}
|
||||||
checkedLevels
|
onCheckedChange={(isChecked) => {
|
||||||
);
|
const updatedLevels = new Set(
|
||||||
polda?.subDestination?.forEach(
|
checkedLevels
|
||||||
(polres: any) => {
|
);
|
||||||
if (isChecked) {
|
polda?.subDestination?.forEach(
|
||||||
updatedLevels.add(Number(polres.id));
|
(polres: any) => {
|
||||||
} else {
|
if (isChecked) {
|
||||||
updatedLevels.delete(Number(polres.id));
|
updatedLevels.add(Number(polres.id));
|
||||||
}
|
} else {
|
||||||
|
updatedLevels.delete(
|
||||||
|
Number(polres.id)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
);
|
}
|
||||||
setCheckedLevels(updatedLevels);
|
);
|
||||||
|
setCheckedLevels(updatedLevels);
|
||||||
// Update unitSelection berdasarkan perubahan
|
|
||||||
updateUnitSelectionFromModal();
|
// 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={checkedLevels.has(
|
||||||
|
Number(polres.id)
|
||||||
|
)}
|
||||||
|
onCheckedChange={() =>
|
||||||
|
handleCheckboxChange(Number(polres.id))
|
||||||
|
}
|
||||||
className="mr-2"
|
className="mr-2"
|
||||||
/>
|
/>
|
||||||
Pilih Semua
|
{polres.name}
|
||||||
</Label>
|
</Label>
|
||||||
{polda?.subDestination?.map((polres: any) => (
|
))}
|
||||||
<Label key={polres.id} className="block mt-1">
|
</div>
|
||||||
<Checkbox
|
)}
|
||||||
checked={checkedLevels.has(Number(polres.id))}
|
</div>
|
||||||
onCheckedChange={() =>
|
))}
|
||||||
handleCheckboxChange(Number(polres.id))
|
|
||||||
}
|
|
||||||
className="mr-2"
|
|
||||||
/>
|
|
||||||
{polres.name}
|
|
||||||
</Label>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue