From 7db3e72476964af99590a83d3f91e3356f2094ea Mon Sep 17 00:00:00 2001 From: wayanrivan Date: Wed, 23 Apr 2025 17:00:02 +0700 Subject: [PATCH] update dashboard view --- src/config/api.config.ts | 2 + .../dashboards/home/DashboardHomePage.tsx | 226 ++++++++++++++---- src/pages/dashboards/home/blocks/Card.tsx | 43 ++-- .../home/blocks/TransactionValue.tsx | 103 ++++++++ 4 files changed, 297 insertions(+), 77 deletions(-) create mode 100644 src/pages/dashboards/home/blocks/TransactionValue.tsx diff --git a/src/config/api.config.ts b/src/config/api.config.ts index bfff0a4..47c5471 100644 --- a/src/config/api.config.ts +++ b/src/config/api.config.ts @@ -9,6 +9,7 @@ interface apiConfigProps { service_disbursement: string; service_notification: string; service_feedback: string; + api_dashboard:string } const API_URL = import.meta.env.VITE_APP_API_URL; @@ -24,6 +25,7 @@ const apiConfig: apiConfigProps = { transaction: `${API_URL}/x`, service_disbursement: `${API_URL}/s`, service_notification: `${API_URL}/n`, + api_dashboard: `${API_URL}/r`, nationality: `https://tpay.shiblysolution.id/cms/api/mobile/list-country/` }; diff --git a/src/pages/dashboards/home/DashboardHomePage.tsx b/src/pages/dashboards/home/DashboardHomePage.tsx index 5c90b52..c2c53b2 100644 --- a/src/pages/dashboards/home/DashboardHomePage.tsx +++ b/src/pages/dashboards/home/DashboardHomePage.tsx @@ -20,6 +20,7 @@ import BalanceCard from './blocks/BalanceCard'; import { getAuth } from '@/auth'; import { useCallApi } from '@/hooks'; import { apiConfig } from '@/config/api.config'; +import TransactionValue from './blocks/TransactionValue'; // sum -> nominal, count-> total type CountType = 'sum' | 'count'; @@ -37,6 +38,98 @@ const DashboardHomePage = () => { 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 [responseStatisticCard, setResponseStatisticCard] = useState(null); + + const fetchData = async () => { + const res = await GetData(`${API_URL}/card-statistic`, {}); + setResponseStatisticCard(res); + }; + + useEffect(() => { + fetchData(); + }, []); + + const [responseGraphic, setresponseGraphic] = useState(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 = {}; + 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); @@ -85,21 +178,15 @@ const DashboardHomePage = () => { setChartLegend(value); }; - // const handleFilter = useCallback( - // (date: DateRange | undefined) => { - // setDateRange({ - // from: date?.from ?? moment(`${selectedYear}-01-01`, 'YYYY-MM-DD').toDate(), - // to: date?.to ?? moment(`${selectedYear}-12-31`, 'YYYY-MM-DD').toDate() - // }); - // }, - // [selectedYear] - // ); - const resetFilter = useCallback(() => { - setDateRange({ - from: moment(`${initialYear}-01-01`, 'YYYY-MM-DD').toDate(), - to: moment(`${initialYear}-12-31`, 'YYYY-MM-DD').toDate() - }); + 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'); @@ -158,24 +245,76 @@ const DashboardHomePage = () => { ) : null} -
- -
- + {/* Cards */} +
+ + + + + + +
+ +
+
+ + +
- {/* - - */} +
- {/* Cards */} -
-
- {cardData.map((data: any) => ( -
- -
- ))} -
-
{/* Chart */} -
+
data.data)} + chartData={staticChartDataApiFetch} chartType={chartType} chartLegend={chartLegend} />
+ + + ); diff --git a/src/pages/dashboards/home/blocks/Card.tsx b/src/pages/dashboards/home/blocks/Card.tsx index 5d1d08f..d2f065a 100644 --- a/src/pages/dashboards/home/blocks/Card.tsx +++ b/src/pages/dashboards/home/blocks/Card.tsx @@ -4,46 +4,31 @@ import { toAbsoluteUrl } from '@/utils/Assets'; interface CardDataProduct { title: string; - total: string; - type: Array<{ label: string; value: string; total: string }>; - count: 'sum' | 'count'; + total: number; + growth: number; + surplus: boolean; + icon: string; } -const Card = ({ title, total, type, count }: CardDataProduct) => { +const Card = ({ title, total, growth, surplus,icon }: CardDataProduct) => { const { isRTL } = useLanguage(); return ( -
+
-
+
{title} - - {count === 'sum' ? fCurrency(total) : total} + {total} + + {surplus ? '▲' : '▼'} {growth} % From Last Week
-
- Chart Icon -
- {/*
-
- {type.map((data: any, index: number) => ( -
-
-
{data.label}
-
- {count === 'sum' ? fCurrency(data.total) : data.total}{' '} -
-
- {index !== type.length - 1 &&
} -
- ))} -
*/}
); diff --git a/src/pages/dashboards/home/blocks/TransactionValue.tsx b/src/pages/dashboards/home/blocks/TransactionValue.tsx new file mode 100644 index 0000000..63c43d5 --- /dev/null +++ b/src/pages/dashboards/home/blocks/TransactionValue.tsx @@ -0,0 +1,103 @@ +import React, { useEffect, useState } from 'react'; +import { apiConfig } from '@/config/api.config'; +import { useCallApi } from '@/hooks'; + +const API_URL = apiConfig.api_dashboard; + +interface Props { + startdate: string; + enddate: string; +} + +const TransactionValue = ({ startdate, enddate }: Props) => { + const { GetData } = useCallApi(); + const [responseTransactionValue, setResponseTransactionValue] = useState(null); + + useEffect(() => { + const fetchDataTransactionValue = async () => { + try { + const res = await GetData(`${API_URL}/transaction-value`, { + date_from: startdate, + date_to: enddate, + }); + setResponseTransactionValue(res); + } catch (error) { + console.error('Error fetching transaction value:', error); + } + }; + + fetchDataTransactionValue(); + }, [startdate, enddate, GetData]); + + let transactionData: any[] = []; + if (responseTransactionValue?.data.length > 0) { + for (let i = 0; i < responseTransactionValue?.data.length; i++) { + let type = ""; + let unit = ""; + + if(responseTransactionValue?.data[i].total_amount>=1000 && responseTransactionValue?.data[i].total_amount<1000000){ + unit = "K" + }else if(responseTransactionValue?.data[i].total_amount>=1000000 && responseTransactionValue?.data[i].total_amount<1000000000){ + unit = "M" + } + else if(responseTransactionValue?.data[i].total_amount>=1000000000){ + unit = "B" + } + + switch (responseTransactionValue?.data[i].kind) { + case "P": + type = "Purchase"; + break; + case "T": + type = "Transfer"; + break; + case "U": + type = "Top Up"; + break; + case "W": + type = "Withdraw"; + break; + case "R": + type = "Return"; + break; + default: + type = "Unknown"; + break; + } + + transactionData.push({ + label: type, + value: responseTransactionValue?.data[i].total_amount, + unit: unit + }); + } + } + + const maxValue = transactionData.length > 0 ? Math.max(...transactionData.map(item => item.value)) : 1; + + return ( +
+
+

Transaction Value

+
+
+ {transactionData.map((item, index) => ( +
+ {item.label} +
+
+
+
+
+ {item.value}{item.unit} +
+ ))} +
+
+ ); +}; + +export default TransactionValue;