mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
237 lines
6.4 KiB
TypeScript
237 lines
6.4 KiB
TypeScript
'use client';
|
|
|
|
import { MouseEventHandler, RefObject, useState } from 'react';
|
|
|
|
import { Icon } from '@iconify/react';
|
|
import Modal from '@/components/Modal';
|
|
import Button, { ButtonProps } from '@/components/Button';
|
|
|
|
import { cn } from '@/lib/helper';
|
|
|
|
export type IconPosition = 'left' | 'center' | 'right';
|
|
|
|
export interface ConfirmationModalProps {
|
|
ref: RefObject<HTMLDialogElement | null>;
|
|
type?: 'info' | 'success' | 'error';
|
|
text?: string;
|
|
subtitleText?: string;
|
|
closeOnBackdrop?: boolean;
|
|
primaryButton?: ButtonProps & {
|
|
text?: string;
|
|
};
|
|
secondaryButton?: ButtonProps & {
|
|
text?: string;
|
|
};
|
|
className?: {
|
|
modal?: string;
|
|
modalBox?: string;
|
|
};
|
|
children?: React.ReactNode;
|
|
iconSize?: number;
|
|
iconPosition?: IconPosition;
|
|
}
|
|
|
|
const iconConfig = {
|
|
info: {
|
|
icon: 'material-symbols:info-outline-rounded',
|
|
iconClassName: 'text-info-content',
|
|
innerRingClassName: 'bg-info',
|
|
middleRingClassName: 'bg-info/12',
|
|
outerRingClassName: 'border-info/12 bg-info/8',
|
|
},
|
|
success: {
|
|
icon: 'heroicons:check',
|
|
iconClassName: 'text-white',
|
|
innerRingClassName: 'bg-success',
|
|
middleRingClassName: 'bg-success/12',
|
|
outerRingClassName: 'border-success/12 bg-success/8',
|
|
},
|
|
error: {
|
|
icon: 'heroicons:exclamation-triangle',
|
|
iconClassName: 'text-error-content',
|
|
innerRingClassName: 'bg-error',
|
|
middleRingClassName: 'bg-error/12',
|
|
outerRingClassName: 'border-error/12 bg-error/8',
|
|
},
|
|
} as const;
|
|
|
|
const ConfirmationModalIcon = ({
|
|
type,
|
|
size = 16,
|
|
}: {
|
|
type: 'info' | 'success' | 'error';
|
|
size?: number;
|
|
}) => {
|
|
const config = iconConfig[type];
|
|
|
|
return (
|
|
<div
|
|
className={cn('rounded-full border p-[5px]', config.outerRingClassName)}
|
|
>
|
|
<div className={cn('rounded-full p-2', config.middleRingClassName)}>
|
|
<div
|
|
className={cn(
|
|
'rounded-full p-1 flex items-center justify-center',
|
|
config.innerRingClassName
|
|
)}
|
|
>
|
|
<Icon
|
|
icon={config.icon}
|
|
width={size}
|
|
height={size}
|
|
className={config.iconClassName}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const ConfirmationModal = ({
|
|
ref,
|
|
type = 'info',
|
|
text,
|
|
subtitleText,
|
|
closeOnBackdrop,
|
|
primaryButton,
|
|
secondaryButton,
|
|
className,
|
|
children,
|
|
iconSize = 16,
|
|
iconPosition = 'center',
|
|
}: ConfirmationModalProps) => {
|
|
const [isPrimaryButtonLoading, setIsPrimaryButtonLoading] = useState(false);
|
|
|
|
const closeModalHandler = () => {
|
|
ref.current?.close();
|
|
};
|
|
|
|
const primaryButtonClickHandler: MouseEventHandler<
|
|
HTMLButtonElement
|
|
> = async (event) => {
|
|
setIsPrimaryButtonLoading(true);
|
|
|
|
await primaryButton?.onClick?.(event);
|
|
|
|
setIsPrimaryButtonLoading(false);
|
|
};
|
|
|
|
return (
|
|
<Modal
|
|
ref={ref}
|
|
closeOnBackdrop={closeOnBackdrop}
|
|
className={{
|
|
...className,
|
|
modalBox: cn('rounded-xl p-4', className?.modalBox),
|
|
}}
|
|
>
|
|
<div className='w-full flex flex-col gap-4'>
|
|
{iconPosition === 'center' ? (
|
|
<>
|
|
<div className='w-fit mx-auto'>
|
|
<ConfirmationModalIcon type={type} size={iconSize} />
|
|
</div>
|
|
|
|
<p className='text-center font-medium'>
|
|
{text ?? 'Apakah anda yakin ingin melakukan hal ini?'}
|
|
</p>
|
|
|
|
{subtitleText && (
|
|
<p className='text-center text-sm text-gray-400'>
|
|
{subtitleText}
|
|
</p>
|
|
)}
|
|
</>
|
|
) : (
|
|
<div
|
|
className={cn('flex flex-row items-center gap-3', {
|
|
'flex-row': iconPosition === 'left',
|
|
'flex-row-reverse': iconPosition === 'right',
|
|
})}
|
|
>
|
|
<div className='w-fit'>
|
|
<ConfirmationModalIcon type={type} size={iconSize} />
|
|
</div>
|
|
|
|
<div className='flex flex-col gap-1'>
|
|
<p className='text-sm font-semibold'>
|
|
{text ?? 'Apakah anda yakin ingin melakukan hal ini?'}
|
|
</p>
|
|
|
|
{subtitleText && (
|
|
<p className='text-xs text-base-content/50'>{subtitleText}</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{children && <div className='w-full'>{children}</div>}
|
|
|
|
{(secondaryButton || primaryButton) && (
|
|
<div
|
|
className={cn('w-full grid gap-3', {
|
|
'grid-cols-2': secondaryButton && primaryButton,
|
|
'grid-cols-1':
|
|
(secondaryButton && !primaryButton) ||
|
|
(!secondaryButton && primaryButton),
|
|
})}
|
|
>
|
|
{secondaryButton && secondaryButton.text && (
|
|
<Button
|
|
{...secondaryButton}
|
|
variant='outline'
|
|
color={secondaryButton?.color}
|
|
isLoading={secondaryButton?.isLoading}
|
|
disabled={
|
|
secondaryButton?.isLoading !== undefined
|
|
? secondaryButton?.isLoading
|
|
: isPrimaryButtonLoading
|
|
}
|
|
onClick={(e) => {
|
|
if (secondaryButton?.onClick) {
|
|
secondaryButton.onClick(e);
|
|
} else {
|
|
closeModalHandler();
|
|
}
|
|
}}
|
|
className={cn(
|
|
'p-2 rounded-xl text-sm',
|
|
secondaryButton?.className
|
|
)}
|
|
>
|
|
{secondaryButton?.text ?? 'Tidak'}
|
|
</Button>
|
|
)}
|
|
|
|
{primaryButton && primaryButton.text && (
|
|
<Button
|
|
{...primaryButton}
|
|
color={primaryButton?.color ?? 'info'}
|
|
onClick={primaryButtonClickHandler}
|
|
isLoading={
|
|
primaryButton?.isLoading !== undefined
|
|
? primaryButton?.isLoading
|
|
: isPrimaryButtonLoading
|
|
}
|
|
disabled={
|
|
primaryButton?.isLoading !== undefined
|
|
? primaryButton?.isLoading
|
|
: isPrimaryButtonLoading
|
|
}
|
|
className={cn(
|
|
'p-2 rounded-xl text-sm',
|
|
primaryButton?.className
|
|
)}
|
|
>
|
|
{primaryButton?.text ?? 'Ya'}
|
|
</Button>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</Modal>
|
|
);
|
|
};
|
|
|
|
export default ConfirmationModal;
|