Skip to content

Commit

Permalink
feat: history table redesign
Browse files Browse the repository at this point in the history
  • Loading branch information
dcodes05 committed Sep 9, 2023
1 parent 0bfcfbc commit c56bf88
Show file tree
Hide file tree
Showing 23 changed files with 816 additions and 187 deletions.
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
]
}
],
"@typescript-eslint/no-explicit-any": false,
"react/react-in-jsx-scope": "off",
"no-empty": ["error", { "allowEmptyCatch": true }],
// Unused imports rules
Expand Down
2 changes: 1 addition & 1 deletion .graphqlconfig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"name": "Subsquid GraphQL Schema",
"schemaPath": "https://squid.subsquid.io/origin-squid/v/v4/graphql"
"schemaPath": "https://squid.subsquid.io/origin-squid/v/v5/graphql"
}
14 changes: 5 additions & 9 deletions apps/oeth/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Container, Stack } from '@mui/material';
import { HistoryView } from '@origin/oeth/history';
import { ApyHeader } from '@origin/oeth/shared';
import { SwapView } from '@origin/oeth/swap';
import { Route, Routes } from 'react-router-dom';

Expand All @@ -23,14 +22,11 @@ export function App() {
}}
maxWidth="sm"
>
<ApyHeader />
<Stack mt={3}>
<Routes>
<Route index element={<SwapView />} />
<Route path="history" element={<HistoryView />} />
<Route path="swap" element={<SwapView />} />
</Routes>
</Stack>
<Routes>
<Route index element={<SwapView />} />
<Route path="history" element={<HistoryView />} />
<Route path="swap" element={<SwapView />} />
</Routes>
</Container>
</Stack>
);
Expand Down
46 changes: 46 additions & 0 deletions libs/oeth/history/src/components/ExportData.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React, { useCallback, useMemo, useRef } from 'react';

import { Box, Link } from '@mui/material';
import { useIntl } from 'react-intl';

import { HistoryFilterButton } from './HistoryButton';
import { Rows } from './HistoryTable';

interface Props {
data?: Rows;
}

export function ExportData({ data }: Props) {
const link = useRef<HTMLAnchorElement>(null);
const intl = useIntl();

const generateCSV = useCallback(() => {
const rows = [['Date', 'Type', 'Amount', 'Balance', 'Transaction Hash']];
data.forEach((row) =>
rows.push([row.timestamp, row.type, row.value, row.balance, row.txHash]),
);
link.current.href =
'data:text/csv;charset=utf-8,' +
encodeURI(rows.map((e) => e.join(',')).join('\n'));
link.current.click();
}, [data]);

return (
<>
<Link
ref={link}
sx={{ display: 'none' }}
target="_blank"
download="transaction_history.csv"
></Link>
<HistoryFilterButton onClick={generateCSV}>
<Box
component="img"
src="/images/download.svg"
sx={{ height: '0.75rem', width: '0.75rem' }}
></Box>
{intl.formatMessage({ defaultMessage: 'CSV' })}
</HistoryFilterButton>
</>
);
}
177 changes: 177 additions & 0 deletions libs/oeth/history/src/components/Filters.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import React, { useState } from 'react';

import {
Box,
Button,
Checkbox,
Divider,
FormControlLabel,
FormLabel,
MenuItem,
MenuList,
Popover,
Stack,
Typography,
alpha,
useTheme,
} from '@mui/material';
import {
ActionButton,
CheckboxIcon,
EmptyCheckbox,
} from '@origin/shared/components';
import { useIntl } from 'react-intl';

import { HistoryFilterButton } from './HistoryButton';

const styles = {
fontSize: '0.75rem',
fontWeight: 500,
lineHeight: '1.25rem',
color: 'primary.contrastText',
};

interface Props {
onChange: (values: string[]) => void;
}

export function HistoryFilters({ onChange }: Props) {
const [selected, setSelectedTypes] = useState<string[]>([]);
const intl = useIntl();
const theme = useTheme();
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);

return (
<>
<HistoryFilterButton onClick={(e) => setAnchorEl(e.currentTarget)}>
Filters
<Box
sx={{
width: '4px',
height: '4px',
borderRadius: '50%',
background: (theme) => theme.palette.primary.contrastText,
}}
></Box>
{selected.length}
</HistoryFilterButton>
<Popover
open={!!anchorEl}
anchorEl={anchorEl}
onClose={() => setAnchorEl(null)}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'right',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'right',
}}
sx={{
'& .MuiPaper-root.MuiPopover-paper': {
boxSizing: 'border-box',
background: 'background-paper',
maxWidth: '16.5rem',
width: '100%',
borderRadius: 2,
[theme.breakpoints.down('md')]: {
left: '0 !important',
right: 0,
marginInline: 'auto',
},
},
}}
>
<Typography color="primary.contrastText" sx={{ padding: 2 }}>
{intl.formatMessage({ defaultMessage: 'Filters' })}
</Typography>
<Divider sx={{ marginBlockEnd: 2 }} />

{['Yield', 'Swap', 'Sent', 'Received'].map((label) => (
<Stack
key={label}
direction="row"
justifyContent="space-between"
alignItems="center"
sx={{
paddingInline: 2,
paddingBlock: 0,
paddingTop: 0,
paddingBottom: 0,
...styles,
':hover': {
background: (theme) => theme.palette.grey[700],
},
}}
>
<FormLabel htmlFor={label} sx={{ color: 'primary.contrastText' }}>
{label}
</FormLabel>

<Checkbox
inputProps={{ id: label }}
checked={selected.includes(label)}
checkedIcon={<CheckboxIcon />}
icon={<EmptyCheckbox />}
sx={{
'& svg, input': {
width: '1.25rem',
height: '1.25rem',
top: 'auto',
left: 'auto',
},
'&:hover:has(input:checked) svg': {
fill: (theme) => alpha(theme.palette.secondary.main, 0.4),
strokeWidth: '1px',
stroke: (theme) => theme.palette.primary.main,
},
'&:hover:has(input:not(:checked)) svg': {
fill: (theme) => alpha(theme.palette.secondary.main, 0.4),
},
}}
onChange={(e) =>
setSelectedTypes((prev) => {
if (e.target.checked) {
return [...prev, label];
} else {
return prev.filter((val) => val !== label);
}
})
}
/>
</Stack>
))}

<Divider sx={{ marginBlockStart: 2 }} />
<Stack
direction="row"
justifyContent="flex-end"
sx={{ padding: 2 }}
gap={1}
>
<Button
variant="text"
sx={styles}
onClick={() => setSelectedTypes([])}
>
{intl.formatMessage({ defaultMessage: 'Clear all' })}
</Button>
<ActionButton
sx={{
paddingInline: 2,
paddingBlock: 0.5,
maxWidth: '4rem',
...styles,
}}
onClick={() => {
setAnchorEl(null);
onChange(selected);
}}
>
{intl.formatMessage({ defaultMessage: 'Apply' })}
</ActionButton>
</Stack>
</Popover>
</>
);
}
40 changes: 10 additions & 30 deletions libs/oeth/history/src/components/HistoryButton.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { alpha, Box, Button } from '@mui/material';
import { alpha, Button } from '@mui/material';

import type { BoxProps, ButtonProps } from '@mui/material';
import type { ButtonProps } from '@mui/material';

interface Props extends ButtonProps {
circle?: boolean;
Expand All @@ -19,49 +19,29 @@ export function HistoryFilterButton({
<Button
variant="contained"
onClick={onClick}
disableRipple
sx={{
background: (theme) => alpha(theme.palette.common.white, 0.1),
color: selected ? 'primary.contrastText' : 'text.primary',
display: 'flex',
alignItems: 'center',
backgroundImage: 'none',
gap: 1.5,
borderRadius: 14,
gap: 1,
borderRadius: 8,
paddingInline: 2,
paddingBlock: 0.5,
fontSize: (theme) => theme.typography.pxToRem(12),
'&:hover': {
color: 'primary.contrastText',
fontWeight: 500,
fontStyle: 'normal',
lineHeight: (theme) => theme.typography.pxToRem(20),
':hover': {
background: (theme) => alpha(theme.palette.common.white, 0.1),
},
...sx,
}}
{...rest}
>
{children}
{circle ? (
<Circle
sx={{
background: (theme) =>
selected
? theme.palette.background.gradient3
: theme.palette.background.paper,
}}
/>
) : undefined}
</Button>
);
}

function Circle(props: BoxProps) {
return (
<Box
sx={{
backgroundColor: (theme) => theme.palette.background.default,
height: '0.5rem',
width: '0.5rem',
borderRadius: '100%',
...props,
}}
/>
);
}
Loading

0 comments on commit c56bf88

Please sign in to comment.