266 lines
6.5 KiB
TypeScript
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
|