@@ -741,7 +842,7 @@ export default function FormImageDetail() {

handleImageLoad(e, index)}
className={`h-[100px] object-cover ${
@@ -963,11 +1064,11 @@ export default function FormImageDetail() {
- {Number(detail?.needApprovalFromLevel) == Number(userLevelId) ||
+ {Number(detail?.needApprovalFromLevel || 0) == Number(userLevelId) ||
(detail?.isInternationalMedia == true &&
detail?.isForwardFromNational == true &&
Number(detail?.statusId) == 1) ? (
- Number(detail?.uploadedById) == Number(userId) ? (
+ Number(detail?.createdById || detail?.uploadedById) == Number(userId) ? (
""
) : (
diff --git a/components/table/table-pagination.tsx b/components/table/table-pagination.tsx
index 06593f5..ff32167 100644
--- a/components/table/table-pagination.tsx
+++ b/components/table/table-pagination.tsx
@@ -35,10 +35,20 @@ const TablePagination = ({
useEffect(() => {
const pageFromUrl = searchParams?.get("page");
+ console.log("pagination: pageFromUrl :", pageFromUrl);
+
if (pageFromUrl) {
const pageIndex = Math.min(Math.max(1, Number(pageFromUrl)), totalPage);
setCurrentPageIndex(pageIndex);
- table.setPageIndex(pageIndex - 1); // Sinkronisasi tabel dengan URL
+
+ console.log("handlePageChange: pageIndex :", pageIndex);
+ console.log("handlePageChange: table.setPageIndex with :", pageIndex - 1);
+
+ table.setPageIndex(pageIndex - 1); // ✅ Konversi 1-based ke 0-based
+ } else {
+ // Jika tidak ada page di URL, set ke halaman 1
+ setCurrentPageIndex(1);
+ table.setPageIndex(0); // ✅ Set ke index 0 untuk halaman 1
}
}, [searchParams, totalPage, table]);
@@ -47,9 +57,13 @@ const TablePagination = ({
const searchParams = new URLSearchParams(window.location.search);
searchParams.set("page", clampedPageIndex.toString());
+ console.log("handlePageChange: pageIndex :", pageIndex);
+ console.log("handlePageChange: clampedPageIndex :", clampedPageIndex);
+ console.log("handlePageChange: table.setPageIndex with :", clampedPageIndex - 1);
+
router.push(`${window.location.pathname}?${searchParams.toString()}`);
setCurrentPageIndex(clampedPageIndex);
- table.setPageIndex(clampedPageIndex - 1); // Perbarui tabel dengan index berbasis 0
+ table.setPageIndex(clampedPageIndex - 1); // ✅ Perbarui tabel dengan index berbasis 0
};
const generatePageNumbers = () => {
diff --git a/components/ui/tabs.tsx b/components/ui/tabs.tsx
new file mode 100644
index 0000000..c1030c2
--- /dev/null
+++ b/components/ui/tabs.tsx
@@ -0,0 +1,55 @@
+"use client"
+
+import * as React from "react"
+import * as TabsPrimitive from "@radix-ui/react-tabs"
+
+import { cn } from "@/lib/utils"
+
+const Tabs = TabsPrimitive.Root
+
+const TabsList = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+TabsList.displayName = TabsPrimitive.List.displayName
+
+const TabsTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
+
+const TabsContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+TabsContent.displayName = TabsPrimitive.Content.displayName
+
+export { Tabs, TabsList, TabsTrigger, TabsContent }
diff --git a/package-lock.json b/package-lock.json
index 7aa83b7..40e9afa 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -20,6 +20,7 @@
"@radix-ui/react-select": "^2.2.5",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-switch": "^1.2.5",
+ "@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-visually-hidden": "^1.2.3",
"@tanstack/react-table": "^8.21.3",
"@tinymce/tinymce-react": "^6.3.0",
@@ -2333,6 +2334,93 @@
}
}
},
+ "node_modules/@radix-ui/react-tabs": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz",
+ "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.3",
+ "@radix-ui/react-context": "1.1.2",
+ "@radix-ui/react-direction": "1.1.1",
+ "@radix-ui/react-id": "1.1.1",
+ "@radix-ui/react-presence": "1.1.5",
+ "@radix-ui/react-primitive": "2.1.3",
+ "@radix-ui/react-roving-focus": "1.1.11",
+ "@radix-ui/react-use-controllable-state": "1.2.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/primitive": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
+ "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="
+ },
+ "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-presence": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz",
+ "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2",
+ "@radix-ui/react-use-layout-effect": "1.1.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-roving-focus": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz",
+ "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.3",
+ "@radix-ui/react-collection": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.2",
+ "@radix-ui/react-context": "1.1.2",
+ "@radix-ui/react-direction": "1.1.1",
+ "@radix-ui/react-id": "1.1.1",
+ "@radix-ui/react-primitive": "2.1.3",
+ "@radix-ui/react-use-callback-ref": "1.1.1",
+ "@radix-ui/react-use-controllable-state": "1.2.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-use-callback-ref": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
diff --git a/package.json b/package.json
index eb4e186..8f43152 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
"@radix-ui/react-select": "^2.2.5",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-switch": "^1.2.5",
+ "@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-visually-hidden": "^1.2.3",
"@tanstack/react-table": "^8.21.3",
"@tinymce/tinymce-react": "^6.3.0",
diff --git a/service/content/content.ts b/service/content/content.ts
index 3b2485c..90a54eb 100644
--- a/service/content/content.ts
+++ b/service/content/content.ts
@@ -413,4 +413,125 @@ export async function listDataTeksNew(
startDate,
endDate
);
+}
+
+// Interface for pending approval data
+export interface PendingApprovalData {
+ id: number;
+ title: string;
+ slug: string;
+ description: string;
+ categoryName: string;
+ authorName: string;
+ submittedAt: string;
+ currentStep: number;
+ totalSteps: number;
+ priority: string;
+ daysInQueue: number;
+ workflowName: string;
+ canApprove: boolean;
+ estimatedTime: string;
+}
+
+export interface PendingApprovalResponse {
+ success: boolean;
+ code: number;
+ messages: string[];
+ data: PendingApprovalData[];
+}
+
+// Function to fetch pending approval data
+export async function listPendingApproval(
+ page: number = 1,
+ limit: number = 10
+) {
+ const url = `articles/pending-approval?page=${page}&limit=${limit}`;
+ return await httpGetInterceptor(url);
+}
+
+// Interface for article file data
+export interface ArticleFileData {
+ id: number;
+ articleId: number;
+ filePath: string;
+ fileUrl: string;
+ fileName: string;
+ fileThumbnail: string | null;
+ fileAlt: string;
+ widthPixel: number | null;
+ heightPixel: number | null;
+ size: string;
+ downloadCount: number;
+ createdById: number;
+ statusId: number;
+ isPublish: boolean;
+ publishedAt: string | null;
+ isActive: boolean;
+ createdAt: string;
+ updatedAt: string;
+}
+
+// Interface for article category data
+export interface ArticleCategoryData {
+ 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;
+}
+
+// Interface for article detail data
+export interface ArticleDetailData {
+ id: number;
+ title: string;
+ slug: string;
+ description: string;
+ htmlDescription: string;
+ categoryId: number;
+ categoryName: string;
+ typeId: number;
+ tags: string;
+ thumbnailUrl: string;
+ pageUrl: string | null;
+ createdById: number;
+ createdByName: string;
+ shareCount: number;
+ viewCount: number;
+ commentCount: number;
+ aiArticleId: number | null;
+ oldId: number;
+ statusId: number;
+ isBanner: boolean;
+ isPublish: boolean;
+ publishedAt: string | null;
+ isActive: boolean;
+ createdAt: string;
+ updatedAt: string;
+ files: ArticleFileData[];
+ categories: ArticleCategoryData[];
+}
+
+export interface ArticleDetailResponse {
+ success: boolean;
+ code: number;
+ messages: string[];
+ data: ArticleDetailData;
+}
+
+// Function to fetch article detail
+export async function getArticleDetail(id: number) {
+ const url = `articles/${id}`;
+ return await httpGetInterceptor(url);
}
\ No newline at end of file