mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 21:41:57 +00:00
feat(FE): create floation actions button
This commit is contained in:
@@ -36,7 +36,7 @@ export default function ProjectFlockLayout({
|
||||
return (
|
||||
<>
|
||||
{/* List page always rendered */}
|
||||
<div>
|
||||
<div className='min-h-sceen w-full relative'>
|
||||
<ProjectFlockTable
|
||||
refresh={() => !isOpen && router.push('/production/project-flock')}
|
||||
/>
|
||||
@@ -48,7 +48,7 @@ export default function ProjectFlockLayout({
|
||||
setOpen={(v) => {
|
||||
if (!v) router.push('/production/project-flock');
|
||||
}}
|
||||
closeOnBackdropClick={false}
|
||||
closeOnBackdropClick={isDetail ? true : false}
|
||||
onBackdropClick={handleBackdropClick}
|
||||
variant='right'
|
||||
sidebarContent={isOpen && <div className='p-4'>{children}</div>}
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
'use client';
|
||||
|
||||
import { cn } from '@/lib/helper';
|
||||
import { Icon } from '@iconify/react';
|
||||
|
||||
type FloatingActionsButtonProps = {
|
||||
actions: {
|
||||
action: 'DETAIL' | 'EDIT' | 'DELETE';
|
||||
icon: string;
|
||||
label?: string;
|
||||
onClick?: () => void;
|
||||
hidden?: boolean;
|
||||
}[];
|
||||
approvals: {
|
||||
action: 'APPROVED' | 'REJECTED';
|
||||
icon: string;
|
||||
label?: string;
|
||||
onClick?: () => void;
|
||||
}[];
|
||||
selectedRowIds: number[];
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
const FloatingActionsButton = ({
|
||||
actions,
|
||||
approvals,
|
||||
selectedRowIds,
|
||||
onClose,
|
||||
}: FloatingActionsButtonProps) => {
|
||||
// Jika tidak ada baris yang dipilih, jangan tampilkan FAB
|
||||
const positionStyles =
|
||||
selectedRowIds.length > 0 ? 'bottom-[10%]' : 'bottom-[-100%]';
|
||||
|
||||
// Helper untuk menentukan gaya warna tombol approval
|
||||
const getApprovalColor = (action: 'APPROVED' | 'REJECTED') => {
|
||||
if (action === 'APPROVED') return 'success';
|
||||
if (action === 'REJECTED') return 'error';
|
||||
return 'primary';
|
||||
};
|
||||
|
||||
const getActionColor = (action: 'DETAIL' | 'EDIT' | 'DELETE') => {
|
||||
if (action === 'DETAIL') return 'white';
|
||||
if (action === 'EDIT') return 'warning';
|
||||
if (action === 'DELETE') return 'error';
|
||||
return 'primary';
|
||||
};
|
||||
|
||||
return (
|
||||
// Container utama FAB
|
||||
<div
|
||||
className={cn(
|
||||
`absolute ${positionStyles} inset-x-1/2 -translate-x-1/2 z-50`,
|
||||
'mx-auto w-full max-w-xl sm:mx-0 bg-base-300 p-4 rounded-xl shadow-md transition-all duration-300 transform',
|
||||
'bg-slate-950 backdrop-blur-md'
|
||||
)}
|
||||
>
|
||||
<div className='flex flex-col gap-3'>
|
||||
{/* === BARIS ATAS: Status Seleksi dan Actions (Termasuk Close) === */}
|
||||
<div className='flex justify-between items-center text-white'>
|
||||
<h4 className='text-base font-semibold'>
|
||||
{selectedRowIds.length} Selected
|
||||
</h4>
|
||||
|
||||
<div className='flex flex-row gap-1 items-stretch'>
|
||||
<div className='flex gap-4 items-center'>
|
||||
{/* Render Aksi dari props.actions */}
|
||||
{actions
|
||||
.filter((action) => !action.hidden)
|
||||
.map((action, index) => {
|
||||
return (
|
||||
<button
|
||||
key={index}
|
||||
onClick={action.onClick}
|
||||
className='text-white hover:text-gray-400 tooltip tooltip-bottom'
|
||||
data-tip={action.label}
|
||||
>
|
||||
<Icon
|
||||
icon={action.icon}
|
||||
width={24}
|
||||
height={24}
|
||||
className={`text-${getActionColor(action.action)} font-thin`}
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
|
||||
<div className='border-[0.5px] border-white/30 h-full'></div>
|
||||
|
||||
{/* Tombol Close */}
|
||||
<button
|
||||
onClick={onClose}
|
||||
className='text-white hover:text-gray-400'
|
||||
>
|
||||
<Icon icon='mdi:close' width={24} height={24} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* === BARIS BAWAH: Approval Buttons (Approve/Reject) === */}
|
||||
<div className={`grid grid-cols-${approvals.length} gap-3`}>
|
||||
{approvals.map((approval, index) => (
|
||||
<button
|
||||
key={index}
|
||||
onClick={approval.onClick}
|
||||
className={cn(
|
||||
'btn btn-lg w-full',
|
||||
'bg-white/20 hover:bg-white/40 border-white/30 hover:border-white/50',
|
||||
'text-white/50 hover:text-white/100 font-semibold flex items-center gap-2 rounded-lg transition-all duration-200'
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
icon={approval.icon}
|
||||
width={20}
|
||||
height={20}
|
||||
className={`text-${getApprovalColor(approval.action)}`}
|
||||
/>
|
||||
{approval.label || approval.action}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FloatingActionsButton;
|
||||
@@ -1,6 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import Button from '@/components/Button';
|
||||
import FloatingActionsButton from '@/components/FloatingActionsButton';
|
||||
import CheckboxInput from '@/components/input/CheckboxInput';
|
||||
import DebouncedTextInput from '@/components/input/DebouncedTextInput';
|
||||
import SelectInput, { OptionType } from '@/components/input/SelectInput';
|
||||
@@ -20,6 +21,7 @@ import { Kandang } from '@/types/api/master-data/kandang';
|
||||
import { ProjectFlock } from '@/types/api/production/project-flock';
|
||||
import { Icon } from '@iconify/react';
|
||||
import { CellContext, SortingState } from '@tanstack/react-table';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { ChangeEventHandler, useEffect, useState } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
import useSWR from 'swr';
|
||||
@@ -119,6 +121,7 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
||||
periodFilter: 'period',
|
||||
},
|
||||
});
|
||||
const router = useRouter();
|
||||
|
||||
// State
|
||||
const [rowSelection, setRowSelection] = useState<Record<string, boolean>>({});
|
||||
@@ -262,7 +265,7 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='w-full p-0 sm:p-4'>
|
||||
<div className='min-h-screen w-full p-0 sm:p-4'>
|
||||
<div className='flex flex-col gap-2 mb-4'>
|
||||
<div className='w-full flex flex-col justify-between items-end gap-2'>
|
||||
<div className='flex flex-col sm:flex-row gap-3 w-full'>
|
||||
@@ -577,6 +580,57 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FloatingActionsButton
|
||||
actions={[
|
||||
{
|
||||
action: 'DETAIL',
|
||||
icon: 'mdi:eye-outline',
|
||||
label: 'Lihat Detail',
|
||||
hidden: selectedRowIds.length !== 1,
|
||||
onClick() {
|
||||
router.push(
|
||||
`/production/project-flock/detail?projectFlockId=${selectedRowIds[0]}`
|
||||
);
|
||||
setRowSelection({});
|
||||
},
|
||||
},
|
||||
{
|
||||
action: 'DELETE',
|
||||
icon: 'material-symbols:delete-outline-rounded',
|
||||
label: 'Hapus Massal',
|
||||
onClick: () => {
|
||||
toast.error(`Konfirmasi hapus ${selectedRowIds.length} data.`);
|
||||
},
|
||||
},
|
||||
]}
|
||||
approvals={[
|
||||
{
|
||||
icon: 'material-symbols:check',
|
||||
label: 'Approve',
|
||||
action: 'APPROVED',
|
||||
onClick: () => {
|
||||
setApprovalAction('APPROVED');
|
||||
confirmModal.openModal();
|
||||
setRowSelection({});
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: 'mdi:times',
|
||||
label: 'Reject',
|
||||
action: 'REJECTED',
|
||||
onClick: () => {
|
||||
setApprovalAction('REJECTED');
|
||||
confirmModal.openModal();
|
||||
setRowSelection({});
|
||||
},
|
||||
},
|
||||
]}
|
||||
selectedRowIds={selectedRowIds}
|
||||
onClose={() => {
|
||||
setRowSelection({});
|
||||
}}
|
||||
/>
|
||||
|
||||
<ConfirmationModal
|
||||
ref={deleteModal.ref}
|
||||
type='error'
|
||||
|
||||
Reference in New Issue
Block a user