diff --git a/components/LaunchPad/Sale/CreateTokenSale.tsx b/components/LaunchPad/Sale/CreateTokenSale.tsx index 1feefc053..97631819c 100644 --- a/components/LaunchPad/Sale/CreateTokenSale.tsx +++ b/components/LaunchPad/Sale/CreateTokenSale.tsx @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -import React, { useContext, useState } from 'react' +import React, { useContext, useEffect, useState } from 'react' import { CreatorAddress } from '../CreatorAddress' import { Note } from '../note' @@ -28,6 +28,27 @@ import Button from '@mui/material/Button' import OutlinedInput from '@/components/Input/OutlinedInput' import { styles } from '../styles.css' import { WalletReducerContext } from '@/hooks/WalletsReducerProvider' +import { TokenSearchInput } from '../TokenSearchInput' + +const columns = [ + { + id: 'symbol', + label: 'Symbol' + }, + { + id: 'assetName', + label: 'Name' + }, + { + id: 'assetId', + label: 'AssetId' + }, + { + id: 'availableBalance', + label: 'Available Balance', + format: (value) => value.toLocaleString('en-US') + } +] const initialValues = { assetId: '', @@ -39,12 +60,71 @@ export const CreateTokenSale = () => { const { activeWallet } = useContext(WalletReducerContext) const [loading, setLoading] = useState(false) const [formData, setFormData] = useState(initialValues) + const [assetList, setAssetList] = useState([]) + const { assetId, quantity, perUnit } = formData const onChange = (e) => { setFormData({ ...formData, [e.target.name]: e.target.value }) } + const fetchUserAssets = () => { + setAssetList([ + { + assetId: 7789624, + symbol: 'BUSD', + assetName: 'BUSD Token', + availableBalance: 300000 + }, + { + assetId: 6789654, + symbol: 'UCDC', + assetName: 'UCDC Token', + availableBalance: 200000 + }, + { + assetId: 3789654, + symbol: 'goBTC', + assetName: 'goBTC', + availableBalance: 240000 + }, + { + assetId: 6789654, + symbol: 'UCDC', + assetName: 'UCDC', + availableBalance: 100000 + }, + { + assetId: 6789654, + symbol: 'UCDC', + assetName: 'UCDC', + availableBalance: 200000 + }, + { + assetId: 6789654, + symbol: 'UCDC', + assetName: 'UCDC', + availableBalance: 200000 + }, + { + assetId: 6789654, + symbol: 'UCDC', + assetName: 'UCDC', + availableBalance: 200000 + }, + { + assetId: 6789654, + symbol: 'UCDC', + assetName: 'UCDC', + availableBalance: 200000 + } + ]) + } + + useEffect(() => { + fetchUserAssets() + }, []) + const onSubmit = async (e) => { e.preventDefault() @@ -80,16 +160,14 @@ export const CreateTokenSale = () => { Choose Asset: - - onChange(e)} - sx={styles.input} - /> - + diff --git a/components/LaunchPad/Sale/ManageTokenSale.tsx b/components/LaunchPad/Sale/ManageTokenSale.tsx index 5fc001de6..ae9cdfdc8 100644 --- a/components/LaunchPad/Sale/ManageTokenSale.tsx +++ b/components/LaunchPad/Sale/ManageTokenSale.tsx @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -import React, { useContext, useState } from 'react' +import React, { useContext, useEffect, useState } from 'react' import { CreatorAddress } from '../CreatorAddress' import { Icon } from '@iconify/react' @@ -30,6 +30,7 @@ import { styles } from '../styles.css' import { CopyIcon } from '../copyIcon' import { LinearProgressWithLabel } from '../progressBar' import { WalletReducerContext } from '@/hooks/WalletsReducerProvider' +import { TokenSearchInput } from '../TokenSearchInput' const initialValues = { tokenName: '', @@ -39,9 +40,30 @@ const initialValues = { showTotalForSale: false } +const columns = [ + { + id: 'symbol', + label: 'Symbol' + }, + { + id: 'assetName', + label: 'Name' + }, + { + id: 'assetId', + label: 'AssetId' + }, + { + id: 'availableBalance', + label: 'Available Balance', + format: (value) => value.toLocaleString('en-US') + } +] + export const ManageTokenSale = () => { const { activeWallet } = useContext(WalletReducerContext) const [formData, setFormData] = useState(initialValues) + const [assetList, setAssetList] = useState([]) const { tokenName, pricePerToken, showPricePerToken, totalForSale, showTotalForSale } = formData const onChange = (e) => { @@ -61,6 +83,63 @@ export const ManageTokenSale = () => { e.preventDefault() } + const fetchUserAssets = () => { + setAssetList([ + { + assetId: 7789624, + symbol: 'BUSD', + assetName: 'BUSD Token', + availableBalance: 300000 + }, + { + assetId: 6789654, + symbol: 'UCDC', + assetName: 'UCDC Token', + availableBalance: 200000 + }, + { + assetId: 3789654, + symbol: 'goBTC', + assetName: 'goBTC', + availableBalance: 240000 + }, + { + assetId: 6789654, + symbol: 'UCDC', + assetName: 'UCDC', + availableBalance: 100000 + }, + { + assetId: 6789654, + symbol: 'UCDC', + assetName: 'UCDC', + availableBalance: 200000 + }, + { + assetId: 6789654, + symbol: 'UCDC', + assetName: 'UCDC', + availableBalance: 200000 + }, + { + assetId: 6789654, + symbol: 'UCDC', + assetName: 'UCDC', + availableBalance: 200000 + }, + { + assetId: 6789654, + symbol: 'UCDC', + assetName: 'UCDC', + availableBalance: 200000 + } + ]) + } + + useEffect(() => { + fetchUserAssets() + }, []) + return ( <> @@ -89,16 +168,15 @@ export const ManageTokenSale = () => { Choose Sale to Manage: - - onChange(e)} - sx={styles.input} - /> - + + Search with Asset Name or Asset ID - Only ASAs created by the currently connected wallet will show as options. diff --git a/components/LaunchPad/Sale/SearchTable.tsx b/components/LaunchPad/Sale/SearchTable.tsx new file mode 100644 index 000000000..fb9f2dc45 --- /dev/null +++ b/components/LaunchPad/Sale/SearchTable.tsx @@ -0,0 +1,176 @@ +import React, { MutableRefObject, useEffect, useMemo, useState } from 'react' +import Icon from '@/components/Icon' + +//MUI components +import Paper from '@mui/material/Paper' +import Table from '@mui/material/Table' +import TableBody from '@mui/material/TableBody' +import TableCell, { tableCellClasses } from '@mui/material/TableCell' +import TableContainer from '@mui/material/TableContainer' +import TableHead from '@mui/material/TableHead' +import TableRow from '@mui/material/TableRow' +import { styled } from '@mui/material/styles' +import Box from '@mui/material/Box' +import TableSortLabel from '@mui/material/TableSortLabel' + +const StyledTableCell = styled(TableCell)(({ theme }) => ({ + color: 'gray.800', + [`&.${tableCellClasses.head}`]: { + backgroundColor: 'white', + border: '0.1px solid', + borderColor: theme.palette.primary.light, + fontSize: 10, + textTransform: 'uppercase', + paddingBlock: '3px' + }, + [`&.${tableCellClasses.body}`]: { + fontWeight: 600, + fontSize: 14, + border: '0.1px solid', + borderColor: theme.palette.primary.light, + paddingBlock: '8px' + } +})) + +const SortIcon = styled(Icon)` + margin-left: 0.25rem; +` + +export type TableColumnType = { + id: string + label: string + align?: 'left' | 'center' | 'right' | 'justify' | 'inherit' + minWidth?: number + format?: (val: number) => string +} + +export const SearchTable = ({ + columns, + rowData, + showTable, + setShowTable, + dropdownRef +}: { + columns: Array + rowData: Array + showTable: boolean + setShowTable: (v: boolean) => void + dropdownRef: MutableRefObject +}) => { + const [order, setOrder] = useState<'asc' | 'desc'>('desc') + const [orderBy, setOrderBy] = useState('assetName') + + const createSortHandler = (property: string) => { + const isAsc = orderBy === property && order === 'asc' + setOrder(isAsc ? 'desc' : 'asc') + setOrderBy(property) + } + + const sortedData = useMemo(() => { + if (order === 'asc') { + return rowData.sort((a, b) => `${a[orderBy]}`.localeCompare(`${b[orderBy]}`)) + } else { + return rowData.sort((a, b) => `${b[orderBy]}`.localeCompare(`${a[orderBy]}`)) + } + }, [rowData, order, orderBy]) + + const handleClickOutside = (event) => { + if (dropdownRef.current && !dropdownRef.current.contains(event.target)) { + setShowTable(false) + } + } + + useEffect(() => { + document.addEventListener('click', handleClickOutside, true) + return () => { + document.removeEventListener('click', handleClickOutside, true) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + if (rowData.length === 0 || !showTable) { + return null + } + + return ( + + + + + + {columns.map((col) => ( + + createSortHandler(col.id)} + sx={{ + '.css-1vweko9-MuiSvgIcon-root-MuiTableSortLabel-icon, .css-3l415a-MuiSvgIcon-root-MuiTableSortLabel-icon, .css-s6n6v6, .css-tqymag': + { + display: 'none' + } + }} + > + {col.label} + + + + + + ))} + + + + {sortedData.map((row, index) => { + return ( + + {columns.map((column) => { + return ( + + {column.format && typeof row[column.id] === 'number' + ? column.format(row[column.id]) + : row[column.id]} + + ) + })} + + ) + })} + +
+
+
+ ) +} diff --git a/components/LaunchPad/Token/ManageToken.tsx b/components/LaunchPad/Token/ManageToken.tsx index 14ed4fbdb..1dd9669af 100644 --- a/components/LaunchPad/Token/ManageToken.tsx +++ b/components/LaunchPad/Token/ManageToken.tsx @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -import React, { ChangeEvent, useContext, useState } from 'react' +import React, { ChangeEvent, useContext, useEffect, useState } from 'react' import { CreatorAddress } from '../CreatorAddress' import { Icon } from '@iconify/react' @@ -32,6 +32,7 @@ import { CopyIcon } from '../copyIcon' import { WalletReducerContext } from '@/hooks/WalletsReducerProvider' import { isValidAddr, truncatedWalletAddress } from '@/components/helpers' import { ErrorMessage } from '../ErrorMessage' +import { TokenSearchInput } from '../TokenSearchInput' const initialValues = { tokenName: '', @@ -46,10 +47,30 @@ const initialValues = { freezeAddr: 'V537CZGHERY87634WVQCAGFYTRYH' } +const columns = [ + { + id: 'symbol', + label: 'Symbol' + }, + { + id: 'assetName', + label: 'Name' + }, + { + id: 'assetId', + label: 'AssetId' + }, + { + id: 'totalQuantity', + label: 'Quantity' + } +] + export const ManageToken = () => { const { activeWallet } = useContext(WalletReducerContext) const [loading, setLoading] = useState(false) const [formData, setFormData] = useState(initialValues) + const [assetList, setAssetList] = useState([]) const [error, setError] = useState({}) const { tokenName, @@ -116,6 +137,62 @@ export const ManageToken = () => { setLoading(false) } + const fetchUserAssets = () => { + setAssetList([ + { + assetId: 7789624, + symbol: 'BUSD', + assetName: 'BUSD Token', + totalQuantity: 300000 + }, + { + assetId: 6789654, + symbol: 'UCDC', + assetName: 'UCDC Token', + totalQuantity: 200000 + }, + { + assetId: 3789654, + symbol: 'goBTC', + assetName: 'goBTC', + totalQuantity: 240000 + }, + { + assetId: 6789654, + symbol: 'UCDC', + assetName: 'UCDC', + totalQuantity: 100000 + }, + { + assetId: 6789654, + symbol: 'UCDC', + assetName: 'UCDC', + totalQuantity: 200000 + }, + { + assetId: 6789654, + symbol: 'UCDC', + assetName: 'UCDC', + totalQuantity: 200000 + }, + { + assetId: 6789654, + symbol: 'UCDC', + assetName: 'UCDC', + totalQuantity: 200000 + }, + { + assetId: 6789654, + symbol: 'UCDC', + assetName: 'UCDC', + totalQuantity: 200000 + } + ]) + } + + useEffect(() => { + fetchUserAssets() + }, []) return ( <> @@ -144,16 +221,15 @@ export const ManageToken = () => { Choose Token to Manage: - - onChange(e)} - sx={styles.input} - /> - + + Search with Asset Name or Asset ID - Only ASAs created by the currently connected wallet will show as options. diff --git a/components/LaunchPad/TokenSearchInput.tsx b/components/LaunchPad/TokenSearchInput.tsx new file mode 100644 index 000000000..24386646d --- /dev/null +++ b/components/LaunchPad/TokenSearchInput.tsx @@ -0,0 +1,49 @@ +import React, { useRef, useState } from 'react' +import { SearchTable, TableColumnType } from './Sale/SearchTable' +import { styles } from './styles.css' + +//MUI Components +import Box from '@mui/material/Box' + +// Custom Styled Components +import OutlinedInput from '@/components/Input/OutlinedInput' + +export const TokenSearchInput = ({ + value, + name, + onChange, + columns, + rowData, + placeholder +}: { + value: string | number + name: string + onChange: (e: unknown) => void + columns: TableColumnType[] + rowData: unknown[] + placeholder: string +}) => { + const [showTable, setShowTable] = useState(false) + const dropdownRef = useRef(null) + + return ( + + onChange(e)} + onFocus={() => setShowTable(true)} + sx={styles.input} + /> + + + ) +}