From c44e63bd2b5b36c8cf8cb0d93e4e2fd6819a137d Mon Sep 17 00:00:00 2001 From: randy-ar Date: Sat, 27 Dec 2025 16:36:07 +0700 Subject: [PATCH] fix(FE): fix page responsive in project flock dan marketing modules --- src/components/Drawer.tsx | 4 +- src/components/helper/RequireAuth.tsx | 239 +++++------------- .../pages/marketing/MarketingTable.tsx | 1 + .../pages/marketing/form/MarketingForm.tsx | 4 +- .../delivery-order/DeliverOrderProduct.tsx | 2 +- .../sales-order/SalesOrderProductForm.tsx | 2 +- .../form/ProductionStandardForm.tsx | 1 - .../project-flock/ProjectFlockTable.tsx | 2 +- src/components/table/TableRowSizeSelector.tsx | 5 +- .../production-standard.dummy.json | 2 +- 10 files changed, 79 insertions(+), 183 deletions(-) diff --git a/src/components/Drawer.tsx b/src/components/Drawer.tsx index 17b8a56f..4cb59cdb 100644 --- a/src/components/Drawer.tsx +++ b/src/components/Drawer.tsx @@ -64,7 +64,7 @@ const Drawer = ({ ), drawerSidebarContent: cn( baseClassNames.drawerSidebarContent, - 'w-full min-w-120 sm:w-fit' + 'w-full sm:min-w-120 sm:w-fit' ), }; } else if (variant === 'left') { @@ -76,7 +76,7 @@ const Drawer = ({ ), drawerSidebarContent: cn( baseClassNames.drawerSidebarContent, - 'w-full min-w-120 sm:w-fit' + 'w-full sm:min-w-120 sm:w-fit' ), }; } diff --git a/src/components/helper/RequireAuth.tsx b/src/components/helper/RequireAuth.tsx index dbd4b6bc..9dbd2557 100644 --- a/src/components/helper/RequireAuth.tsx +++ b/src/components/helper/RequireAuth.tsx @@ -1,197 +1,90 @@ 'use client'; import { ReactNode, useEffect } from 'react'; -import { useRouter } from 'next/navigation'; -import useSWRImmutable from 'swr/immutable'; +import useSWR from 'swr'; import { useAuth } from '@/services/hooks/useAuth'; import { httpClientFetcher, SWRHttpKey } from '@/services/http/client'; -import { isResponseSuccess } from '@/lib/api-helper'; -import { GetMeResponse } from '@/types/api/api-general'; - -// TODO: delete this later, DONT HARDCODE USER DATA -const DUMMY_USER = { - id: 1, - email: 'admin@mbugroup.id', - npk: '0001', - name: 'Super Admin', - image: null, - created_at: '2025-09-30T03:24:20.899229Z', - updated_at: '2025-09-30T03:24:20.899229Z', - roles: [ - { - id: 1, - key: 'mbu.super_admin', - name: 'MBU Administrator', - client: { - id: 1, - name: 'PT Mitra Berlian Unggas', - alias: 'MBU', - }, - permissions: [ - { - id: 1, - name: 'mbu:purchase:read', - action: 'read', - client: { - id: 1, - name: 'PT Mitra Berlian Unggas', - alias: 'MBU', - }, - }, - { - id: 2, - name: 'mbu:purchase:create', - action: 'create', - client: { - id: 1, - name: 'PT Mitra Berlian Unggas', - alias: 'MBU', - }, - }, - { - id: 3, - name: 'mbu:purchase:approve', - action: 'approve', - client: { - id: 1, - name: 'PT Mitra Berlian Unggas', - alias: 'MBU', - }, - }, - ], - }, - { - id: 2, - key: 'lti.super_admin', - name: 'LTI Administrator', - client: { - id: 2, - name: 'PT Lumbung Telur Indonesia', - alias: 'LTI', - }, - permissions: [ - { - id: 4, - name: 'lti:purchase:read', - action: 'read', - client: { - id: 2, - name: 'PT Lumbung Telur Indonesia', - alias: 'LTI', - }, - }, - { - id: 5, - name: 'lti:purchase:create', - action: 'create', - client: { - id: 2, - name: 'PT Lumbung Telur Indonesia', - alias: 'LTI', - }, - }, - { - id: 6, - name: 'lti:purchase:approve', - action: 'approve', - client: { - id: 2, - name: 'PT Lumbung Telur Indonesia', - alias: 'LTI', - }, - }, - ], - }, - { - id: 3, - key: 'manbu.super_admin', - name: 'MANBU Administrator', - client: { - id: 3, - name: 'PT Mandiri Berlian Unggas', - alias: 'MANBU', - }, - permissions: [ - { - id: 7, - name: 'manbu:purchase:read', - action: 'read', - client: { - id: 3, - name: 'PT Mandiri Berlian Unggas', - alias: 'MANBU', - }, - }, - { - id: 8, - name: 'manbu:purchase:create', - action: 'create', - client: { - id: 3, - name: 'PT Mandiri Berlian Unggas', - alias: 'MANBU', - }, - }, - { - id: 9, - name: 'manbu:purchase:approve', - action: 'approve', - client: { - id: 3, - name: 'PT Mandiri Berlian Unggas', - alias: 'MANBU', - }, - }, - ], - }, - ], -}; +import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; +import { BaseApiResponse, GetMeResponse } from '@/types/api/api-general'; +import { AxiosError } from 'axios'; +import { redirectToSSO } from '@/lib/auth-helper'; interface RequireAuthProps { children?: ReactNode; } const RequireAuth = ({ children }: RequireAuthProps) => { - const router = useRouter(); - const { setUser, setIsLoadingUser } = useAuth(); + const { user, setUser, setIsLoadingUser } = useAuth(); - const { data: userResponse, isLoading: isLoadingUserResponse } = - useSWRImmutable( - '/auth/sso/userinfo', - httpClientFetcher, - { - shouldRetryOnError: false, - revalidateOnFocus: false, - revalidateOnReconnect: false, - refreshInterval: 0, - } - ); + const { + data: userResponse, + isLoading: isLoadingUserResponse, + error: userErrorResponse, + } = useSWR< + GetMeResponse & { ok?: boolean }, + AxiosError, + SWRHttpKey + >('/sso/userinfo', httpClientFetcher, { + shouldRetryOnError: false, - useEffect(() => { - setIsLoadingUser(isLoadingUserResponse); - }, [isLoadingUserResponse, setIsLoadingUser]); + // refresh every 13 minutes + refreshInterval: 13 * 60 * 1000, + }); useEffect(() => { if (isResponseSuccess(userResponse)) { setUser(userResponse.data); - } else { - // router.replace(process.env.NEXT_PUBLIC_SSO_LOGIN_URL as string); - // TODO: remove this later, DONT HARDCODE USER DATA - setUser(DUMMY_USER); } - }, [userResponse, setIsLoadingUser, setUser]); + }, [userResponse, setUser]); - // TODO: uncomment this later - // if (isLoadingUserResponse && !userResponse) { - // return ( - //
- // - //
- // ); - // } + // Explicitly handle 401 redirect from the component level + useEffect(() => { + if ( + isResponseError(userResponse) && + userErrorResponse?.response?.status === 401 + ) { + // Clear cache to prevent stale data from rendering children + // mutate('/sso/userinfo', undefined, { revalidate: false }); // Optional: if using global mutate + setUser(undefined); + redirectToSSO(); + } + }, [userErrorResponse, setUser, userResponse]); - return <>{children}; + useEffect(() => { + setIsLoadingUser(isLoadingUserResponse); + }, [isLoadingUserResponse]); + + if ( + (isLoadingUserResponse && !userResponse && !userErrorResponse) || + (!userResponse && !userErrorResponse) + ) { + return ( +
+ +
+ ); + } + + if (userErrorResponse) { + return ( +
+

Authentication Failed

+

+ Please try refreshing the page or contact support if the problem + persists. +

+ +
+ ); + } + + return <>{isResponseSuccess(userResponse) && user && children}; }; export default RequireAuth; diff --git a/src/components/pages/marketing/MarketingTable.tsx b/src/components/pages/marketing/MarketingTable.tsx index d1d5940a..49927e05 100644 --- a/src/components/pages/marketing/MarketingTable.tsx +++ b/src/components/pages/marketing/MarketingTable.tsx @@ -305,6 +305,7 @@ const MarketingTable = () => { value={pageSize} onChange={pageSizeChangeHandler} options={ROWS_OPTIONS} + className='flex sm:flex-row flex-col gap-3 items-end justify-end' > {/* select multiple product */} -
+
+
)} -
+
{JSON.stringify(formik.errors)} */} -
+
void }) => { return ( <> -
+
diff --git a/src/components/table/TableRowSizeSelector.tsx b/src/components/table/TableRowSizeSelector.tsx index 645151b6..9df9b426 100644 --- a/src/components/table/TableRowSizeSelector.tsx +++ b/src/components/table/TableRowSizeSelector.tsx @@ -1,4 +1,5 @@ import SelectInput from '@/components/input/SelectInput'; +import { cn } from '@/lib/helper'; export interface OptionType { label: string; @@ -10,6 +11,7 @@ interface TableRowSizeSelectorProps { onChange: (val: OptionType | OptionType[] | null) => void; options: OptionType[]; children?: React.ReactNode; + className?: string; } export const TableRowSizeSelector = ({ @@ -17,9 +19,10 @@ export const TableRowSizeSelector = ({ onChange, options, children, + className, }: TableRowSizeSelectorProps) => { return ( -
+
{children}