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

View File

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

View File

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