From e2d0e17846b2c4de33eb3f70eae02a16add3613f Mon Sep 17 00:00:00 2001 From: Sabda Yagra Date: Mon, 6 Oct 2025 21:17:48 +0700 Subject: [PATCH] fix: all section in tenant and others --- .../admin/categories/detail/[id]/page.tsx | 5 +- .../admin/categories/update/[id]/page.tsx | 16 + .../audio-visual/components/columns.tsx | 26 +- .../audio-visual/components/table-video.tsx | 25 +- .../content/audio-visual/create/page.tsx | 3 +- .../content/image/components/columns.tsx | 8 +- .../content/image/components/table-image.tsx | 1 - .../admin/content/image/create/page.tsx | 4 +- .../admin/content/image/detail/[id]/page.tsx | 5 +- .../admin/content/image/update/[id]/page.tsx | 2 + .../settings/tenant/component/columns.tsx | 120 +++- .../tenant/component/table-user-level.tsx | 81 ++- .../settings/tenant/update/[id]/page.tsx | 9 - components/form/UserLevelsForm.tsx | 12 - .../categories/categories-detail-form.tsx | 16 +- .../categories/categories-update-form.tsx | 272 ++++++++ .../form/content/image/image-detail-form.tsx | 73 +- components/form/content/image/image-form.tsx | 7 +- .../form/content/image/image-update-form.tsx | 8 +- components/form/sign-up.tsx | 68 +- components/form/tenant/tenant-update-form.tsx | 200 +++++- hooks/hooks/use-registration.ts | 622 ++++++++++-------- hooks/use-registration.ts | 622 ++++++++++-------- lib/swal.ts | 38 ++ service/categories/categories.ts | 15 +- service/content/content.ts | 4 + service/tenant.ts | 16 +- 27 files changed, 1543 insertions(+), 735 deletions(-) create mode 100644 app/[locale]/(admin)/admin/categories/update/[id]/page.tsx delete mode 100644 app/[locale]/(admin)/admin/settings/tenant/update/[id]/page.tsx create mode 100644 components/form/categories/categories-update-form.tsx diff --git a/app/[locale]/(admin)/admin/categories/detail/[id]/page.tsx b/app/[locale]/(admin)/admin/categories/detail/[id]/page.tsx index a3ea843..10d8860 100644 --- a/app/[locale]/(admin)/admin/categories/detail/[id]/page.tsx +++ b/app/[locale]/(admin)/admin/categories/detail/[id]/page.tsx @@ -1,11 +1,12 @@ import CategoriesDetailForm from "@/components/form/categories/categories-detail-form"; import FormImageDetail from "@/components/form/content/image/image-detail-form"; +import SiteBreadcrumb from "@/components/site-breadcrumb"; const CategoriesDetailPage = async () => { return (
- {/* */} -
+ +
{" "}
diff --git a/app/[locale]/(admin)/admin/categories/update/[id]/page.tsx b/app/[locale]/(admin)/admin/categories/update/[id]/page.tsx new file mode 100644 index 0000000..a58c3fd --- /dev/null +++ b/app/[locale]/(admin)/admin/categories/update/[id]/page.tsx @@ -0,0 +1,16 @@ +import CategoriesUpdateForm from "@/components/form/categories/categories-update-form"; +import SiteBreadcrumb from "@/components/site-breadcrumb"; +import React from "react"; + +const page = () => { + return ( +
+ +
+ +
+
+ ); +}; + +export default page; diff --git a/app/[locale]/(admin)/admin/content/audio-visual/components/columns.tsx b/app/[locale]/(admin)/admin/content/audio-visual/components/columns.tsx index 9e152ad..e03ec4c 100644 --- a/app/[locale]/(admin)/admin/content/audio-visual/components/columns.tsx +++ b/app/[locale]/(admin)/admin/content/audio-visual/components/columns.tsx @@ -17,6 +17,7 @@ import withReactContent from "sweetalert2-react-content"; import { error } from "@/lib/swal"; import Link from "next/link"; import { deleteMedia } from "@/service/content"; +import { deleteArticle } from "@/service/content/content"; const useTableColumns = () => { const MySwal = withReactContent(Swal); @@ -55,12 +56,10 @@ const useTableColumns = () => { const categoryName = row.getValue("categoryName"); const categories = row.original.categories; // Handle new API structure with categories array - const displayName = categoryName || (categories && categories.length > 0 ? categories[0].title : "-"); - return ( - - {displayName} - - ); + const displayName = + categoryName || + (categories && categories.length > 0 ? categories[0].title : "-"); + return {displayName}; }, }, { @@ -191,13 +190,8 @@ const useTableColumns = () => { const MySwal = withReactContent(Swal); async function doDelete(id: any) { - // loading(); - const data = { - id, - }; - - const response = await deleteMedia(data); - + const data = { id }; + const response = await deleteArticle(id); if (response?.error) { error(response.message); return false; @@ -275,8 +269,8 @@ const useTableColumns = () => { Edit */} - {(Number(row.original.uploadedById) === Number(userId) || - isMabesApprover) && ( + {/* {(Number(row.original.uploadedById) === Number(userId) || + isMabesApprover) && ( */} @@ -285,7 +279,7 @@ const useTableColumns = () => { Edit - )} + {/* )} */} handleDeleteMedia(row.original.id)} className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-white rounded-none" diff --git a/app/[locale]/(admin)/admin/content/audio-visual/components/table-video.tsx b/app/[locale]/(admin)/admin/content/audio-visual/components/table-video.tsx index 68ace44..d75f01b 100644 --- a/app/[locale]/(admin)/admin/content/audio-visual/components/table-video.tsx +++ b/app/[locale]/(admin)/admin/content/audio-visual/components/table-video.tsx @@ -176,13 +176,12 @@ const TableVideo = () => { : ""; try { - // Using the new interface-based approach for video content const filters: ArticleFilters = { - page: page, + page, totalPage: Number(showData), title: search || undefined, categoryId: categoryFilter ? Number(categoryFilter) : undefined, - typeId: 2, // video content type + typeId: 2, // ✅ untuk video statusId: statusFilter?.length > 0 ? Number(statusFilter[0]) : undefined, startDate: formattedStartDate || undefined, @@ -197,28 +196,28 @@ const TableVideo = () => { item.no = (page - 1) * Number(showData) + index + 1; }); setDataTable(data); - setTotalData(data.length); - setTotalPage(Math.ceil(data.length / Number(showData))); + setTotalData(res?.data?.meta?.count || data.length); + setTotalPage( + Math.ceil((res?.data?.meta?.count || data.length) / Number(showData)) + ); } else if (Array.isArray(data?.content)) { const contentData = data.content; contentData.forEach((item: any, index: number) => { item.no = (page - 1) * Number(showData) + index + 1; }); setDataTable(contentData); - setTotalData(data?.totalElements ?? contentData.length); + setTotalData(res?.data?.meta?.count || contentData.length); setTotalPage( - data?.totalPages ?? Math.ceil(contentData.length / Number(showData)) + Math.ceil( + (res?.data?.meta?.count || contentData.length) / Number(showData) + ) ); } else { setDataTable([]); - setTotalData(0); - setTotalPage(1); } - } catch (error) { - console.error("Error fetching tasks:", error); + } catch (err) { + console.error("Error fetching tasks:", err); setDataTable([]); - setTotalData(0); - setTotalPage(1); } } diff --git a/app/[locale]/(admin)/admin/content/audio-visual/create/page.tsx b/app/[locale]/(admin)/admin/content/audio-visual/create/page.tsx index efbd24d..bb682d9 100644 --- a/app/[locale]/(admin)/admin/content/audio-visual/create/page.tsx +++ b/app/[locale]/(admin)/admin/content/audio-visual/create/page.tsx @@ -4,7 +4,8 @@ import SiteBreadcrumb from "@/components/site-breadcrumb"; const VideoCreatePage = async () => { return (
-
+ +
diff --git a/app/[locale]/(admin)/admin/content/image/components/columns.tsx b/app/[locale]/(admin)/admin/content/image/components/columns.tsx index af11de1..00afed8 100644 --- a/app/[locale]/(admin)/admin/content/image/components/columns.tsx +++ b/app/[locale]/(admin)/admin/content/image/components/columns.tsx @@ -14,11 +14,11 @@ import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { format } from "date-fns"; import { useRouter } from "next/navigation"; -import { deleteMedia } from "@/service/content/content"; import { error } from "@/lib/swal"; import Swal from "sweetalert2"; import withReactContent from "sweetalert2-react-content"; import Link from "next/link"; +import { deleteArticle } from "@/service/content/content"; const useTableColumns = () => { const MySwal = withReactContent(Swal); @@ -172,7 +172,7 @@ const useTableColumns = () => { async function doDelete(id: any) { const data = { id }; - const response = await deleteMedia(data); + const response = await deleteArticle(id); if (response?.error) { error(response.message); return false; @@ -240,14 +240,14 @@ const useTableColumns = () => { View - {(Number(row.original.uploadedById) === Number(userId) || isMabesApprover) && ( + {/* {(Number(row.original.uploadedById) === Number(userId) || isMabesApprover) && ( */} Edit - )} + {/* )} */} handleDeleteMedia(row.original.id)} className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-white rounded-none" diff --git a/app/[locale]/(admin)/admin/content/image/components/table-image.tsx b/app/[locale]/(admin)/admin/content/image/components/table-image.tsx index 0b90c73..4f4f4ed 100644 --- a/app/[locale]/(admin)/admin/content/image/components/table-image.tsx +++ b/app/[locale]/(admin)/admin/content/image/components/table-image.tsx @@ -52,7 +52,6 @@ import { useParams, useRouter, useSearchParams } from "next/navigation"; import TablePagination from "@/components/table/table-pagination"; import { - deleteMedia, listDataImage, listArticles, listArticlesWithFilters, diff --git a/app/[locale]/(admin)/admin/content/image/create/page.tsx b/app/[locale]/(admin)/admin/content/image/create/page.tsx index f551973..52c7c8e 100644 --- a/app/[locale]/(admin)/admin/content/image/create/page.tsx +++ b/app/[locale]/(admin)/admin/content/image/create/page.tsx @@ -4,8 +4,8 @@ import SiteBreadcrumb from "@/components/site-breadcrumb"; const ImageCreatePage = async () => { return (
- {/* */} -
+ +
diff --git a/app/[locale]/(admin)/admin/content/image/detail/[id]/page.tsx b/app/[locale]/(admin)/admin/content/image/detail/[id]/page.tsx index 6562882..51bfcac 100644 --- a/app/[locale]/(admin)/admin/content/image/detail/[id]/page.tsx +++ b/app/[locale]/(admin)/admin/content/image/detail/[id]/page.tsx @@ -1,10 +1,11 @@ import FormImageDetail from "@/components/form/content/image/image-detail-form"; +import SiteBreadcrumb from "@/components/site-breadcrumb"; const ImageDetailPage = async () => { return (
- {/* */} -
+ +
diff --git a/app/[locale]/(admin)/admin/content/image/update/[id]/page.tsx b/app/[locale]/(admin)/admin/content/image/update/[id]/page.tsx index cd94205..12382a6 100644 --- a/app/[locale]/(admin)/admin/content/image/update/[id]/page.tsx +++ b/app/[locale]/(admin)/admin/content/image/update/[id]/page.tsx @@ -1,8 +1,10 @@ import FormImageUpdate from "@/components/form/content/image/image-update-form"; +import SiteBreadcrumb from "@/components/site-breadcrumb"; const ImageUpdatePage = async () => { return (
+
diff --git a/app/[locale]/(admin)/admin/settings/tenant/component/columns.tsx b/app/[locale]/(admin)/admin/settings/tenant/component/columns.tsx index 87b645d..695fb96 100644 --- a/app/[locale]/(admin)/admin/settings/tenant/component/columns.tsx +++ b/app/[locale]/(admin)/admin/settings/tenant/component/columns.tsx @@ -11,17 +11,21 @@ import { DropdownMenuItem, } from "@/components/ui/dropdown-menu"; import { Button } from "@/components/ui/button"; -import { Badge } from "@/components/ui/badge"; -import { format } from "date-fns"; import { useRouter } from "next/navigation"; -import { deleteMedia } from "@/service/content/content"; import { error } from "@/lib/swal"; import Swal from "sweetalert2"; import withReactContent from "sweetalert2-react-content"; -import Link from "next/link"; import { deleteUserLevel } from "@/service/tenant"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, +} from "@/components/ui/dialog"; +import { getUserLevelDetail } from "@/service/tenant"; -const useTableColumns = () => { +const useTableColumns = (onEdit?: (data: any) => void) => { const MySwal = withReactContent(Swal); const userLevelId = getCookiesDecrypt("ulie"); @@ -185,6 +189,23 @@ const useTableColumns = () => { cell: ({ row }) => { const router = useRouter(); const MySwal = withReactContent(Swal); + const [isDialogOpen, setIsDialogOpen] = React.useState(false); + const [detailData, setDetailData] = React.useState(null); + + const handleView = async (id: number) => { + try { + const res = await getUserLevelDetail(id); + if (!res?.error) { + setDetailData(res?.data?.data); + setIsDialogOpen(true); + } else { + error(res?.message || "Gagal memuat detail user level"); + } + } catch (err) { + console.error("View error:", err); + error("Terjadi kesalahan saat memuat data."); + } + }; async function doDelete(id: number) { const response = await deleteUserLevel(id); @@ -242,7 +263,6 @@ const useTableColumns = () => { const userId = getCookiesDecrypt("uie"); const userLevelId = getCookiesDecrypt("ulie"); const roleId = getCookiesDecrypt("urie"); - React.useEffect(() => { if (userLevelId !== undefined && roleId !== undefined) { setIsMabesApprover( @@ -251,43 +271,91 @@ const useTableColumns = () => { } }, [userLevelId, roleId]); + const [open, setOpen] = React.useState(false); + return ( - + - {/* - - - View - - */} - {/* {(Number(row.original.uploadedById) === Number(userId) || isMabesApprover) && ( */} - - - - Edit - - - {/* )} */} handleDeleteMedia(row.original.id)} + onClick={() => { + setOpen(false); + handleView(row.original.id); + }} + className="p-2 border-b text-default-700 rounded-none cursor-pointer" + > + + View + + { + setOpen(false); // ⬅️ tutup dropdown manual + onEdit?.(row.original); + }} + className="p-2 border-b text-default-700 rounded-none cursor-pointer" + > + + Edit + + + { + setOpen(false); // ⬅️ pastikan dropdown tertutup sebelum alert + handleDeleteMedia(row.original.id); + }} className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-white rounded-none" > Delete + {/* ✅ Dialog Detail User Level */} + + + + Detail User Level + + Informasi lengkap dari user level ID: {detailData?.id} + + + + {detailData ? ( +
+

+ Name:{" "} + {detailData.aliasName} +

+

+ Group:{" "} + {detailData.group} +

+

+ Parent Level:{" "} + {detailData.parentLevelName || "-"} +

+

+ Created At:{" "} + {detailData.createdAt + ? new Date(detailData.createdAt).toLocaleString("id-ID") + : "-"} +

+
+ ) : ( +

Memuat data...

+ )} + +
+ +
+
+
); }, diff --git a/app/[locale]/(admin)/admin/settings/tenant/component/table-user-level.tsx b/app/[locale]/(admin)/admin/settings/tenant/component/table-user-level.tsx index 32725cd..da9d1a3 100644 --- a/app/[locale]/(admin)/admin/settings/tenant/component/table-user-level.tsx +++ b/app/[locale]/(admin)/admin/settings/tenant/component/table-user-level.tsx @@ -48,6 +48,9 @@ import { } from "@tanstack/react-table"; import TablePagination from "@/components/table/table-pagination"; import useTableColumns from "./columns"; +import TenantUpdateForm from "@/components/form/tenant/tenant-update-form"; +import { errorAutoClose, successAutoClose } from "@/lib/swal"; +import { close, loading } from "@/config/swal"; function TenantSettingsContentTable() { const [activeTab, setActiveTab] = useState("workflows"); @@ -59,6 +62,14 @@ function TenantSettingsContentTable() { const [isEditingWorkflow, setIsEditingWorkflow] = useState(false); const { checkWorkflowStatus } = useWorkflowStatusCheck(); const { showWorkflowModal } = useWorkflowModal(); + const [isEditDialogOpen, setIsEditDialogOpen] = React.useState(false); + const [editingUserLevel, setEditingUserLevel] = + React.useState(null); + + const handleEditUserLevel = (userLevel: UserLevel) => { + setEditingUserLevel(userLevel); + setIsEditDialogOpen(true); + }; React.useEffect(() => { loadData(); @@ -90,6 +101,8 @@ function TenantSettingsContentTable() { }); setUserLevels(data); console.log("LLL", data); + setTotalData(data.length); + setTotalPage(Math.ceil(data.length / Number(showData))); } } catch (error) { console.error("Error loading data:", error); @@ -107,19 +120,29 @@ function TenantSettingsContentTable() { const handleUserLevelSave = async (data: UserLevelsCreateRequest) => { try { + loading(); + const response = await createUserLevel(data); + close(); + if (response?.error) { - console.error("Error creating user level:", response?.message); - } else { - console.log("User level created successfully:", response); + errorAutoClose(response.message || "Gagal membuat user level."); + return; } + + successAutoClose("User level berhasil dibuat."); + + setIsUserLevelDialogOpen(false); + + setTimeout(async () => { + await loadData(); + }, 3000); } catch (error) { + close(); + errorAutoClose("Terjadi kesalahan saat membuat user level."); console.error("Error creating user level:", error); } - - setIsUserLevelDialogOpen(false); - await loadData(); }; const handleBulkUserLevelSave = async (data: UserLevelsCreateRequest[]) => { @@ -127,7 +150,10 @@ function TenantSettingsContentTable() { await loadData(); }; - const columns = useTableColumns(); + const columns = React.useMemo( + () => useTableColumns((data) => handleEditUserLevel(data)), + [] + ); const [showData, setShowData] = React.useState("10"); const [page, setPage] = React.useState(1); const [totalPage, setTotalPage] = React.useState(1); @@ -759,7 +785,7 @@ function TenantSettingsContentTable() { )} - + {table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => ( @@ -805,6 +831,45 @@ function TenantSettingsContentTable() { )}
+ + + + + {editingUserLevel + ? `Edit User Level: ${editingUserLevel.name}` + : "Edit User Level"} + + + + {editingUserLevel ? ( + { + setIsEditDialogOpen(false); + setEditingUserLevel(null); + await loadData(); + }} + onCancel={() => { + setIsEditDialogOpen(false); + setEditingUserLevel(null); + }} + /> + ) : ( +

Loading...

+ )} +
+
+ { - return ( -
page
- ) -} - -export default page \ No newline at end of file diff --git a/components/form/UserLevelsForm.tsx b/components/form/UserLevelsForm.tsx index 458e3bc..82538b9 100644 --- a/components/form/UserLevelsForm.tsx +++ b/components/form/UserLevelsForm.tsx @@ -44,28 +44,21 @@ export const UserLevelsForm: React.FC = ({ isActive: true, }); - // Bulk form state const [bulkFormData, setBulkFormData] = useState([]); - - // API data const [userLevels, setUserLevels] = useState([]); const [provinces, setProvinces] = useState([]); - - // UI state const [errors, setErrors] = useState>({}); const [isSubmitting, setIsSubmitting] = useState(false); const [expandedHierarchy, setExpandedHierarchy] = useState(false); const [isLoadingData, setIsLoadingData] = useState(true); const [activeTab, setActiveTab] = useState(mode === "single" ? "basic" : "bulk"); - // Load initial data useEffect(() => { if (initialData) { setFormData(initialData); } }, [initialData]); - // Load API data useEffect(() => { const loadData = async () => { try { @@ -86,7 +79,6 @@ export const UserLevelsForm: React.FC = ({ loadData(); }, []); - // Validation const validateForm = (data: UserLevelsCreateRequest): Record => { const newErrors: Record = {}; @@ -133,7 +125,6 @@ export const UserLevelsForm: React.FC = ({ return isValid; }; - // Form handlers const handleFieldChange = (field: keyof UserLevelsCreateRequest, value: any) => { setFormData(prev => ({ ...prev, [field]: value })); if (errors[field]) { @@ -151,11 +142,8 @@ export const UserLevelsForm: React.FC = ({ const handleTabChange = (value: string) => { setActiveTab(value); - // Update mode based on active tab if (value === "bulk") { - // Mode will be determined by activeTab in handleSubmit } else { - // Mode will be determined by activeTab in handleSubmit } }; diff --git a/components/form/categories/categories-detail-form.tsx b/components/form/categories/categories-detail-form.tsx index 28fb13c..f9f9428 100644 --- a/components/form/categories/categories-detail-form.tsx +++ b/components/form/categories/categories-detail-form.tsx @@ -49,9 +49,9 @@ export default function CategoriesDetailForm() { return (
{detail ? ( -
- {/* MAIN FORM */} - +
+ {/* MAIN FORM */} +

Form Category Detail

{/* Title */} @@ -79,7 +79,7 @@ export default function CategoriesDetailForm() { {/* Thumbnail */}
- + Category Thumbnail {/* Tags */} -
+
-
+
{detail?.tags?.length > 0 ? ( detail.tags.map((tag: string, i: number) => ( @@ -106,7 +106,7 @@ export default function CategoriesDetailForm() { {/* SIDEBAR */} -
+
{/* Creator */}
@@ -155,7 +155,7 @@ export default function CategoriesDetailForm() { diff --git a/components/form/categories/categories-update-form.tsx b/components/form/categories/categories-update-form.tsx new file mode 100644 index 0000000..38a75a7 --- /dev/null +++ b/components/form/categories/categories-update-form.tsx @@ -0,0 +1,272 @@ +"use client"; +import React, { useEffect, useState, ChangeEvent } from "react"; +import { useParams, useRouter } from "next/navigation"; +import { Card } from "@/components/ui/card"; +import { Label } from "@/components/ui/label"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { Checkbox } from "@/components/ui/checkbox"; +import { formatDateToIndonesian } from "@/utils/globals"; +import { + getArticleCategoryDetail, + updateArticleCategory, + uploadArticleCategoryThumbnail, +} from "@/service/categories/categories"; +import withReactContent from "sweetalert2-react-content"; +import Swal from "sweetalert2"; + +type CategoryDetail = { + id: number; + title: string; + description: string; + thumbnailUrl: string; + slug: string | null; + tags: string[]; + parentId: number; + createdById: number; + createdByName?: string; + statusId: number; + isPublish: boolean; + publishedAt: string | null; + isActive: boolean; + createdAt: string; + updatedAt: string; +}; + +export default function CategoriesUpdateForm() { + const { id } = useParams() as { id: string }; + const router = useRouter(); + const MySwal = withReactContent(Swal); + + const [formData, setFormData] = useState(null); + const [isSubmitting, setIsSubmitting] = useState(false); + const [thumbnailPreview, setThumbnailPreview] = useState(null); + const [thumbnailFile, setThumbnailFile] = useState(null); + const [isUploading, setIsUploading] = useState(false); + + useEffect(() => { + async function init() { + if (id) { + try { + const res = await getArticleCategoryDetail(Number(id)); + setFormData(res?.data?.data); + setThumbnailPreview(res?.data?.data?.thumbnailUrl || null); + } catch (err) { + console.error("Error fetching category detail:", err); + } + } + } + init(); + }, [id]); + + const handleChange = ( + e: React.ChangeEvent + ) => { + const { name, value } = e.target; + setFormData((prev) => (prev ? { ...prev, [name]: value } : prev)); + }; + + const handleCheckboxChange = (name: string, value: boolean) => { + setFormData((prev) => (prev ? { ...prev, [name]: value } : prev)); + }; + + const handleThumbnailChange = (e: ChangeEvent) => { + const file = e.target.files?.[0]; + if (!file) return; + setThumbnailFile(file); + setThumbnailPreview(URL.createObjectURL(file)); + }; + + const handleThumbnailUpload = async (categoryId: number) => { + if (!thumbnailFile) return null; + setIsUploading(true); + + try { + const formData = new FormData(); + formData.append("files", thumbnailFile); // sesuai Swagger: "files" + + const res = await uploadArticleCategoryThumbnail(categoryId, formData); + + if (!res?.error) { + MySwal.fire("Sukses", "Thumbnail berhasil diunggah!", "success"); + return res?.data?.file_url || null; + } else { + MySwal.fire( + "Error", + res?.message || "Gagal mengunggah thumbnail", + "error" + ); + return null; + } + } catch (error) { + console.error("Upload error:", error); + MySwal.fire("Error", "Gagal mengunggah file", "error"); + return null; + } finally { + setIsUploading(false); + } + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (!formData) return; + + setIsSubmitting(true); + + try { + // 🔹 upload thumbnail jika ada + if (thumbnailFile) { + await handleThumbnailUpload(Number(id)); + } + + // 🔹 hanya kirim data mandatory + const payload = { + id: formData.id, + description: formData.description || "", + statusId: formData.statusId || 1, + title: formData.title || "", + }; + + const res = await updateArticleCategory(Number(id), payload); + + if (!res?.error) { + MySwal.fire({ + icon: "success", + title: "Sukses!", + text: "Kategori berhasil diperbarui.", + }); + router.push("/admin/categories"); + } else { + MySwal.fire({ + icon: "error", + title: "Gagal", + text: res.message || "Gagal memperbarui kategori", + }); + } + } catch (err) { + console.error("Update error:", err); + MySwal.fire({ + icon: "error", + title: "Error!", + text: "Terjadi kesalahan server.", + }); + } finally { + setIsSubmitting(false); + } + }; + + return ( + + {formData ? ( +
+ {/* MAIN FORM */} + +

Edit Category

+ + {/* Title */} +
+ + +
+ + {/* Description */} +
+ +