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',