feat(FE-279): Add functionality closing project flock

This commit is contained in:
randy-ar
2025-12-05 22:55:11 +07:00
parent c69d9dd605
commit 885e4250fd
16 changed files with 1464 additions and 225 deletions
+166 -33
View File
@@ -6,9 +6,147 @@ import useSWRImmutable from 'swr/immutable';
import { useAuth } from '@/services/hooks/useAuth';
import { httpClientFetcher, SWRHttpKey } from '@/services/http/client';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { BaseApiResponse, GetMeResponse } from '@/types/api/api-general';
import { AxiosError } from 'axios';
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',
},
},
],
},
],
};
interface RequireAuthProps {
children?: ReactNode;
@@ -18,20 +156,17 @@ const RequireAuth = ({ children }: RequireAuthProps) => {
const router = useRouter();
const { setUser, setIsLoadingUser } = useAuth();
const {
data: userResponse,
isLoading: isLoadingUserResponse,
error: userErrorResponse,
} = useSWRImmutable<
GetMeResponse & { ok?: boolean },
AxiosError<BaseApiResponse>,
SWRHttpKey
>('/sso/userinfo', httpClientFetcher, {
shouldRetryOnError: false,
revalidateOnFocus: false,
revalidateOnReconnect: false,
refreshInterval: 0,
});
const { data: userResponse, isLoading: isLoadingUserResponse } =
useSWRImmutable<GetMeResponse & { ok?: boolean }, unknown, SWRHttpKey>(
'/auth/sso/userinfo',
httpClientFetcher,
{
shouldRetryOnError: false,
revalidateOnFocus: false,
revalidateOnReconnect: false,
refreshInterval: 0,
}
);
useEffect(() => {
setIsLoadingUser(isLoadingUserResponse);
@@ -40,25 +175,23 @@ const RequireAuth = ({ children }: RequireAuthProps) => {
useEffect(() => {
if (isResponseSuccess(userResponse)) {
setUser(userResponse.data);
} else if (
isResponseError(userErrorResponse?.response?.data) &&
typeof window !== 'undefined'
) {
router.replace(
`${process.env.NEXT_PUBLIC_SSO_LOGIN_URL as string}?redirect_url=${window.location.href}`
);
} else {
// router.replace(process.env.NEXT_PUBLIC_SSO_LOGIN_URL as string);
// TODO: remove this later, DONT HARDCODE USER DATA
setUser(DUMMY_USER);
}
}, [userResponse, userErrorResponse, setIsLoadingUser, setUser]);
}, [userResponse, setIsLoadingUser, setUser]);
if (isLoadingUserResponse && !userResponse && !userErrorResponse) {
return (
<div className='w-full flex flex-row justify-center items-center p-4'>
<span className='loading loading-spinner loading-xl' />
</div>
);
}
// TODO: uncomment this later
// if (isLoadingUserResponse && !userResponse) {
// return (
// <div className='w-full flex flex-row justify-center items-center p-4'>
// <span className='loading loading-spinner loading-xl' />
// </div>
// );
// }
return <>{isResponseSuccess(userResponse) && children}</>;
return <>{children}</>;
};
export default RequireAuth;
@@ -0,0 +1,104 @@
'use client';
import { Icon } from '@iconify/react';
import Link from 'next/link';
import { ReactNode } from 'react';
import { cn } from '@/lib/helper';
export interface DrawerHeaderProps {
// Left side props
leftIcon?: string;
leftIconSize?: number;
leftIconHref?: string;
leftIconOnClick?: () => void;
leftIconClassName?: string;
// Subtitle/label props
subtitle?: string | ReactNode;
subtitleClassName?: string;
// Right side actions (children)
children?: ReactNode;
// Container props
className?: string;
showDivider?: boolean;
}
const DrawerHeader = ({
leftIcon = 'mdi:close',
leftIconSize = 24,
leftIconHref,
leftIconOnClick,
leftIconClassName,
subtitle,
subtitleClassName,
children,
className,
showDivider = true,
}: DrawerHeaderProps) => {
const renderLeftIcon = () => {
const iconElement = (
<Icon
icon={leftIcon}
width={leftIconSize}
height={leftIconSize}
className={cn('cursor-pointer', leftIconClassName)}
/>
);
if (leftIconHref) {
return (
<Link href={leftIconHref} className='hover:text-gray-400'>
{iconElement}
</Link>
);
}
if (leftIconOnClick) {
return (
<button
onClick={leftIconOnClick}
className='hover:text-gray-400 bg-transparent border-none p-0'
>
{iconElement}
</button>
);
}
return iconElement;
};
return (
<div
className={cn(
'flex flex-row justify-between items-center px-4 pt-4',
className
)}
>
{/* Left Side */}
<div className='flex flex-row h-full gap-2 items-center'>
{renderLeftIcon()}
{showDivider && subtitle && (
<div className='divider divider-horizontal p-0 m-0'></div>
)}
{subtitle && (
<div className={cn('text-sm text-neutral', subtitleClassName)}>
{subtitle}
</div>
)}
</div>
{/* Right Side Actions */}
{children && (
<div className='flex flex-row gap-3 justify-end items-center'>
{children}
</div>
)}
</div>
);
};
export default DrawerHeader;