feat: update pagination and create image, update login

This commit is contained in:
hanif salafi 2025-09-17 08:43:27 +07:00
parent 4b25136c6f
commit d07e92aa2a
19 changed files with 769 additions and 205 deletions

View File

@ -51,11 +51,17 @@ const useTableColumns = () => {
{
accessorKey: "categoryName",
header: "Category Name",
cell: ({ row }) => (
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 (
<span className="whitespace-nowrap">
{row.getValue("categoryName")}
{displayName}
</span>
),
);
},
},
{
accessorKey: "createdAt",
@ -77,7 +83,9 @@ const useTableColumns = () => {
accessorKey: "creatorName",
header: "Creator Group",
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("creatorName")}</span>
<span className="whitespace-nowrap">
{row.getValue("creatorName") || row.getValue("createdByName")}
</span>
),
},
{
@ -85,7 +93,7 @@ const useTableColumns = () => {
header: "Source",
cell: ({ row }) => (
<span className="whitespace-nowrap">
{row.getValue("creatorGroupLevelName")}
{row.getValue("creatorGroupLevelName") || "-"}
</span>
),
},

View File

@ -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;
// 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);
}

View File

@ -51,11 +51,17 @@ const useTableColumns = () => {
{
accessorKey: "categoryName",
header: "Category Name",
cell: ({ row }) => (
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 (
<span className="whitespace-nowrap">
{row.getValue("categoryName")}
{displayName}
</span>
),
);
},
},
{
accessorKey: "createdAt",
@ -77,7 +83,9 @@ const useTableColumns = () => {
accessorKey: "creatorName",
header: "Creator Group",
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("creatorName")}</span>
<span className="whitespace-nowrap">
{row.getValue("creatorName") || row.getValue("createdByName")}
</span>
),
},
{
@ -85,7 +93,7 @@ const useTableColumns = () => {
header: "Source",
cell: ({ row }) => (
<span className="whitespace-nowrap">
{row.getValue("creatorGroupLevelName")}
{row.getValue("creatorGroupLevelName") || "-"}
</span>
),
},

View File

@ -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;
// 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);
}

View File

@ -51,11 +51,17 @@ const useTableColumns = () => {
{
accessorKey: "categoryName",
header: "Category Name",
cell: ({ row }) => (
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 (
<span className="whitespace-nowrap">
{row.getValue("categoryName")}
{displayName}
</span>
),
);
},
},
{
accessorKey: "createdAt",
@ -77,7 +83,9 @@ const useTableColumns = () => {
accessorKey: "creatorName",
header: "Creator Group",
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("creatorName")}</span>
<span className="whitespace-nowrap">
{row.getValue("creatorName") || row.getValue("createdByName")}
</span>
),
},
{
@ -85,7 +93,7 @@ const useTableColumns = () => {
header: "Source",
cell: ({ row }) => (
<span className="whitespace-nowrap">
{row.getValue("creatorGroupLevelName")}
{row.getValue("creatorGroupLevelName") || "-"}
</span>
),
},

View File

@ -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;
// 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);
}

View File

@ -53,11 +53,17 @@ const useTableColumns = () => {
{
accessorKey: "categoryName",
header: "Category Name",
cell: ({ row }) => (
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 (
<span className="whitespace-nowrap">
{row.getValue("categoryName")}
{displayName}
</span>
),
);
},
},
{
accessorKey: "createdAt",
@ -79,7 +85,9 @@ const useTableColumns = () => {
accessorKey: "creatorName",
header: "Creator Group",
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("creatorName")}</span>
<span className="whitespace-nowrap">
{row.getValue("creatorName") || row.getValue("createdByName")}
</span>
),
},
{
@ -87,7 +95,7 @@ const useTableColumns = () => {
header: "Source",
cell: ({ row }) => (
<span className="whitespace-nowrap">
{row.getValue("creatorGroupLevelName")}
{row.getValue("creatorGroupLevelName") || "-"}
</span>
),
},

View File

@ -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;
// 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);
}

View File

@ -28,6 +28,7 @@ const AuthPage = () => {
setLoginCredentials(data);
try {
const result = await validateEmail(data);
console.log("result : ", result);
switch (result) {
case "skip":
handleOTPSuccess();

View File

@ -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,38 +626,103 @@ 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");
};

View File

@ -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");

View File

@ -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;
}

View File

@ -2,7 +2,7 @@ import type { NextConfig } from "next";
const nextConfig = {
images: {
domains: ["netidhub.com"],
domains: ["kontenhumas.com"],
},
};

View File

@ -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() {

View File

@ -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
);
}

View File

@ -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
);
}

View File

@ -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,
});

View File

@ -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,
});

View File

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