diff --git a/src/app/[lang]/(dashboard)/(private)/apps/cash-bank/page.tsx b/src/app/[lang]/(dashboard)/(private)/apps/cash-bank/page.tsx new file mode 100644 index 0000000..b63c066 --- /dev/null +++ b/src/app/[lang]/(dashboard)/(private)/apps/cash-bank/page.tsx @@ -0,0 +1,7 @@ +import CashBankList from '@/views/apps/cash-bank/CashBankList' + +const CashBankPage = () => { + return +} + +export default CashBankPage diff --git a/src/components/layout/vertical/VerticalMenu.tsx b/src/components/layout/vertical/VerticalMenu.tsx index 007da5d..e963920 100644 --- a/src/components/layout/vertical/VerticalMenu.tsx +++ b/src/components/layout/vertical/VerticalMenu.tsx @@ -116,6 +116,14 @@ const VerticalMenu = ({ dictionary, scrollMenu }: Props) => { }> {dictionary['navigation'].list} + } + exactMatch={false} + activeUrl='/apps/cash-bank' + > + {dictionary['navigation'].cash_and_bank} + }> {dictionary['navigation'].list} diff --git a/src/data/dictionaries/en.json b/src/data/dictionaries/en.json index c9ab714..df083d2 100644 --- a/src/data/dictionaries/en.json +++ b/src/data/dictionaries/en.json @@ -123,6 +123,7 @@ "deliveries": "Deliveries", "sales_orders": "Orders", "quotes": "Quotes", - "expenses": "Expenses" + "expenses": "Expenses", + "cash_and_bank": "Cash & Bank" } } diff --git a/src/data/dictionaries/id.json b/src/data/dictionaries/id.json index 5c4742b..53ddf6d 100644 --- a/src/data/dictionaries/id.json +++ b/src/data/dictionaries/id.json @@ -123,6 +123,7 @@ "deliveries": "Pengiriman", "sales_orders": "Pemesanan", "quotes": "Penawaran", - "expenses": "Biaya" + "expenses": "Biaya", + "cash_and_bank": "Kas & Bank" } } diff --git a/src/views/apps/cash-bank/CashBankCard.tsx b/src/views/apps/cash-bank/CashBankCard.tsx new file mode 100644 index 0000000..94f1b10 --- /dev/null +++ b/src/views/apps/cash-bank/CashBankCard.tsx @@ -0,0 +1,263 @@ +'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' + +// 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 + onButtonClick?: () => void + chartColor?: string + height?: number + currency?: 'IDR' | 'USD' | 'EUR' + showButton?: boolean + maxValue?: number + enableHover?: boolean +} + +const CashBankCard = ({ + title, + accountNumber, + balances, + chartData, + categories, + onButtonClick, + 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 ( + + + + + + {title} + + + {accountNumber} + + + {showButton && ( + + )} + + + + {balances.map((balance, index) => ( + + + {formatBalance(balance.amount)} + + + {balance.label} + + + ))} + + + } + sx={{ + pb: 0, + '& .MuiCardHeader-content': { + width: '100%' + } + }} + /> + + + + + ) +} + +export default CashBankCard diff --git a/src/views/apps/cash-bank/CashBankList.tsx b/src/views/apps/cash-bank/CashBankList.tsx new file mode 100644 index 0000000..740fe03 --- /dev/null +++ b/src/views/apps/cash-bank/CashBankList.tsx @@ -0,0 +1,326 @@ +'use client' + +import { useState, useMemo, useEffect } from 'react' +import Grid from '@mui/material/Grid2' +import TextField, { TextFieldProps } from '@mui/material/TextField' +import InputAdornment from '@mui/material/InputAdornment' +import Box from '@mui/material/Box' +import Typography from '@mui/material/Typography' +import Chip from '@mui/material/Chip' +import FormControl from '@mui/material/FormControl' +import InputLabel from '@mui/material/InputLabel' +import Select from '@mui/material/Select' +import MenuItem from '@mui/material/MenuItem' +import CashBankCard from './CashBankCard' // Adjust import path as needed +import CustomTextField from '@/@core/components/mui/TextField' + +// Types +interface BankAccount { + id: string + title: string + accountNumber: string + balances: Array<{ + amount: string | number + label: string + }> + chartData: Array<{ + name: string + data: number[] + }> + categories: string[] + chartColor?: string + currency: 'IDR' | 'USD' | 'EUR' + accountType: 'giro' | 'savings' | 'investment' | 'credit' | 'cash' + bank: string + status: 'active' | 'inactive' | 'blocked' +} + +// Dummy Data +const dummyAccounts: BankAccount[] = [ + { + id: '1', + title: 'Giro', + accountNumber: '1-10003', + balances: [ + { amount: '7.313.321', label: 'Saldo di bank' }, + { amount: '30.631.261', label: 'Saldo di kledo' } + ], + chartData: [ + { + name: 'Saldo', + data: [ + 20000000, 21000000, 20500000, 20800000, 21500000, 22000000, 25000000, 26000000, 28000000, 29000000, 30000000, + 31000000 + ] + } + ], + categories: ['Apr', 'Mei', 'Jun', 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des', 'Jan', 'Feb', 'Mar'], + chartColor: '#ff6b9d', + currency: 'IDR', + accountType: 'giro', + bank: 'Bank Mandiri', + status: 'active' + }, + { + id: '2', + title: 'Tabungan Premium', + accountNumber: 'SAV-001234', + balances: [ + { amount: 15420000, label: 'Saldo Tersedia' }, + { amount: 18750000, label: 'Total Saldo' } + ], + chartData: [ + { + name: 'Balance', + data: [ + 12000000, 13500000, 14200000, 15000000, 15800000, 16200000, 17000000, 17500000, 18000000, 18200000, 18500000, + 18750000 + ] + } + ], + categories: ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des'], + chartColor: '#4285f4', + currency: 'IDR', + accountType: 'savings', + bank: 'Bank BCA', + status: 'active' + }, + { + id: '3', + title: 'Investment Portfolio', + accountNumber: 'INV-789012', + balances: [ + { amount: 125000, label: 'Portfolio Value' }, + { amount: 8750, label: 'Total Gains' } + ], + chartData: [ + { + name: 'Portfolio Value', + data: [110000, 115000, 112000, 118000, 122000, 119000, 125000, 128000, 126000, 130000, 127000, 125000] + } + ], + categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + currency: 'USD', + accountType: 'investment', + bank: 'Charles Schwab', + status: 'active' + }, + { + id: '4', + title: 'Kartu Kredit Platinum', + accountNumber: 'CC-456789', + balances: [ + { amount: 2500000, label: 'Saldo Saat Ini' }, + { amount: 47500000, label: 'Limit Tersedia' } + ], + chartData: [ + { + name: 'Spending', + data: [ + 1200000, 1800000, 2200000, 1900000, 2100000, 2400000, 2800000, 2600000, 2300000, 2500000, 2700000, 2500000 + ] + } + ], + categories: ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des'], + currency: 'IDR', + accountType: 'credit', + bank: 'Bank BNI', + status: 'active' + }, + { + id: '5', + title: 'Deposito Berjangka', + accountNumber: 'DEP-334455', + balances: [ + { amount: 50000000, label: 'Pokok Deposito' }, + { amount: 2500000, label: 'Bunga Terkumpul' } + ], + chartData: [ + { + name: 'Deposito Growth', + data: [ + 50000000, 50200000, 50420000, 50650000, 50880000, 51120000, 51360000, 51610000, 51860000, 52120000, 52380000, + 52500000 + ] + } + ], + categories: ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des'], + currency: 'IDR', + accountType: 'savings', + bank: 'Bank BRI', + status: 'active' + }, + { + id: '6', + title: 'Cash Management', + accountNumber: 'CSH-111222', + balances: [{ amount: 5000, label: 'Available Cash' }], + chartData: [ + { + name: 'Cash Flow', + data: [4000, 4500, 4200, 4800, 5200, 4900, 5000, 5300, 5100, 5400, 5200, 5000] + } + ], + categories: ['Q1', 'Q2', 'Q3', 'Q4', 'Q1', 'Q2', 'Q3', 'Q4', 'Q1', 'Q2', 'Q3', 'Q4'], + chartColor: '#00bcd4', + currency: 'USD', + accountType: 'cash', + bank: 'Wells Fargo', + status: 'active' + }, + { + id: '7', + title: 'Rekening Bisnis', + accountNumber: 'BIZ-998877', + balances: [ + { amount: 85000000, label: 'Saldo Operasional' }, + { amount: 15000000, label: 'Dana Cadangan' } + ], + chartData: [ + { + name: 'Business Account', + data: [ + 70000000, 75000000, 80000000, 82000000, 85000000, 88000000, 90000000, 87000000, 85000000, 89000000, 92000000, + 100000000 + ] + } + ], + categories: ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des'], + chartColor: '#ff9800', + currency: 'IDR', + accountType: 'giro', + bank: 'Bank Mandiri', + status: 'active' + }, + { + id: '8', + title: 'Tabungan Pendidikan', + accountNumber: 'EDU-567890', + balances: [ + { amount: 25000000, label: 'Dana Pendidikan' }, + { amount: 3500000, label: 'Bunga Terkumpul' } + ], + chartData: [ + { + name: 'Education Savings', + data: [ + 20000000, 21000000, 22000000, 23000000, 24000000, 24500000, 25000000, 25500000, 26000000, 27000000, 28000000, + 28500000 + ] + } + ], + categories: ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des'], + chartColor: '#3f51b5', + currency: 'IDR', + accountType: 'savings', + bank: 'Bank BCA', + status: 'inactive' + } +] +const DebouncedInput = ({ + value: initialValue, + onChange, + debounce = 500, + ...props +}: { + value: string | number + onChange: (value: string | number) => void + debounce?: number +} & Omit) => { + // States + const [value, setValue] = useState(initialValue) + + useEffect(() => { + setValue(initialValue) + }, [initialValue]) + + useEffect(() => { + const timeout = setTimeout(() => { + onChange(value) + }, debounce) + + return () => clearTimeout(timeout) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [value]) + + return setValue(e.target.value)} /> +} +const CashBankList = () => { + const [searchQuery, setSearchQuery] = useState('') + + // Handle button clicks + const handleAccountAction = (accountId: string, action: string) => { + console.log(`Action: ${action} for account: ${accountId}`) + // Implement your action logic here + } + + // Filter and search logic + const filteredAccounts = useMemo(() => { + return dummyAccounts.filter(account => { + const matchesSearch = + account.title.toLowerCase().includes(searchQuery.toLowerCase()) || + account.accountNumber.toLowerCase().includes(searchQuery.toLowerCase()) || + account.bank.toLowerCase().includes(searchQuery.toLowerCase()) + return matchesSearch + }) + }, [searchQuery]) + + return ( + + {/* Search and Filters */} + + + + setSearchQuery(value as string)} + placeholder='Cari ' + className='max-sm:is-full' + /> + + + + + {/* Account Cards */} + + {filteredAccounts.length > 0 ? ( + filteredAccounts.map(account => ( + + handleAccountAction(account.id, account.accountType || 'default')} + /> + + )) + ) : ( + + + + Tidak ada akun yang ditemukan + + + Coba ubah kata kunci pencarian atau filter yang digunakan + + + + )} + + + ) +} + +export default CashBankList