fix: image table on dashboard

This commit is contained in:
diced 2022-08-17 15:01:23 -07:00
parent 9b60147e11
commit e911db4c1a
No known key found for this signature in database
GPG key ID: 370BD1BA142842D1
5 changed files with 826 additions and 939 deletions

View file

@ -5,3 +5,5 @@ plugins:
spec: "@yarnpkg/plugin-interactive-tools"
yarnPath: .yarn/releases/yarn-3.2.1.cjs
checksumBehavior: "update"

View file

@ -15,6 +15,7 @@
"docker:build-dev": "docker-compose --file docker-compose.dev.yml up --build"
},
"dependencies": {
"@dicedtomato/mantine-data-grid": "0.0.20",
"@emotion/react": "^11.9.3",
"@emotion/server": "^11.4.0",
"@iarna/toml": "2.2.5",
@ -34,6 +35,7 @@
"argon2": "^0.28.5",
"colorette": "^2.0.19",
"cookie": "^0.5.0",
"dayjs": "^1.11.5",
"dotenv": "^16.0.1",
"dotenv-expand": "^8.0.3",
"fecha": "^4.2.3",
@ -72,4 +74,4 @@
"url": "https://github.com/diced/zipline.git"
},
"packageManager": "yarn@3.2.1"
}
}

View file

@ -1,168 +0,0 @@
/* eslint-disable react/jsx-key */
// Code taken from https://codesandbox.io/s/eojw8 and is modified a bit (the entire src/components/table directory)
import {
ActionIcon,
createStyles,
Divider,
Group, Image, Pagination,
Select,
Table,
Text,
useMantineTheme,
} from '@mantine/core';
import {
usePagination,
useTable,
} from 'react-table';
import { CopyIcon, DeleteIcon, EnterIcon } from './icons';
const pageSizeOptions = ['10', '25', '50'];
const useStyles = createStyles((t) => ({
root: { height: '100%', display: 'block', marginTop: 10 },
tableContainer: {
display: 'block',
overflow: 'auto',
'& > table': {
'& > thead': { backgroundColor: t.colorScheme === 'dark' ? t.colors.dark[6] : t.colors.gray[0], zIndex: 1 },
'& > thead > tr > th': { padding: t.spacing.md },
'& > tbody > tr > td': { padding: t.spacing.md },
},
borderRadius: 6,
},
stickHeader: { top: 0, position: 'sticky' },
disableSortIcon: { color: t.colors.gray[5] },
sortDirectionIcon: { transition: 'transform 200ms ease' },
}));
export function FilePreview({ url, type }) {
const Type = props => {
return {
'video': <video autoPlay controls {...props} />,
'image': <Image {...props} />,
'audio': <audio autoPlay controls {...props} />,
}[type.split('/')[0]];
};
return (
<Type
sx={{ maxWidth: '10vw', maxHeight: '100vh' }}
style={{ maxWidth: '10vw', maxHeight: '100vh' }}
mr='sm'
src={url}
alt={'Unable to preview file'}
/>
);
}
export default function ImagesTable({
columns,
data = [],
serverSideDataSource = false,
initialPageSize = 10,
initialPageIndex = 0,
pageCount = 0,
total = 0,
deleteImage, copyImage, viewImage,
}) {
const { classes } = useStyles();
const theme = useMantineTheme();
const tableOptions = useTable(
{
data,
columns,
pageCount,
initialState: { pageSize: initialPageSize, pageIndex: initialPageIndex },
},
usePagination
);
const {
getTableProps, getTableBodyProps, headerGroups, rows, prepareRow, page, gotoPage, setPageSize, state: { pageIndex, pageSize },
} = tableOptions;
const getPageRecordInfo = () => {
const firstRowNum = pageIndex * pageSize + 1;
const totalRows = rows.length;
const currLastRowNum = (pageIndex + 1) * pageSize;
let lastRowNum = currLastRowNum < totalRows ? currLastRowNum : totalRows;
return `${firstRowNum} - ${lastRowNum} of ${totalRows}`;
};
const getPageCount = () => Math.ceil(rows.length / pageSize);
const handlePageChange = (pageNum) => gotoPage(pageNum - 1);
const renderHeader = () => headerGroups.map(hg => (
<tr {...hg.getHeaderGroupProps()}>
{hg.headers.map(column => (
<th {...column.getHeaderProps()}>
<Group noWrap position={column.align || 'apart'}>
<div>{column.render('Header')}</div>
</Group>
</th>
))}
<th>Actions</th>
</tr>
));
const renderRow = rows => rows.map(row => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => (
<td align={cell.column.align || 'left'} {...cell.getCellProps()}>
{cell.render('Cell')}
</td>
))}
<td align='right'>
<Group noWrap>
<ActionIcon color='red' variant='outline' onClick={() => deleteImage(row)}><DeleteIcon /></ActionIcon>
<ActionIcon color='primary' variant='outline' onClick={() => copyImage(row)}><CopyIcon /></ActionIcon>
<ActionIcon color='green' variant='outline' onClick={() => viewImage(row)}><EnterIcon /></ActionIcon>
</Group>
</td>
</tr>
);
});
return (
<div className={classes.root}>
<div
className={classes.tableContainer}
style={{ height: 'calc(100% - 44px)' }}
>
<Table {...getTableProps()}>
<thead style={{ backgroundColor: theme.other.hover }}>
{renderHeader()}
</thead>
<tbody {...getTableBodyProps()}>
{renderRow(page)}
</tbody>
</Table>
</div>
<Divider mb='md' variant='dotted' />
<Group position='left'>
<Text size='sm'>Rows per page: </Text>
<Select
style={{ width: '72px' }}
variant='filled'
data={pageSizeOptions}
value={pageSize + ''}
onChange={pageSize => setPageSize(Number(pageSize))} />
<Divider orientation='vertical' />
<Text size='sm'>{getPageRecordInfo()}</Text>
<Divider orientation='vertical' />
<Pagination
page={pageIndex + 1}
total={getPageCount()}
onChange={handlePageChange} />
</Group>
</div>
);
}

View file

@ -1,21 +1,20 @@
import { SimpleGrid, Skeleton, Text, Title } from '@mantine/core';
import { SimpleGrid, Skeleton, Title, Card as MantineCard, useMantineTheme, Box } from '@mantine/core';
import { randomId, useClipboard } from '@mantine/hooks';
import { showNotification } from '@mantine/notifications';
import Card from 'components/Card';
import File from 'components/File';
import { CopyIcon, CrossIcon, DeleteIcon } from 'components/icons';
import ImagesTable from 'components/ImagesTable';
import { CopyIcon, CrossIcon, DeleteIcon, EnterIcon } from 'components/icons';
import Link from 'components/Link';
import MutedText from 'components/MutedText';
import { bytesToRead } from 'lib/clientUtils';
import useFetch from 'lib/hooks/useFetch';
import { useStoreSelector } from 'lib/redux/store';
import { DataGrid, dateFilterFn, stringFilterFn } from '@dicedtomato/mantine-data-grid';
import { useEffect, useState } from 'react';
type Aligns = 'inherit' | 'right' | 'left' | 'center' | 'justify';
export default function Dashboard() {
const user = useStoreSelector(state => state.user);
const theme = useMantineTheme();
const [images, setImages] = useState([]);
const [recent, setRecent] = useState([]);
@ -117,17 +116,77 @@ export default function Dashboard() {
<Title mt='md'>Files</Title>
<MutedText size='md'>View your gallery <Link href='/dashboard/files'>here</Link>.</MutedText>
<ImagesTable
columns={[
{ accessor: 'file', Header: 'Name', minWidth: 170, align: 'inherit' as Aligns },
{ accessor: 'mimetype', Header: 'Type', minWidth: 100, align: 'inherit' as Aligns },
{ accessor: 'created_at', Header: 'Date' },
]}
data={images}
deleteImage={deleteImage}
copyImage={copyImage}
viewImage={viewImage}
/>
<Box>
<DataGrid
data={images}
loading={images.length ? false : true}
withPagination={true}
withColumnResizing={false}
withColumnFilters={true}
noEllipsis={true}
withSorting={true}
highlightOnHover={true}
CopyIcon={CopyIcon}
DeleteIcon={DeleteIcon}
EnterIcon={EnterIcon}
deleteImage={deleteImage}
copyImage={copyImage}
viewImage={viewImage}
styles={{
dataCell: {
width: '100%',
},
td: {
':nth-child(1)': {
minWidth: 170,
},
':nth-child(2)': {
minWidth: 100,
},
},
th: {
':nth-child(1)': {
minWidth: 170,
padding: theme.spacing.lg,
borderTopLeftRadius: theme.radius.sm,
},
':nth-child(2)': {
minWidth: 100,
padding: theme.spacing.lg,
},
':nth-child(3)': {
padding: theme.spacing.lg,
},
':nth-child(4)': {
padding: theme.spacing.lg,
borderTopRightRadius: theme.radius.sm,
},
},
thead: {
backgroundColor: theme.colors.dark[6],
},
}}
empty={<></>}
columns={[
{
accessorKey: 'file',
header: 'Name',
filterFn: stringFilterFn,
},
{
accessorKey: 'mimetype',
header: 'Type',
filterFn: stringFilterFn,
},
{
accessorKey: 'created_at',
header: 'Date',
filterFn: dateFilterFn,
},
]}
/>
</Box>
</>
);
}

1500
yarn.lock

File diff suppressed because it is too large Load diff