fix: fixing point 1 -4

This commit is contained in:
Sabda Yagra 2025-07-21 13:55:24 +07:00
parent 6c5bb50661
commit b03da630ba
3 changed files with 227 additions and 165 deletions

View File

@ -74,7 +74,7 @@ const TaskTable = () => {
const [columnVisibility, setColumnVisibility] = const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({}); React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({}); const [rowSelection, setRowSelection] = React.useState({});
const [showData, setShowData] = React.useState("50"); const [showData, setShowData] = React.useState("10");
const [pagination, setPagination] = React.useState<PaginationState>({ const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0, pageIndex: 0,
pageSize: Number(showData), pageSize: Number(showData),
@ -206,7 +206,7 @@ const TaskTable = () => {
<span <span
className={` ${ className={` ${
isSpecificAttention isSpecificAttention
? "bg-default-900 text-white" ? "bg-default-900 text-white dark:text-black"
: "dark:text-default-700 border-2" : "dark:text-default-700 border-2"
} }
px-[18px] py-1 transition duration-100 rounded`} px-[18px] py-1 transition duration-100 rounded`}
@ -217,7 +217,7 @@ const TaskTable = () => {
className={` className={`
${ ${
!isSpecificAttention !isSpecificAttention
? "bg-default-900 text-white" ? "bg-default-900 text-white dark:text-black"
: " dark:text-default-700 border-2" : " dark:text-default-700 border-2"
} }
px-[18px] py-1 transition duration-100 rounded px-[18px] py-1 transition duration-100 rounded

View File

@ -105,12 +105,10 @@ interface FileWithPreview extends File {
export default function FormImageUpdate() { export default function FormImageUpdate() {
const MySwal = withReactContent(Swal); const MySwal = withReactContent(Swal);
const router = useRouter(); const router = useRouter();
const { id } = useParams() as { id: string }; const { id } = useParams() as { id: string };
console.log(id); console.log(id);
const editor = useRef(null); const editor = useRef(null);
type ImageSchema = z.infer<typeof imageSchema>; type ImageSchema = z.infer<typeof imageSchema>;
let progressInfo: any = []; let progressInfo: any = [];
let counterUpdateProgress = 0; let counterUpdateProgress = 0;
const [progressList, setProgressList] = useState<any>([]); const [progressList, setProgressList] = useState<any>([]);
@ -122,7 +120,6 @@ export default function FormImageUpdate() {
const taskId = Cookies.get("taskId"); const taskId = Cookies.get("taskId");
const scheduleId = Cookies.get("scheduleId"); const scheduleId = Cookies.get("scheduleId");
const scheduleType = Cookies.get("scheduleType"); const scheduleType = Cookies.get("scheduleType");
const [categories, setCategories] = useState<Category[]>([]); const [categories, setCategories] = useState<Category[]>([]);
const [selectedCategory, setSelectedCategory] = useState<any>(); const [selectedCategory, setSelectedCategory] = useState<any>();
const [tags, setTags] = useState<any[]>([]); const [tags, setTags] = useState<any[]>([]);
@ -133,11 +130,19 @@ export default function FormImageUpdate() {
const [files, setFiles] = useState<FileWithPreview[]>([]); const [files, setFiles] = useState<FileWithPreview[]>([]);
const [filesTemp, setFilesTemp] = useState<File[]>([]); const [filesTemp, setFilesTemp] = useState<File[]>([]);
const [publishedFor, setPublishedFor] = useState<string[]>([]); const [publishedFor, setPublishedFor] = useState<string[]>([]);
const [thumbnailFile, setThumbnailFile] = useState<File | null>(null);
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
const [selectedOptions, setSelectedOptions] = useState<{ const [selectedOptions, setSelectedOptions] = useState<{
[fileId: number]: string[]; [fileId: number]: string[];
}>({}); }>({});
const handleThumbnailChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
setThumbnailFile(file);
}
};
const options: Option[] = [ const options: Option[] = [
{ id: "all", name: "SEMUA" }, { id: "all", name: "SEMUA" },
{ id: "5", name: "UMUM" }, { id: "5", name: "UMUM" },
@ -176,35 +181,6 @@ export default function FormImageUpdate() {
resolver: zodResolver(imageSchema), resolver: zodResolver(imageSchema),
}); });
// const handleKeyDown = (e: any) => {
// const newTag = e.target.value.trim(); // Ambil nilai input
// if (e.key === "Enter" && newTag) {
// e.preventDefault(); // Hentikan submit form
// if (!tags.includes(newTag)) {
// setTags((prevTags) => [...prevTags, newTag]); // Tambah tag baru
// setValue("tags", ""); // Kosongkan input
// }
// }
// };
const handleImageChange = (event: ChangeEvent<HTMLInputElement>) => {
if (event.target.files) {
const files = Array.from(event.target.files);
setSelectedFiles((prevImages: any) => [...prevImages, ...files]);
console.log("DATAFILE::", selectedFiles);
}
};
const handleRemoveImage = (index: number) => {
setSelectedFiles((prevImages) => prevImages.filter((_, i) => i !== index));
};
// const handleCheckboxChange = (id: number) => {
// setSelectedPublishers((prev) =>
// prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
// );
// };
useEffect(() => { useEffect(() => {
async function initState() { async function initState() {
getCategories(); getCategories();
@ -218,9 +194,9 @@ export default function FormImageUpdate() {
e.preventDefault(); e.preventDefault();
const newTag = e.currentTarget.value.trim(); const newTag = e.currentTarget.value.trim();
if (!tags.includes(newTag)) { if (!tags.includes(newTag)) {
setTags((prevTags) => [...prevTags, newTag]); // Tambahkan tag baru setTags((prevTags) => [...prevTags, newTag]);
if (inputRef.current) { if (inputRef.current) {
inputRef.current.value = ""; // Kosongkan input inputRef.current.value = "";
} }
} }
} }
@ -251,7 +227,7 @@ export default function FormImageUpdate() {
if (findCategory) { if (findCategory) {
// setValue("categoryId", findCategory.id); // setValue("categoryId", findCategory.id);
setSelectedCategory(findCategory.id); // Set the selected category setSelectedCategory(findCategory.id);
const response = await getTagsBySubCategoryId(findCategory.id); const response = await getTagsBySubCategoryId(findCategory.id);
setTags(response?.data?.data); setTags(response?.data?.data);
} }
@ -268,16 +244,13 @@ export default function FormImageUpdate() {
const details = response?.data?.data; const details = response?.data?.data;
setDetail(details); setDetail(details);
// Set the selected target to the category ID from details
setSelectedTarget(String(details.category.id)); setSelectedTarget(String(details.category.id));
// Set form values immediately and then again after a delay to ensure editor is ready
setValue("title", details.title); setValue("title", details.title);
setValue("description", details.htmlDescription); setValue("description", details.htmlDescription);
setValue("creatorName", details.creatorName); setValue("creatorName", details.creatorName);
// Set again after delay to ensure editor has loaded
setTimeout(() => { setTimeout(() => {
setValue("title", details.title); setValue("title", details.title);
setValue("description", details.htmlDescription); setValue("description", details.htmlDescription);
@ -307,7 +280,7 @@ export default function FormImageUpdate() {
} }
} }
initState(); initState();
}, [id, setValue]); // Remove refresh dependency and add id }, [id, setValue]);
const mapPlacementsToOptions = (placements: string): string[] => { const mapPlacementsToOptions = (placements: string): string[] => {
const mapping: Record<string, string> = { const mapping: Record<string, string> = {
@ -317,7 +290,6 @@ export default function FormImageUpdate() {
polres: "internasional", polres: "internasional",
}; };
// Jika placements hanya "all", langsung aktifkan semua checkbox
if (placements.trim() === "all") { if (placements.trim() === "all") {
return ["all", "nasional", "wilayah", "internasional"]; return ["all", "nasional", "wilayah", "internasional"];
} }
@ -336,7 +308,6 @@ export default function FormImageUpdate() {
const handleCheckboxChange = (id: string) => { const handleCheckboxChange = (id: string) => {
if (id === "all") { if (id === "all") {
// Select all options except "all"
const allOptions = options const allOptions = options
.filter((opt) => opt.id !== "all") .filter((opt) => opt.id !== "all")
.map((opt) => opt.id); .map((opt) => opt.id);
@ -344,7 +315,6 @@ export default function FormImageUpdate() {
publishedFor.length === allOptions.length ? [] : allOptions publishedFor.length === allOptions.length ? [] : allOptions
); );
} else { } else {
// Toggle individual option
setPublishedFor((prev) => setPublishedFor((prev) =>
prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id] prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
); );
@ -380,9 +350,11 @@ export default function FormImageUpdate() {
} }
const formMedia = new FormData(); const formMedia = new FormData();
const thumbnail = files[0]; const thumbnail = thumbnailFile || files[0];
formMedia.append("file", thumbnail); formMedia.append("file", thumbnail);
const responseThumbnail = await uploadThumbnail(id, formMedia); const responseThumbnail = await uploadThumbnail(id, formMedia);
if (responseThumbnail?.error == true) { if (responseThumbnail?.error == true) {
error(responseThumbnail?.message); error(responseThumbnail?.message);
return false; return false;
@ -448,7 +420,7 @@ export default function FormImageUpdate() {
filename: file.name, filename: file.name,
filetype: file.type, filetype: file.type,
duration, duration,
isWatermark: "true", // hardcode isWatermark: "true",
}, },
onBeforeRequest: function (req) { onBeforeRequest: function (req) {
var xhr = req.getUnderlyingObject(); var xhr = req.getUnderlyingObject();
@ -554,7 +526,7 @@ export default function FormImageUpdate() {
const fileList = files.map((file: any) => ( const fileList = files.map((file: any) => (
<div <div
key={file.id} // Gunakan ID file sebagai key key={file.id}
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"
> >
<div className="flex gap-3 items-center"> <div className="flex gap-3 items-center">
@ -579,7 +551,7 @@ export default function FormImageUpdate() {
color="destructive" color="destructive"
variant="outline" variant="outline"
className="border-none rounded-full" className="border-none rounded-full"
onClick={() => handleDeleteFile(file.id)} // Kirim ID spesifik onClick={() => handleDeleteFile(file.id)}
> >
<Icon icon="tabler:x" className="h-5 w-5" /> <Icon icon="tabler:x" className="h-5 w-5" />
</Button> </Button>
@ -590,21 +562,18 @@ export default function FormImageUpdate() {
setSelectedOptions((prev: any) => { setSelectedOptions((prev: any) => {
const currentSelections = prev[fileId] || []; const currentSelections = prev[fileId] || [];
if (value === "all") { if (value === "all") {
// If "all" is clicked, toggle all options
if (currentSelections.includes("all")) { if (currentSelections.includes("all")) {
return { ...prev, [fileId]: [] }; // Deselect all return { ...prev, [fileId]: [] };
} }
return { return {
...prev, ...prev,
[fileId]: ["all", "nasional", "wilayah", "internasional"], [fileId]: ["all", "nasional", "wilayah", "internasional"],
}; // Select all };
} else { } else {
// If any other checkbox is clicked, toggle that checkbox
const updatedSelections = currentSelections.includes(value) const updatedSelections = currentSelections.includes(value)
? currentSelections.filter((option: any) => option !== value) ? currentSelections.filter((option: any) => option !== value)
: [...currentSelections, value]; : [...currentSelections, value];
// If all individual options are selected, include "all" automatically
const isAllSelected = ["nasional", "wilayah", "internasional"].every( const isAllSelected = ["nasional", "wilayah", "internasional"].every(
(opt) => updatedSelections.includes(opt) (opt) => updatedSelections.includes(opt)
); );
@ -671,7 +640,9 @@ export default function FormImageUpdate() {
<div className="flex flex-col lg:flex-row gap-10"> <div className="flex flex-col lg:flex-row gap-10">
<Card className="w-full lg:w-8/12"> <Card className="w-full lg:w-8/12">
<div className="px-6 py-6"> <div className="px-6 py-6">
<p className="text-lg font-semibold mb-3">{t("form-image", { defaultValue: "Form Image" })}</p> <p className="text-lg font-semibold mb-3">
{t("form-image", { defaultValue: "Form Image" })}
</p>
<div className="gap-5 mb-5"> <div className="gap-5 mb-5">
{/* Input Title */} {/* Input Title */}
<div className="space-y-2 py-3"> <div className="space-y-2 py-3">
@ -710,14 +681,18 @@ export default function FormImageUpdate() {
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{/* Show the category from details if it doesn't exist in categories list */} {/* Show the category from details if it doesn't exist in categories list */}
{detail && !categories.find(cat => String(cat.id) === String(detail.category.id)) && ( {detail &&
<SelectItem !categories.find(
key={String(detail.category.id)} (cat) =>
value={String(detail.category.id)} String(cat.id) === String(detail.category.id)
> ) && (
{detail.category.name} <SelectItem
</SelectItem> key={String(detail.category.id)}
)} value={String(detail.category.id)}
>
{detail.category.name}
</SelectItem>
)}
{categories.map((category) => ( {categories.map((category) => (
<SelectItem <SelectItem
key={String(category.id)} key={String(category.id)}
@ -732,12 +707,17 @@ export default function FormImageUpdate() {
</div> </div>
<div className="py-3 space-y-2"> <div className="py-3 space-y-2">
<Label>{t("description", { defaultValue: "Description" })}</Label> <Label>
{t("description", { defaultValue: "Description" })}
</Label>
<Controller <Controller
control={control} control={control}
name="description" name="description"
render={({ field }) => ( render={({ field }) => (
<CustomEditor onChange={field.onChange} initialData={field.value} /> <CustomEditor
onChange={field.onChange}
initialData={field.value}
/>
)} )}
/> />
{errors.description?.message && ( {errors.description?.message && (
@ -747,7 +727,9 @@ export default function FormImageUpdate() {
)} )}
</div> </div>
<div className="py-3 space-y-2"> <div className="py-3 space-y-2">
<Label>{t("select-file", { defaultValue: "Select File" })}</Label> <Label>
{t("select-file", { defaultValue: "Select File" })}
</Label>
{/* <Input {/* <Input
id="fileInput" id="fileInput"
type="file" type="file"
@ -763,7 +745,9 @@ export default function FormImageUpdate() {
{t("drag-file", { defaultValue: "Drag File" })} {t("drag-file", { defaultValue: "Drag File" })}
</h4> </h4>
<div className=" text-xs text-muted-foreground"> <div className=" text-xs text-muted-foreground">
{t("upload-file-max", { defaultValue: "Upload File Max" })} {t("upload-file-max", {
defaultValue: "Upload File Max",
})}
</div> </div>
</div> </div>
</div> </div>
@ -772,7 +756,9 @@ export default function FormImageUpdate() {
<div>{fileList}</div> <div>{fileList}</div>
<div className=" flex justify-between gap-2"> <div className=" flex justify-between gap-2">
<div className="flex flex-row items-center gap-3 py-3"> <div className="flex flex-row items-center gap-3 py-3">
<Label>{t("watermark", { defaultValue: "Watermark" })}</Label> <Label>
{t("watermark", { defaultValue: "Watermark" })}
</Label>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Switch defaultChecked color="primary" id="c2" /> <Switch defaultChecked color="primary" id="c2" />
</div> </div>
@ -812,7 +798,9 @@ export default function FormImageUpdate() {
rel="noopener noreferrer" rel="noopener noreferrer"
className="text-blue-500 text-sm" className="text-blue-500 text-sm"
> >
{t("view-file", { defaultValue: "View File" })} {t("view-file", {
defaultValue: "View File",
})}
</a> </a>
</div> </div>
<div> <div>
@ -830,7 +818,9 @@ export default function FormImageUpdate() {
} }
className="form-checkbox" className="form-checkbox"
/> />
<span>{t("all", { defaultValue: "All" })}</span> <span>
{t("all", { defaultValue: "All" })}
</span>
</Label> </Label>
</div> </div>
<div> <div>
@ -899,7 +889,7 @@ export default function FormImageUpdate() {
</div> </div>
</Card> </Card>
<div className="w-full lg:w-4/12"> <div className="w-full lg:w-4/12">
<Card className="h-[900px] md:h-[1100px] lg:h-[800px]"> <Card className="h-[900px] md:h-[1100px] lg:h-fit">
<div className="px-3 py-3"> <div className="px-3 py-3">
<div className="space-y-2"> <div className="space-y-2">
<Label>{t("creator", { defaultValue: "Creator" })}</Label> <Label>{t("creator", { defaultValue: "Creator" })}</Label>
@ -923,7 +913,7 @@ export default function FormImageUpdate() {
)} )}
</div> </div>
</div> </div>
<div className="mt-3 px-3 space-y-2"> {/* <div className="mt-3 px-3 space-y-2">
<Label>{t("preview", { defaultValue: "Preview" })}</Label> <Label>{t("preview", { defaultValue: "Preview" })}</Label>
<Card className="mt-2"> <Card className="mt-2">
<img <img
@ -932,7 +922,37 @@ export default function FormImageUpdate() {
className="w-full h-auto rounded" className="w-full h-auto rounded"
/> />
</Card> </Card>
</div> */}
<div className="mt-3 px-3 space-y-2">
<Label>{t("preview", { defaultValue: "Preview" })}</Label>
<Input
type="file"
accept="image/*"
onChange={(e) => {
const file = e.target.files?.[0];
if (file) {
setThumbnailFile(file);
}
}}
className="dark:border dark:border-gray-500 dark:rounded-lg"
/>
<Card className="mt-2">
<img
src={
thumbnailFile
? URL.createObjectURL(thumbnailFile)
: detail?.thumbnailLink
? `${detail.thumbnailLink}?v=${Date.now()}`
: ""
}
alt="Thumbnail Preview"
className="w-full h-auto rounded"
/>
</Card>
</div> </div>
<div className="px-3 py-3"> <div className="px-3 py-3">
<div className="space-y-2"> <div className="space-y-2">
<Label>{t("tags", { defaultValue: "Tags" })}</Label> <Label>{t("tags", { defaultValue: "Tags" })}</Label>
@ -980,7 +1000,9 @@ export default function FormImageUpdate() {
</div> </div>
<div className="px-3 py-3"> <div className="px-3 py-3">
<div className="flex flex-col gap-6 space-y-2"> <div className="flex flex-col gap-6 space-y-2">
<Label>{t("publish-target", { defaultValue: "Publish Target" })}</Label> <Label>
{t("publish-target", { defaultValue: "Publish Target" })}
</Label>
{options.map((option: Option) => ( {options.map((option: Option) => (
<div key={option.id} className="flex gap-2 items-center"> <div key={option.id} className="flex gap-2 items-center">
<Checkbox <Checkbox
@ -1000,7 +1022,9 @@ export default function FormImageUpdate() {
</div> </div>
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm"> <div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
<MailIcon /> <MailIcon />
<p className="">{t("suggestion-box", { defaultValue: "Suggestion Box" })} (0)</p> <p className="">
{t("suggestion-box", { defaultValue: "Suggestion Box" })} (0)
</p>
</div> </div>
<div className="px-3 py-3"> <div className="px-3 py-3">
<p>{t("information", { defaultValue: "Information" })}:</p> <p>{t("information", { defaultValue: "Information" })}:</p>

View File

@ -90,6 +90,17 @@ interface FileUploaded {
url: string; url: string;
} }
interface Destination {
id: string;
name: string;
subDestination?: SubDestination[];
}
interface SubDestination {
id: string;
name: string;
}
type Url = { type Url = {
id: number; id: number;
attachmentUrl: string; attachmentUrl: string;
@ -137,7 +148,7 @@ export default function FormTaskEdit() {
const [detail, setDetail] = useState<taskDetail>(); const [detail, setDetail] = useState<taskDetail>();
const [urlInputs, setUrlInputs] = useState<Url[]>([]); const [urlInputs, setUrlInputs] = useState<Url[]>([]);
const [refresh] = useState(false); const [refresh] = useState(false);
const [listDest, setListDest] = useState([]); const [listDest, setListDest] = useState<Destination[]>([]);
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);
@ -177,6 +188,12 @@ export default function FormTaskEdit() {
resolver: zodResolver(taskSchema), resolver: zodResolver(taskSchema),
}); });
useEffect(() => {
if (detail?.taskType) {
setTaskType(detail.taskType.toString());
}
}, [detail]);
// const handleRadioChange = (event: React.ChangeEvent<HTMLInputElement>) => { // const handleRadioChange = (event: React.ChangeEvent<HTMLInputElement>) => {
// const selectedValue = Number(event.target.value); // const selectedValue = Number(event.target.value);
// setMainType(selectedValue); // setMainType(selectedValue);
@ -425,7 +442,7 @@ export default function FormTaskEdit() {
console.log("Form Data Submitted:", requestData); console.log("Form Data Submitted:", requestData);
console.log("response", response); console.log("response", response);
const id = response?.data?.data.id; const id = response?.data?.data.id;
loading(); // loading();
if (imageFiles?.length == 0) { if (imageFiles?.length == 0) {
setIsImageUploadFinish(true); setIsImageUploadFinish(true);
} }
@ -471,6 +488,26 @@ export default function FormTaskEdit() {
// }); // });
}; };
useEffect(() => {
const updated = new Set(checkedLevels);
if (unitSelection.polda) {
listDest.forEach((polda) => {
updated.add(polda.id); // hanya id polda
});
}
if (unitSelection.polres) {
listDest.forEach((polda) => {
polda?.subDestination?.forEach((polres: any) => {
updated.add(polres.id); // hanya id polres
});
});
}
setCheckedLevels(updated);
}, [unitSelection.polda, unitSelection.polres, listDest]);
const onSubmit = (data: TaskSchema) => { const onSubmit = (data: TaskSchema) => {
MySwal.fire({ MySwal.fire({
title: "Simpan Data", title: "Simpan Data",
@ -732,25 +769,29 @@ export default function FormTaskEdit() {
</Select> </Select>
</div> </div>
<div className="flex flex-wrap gap-3 mt-6 lg:pt-7 lg:ml-3"> <div className="flex flex-wrap gap-3 mt-6 lg:pt-7 lg:ml-3">
{Object.keys(unitSelection).map((key) => ( {Object.keys(unitSelection).map((key) => {
<div className="flex items-center gap-2" key={key}> const isDisabled = key === "polres" && !unitSelection.polda;
<Checkbox return (
id={key} <div className="flex items-center gap-2" key={key}>
checked={ <Checkbox
unitSelection[key as keyof typeof unitSelection] id={key}
} checked={
onCheckedChange={(value) => unitSelection[key as keyof typeof unitSelection]
handleUnitChange( }
key as keyof typeof unitSelection, disabled={isDisabled}
value as boolean onCheckedChange={(value) =>
) handleUnitChange(
} key as keyof typeof unitSelection,
/> value as boolean
<Label htmlFor={key}> )
{key.charAt(0).toUpperCase() + key.slice(1)} }
</Label> />
</div> <Label htmlFor={key}>
))} {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>
@ -766,71 +807,67 @@ export default function FormTaskEdit() {
</DialogTitle> </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"> const poldaChecked = unitSelection.polda; // kontrol polda luar
<Label className="flex items-center"> const polresChecked = unitSelection.polres; // kontrol polres luar
<Checkbox
checked={checkedLevels.has(polda.id)} const isPoldaDisabled = poldaChecked; // lock checkbox polda dialog jika polda luar dicentang
onCheckedChange={() => const isPolresDisabled = polresChecked; // lock checkbox polres dialog jika polres luar dicentang
handleCheckboxChange(polda.id)
} return (
className="mr-3" <div key={polda.id} className="border p-2">
/> <Label className="flex items-center">
{polda.name} <Checkbox
<button checked={
onClick={() => toggleExpand(polda.id)} poldaChecked || checkedLevels.has(polda.id)
className="ml-2 focus:outline-none" } // auto-centang jika polda luar dicentang
> disabled={isPoldaDisabled}
{expandedPolda[polda.id] ? ( onCheckedChange={() => {
<ChevronUp size={16} /> if (isPoldaDisabled) return;
) : ( handleCheckboxChange(polda.id);
<ChevronDown size={16} /> }}
)} className="mr-3"
</button> />
</Label> {polda.name}
{expandedPolda[polda.id] && ( <button
<div className="ml-6 mt-2"> onClick={() => toggleExpand(polda.id)}
<Label className="block"> className="ml-2 focus:outline-none"
<Checkbox >
checked={polda?.subDestination?.every( {expandedPolda[polda.id] ? (
(polres: any) => <ChevronUp size={16} />
checkedLevels.has(polres.id) ) : (
)} <ChevronDown size={16} />
onCheckedChange={(isChecked) => { )}
const updatedLevels = new Set( </button>
checkedLevels </Label>
);
polda?.subDestination?.forEach( {expandedPolda[polda.id] && (
(polres: any) => { <div className="ml-6 mt-2">
if (isChecked) { {polda?.subDestination?.map((polres: any) => (
updatedLevels.add(polres.id); <Label
} else { key={polres.id}
updatedLevels.delete(polres.id); className="block mt-1"
} >
} <Checkbox
); checked={
setCheckedLevels(updatedLevels); polresChecked ||
}} checkedLevels.has(polres.id)
className="mr-2" } // auto-centang jika polres luar dicentang
/> disabled={isPolresDisabled}
Pilih Semua Polres onCheckedChange={() => {
</Label> if (isPolresDisabled) return;
{polda?.subDestination?.map((polres: any) => ( handleCheckboxChange(polres.id);
<Label key={polres.id} className="block mt-1"> }}
<Checkbox className="mr-2"
checked={checkedLevels.has(polres.id)} />
onCheckedChange={() => {polres.name}
handleCheckboxChange(polres.id) </Label>
} ))}
className="mr-2" </div>
/> )}
{polres.name} </div>
</Label> );
))} })}
</div>
)}
</div>
))}
</div> </div>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
@ -856,14 +893,15 @@ export default function FormTaskEdit() {
{t("assigment-type", { defaultValue: "Assigment Type" })}{" "} {t("assigment-type", { defaultValue: "Assigment Type" })}{" "}
</Label> </Label>
<RadioGroup <RadioGroup
defaultValue={detail.taskType.toString()} value={taskType} // ✅ controlled
onValueChange={(value) => setTaskType(String(value))} onValueChange={(value) => setTaskType(value)}
className="flex flex-wrap gap-3" className="flex flex-wrap gap-3"
> >
<RadioGroupItem value="atensi-khusus" id="khusus" /> <RadioGroupItem value="atensi-khusus" id="khusus" />
<Label htmlFor="atensi-khusus">Atensi Khusus</Label> <Label htmlFor="khusus">Atensi Khusus</Label>
<RadioGroupItem value="tugas-harian" id="harian" /> <RadioGroupItem value="tugas-harian" id="harian" />
<Label htmlFor="tugas-harian">Tugas Harian</Label> <Label htmlFor="harian">Tugas Harian</Label>
</RadioGroup> </RadioGroup>
</div> </div>
{/* RadioGroup Assignment Category */} {/* RadioGroup Assignment Category */}