diff --git a/app/(admin)/admin/content/audio-visual/components/audio-visual-tabs.tsx b/app/(admin)/admin/content/audio-visual/components/audio-visual-tabs.tsx index b748ae2..fe8b8a1 100644 --- a/app/(admin)/admin/content/audio-visual/components/audio-visual-tabs.tsx +++ b/app/(admin)/admin/content/audio-visual/components/audio-visual-tabs.tsx @@ -42,7 +42,7 @@ const AudioVisualTabs = () => { - + diff --git a/app/(admin)/admin/content/audio-visual/components/pending-approval-table.tsx b/app/(admin)/admin/content/audio-visual/components/pending-approval-table.tsx index c202289..c04215f 100644 --- a/app/(admin)/admin/content/audio-visual/components/pending-approval-table.tsx +++ b/app/(admin)/admin/content/audio-visual/components/pending-approval-table.tsx @@ -42,7 +42,11 @@ import TablePagination from "@/components/table/table-pagination"; import { listPendingApproval, PendingApprovalData } from "@/service/content/content"; import usePendingApprovalColumns from "./pending-approval-columns"; -const PendingApprovalTable = () => { +type PendingApprovalTableProps = { + typeId?: number; +}; + +const PendingApprovalTable = ({ typeId }: PendingApprovalTableProps) => { const searchParams = useSearchParams(); const [dataTable, setDataTable] = React.useState([]); const [totalData, setTotalData] = React.useState(0); @@ -111,7 +115,7 @@ const PendingApprovalTable = () => { async function fetchPendingData() { try { console.log("fetchPendingData: page =", page, "showData =", showData, "Number(showData) =", Number(showData)); - const res = await listPendingApproval(page, Number(showData)); + const res = await listPendingApproval(page, Number(showData), typeId); if (res && !res.error && res.data) { // Filter data based on search if provided diff --git a/app/(admin)/admin/content/audio-visual/components/table-video.tsx b/app/(admin)/admin/content/audio-visual/components/table-video.tsx index 3345c7e..68ace44 100644 --- a/app/(admin)/admin/content/audio-visual/components/table-video.tsx +++ b/app/(admin)/admin/content/audio-visual/components/table-video.tsx @@ -39,12 +39,12 @@ import columns from "./columns"; import { Label } from "@/components/ui/label"; import { format } from "date-fns"; import useTableColumns from "./columns"; -import { - listEnableCategory, +import { + listEnableCategory, listDataVideo, listArticles, listArticlesWithFilters, - ArticleFilters + ArticleFilters, } from "@/service/content"; import { SortingState, @@ -174,6 +174,7 @@ const TableVideo = () => { const formattedEndDate = endDate ? format(new Date(endDate), "yyyy-MM-dd") : ""; + try { // Using the new interface-based approach for video content const filters: ArticleFilters = { @@ -182,15 +183,15 @@ const TableVideo = () => { title: search || undefined, categoryId: categoryFilter ? Number(categoryFilter) : undefined, typeId: 2, // video content type - statusId: statusFilter?.length > 0 ? Number(statusFilter[0]) : undefined, + statusId: + statusFilter?.length > 0 ? Number(statusFilter[0]) : undefined, startDate: formattedStartDate || undefined, endDate: formattedEndDate || undefined, }; const res = await listArticlesWithFilters(filters); - const data = res?.data?.data; - // Handle new articles API response structure + if (Array.isArray(data)) { data.forEach((item: any, index: number) => { item.no = (page - 1) * Number(showData) + index + 1; @@ -198,21 +199,80 @@ const TableVideo = () => { setDataTable(data); setTotalData(data.length); setTotalPage(Math.ceil(data.length / Number(showData))); - } else { - // Fallback to old structure if API still returns old format - const contentData = data?.content; + } 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); - setTotalPage(data?.totalPages); + setTotalData(data?.totalElements ?? contentData.length); + setTotalPage( + data?.totalPages ?? Math.ceil(contentData.length / Number(showData)) + ); + } else { + setDataTable([]); + setTotalData(0); + setTotalPage(1); } } catch (error) { console.error("Error fetching tasks:", error); + setDataTable([]); + setTotalData(0); + setTotalPage(1); } } + // async function fetchData() { + // const formattedStartDate = startDate + // ? format(new Date(startDate), "yyyy-MM-dd") + // : ""; + // const formattedEndDate = endDate + // ? format(new Date(endDate), "yyyy-MM-dd") + // : ""; + // try { + // // Using the new interface-based approach for video content + // const filters: ArticleFilters = { + // page: page, + // totalPage: Number(showData), + // title: search || undefined, + // categoryId: categoryFilter ? Number(categoryFilter) : undefined, + // typeId: 2, // video content type + // statusId: + // statusFilter?.length > 0 ? Number(statusFilter[0]) : undefined, + // startDate: formattedStartDate || undefined, + // endDate: formattedEndDate || undefined, + // }; + + // const res = await listArticlesWithFilters(filters); + + // const data = res?.data?.data; + // // Handle new articles API response structure + // if (Array.isArray(data)) { + // data.forEach((item: any, index: number) => { + // item.no = (page - 1) * Number(showData) + index + 1; + // }); + // setDataTable(data); + // setTotalData(data.length); + // setTotalPage(Math.ceil(data.length / Number(showData))); + // } else { + // // Fallback to old structure if API still returns old format + // 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); + // setTotalPage( + // data?.totalPages ?? Math.ceil(contentData.length / Number(showData)) + // ); + // // setTotalData(data?.totalElements); + // // setTotalPage(data?.totalPages); + // } + // } catch (error) { + // console.error("Error fetching tasks:", error); + // } + // } + const handleSearch = (e: React.ChangeEvent) => { setSearch(e.target.value); // Perbarui state search table.getColumn("title")?.setFilterValue(e.target.value); // Set filter tabel diff --git a/app/(admin)/admin/content/audio/components/audio-tabs.tsx b/app/(admin)/admin/content/audio/components/audio-tabs.tsx index 4fe7595..efc5af8 100644 --- a/app/(admin)/admin/content/audio/components/audio-tabs.tsx +++ b/app/(admin)/admin/content/audio/components/audio-tabs.tsx @@ -42,7 +42,7 @@ const AudioTabs = () => { - + diff --git a/app/(admin)/admin/content/audio/components/pending-approval-table.tsx b/app/(admin)/admin/content/audio/components/pending-approval-table.tsx index c202289..c04215f 100644 --- a/app/(admin)/admin/content/audio/components/pending-approval-table.tsx +++ b/app/(admin)/admin/content/audio/components/pending-approval-table.tsx @@ -42,7 +42,11 @@ import TablePagination from "@/components/table/table-pagination"; import { listPendingApproval, PendingApprovalData } from "@/service/content/content"; import usePendingApprovalColumns from "./pending-approval-columns"; -const PendingApprovalTable = () => { +type PendingApprovalTableProps = { + typeId?: number; +}; + +const PendingApprovalTable = ({ typeId }: PendingApprovalTableProps) => { const searchParams = useSearchParams(); const [dataTable, setDataTable] = React.useState([]); const [totalData, setTotalData] = React.useState(0); @@ -111,7 +115,7 @@ const PendingApprovalTable = () => { async function fetchPendingData() { try { console.log("fetchPendingData: page =", page, "showData =", showData, "Number(showData) =", Number(showData)); - const res = await listPendingApproval(page, Number(showData)); + const res = await listPendingApproval(page, Number(showData), typeId); if (res && !res.error && res.data) { // Filter data based on search if provided diff --git a/app/(admin)/admin/content/audio/components/table-audio.tsx b/app/(admin)/admin/content/audio/components/table-audio.tsx index 1630de2..662ccaa 100644 --- a/app/(admin)/admin/content/audio/components/table-audio.tsx +++ b/app/(admin)/admin/content/audio/components/table-audio.tsx @@ -171,51 +171,106 @@ const TableAudio = () => { }); }; - async function fetchData() { - const formattedStartDate = startDate - ? format(new Date(startDate), "yyyy-MM-dd") - : ""; - const formattedEndDate = endDate - ? format(new Date(endDate), "yyyy-MM-dd") - : ""; - try { - // Using the new interface-based approach for audio content - const filters: ArticleFilters = { - page: page, - totalPage: Number(showData), - title: search || undefined, - categoryId: categoryFilter ? Number(categoryFilter) : undefined, - typeId: 4, // audio content type - statusId: statusFilter?.length > 0 ? Number(statusFilter[0]) : undefined, - startDate: formattedStartDate || undefined, - endDate: formattedEndDate || undefined, - }; - - const res = await listArticlesWithFilters(filters); - - const data = res?.data?.data; - // Handle new articles API response structure - if (Array.isArray(data)) { - data.forEach((item: any, index: number) => { - item.no = (page - 1) * Number(showData) + index + 1; - }); - setDataTable(data); - setTotalData(data.length); - setTotalPage(Math.ceil(data.length / Number(showData))); - } else { - // Fallback to old structure if API still returns old format - const contentData = data?.content; - contentData.forEach((item: any, index: number) => { - item.no = (page - 1) * Number(showData) + index + 1; - }); - setDataTable(contentData); - setTotalData(data?.totalElements); - setTotalPage(data?.totalPages); + async function fetchData() { + const formattedStartDate = startDate + ? format(new Date(startDate), "yyyy-MM-dd") + : ""; + const formattedEndDate = endDate + ? format(new Date(endDate), "yyyy-MM-dd") + : ""; + + try { + // Using the new interface-based approach for video content + const filters: ArticleFilters = { + page: page, + totalPage: Number(showData), + title: search || undefined, + categoryId: categoryFilter ? Number(categoryFilter) : undefined, + typeId: 2, // video content type + statusId: + statusFilter?.length > 0 ? Number(statusFilter[0]) : undefined, + startDate: formattedStartDate || undefined, + endDate: formattedEndDate || undefined, + }; + + const res = await listArticlesWithFilters(filters); + const data = res?.data?.data; + + if (Array.isArray(data)) { + data.forEach((item: any, index: number) => { + item.no = (page - 1) * Number(showData) + index + 1; + }); + setDataTable(data); + setTotalData(data.length); + setTotalPage(Math.ceil(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); + setTotalPage( + data?.totalPages ?? Math.ceil(contentData.length / Number(showData)) + ); + } else { + setDataTable([]); + setTotalData(0); + setTotalPage(1); + } + } catch (error) { + console.error("Error fetching tasks:", error); + setDataTable([]); + setTotalData(0); + setTotalPage(1); } - } catch (error) { - console.error("Error fetching tasks:", error); } - } + + // async function fetchData() { + // const formattedStartDate = startDate + // ? format(new Date(startDate), "yyyy-MM-dd") + // : ""; + // const formattedEndDate = endDate + // ? format(new Date(endDate), "yyyy-MM-dd") + // : ""; + // try { + // // Using the new interface-based approach for audio content + // const filters: ArticleFilters = { + // page: page, + // totalPage: Number(showData), + // title: search || undefined, + // categoryId: categoryFilter ? Number(categoryFilter) : undefined, + // typeId: 4, // audio content type + // statusId: statusFilter?.length > 0 ? Number(statusFilter[0]) : undefined, + // startDate: formattedStartDate || undefined, + // endDate: formattedEndDate || undefined, + // }; + + // const res = await listArticlesWithFilters(filters); + + // const data = res?.data?.data; + // // Handle new articles API response structure + // if (Array.isArray(data)) { + // data.forEach((item: any, index: number) => { + // item.no = (page - 1) * Number(showData) + index + 1; + // }); + // setDataTable(data); + // setTotalData(data.length); + // setTotalPage(Math.ceil(data.length / Number(showData))); + // } else { + // // Fallback to old structure if API still returns old format + // const contentData = data?.content; + // contentData.forEach((item: any, index: number) => { + // item.no = (page - 1) * Number(showData) + index + 1; + // }); + // setDataTable(contentData); + // setTotalData(data?.totalElements); + // setTotalPage(data?.totalPages); + // } + // } catch (error) { + // console.error("Error fetching tasks:", error); + // } + // } const handleSearch = (e: React.ChangeEvent) => { setSearch(e.target.value); // Perbarui state search diff --git a/app/(admin)/admin/content/document/components/document-tabs.tsx b/app/(admin)/admin/content/document/components/document-tabs.tsx index 7774889..aaee1cb 100644 --- a/app/(admin)/admin/content/document/components/document-tabs.tsx +++ b/app/(admin)/admin/content/document/components/document-tabs.tsx @@ -42,7 +42,7 @@ const DocumentTabs = () => { - + diff --git a/app/(admin)/admin/content/document/components/pending-approval-table.tsx b/app/(admin)/admin/content/document/components/pending-approval-table.tsx index 21102c5..79563c8 100644 --- a/app/(admin)/admin/content/document/components/pending-approval-table.tsx +++ b/app/(admin)/admin/content/document/components/pending-approval-table.tsx @@ -42,7 +42,11 @@ import TablePagination from "@/components/table/table-pagination"; import { listPendingApproval, PendingApprovalData } from "@/service/content/content"; import usePendingApprovalColumns from "./pending-approval-columns"; -const PendingApprovalTable = () => { +type PendingApprovalTableProps = { + typeId?: number; +}; + +const PendingApprovalTable = ({ typeId }: PendingApprovalTableProps) => { const searchParams = useSearchParams(); const [dataTable, setDataTable] = React.useState([]); const [totalData, setTotalData] = React.useState(0); @@ -95,7 +99,7 @@ const PendingApprovalTable = () => { async function fetchPendingData() { try { - const res = await listPendingApproval(page, Number(showData)); + const res = await listPendingApproval(page, Number(showData), typeId); if (res && !res.error && res.data) { // Filter data based on search if provided diff --git a/app/(admin)/admin/content/document/components/table-teks.tsx b/app/(admin)/admin/content/document/components/table-teks.tsx index d2a4d6e..65b2697 100644 --- a/app/(admin)/admin/content/document/components/table-teks.tsx +++ b/app/(admin)/admin/content/document/components/table-teks.tsx @@ -177,23 +177,24 @@ const TableTeks = () => { const formattedEndDate = endDate ? format(new Date(endDate), "yyyy-MM-dd") : ""; + try { - // Using the new interface-based approach for cleaner code + // Using the new interface-based approach for video content const filters: ArticleFilters = { page: page, totalPage: Number(showData), title: search || undefined, categoryId: categoryFilter ? Number(categoryFilter) : undefined, - typeId: 3, // text content type - statusId: statusFilter?.length > 0 ? Number(statusFilter[0]) : undefined, + typeId: 2, // video content type + statusId: + statusFilter?.length > 0 ? Number(statusFilter[0]) : undefined, startDate: formattedStartDate || undefined, endDate: formattedEndDate || undefined, }; const res = await listArticlesWithFilters(filters); - const data = res?.data?.data; - // Handle new articles API response structure + if (Array.isArray(data)) { data.forEach((item: any, index: number) => { item.no = (page - 1) * Number(showData) + index + 1; @@ -201,21 +202,75 @@ const TableTeks = () => { setDataTable(data); setTotalData(data.length); setTotalPage(Math.ceil(data.length / Number(showData))); - } else { - // Fallback to old structure if API still returns old format - const contentData = data?.content; + } 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); - setTotalPage(data?.totalPages); + setTotalData(data?.totalElements ?? contentData.length); + setTotalPage( + data?.totalPages ?? Math.ceil(contentData.length / Number(showData)) + ); + } else { + setDataTable([]); + setTotalData(0); + setTotalPage(1); } } catch (error) { console.error("Error fetching tasks:", error); + setDataTable([]); + setTotalData(0); + setTotalPage(1); } } + // async function fetchData() { + // const formattedStartDate = startDate + // ? format(new Date(startDate), "yyyy-MM-dd") + // : ""; + // const formattedEndDate = endDate + // ? format(new Date(endDate), "yyyy-MM-dd") + // : ""; + // try { + // // Using the new interface-based approach for cleaner code + // const filters: ArticleFilters = { + // page: page, + // totalPage: Number(showData), + // title: search || undefined, + // categoryId: categoryFilter ? Number(categoryFilter) : undefined, + // typeId: 3, // text content type + // statusId: statusFilter?.length > 0 ? Number(statusFilter[0]) : undefined, + // startDate: formattedStartDate || undefined, + // endDate: formattedEndDate || undefined, + // }; + + // const res = await listArticlesWithFilters(filters); + + // const data = res?.data?.data; + // // Handle new articles API response structure + // if (Array.isArray(data)) { + // data.forEach((item: any, index: number) => { + // item.no = (page - 1) * Number(showData) + index + 1; + // }); + // setDataTable(data); + // setTotalData(data.length); + // setTotalPage(Math.ceil(data.length / Number(showData))); + // } else { + // // Fallback to old structure if API still returns old format + // const contentData = data?.content; + // contentData.forEach((item: any, index: number) => { + // item.no = (page - 1) * Number(showData) + index + 1; + // }); + // setDataTable(contentData); + // setTotalData(data?.totalElements); + // setTotalPage(data?.totalPages); + // } + // } catch (error) { + // console.error("Error fetching tasks:", error); + // } + // } + const handleSearch = (e: React.ChangeEvent) => { setSearch(e.target.value); // Perbarui state search table.getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel diff --git a/app/(admin)/admin/content/image/components/image-tabs.tsx b/app/(admin)/admin/content/image/components/image-tabs.tsx index 87605aa..1a48136 100644 --- a/app/(admin)/admin/content/image/components/image-tabs.tsx +++ b/app/(admin)/admin/content/image/components/image-tabs.tsx @@ -42,7 +42,7 @@ const ImageTabs = () => { - + diff --git a/app/(admin)/admin/content/image/components/pending-approval-table.tsx b/app/(admin)/admin/content/image/components/pending-approval-table.tsx index 3d9b052..6a6d110 100644 --- a/app/(admin)/admin/content/image/components/pending-approval-table.tsx +++ b/app/(admin)/admin/content/image/components/pending-approval-table.tsx @@ -43,7 +43,11 @@ import { listPendingApproval, PendingApprovalData } from "@/service/content/cont import usePendingApprovalColumns from "./pending-approval-columns"; import { fi } from "date-fns/locale"; -const PendingApprovalTable = () => { +type PendingApprovalTableProps = { + typeId?: number; +}; + +const PendingApprovalTable = ({ typeId }: PendingApprovalTableProps) => { const searchParams = useSearchParams(); const [dataTable, setDataTable] = React.useState([]); const [totalData, setTotalData] = React.useState(0); @@ -112,7 +116,7 @@ const PendingApprovalTable = () => { async function fetchPendingData() { try { console.log("fetchPendingData: page =", page, "showData =", showData, "Number(showData) =", Number(showData)); - const res = await listPendingApproval(page, Number(showData)); + const res = await listPendingApproval(page, Number(showData), typeId); if (res && !res.error && res.data.data) { // Filter data based on search if provided diff --git a/components/form/content/audio-visual/video-form.tsx b/components/form/content/audio-visual/video-form.tsx index ff462b7..f0dbf50 100644 --- a/components/form/content/audio-visual/video-form.tsx +++ b/components/form/content/audio-visual/video-form.tsx @@ -123,6 +123,7 @@ export default function FormVideo() { const [publishedForError, setPublishedForError] = useState( null ); + const userId = Cookies.get("userId"); const [selectedSize, setSelectedSize] = useState(""); const [detailData, setDetailData] = useState(null); const [articleImages, setArticleImages] = useState([]); @@ -478,24 +479,26 @@ export default function FormVideo() { // Use new Article Categories API const category = await listArticleCategories(1, 100); console.log("Article categories response:", category); - + if (category?.error) { console.error("Failed to fetch article categories:", category.message); // Fallback to old API if new one fails const fallbackCategory = await listEnableCategory(fileTypeId); - const resCategory: Category[] = fallbackCategory?.data.data.content || []; + const resCategory: Category[] = + fallbackCategory?.data.data.content || []; setCategories(resCategory); return; } // Handle new API response structure - const resCategory: Category[] = category?.data?.data?.map((item: any) => ({ - id: item.id, - name: item.title, // map title to name for backward compatibility - title: item.title, - description: item.description, - ...item - })) || []; + const resCategory: Category[] = + category?.data?.data?.map((item: any) => ({ + id: item.id, + name: item.title, // map title to name for backward compatibility + title: item.title, + description: item.description, + ...item, + })) || []; setCategories(resCategory); console.log("Article categories loaded:", resCategory); @@ -516,7 +519,8 @@ export default function FormVideo() { // Fallback to old API if error occurs try { const fallbackCategory = await listEnableCategory(fileTypeId); - const resCategory: Category[] = fallbackCategory?.data.data.content || []; + const resCategory: Category[] = + fallbackCategory?.data.data.content || []; setCategories(resCategory); } catch (fallbackError) { console.error("Fallback category fetch also failed:", fallbackError); @@ -577,6 +581,23 @@ export default function FormVideo() { return; } + function formatDateForBackend(date: Date) { + const pad = (n: number) => (n < 10 ? "0" + n : n); + return ( + date.getFullYear() + + "-" + + pad(date.getMonth() + 1) + + "-" + + pad(date.getDate()) + + " " + + pad(date.getHours()) + + ":" + + pad(date.getMinutes()) + + ":" + + pad(date.getSeconds()) + ); + } + let requestData: { title: string; description: string; @@ -618,16 +639,22 @@ export default function FormVideo() { if (id == undefined) { // New Articles API request data structure const articleData: CreateArticleData = { - title: finalTitle, + aiArticleId: 0, // default 0 + categoryIds: selectedCategory.toString(), + createdAt: formatDateForBackend(new Date()), // ✅ format sesuai backend + createdById: Number(userId), // isi dengan userId valid description: htmlToString(finalDescription), htmlDescription: finalDescription, - categoryIds: selectedCategory.toString(), - typeId: 4, // Video content type - tags: finalTags, isDraft: true, isPublish: false, oldId: 0, - slug: finalTitle.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, ''), + slug: finalTitle + .toLowerCase() + .replace(/\s+/g, "-") + .replace(/[^a-z0-9-]/g, ""), + tags: finalTags, + title: finalTitle, + typeId: 1, // Image content type }; // Use new Articles API @@ -636,7 +663,11 @@ export default function FormVideo() { console.log("Article API Response:", response); if (response?.error) { - MySwal.fire("Error", response.message || "Failed to create article", "error"); + MySwal.fire( + "Error", + response.message || "Failed to create article", + "error" + ); return false; } @@ -647,53 +678,69 @@ export default function FormVideo() { // Upload files using new article-files API const formData = new FormData(); - + // Add all files to FormData files.forEach((file, index) => { - formData.append('files', file); + formData.append("files", file); }); console.log("Uploading files to article:", articleId); console.log("Files to upload:", files.length); - + try { const uploadResponse = await uploadArticleFiles(articleId, formData); - + if (uploadResponse?.error) { - MySwal.fire("Error", uploadResponse.message || "Failed to upload files", "error"); + MySwal.fire( + "Error", + uploadResponse.message || "Failed to upload files", + "error" + ); return false; } console.log("Files uploaded successfully:", uploadResponse); - + // Upload thumbnail using first file as thumbnail if (files.length > 0) { const thumbnailFormData = new FormData(); - thumbnailFormData.append('files', files[0]); // Use first file as thumbnail - + thumbnailFormData.append("files", files[0]); // Use first file as thumbnail + console.log("Uploading thumbnail for article:", articleId); - + try { - const thumbnailResponse = await uploadArticleThumbnail(articleId, thumbnailFormData); - + const thumbnailResponse = await uploadArticleThumbnail( + articleId, + thumbnailFormData + ); + if (thumbnailResponse?.error) { - console.warn("Thumbnail upload failed:", thumbnailResponse.message); + console.warn( + "Thumbnail upload failed:", + thumbnailResponse.message + ); // Don't fail the whole process if thumbnail upload fails } else { - console.log("Thumbnail uploaded successfully:", thumbnailResponse); + console.log( + "Thumbnail uploaded successfully:", + thumbnailResponse + ); } } catch (thumbnailError) { console.warn("Thumbnail upload error:", thumbnailError); // Don't fail the whole process if thumbnail upload fails } } - } catch (uploadError) { console.error("Upload error:", uploadError); - MySwal.fire("Error", "Failed to upload files. Please try again.", "error"); + MySwal.fire( + "Error", + "Failed to upload files. Please try again.", + "error" + ); return false; } - + // Show success message MySwal.fire({ title: "Sukses", @@ -702,13 +749,13 @@ export default function FormVideo() { confirmButtonColor: "#3085d6", confirmButtonText: "OK", }).then(() => { - router.push("/admin/content/video"); + router.push("/admin/content/audio-visual"); }); - + Cookies.remove("idCreate"); return; } - + Cookies.remove("idCreate"); }; @@ -985,7 +1032,6 @@ export default function FormVideo() { name="categoryId" render={({ field }) => ( - Category { diff --git a/components/form/content/audio/audio-form.tsx b/components/form/content/audio/audio-form.tsx index 04b56d4..e43774e 100644 --- a/components/form/content/audio/audio-form.tsx +++ b/components/form/content/audio/audio-form.tsx @@ -147,6 +147,7 @@ export default function FormAudio() { const [publishedFor, setPublishedFor] = useState([]); const [fileError, setFileError] = useState(null); type FileWithPreview = File & { preview: string }; + const userId = Cookies.get("userId"); const options: Option[] = [ { id: "all", label: "SEMUA" }, @@ -220,8 +221,7 @@ export default function FormAudio() { file.size <= 100 * 1024 * 1024 ), { - message: - "Hanya file .mp3, .wav, maksimal 100MB yang diperbolehkan.", + message: "Hanya file .mp3, .wav, maksimal 100MB yang diperbolehkan.", } ), categoryId: z.string().min(1, { message: "Kategori wajib dipilih." }), @@ -480,24 +480,26 @@ export default function FormAudio() { // Use new Article Categories API const category = await listArticleCategories(1, 100); console.log("Article categories response:", category); - + if (category?.error) { console.error("Failed to fetch article categories:", category.message); // Fallback to old API if new one fails const fallbackCategory = await listEnableCategory(fileTypeId); - const resCategory: Category[] = fallbackCategory?.data.data.content || []; + const resCategory: Category[] = + fallbackCategory?.data.data.content || []; setCategories(resCategory); return; } // Handle new API response structure - const resCategory: Category[] = category?.data?.data?.map((item: any) => ({ - id: item.id, - name: item.title, // map title to name for backward compatibility - title: item.title, - description: item.description, - ...item - })) || []; + const resCategory: Category[] = + category?.data?.data?.map((item: any) => ({ + id: item.id, + name: item.title, // map title to name for backward compatibility + title: item.title, + description: item.description, + ...item, + })) || []; setCategories(resCategory); console.log("Article categories loaded:", resCategory); @@ -518,7 +520,8 @@ export default function FormAudio() { // Fallback to old API if error occurs try { const fallbackCategory = await listEnableCategory(fileTypeId); - const resCategory: Category[] = fallbackCategory?.data.data.content || []; + const resCategory: Category[] = + fallbackCategory?.data.data.content || []; setCategories(resCategory); } catch (fallbackError) { console.error("Fallback category fetch also failed:", fallbackError); @@ -575,6 +578,23 @@ export default function FormAudio() { return; } + function formatDateForBackend(date: Date) { + const pad = (n: number) => (n < 10 ? "0" + n : n); + return ( + date.getFullYear() + + "-" + + pad(date.getMonth() + 1) + + "-" + + pad(date.getDate()) + + " " + + pad(date.getHours()) + + ":" + + pad(date.getMinutes()) + + ":" + + pad(date.getSeconds()) + ); + } + let requestData: { title: string; description: string; @@ -616,16 +636,22 @@ export default function FormAudio() { if (id == undefined) { // New Articles API request data structure const articleData: CreateArticleData = { - title: finalTitle, + aiArticleId: 0, // default 0 + categoryIds: selectedCategory.toString(), + createdAt: formatDateForBackend(new Date()), // ✅ format sesuai backend + createdById: Number(userId), // isi dengan userId valid description: htmlToString(finalDescription), htmlDescription: finalDescription, - categoryIds: selectedCategory.toString(), - typeId: 3, // Audio content type - tags: finalTags, isDraft: true, isPublish: false, oldId: 0, - slug: finalTitle.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, ''), + slug: finalTitle + .toLowerCase() + .replace(/\s+/g, "-") + .replace(/[^a-z0-9-]/g, ""), + tags: finalTags, + title: finalTitle, + typeId: 1, // Image content type }; // Use new Articles API @@ -634,7 +660,11 @@ export default function FormAudio() { console.log("Article API Response:", response); if (response?.error) { - MySwal.fire("Error", response.message || "Failed to create article", "error"); + MySwal.fire( + "Error", + response.message || "Failed to create article", + "error" + ); return false; } @@ -645,53 +675,69 @@ export default function FormAudio() { // Upload files using new article-files API const formData = new FormData(); - + // Add all files to FormData files.forEach((file, index) => { - formData.append('files', file); + formData.append("files", file); }); console.log("Uploading files to article:", articleId); console.log("Files to upload:", files.length); - + try { const uploadResponse = await uploadArticleFiles(articleId, formData); - + if (uploadResponse?.error) { - MySwal.fire("Error", uploadResponse.message || "Failed to upload files", "error"); + MySwal.fire( + "Error", + uploadResponse.message || "Failed to upload files", + "error" + ); return false; } console.log("Files uploaded successfully:", uploadResponse); - + // Upload thumbnail using first file as thumbnail if (files.length > 0) { const thumbnailFormData = new FormData(); - thumbnailFormData.append('files', files[0]); // Use first file as thumbnail - + thumbnailFormData.append("files", files[0]); // Use first file as thumbnail + console.log("Uploading thumbnail for article:", articleId); - + try { - const thumbnailResponse = await uploadArticleThumbnail(articleId, thumbnailFormData); - + const thumbnailResponse = await uploadArticleThumbnail( + articleId, + thumbnailFormData + ); + if (thumbnailResponse?.error) { - console.warn("Thumbnail upload failed:", thumbnailResponse.message); + console.warn( + "Thumbnail upload failed:", + thumbnailResponse.message + ); // Don't fail the whole process if thumbnail upload fails } else { - console.log("Thumbnail uploaded successfully:", thumbnailResponse); + console.log( + "Thumbnail uploaded successfully:", + thumbnailResponse + ); } } catch (thumbnailError) { console.warn("Thumbnail upload error:", thumbnailError); // Don't fail the whole process if thumbnail upload fails } } - } catch (uploadError) { console.error("Upload error:", uploadError); - MySwal.fire("Error", "Failed to upload files. Please try again.", "error"); + MySwal.fire( + "Error", + "Failed to upload files. Please try again.", + "error" + ); return false; } - + // Show success message MySwal.fire({ title: "Sukses", @@ -702,11 +748,11 @@ export default function FormAudio() { }).then(() => { router.push("/admin/content/audio"); }); - + Cookies.remove("idCreate"); return; } - + Cookies.remove("idCreate"); }; @@ -975,7 +1021,6 @@ export default function FormAudio() { name="categoryId" render={({ field }) => ( - Category { diff --git a/components/form/content/document/teks-form.tsx b/components/form/content/document/teks-form.tsx index 50404be..5d30536 100644 --- a/components/form/content/document/teks-form.tsx +++ b/components/form/content/document/teks-form.tsx @@ -131,7 +131,7 @@ export default function FormTeks() { polda: false, polres: false, }); - + const userId = Cookies.get("userId"); let fileTypeId = "2"; let progressInfo: any = []; let counterUpdateProgress = 0; @@ -476,24 +476,26 @@ export default function FormTeks() { // Use new Article Categories API const category = await listArticleCategories(1, 100); console.log("Article categories response:", category); - + if (category?.error) { console.error("Failed to fetch article categories:", category.message); // Fallback to old API if new one fails const fallbackCategory = await listEnableCategory(fileTypeId); - const resCategory: Category[] = fallbackCategory?.data.data.content || []; + const resCategory: Category[] = + fallbackCategory?.data.data.content || []; setCategories(resCategory); return; } // Handle new API response structure - const resCategory: Category[] = category?.data?.data?.map((item: any) => ({ - id: item.id, - name: item.title, // map title to name for backward compatibility - title: item.title, - description: item.description, - ...item - })) || []; + const resCategory: Category[] = + category?.data?.data?.map((item: any) => ({ + id: item.id, + name: item.title, // map title to name for backward compatibility + title: item.title, + description: item.description, + ...item, + })) || []; setCategories(resCategory); console.log("Article categories loaded:", resCategory); @@ -514,7 +516,8 @@ export default function FormTeks() { // Fallback to old API if error occurs try { const fallbackCategory = await listEnableCategory(fileTypeId); - const resCategory: Category[] = fallbackCategory?.data.data.content || []; + const resCategory: Category[] = + fallbackCategory?.data.data.content || []; setCategories(resCategory); } catch (fallbackError) { console.error("Fallback category fetch also failed:", fallbackError); @@ -579,6 +582,23 @@ export default function FormTeks() { return; } + function formatDateForBackend(date: Date) { + const pad = (n: number) => (n < 10 ? "0" + n : n); + return ( + date.getFullYear() + + "-" + + pad(date.getMonth() + 1) + + "-" + + pad(date.getDate()) + + " " + + pad(date.getHours()) + + ":" + + pad(date.getMinutes()) + + ":" + + pad(date.getSeconds()) + ); + } + let requestData: { title: string; description: string; @@ -620,25 +640,34 @@ export default function FormTeks() { if (id == undefined) { // New Articles API request data structure const articleData: CreateArticleData = { - title: finalTitle, + aiArticleId: 0, // default 0 + categoryIds: selectedCategory.toString(), + createdAt: formatDateForBackend(new Date()), // ✅ format sesuai backend + createdById: Number(userId), // isi dengan userId valid description: htmlToString(finalDescription), htmlDescription: finalDescription, - categoryIds: selectedCategory.toString(), - typeId: 2, // Document content type - tags: finalTags, isDraft: true, isPublish: false, oldId: 0, - slug: finalTitle.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, ''), + slug: finalTitle + .toLowerCase() + .replace(/\s+/g, "-") + .replace(/[^a-z0-9-]/g, ""), + tags: finalTags, + title: finalTitle, + typeId: 1, // Image content type }; - // Use new Articles API const response = await createArticle(articleData); console.log("Article Data Submitted:", articleData); console.log("Article API Response:", response); if (response?.error) { - MySwal.fire("Error", response.message || "Failed to create article", "error"); + MySwal.fire( + "Error", + response.message || "Failed to create article", + "error" + ); return false; } @@ -649,53 +678,69 @@ export default function FormTeks() { // Upload files using new article-files API const formData = new FormData(); - + // Add all files to FormData files.forEach((file, index) => { - formData.append('files', file); + formData.append("files", file); }); console.log("Uploading files to article:", articleId); console.log("Files to upload:", files.length); - + try { const uploadResponse = await uploadArticleFiles(articleId, formData); - + if (uploadResponse?.error) { - MySwal.fire("Error", uploadResponse.message || "Failed to upload files", "error"); + MySwal.fire( + "Error", + uploadResponse.message || "Failed to upload files", + "error" + ); return false; } console.log("Files uploaded successfully:", uploadResponse); - + // Upload thumbnail using first file as thumbnail if (files.length > 0) { const thumbnailFormData = new FormData(); - thumbnailFormData.append('files', files[0]); // Use first file as thumbnail - + thumbnailFormData.append("files", files[0]); // Use first file as thumbnail + console.log("Uploading thumbnail for article:", articleId); - + try { - const thumbnailResponse = await uploadArticleThumbnail(articleId, thumbnailFormData); - + const thumbnailResponse = await uploadArticleThumbnail( + articleId, + thumbnailFormData + ); + if (thumbnailResponse?.error) { - console.warn("Thumbnail upload failed:", thumbnailResponse.message); + console.warn( + "Thumbnail upload failed:", + thumbnailResponse.message + ); // Don't fail the whole process if thumbnail upload fails } else { - console.log("Thumbnail uploaded successfully:", thumbnailResponse); + console.log( + "Thumbnail uploaded successfully:", + thumbnailResponse + ); } } catch (thumbnailError) { console.warn("Thumbnail upload error:", thumbnailError); // Don't fail the whole process if thumbnail upload fails } } - } catch (uploadError) { console.error("Upload error:", uploadError); - MySwal.fire("Error", "Failed to upload files. Please try again.", "error"); + MySwal.fire( + "Error", + "Failed to upload files. Please try again.", + "error" + ); return false; } - + // Show success message MySwal.fire({ title: "Sukses", @@ -706,11 +751,11 @@ export default function FormTeks() { }).then(() => { router.push("/admin/content/document"); }); - + Cookies.remove("idCreate"); return; } - + Cookies.remove("idCreate"); }; @@ -963,42 +1008,41 @@ export default function FormTeks() { Category - ( - - Category - { - field.onChange(value); - setSelectedCategory(value); - }} - > - - - - - {categories.map((category) => ( - - {category.name} - - ))} - - + ( + + { + field.onChange(value); + setSelectedCategory(value); + }} + > + + + + + {categories.map((category) => ( + + {category.name} + + ))} + + - {errors.categoryId && ( - - {errors.categoryId.message} - + {errors.categoryId && ( + + {errors.categoryId.message} + + )} + )} - - )} - /> + /> diff --git a/package-lock.json b/package-lock.json index 40e9afa..9723ef8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2338,6 +2338,7 @@ "version": "1.1.13", "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", + "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", diff --git a/service/content/content.ts b/service/content/content.ts index 04c7fc3..1699654 100644 --- a/service/content/content.ts +++ b/service/content/content.ts @@ -456,9 +456,10 @@ export interface PendingApprovalResponse { // Function to fetch pending approval data export async function listPendingApproval( page: number = 1, - limit: number = 10 + limit: number = 10, + typeId?: number ) { - const url = `articles/pending-approval?page=${page}&limit=${limit}`; + const url = `articles/pending-approval?page=${page}&limit=${limit}&typeId=${typeId}`; return await httpGetInterceptor(url); }
- {errors.categoryId.message} -
+ {errors.categoryId.message} +