409 lines
14 KiB
TypeScript
409 lines
14 KiB
TypeScript
import { Container, KeenIcon, DefaultTooltip } from '@/components';
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue
|
|
} from '@/components/ui/select';
|
|
import { DateRange } from 'react-day-picker';
|
|
import { useState, useEffect, useCallback } from 'react';
|
|
import { Card, Chart, YearPicker } from './blocks';
|
|
import moment from 'moment';
|
|
import { useFetchCardData, useFetchChartData } from './hooks';
|
|
import { Button } from '@/components/ui/button';
|
|
import { useFetchYear } from './hooks/useFetchYear';
|
|
import { get5LastYear } from '@/utils/Date';
|
|
import { staticChartData } from './staticChart';
|
|
import { Helmet } from 'react-helmet';
|
|
import BalanceCard from './blocks/BalanceCard';
|
|
import { getAuth } from '@/auth';
|
|
import { useCallApi } from '@/hooks';
|
|
import { apiConfig } from '@/config/api.config';
|
|
import TransactionValue from './blocks/TransactionValue';
|
|
import TransactionPieChart from './blocks/TransactionPieChart';
|
|
import MemberActivity from './blocks/MemberActivity';
|
|
import BankSaldo from './blocks/BankSaldo';
|
|
|
|
// sum -> nominal, count-> total
|
|
type CountType = 'sum' | 'count';
|
|
type ChartType = 'line' | 'bar';
|
|
type ChartLegend = 'true' | 'false';
|
|
|
|
const DashboardHomePage = () => {
|
|
const selectYear = get5LastYear();
|
|
const [initialYear, setInitialYear] = useState<string>('');
|
|
const [selectedYear, setSelectedYear] = useState<string>('');
|
|
const [count, setCount] = useState<CountType>('sum');
|
|
const [chartType, setChartType] = useState<ChartType>('line');
|
|
const [chartLegend, setChartLegend] = useState<ChartLegend>('true');
|
|
const [dateRange, setDateRange] = useState<{ from: Date; to: Date }>({
|
|
from: new Date(),
|
|
to: new Date()
|
|
});
|
|
|
|
const getFirstDayOfMonth = () => {
|
|
const now = new Date();
|
|
return new Date(now.getFullYear(), now.getMonth(), 1);
|
|
};
|
|
|
|
const getToday = () => {
|
|
return new Date();
|
|
};
|
|
|
|
const [fromDate, setFromDate] = useState(getFirstDayOfMonth());
|
|
const [toDate, setToDate] = useState(getToday());
|
|
|
|
|
|
const API_URL = apiConfig.api_dashboard;
|
|
const { GetData } = useCallApi();
|
|
|
|
const API_URL_BANK = apiConfig.service_wallet;
|
|
|
|
const [responseStatisticCard, setResponseStatisticCard] = useState<any>(null);
|
|
|
|
const fetchData = async () => {
|
|
const res = await GetData(`${API_URL}/card-statistic`, {});
|
|
setResponseStatisticCard(res);
|
|
};
|
|
|
|
const [bankaccount, setbankaccount] = useState<any>(null);
|
|
|
|
const fetchDataBankAccount = async () => {
|
|
const res = await GetData(`${API_URL_BANK}/dashboard/balance/account/${getAuth()?.user?.customer?.id}`, {});
|
|
setbankaccount(res);
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchData();
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
fetchDataBankAccount();
|
|
}, []);
|
|
|
|
const [responseGraphic, setresponseGraphic] = useState<any>(null);
|
|
|
|
useEffect(() => {
|
|
const fetchDataGraphic = async () => {
|
|
const res = await GetData(`${API_URL}/cashin-vs-cashout`, {
|
|
date_from: fromDate.toISOString(),
|
|
date_to: toDate.toISOString()
|
|
});
|
|
setresponseGraphic(res);
|
|
};
|
|
|
|
fetchDataGraphic();
|
|
}, [fromDate, toDate]);
|
|
|
|
let staticChartDataApiFetch = [];
|
|
|
|
// Make map for date => { cashin: 0, cashout: 0 }
|
|
const dataMap: Record<string, { cashin: number; cashout: number }> = {};
|
|
const current = moment(fromDate);
|
|
const end = moment(toDate);
|
|
|
|
// Inisialization dataMap with all dates
|
|
while (current.isSameOrBefore(end, 'day')) {
|
|
const dateStr = current.format('DD-MM-YYYY');
|
|
dataMap[dateStr] = { cashin: 0, cashout: 0 };
|
|
current.add(1, 'day');
|
|
}
|
|
|
|
// Add cashin
|
|
if (Array.isArray(responseGraphic?.data.total_cashin)) {
|
|
responseGraphic.data.total_cashin.forEach((item: any) => {
|
|
const date = moment(item.created_date).format('DD-MM-YYYY');
|
|
// Only update cashin if it's not already set (meaning no value was added before)
|
|
if (dataMap[date]) {
|
|
dataMap[date].cashin = Number(item.total_count ?? 0);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Add cashout
|
|
if (Array.isArray(responseGraphic?.data.total_cashout)) {
|
|
responseGraphic.data.total_cashout.forEach((item: any) => {
|
|
const date = moment(item.created_date).format('DD-MM-YYYY');
|
|
// Only update cashout if it's not already set (meaning no value was added before)
|
|
if (dataMap[date]) {
|
|
dataMap[date].cashout = Number(item.total_count ?? 0);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Change to array for chart
|
|
staticChartDataApiFetch = Object.entries(dataMap).map(([month, values]) => ({
|
|
month,
|
|
cashin: values.cashin,
|
|
cashout: values.cashout
|
|
}));
|
|
|
|
// Change to array for chart
|
|
staticChartDataApiFetch = Object.entries(dataMap).map(([month, values]) => ({
|
|
month,
|
|
cashin: values.cashin,
|
|
cashout: values.cashout
|
|
}));
|
|
|
|
const currentRole = getAuth()?.role_name;
|
|
const idCustomer = getAuth()?.user.customer?.id;
|
|
// console.log(currentRole);
|
|
|
|
// Menyusun tanggal awal dan akhir berdasarkan selectedYear
|
|
useEffect(() => {
|
|
if (selectYear.length > 0 && !selectedYear) {
|
|
let latestYear = Math.max(...selectYear.map((item) => parseInt(item, 10))).toString();
|
|
// console.log('latestYear :', latestYear);
|
|
setSelectedYear(latestYear);
|
|
setInitialYear(latestYear);
|
|
}
|
|
}, [selectYear, selectedYear]);
|
|
|
|
useEffect(() => {
|
|
if (selectedYear) {
|
|
setDateRange({
|
|
from: moment(`${selectedYear}-01-01`, 'YYYY-MM-DD').toDate(),
|
|
to: moment(`${selectedYear}-12-31`, 'YYYY-MM-DD').toDate()
|
|
});
|
|
}
|
|
}, [selectedYear]);
|
|
|
|
useEffect(() => {
|
|
if (initialYear) {
|
|
setDateRange({
|
|
from: moment(`${initialYear}-01-01`, 'YYYY-MM-DD').toDate(),
|
|
to: moment(`${initialYear}-12-31`, 'YYYY-MM-DD').toDate()
|
|
});
|
|
}
|
|
}, [initialYear]);
|
|
|
|
const handleYearChange = (year: string) => {
|
|
setSelectedYear(year);
|
|
};
|
|
|
|
const handleCountType = (value: CountType) => {
|
|
setCount(value);
|
|
};
|
|
|
|
const handleChartType = (value: ChartType) => {
|
|
setChartType(value);
|
|
};
|
|
|
|
const handleChartLegend = (value: ChartLegend) => {
|
|
setChartLegend(value);
|
|
};
|
|
|
|
const resetFilter = useCallback(() => {
|
|
const firstDay = getFirstDayOfMonth();
|
|
const today = getToday();
|
|
|
|
setFromDate(firstDay);
|
|
setToDate(today);
|
|
|
|
// reset filter yang lain kalau perlu
|
|
setDateRange({ from: firstDay, to: today });
|
|
setSelectedYear(initialYear);
|
|
setCount('sum');
|
|
setChartType('line');
|
|
setChartLegend('true');
|
|
}, [initialYear]);
|
|
|
|
const { cardData } = useFetchCardData(
|
|
moment(dateRange.from).format('YYYY-MM-DD'),
|
|
moment(dateRange.to).format('YYYY-MM-DD'),
|
|
count
|
|
);
|
|
|
|
const { chartData } = useFetchChartData(
|
|
moment(dateRange.from).format('YYYY-MM-DD'),
|
|
moment(dateRange.to).format('YYYY-MM-DD'),
|
|
count
|
|
);
|
|
|
|
const toolbar = (
|
|
<div className="flex gap-3 items-center w-1/2">
|
|
<div className="w-auto min-w-[120px]">
|
|
<Select value={chartType} onValueChange={handleChartType}>
|
|
<SelectTrigger size="sm">
|
|
<SelectValue placeholder="Select Chart Type" />
|
|
</SelectTrigger>
|
|
<SelectContent className="w-32">
|
|
<SelectItem value="line">Line</SelectItem>
|
|
<SelectItem value="bar">Bar</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div className="w-auto min-w-[120px]">
|
|
<Select value={chartLegend} onValueChange={handleChartLegend}>
|
|
<SelectTrigger size="sm">
|
|
<SelectValue placeholder="Select Legend Visibility" />
|
|
</SelectTrigger>
|
|
<SelectContent className="w-full">
|
|
<SelectItem value="true">Show Legend</SelectItem>
|
|
<SelectItem value="false">Hide Legend</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
const number: number = responseStatisticCard?.data.total_cash_in ?? 0;
|
|
const formattedNumber: number = parseFloat(number.toFixed(2));
|
|
|
|
const numbercashout: number = responseStatisticCard?.data.total_cash_out ?? 0;
|
|
const formattedNumbercashout: number = parseFloat(numbercashout.toFixed(2));
|
|
|
|
return (
|
|
<>
|
|
<Helmet>
|
|
<title>TPAY | Dashboard</title>
|
|
</Helmet>
|
|
<Container>
|
|
{/* Account Balance Cards */}
|
|
{/* {currentRole === 'Escrow' || currentRole === 'Master Agent' ? (
|
|
<div className="grid gap-5 lg:gap-7.5 mb-14 mt-7">
|
|
<BalanceCard id={idCustomer} />
|
|
</div>
|
|
) : null} */}
|
|
|
|
<div className="flex space-x-4 mt-5">
|
|
{bankaccount?.data && bankaccount?.data.length > 0
|
|
&& getAuth()?.statusbalance=='Y'
|
|
? (
|
|
bankaccount?.data.map((bankaccountdatas: { id_balance:string,amount: string, credit_limit: string, monthly_limit: string; wallet: string; }, index: number) => (
|
|
<BankSaldo
|
|
title={bankaccountdatas.wallet}
|
|
balance={bankaccountdatas.amount}
|
|
creditLimit={bankaccountdatas.credit_limit}
|
|
monthlyLimit={bankaccountdatas.monthly_limit}
|
|
idbalance={bankaccountdatas.id_balance}
|
|
/>
|
|
))
|
|
) : (
|
|
""
|
|
)}
|
|
|
|
</div>
|
|
|
|
{/* Cards */}
|
|
<div className="flex gap-6 overflow-x-auto pb-2 mt-5">
|
|
<Card
|
|
title="Registered Users"
|
|
total={responseStatisticCard?.data.total_registered ?? 0}
|
|
growth={parseFloat((responseStatisticCard?.data?.registered_last_week.percent ?? 0).toFixed(2)) ?? 0}
|
|
surplus={responseStatisticCard?.data.registered_last_week.surplus ?? false}
|
|
icon="test"
|
|
/>
|
|
<Card
|
|
title="Unregistered Users"
|
|
total={responseStatisticCard?.data.total_unregistered ?? 0}
|
|
growth={parseFloat((responseStatisticCard?.data?.unregistered_last_week.percent ?? 0).toFixed(2)) ?? 0}
|
|
surplus={responseStatisticCard?.data.unregistered_last_week.surplus ?? false}
|
|
icon="test"
|
|
|
|
/>
|
|
<Card
|
|
title="Total Cash-in"
|
|
total={formattedNumber.toFixed(2)} // now a number: 972.80
|
|
growth={parseFloat((responseStatisticCard?.data?.cash_in_last_week.percent ?? 0).toFixed(2)) ?? 0}
|
|
surplus={responseStatisticCard?.data.cash_in_last_week.surplus ?? false}
|
|
icon="test"
|
|
/>
|
|
<Card
|
|
title="Total Cash-out"
|
|
total={formattedNumbercashout.toFixed(2)} // now a number: 972.80
|
|
growth={parseFloat((responseStatisticCard?.data?.cash_out_last_week.percent ?? 0).toFixed(2)) ?? 0}
|
|
surplus={responseStatisticCard?.data.cash_out_last_week.surplus ?? false}
|
|
icon="test"
|
|
/>
|
|
<Card
|
|
title="Active Event"
|
|
total={responseStatisticCard?.data.total_event ?? 0}
|
|
growth={parseFloat((responseStatisticCard?.data?.event_last_week.percent ?? 0).toFixed(2)) ?? 0}
|
|
surplus={responseStatisticCard?.data.event_last_week.surplus ?? false}
|
|
icon="test"
|
|
/>
|
|
<Card
|
|
title="Total Billing"
|
|
total={responseStatisticCard?.data.total_billing ?? 0}
|
|
growth={parseFloat((responseStatisticCard?.data?.billing_last_week.percent ?? 0).toFixed(2)) ?? 0}
|
|
surplus={responseStatisticCard?.data.billing_last_week.surplus ?? false}
|
|
icon='test'
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex gap-3 items-center mb-6 mt-6">
|
|
<div className="flex gap-3 items-center w-full md:w-auto">
|
|
<label className="input input-sm w-[160px]">
|
|
From
|
|
<input
|
|
type="date"
|
|
name="from"
|
|
value={moment(fromDate).format('YYYY-MM-DD')}
|
|
onChange={(e) => {
|
|
if (e.target.value) {
|
|
setFromDate(new Date(e.target.value));
|
|
} else {
|
|
// Reset to default value (e.g. first day of current month)
|
|
setFromDate(getFirstDayOfMonth());
|
|
}
|
|
}}
|
|
/>
|
|
</label>
|
|
|
|
<label className="input input-sm w-[160px]">
|
|
To
|
|
<input
|
|
type="date"
|
|
name="to"
|
|
value={moment(toDate).format('YYYY-MM-DD')}
|
|
onChange={(e) => {
|
|
if (e.target.value) {
|
|
setToDate(new Date(e.target.value));
|
|
} else {
|
|
// Reset to default value (e.g. today)
|
|
setToDate(getToday());
|
|
}
|
|
}}
|
|
/>
|
|
</label>
|
|
</div>
|
|
|
|
<DefaultTooltip title="Reset Filter" placement="top">
|
|
<Button variant="outline" className="h-7.5" onClick={resetFilter}>
|
|
<KeenIcon icon="arrow-circle-left" />
|
|
</Button>
|
|
</DefaultTooltip>
|
|
</div>
|
|
|
|
|
|
{/* Chart */}
|
|
<div className="grid gap-5 lg:gap-7.5 mt-5">
|
|
<div className="grid lg:grid-cols-1 gap-y-5 lg:gap-5 items-stretch">
|
|
<div className="lg:col-span-1">
|
|
<Chart
|
|
title="Overview"
|
|
count={12}
|
|
toolbar={toolbar}
|
|
chartData={staticChartDataApiFetch}
|
|
chartType={chartType}
|
|
chartLegend={chartLegend}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex space-x-4 mt-5">
|
|
<TransactionValue startdate={fromDate.toISOString()} enddate={toDate.toISOString()} />
|
|
<TransactionPieChart startdate={fromDate.toISOString()} enddate={toDate.toISOString()} />
|
|
<MemberActivity startdate={fromDate.toISOString()} enddate={toDate.toISOString()} />
|
|
</div>
|
|
|
|
</Container>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default DashboardHomePage;
|