diff --git a/app/(admin)/admin/content/audio-visual/components/columns.tsx b/app/(admin)/admin/content/audio-visual/components/columns.tsx index 6625b88..9e152ad 100644 --- a/app/(admin)/admin/content/audio-visual/components/columns.tsx +++ b/app/(admin)/admin/content/audio-visual/components/columns.tsx @@ -51,11 +51,17 @@ const useTableColumns = () => { { accessorKey: "categoryName", header: "Category Name", - cell: ({ row }) => ( - - {row.getValue("categoryName")} - - ), + cell: ({ row }) => { + 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} + + ); + }, }, { accessorKey: "createdAt", @@ -77,7 +83,9 @@ const useTableColumns = () => { accessorKey: "creatorName", header: "Creator Group", cell: ({ row }) => ( - {row.getValue("creatorName")} + + {row.getValue("creatorName") || row.getValue("createdByName")} + ), }, { @@ -85,7 +93,7 @@ const useTableColumns = () => { header: "Source", cell: ({ row }) => ( - {row.getValue("creatorGroupLevelName")} + {row.getValue("creatorGroupLevelName") || "-"} ), }, 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 1068ff5..3345c7e 100644 --- a/app/(admin)/admin/content/audio-visual/components/table-video.tsx +++ b/app/(admin)/admin/content/audio-visual/components/table-video.tsx @@ -39,7 +39,13 @@ import columns from "./columns"; import { Label } from "@/components/ui/label"; import { format } from "date-fns"; import useTableColumns from "./columns"; -import { listEnableCategory, listDataVideo } from "@/service/content"; +import { + listEnableCategory, + listDataVideo, + listArticles, + listArticlesWithFilters, + ArticleFilters +} from "@/service/content"; import { SortingState, ColumnFiltersState, @@ -169,32 +175,39 @@ const TableVideo = () => { ? format(new Date(endDate), "yyyy-MM-dd") : ""; try { - const isForSelf = Number(roleId) === 4; - const res = await listDataVideo( - showData, - page - 1, - isForSelf, - !isForSelf, - categoryFilter, - statusFilter, - statusFilter?.sort().join(",").includes("1") ? userLevelId : "", - filterByCreator, - filterBySource, - formattedStartDate, // Pastikan format sesuai - formattedEndDate, // Pastikan format sesuai - search, - filterByCreatorGroup - ); + // 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; - 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); + // 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); } diff --git a/app/(admin)/admin/content/audio/components/columns.tsx b/app/(admin)/admin/content/audio/components/columns.tsx index 0cb17c8..212a80a 100644 --- a/app/(admin)/admin/content/audio/components/columns.tsx +++ b/app/(admin)/admin/content/audio/components/columns.tsx @@ -51,11 +51,17 @@ const useTableColumns = () => { { accessorKey: "categoryName", header: "Category Name", - cell: ({ row }) => ( - - {row.getValue("categoryName")} - - ), + cell: ({ row }) => { + 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} + + ); + }, }, { accessorKey: "createdAt", @@ -77,7 +83,9 @@ const useTableColumns = () => { accessorKey: "creatorName", header: "Creator Group", cell: ({ row }) => ( - {row.getValue("creatorName")} + + {row.getValue("creatorName") || row.getValue("createdByName")} + ), }, { @@ -85,7 +93,7 @@ const useTableColumns = () => { header: "Source", cell: ({ row }) => ( - {row.getValue("creatorGroupLevelName")} + {row.getValue("creatorGroupLevelName") || "-"} ), }, diff --git a/app/(admin)/admin/content/audio/components/table-audio.tsx b/app/(admin)/admin/content/audio/components/table-audio.tsx index 831978f..1630de2 100644 --- a/app/(admin)/admin/content/audio/components/table-audio.tsx +++ b/app/(admin)/admin/content/audio/components/table-audio.tsx @@ -55,7 +55,10 @@ import { listDataAudio, listDataImage, listDataVideo, + listArticles, + listArticlesWithFilters, listEnableCategory, + ArticleFilters, } from "@/service/content/content"; import { Label } from "@/components/ui/label"; import { format } from "date-fns"; @@ -176,32 +179,39 @@ const TableAudio = () => { ? format(new Date(endDate), "yyyy-MM-dd") : ""; try { - const isForSelf = Number(roleId) === 4; - const res = await listDataAudio( - showData, - page - 1, - isForSelf, - !isForSelf, - categoryFilter, - statusFilter, - statusFilter?.sort().join(",").includes("1") ? userLevelId : "", - filterByCreator, - filterBySource, - formattedStartDate, // Pastikan format sesuai - formattedEndDate, // Pastikan format sesuai - search, - filterByCreatorGroup - ); + // 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; - 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); + // 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); } diff --git a/app/(admin)/admin/content/document/components/columns.tsx b/app/(admin)/admin/content/document/components/columns.tsx index bffb48d..9761a67 100644 --- a/app/(admin)/admin/content/document/components/columns.tsx +++ b/app/(admin)/admin/content/document/components/columns.tsx @@ -51,11 +51,17 @@ const useTableColumns = () => { { accessorKey: "categoryName", header: "Category Name", - cell: ({ row }) => ( - - {row.getValue("categoryName")} - - ), + cell: ({ row }) => { + 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} + + ); + }, }, { accessorKey: "createdAt", @@ -77,7 +83,9 @@ const useTableColumns = () => { accessorKey: "creatorName", header: "Creator Group", cell: ({ row }) => ( - {row.getValue("creatorName")} + + {row.getValue("creatorName") || row.getValue("createdByName")} + ), }, { @@ -85,7 +93,7 @@ const useTableColumns = () => { header: "Source", cell: ({ row }) => ( - {row.getValue("creatorGroupLevelName")} + {row.getValue("creatorGroupLevelName") || "-"} ), }, diff --git a/app/(admin)/admin/content/document/components/table-teks.tsx b/app/(admin)/admin/content/document/components/table-teks.tsx index a208095..d2a4d6e 100644 --- a/app/(admin)/admin/content/document/components/table-teks.tsx +++ b/app/(admin)/admin/content/document/components/table-teks.tsx @@ -54,7 +54,10 @@ import columns from "./columns"; import { listDataImage, listDataTeks, + listArticles, + listArticlesWithFilters, listEnableCategory, + ArticleFilters, } from "@/service/content/content"; import { Label } from "@/components/ui/label"; import { format } from "date-fns"; @@ -175,32 +178,39 @@ const TableTeks = () => { ? format(new Date(endDate), "yyyy-MM-dd") : ""; try { - const isForSelf = Number(roleId) === 4; - const res = await listDataTeks( - showData, - page - 1, - isForSelf, - !isForSelf, - categoryFilter, - statusFilter, - statusFilter?.sort().join(",").includes("1") ? userLevelId : "", - filterByCreator, - filterBySource, - formattedStartDate, // Pastikan format sesuai - formattedEndDate, // Pastikan format sesuai - search, - filterByCreatorGroup - ); + // 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; - 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); + // 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); } diff --git a/app/(admin)/admin/content/image/components/columns.tsx b/app/(admin)/admin/content/image/components/columns.tsx index 7455581..ca690a1 100644 --- a/app/(admin)/admin/content/image/components/columns.tsx +++ b/app/(admin)/admin/content/image/components/columns.tsx @@ -53,11 +53,17 @@ const useTableColumns = () => { { accessorKey: "categoryName", header: "Category Name", - cell: ({ row }) => ( - - {row.getValue("categoryName")} - - ), + cell: ({ row }) => { + 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} + + ); + }, }, { accessorKey: "createdAt", @@ -79,7 +85,9 @@ const useTableColumns = () => { accessorKey: "creatorName", header: "Creator Group", cell: ({ row }) => ( - {row.getValue("creatorName")} + + {row.getValue("creatorName") || row.getValue("createdByName")} + ), }, { @@ -87,7 +95,7 @@ const useTableColumns = () => { header: "Source", cell: ({ row }) => ( - {row.getValue("creatorGroupLevelName")} + {row.getValue("creatorGroupLevelName") || "-"} ), }, diff --git a/app/(admin)/admin/content/image/components/table-image.tsx b/app/(admin)/admin/content/image/components/table-image.tsx index 8167c98..5fd7a56 100644 --- a/app/(admin)/admin/content/image/components/table-image.tsx +++ b/app/(admin)/admin/content/image/components/table-image.tsx @@ -54,7 +54,10 @@ import TablePagination from "@/components/table/table-pagination"; import { deleteMedia, listDataImage, + listArticles, + listArticlesWithFilters, listEnableCategory, + ArticleFilters, } from "@/service/content/content"; import { loading } from "@/config/swal"; @@ -183,33 +186,39 @@ const TableImage = () => { ? format(new Date(endDate), "yyyy-MM-dd") : ""; try { - const isForSelf = Number(roleId) === 4; - const res = await listDataImage( - showData, - page - 1, - isForSelf, - !isForSelf, - categoryFilter, - statusFilter, - statusFilter?.sort().join(",").includes("1") ? userLevelId : "", - filterByCreator, - filterBySource, - formattedStartDate, - formattedEndDate, - search, - filterByCreatorGroup, - locale == "en" - ); + // Using the new interface-based approach for image content + const filters: ArticleFilters = { + page: page, + totalPage: Number(showData), + title: search || undefined, + categoryId: categoryFilter ? Number(categoryFilter) : undefined, + typeId: 1, // image 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; - 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); + // 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); } diff --git a/app/auth/page.tsx b/app/auth/page.tsx index 4c09548..ab7f7b4 100644 --- a/app/auth/page.tsx +++ b/app/auth/page.tsx @@ -28,6 +28,7 @@ const AuthPage = () => { setLoginCredentials(data); try { const result = await validateEmail(data); + console.log("result : ", result); switch (result) { case "skip": handleOTPSuccess(); diff --git a/components/form/content/image/image-form.tsx b/components/form/content/image/image-form.tsx index 8ab7d2b..1bc1d75 100644 --- a/components/form/content/image/image-form.tsx +++ b/components/form/content/image/image-form.tsx @@ -32,9 +32,14 @@ import { Switch } from "@/components/ui/switch"; import Cookies from "js-cookie"; import { createMedia, + createArticle, getTagsBySubCategoryId, listEnableCategory, + listArticleCategories, uploadThumbnail, + uploadArticleFiles, + uploadArticleThumbnail, + CreateArticleData, } from "@/service/content/content"; import { Textarea } from "@/components/ui/textarea"; import { @@ -466,11 +471,30 @@ export default function FormImage() { const getCategories = async () => { try { - const category = await listEnableCategory(fileTypeId); - const resCategory: Category[] = category?.data.data.content; + // 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 || []; + 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 + })) || []; setCategories(resCategory); - console.log("data category", resCategory); + console.log("Article categories loaded:", resCategory); if (scheduleId && scheduleType === "3") { const findCategory = resCategory.find((o) => @@ -478,7 +502,6 @@ export default function FormImage() { ); if (findCategory) { - // setValue("categoryId", findCategory.id); setSelectedCategory(findCategory.id); const response = await getTagsBySubCategoryId(findCategory.id); setTags(response?.data?.data); @@ -486,6 +509,14 @@ export default function FormImage() { } } catch (error) { console.error("Failed to fetch categories:", error); + // Fallback to old API if error occurs + try { + const fallbackCategory = await listEnableCategory(fileTypeId); + const resCategory: Category[] = fallbackCategory?.data.data.content || []; + setCategories(resCategory); + } catch (fallbackError) { + console.error("Fallback category fetch also failed:", fallbackError); + } } }; @@ -541,6 +572,21 @@ export default function FormImage() { return; } + // New Articles API request data structure + const articleData: CreateArticleData = { + title: finalTitle, + description: htmlToString(finalDescription), + htmlDescription: finalDescription, + categoryIds: selectedCategory.toString(), + typeId: 1, // Image content type + tags: finalTags, + isDraft: true, + isPublish: false, + oldId: 0, + slug: finalTitle.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, ''), + }; + + // Keep old structure for backward compatibility if needed let requestData: { title: string; description: string; @@ -580,37 +626,102 @@ export default function FormImage() { } if (id == undefined) { - const response = await createMedia(requestData); - console.log("Form Data Submitted:", requestData); + // Use new Articles API + const response = await createArticle(articleData); + console.log("Article Data Submitted:", articleData); + console.log("Article API Response:", response); - Cookies.set("idCreate", response?.data?.data, { expires: 1 }); - id = response?.data?.data; - const formMedia = new FormData(); - const thumbnail = files[0]; - formMedia.append("file", thumbnail); - const responseThumbnail = await uploadThumbnail(id, formMedia); - if (responseThumbnail?.error == true) { - error(responseThumbnail?.message); + if (response?.error) { + MySwal.fire("Error", response.message || "Failed to create article", "error"); return false; } - } - const progressInfoArr = files.map((item) => ({ - percentage: 0, - fileName: item.name, - })); - progressInfo = progressInfoArr; - setIsStartUpload(true); - setProgressList(progressInfoArr); - close(); - files.map(async (item: any, index: number) => { - await uploadResumableFile( - index, - String(id), - item, - fileTypeId == "2" || fileTypeId == "4" ? item.duration : "0" - ); - }); + // Get the article ID from the new API response + const articleId = response?.data?.data?.id; + Cookies.set("idCreate", articleId, { expires: 1 }); + id = articleId; + + // Upload files using new article-files API + const formData = new FormData(); + + // Add all files to FormData + files.forEach((file, index) => { + 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"); + 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 + + console.log("Uploading thumbnail for article:", articleId); + + try { + const thumbnailResponse = await uploadArticleThumbnail(articleId, thumbnailFormData); + + if (thumbnailResponse?.error) { + console.warn("Thumbnail upload failed:", thumbnailResponse.message); + // Don't fail the whole process if thumbnail upload fails + } else { + 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"); + return false; + } + + // Show success message + MySwal.fire({ + title: "Sukses", + text: "Article dan files berhasil disimpan.", + icon: "success", + confirmButtonColor: "#3085d6", + confirmButtonText: "OK", + }).then(() => { + router.push("/admin/content/image"); + }); + + Cookies.remove("idCreate"); + return; + } + + // Keep old upload logic for backward compatibility + // const progressInfoArr = files.map((item) => ({ + // percentage: 0, + // fileName: item.name, + // })); + // progressInfo = progressInfoArr; + // setIsStartUpload(true); + // setProgressList(progressInfoArr); + + // files.map(async (item: any, index: number) => { + // await uploadResumableFile( + // index, + // String(id), + // item, + // fileTypeId == "2" || fileTypeId == "4" ? item.duration : "0" + // ); + // }); Cookies.remove("idCreate"); }; diff --git a/hooks/use-auth.ts b/hooks/use-auth.ts index 7f8af33..edd51f9 100644 --- a/hooks/use-auth.ts +++ b/hooks/use-auth.ts @@ -82,9 +82,7 @@ export const useAuth = (): AuthContextType => { // Attempt login const response = await doLogin({ - ...credentials, - grantType: AUTH_CONSTANTS.GRANT_TYPE, - clientId: AUTH_CONSTANTS.CLIENT_ID, + ...credentials }); if (response?.error) { @@ -92,7 +90,12 @@ export const useAuth = (): AuthContextType => { throw new Error("Invalid username or password"); } - const { access_token, refresh_token } = response?.data || {}; + console.log("response : ", response?.data); + + const { access_token, refresh_token } = response?.data?.data || {}; + + console.log("access_token : ", access_token); + console.log("refresh_token : ", refresh_token); if (!access_token || !refresh_token) { throw new Error("Invalid response from server"); @@ -103,7 +106,9 @@ export const useAuth = (): AuthContextType => { // Get user profile const profileResponse = await getProfile(access_token); - const profile: ProfileData = profileResponse?.data?.data; + const profile: any = profileResponse?.data?.data; + + console.log("profile : ", profile); if (!profile) { throw new Error("Failed to fetch user profile"); @@ -124,12 +129,17 @@ export const useAuth = (): AuthContextType => { loginRateLimiter.resetAttempts(credentials.username); + + console.log("navigationPath : ", profile.userRoleId); + const navigationPath = getNavigationPath( - profile.roleId, - profile.userLevel?.id, - profile.userLevel?.parentLevelId + profile.userRoleId, + profile.userLevelId, + profile.userLevelGroup ); + console.log("navigationPath : ", navigationPath); + setState({ isAuthenticated: true, user: profile, @@ -198,7 +208,7 @@ export const useEmailValidation = () => { throw new Error(response?.message || "Email validation failed"); } - const message = response?.data?.message; + const message = response?.data?.messages[0]; switch (message) { case "Continue to setup email": @@ -301,8 +311,12 @@ export const useOTPVerification = () => { if (otp.length !== 6) { throw new Error("OTP must be exactly 6 digits"); } + const data = { + username : username, + otpCode : otp, + }; - const response = await verifyOTPByUsername(username, otp); + const response = await verifyOTPByUsername(data); if (response?.error) { throw new Error(response.message || "OTP verification failed"); diff --git a/lib/auth-utils.ts b/lib/auth-utils.ts index 31f6a49..a0e64dd 100644 --- a/lib/auth-utils.ts +++ b/lib/auth-utils.ts @@ -15,7 +15,7 @@ export const NAVIGATION_CONFIG: NavigationConfig[] = [ path: "/admin/dashboard/executive", label: "Executive Dashboard", }, - { roleId: 3, path: "/admin/dashboard", label: "Dashboard" }, + { roleId: 1, path: "/admin/dashboard", label: "Dashboard" }, { roleId: 4, path: "/admin/dashboard", label: "Dashboard" }, { roleId: 9, path: "/in/supervisor/ticketing", label: "Ticketing" }, { roleId: 10, path: "/admin/dashboard", label: "Dashboard" }, @@ -49,30 +49,29 @@ export const setAuthCookies = ( }); }; -export const setProfileCookies = (profile: ProfileData): void => { - Cookies.set("home_path", profile.homePath || "", { expires: 1 }); - Cookies.set("profile_picture", profile.profilePictureUrl || "", { - expires: 1, - }); - Cookies.set("state", profile.userLevel?.name || "", { expires: 1 }); - Cookies.set("state-prov", profile.userLevel?.province?.provName || "", { - expires: 1, - }); +export const setProfileCookies = (profile: any): void => { + // Cookies.set("home_path", profile.homePath || "", { expires: 1 }); + // Cookies.set("profile_picture", profile.profilePictureUrl || "", { + // expires: 1, + // }); + // Cookies.set("state", profile.userLevel?.name || "", { expires: 1 }); + // Cookies.set("state-prov", profile.userLevel?.province?.provName || "", { + // expires: 1, + // }); setCookiesEncrypt("uie", profile.id, { expires: 1 }); - setCookiesEncrypt("urie", profile.roleId.toString(), { expires: 1 }); - setCookiesEncrypt("urne", profile.role?.name || "", { expires: 1 }); - setCookiesEncrypt("ulie", profile.userLevel?.id.toString() || "", { - expires: 1, - }); - setCookiesEncrypt( - "uplie", - profile.userLevel?.parentLevelId?.toString() || "", - { expires: 1 } - ); - setCookiesEncrypt("ulne", profile.userLevel?.levelNumber.toString() || "", { + setCookiesEncrypt("urie", profile.userRoleId.toString(), { expires: 1 }); + setCookiesEncrypt("ulie", profile.userLevelId.toString() || "", { expires: 1, }); + // setCookiesEncrypt( + // "uplie", + // profile.userLevel?.parentLevelId?.toString() || "", + // { expires: 1 } + // ); + // setCookiesEncrypt("ulne", profile.userLevel?.levelNumber.toString() || "", { + // expires: 1, + // }); setCookiesEncrypt("ufne", profile.fullname, { expires: 1 }); setCookiesEncrypt("ulnae", profile.userLevel?.name || "", { expires: 1 }); setCookiesEncrypt("uinse", profile.instituteId || "", { expires: 1 }); @@ -130,7 +129,7 @@ export const isSpecialLevel = ( export const getNavigationPath = ( roleId: number, userLevelId?: number, - parentLevelId?: number + userLevelGroup?: number ): string => { const config = NAVIGATION_CONFIG.find((nav) => nav.roleId === roleId); @@ -139,12 +138,11 @@ export const getNavigationPath = ( if (config) { // Special handling for role 2 with specific level conditions if ( - roleId === 2 && - userLevelId && - parentLevelId && - isSpecialLevel(userLevelId, parentLevelId) + roleId === 1 && + userLevelId + // isSpecialLevel(userLevelId, parentLevelId) ) { - return "/in/dashboard"; + return "/admin/dashboard"; } return config.path; } diff --git a/next.config.ts b/next.config.ts index 24e9716..b2a4a77 100644 --- a/next.config.ts +++ b/next.config.ts @@ -2,7 +2,7 @@ import type { NextConfig } from "next"; const nextConfig = { images: { - domains: ["netidhub.com"], + domains: ["kontenhumas.com"], }, }; diff --git a/service/auth.ts b/service/auth.ts index 52f8b0e..0492ddf 100644 --- a/service/auth.ts +++ b/service/auth.ts @@ -13,7 +13,7 @@ export async function login(data: any) { } export async function doLogin(data: any) { - const pathUrl = "signin"; + const pathUrl = "users/login"; return httpPost(pathUrl, data); } @@ -44,7 +44,7 @@ export async function getCsrfToken() { "content-type": "application/json", }; return httpGet(pathUrl, headers); - // const url = 'https://netidhub.com/api/csrf'; + // const url = 'https://kontenhumas.com/api/csrf'; // try { // const response = await fetch(url, { // method: 'GET', @@ -69,8 +69,8 @@ export async function getProfile(token: any) { } export async function postEmailValidation(data: any) { - const url = "public/users/email-validation"; - return httpPostInterceptor(url, data); + const url = "users/email-validation"; + return httpPost(url, data); } export async function postSetupEmail(data: any) { @@ -78,9 +78,9 @@ export async function postSetupEmail(data: any) { return httpPostInterceptor(url, data); } -export async function verifyOTPByUsername(username: any, otp: any) { - const url = `public/users/verify-otp?username=${username}&otp=${otp}`; - return httpPost(url); +export async function verifyOTPByUsername(data: any) { + const url = `users/otp-validation`; + return httpPost(url, data); } export async function getSubjects() { diff --git a/service/content.ts b/service/content.ts index 0ea10b9..7dede56 100644 --- a/service/content.ts +++ b/service/content.ts @@ -5,6 +5,60 @@ import { httpPostInterceptor, } from "./http-config/http-interceptor-service"; +// Interface for Articles API filters +export interface ArticleFilters { + page?: number; + totalPage?: number; + title?: string; + description?: string; + categoryId?: number; + category?: string; + typeId?: number; + tags?: string; + createdById?: number; + statusId?: number; + isBanner?: boolean; + isPublish?: boolean; + isDraft?: boolean; + startDate?: string; + endDate?: string; +} + +// Interface for creating new article +export interface CreateArticleData { + title: string; + description: string; + htmlDescription: string; + categoryIds: string; + typeId: number; + tags: string; + isDraft: boolean; + isPublish: boolean; + oldId: number; + slug: string; +} + +// Interface for Article Category +export interface ArticleCategory { + id: number; + title: string; + description: string; + thumbnailUrl: string; + slug: string | null; + tags: string[]; + thumbnailPath: string | null; + parentId: number; + oldCategoryId: number | null; + createdById: number; + statusId: number; + isPublish: boolean; + publishedAt: string | null; + isEnabled: boolean | null; + isActive: boolean; + createdAt: string; + updatedAt: string; +} + export async function listDataAll( isForSelf: any, isApproval: any, @@ -150,6 +204,12 @@ export async function createMedia(data: any) { return httpPostInterceptor(url, data); } +// New Articles API - Create Article +export async function createArticle(data: CreateArticleData) { + const url = "articles"; + return httpPostInterceptor(url, data); +} + export async function uploadThumbnail(id: any, data: any) { const url = `media/upload?id=${id}&operation=thumbnail`; const headers = { @@ -158,6 +218,30 @@ export async function uploadThumbnail(id: any, data: any) { return httpPostInterceptor(url, data, headers); } +// New Articles API - Upload Article Files +export async function uploadArticleFiles(articleId: string | number, files: FormData) { + const url = `article-files/${articleId}`; + const headers = { + "Content-Type": "multipart/form-data", + }; + return httpPostInterceptor(url, files, headers); +} + +// New Articles API - Upload Article Thumbnail +export async function uploadArticleThumbnail(articleId: string | number, thumbnail: FormData) { + const url = `articles/thumbnail/${articleId}`; + const headers = { + "Content-Type": "multipart/form-data", + }; + return httpPostInterceptor(url, thumbnail, headers); +} + +// New Articles API - Get Article Categories +export async function listArticleCategories(page: number = 1, limit: number = 100) { + const url = `article-categories?page=${page}&limit=${limit}`; + return httpGetInterceptor(url); +} + export async function detailSPIT(id: any) { const url = `media/spit?id=${id}`; return httpGetInterceptor(url); @@ -211,3 +295,101 @@ export async function postActivityLog(data: any) { const url = `activity`; return httpPost(url, data); } + +// New Articles API function with complete filter support +export async function listArticles( + page: number = 1, + totalPage: number = 10, + title?: string, + description?: string, + categoryId?: number, + category?: string, + typeId?: number, + tags?: string, + createdById?: number, + statusId?: number, + isBanner?: boolean, + isPublish?: boolean, + isDraft?: boolean, + startDate?: string, + endDate?: string +) { + let url = `articles?page=${page}&totalPage=${totalPage}`; + + // Add optional query parameters based on available filters + if (title) url += `&title=${encodeURIComponent(title)}`; + if (description) url += `&description=${encodeURIComponent(description)}`; + if (categoryId !== undefined) url += `&categoryId=${categoryId}`; + if (category) url += `&category=${encodeURIComponent(category)}`; + if (typeId !== undefined) url += `&typeId=${typeId}`; + if (tags) url += `&tags=${encodeURIComponent(tags)}`; + if (createdById !== undefined) url += `&createdById=${createdById}`; + if (statusId !== undefined) url += `&statusId=${statusId}`; + if (isBanner !== undefined) url += `&isBanner=${isBanner}`; + if (isPublish !== undefined) url += `&isPublish=${isPublish}`; + if (isDraft !== undefined) url += `&isDraft=${isDraft}`; + if (startDate) url += `&startDate=${startDate}`; + if (endDate) url += `&endDate=${endDate}`; + + return await httpGetInterceptor(url); +} + +// Alternative function using interface for better type safety +export async function listArticlesWithFilters(filters: ArticleFilters) { + return await listArticles( + filters.page, + filters.totalPage, + filters.title, + filters.description, + filters.categoryId, + filters.category, + filters.typeId, + filters.tags, + filters.createdById, + filters.statusId, + filters.isBanner, + filters.isPublish, + filters.isDraft, + filters.startDate, + filters.endDate + ); +} + +// Backward compatible wrapper for listDataTeks with new articles API +export async function listDataTeksNew( + size: any, + page: any, + isForSelf: any, + isApproval: any, + categoryFilter: any, + statusFilter: any, + needApprovalFromLevel: any, + creator: any, + source: any, + startDate: any, + endDate: any, + title: string = "", + creatorGroup: string = "" +) { + // Convert old parameters to new API format + const categoryId = categoryFilter ? Number(categoryFilter) : undefined; + const statusId = statusFilter?.length > 0 ? Number(statusFilter[0]) : undefined; + + return await listArticles( + page + 1, // API expects 1-based page + Number(size), + title, + undefined, // description + categoryId, + undefined, // category name + 3, // typeId for text content + undefined, // tags + undefined, // createdById + statusId, + undefined, // isBanner + undefined, // isPublish + undefined, // isDraft + startDate, + endDate + ); +} \ No newline at end of file diff --git a/service/content/content.ts b/service/content/content.ts index 6a3924a..3b2485c 100644 --- a/service/content/content.ts +++ b/service/content/content.ts @@ -5,6 +5,60 @@ import { httpPostInterceptor, } from "../http-config/http-interceptor-service"; +// Interface for Articles API filters +export interface ArticleFilters { + page?: number; + totalPage?: number; + title?: string; + description?: string; + categoryId?: number; + category?: string; + typeId?: number; + tags?: string; + createdById?: number; + statusId?: number; + isBanner?: boolean; + isPublish?: boolean; + isDraft?: boolean; + startDate?: string; + endDate?: string; +} + +// Interface for creating new article +export interface CreateArticleData { + title: string; + description: string; + htmlDescription: string; + categoryIds: string; + typeId: number; + tags: string; + isDraft: boolean; + isPublish: boolean; + oldId: number; + slug: string; +} + +// Interface for Article Category +export interface ArticleCategory { + id: number; + title: string; + description: string; + thumbnailUrl: string; + slug: string | null; + tags: string[]; + thumbnailPath: string | null; + parentId: number; + oldCategoryId: number | null; + createdById: number; + statusId: number; + isPublish: boolean; + publishedAt: string | null; + isEnabled: boolean | null; + isActive: boolean; + createdAt: string; + updatedAt: string; +} + // export async function listDataAll( // isForSelf, // isApproval, @@ -171,6 +225,12 @@ export async function createMedia(data: any) { return httpPostInterceptor(url, data); } +// New Articles API - Create Article +export async function createArticle(data: CreateArticleData) { + const url = "articles"; + return httpPostInterceptor(url, data); +} + export async function uploadThumbnail(id: any, data: any) { const url = `media/upload?id=${id}&operation=thumbnail`; const headers = { @@ -179,6 +239,30 @@ export async function uploadThumbnail(id: any, data: any) { return httpPostInterceptor(url, data, headers); } +// New Articles API - Upload Article Files +export async function uploadArticleFiles(articleId: string | number, files: FormData) { + const url = `article-files/${articleId}`; + const headers = { + "Content-Type": "multipart/form-data", + }; + return httpPostInterceptor(url, files, headers); +} + +// New Articles API - Upload Article Thumbnail +export async function uploadArticleThumbnail(articleId: string | number, thumbnail: FormData) { + const url = `articles/thumbnail/${articleId}`; + const headers = { + "Content-Type": "multipart/form-data", + }; + return httpPostInterceptor(url, thumbnail, headers); +} + +// New Articles API - Get Article Categories +export async function listArticleCategories(page: number = 1, limit: number = 100) { + const url = `article-categories?page=${page}&limit=${limit}`; + return httpGetInterceptor(url); +} + export async function detailSPIT(id: any) { const url = `media/spit?id=${id}`; return httpGetInterceptor(url); @@ -232,3 +316,101 @@ export async function postActivityLog(data: any) { const url = `activity`; return httpPost(url, data); } + +// New Articles API function with complete filter support +export async function listArticles( + page: number = 1, + totalPage: number = 10, + title?: string, + description?: string, + categoryId?: number, + category?: string, + typeId?: number, + tags?: string, + createdById?: number, + statusId?: number, + isBanner?: boolean, + isPublish?: boolean, + isDraft?: boolean, + startDate?: string, + endDate?: string +) { + let url = `articles?page=${page}&totalPage=${totalPage}`; + + // Add optional query parameters based on available filters + if (title) url += `&title=${encodeURIComponent(title)}`; + if (description) url += `&description=${encodeURIComponent(description)}`; + if (categoryId !== undefined) url += `&categoryId=${categoryId}`; + if (category) url += `&category=${encodeURIComponent(category)}`; + if (typeId !== undefined) url += `&typeId=${typeId}`; + if (tags) url += `&tags=${encodeURIComponent(tags)}`; + if (createdById !== undefined) url += `&createdById=${createdById}`; + if (statusId !== undefined) url += `&statusId=${statusId}`; + if (isBanner !== undefined) url += `&isBanner=${isBanner}`; + if (isPublish !== undefined) url += `&isPublish=${isPublish}`; + if (isDraft !== undefined) url += `&isDraft=${isDraft}`; + if (startDate) url += `&startDate=${startDate}`; + if (endDate) url += `&endDate=${endDate}`; + + return await httpGetInterceptor(url); +} + +// Alternative function using interface for better type safety +export async function listArticlesWithFilters(filters: ArticleFilters) { + return await listArticles( + filters.page, + filters.totalPage, + filters.title, + filters.description, + filters.categoryId, + filters.category, + filters.typeId, + filters.tags, + filters.createdById, + filters.statusId, + filters.isBanner, + filters.isPublish, + filters.isDraft, + filters.startDate, + filters.endDate + ); +} + +// Backward compatible wrapper for listDataTeks with new articles API +export async function listDataTeksNew( + size: any, + page: any, + isForSelf: any, + isApproval: any, + categoryFilter: any, + statusFilter: any, + needApprovalFromLevel: any, + creator: any, + source: any, + startDate: any, + endDate: any, + title: string = "", + creatorGroup: string = "" +) { + // Convert old parameters to new API format + const categoryId = categoryFilter ? Number(categoryFilter) : undefined; + const statusId = statusFilter?.length > 0 ? Number(statusFilter[0]) : undefined; + + return await listArticles( + page + 1, // API expects 1-based page + Number(size), + title, + undefined, // description + categoryId, + undefined, // category name + 3, // typeId for text content + undefined, // tags + undefined, // createdById + statusId, + undefined, // isBanner + undefined, // isPublish + undefined, // isDraft + startDate, + endDate + ); +} \ No newline at end of file diff --git a/service/http-config/axios-base-instance.ts b/service/http-config/axios-base-instance.ts index e6c5e70..c59c74b 100644 --- a/service/http-config/axios-base-instance.ts +++ b/service/http-config/axios-base-instance.ts @@ -1,11 +1,12 @@ import axios from "axios"; -const baseURL = "https://netidhub.com/api/"; +const baseURL = "https://kontenhumas.com/api/"; const axiosBaseInstance = axios.create({ baseURL, headers: { "content-type": "application/json", + "X-Client-Key": "b1ce6602-07ad-46c2-85eb-0cd6decfefa3", }, withCredentials: true, }); diff --git a/service/http-config/axios-interceptor-instance.ts b/service/http-config/axios-interceptor-instance.ts index a3522e8..28679db 100644 --- a/service/http-config/axios-interceptor-instance.ts +++ b/service/http-config/axios-interceptor-instance.ts @@ -2,7 +2,7 @@ import axios from "axios"; import Cookies from "js-cookie"; import { getCsrfToken, login } from "../auth"; -const baseURL = "https://netidhub.com/api/"; +const baseURL = "https://kontenhumas.com/api/"; const refreshToken = Cookies.get("refresh_token"); @@ -10,6 +10,7 @@ const axiosInterceptorInstance = axios.create({ baseURL, headers: { "content-type": "application/json", + "X-Client-Key": "b1ce6602-07ad-46c2-85eb-0cd6decfefa3", }, withCredentials: true, }); diff --git a/service/landing/landing.ts b/service/landing/landing.ts index 0e34580..a45e84c 100644 --- a/service/landing/landing.ts +++ b/service/landing/landing.ts @@ -12,7 +12,7 @@ export async function getCsrfToken() { "content-type": "application/json", }; return httpGet(pathUrl, headers); - // const url = 'https://netidhub.com/api/csrf'; + // const url = 'https://kontenhumas.com/api/csrf'; // try { // const response = await fetch(url, { // method: 'GET',