250 lines
6.6 KiB
TypeScript
250 lines
6.6 KiB
TypeScript
// React Imports
|
|
import React, { useState } from 'react'
|
|
|
|
// MUI Imports
|
|
import Button from '@mui/material/Button'
|
|
import Menu from '@mui/material/Menu'
|
|
import MenuItem from '@mui/material/MenuItem'
|
|
import { styled } from '@mui/material/styles'
|
|
|
|
const DropdownButton = styled(Button)(({ theme }) => ({
|
|
textTransform: 'none',
|
|
fontWeight: 400,
|
|
borderRadius: '8px',
|
|
borderColor: '#e0e0e0',
|
|
color: '#666',
|
|
'&:hover': {
|
|
borderColor: '#ccc',
|
|
backgroundColor: 'rgba(0, 0, 0, 0.04)'
|
|
}
|
|
}))
|
|
|
|
interface StatusFilterTabsProps {
|
|
/**
|
|
* Array of status options to display as filter tabs
|
|
*/
|
|
statusOptions: string[]
|
|
/**
|
|
* Currently selected status filter
|
|
*/
|
|
selectedStatus: string
|
|
/**
|
|
* Callback function when a status is selected
|
|
*/
|
|
onStatusChange: (status: string) => void
|
|
/**
|
|
* Custom className for the container
|
|
*/
|
|
className?: string
|
|
/**
|
|
* Custom styles for the container
|
|
*/
|
|
containerStyle?: React.CSSProperties
|
|
/**
|
|
* Size of the buttons
|
|
*/
|
|
buttonSize?: 'small' | 'medium' | 'large'
|
|
/**
|
|
* Maximum number of status options to show as buttons before switching to dropdown
|
|
*/
|
|
maxButtonsBeforeDropdown?: number
|
|
/**
|
|
* Label for the dropdown when there are many options
|
|
*/
|
|
dropdownLabel?: string
|
|
}
|
|
|
|
const StatusFilterTabs: React.FC<StatusFilterTabsProps> = ({
|
|
statusOptions,
|
|
selectedStatus,
|
|
onStatusChange,
|
|
className = '',
|
|
containerStyle = {},
|
|
buttonSize = 'small',
|
|
maxButtonsBeforeDropdown = 5,
|
|
dropdownLabel = 'Lainnya'
|
|
}) => {
|
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)
|
|
const open = Boolean(anchorEl)
|
|
|
|
const handleDropdownClick = (event: React.MouseEvent<HTMLElement>) => {
|
|
setAnchorEl(event.currentTarget)
|
|
}
|
|
|
|
const handleDropdownClose = () => {
|
|
setAnchorEl(null)
|
|
}
|
|
|
|
const handleDropdownItemClick = (status: string) => {
|
|
onStatusChange(status)
|
|
handleDropdownClose()
|
|
}
|
|
|
|
// If status options are <= maxButtonsBeforeDropdown, show all as buttons
|
|
if (statusOptions.length <= maxButtonsBeforeDropdown) {
|
|
return (
|
|
<div className='flex flex-wrap gap-2'>
|
|
{statusOptions.map(status => (
|
|
<Button
|
|
key={status}
|
|
variant={selectedStatus === status ? 'contained' : 'outlined'}
|
|
color={selectedStatus === status ? 'primary' : 'inherit'}
|
|
onClick={() => onStatusChange(status)}
|
|
size={buttonSize}
|
|
className='rounded-lg'
|
|
sx={{
|
|
textTransform: 'none',
|
|
fontWeight: selectedStatus === status ? 600 : 400,
|
|
borderRadius: '8px',
|
|
...(selectedStatus !== status && {
|
|
borderColor: '#e0e0e0',
|
|
color: '#666'
|
|
})
|
|
}}
|
|
>
|
|
{status}
|
|
</Button>
|
|
))}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
// If more than maxButtonsBeforeDropdown, show first few as buttons and rest in dropdown
|
|
const buttonStatuses = statusOptions.slice(0, maxButtonsBeforeDropdown - 1)
|
|
const dropdownStatuses = statusOptions.slice(maxButtonsBeforeDropdown - 1)
|
|
const isDropdownItemSelected = dropdownStatuses.includes(selectedStatus)
|
|
|
|
return (
|
|
<div className='flex flex-wrap gap-2'>
|
|
{/* Regular buttons for first few statuses */}
|
|
{buttonStatuses.map(status => (
|
|
<Button
|
|
key={status}
|
|
variant={selectedStatus === status ? 'contained' : 'outlined'}
|
|
color={selectedStatus === status ? 'primary' : 'inherit'}
|
|
onClick={() => onStatusChange(status)}
|
|
size={buttonSize}
|
|
className='rounded-lg'
|
|
sx={{
|
|
textTransform: 'none',
|
|
fontWeight: selectedStatus === status ? 600 : 400,
|
|
borderRadius: '8px',
|
|
...(selectedStatus !== status && {
|
|
borderColor: '#e0e0e0',
|
|
color: '#666'
|
|
})
|
|
}}
|
|
>
|
|
{status}
|
|
</Button>
|
|
))}
|
|
|
|
{/* Dropdown button for remaining statuses */}
|
|
<DropdownButton
|
|
variant='outlined'
|
|
onClick={handleDropdownClick}
|
|
size={buttonSize}
|
|
endIcon={<i className='tabler-chevron-down' />}
|
|
sx={{
|
|
...(isDropdownItemSelected && {
|
|
backgroundColor: 'primary.main',
|
|
color: 'primary.contrastText',
|
|
borderColor: 'primary.main',
|
|
fontWeight: 600,
|
|
'&:hover': {
|
|
backgroundColor: 'primary.dark',
|
|
borderColor: 'primary.dark'
|
|
}
|
|
})
|
|
}}
|
|
>
|
|
{isDropdownItemSelected ? selectedStatus : dropdownLabel}
|
|
</DropdownButton>
|
|
|
|
<Menu
|
|
anchorEl={anchorEl}
|
|
open={open}
|
|
onClose={handleDropdownClose}
|
|
PaperProps={{
|
|
elevation: 3,
|
|
sx: {
|
|
mt: 1,
|
|
borderRadius: '8px',
|
|
minWidth: '160px'
|
|
}
|
|
}}
|
|
transformOrigin={{ horizontal: 'left', vertical: 'top' }}
|
|
anchorOrigin={{ horizontal: 'left', vertical: 'bottom' }}
|
|
>
|
|
{dropdownStatuses.map(status => (
|
|
<MenuItem
|
|
key={status}
|
|
onClick={() => handleDropdownItemClick(status)}
|
|
selected={selectedStatus === status}
|
|
sx={{
|
|
fontSize: '14px',
|
|
fontWeight: selectedStatus === status ? 600 : 400,
|
|
color: selectedStatus === status ? 'primary.main' : 'text.primary'
|
|
}}
|
|
>
|
|
{status}
|
|
</MenuItem>
|
|
))}
|
|
</Menu>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default StatusFilterTabs
|
|
|
|
// Example usage:
|
|
/*
|
|
import StatusFilterTabs from '@/components/StatusFilterTabs'
|
|
|
|
// In your component:
|
|
const [statusFilter, setStatusFilter] = useState('Semua')
|
|
|
|
// For few statuses (will show all as buttons)
|
|
const expenseStatusOptions = ['Semua', 'Belum Dibayar', 'Dibayar Sebagian', 'Lunas']
|
|
|
|
// For many statuses (will show some buttons + dropdown)
|
|
const manyStatusOptions = [
|
|
'Semua',
|
|
'Belum Dibayar',
|
|
'Dibayar Sebagian',
|
|
'Lunas',
|
|
'Void',
|
|
'Retur',
|
|
'Jatuh Tempo',
|
|
'Transaksi Berulang',
|
|
'Ditangguhkan',
|
|
'Dibatalkan'
|
|
]
|
|
|
|
<StatusFilterTabs
|
|
statusOptions={expenseStatusOptions}
|
|
selectedStatus={statusFilter}
|
|
onStatusChange={setStatusFilter}
|
|
/>
|
|
|
|
// With many options (will automatically use dropdown)
|
|
<StatusFilterTabs
|
|
statusOptions={manyStatusOptions}
|
|
selectedStatus={statusFilter}
|
|
onStatusChange={setStatusFilter}
|
|
maxButtonsBeforeDropdown={4} // Show 3 buttons + dropdown
|
|
dropdownLabel="Status Lain"
|
|
/>
|
|
|
|
// Custom configuration
|
|
<StatusFilterTabs
|
|
statusOptions={manyStatusOptions}
|
|
selectedStatus={statusFilter}
|
|
onStatusChange={setStatusFilter}
|
|
maxButtonsBeforeDropdown={3}
|
|
dropdownLabel="Lainnya"
|
|
buttonSize="medium"
|
|
showBorder={false}
|
|
/>
|
|
*/
|