diff options
author | Navan Chauhan <navanchauhan@gmail.com> | 2025-04-27 22:44:20 -0600 |
---|---|---|
committer | Navan Chauhan <navanchauhan@gmail.com> | 2025-04-27 22:44:20 -0600 |
commit | 37787895d56888ab44362252f21fb05c05e97250 (patch) | |
tree | 5ff7eaababa6fffd5dc950920f521c5f242010af /client/src/components/BidsPage.jsx | |
parent | f32142947b853076889801913d47b8c2c0f4f456 (diff) |
reorganize
Diffstat (limited to 'client/src/components/BidsPage.jsx')
-rw-r--r-- | client/src/components/BidsPage.jsx | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/client/src/components/BidsPage.jsx b/client/src/components/BidsPage.jsx new file mode 100644 index 0000000..e0c4128 --- /dev/null +++ b/client/src/components/BidsPage.jsx @@ -0,0 +1,182 @@ +import React, { useEffect, useState } from 'react'; +import { Table, Typography, Spin, Message, Card, Empty, Badge } from '@arco-design/web-react'; +import { IconArrowRise, IconArrowFall } from '@arco-design/web-react/icon'; +import '@arco-design/web-react/dist/css/arco.css'; +import API_BASE_URL from './config'; + +const columns = (bids) => [ + { + title: 'Timestamp', + dataIndex: 'timestamp', + sorter: (a, b) => new Date(a.timestamp) - new Date(b.timestamp), + render: (val) => new Date(val).toLocaleString(), + width: 180, + }, + { + title: 'Quantity (MW)', + dataIndex: 'quantity', + sorter: (a, b) => a.quantity - b.quantity, + render: (val) => val.toFixed(2), + width: 180, + }, + { + title: 'Price ($/MWh)', + dataIndex: 'price', + sorter: (a, b) => a.price - b.price, + render: (val) => `$${val.toFixed(2)}`, + width: 180, + }, + { + title: 'Status', + dataIndex: 'status', + sorter: (a, b) => a.status.localeCompare(b.status), + filters: [ + { text: 'Submitted', value: 'Submitted' }, + { text: 'Success', value: 'Success' }, + { text: 'Fail', value: 'Fail' }, + ], + onFilter: (value, record) => record.status === value, + render: (val) => ( + <Badge status={val === 'Success' ? 'success' : val === 'Fail' ? 'error' : 'processing'} text={val} /> + ), + width: 180, + }, + { + title: 'PnL', + dataIndex: 'pnl', + sorter: (a, b) => (a.pnl || 0) - (b.pnl || 0), + render: (val) => { + if (val === null) return 'N/A'; + const isProfit = val >= 0; + const color = isProfit ? 'green' : 'red'; + return ( + <Typography.Text style={{ color, display: 'flex', alignItems: 'center', gap: 4 }}> + {isProfit ? <IconArrowRise /> : <IconArrowFall />} + {val.toFixed(2)} + </Typography.Text> + ); + }, + width: 180, + }, + { + title: 'Market', + dataIndex: 'market', + sorter: (a, b) => a.market.localeCompare(b.market), + filters: [ + { text: 'ISONE', value: 'ISONE' }, + { text: 'NYISO', value: 'NYISO' }, + { text: 'MISO', value: 'MISO' }, + ], + onFilter: (value, record) => record.market === value, + render: (val) => val, + width: 180, + }, +]; + +function BidsPage() { + const [bids, setBids] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + fetch(`${API_BASE_URL}/bids/`) + .then((res) => { + if (!res.ok) throw new Error('Failed to fetch bids'); + return res.json(); + }) + .then((json) => setBids(json)) + .catch((err) => { + console.error(err); + Message.error('Failed to load bids.'); + }) + .finally(() => setLoading(false)); + }, []); + + return ( + <div style={{ padding: 20, backgroundColor: 'var(--color-fill-2)', minHeight: 'calc(100vh - 150px)', animation: 'fadeSlideIn 0.6s ease' }}> + <Typography.Title heading={3} style={{ textAlign: 'center', marginBottom: 20 }}> + Your Submitted Bids + </Typography.Title> + + <Card + style={{ + marginTop: 16, + borderRadius: 16, + boxShadow: '0 8px 24px rgba(0,0,0,0.08)', + background: 'linear-gradient(135deg, rgba(255,255,255,0.95) 0%, rgba(245,245,245,0.95) 100%)', + backdropFilter: 'blur(8px)', + overflow: 'hidden', + padding: 20 + }} + > + {loading ? ( + <div style={{ textAlign: 'center', padding: 80 }}> + <Spin /> + </div> + ) : bids.length === 0 ? ( + <div style={{ textAlign: 'center', padding: 80 }}> + <Empty description="No bids found yet." /> + </div> + ) : ( + <div className="responsive-table-wrapper"> + <Table + columns={columns(bids)} + data={bids} + rowKey="id" + pagination={{ + pageSize: 50, + sizeCanChange: true, + showTotal: true, + }} + scroll={{ x: true }} + border + style={{ transition: 'opacity 0.5s ease-in-out' }} + rowClassName={() => 'table-row-hover'} + /> + </div> + )} + </Card> + + <style>{` + @keyframes fadeSlideIn { + 0% { + opacity: 0; + transform: translateY(20px); + } + 100% { + opacity: 1; + transform: translateY(0); + } + } + .table-row-hover:hover { + background-color: var(--color-fill-3); + transition: background-color 0.3s ease; + } + + responsive-table-wrapper { + width: 100%; + overflow-x: auto; + } + + /* Prevent wrapping in header and cells */ + .responsive-table-wrapper table th, + .responsive-table-wrapper table td { + white-space: nowrap; + } + + /* Optional: smaller fonts on mobile */ + @media (max-width: 768px) { + .responsive-table-wrapper table { + font-size: 12px; + } + .responsive-table-wrapper th, + .responsive-table-wrapper td { + padding: 8px; + } + } + + `}</style> + </div> + ); +} + +export default BidsPage; |