From d15c8b4f8a4112a4b711a3f7d1be85227e25ebb0 Mon Sep 17 00:00:00 2001 From: Ibinola Date: Tue, 17 Dec 2024 12:18:43 +0100 Subject: [PATCH] feat: add history page, implement usePosition hook to fetch table data --- frontend/src/App.jsx | 29 +- frontend/src/hooks/usePosition.js | 23 + .../position_history/PositionHistory.jsx | 99 ++++ .../position_history/position_history.css | 477 ++++++++++++++++++ frontend/src/utils/formatDate.js | 12 + 5 files changed, 625 insertions(+), 15 deletions(-) create mode 100644 frontend/src/hooks/usePosition.js create mode 100644 frontend/src/pages/spotnet/position_history/PositionHistory.jsx create mode 100644 frontend/src/pages/spotnet/position_history/position_history.css create mode 100644 frontend/src/utils/formatDate.js diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 43926cc1..255dcfad 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -19,6 +19,7 @@ import OverviewPage from 'pages/spotnet/overview/Overview'; import { ActionModal } from 'components/ui/ActionModal'; import Stake from 'pages/vault/stake/Stake'; import { notifyError } from 'utils/notification'; +import PositionHistory from 'pages/spotnet/position_history/PositionHistory'; function App() { const { walletId, setWalletId, removeWalletId } = useWalletStore(); @@ -27,7 +28,6 @@ function App() { const connectWalletMutation = useConnectWallet(setWalletId); - const handleConnectWallet = () => { connectWalletMutation.mutate(); }; @@ -49,14 +49,16 @@ function App() { useEffect(() => { if (window.Telegram?.WebApp?.initData) { - getTelegramUserWalletId(window.Telegram.WebApp.initDataUnsafe.user.id).then((linked_wallet_id) => { - setWalletId(linked_wallet_id); - window.Telegram.WebApp.ready(); - }).catch((error) => { - console.error('Error getting Telegram user wallet ID:', error); - notifyError('Error loading wallet'); - window.Telegram.WebApp.ready(); - }); + getTelegramUserWalletId(window.Telegram.WebApp.initDataUnsafe.user.id) + .then((linked_wallet_id) => { + setWalletId(linked_wallet_id); + window.Telegram.WebApp.ready(); + }) + .catch((error) => { + console.error('Error getting Telegram user wallet ID:', error); + notifyError('Error loading wallet'); + window.Telegram.WebApp.ready(); + }); } }, [window.Telegram?.WebApp?.initDataUnsafe]); @@ -76,14 +78,11 @@ function App() { />, document.body )} -
+
} /> - : } /> @@ -92,7 +91,7 @@ function App() { } /> } /> } /> - + } /> } />
diff --git a/frontend/src/hooks/usePosition.js b/frontend/src/hooks/usePosition.js new file mode 100644 index 00000000..17497c56 --- /dev/null +++ b/frontend/src/hooks/usePosition.js @@ -0,0 +1,23 @@ +import { useQuery } from '@tanstack/react-query'; +import { axiosInstance } from 'utils/axios'; + +const fetchPositionHistoryTable = async (walletId) => { + if (!walletId) { + throw new Error('Wallet ID is undefined'); + } + const response = await axiosInstance.get(`/api/user-positions/${walletId}`); + return response.data; +}; + +const usePositionHistoryTable = (walletId) => { + return useQuery({ + queryKey: ['positionHistory', walletId], + queryFn: () => fetchPositionHistoryTable(walletId), + enabled: !!walletId, + onError: (error) => { + console.error('Error during fetching position history:', error); + }, + }); +}; + +export { fetchPositionHistoryTable, usePositionHistoryTable }; diff --git a/frontend/src/pages/spotnet/position_history/PositionHistory.jsx b/frontend/src/pages/spotnet/position_history/PositionHistory.jsx new file mode 100644 index 00000000..9c73735b --- /dev/null +++ b/frontend/src/pages/spotnet/position_history/PositionHistory.jsx @@ -0,0 +1,99 @@ +import React, { useEffect } from 'react'; +import './position_history.css'; +import newIcon from '../../../assets/icons/borrow-balance-icon.png'; +import { ReactComponent as HealthIcon } from 'assets/icons/health.svg'; +import { usePositionHistoryTable } from 'hooks/usePosition'; +import Spinner from 'components/spinner/Spinner'; +import { formatDate } from 'utils/formatDate'; +import { useWalletStore } from 'stores/useWalletStore'; + +function PositionHistory() { + const { walletId } = useWalletStore(); + + const { data, isLoading } = usePositionHistoryTable(walletId); + + useEffect(() => { + console.log('Fetching data for walletId:', walletId); + }, [walletId]); + + return ( +
+
+

zkLend Position History

+
+
+
+
+ + Health Factor +
+
+ 1.47570678 +
+
+
+
+ Borrow Balance Icon{' '} + Borrow Balance +
+
+ $ + -55.832665 +
+
+
+
+ +
+
+

Position History

+
+ +
+ {isLoading ? ( +
+ +
+ ) : ( + + + + + + + + + + + + + + + + + {data?.map((data, index) => ( + + + + + + + + + + + + ))} + +
TokenAmountCreated AtStatusStart PriceMultiplierLiquidatedClosed At
{index + 1}.{data.token_symbol.toUpperCase()}{Number(data.amount).toFixed(2)}{formatDate(data.created_at)}{data.status.charAt(0).toUpperCase() + data.status.slice(1)}${data.start_price.toFixed(2)}{data.multiplier.toFixed(1)}{data.is_liquidated ? 'Yes' : 'No'} + {data.datetime_liquidation ? new Date(data.datetime_liquidation).toLocaleDateString() : 'N/A'} +
+ )} +
+
+
+
+ ); +} + +export default PositionHistory; diff --git a/frontend/src/pages/spotnet/position_history/position_history.css b/frontend/src/pages/spotnet/position_history/position_history.css new file mode 100644 index 00000000..3dff4b36 --- /dev/null +++ b/frontend/src/pages/spotnet/position_history/position_history.css @@ -0,0 +1,477 @@ +body { + font-family: + 'Open Sans', + -apple-system, + Roboto, + Helvetica, + sans-serif; +} + +.position-wrapper { + background: url('../../../../public/background.png') no-repeat; + background-size: cover; + background-position: center 39%; + min-height: 100vh; + padding: 1rem; + padding-top: 1rem; +} + +.position-container { + display: flex; + flex-direction: column; + justify-content: center; + gap: 32px; +} + +.position-content { + width: 642px; + gap: 24px; + border-radius: 20px; + padding: 1rem; + color: white; + text-align: center; + display: flex; + margin: 0 auto; + justify-content: center; + flex-direction: column; + align-items: center; +} + +.position-title { + font-size: 20px; + font-weight: 600; + color: #ffffff; + text-align: center; +} + +.position-top-cards { + display: flex; + gap: 0.8rem; + width: 642px; + height: 101px; +} + +.top-card-value { + font-size: 24px; +} + +.dashboard-error { + color: red; +} + +.position-card { + width: 309px; + height: 101px; + border: red 2px solid; + background: rgba(18, 7, 33, 1); + border: 1px solid rgba(54, 41, 78, 1); + border-radius: 10px; + padding-top: 4px; + padding-right: 24px; + padding-left: 24px; + text-align: center; +} + +.position-card-header { + margin-top: 10px; + display: flex; + justify-content: center; + align-items: center; + border: none; + color: white; +} + +.icon { + width: 32px; + height: 32px; + margin-right: 8px; + background: rgba(32, 19, 56, 1); + border-radius: 900px; + display: flex; + align-items: center; + justify-content: center; + padding: 7px; +} + +.position-card-value { + font-size: 24px; + font-weight: 600; + color: white; +} + +.position-content-table { + border-radius: 20px; + padding: 1rem; + display: flex; + flex-direction: column; + margin: 0 auto; + width: 70%; +} + +.position-table { + margin: 0 auto; + display: flex; + border: 1px solid #36294e; + border-radius: 9px; + justify-content: center; + align-items: center; + width: 100%; + overflow-x: auto; +} + +.position-table table { + width: 100%; + border-collapse: collapse; + font-size: 14px; + border-radius: 8px; + overflow: hidden; +} + +.position-table thead { + font-size: 14px; + color: var(--gray); + border-bottom: 1px solid #36294e; +} + +.position-table thead th { + padding-top: 40px; + padding-bottom: 10px; + max-width: 200px; + text-align: center; +} + +.position-table tbody tr:nth-child(even) { + background-color: rgba(18, 7, 33, 0.5); +} + +.position-table tbody td { + padding: 18px; + text-align: center; + font-weight: 600; + white-space: nowrap; + font-size: 14px; +} + +.index { + color: var(--gray); +} + +.position-table-title { + font-size: 16px; + font-weight: 600; + color: var(--primary); + margin-bottom: 8px; +} + +@media (max-width: 950px) { + .top-card-value { + font-size: 22px; + } +} + +@media (max-width: 768px) { + .dashboard-wrapper { + padding: 1rem; + } + + .dashboard-container { + width: 100%; + max-width: 100%; + margin: 0 auto; + box-sizing: border-box; + } + + .dashboard-content { + width: 100%; + max-width: 100%; + padding: 0.5rem; + gap: 16px; + } + + .top-cards-dashboard { + flex-direction: row; + height: auto; + margin: 0 auto; + align-items: center; + justify-content: center; + width: 500px; + } + + .card { + height: 90px; + padding: 8px 2px 14px 2px; + border-radius: 14px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + } + + .card-header { + padding: 0; + } + + .card-header .label { + font-size: 14px; + font-weight: 400; + } + + .icon { + width: 24px; + height: 24px; + margin-right: 4px; + padding: 3px; + } + + .tab-title { + font-size: 14px; + white-space: nowrap; + } + + .dashboard-btn { + border-radius: 16px; + display: flex; + justify-content: center; + align-items: center; + width: 500px; + height: 55px; + } + + .tab { + padding: 0.5rem 0.5rem; + } + + .dashboard-info-card { + height: auto; + min-height: 280px; + border-radius: 16px !important; + padding: 16px 24px !important; + margin: 20px auto; + width: 500px; + } + + .tab-content { + width: 100%; + height: auto; + padding: 16px; + } + + .balance-info { + width: 100%; + height: auto; + } + + .dashboard-title { + font-size: 24px; + } + + .tab-title { + font-size: 14px; + font-weight: 600; + } + + .currency-symbol { + font-size: 18px; + } + + .currency-name { + font-size: 18px; + } + + .balance-label, + .balance-value, + .current-sum-red { + font-size: 14px; + } + + .top-card-value { + font-size: 18px; + } + + .tabs { + padding: 0 8px; + gap: 2px; + } + + .tab { + padding: 8px 12px; + font-size: 14px; + white-space: nowrap; + } + + .tab-divider { + width: 2px; + height: 16px; + margin: 0 4px; + } + + .tab-icon { + width: 20px; + height: 20px; + margin-right: 4px; + } + + .tab-indicator-container { + bottom: -12px; + width: calc(100% - 16px); + } + + .lucide-up-icon { + width: 22px; + height: 22px; + margin-left: 6px; + } + + .lucide-down-icon { + width: 20px; + height: 20px; + margin-left: 6px; + } + + .current-sum-green { + font-size: 14px; + } +} + +@media (max-width: 550px) { + .dashboard-info-card { + height: auto; + min-height: 280px; + padding: 14px 22px !important; + margin: 15px auto; + width: 450px; + } + + .dashboard-btn { + width: 450px; + } + + .top-cards-dashboard { + width: 450px; + } +} + +@media (max-width: 480px) { + .dashboard-info-card { + height: auto; + min-height: 280px; + padding: 14px 22px !important; + margin: 15px auto; + width: 380px; + } + + .top-card-value { + font-size: 16px; + } + + .dashboard-btn { + width: 380px; + font-size: 14px; + height: 50px; + } + + .top-cards-dashboard { + width: 380px; + gap: 8px; + } + .dashboard-wrapper { + padding: 0.75rem; + } + + .dashboard-content { + gap: 20px; + } + + .tab-title { + font-size: 14px; + } + + .tab-content { + width: fit-content; + font-size: 14px; + } + + .currency-symbol { + font-size: 16px; + } + + .currency-name { + font-size: 16px; + } + + .icon { + width: 20px; + height: 20px; + } + + .balance-info { + width: fit-content; + } + + .card { + flex: 1; + padding: 16px 24px; + } + + .card-header { + gap: 4px; + } + + .tab { + padding: 6px 8px; + font-size: 12px; + } +} + +@media (max-width: 400px) { + .dashboard-info-card { + height: auto; + min-height: 280px; + padding: 14px 22px !important; + margin: 15px auto; + width: 300px; + } + + .top-cards-dashboard { + width: 300px; + } + + .dashboard-btn { + width: 300px; + height: 45px; + } + + .top-card-value { + font-size: 14px; + } + + .tab-title { + font-size: 12px; + } +} + +@media (max-width: 320px) { + .dashboard-info-card { + height: auto; + padding: 10px 10px !important; + margin: 15px auto; + width: 280px; + } + + .top-cards-dashboard { + width: 280px; + } + + .dashboard-btn { + width: 300px; + height: 45px; + } + + .card { + padding: 10px 16px; + } + + .tabs { + padding: 0; + } +} diff --git a/frontend/src/utils/formatDate.js b/frontend/src/utils/formatDate.js new file mode 100644 index 00000000..f6c89f76 --- /dev/null +++ b/frontend/src/utils/formatDate.js @@ -0,0 +1,12 @@ +export function formatDate(timestamp) { + const date = new Date(timestamp); + const day = String(date.getDate()).padStart(2, '0'); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const year = String(date.getFullYear()).slice(2); + const hours = String(date.getHours() % 12 || 12).padStart(2, '0'); + const minutes = String(date.getMinutes()).padStart(2, '0'); + const period = date.getHours() >= 12 ? 'PM' : 'AM'; + + return `${day}/${month}/${year} - ${hours}:${minutes}${period}` +} +