2025-09-10 19:42:26 +07:00

266 lines
6.5 KiB
TypeScript

'use client'
// Next Imports
import dynamic from 'next/dynamic'
// MUI Imports
import Card from '@mui/material/Card'
import CardHeader from '@mui/material/CardHeader'
import CardContent from '@mui/material/CardContent'
import Typography from '@mui/material/Typography'
import Box from '@mui/material/Box'
import Button from '@mui/material/Button'
// Third-party Imports
import type { ApexOptions } from 'apexcharts'
import Link from 'next/link'
// Styled Component Imports
const AppReactApexCharts = dynamic(() => import('@/libs/styles/AppReactApexCharts'))
// Types
interface Balance {
amount: string | number
label: string
}
interface ChartData {
name: string
data: number[]
}
interface CashBankCardProps {
title: string
accountNumber: string
balances: Balance[]
chartData: ChartData[]
categories: string[]
buttonText?: string
buttonIcon?: string
href?: string
chartColor?: string
height?: number
currency?: 'IDR' | 'USD' | 'EUR'
showButton?: boolean
maxValue?: number
enableHover?: boolean
}
const CashBankCard = ({
title,
accountNumber,
balances,
chartData,
categories,
href,
chartColor = '#ff6b9d',
height = 300,
currency = 'IDR',
showButton = true,
maxValue,
enableHover = true
}: CashBankCardProps) => {
// Vars
const divider = 'var(--mui-palette-divider)'
const disabledText = 'var(--mui-palette-text-disabled)'
const primaryText = 'var(--mui-palette-text-primary)'
// Auto calculate maxValue if not provided
const calculatedMaxValue = maxValue || Math.max(...chartData.flatMap(series => series.data)) * 1.2
// Currency formatter
const formatCurrency = (value: number) => {
const formatters = {
IDR: new Intl.NumberFormat('id-ID', {
style: 'currency',
currency: 'IDR',
minimumFractionDigits: 0
}),
USD: new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 0
}),
EUR: new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR',
minimumFractionDigits: 0
})
}
return formatters[currency].format(value)
}
// Format balance display
const formatBalance = (amount: string | number) => {
if (typeof amount === 'string') return amount
return new Intl.NumberFormat('id-ID').format(amount)
}
// Y-axis label formatter based on currency
const formatYAxisLabel = (val: number) => {
if (currency === 'IDR') {
return (val / 1000000).toFixed(0) + '.000.000'
}
return (val / 1000).toFixed(0) + 'K'
}
const options: ApexOptions = {
chart: {
height: height,
type: 'area',
parentHeightOffset: 0,
zoom: { enabled: false },
toolbar: { show: false }
},
colors: [chartColor],
fill: {
type: 'gradient',
gradient: {
shade: 'light',
type: 'vertical',
shadeIntensity: 0.4,
gradientToColors: [chartColor],
inverseColors: false,
opacityFrom: 0.8,
opacityTo: 0.1,
stops: [0, 100]
}
},
stroke: {
curve: 'smooth',
width: 3
},
dataLabels: { enabled: false },
grid: {
show: true,
borderColor: divider,
strokeDashArray: 3,
padding: {
top: -10,
bottom: -10,
left: 20,
right: 20
},
xaxis: {
lines: { show: true }
},
yaxis: {
lines: { show: true }
}
},
tooltip: {
enabled: true,
y: {
formatter: function (val: number) {
return formatCurrency(val)
}
}
},
yaxis: {
min: 0,
max: calculatedMaxValue,
tickAmount: 7,
labels: {
style: {
colors: disabledText,
fontSize: '12px',
fontWeight: '400'
},
formatter: function (val: number) {
return formatYAxisLabel(val)
}
}
},
xaxis: {
axisBorder: { show: false },
axisTicks: { show: false },
crosshairs: {
stroke: { color: divider }
},
labels: {
style: {
colors: disabledText,
fontSize: '12px',
fontWeight: '400'
}
},
categories: categories
},
legend: {
show: false
}
}
return (
<Card
sx={{
height: '100%',
transition: 'transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out',
'&:hover': enableHover
? {
transform: 'translateY(-4px)',
boxShadow: '0 12px 35px rgba(0, 0, 0, 0.15)'
}
: {},
cursor: enableHover ? 'pointer' : 'default'
}}
>
<CardHeader
title={
<Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', mb: 2 }}>
<Box>
<Typography variant='h6' sx={{ fontWeight: 600, color: primaryText }}>
{title}
</Typography>
<Typography variant='body2' sx={{ color: disabledText }}>
{accountNumber}
</Typography>
</Box>
{showButton && (
<Button
variant='contained'
size='small'
component={Link}
href={href}
className='bg-primary '
sx={{
textTransform: 'none',
fontSize: '12px'
}}
>
<i className='tabler-wallet text-sm mr-2' /> Atur Akun
</Button>
)}
</Box>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, mb: 3 }}>
{balances.map((balance, index) => (
<Box key={index}>
<Typography variant='h4' sx={{ fontWeight: 700, color: primaryText }}>
{formatBalance(balance.amount)}
</Typography>
<Typography variant='body2' sx={{ color: disabledText }}>
{balance.label}
</Typography>
</Box>
))}
</Box>
</Box>
}
sx={{
pb: 0,
'& .MuiCardHeader-content': {
width: '100%'
}
}}
/>
<CardContent sx={{ pt: 0 }}>
<AppReactApexCharts type='area' width='100%' height={height} options={options} series={chartData} />
</CardContent>
</Card>
)
}
export default CashBankCard