From 552d1de84c14d920f17e305c773c0abb46fc00eb Mon Sep 17 00:00:00 2001 From: wayanrivan Date: Thu, 24 Apr 2025 11:12:00 +0700 Subject: [PATCH] dashboard view done --- .../dashboards/home/DashboardHomePage.tsx | 11 +- .../dashboards/home/blocks/MemberActivity.tsx | 114 ++++++++++++++++++ .../home/blocks/TransactionPieChart.tsx | 89 ++++++++++++++ .../home/blocks/TransactionValue.tsx | 65 +++++----- .../blocks/DetailTransaction.tsx | 5 +- 5 files changed, 250 insertions(+), 34 deletions(-) create mode 100644 src/pages/dashboards/home/blocks/MemberActivity.tsx create mode 100644 src/pages/dashboards/home/blocks/TransactionPieChart.tsx diff --git a/src/pages/dashboards/home/DashboardHomePage.tsx b/src/pages/dashboards/home/DashboardHomePage.tsx index c2c53b2..c591184 100644 --- a/src/pages/dashboards/home/DashboardHomePage.tsx +++ b/src/pages/dashboards/home/DashboardHomePage.tsx @@ -21,6 +21,8 @@ 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'; // sum -> nominal, count-> total type CountType = 'sum' | 'count'; @@ -339,10 +341,11 @@ const DashboardHomePage = () => { - +
+ + + +
diff --git a/src/pages/dashboards/home/blocks/MemberActivity.tsx b/src/pages/dashboards/home/blocks/MemberActivity.tsx new file mode 100644 index 0000000..bdfd43e --- /dev/null +++ b/src/pages/dashboards/home/blocks/MemberActivity.tsx @@ -0,0 +1,114 @@ +import React, { useEffect, useState } from 'react'; +import { UsersIcon, UserCheckIcon } from "lucide-react"; +import { apiConfig } from '@/config/api.config'; +import { useCallApi } from '@/hooks'; + +const API_URL = apiConfig.api_dashboard; + +interface Props { + startdate: string; + enddate: string; +} + +const MemberActivity = ({ startdate, enddate }: Props) => { + const { GetData } = useCallApi(); + const [responseTransactionValue, setResponseTransactionValue] = useState(null); + + useEffect(() => { + const fetchDataTransactionValue = async () => { + try { + const res = await GetData(`${API_URL}/active-user`, { + date_from: startdate, + date_to: enddate, + }); + setResponseTransactionValue(res); + } catch (error) { + console.error('Error fetching transaction value:', error); + } + }; + + fetchDataTransactionValue(); + }, [startdate, enddate, GetData]); + + const percentage = responseTransactionValue?.data?.total_customer_active_percentage ?? 0; + const totalCustomer = responseTransactionValue?.data?.total_customer ?? 0; + const activeCustomer = responseTransactionValue?.data?.total_customer_active ?? 0; + + // Hitung sudut pointer + const angle = 180 - (percentage / 100) * 180; // 180° (kiri bawah) ke 0° (kanan bawah) + const radians = (angle * Math.PI) / 180; // Mengubah derajat ke radian + const radius = 40; // Radius dari setengah lingkaran + const center = 50; // Titik pusat lingkaran + const pointerLength = 25; // Panjang pointer + + const x = center + pointerLength * Math.cos(radians); + const y = center + pointerLength * Math.sin(radians); + + // Hitung titik akhir untuk arc aktif + const arcAngle = (Math.PI * percentage) / 100; + const arcX = 50 + radius * Math.cos(Math.PI - arcAngle); + const arcY = 50 - radius * Math.sin(arcAngle); + + return ( +
+
+

Member Activity

+
+ +
+ {/* Sidebar */} +
    +
  • + + Total Customer: {totalCustomer} +
  • +
  • + + Active Customer: {activeCustomer} +
  • +
+ + {/* Gauge Chart */} +
+
+

Active User

+
+ + {/* Background arc */} + + {/* Active arc */} + 50 ? 1 : 0} 1 ${arcX} ${arcY}`} + fill="none" + stroke="#34d399" + strokeWidth="10" + /> + {/* Pointer */} + + +
+ {percentage}% + 100% +
+
+
+
+
+
+ ); +}; + +export default MemberActivity; diff --git a/src/pages/dashboards/home/blocks/TransactionPieChart.tsx b/src/pages/dashboards/home/blocks/TransactionPieChart.tsx new file mode 100644 index 0000000..d7b768d --- /dev/null +++ b/src/pages/dashboards/home/blocks/TransactionPieChart.tsx @@ -0,0 +1,89 @@ +import React, { useEffect, useState } from 'react'; +import { + PieChart, + Pie, + Cell, + ResponsiveContainer, +} from 'recharts'; +import { apiConfig } from '@/config/api.config'; +import { useCallApi } from '@/hooks'; + +const API_URL = apiConfig.api_dashboard; + +interface Props { + startdate: string; + enddate: string; +} + +const TransactionPieChart = ({ startdate, enddate }: Props) => { + + const { GetData } = useCallApi(); + const [responseTransactionValue, setResponseTransactionValue] = useState(null); + + useEffect(() => { + const fetchDataTransactionValue = async () => { + try { + const res = await GetData(`${API_URL}/transaction-chart`, { + date_from: startdate, + date_to: enddate, + }); + setResponseTransactionValue(res); + } catch (error) { + console.error('Error fetching transaction value:', error); + } + }; + + fetchDataTransactionValue(); + }, [startdate, enddate, GetData]); + + const data = [ + { name: 'Transfer', value: parseFloat((responseTransactionValue?.data?.T ?? 0).toFixed(2)), color: '#3490dc' }, + { name: 'Return', value: parseFloat((responseTransactionValue?.data?.R ?? 0).toFixed(2)), color: '#a0aec0' }, + { name: 'Topup', value: parseFloat((responseTransactionValue?.data?.U ?? 0).toFixed(2)), color: '#9f7aea' }, + { name: 'Purchase', value: parseFloat((responseTransactionValue?.data?.P ?? 0).toFixed(2)), color: '#baf7c5' }, + { name: 'Withdraw', value: parseFloat((responseTransactionValue?.data?.W ?? 0).toFixed(2)), color: '#f56565' }, + ]; + + return ( +
+
+

Transaction Chart

+
+ +
+
+ + + + {data.map((entry, index) => ( + + ))} + + + +
+ +
+
Active Transaction
+ {data.map((entry, index) => ( +
+
+
+ {entry.name} +
+ {entry.value}% +
+ ))} +
+
+
+ ); +}; + +export default TransactionPieChart; diff --git a/src/pages/dashboards/home/blocks/TransactionValue.tsx b/src/pages/dashboards/home/blocks/TransactionValue.tsx index 63c43d5..bb6c53d 100644 --- a/src/pages/dashboards/home/blocks/TransactionValue.tsx +++ b/src/pages/dashboards/home/blocks/TransactionValue.tsx @@ -10,7 +10,7 @@ interface Props { } const TransactionValue = ({ startdate, enddate }: Props) => { - const { GetData } = useCallApi(); + const { GetData } = useCallApi(); const [responseTransactionValue, setResponseTransactionValue] = useState(null); useEffect(() => { @@ -30,21 +30,22 @@ const TransactionValue = ({ startdate, enddate }: Props) => { }, [startdate, enddate, GetData]); let transactionData: any[] = []; - if (responseTransactionValue?.data.length > 0) { - for (let i = 0; i < responseTransactionValue?.data.length; i++) { - let type = ""; + 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" + const amount = responseTransactionValue.data[i].total_amount; + + if (amount >= 1000 && amount < 1000000) { + unit = "K"; + } else if (amount >= 1000000 && amount < 1000000000) { + unit = "M"; + } else if (amount >= 1000000000) { + unit = "B"; } - switch (responseTransactionValue?.data[i].kind) { + switch (responseTransactionValue.data[i].kind) { case "P": type = "Purchase"; break; @@ -61,13 +62,13 @@ const TransactionValue = ({ startdate, enddate }: Props) => { type = "Return"; break; default: - type = "Unknown"; + type = "Unknown"; break; } transactionData.push({ label: type, - value: responseTransactionValue?.data[i].total_amount, + value: amount, unit: unit }); } @@ -76,26 +77,32 @@ const TransactionValue = ({ startdate, enddate }: Props) => { const maxValue = transactionData.length > 0 ? Math.max(...transactionData.map(item => item.value)) : 1; return ( -
+

Transaction Value

-
- {transactionData.map((item, index) => ( -
- {item.label} -
-
-
+ {transactionData.length > 0 ? ( +
+ {transactionData.map((item, index) => ( +
+ {item.label} +
+
+
+
+ {new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(item.value)}{item.unit}
- {item.value}{item.unit} -
- ))} -
+ ))} +
+ ) : ( +
+ No Data Available +
+ )}
); }; diff --git a/src/pages/transaction/history-transaction/blocks/DetailTransaction.tsx b/src/pages/transaction/history-transaction/blocks/DetailTransaction.tsx index 199a8bc..39f5b07 100644 --- a/src/pages/transaction/history-transaction/blocks/DetailTransaction.tsx +++ b/src/pages/transaction/history-transaction/blocks/DetailTransaction.tsx @@ -45,6 +45,8 @@ const DetailTransaction = () => { id: selectedTransactionId }); setTransactionDetails(response?.data); + console.log(response?.data); + console.log(selectedTransactionId); } catch (error) { console.error('Error fetching transaction', error); } @@ -72,6 +74,7 @@ const DetailTransaction = () => { Transaction Details + {/* Tabs Navigation */}
@@ -207,7 +210,7 @@ const DetailTransaction = () => {

Description

-

{transactionDetails?.description}

+

{transactionDetails?.description || '-'}

Name