mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
Merge branch 'development' of gitlab.com:mbugroup/lti-web-client into feat/FE/US-76/TASK-114-129-136-slicing-ui-and-validation-create-edit-daily-recording-growing-form
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
@import 'tailwindcss';
|
||||
@plugin "daisyui";
|
||||
@import '../styles/daisyui.css';
|
||||
|
||||
:root {
|
||||
--color-primary: #1f74bf;
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { cn } from '@/lib/helper';
|
||||
import { Color } from '@/types/theme';
|
||||
|
||||
interface TooltipProps {
|
||||
children?: ReactNode;
|
||||
content?: ReactNode;
|
||||
className?: {
|
||||
wrapper?: string;
|
||||
content?: string;
|
||||
};
|
||||
open?: boolean;
|
||||
color?: Color;
|
||||
position?: 'top' | 'bottom' | 'left' | 'right';
|
||||
}
|
||||
|
||||
const Tooltip = ({
|
||||
children,
|
||||
content,
|
||||
className,
|
||||
open,
|
||||
color,
|
||||
position,
|
||||
}: TooltipProps) => {
|
||||
const tooltipBaseClassName = cn('tooltip', {
|
||||
'tooltip-open': typeof open === 'boolean' && open,
|
||||
|
||||
'tooltip-top': position === 'top',
|
||||
'tooltip-bottom': position === 'bottom',
|
||||
'tooltip-left': position === 'left',
|
||||
'tooltip-right': position === 'right',
|
||||
|
||||
'tooltip-primary': color === 'primary',
|
||||
'tooltip-secondary': color === 'secondary',
|
||||
'tooltip-accent': color === 'accent',
|
||||
'tooltip-neutral': color === 'neutral',
|
||||
'tooltip-info': color === 'info',
|
||||
'tooltip-success': color === 'success',
|
||||
'tooltip-warning': color === 'warning',
|
||||
'tooltip-error': color === 'error',
|
||||
});
|
||||
return (
|
||||
<div className={cn(tooltipBaseClassName, className?.wrapper)}>
|
||||
<div
|
||||
className={cn(
|
||||
'tooltip-content',
|
||||
'max-w-60 sm:max-w-xs',
|
||||
className?.content
|
||||
)}
|
||||
>
|
||||
{content}
|
||||
</div>
|
||||
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Tooltip;
|
||||
@@ -0,0 +1,64 @@
|
||||
import { Icon } from '@iconify/react';
|
||||
import Steps from '@/components/steps/Steps';
|
||||
import StepItem from '@/components/steps/StepItem';
|
||||
import Tooltip from '@/components/Tooltip';
|
||||
|
||||
import { formatDate } from '@/lib/helper';
|
||||
import { ApprovalsLine } from '@/types/api/api-general';
|
||||
|
||||
interface ApprovalStepsProps {
|
||||
approvals: ApprovalsLine;
|
||||
}
|
||||
|
||||
const ApprovalSteps = ({ approvals }: ApprovalStepsProps) => {
|
||||
return (
|
||||
<Steps direction='vertical' className='w-full md:steps-horizontal'>
|
||||
{approvals.map((approval, idx) => {
|
||||
const stepItemColor =
|
||||
approval.status === 'approved'
|
||||
? 'success'
|
||||
: approval.status === 'rejected'
|
||||
? 'error'
|
||||
: undefined;
|
||||
|
||||
const stepItemIcon =
|
||||
approval.status === 'approved'
|
||||
? 'material-symbols:check-rounded'
|
||||
: approval.status === 'rejected'
|
||||
? 'material-symbols:close-rounded'
|
||||
: 'bxs:hourglass';
|
||||
|
||||
return (
|
||||
<StepItem
|
||||
key={idx}
|
||||
color={stepItemColor}
|
||||
icon={
|
||||
approval.status !== 'waiting' && (
|
||||
<Tooltip
|
||||
color={stepItemColor}
|
||||
position='right'
|
||||
className={{
|
||||
wrapper: 'md:tooltip-bottom',
|
||||
}}
|
||||
content={
|
||||
<div className='flex flex-col text-base'>
|
||||
<span>{formatDate(approval.date, 'YYYY-MM-DD')}</span>
|
||||
<span>Oleh: {approval.action_by}</span>
|
||||
<span>Catatan: {approval.notes}</span>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Icon icon={stepItemIcon} width={24} height={24} />
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
>
|
||||
{approval.role}
|
||||
</StepItem>
|
||||
);
|
||||
})}
|
||||
</Steps>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApprovalSteps;
|
||||
@@ -0,0 +1,34 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { cn } from '@/lib/helper';
|
||||
import { Color } from '@/types/theme';
|
||||
|
||||
interface StepItemProps {
|
||||
children?: ReactNode;
|
||||
icon?: ReactNode;
|
||||
className?: string;
|
||||
color?: Color;
|
||||
}
|
||||
|
||||
const StepItem = ({ children, icon, className, color }: StepItemProps) => {
|
||||
const stepItemBaseClassName = cn('step', {
|
||||
'step-primary': color === 'primary',
|
||||
'step-secondary': color === 'secondary',
|
||||
'step-accent': color === 'accent',
|
||||
'step-neutral': color === 'neutral',
|
||||
'step-info': color === 'info',
|
||||
'step-success': color === 'success',
|
||||
'step-warning': color === 'warning',
|
||||
'step-error': color === 'error',
|
||||
});
|
||||
|
||||
return (
|
||||
<li className={cn(stepItemBaseClassName, className)}>
|
||||
<span className='step-icon'>{icon}</span>
|
||||
|
||||
<div>{children}</div>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
export default StepItem;
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { cn } from '@/lib/helper';
|
||||
|
||||
interface StepsProps {
|
||||
children?: ReactNode;
|
||||
className?: string;
|
||||
direction?: 'horizontal' | 'vertical';
|
||||
}
|
||||
|
||||
const Steps = ({ children, className, direction }: StepsProps) => {
|
||||
const stepsBaseClassName = cn('steps gap-2', {
|
||||
'steps-horizontal': direction === 'horizontal',
|
||||
'steps-vertical': direction === 'vertical',
|
||||
});
|
||||
|
||||
return (
|
||||
<ul className={cn(stepsBaseClassName, 'overflow-visible!', className)}>
|
||||
{children}
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
|
||||
export default Steps;
|
||||
@@ -6,22 +6,43 @@ type AuthStore = {
|
||||
isLoadingUser?: boolean;
|
||||
setUser: (newUserData?: UserWithRoles) => void;
|
||||
setIsLoadingUser: (isLoading?: boolean) => void;
|
||||
permissionCheck: (permissionName: string) => boolean;
|
||||
};
|
||||
|
||||
const useAuthStore = create<AuthStore>()((set) => ({
|
||||
const useAuthStore = create<AuthStore>()((set, get) => ({
|
||||
user: undefined,
|
||||
isLoadingUser: false,
|
||||
setUser: (newUserData) => set({ user: newUserData }),
|
||||
setIsLoadingUser: (isLoading) => set({ isLoadingUser: Boolean(isLoading) }),
|
||||
|
||||
permissionCheck: (name) => {
|
||||
const { user, isLoadingUser } = get();
|
||||
|
||||
if (!isLoadingUser && user) {
|
||||
const isAllowed = user.roles.some((role) => {
|
||||
const isPermissionNameAllowed = role.permissions.some(
|
||||
(permission) => permission.name === name
|
||||
);
|
||||
|
||||
return isPermissionNameAllowed;
|
||||
});
|
||||
|
||||
return isAllowed;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
}));
|
||||
|
||||
export const useAuth = () => {
|
||||
const { user, setUser, isLoadingUser, setIsLoadingUser } = useAuthStore();
|
||||
const { user, setUser, isLoadingUser, setIsLoadingUser, permissionCheck } =
|
||||
useAuthStore();
|
||||
|
||||
return {
|
||||
user,
|
||||
setUser,
|
||||
isLoadingUser,
|
||||
setIsLoadingUser,
|
||||
permissionCheck,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
@layer utilities {
|
||||
.step.step-success::before {
|
||||
--step-bg: var(--color-success);
|
||||
--step-fg: var(--color-success-content);
|
||||
}
|
||||
|
||||
.step.step-error::before {
|
||||
--step-bg: var(--color-error);
|
||||
--step-fg: var(--color-error-content);
|
||||
}
|
||||
}
|
||||
Vendored
+38
@@ -24,6 +24,36 @@ export type LogoutResponse = BaseApiResponse;
|
||||
|
||||
export type GetMeResponse = BaseApiResponse<UserWithRoles>;
|
||||
|
||||
export type Client = {
|
||||
id: number;
|
||||
name: stirng;
|
||||
alias: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
export type Permission = {
|
||||
id: number;
|
||||
name: string;
|
||||
action: string;
|
||||
client: Omit<Client, 'created_at' | 'updated_at'>;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
export type Role = {
|
||||
id: number;
|
||||
key: string;
|
||||
name: string;
|
||||
client: Omit<Client, 'created_at' | 'updated_at'>;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
export type RoleWithPermissions = Omit<Role, 'created_at' | 'updated_at'> & {
|
||||
permissions: Omit<Permission, 'created_at' | 'updated_at'>[];
|
||||
};
|
||||
|
||||
export type User = {
|
||||
id: number;
|
||||
email: string;
|
||||
@@ -66,3 +96,11 @@ export type flags =
|
||||
| 'STARTER'
|
||||
| 'FINISHER'
|
||||
| 'OVK';
|
||||
|
||||
export type ApprovalsLine = {
|
||||
action_by?: string;
|
||||
date?: string;
|
||||
notes?: string;
|
||||
role?: string;
|
||||
status: 'approved' | 'rejected' | 'waiting';
|
||||
}[];
|
||||
|
||||
Reference in New Issue
Block a user