From 93b31c7d4e31c721e639606683ae8ff6a2040c25 Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Thu, 3 Jul 2025 16:41:44 +0700 Subject: [PATCH 01/16] feat: update minor --- app/popular/page.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/popular/page.tsx b/app/popular/page.tsx index 3684a13..60e05d8 100644 --- a/app/popular/page.tsx +++ b/app/popular/page.tsx @@ -1,10 +1,6 @@ -import Author from "@/components/landing-page/author"; import Footer from "@/components/landing-page/footer"; import Header from "@/components/landing-page/header"; -import Latest from "@/components/landing-page/latest"; -import LatestandPopular from "@/components/landing-page/latest-and-popular"; import Navbar from "@/components/landing-page/navbar"; -import News from "@/components/landing-page/news"; import Image from "next/image"; export default function Home() { From 132485e5fc1b091ad760397cd56e3d5bf7af5d2d Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Thu, 3 Jul 2025 17:15:38 +0700 Subject: [PATCH 02/16] feat: update articles admin --- components/table/article-table.tsx | 11 +++----- service/article.ts | 26 +++++++++++++++++-- service/http-config/http-base-services.ts | 4 +-- .../http-config/http-interceptor-services.ts | 3 ++- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/components/table/article-table.tsx b/components/table/article-table.tsx index ff15804..319f42a 100644 --- a/components/table/article-table.tsx +++ b/components/table/article-table.tsx @@ -19,7 +19,7 @@ import Cookies from "js-cookie"; import { deleteArticle, getArticleByCategory, - getListArticle, + getArticlePagination, updateIsBannerArticle, } from "@/service/article"; import { @@ -91,9 +91,6 @@ export default function ArticleTable() { useEffect(() => { initState(); - }, [page, showData, startDateValue, selectedCategories]); - - useEffect(() => { getCategories(); }, []); @@ -103,7 +100,7 @@ export default function ArticleTable() { setCategories(data); } - async function initState() { + const initState = useCallback(async () => { loading(); const req = { limit: showData, @@ -116,11 +113,11 @@ export default function ArticleTable() { sort: "desc", sortBy: "created_at", }; - const res = await getListArticle(req); + const res = await getArticlePagination(req); await getTableNumber(parseInt(showData), res.data?.data); setTotalPage(res?.data?.meta?.totalPage); close(); - } + }, [page]); const getTableNumber = async (limit: number, data: Article[]) => { if (data) { diff --git a/service/article.ts b/service/article.ts index 98d1d5b..5d570aa 100644 --- a/service/article.ts +++ b/service/article.ts @@ -1,7 +1,7 @@ import { PaginationRequest } from "@/types/globals"; import Cookies from "js-cookie"; import { httpGet } from "./http-config/http-base-services"; -import { httpDeleteInterceptor, httpPostInterceptor, httpPutInterceptor } from "./http-config/http-interceptor-services"; +import { httpDeleteInterceptor, httpGetInterceptor, httpPostInterceptor, httpPutInterceptor } from "./http-config/http-interceptor-services"; export async function getListArticle(props: PaginationRequest) { const { @@ -29,6 +29,28 @@ export async function getListArticle(props: PaginationRequest) { ); } +export async function getArticlePagination(props: PaginationRequest) { + const { + page, + limit, + search, + startDate, + endDate, + category, + sortBy, + sort, + categorySlug, + isBanner, + } = props; + return await httpGetInterceptor( + `/articles?limit=${limit}&page=${page}&title=${search}&startDate=${startDate || ""}&endDate=${ + endDate || "" + }&categoryId=${category || ""}&sortBy=${sortBy || "created_at"}&sort=${ + sort || "asc" + }&category=${categorySlug || ""}&isBanner=${isBanner || ""}` + ); +} + export async function getTopArticles(props: PaginationRequest) { const { page, limit, search, startDate, endDate, isPublish, category } = props; @@ -75,7 +97,7 @@ export async function deleteArticle(id: string) { } export async function getArticleByCategory() { - return await httpGet(`/article-categories?limit=1000`); + return await httpGetInterceptor(`/article-categories?limit=1000`); } export async function getCategoryPagination(data: any) { return await httpGet( diff --git a/service/http-config/http-base-services.ts b/service/http-config/http-base-services.ts index 92e46f9..f958b02 100644 --- a/service/http-config/http-base-services.ts +++ b/service/http-config/http-base-services.ts @@ -5,8 +5,8 @@ const defaultHeaders = { "X-Client-Key": process.env.MEDOLS_CLIENT_KEY }; -export async function httpGet(pathUrl: any, headers?: any) { - +export async function httpGet(pathUrl: any, headers?: any) { + console.log("X-HEADERS : ", defaultHeaders) const mergedHeaders = { ...defaultHeaders, ...headers, diff --git a/service/http-config/http-interceptor-services.ts b/service/http-config/http-interceptor-services.ts index a9238b6..4dbe657 100644 --- a/service/http-config/http-interceptor-services.ts +++ b/service/http-config/http-interceptor-services.ts @@ -9,8 +9,9 @@ const defaultHeaders = { }; export async function httpGetInterceptor(pathUrl: any) { + console.log("X-HEADERS : ", defaultHeaders) const response = await axiosInterceptorInstance - .get(pathUrl) + .get(pathUrl, { headers: defaultHeaders }) .catch((error) => error.response); console.log("Response interceptor : ", response); if (response?.status == 200 || response?.status == 201) { From 3f4476977b158cbace903dc54a0a7a1f8d253785 Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Thu, 3 Jul 2025 17:41:08 +0700 Subject: [PATCH 03/16] feat: update default images --- public/default-image.jpg | Bin 0 -> 158082 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 public/default-image.jpg diff --git a/public/default-image.jpg b/public/default-image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ce9446e24bd04702891ba02160cb0bb346d14572 GIT binary patch literal 158082 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 literal 0 HcmV?d00001 From cd30bc4561c4101ca222e95bfc5646f2c7299129 Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Thu, 3 Jul 2025 19:23:45 +0700 Subject: [PATCH 04/16] feat: update Dockerfile --- Dockerfile | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 335d444..5b931f0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ FROM node:23.5.0-alpine ENV PORT 3000 # Install pnpm secara global -RUN npm install -g pnpm +RUN npm install -g npm # Membuat direktori aplikasi dan mengatur sebagai working directory WORKDIR /usr/src/app @@ -17,17 +17,18 @@ COPY package.json ./ COPY vendor/ckeditor5 ./vendor/ckeditor5 # Install dependencies -RUN pnpm install +RUN npm install # RUN pnpm install --frozen-lockfile # Menyalin source code aplikasi COPY . . # Build aplikasi -RUN NODE_OPTIONS="--max-old-space-size=4096" pnpm next build +# RUN NODE_OPTIONS="--max-old-space-size=4096" npm run build +RUN npm run build # Expose port untuk server EXPOSE 3000 # Perintah untuk menjalankan aplikasi -CMD ["pnpm", "run", "start"] \ No newline at end of file +CMD ["npm", "run", "start"] \ No newline at end of file From cabd73822fb5058003e002d73dca0eb00d34d88a Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Thu, 3 Jul 2025 21:18:02 +0700 Subject: [PATCH 05/16] feat: update Dockerfile --- Dockerfile | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5b931f0..335d444 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ FROM node:23.5.0-alpine ENV PORT 3000 # Install pnpm secara global -RUN npm install -g npm +RUN npm install -g pnpm # Membuat direktori aplikasi dan mengatur sebagai working directory WORKDIR /usr/src/app @@ -17,18 +17,17 @@ COPY package.json ./ COPY vendor/ckeditor5 ./vendor/ckeditor5 # Install dependencies -RUN npm install +RUN pnpm install # RUN pnpm install --frozen-lockfile # Menyalin source code aplikasi COPY . . # Build aplikasi -# RUN NODE_OPTIONS="--max-old-space-size=4096" npm run build -RUN npm run build +RUN NODE_OPTIONS="--max-old-space-size=4096" pnpm next build # Expose port untuk server EXPOSE 3000 # Perintah untuk menjalankan aplikasi -CMD ["npm", "run", "start"] \ No newline at end of file +CMD ["pnpm", "run", "start"] \ No newline at end of file From af9000498a11139215ae99ac065cea9b73a62b5c Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Thu, 3 Jul 2025 21:35:45 +0700 Subject: [PATCH 06/16] feat: update bug fixing chunk --- app/layout.tsx | 6 +- .../landing-page/latest-and-popular.tsx | 12 +- components/layout/chunk-error-boundary.tsx | 104 ++++++++++++++++++ .../main/dashboard/chart/column-chart.tsx | 7 +- next.config.ts | 47 ++++++++ utils/chunk-error-handler.ts | 87 +++++++++++++++ utils/dynamic-import.ts | 70 ++++++++++++ 7 files changed, 325 insertions(+), 8 deletions(-) create mode 100644 components/layout/chunk-error-boundary.tsx create mode 100644 utils/chunk-error-handler.ts create mode 100644 utils/dynamic-import.ts diff --git a/app/layout.tsx b/app/layout.tsx index 63e2178..e9bc5ad 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,6 +1,8 @@ import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; +import ChunkErrorBoundary from "@/components/layout/chunk-error-boundary"; +import "@/utils/chunk-error-handler"; const geistSans = Geist({ variable: "--font-geist-sans", @@ -25,7 +27,9 @@ export default function RootLayout({ return ( - {children} + + {children} + ); diff --git a/components/landing-page/latest-and-popular.tsx b/components/landing-page/latest-and-popular.tsx index c3942b2..67bdd64 100644 --- a/components/landing-page/latest-and-popular.tsx +++ b/components/landing-page/latest-and-popular.tsx @@ -250,7 +250,11 @@ export default function LatestandPopular() {
{"article.title"}
{"article?.title"} { + constructor(props: Props) { + super(props); + this.state = { hasError: false }; + } + + static getDerivedStateFromError(error: Error): State { + // Check if it's a chunk loading error + if (error.name === 'ChunkLoadError' || error.message.includes('Loading chunk')) { + return { hasError: true, error }; + } + return { hasError: false }; + } + + componentDidCatch(error: Error, errorInfo: ErrorInfo) { + console.error('Chunk loading error:', error, errorInfo); + + // If it's a chunk loading error, try to reload the page + if (error.name === 'ChunkLoadError' || error.message.includes('Loading chunk')) { + this.setState({ hasError: true, error }); + } + } + + handleRetry = () => { + // Clear the error state and reload the page + this.setState({ hasError: false, error: undefined }); + window.location.reload(); + }; + + render() { + if (this.state.hasError) { + if (this.props.fallback) { + return this.props.fallback; + } + + return ( +
+
+
+
+ +
+

+ Chunk Loading Error +

+

+ There was an issue loading a part of the application. This usually happens when the application has been updated. +

+
+ +
+ + + +
+ + {process.env.NODE_ENV === 'development' && this.state.error && ( +
+ + Error Details (Development) + +
+                  {this.state.error.message}
+                
+
+ )} +
+
+ ); + } + + return this.props.children; + } +} + +export default ChunkErrorBoundary; \ No newline at end of file diff --git a/components/main/dashboard/chart/column-chart.tsx b/components/main/dashboard/chart/column-chart.tsx index 747bd02..d29575c 100644 --- a/components/main/dashboard/chart/column-chart.tsx +++ b/components/main/dashboard/chart/column-chart.tsx @@ -1,7 +1,7 @@ "use client"; import { getStatisticMonthly } from "@/service/article"; import React, { useEffect, useState } from "react"; -import dynamic from "next/dynamic"; +import { SafeReactApexChart } from "@/utils/dynamic-import"; type WeekData = { week: number; @@ -53,9 +53,6 @@ const ApexChartColumn = (props: { const [seriesComment, setSeriesComment] = useState([]); const [seriesView, setSeriesView] = useState([]); const [seriesShare, setSeriesShare] = useState([]); - const ReactApexChart = dynamic(() => import("react-apexcharts"), { - ssr: false, - }); useEffect(() => { initFetch(); @@ -132,7 +129,7 @@ const ApexChartColumn = (props: { return (
- { + // Handle chunk loading errors + config.optimization = { + ...config.optimization, + splitChunks: { + ...config.optimization.splitChunks, + chunks: 'all', + cacheGroups: { + ...config.optimization.splitChunks?.cacheGroups, + vendor: { + test: /[\\/]node_modules[\\/]/, + name: 'vendors', + chunks: 'all', + }, + // Separate CKEditor chunks + ckeditor: { + test: /[\\/]node_modules[\\/]@ckeditor[\\/]/, + name: 'ckeditor', + chunks: 'all', + priority: 20, + }, + // Separate ApexCharts chunks + apexcharts: { + test: /[\\/]node_modules[\\/](apexcharts|react-apexcharts)[\\/]/, + name: 'apexcharts', + chunks: 'all', + priority: 20, + }, + }, + }, + }; + + // Add error handling for chunk loading + if (!isServer) { + config.output = { + ...config.output, + chunkFilename: '[name].[chunkhash].js', + filename: '[name].[chunkhash].js', + }; + } + + return config; + }, + // Add experimental features for better chunk handling + experimental: { + optimizePackageImports: ['@ckeditor/ckeditor5-react', 'react-apexcharts'], + }, }; export default nextConfig; diff --git a/utils/chunk-error-handler.ts b/utils/chunk-error-handler.ts new file mode 100644 index 0000000..fc09c39 --- /dev/null +++ b/utils/chunk-error-handler.ts @@ -0,0 +1,87 @@ +// Global chunk loading error handler +export function setupChunkErrorHandler() { + if (typeof window === 'undefined') return; + + // Handle chunk loading errors + window.addEventListener('error', (event) => { + const error = event.error; + + // Check if it's a chunk loading error + if ( + error?.name === 'ChunkLoadError' || + error?.message?.includes('Loading chunk') || + error?.message?.includes('Failed to fetch') + ) { + console.warn('Chunk loading error detected:', error); + + // Prevent the error from being logged to console + event.preventDefault(); + + // Show a user-friendly message + const message = document.createElement('div'); + message.style.cssText = ` + position: fixed; + top: 20px; + right: 20px; + background: #fef2f2; + border: 1px solid #fecaca; + color: #dc2626; + padding: 12px 16px; + border-radius: 8px; + font-size: 14px; + z-index: 9999; + max-width: 300px; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); + `; + message.innerHTML = ` +
+ ⚠️ + Application update detected. Please refresh the page. +
+ + `; + + document.body.appendChild(message); + + // Auto-remove after 10 seconds + setTimeout(() => { + if (message.parentNode) { + message.parentNode.removeChild(message); + } + }, 10000); + } + }); + + // Handle unhandled promise rejections (which might include chunk loading errors) + window.addEventListener('unhandledrejection', (event) => { + const error = event.reason; + + if ( + error?.name === 'ChunkLoadError' || + error?.message?.includes('Loading chunk') || + error?.message?.includes('Failed to fetch') + ) { + console.warn('Unhandled chunk loading rejection:', error); + event.preventDefault(); + + // Reload the page after a short delay + setTimeout(() => { + window.location.reload(); + }, 1000); + } + }); +} + +// Auto-setup when this module is imported +if (typeof window !== 'undefined') { + setupChunkErrorHandler(); +} \ No newline at end of file diff --git a/utils/dynamic-import.ts b/utils/dynamic-import.ts new file mode 100644 index 0000000..2469892 --- /dev/null +++ b/utils/dynamic-import.ts @@ -0,0 +1,70 @@ +import dynamic from 'next/dynamic'; +import { ComponentType } from 'react'; + +interface DynamicImportOptions { + ssr?: boolean; + loading?: () => React.ReactElement; + retries?: number; + retryDelay?: number; +} + +export function createSafeDynamicImport>( + importFn: () => Promise<{ default: T }>, + options: DynamicImportOptions = {} +) { + const { ssr = false, loading, retries = 3, retryDelay = 1000 } = options; + + return dynamic( + () => { + return new Promise((resolve, reject) => { + let attempts = 0; + + const attemptImport = async () => { + try { + const module = await importFn(); + resolve(module.default); + } catch (error) { + attempts++; + + // Check if it's a chunk loading error + if ( + (error as any)?.name === 'ChunkLoadError' || + (error as any)?.message?.includes('Loading chunk') || + (error as any)?.message?.includes('Failed to fetch') + ) { + if (attempts < retries) { + console.warn(`Chunk loading failed, retrying... (${attempts}/${retries})`); + setTimeout(attemptImport, retryDelay); + return; + } + } + + reject(error); + } + }; + + attemptImport(); + }); + }, + { + ssr, + loading, + } + ); +} + +// Predefined safe dynamic imports for common components +export const SafeCustomEditor = createSafeDynamicImport( + () => import('@/components/editor/custom-editor'), + { ssr: false } +); + +export const SafeViewEditor = createSafeDynamicImport( + () => import('@/components/editor/view-editor'), + { ssr: false } +); + +export const SafeReactApexChart = createSafeDynamicImport( + () => import('react-apexcharts'), + { ssr: false } +); \ No newline at end of file From b76f661b809b95dc12cd6ccf842ec63d7494aa2d Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Thu, 3 Jul 2025 21:55:53 +0700 Subject: [PATCH 07/16] feat: update fixing user services --- service/http-config/http-base-services.ts | 8 +- .../http-config/http-interceptor-services.ts | 2 +- service/master-user.ts | 94 ++++++------------- 3 files changed, 34 insertions(+), 70 deletions(-) diff --git a/service/http-config/http-base-services.ts b/service/http-config/http-base-services.ts index f958b02..9a87ffc 100644 --- a/service/http-config/http-base-services.ts +++ b/service/http-config/http-base-services.ts @@ -33,9 +33,13 @@ export async function httpGet(pathUrl: any, headers?: any) { } } -export async function httpPost(pathUrl: any, headers: any, data: any) { +export async function httpPost(pathUrl: any, data: any, headers?: any) { + const mergedHeaders = { + ...defaultHeaders, + ...headers, + }; const response = await axiosBaseInstance - .post(pathUrl, data, { headers }) + .post(pathUrl, data, { headers: mergedHeaders }) .catch(function (error) { console.log(error); return error.response; diff --git a/service/http-config/http-interceptor-services.ts b/service/http-config/http-interceptor-services.ts index 4dbe657..416b738 100644 --- a/service/http-config/http-interceptor-services.ts +++ b/service/http-config/http-interceptor-services.ts @@ -98,7 +98,7 @@ export async function httpPutInterceptor(pathUrl: any, data: any, headers?: any) } } -export async function httpDeleteInterceptor(pathUrl: any, headers: any) { +export async function httpDeleteInterceptor(pathUrl: any, headers?: any) { const resCsrf = await getCsrfToken(); const csrfToken = resCsrf?.data?.csrf_token; diff --git a/service/master-user.ts b/service/master-user.ts index cf875e1..f92f9db 100644 --- a/service/master-user.ts +++ b/service/master-user.ts @@ -1,6 +1,6 @@ -import { httpDeleteInterceptor, httpGet, httpPost, httpPut } from "./http-config/axios-base-service"; import Cookies from "js-cookie"; -import axiosBaseInstance from "./http-config/http-base-instance"; +import { httpGet, httpPost } from "./http-config/http-base-services"; +import { httpDeleteInterceptor, httpGetInterceptor, httpPostInterceptor, httpPutInterceptor } from "./http-config/http-interceptor-services"; const token = Cookies.get("access_token"); const id = Cookies.get("uie"); @@ -13,46 +13,32 @@ export async function listMasterUsers(data: any) { } export async function createMasterUser(data: any) { - const headers = { - "content-type": "application/json", - }; const pathUrl = `/users`; - return await httpPost(pathUrl, headers, data); + return await httpPostInterceptor(pathUrl, data); } + export async function emailValidation(data: any) { - const headers = { - "content-type": "application/json", - }; const pathUrl = `/users/email-validation`; - return await httpPost(pathUrl, headers, data); + return await httpPost(pathUrl, data); } export async function setupEmail(data: any) { - const headers = { - "content-type": "application/json", - }; const pathUrl = `/users/setup-email`; - return await httpPost(pathUrl, headers, data); + return await httpPost(pathUrl, data); } export async function getDetailMasterUsers(id: string) { - const headers = { - "content-type": "application/json", - }; - return await httpGet(`/users/detail/${id}`, headers); + const pathUrl = `/users/detail/${id}`; + return await httpGetInterceptor(pathUrl); } export async function editMasterUsers(data: any, id: string) { - const headers = { - "content-type": "application/json", - }; - return await httpPut(`/users/${id}`, headers, data); + const pathUrl = `/users/${id}` + return await httpPutInterceptor(pathUrl, data); } export async function deleteMasterUser(id: string) { - const headers = { - "content-type": "application/json", - }; - return await httpDeleteInterceptor(`/users/${id}`, headers); + const pathUrl = `/users/${id}` + return await httpDeleteInterceptor(pathUrl); } export async function postSignIn(data: any) { @@ -73,18 +59,12 @@ export async function getProfile(code?: string) { } export async function updateProfile(data: any) { - const headers = { - "content-type": "application/json", - Authorization: `Bearer ${token}`, - }; - return await httpPut(`/users/${id}`, headers, data); + const pathUrl = `/users/${id}`; + return await httpPutInterceptor(pathUrl, data); } export async function savePassword(data: any) { - const headers = { - "content-type": "application/json", - Authorization: `Bearer ${token}`, - }; - return await httpPost(`/users/save-password`, headers, data); + const pathUrl = `/users/save-password` + return await httpPostInterceptor(pathUrl, data); } export async function resetPassword(data: any) { @@ -102,23 +82,13 @@ export async function checkUsernames(username: string) { } export async function otpRequest(email: string, name: string) { - const headers = { - "content-type": "application/json", - }; - return await httpPost(`/users/otp-request`, headers, { email, name }); + const pathUrl = `/users/otp-request`; + return await httpPost(pathUrl, { email, name }); } export async function otpValidation(email: string, otpCode: string) { - const headers = { - "content-type": "application/json", - }; - return await httpPost(`/users/otp-validation`, headers, { email, otpCode }); -} -export async function otpValidationLogin(data: any) { - const headers = { - "content-type": "application/json", - }; - return await httpPost(`/users/otp-validation`, headers, data); + const pathUrl = `/users/otp-validation` + return await httpPost(pathUrl, { email, otpCode }); } export async function postArticleComment(data: any) { @@ -134,31 +104,21 @@ export async function postArticleComment(data: any) { } export async function editArticleComment(data: any, id: number) { - const headers = { - "content-type": "application/json", - Authorization: `Bearer ${token}`, - }; - return await httpPut(`/article-comments/${id}`, headers, data); + const pathUrl = `/article-comments/${id}`; + return await httpPutInterceptor(pathUrl, data); } export async function getArticleComment(id: string) { - const headers = { - "content-type": "application/json", - }; - return await httpGet(`/article-comments?isPublic=true&articleId=${id}`, headers); + const pathUrl = `/article-comments?isPublic=true&articleId=${id}`; + return await httpGet(pathUrl); } export async function deleteArticleComment(id: number) { - const headers = { - "content-type": "application/json", - }; - return await httpDeleteInterceptor(`/article-comments/${id}`, headers); + const pathUrl = `/article-comments/${id}` + return await httpDeleteInterceptor(pathUrl); } export async function getCsrfToken() { const pathUrl = "csrf-token"; - const headers = { - "content-type": "application/json", - }; - return httpGet(pathUrl, headers); + return httpGet(pathUrl); } \ No newline at end of file From 9dbc02dd0f362d74dcad1f5044a565db2ec0a45c Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Thu, 3 Jul 2025 22:11:17 +0700 Subject: [PATCH 08/16] feat: update added env --- .env | 1 + .env.local | 1 + .gitignore | 3 --- 3 files changed, 2 insertions(+), 3 deletions(-) create mode 100644 .env create mode 100644 .env.local diff --git a/.env b/.env new file mode 100644 index 0000000..f104216 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +MEDOLS_CLIENT_KEY=bb65b1ad-e954-4a1a-b4d0-74df5bb0b640 \ No newline at end of file diff --git a/.env.local b/.env.local new file mode 100644 index 0000000..f104216 --- /dev/null +++ b/.env.local @@ -0,0 +1 @@ +MEDOLS_CLIENT_KEY=bb65b1ad-e954-4a1a-b4d0-74df5bb0b640 \ No newline at end of file diff --git a/.gitignore b/.gitignore index bfe57c0..acf2fe1 100644 --- a/.gitignore +++ b/.gitignore @@ -30,9 +30,6 @@ yarn-debug.log* yarn-error.log* .pnpm-debug.log* -# env files (can opt-in for committing if needed) -.env* - # vercel .vercel From e46cc9141d1a128b5919b45af5986f8314e03582 Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Thu, 3 Jul 2025 22:45:34 +0700 Subject: [PATCH 09/16] feat: rollback next.config --- next.config.ts | 43 ------------------------------------------- 1 file changed, 43 deletions(-) diff --git a/next.config.ts b/next.config.ts index b2329f5..1763619 100644 --- a/next.config.ts +++ b/next.config.ts @@ -10,49 +10,6 @@ const nextConfig: NextConfig = { eslint: { ignoreDuringBuilds: true, }, - webpack: (config, { isServer }) => { - // Handle chunk loading errors - config.optimization = { - ...config.optimization, - splitChunks: { - ...config.optimization.splitChunks, - chunks: 'all', - cacheGroups: { - ...config.optimization.splitChunks?.cacheGroups, - vendor: { - test: /[\\/]node_modules[\\/]/, - name: 'vendors', - chunks: 'all', - }, - // Separate CKEditor chunks - ckeditor: { - test: /[\\/]node_modules[\\/]@ckeditor[\\/]/, - name: 'ckeditor', - chunks: 'all', - priority: 20, - }, - // Separate ApexCharts chunks - apexcharts: { - test: /[\\/]node_modules[\\/](apexcharts|react-apexcharts)[\\/]/, - name: 'apexcharts', - chunks: 'all', - priority: 20, - }, - }, - }, - }; - - // Add error handling for chunk loading - if (!isServer) { - config.output = { - ...config.output, - chunkFilename: '[name].[chunkhash].js', - filename: '[name].[chunkhash].js', - }; - } - - return config; - }, // Add experimental features for better chunk handling experimental: { optimizePackageImports: ['@ckeditor/ckeditor5-react', 'react-apexcharts'], From cd7c464466c0266d00f9a0a9c017abd6f88d4c15 Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Thu, 3 Jul 2025 22:56:44 +0700 Subject: [PATCH 10/16] feat: update env --- .env.local | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .env.local diff --git a/.env.local b/.env.local deleted file mode 100644 index f104216..0000000 --- a/.env.local +++ /dev/null @@ -1 +0,0 @@ -MEDOLS_CLIENT_KEY=bb65b1ad-e954-4a1a-b4d0-74df5bb0b640 \ No newline at end of file From a90d65844617a6141f0de63cebc5a0bf5c3a7e6d Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Thu, 3 Jul 2025 22:58:11 +0700 Subject: [PATCH 11/16] feat: update DOckerfile --- Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dockerfile b/Dockerfile index 335d444..2b874db 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,6 +16,9 @@ COPY package.json ./ # Menyalin direktori ckeditor5 jika diperlukan COPY vendor/ckeditor5 ./vendor/ckeditor5 +# Menyalin env +COPY .env .env + # Install dependencies RUN pnpm install # RUN pnpm install --frozen-lockfile From e6ab2f60ebdb4562f31cf57a2d8e3adc71f2e33c Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Thu, 3 Jul 2025 23:10:17 +0700 Subject: [PATCH 12/16] feat: update env clientKey name --- .env | 2 +- next.config.ts | 3 --- service/http-config/axios-interceptor-instance.ts | 2 +- service/http-config/http-base-instance.ts | 2 +- service/http-config/http-base-services.ts | 2 +- service/http-config/http-interceptor-services.ts | 2 +- 6 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.env b/.env index f104216..d5b009c 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -MEDOLS_CLIENT_KEY=bb65b1ad-e954-4a1a-b4d0-74df5bb0b640 \ No newline at end of file +NEXT_PUBLIC_MEDOLS_CLIENT_KEY=bb65b1ad-e954-4a1a-b4d0-74df5bb0b640 \ No newline at end of file diff --git a/next.config.ts b/next.config.ts index 1763619..9e3686f 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,9 +1,6 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - env: { - MEDOLS_CLIENT_KEY: process.env.MEDOLS_CLIENT_KEY, - }, images: { domains: ["mikulnews.com", "dev.mikulnews.com"], }, diff --git a/service/http-config/axios-interceptor-instance.ts b/service/http-config/axios-interceptor-instance.ts index e90570d..f44e5f1 100644 --- a/service/http-config/axios-interceptor-instance.ts +++ b/service/http-config/axios-interceptor-instance.ts @@ -10,7 +10,7 @@ const axiosInterceptorInstance = axios.create({ baseURL, headers: { "Content-Type": "application/json", - "X-Client-Key": process.env.MEDOLS_CLIENT_KEY + "X-Client-Key": process.env.NEXT_PUBLIC_MEDOLS_CLIENT_KEY }, withCredentials: true, }); diff --git a/service/http-config/http-base-instance.ts b/service/http-config/http-base-instance.ts index b7d47a7..4bafab3 100644 --- a/service/http-config/http-base-instance.ts +++ b/service/http-config/http-base-instance.ts @@ -6,7 +6,7 @@ const axiosBaseInstance = axios.create({ baseURL, headers: { "content-type": "application/json", - "X-Client-Key": process.env.MEDOLS_CLIENT_KEY + "X-Client-Key": process.env.NEXT_PUBLIC_MEDOLS_CLIENT_KEY }, withCredentials: true, }); diff --git a/service/http-config/http-base-services.ts b/service/http-config/http-base-services.ts index 9a87ffc..a736c57 100644 --- a/service/http-config/http-base-services.ts +++ b/service/http-config/http-base-services.ts @@ -2,7 +2,7 @@ import axiosBaseInstance from "./http-base-instance"; const defaultHeaders = { "Content-Type": "application/json", - "X-Client-Key": process.env.MEDOLS_CLIENT_KEY + "X-Client-Key": process.env.NEXT_PUBLIC_MEDOLS_CLIENT_KEY }; export async function httpGet(pathUrl: any, headers?: any) { diff --git a/service/http-config/http-interceptor-services.ts b/service/http-config/http-interceptor-services.ts index 416b738..3564888 100644 --- a/service/http-config/http-interceptor-services.ts +++ b/service/http-config/http-interceptor-services.ts @@ -5,7 +5,7 @@ import { getCsrfToken } from "../master-user"; const defaultHeaders = { "Content-Type": "application/json", - "X-Client-Key": process.env.MEDOLS_CLIENT_KEY + "X-Client-Key": process.env.NEXT_PUBLIC_MEDOLS_CLIENT_KEY }; export async function httpGetInterceptor(pathUrl: any) { From 45a9f05efc1dfd573ba1b575149f99c14948c6c6 Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Thu, 3 Jul 2025 23:23:17 +0700 Subject: [PATCH 13/16] feat: update clientKey --- .env | 2 +- service/http-config/axios-interceptor-instance.ts | 2 +- service/http-config/http-base-instance.ts | 2 +- service/http-config/http-base-services.ts | 2 +- service/http-config/http-interceptor-services.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.env b/.env index d5b009c..f104216 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -NEXT_PUBLIC_MEDOLS_CLIENT_KEY=bb65b1ad-e954-4a1a-b4d0-74df5bb0b640 \ No newline at end of file +MEDOLS_CLIENT_KEY=bb65b1ad-e954-4a1a-b4d0-74df5bb0b640 \ No newline at end of file diff --git a/service/http-config/axios-interceptor-instance.ts b/service/http-config/axios-interceptor-instance.ts index f44e5f1..079398f 100644 --- a/service/http-config/axios-interceptor-instance.ts +++ b/service/http-config/axios-interceptor-instance.ts @@ -10,7 +10,7 @@ const axiosInterceptorInstance = axios.create({ baseURL, headers: { "Content-Type": "application/json", - "X-Client-Key": process.env.NEXT_PUBLIC_MEDOLS_CLIENT_KEY + "X-Client-Key": process.env.MEDOLS_CLIENT_KEY || 'bb65b1ad-e954-4a1a-b4d0-74df5bb0b640' }, withCredentials: true, }); diff --git a/service/http-config/http-base-instance.ts b/service/http-config/http-base-instance.ts index 4bafab3..574f160 100644 --- a/service/http-config/http-base-instance.ts +++ b/service/http-config/http-base-instance.ts @@ -6,7 +6,7 @@ const axiosBaseInstance = axios.create({ baseURL, headers: { "content-type": "application/json", - "X-Client-Key": process.env.NEXT_PUBLIC_MEDOLS_CLIENT_KEY + "X-Client-Key": process.env.MEDOLS_CLIENT_KEY || 'bb65b1ad-e954-4a1a-b4d0-74df5bb0b640' }, withCredentials: true, }); diff --git a/service/http-config/http-base-services.ts b/service/http-config/http-base-services.ts index a736c57..25e52fc 100644 --- a/service/http-config/http-base-services.ts +++ b/service/http-config/http-base-services.ts @@ -2,7 +2,7 @@ import axiosBaseInstance from "./http-base-instance"; const defaultHeaders = { "Content-Type": "application/json", - "X-Client-Key": process.env.NEXT_PUBLIC_MEDOLS_CLIENT_KEY + "X-Client-Key": process.env.MEDOLS_CLIENT_KEY || 'bb65b1ad-e954-4a1a-b4d0-74df5bb0b640' }; export async function httpGet(pathUrl: any, headers?: any) { diff --git a/service/http-config/http-interceptor-services.ts b/service/http-config/http-interceptor-services.ts index 3564888..373e40b 100644 --- a/service/http-config/http-interceptor-services.ts +++ b/service/http-config/http-interceptor-services.ts @@ -5,7 +5,7 @@ import { getCsrfToken } from "../master-user"; const defaultHeaders = { "Content-Type": "application/json", - "X-Client-Key": process.env.NEXT_PUBLIC_MEDOLS_CLIENT_KEY + "X-Client-Key": process.env.MEDOLS_CLIENT_KEY || 'bb65b1ad-e954-4a1a-b4d0-74df5bb0b640' }; export async function httpGetInterceptor(pathUrl: any) { From bcbdafa8c6d4269d318b0804f7250da8dd1d3392 Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Thu, 3 Jul 2025 23:34:19 +0700 Subject: [PATCH 14/16] feat: update clientKey --- service/http-config/axios-interceptor-instance.ts | 2 +- service/http-config/http-base-instance.ts | 2 +- service/http-config/http-base-services.ts | 2 +- service/http-config/http-interceptor-services.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/service/http-config/axios-interceptor-instance.ts b/service/http-config/axios-interceptor-instance.ts index 079398f..422a192 100644 --- a/service/http-config/axios-interceptor-instance.ts +++ b/service/http-config/axios-interceptor-instance.ts @@ -10,7 +10,7 @@ const axiosInterceptorInstance = axios.create({ baseURL, headers: { "Content-Type": "application/json", - "X-Client-Key": process.env.MEDOLS_CLIENT_KEY || 'bb65b1ad-e954-4a1a-b4d0-74df5bb0b640' + "X-Client-Key": "bb65b1ad-e954-4a1a-b4d0-74df5bb0b640" }, withCredentials: true, }); diff --git a/service/http-config/http-base-instance.ts b/service/http-config/http-base-instance.ts index 574f160..19fff91 100644 --- a/service/http-config/http-base-instance.ts +++ b/service/http-config/http-base-instance.ts @@ -6,7 +6,7 @@ const axiosBaseInstance = axios.create({ baseURL, headers: { "content-type": "application/json", - "X-Client-Key": process.env.MEDOLS_CLIENT_KEY || 'bb65b1ad-e954-4a1a-b4d0-74df5bb0b640' + "X-Client-Key": "bb65b1ad-e954-4a1a-b4d0-74df5bb0b640" }, withCredentials: true, }); diff --git a/service/http-config/http-base-services.ts b/service/http-config/http-base-services.ts index 25e52fc..7df973c 100644 --- a/service/http-config/http-base-services.ts +++ b/service/http-config/http-base-services.ts @@ -2,7 +2,7 @@ import axiosBaseInstance from "./http-base-instance"; const defaultHeaders = { "Content-Type": "application/json", - "X-Client-Key": process.env.MEDOLS_CLIENT_KEY || 'bb65b1ad-e954-4a1a-b4d0-74df5bb0b640' + "X-Client-Key": "bb65b1ad-e954-4a1a-b4d0-74df5bb0b640" }; export async function httpGet(pathUrl: any, headers?: any) { diff --git a/service/http-config/http-interceptor-services.ts b/service/http-config/http-interceptor-services.ts index 373e40b..4d33d96 100644 --- a/service/http-config/http-interceptor-services.ts +++ b/service/http-config/http-interceptor-services.ts @@ -5,7 +5,7 @@ import { getCsrfToken } from "../master-user"; const defaultHeaders = { "Content-Type": "application/json", - "X-Client-Key": process.env.MEDOLS_CLIENT_KEY || 'bb65b1ad-e954-4a1a-b4d0-74df5bb0b640' + "X-Client-Key": "bb65b1ad-e954-4a1a-b4d0-74df5bb0b640" }; export async function httpGetInterceptor(pathUrl: any) { From f716962438f093bc3d1e8e33c3a86b90822d9009 Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Thu, 3 Jul 2025 23:50:28 +0700 Subject: [PATCH 15/16] feat: fixing login --- service/master-user.ts | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/service/master-user.ts b/service/master-user.ts index f92f9db..931d468 100644 --- a/service/master-user.ts +++ b/service/master-user.ts @@ -42,20 +42,13 @@ export async function deleteMasterUser(id: string) { } export async function postSignIn(data: any) { - const headers = { - accept: "application/json", - "content-type": "application/json", - }; const pathUrl = `/users/login`; - return await httpPost(pathUrl, headers, data); + return await httpPost(pathUrl, data); } export async function getProfile(code?: string) { - const headers = { - "content-type": "application/json", - Authorization: `Bearer ${code || token}`, - }; - return await httpGet(`/users/info`, headers); + const pathUrl = `/users/info`; + return await httpGet(pathUrl); } export async function updateProfile(data: any) { From cd1bf0877179c8ec9d65c18d3cab37b182acf648 Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Fri, 4 Jul 2025 00:18:54 +0700 Subject: [PATCH 16/16] feat: fixing all services --- .../form/article/create-article-form.tsx | 2 +- components/form/form-master-user-edit.tsx | 2 +- components/form/form-master-user.tsx | 2 +- components/form/login.tsx | 4 +- .../table/advertise/advertise-table.tsx | 4 +- service/activity-log.ts | 8 +- service/advertisement.ts | 48 ++---- service/article.ts | 1 - service/http-config/axios-base-instance.ts | 3 +- service/http-config/axios-base-service.ts | 154 ------------------ service/http-config/http-base-instance.ts | 14 -- service/http-config/http-base-services.ts | 2 +- service/http-config/mediahub-base-service.ts | 12 -- service/magazine.tsx | 45 ++--- service/master-categories.tsx | 30 +--- service/master-user-role.ts | 25 +-- service/master-user.ts | 8 +- service/static-page-service.ts | 39 ++--- service/user-levels-service.ts | 30 ++++ service/user-levels/user-levels-service.ts | 52 ------ 20 files changed, 92 insertions(+), 393 deletions(-) delete mode 100644 service/http-config/axios-base-service.ts delete mode 100644 service/http-config/http-base-instance.ts delete mode 100644 service/http-config/mediahub-base-service.ts create mode 100644 service/user-levels-service.ts delete mode 100644 service/user-levels/user-levels-service.ts diff --git a/components/form/article/create-article-form.tsx b/components/form/article/create-article-form.tsx index cfe4e28..1f6b448 100644 --- a/components/form/article/create-article-form.tsx +++ b/components/form/article/create-article-form.tsx @@ -27,7 +27,7 @@ import { saveManualContext, updateManualArticle, } from "@/service/generate-article"; -import { getUserLevels } from "@/service/user-levels/user-levels-service"; +import { getUserLevels } from "@/service/user-levels-service"; import { Checkbox } from "@/components/ui/checkbox"; import { Button } from "@/components/ui/button"; import { getCategoryById } from "@/service/master-categories"; diff --git a/components/form/form-master-user-edit.tsx b/components/form/form-master-user-edit.tsx index 75c3b5f..75b2654 100644 --- a/components/form/form-master-user-edit.tsx +++ b/components/form/form-master-user-edit.tsx @@ -12,7 +12,7 @@ import { z } from "zod"; import ReactSelect from "react-select"; import makeAnimated from "react-select/animated"; import { editMasterUsers, getDetailMasterUsers } from "@/service/master-user"; -import { getAllUserLevels } from "@/service/user-levels/user-levels-service"; +import { getAllUserLevels } from "@/service/user-levels-service"; import { listUserRole } from "@/service/master-user-role"; import { Card } from "../ui/card"; import { Label } from "../ui/label"; diff --git a/components/form/form-master-user.tsx b/components/form/form-master-user.tsx index de4a3d9..3484c09 100644 --- a/components/form/form-master-user.tsx +++ b/components/form/form-master-user.tsx @@ -13,7 +13,7 @@ import ReactSelect from "react-select"; import makeAnimated from "react-select/animated"; import { zodResolver } from "@hookform/resolvers/zod"; import { createMasterUser } from "@/service/master-user"; -import { getAllUserLevels } from "@/service/user-levels/user-levels-service"; +import { getAllUserLevels } from "@/service/user-levels-service"; import { listUserRole } from "@/service/master-user-role"; import { Card } from "../ui/card"; import { Input } from "../ui/input"; diff --git a/components/form/login.tsx b/components/form/login.tsx index 7eeee15..42e3829 100644 --- a/components/form/login.tsx +++ b/components/form/login.tsx @@ -78,13 +78,13 @@ export default function Login() { secure: true, sameSite: "strict", }); - const resActivity = await saveActivity( + await saveActivity( { activityTypeId: 1, url: "https://dev.mikulnews.com/auth", userId: profile?.data?.data?.id, }, - accessData?.id_token + response?.data?.data?.access_token ); Cookies.set("profile_picture", profile?.data?.data?.profilePictureUrl, { expires: 1, diff --git a/components/table/advertise/advertise-table.tsx b/components/table/advertise/advertise-table.tsx index 7213f83..ee07aa4 100644 --- a/components/table/advertise/advertise-table.tsx +++ b/components/table/advertise/advertise-table.tsx @@ -27,12 +27,12 @@ import Image from "next/image"; import { Switch } from "@/components/ui/switch"; import useDisclosure from "@/components/useDisclosure"; import { - createAdvertiseById, createMediaFileAdvertise, deleteAdvertise, editAdvertise, editAdvertiseIsActive, getAdvertise, + getAdvertiseById, } from "@/service/advertisement"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; @@ -226,7 +226,7 @@ export default function AdvertiseTable(props: { triggerRefresh: boolean }) { }; const openModal = async (id: number) => { - const res = await createAdvertiseById(Number(id)); + const res = await getAdvertiseById(Number(id)); const data = res?.data?.data; setValue("id", String(data?.id)); setValue("title", data?.title); diff --git a/service/activity-log.ts b/service/activity-log.ts index 2056d6f..98634ee 100644 --- a/service/activity-log.ts +++ b/service/activity-log.ts @@ -1,5 +1,4 @@ -import { PaginationRequest } from "@/types/globals"; -import { httpGet, httpPost, httpPut } from "./http-config/axios-base-service"; +import { httpGet, httpPost } from "./http-config/http-base-services"; export async function saveActivity(data: any, token?: string) { const headers = token @@ -11,11 +10,10 @@ export async function saveActivity(data: any, token?: string) { "content-type": "application/json", }; const pathUrl = `/activity-logs`; - return await httpPost(pathUrl, headers, data); + return await httpPost(pathUrl, data, headers); } export async function getActivity() { - const headers = { "content-type": "application/json" }; const pathUrl = `/activity-logs/statistics`; - return await httpGet(pathUrl, headers); + return await httpGet(pathUrl); } diff --git a/service/advertisement.ts b/service/advertisement.ts index fe5fa69..90b5123 100644 --- a/service/advertisement.ts +++ b/service/advertisement.ts @@ -1,68 +1,42 @@ -import { - httpDeleteInterceptor, - httpGet, - httpPost, - httpPut, -} from "./http-config/axios-base-service"; import Cookies from "js-cookie"; +import { httpDeleteInterceptor, httpPostInterceptor, httpPutInterceptor } from "./http-config/http-interceptor-services"; +import { httpGet } from "./http-config/http-base-services"; const token = Cookies.get("access_token"); export async function createAdvertise(data: any) { - const headers = { - "content-type": "application/json", - Authorization: `Bearer ${token}`, - }; const pathUrl = `/advertisement`; - return await httpPost(pathUrl, headers, data); + return await httpPostInterceptor(pathUrl, data); } export async function createMediaFileAdvertise(id: string | number, data: any) { const headers = { "content-type": "multipart/form-data", }; const pathUrl = `/advertisement/upload/${id}`; - return await httpPost(pathUrl, headers, data); + return await httpPostInterceptor(pathUrl, data, headers); } export async function getAdvertise(data: any) { - const headers = { - "content-type": "application/json", - }; - const pathUrl = `/advertisement?page=${data?.page || 1}&limit=${ - data?.limit || "" - }&placement=${data?.placement || ""}&isPublish=${data.isPublish || ""}`; - return await httpGet(pathUrl, headers); + const pathUrl = `/advertisement?page=${data?.page || 1}&limit=${data?.limit || ""}&placement=${data?.placement || ""}&isPublish=${data.isPublish || ""}`; + return await httpGet(pathUrl); } -export async function createAdvertiseById(id: number) { - const headers = { - "content-type": "application/json", - }; +export async function getAdvertiseById(id: number) { const pathUrl = `/advertisement/${id}`; - return await httpGet(pathUrl, headers); + return await httpGet(pathUrl); } export async function editAdvertise(data: any) { - const headers = { - "content-type": "application/json", - }; const pathUrl = `/advertisement/${data?.id}`; - return await httpPut(pathUrl, headers, data); + return await httpPutInterceptor(pathUrl, data); } export async function editAdvertiseIsActive(data: any) { - const headers = { - "content-type": "application/json", - Authorization: `Bearer ${token}`, - }; const pathUrl = `/advertisement/publish/${data?.id}?isPublish=${data?.isActive}`; - return await httpPut(pathUrl, headers); + return await httpPutInterceptor(pathUrl, data); } export async function deleteAdvertise(id: number) { - const headers = { - "content-type": "application/json", - }; const pathUrl = `/advertisement/${id}`; - return await httpDeleteInterceptor(pathUrl, headers); + return await httpDeleteInterceptor(pathUrl); } diff --git a/service/article.ts b/service/article.ts index 5d570aa..8faddf4 100644 --- a/service/article.ts +++ b/service/article.ts @@ -1,5 +1,4 @@ import { PaginationRequest } from "@/types/globals"; -import Cookies from "js-cookie"; import { httpGet } from "./http-config/http-base-services"; import { httpDeleteInterceptor, httpGetInterceptor, httpPostInterceptor, httpPutInterceptor } from "./http-config/http-interceptor-services"; diff --git a/service/http-config/axios-base-instance.ts b/service/http-config/axios-base-instance.ts index 03f30f6..de3efbe 100644 --- a/service/http-config/axios-base-instance.ts +++ b/service/http-config/axios-base-instance.ts @@ -5,7 +5,8 @@ const baseURL = "https://dev.mikulnews.com/api"; const axiosBaseInstance = axios.create({ baseURL, headers: { - "content-type": "application/json", + "Content-Type": "application/json", + "X-Client-Key": "bb65b1ad-e954-4a1a-b4d0-74df5bb0b640" }, }); diff --git a/service/http-config/axios-base-service.ts b/service/http-config/axios-base-service.ts deleted file mode 100644 index c33fd16..0000000 --- a/service/http-config/axios-base-service.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { getCsrfToken } from "../master-user"; -import axiosInterceptorInstance from "./axios-interceptor-instance"; -import axiosBaseInstance from "./http-base-instance"; -import mediahubBaseInstance from "./mediahub-base-service"; -import Cookies from "js-cookie"; - -const defaultHeaders = { - "Content-Type": "application/json", -}; - -export async function httpPost(pathUrl: any, headers: any, data?: any) { - const resCsrf = await getCsrfToken(); - const csrfToken = resCsrf?.data?.csrf_token; - - const mergedHeaders = { - ...defaultHeaders, - ...headers, - ...(csrfToken ? { "X-CSRF-TOKEN": csrfToken } : {}), - }; - - const response = await axiosBaseInstance - .post(pathUrl, data, { headers: mergedHeaders }) - .catch(function (error: any) { - console.log(error); - return error.response; - }); - console.log("Response base svc : ", response); - if (response?.status == 200 || response?.status == 201) { - return { - error: false, - message: "success", - data: response?.data, - }; - } else { - return { - error: true, - message: response?.data?.message || response?.data || null, - data: null, - }; - } -} - -export async function httpGet(pathUrl: any, headers: any) { - const response = await axiosInterceptorInstance - .get(pathUrl, { headers }) - .catch(function (error: any) { - console.log(error); - return error.response; - }); - console.log("Response base svc : ", response); - if (response?.status == 200 || response?.status == 201) { - return { - error: false, - message: "success", - data: response?.data, - }; - } else { - return { - error: true, - message: response?.data?.message || response?.data || null, - data: null, - }; - } -} - -export async function httpPut(pathUrl: any, headers: any, data?: any) { - const resCsrf = await getCsrfToken(); - const csrfToken = resCsrf?.data?.csrf_token; - - const defaultHeaders = { - "Content-Type": "application/json", - }; - const mergedHeaders = { - ...defaultHeaders, - ...headers, - ...(csrfToken ? { "X-CSRF-TOKEN": csrfToken } : {}), - }; - - const response = await axiosBaseInstance - .put(pathUrl, data, { headers: mergedHeaders }) - .catch(function (error: any) { - console.log(error); - return error.response; - }); - console.log("Response base svc : ", response); - if (response?.status == 200 || response?.status == 201) { - return { - error: false, - message: "success", - data: response?.data, - }; - } else { - return { - error: true, - message: response?.data?.message || response?.data || null, - data: null, - }; - } -} - -export async function httpDeleteInterceptor(pathUrl: any, headers: any) { - const resCsrf = await getCsrfToken(); - const csrfToken = resCsrf?.data?.csrf_token; - - const defaultHeaders = { - "Content-Type": "application/json", - }; - const mergedHeaders = { - ...defaultHeaders, - ...headers, - ...(csrfToken ? { "X-CSRF-TOKEN": csrfToken } : {}), - }; - - const response = await axiosBaseInstance - .delete(pathUrl, { headers: mergedHeaders }) - .catch((error) => error.response); - console.log("Response interceptor : ", response); - if (response?.status == 200 || response?.status == 201) { - return { - error: false, - message: "success", - data: response?.data, - }; - } else { - return { - error: true, - message: response?.data?.message || response?.data || null, - data: null, - }; - } -} - -export async function mediahubGet(pathUrl: any, headers: any) { - const response = await mediahubBaseInstance - .get(pathUrl, { headers }) - .catch(function (error: any) { - console.log(error); - return error.response; - }); - console.log("Response base svc : ", response); - if (response?.status == 200 || response?.status == 201) { - return { - error: false, - message: "success", - data: response?.data, - }; - } else { - return { - error: true, - message: response?.data?.message || response?.data || null, - data: null, - }; - } -} diff --git a/service/http-config/http-base-instance.ts b/service/http-config/http-base-instance.ts deleted file mode 100644 index 19fff91..0000000 --- a/service/http-config/http-base-instance.ts +++ /dev/null @@ -1,14 +0,0 @@ -import axios from "axios"; - -const baseURL = "https://dev.mikulnews.com/api"; - -const axiosBaseInstance = axios.create({ - baseURL, - headers: { - "content-type": "application/json", - "X-Client-Key": "bb65b1ad-e954-4a1a-b4d0-74df5bb0b640" - }, - withCredentials: true, -}); - -export default axiosBaseInstance; diff --git a/service/http-config/http-base-services.ts b/service/http-config/http-base-services.ts index 7df973c..755d158 100644 --- a/service/http-config/http-base-services.ts +++ b/service/http-config/http-base-services.ts @@ -1,4 +1,4 @@ -import axiosBaseInstance from "./http-base-instance"; +import axiosBaseInstance from "./axios-base-instance"; const defaultHeaders = { "Content-Type": "application/json", diff --git a/service/http-config/mediahub-base-service.ts b/service/http-config/mediahub-base-service.ts deleted file mode 100644 index 499c570..0000000 --- a/service/http-config/mediahub-base-service.ts +++ /dev/null @@ -1,12 +0,0 @@ -import axios from "axios"; - -const baseURL = "https://mediahub.polri.go.id/api"; - -const mediahubBaseInstance = axios.create({ - baseURL, - headers: { - "content-type": "application/json", - }, -}); - -export default mediahubBaseInstance; diff --git a/service/magazine.tsx b/service/magazine.tsx index 9c76d36..d711c8d 100644 --- a/service/magazine.tsx +++ b/service/magazine.tsx @@ -1,75 +1,50 @@ import { PaginationRequest } from "@/types/globals"; -import { - httpDeleteInterceptor, - httpGet, - httpPost, - httpPut, -} from "./http-config/axios-base-service"; import Cookies from "js-cookie"; +import { httpDeleteInterceptor, httpGetInterceptor, httpPostInterceptor, httpPutInterceptor } from "./http-config/http-interceptor-services"; const token = Cookies.get("access_token"); export async function createMagazine(data: any) { - const headers = { - "content-type": "application/json", - Authorization: `Bearer ${token}`, - }; const pathUrl = `/magazines`; - return await httpPost(pathUrl, headers, data); + return await httpPostInterceptor(pathUrl, data); } export async function getListMagazine(props: PaginationRequest) { const { page, limit, search, startDate, endDate } = props; - const headers = { - "content-type": "application/json", - }; - return await httpGet( + return await httpGetInterceptor( `/magazines?limit=${limit}&page=${page}&title=${search}&startDate=${ startDate || "" - }&endDate=${endDate || ""}`, - headers + }&endDate=${endDate || ""}` ); } export async function updateMagazine(id: string, data: any) { - const headers = { - "content-type": "application/json", - }; const pathUrl = `/magazines/${id}`; - return await httpPut(pathUrl, headers, data); + return await httpPutInterceptor(pathUrl, data); } export async function getMagazineById(id: string) { - const headers = { - "content-type": "application/json", - }; - return await httpGet(`/magazines/${id}`, headers); + return await httpGetInterceptor(`/magazines/${id}`); } export async function deleteMagazine(id: string) { - const headers = { - "content-type": "application/json", - }; - return await httpDeleteInterceptor(`magazines/${id}`, headers); + return await httpDeleteInterceptor(`magazines/${id}`); } export async function uploadMagazineFile(id: string, data: any) { const headers = { "content-type": "multipart/form-data", }; - return await httpPost(`/magazine-files/${id}`, headers, data); + return await httpPostInterceptor(`/magazine-files/${id}`, data, headers); } export async function uploadMagazineThumbnail(id: string, data: any) { const headers = { "content-type": "multipart/form-data", }; - return await httpPost(`/magazines/thumbnail/${id}`, headers, data); + return await httpPostInterceptor(`/magazines/thumbnail/${id}`, data, headers); } export async function deleteMagazineFiles(id: number) { - const headers = { - "content-type": "multipart/form-data", - }; - return await httpDeleteInterceptor(`magazine-files/${id}`, headers); + return await httpDeleteInterceptor(`magazine-files/${id}`); } diff --git a/service/master-categories.tsx b/service/master-categories.tsx index 9efd3ec..44276b5 100644 --- a/service/master-categories.tsx +++ b/service/master-categories.tsx @@ -1,47 +1,29 @@ -import { - httpDeleteInterceptor, - httpGet, - httpPost, - httpPut, -} from "./http-config/axios-base-service"; import Cookies from "js-cookie"; +import { httpDeleteInterceptor, httpGetInterceptor, httpPostInterceptor, httpPutInterceptor } from "./http-config/http-interceptor-services"; const token = Cookies.get("access_token"); export async function createCategory(data: any) { - const headers = { - "content-type": "application/json", - Authorization: `Bearer ${token}`, - }; const pathUrl = `/article-categories`; - return await httpPost(pathUrl, headers, data); + return await httpPostInterceptor(pathUrl, data); } export async function updateCategory(id: string, data: any) { - const headers = { - "content-type": "application/json", - }; const pathUrl = `/article-categories/${id}`; - return await httpPut(pathUrl, headers, data); + return await httpPutInterceptor(pathUrl, data); } export async function getCategoryById(id: number) { - const headers = { - "content-type": "application/json", - }; - return await httpGet(`/article-categories/${id}`, headers); + return await httpGetInterceptor(`/article-categories/${id}`); } export async function deleteCategory(id: number) { - const headers = { - "content-type": "application/json", - }; - return await httpDeleteInterceptor(`article-categories/${id}`, headers); + return await httpDeleteInterceptor(`article-categories/${id}`); } export async function uploadCategoryThumbnail(id: string, data: any) { const headers = { "content-type": "multipart/form-data", }; - return await httpPost(`/article-categories/thumbnail/${id}`, headers, data); + return await httpPostInterceptor(`/article-categories/thumbnail/${id}`, data, headers); } diff --git a/service/master-user-role.ts b/service/master-user-role.ts index eda2837..567045b 100644 --- a/service/master-user-role.ts +++ b/service/master-user-role.ts @@ -1,42 +1,29 @@ -import { - httpDeleteInterceptor, - httpGet, - httpPost, -} from "./http-config/axios-base-service"; - import Cookies from "js-cookie"; +import { httpDeleteInterceptor, httpGetInterceptor, httpPostInterceptor } from "./http-config/http-interceptor-services"; const token = Cookies.get("access_token"); export async function listUserRole(data: any) { - const headers = { - "content-type": "application/json", - }; - return await httpGet( - `/user-roles?limit=${data.limit}&page=${data.page}`, - headers + return await httpGetInterceptor( + `/user-roles?limit=${data.limit}&page=${data.page}` ); } export async function createMasterUserRole(data: any) { - const headers = { - "content-type": "application/json", - Authorization: `Bearer ${token}`, - }; const pathUrl = `/user-roles`; - return await httpPost(pathUrl, headers, data); + return await httpPostInterceptor(pathUrl, data); } export async function getMasterUserRoleById(id: any) { const headers = { "content-type": "application/json", }; - return await httpGet(`/user-roles/${id}`, headers); + return await httpGetInterceptor(`/user-roles/${id}`); } export async function deleteMasterUserRole(id: string) { const headers = { "content-type": "application/json", }; - return await httpDeleteInterceptor(`/user-roles/${id}`, headers); + return await httpDeleteInterceptor(`/user-roles/${id}`); } diff --git a/service/master-user.ts b/service/master-user.ts index 931d468..ede054b 100644 --- a/service/master-user.ts +++ b/service/master-user.ts @@ -46,9 +46,13 @@ export async function postSignIn(data: any) { return await httpPost(pathUrl, data); } -export async function getProfile(code?: string) { +export async function getProfile(token?: string) { + const headers = { + "content-type": "application/json", + "Authorization": `Bearer ${token}`, + }; const pathUrl = `/users/info`; - return await httpGet(pathUrl); + return await httpGet(pathUrl, headers); } export async function updateProfile(data: any) { diff --git a/service/static-page-service.ts b/service/static-page-service.ts index ad2a87d..41631ec 100644 --- a/service/static-page-service.ts +++ b/service/static-page-service.ts @@ -1,47 +1,28 @@ import { PaginationRequest } from "@/types/globals"; -import { - httpDeleteInterceptor, - httpGet, - httpPost, - httpPut, -} from "./http-config/axios-base-service"; +import { httpGetInterceptor, httpPostInterceptor, httpPutInterceptor } from "./http-config/http-interceptor-services"; +import { httpGet } from "./http-config/http-base-services"; export async function createCustomStaticPage(data: any) { - const headers = { - "content-type": "application/json", - }; const pathUrl = `/custom-static-pages`; - return await httpPost(pathUrl, headers, data); + return await httpPostInterceptor(pathUrl, data); } export async function editCustomStaticPage(data: any) { - const headers = { - "content-type": "application/json", - }; const pathUrl = `/custom-static-pages/${data.id}`; - return await httpPut(pathUrl, headers, data); + return await httpPutInterceptor(pathUrl, data); } export async function getCustomStaticPage(props: PaginationRequest) { const { page, limit, search } = props; - const headers = { - "content-type": "application/json", - }; - return await httpGet( - `/custom-static-pages?limit=${limit}&page=${page}&title=${search}`, - headers - ); + const pathUrl = `/custom-static-pages?limit=${limit}&page=${page}&title=${search}`; + return await httpGetInterceptor(pathUrl); } + export async function getCustomStaticDetail(id: string) { - const headers = { - "content-type": "application/json", - }; - return await httpGet(`/custom-static-pages/${id}`, headers); + return await httpGetInterceptor(`/custom-static-pages/${id}`); } export async function getCustomStaticDetailBySlug(slug: string) { - const headers = { - "content-type": "application/json", - }; - return await httpGet(`/custom-static-pages/slug/${slug}`, headers); + const pathUrl = `/custom-static-pages/slug/${slug}`; + return await httpGet(pathUrl); } diff --git a/service/user-levels-service.ts b/service/user-levels-service.ts new file mode 100644 index 0000000..3a2ca3e --- /dev/null +++ b/service/user-levels-service.ts @@ -0,0 +1,30 @@ + +import Cookies from "js-cookie"; +import { httpGetInterceptor, httpPostInterceptor, httpPutInterceptor } from "./http-config/http-interceptor-services"; +const token = Cookies.get("access_token"); + +export async function getAllUserLevels(data?: any) { + const pathUrl = `user-levels?limit=${data?.limit || ""}&levelNumber=${ + data?.levelNumber || "" + }&name=${data?.search || ""}&page=${data?.page || "1"}` + return await httpGetInterceptor(pathUrl); +} +export async function getUserLevels(id: string) { + return await httpGetInterceptor(`user-levels/${id}`); +} + +export async function getAccountById(id: string) { + return await httpGetInterceptor(`user-account/findById/${id}`); +} + +export async function createUserLevels(data: any) { + return await httpPostInterceptor(`user-levels`, data); +} + +export async function editUserLevels(id: string, data: any) { + return await httpPutInterceptor(`user-levels/${id}`, data); +} + +export async function changeIsApproval(data: any) { + return await httpPutInterceptor(`user-levels/enable-approval`, data); +} diff --git a/service/user-levels/user-levels-service.ts b/service/user-levels/user-levels-service.ts deleted file mode 100644 index 5f1f457..0000000 --- a/service/user-levels/user-levels-service.ts +++ /dev/null @@ -1,52 +0,0 @@ - -import Cookies from "js-cookie"; -import { httpGet, httpPost, httpPut } from "../http-config/axios-base-service"; - -const token = Cookies.get("access_token"); - -export async function getAllUserLevels(data?: any) { - const headers = { - "content-type": "application/json", - }; - return await httpGet( - `user-levels?limit=${data?.limit || ""}&levelNumber=${ - data?.levelNumber || "" - }&name=${data?.search || ""}&page=${data?.page || "1"}`, - headers - ); -} -export async function getUserLevels(id: string) { - const headers = { - "content-type": "application/json", - }; - return await httpGet(`user-levels/${id}`, headers); -} - -export async function getAccountById(id: string) { - const headers = { - "content-type": "application/json", - }; - return await httpGet(`user-account/findById/${id}`, headers); -} - -export async function createUserLevels(request: any) { - const headers = { - "content-type": "application/json", - }; - return await httpPost(`user-levels`, headers, request); -} - -export async function editUserLevels(id: string, request: any) { - const headers = { - "content-type": "application/json", - }; - return await httpPut(`user-levels/${id}`, headers, request); -} - -export async function changeIsApproval(request: any) { - const headers = { - "content-type": "application/json", - Authorization: `Bearer ${token}`, - }; - return await httpPut(`user-levels/enable-approval`, headers, request); -}