mirror of
https://github.com/strapi/strapi.git
synced 2025-07-26 02:20:32 +00:00
Refacto table to make it dynamic for the CM migration
Signed-off-by: soupette <cyril@strapi.io>
This commit is contained in:
parent
8b278955bc
commit
c3d639e912
@ -10,33 +10,29 @@ import {
|
|||||||
VisuallyHidden,
|
VisuallyHidden,
|
||||||
} from '@strapi/parts';
|
} from '@strapi/parts';
|
||||||
import { SortIcon, useQueryParams } from '@strapi/helper-plugin';
|
import { SortIcon, useQueryParams } from '@strapi/helper-plugin';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
const TableHead = () => {
|
const TableHead = ({ headers, withMainAction, withBulkActions }) => {
|
||||||
const [{ query }, setQuery] = useQueryParams();
|
const [{ query }, setQuery] = useQueryParams();
|
||||||
const sort = query.sort;
|
const sort = query.sort;
|
||||||
const [sortBy, sortOrder] = sort.split(':');
|
const [sortBy, sortOrder] = sort.split(':');
|
||||||
const headers = [
|
|
||||||
{ label: 'Name', value: 'firstname', isSortable: true },
|
|
||||||
{ label: 'Email', value: 'email', isSortable: true },
|
|
||||||
{ label: 'Roles', value: 'roles', isSortable: false },
|
|
||||||
{ label: 'Username', value: 'username', isSortable: true },
|
|
||||||
{ label: 'Active User', value: 'isActive', isSortable: false },
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Thead>
|
<Thead>
|
||||||
<Tr>
|
<Tr>
|
||||||
<Th>
|
{withMainAction && (
|
||||||
<BaseCheckbox aria-label="Select all entries" />
|
<Th>
|
||||||
</Th>
|
<BaseCheckbox aria-label="Select all entries" />
|
||||||
{headers.map(({ label, value, isSortable }) => {
|
</Th>
|
||||||
const isSorted = sortBy === value;
|
)}
|
||||||
|
{headers.map(({ name, metadatas: { sortable: isSortable, label } }) => {
|
||||||
|
const isSorted = sortBy === name;
|
||||||
const isUp = sortOrder === 'ASC';
|
const isUp = sortOrder === 'ASC';
|
||||||
|
|
||||||
const handleClickSort = (shouldAllowClick = true) => {
|
const handleClickSort = (shouldAllowClick = true) => {
|
||||||
if (isSortable && shouldAllowClick) {
|
if (isSortable && shouldAllowClick) {
|
||||||
const nextSortOrder = isSorted && sortOrder === 'ASC' ? 'DESC' : 'ASC';
|
const nextSortOrder = isSorted && sortOrder === 'ASC' ? 'DESC' : 'ASC';
|
||||||
const nextSort = `${value}:${nextSortOrder}`;
|
const nextSort = `${name}:${nextSortOrder}`;
|
||||||
|
|
||||||
setQuery({
|
setQuery({
|
||||||
sort: nextSort,
|
sort: nextSort,
|
||||||
@ -46,7 +42,7 @@ const TableHead = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Th
|
<Th
|
||||||
key={value}
|
key={name}
|
||||||
action={
|
action={
|
||||||
isSorted ? (
|
isSorted ? (
|
||||||
<IconButton
|
<IconButton
|
||||||
@ -73,12 +69,26 @@ const TableHead = () => {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
<Th>
|
{withBulkActions && (
|
||||||
<VisuallyHidden>Actions</VisuallyHidden>
|
<Th>
|
||||||
</Th>
|
<VisuallyHidden>Actions</VisuallyHidden>
|
||||||
|
</Th>
|
||||||
|
)}
|
||||||
</Tr>
|
</Tr>
|
||||||
</Thead>
|
</Thead>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TableHead.defaultProps = {
|
||||||
|
headers: [],
|
||||||
|
withBulkActions: false,
|
||||||
|
withMainAction: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
TableHead.propTypes = {
|
||||||
|
headers: PropTypes.array,
|
||||||
|
withBulkActions: PropTypes.bool,
|
||||||
|
withMainAction: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
export default TableHead;
|
export default TableHead;
|
||||||
|
@ -0,0 +1,83 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { BaseCheckbox, Box, IconButton, Tbody, Td, Text, Tr, Row } from '@strapi/parts';
|
||||||
|
import { EditIcon, DeleteIcon } from '@strapi/icons';
|
||||||
|
import { useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
|
const TableRows = ({ canUpdate, canDelete, headers, withMainAction, withBulkActions, rows }) => {
|
||||||
|
const {
|
||||||
|
push,
|
||||||
|
location: { pathname },
|
||||||
|
} = useHistory();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tbody>
|
||||||
|
{rows.map(data => {
|
||||||
|
return (
|
||||||
|
<Tr key={data.id}>
|
||||||
|
{withMainAction && (
|
||||||
|
<Td>
|
||||||
|
<BaseCheckbox aria-label="Select all entries" />
|
||||||
|
</Td>
|
||||||
|
)}
|
||||||
|
{headers.map(({ key, cellFormatter, name, ...rest }) => {
|
||||||
|
return (
|
||||||
|
<Td key={key}>
|
||||||
|
{typeof cellFormatter === 'function' ? (
|
||||||
|
cellFormatter(data, { key, name, ...rest })
|
||||||
|
) : (
|
||||||
|
<Text textColor="neutral800">{data[name] || '-'}</Text>
|
||||||
|
)}
|
||||||
|
</Td>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
{withBulkActions && (
|
||||||
|
<Td>
|
||||||
|
<Row>
|
||||||
|
{canUpdate && (
|
||||||
|
<IconButton
|
||||||
|
onClick={() => push(`${pathname}/${data.id}`)}
|
||||||
|
label="Edit"
|
||||||
|
noBorder
|
||||||
|
icon={<EditIcon />}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{canDelete && (
|
||||||
|
<Box paddingLeft={1}>
|
||||||
|
<IconButton
|
||||||
|
onClick={() => console.log('delete')}
|
||||||
|
label="Delete"
|
||||||
|
noBorder
|
||||||
|
icon={<DeleteIcon />}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Row>
|
||||||
|
</Td>
|
||||||
|
)}
|
||||||
|
</Tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Tbody>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
TableRows.defaultProps = {
|
||||||
|
canDelete: false,
|
||||||
|
canUpdate: false,
|
||||||
|
rows: [],
|
||||||
|
withBulkActions: false,
|
||||||
|
withMainAction: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
TableRows.propTypes = {
|
||||||
|
canDelete: PropTypes.bool,
|
||||||
|
canUpdate: PropTypes.bool,
|
||||||
|
headers: PropTypes.array.isRequired,
|
||||||
|
rows: PropTypes.array,
|
||||||
|
withBulkActions: PropTypes.bool,
|
||||||
|
withMainAction: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableRows;
|
@ -1,25 +1,17 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {
|
import { Table as TableCompo } from '@strapi/parts';
|
||||||
BaseCheckbox,
|
import { EmptyBodyTable, useQueryParams } from '@strapi/helper-plugin';
|
||||||
Box,
|
|
||||||
IconButton,
|
|
||||||
Table as TableCompo,
|
|
||||||
Tbody,
|
|
||||||
Td,
|
|
||||||
Text,
|
|
||||||
Tr,
|
|
||||||
Row,
|
|
||||||
} from '@strapi/parts';
|
|
||||||
import { EditIcon, DeleteIcon } from '@strapi/icons';
|
|
||||||
import { EmptyBodyTable, Status, useQueryParams } from '@strapi/helper-plugin';
|
|
||||||
import TableHead from './TableHead';
|
import TableHead from './TableHead';
|
||||||
|
import TableRows from './TableRows';
|
||||||
|
|
||||||
const Table = ({ canDelete, canUpdate, rows }) => {
|
const Table = ({ canDelete, canUpdate, headers, rows, withBulkActions, withMainAction }) => {
|
||||||
|
const [entriesToDelete, setEntriesToDelete] = useState([]);
|
||||||
const [{ query }] = useQueryParams();
|
const [{ query }] = useQueryParams();
|
||||||
const ROW_COUNT = rows.length + 1;
|
const ROW_COUNT = rows.length + 1;
|
||||||
const COL_COUNT = 7;
|
const COL_COUNT = 7;
|
||||||
const hasFilters = query.filters !== undefined;
|
const hasFilters = query.filters !== undefined;
|
||||||
|
|
||||||
const content = hasFilters
|
const content = hasFilters
|
||||||
? {
|
? {
|
||||||
id: 'content-manager.components.TableEmpty.withFilters',
|
id: 'content-manager.components.TableEmpty.withFilters',
|
||||||
@ -30,75 +22,42 @@ const Table = ({ canDelete, canUpdate, rows }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<TableCompo colCount={COL_COUNT} rowCount={ROW_COUNT}>
|
<TableCompo colCount={COL_COUNT} rowCount={ROW_COUNT}>
|
||||||
<TableHead />
|
<TableHead
|
||||||
|
headers={headers}
|
||||||
|
withMainAction={withMainAction}
|
||||||
|
withBulkActions={withBulkActions}
|
||||||
|
/>
|
||||||
{!rows.length ? (
|
{!rows.length ? (
|
||||||
<EmptyBodyTable colSpan={COL_COUNT} content={content} />
|
<EmptyBodyTable colSpan={COL_COUNT} content={content} />
|
||||||
) : (
|
) : (
|
||||||
<Tbody>
|
<TableRows
|
||||||
{rows.map(entry => (
|
canDelete={canDelete}
|
||||||
<Tr key={entry.id}>
|
canUpdate={canUpdate}
|
||||||
<Td>
|
data={rows}
|
||||||
<BaseCheckbox aria-label={`Select ${entry.email}`} />
|
headers={headers}
|
||||||
</Td>
|
rows={rows}
|
||||||
<Td>
|
withBulkActions={withBulkActions}
|
||||||
<Text textColor="neutral800">
|
withMainAction={withMainAction}
|
||||||
{entry.firstname} {entry.lastname}
|
/>
|
||||||
</Text>
|
|
||||||
</Td>
|
|
||||||
<Td>{entry.email}</Td>
|
|
||||||
<Td>
|
|
||||||
<Text textColor="neutral800">{entry.roles.map(role => role.name).join(',\n')}</Text>
|
|
||||||
</Td>
|
|
||||||
<Td>
|
|
||||||
<Text textColor="neutral800">{entry.username || '-'}</Text>
|
|
||||||
</Td>
|
|
||||||
<Td>
|
|
||||||
<Row>
|
|
||||||
<Status
|
|
||||||
isActive={entry.isActive}
|
|
||||||
variant={entry.isActive ? 'success' : 'danger'}
|
|
||||||
/>
|
|
||||||
<Text textColor="neutral800">{entry.isActive ? 'Active' : 'Inactive'}</Text>
|
|
||||||
</Row>
|
|
||||||
</Td>
|
|
||||||
<Td>
|
|
||||||
<Row>
|
|
||||||
{canUpdate && (
|
|
||||||
<IconButton
|
|
||||||
onClick={() => console.log('edit')}
|
|
||||||
label="Edit"
|
|
||||||
noBorder
|
|
||||||
icon={<EditIcon />}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{canDelete && (
|
|
||||||
<Box paddingLeft={1}>
|
|
||||||
<IconButton
|
|
||||||
onClick={() => console.log('delete')}
|
|
||||||
label="Delete"
|
|
||||||
noBorder
|
|
||||||
icon={<DeleteIcon />}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Row>
|
|
||||||
</Td>
|
|
||||||
</Tr>
|
|
||||||
))}
|
|
||||||
</Tbody>
|
|
||||||
)}
|
)}
|
||||||
</TableCompo>
|
</TableCompo>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Table.defaultProps = {
|
Table.defaultProps = {
|
||||||
|
headers: [],
|
||||||
rows: [],
|
rows: [],
|
||||||
|
withBulkActions: false,
|
||||||
|
withMainAction: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
Table.propTypes = {
|
Table.propTypes = {
|
||||||
canDelete: PropTypes.bool.isRequired,
|
canDelete: PropTypes.bool.isRequired,
|
||||||
canUpdate: PropTypes.bool.isRequired,
|
canUpdate: PropTypes.bool.isRequired,
|
||||||
|
headers: PropTypes.array,
|
||||||
rows: PropTypes.array,
|
rows: PropTypes.array,
|
||||||
|
withBulkActions: PropTypes.bool,
|
||||||
|
withMainAction: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Table;
|
export default Table;
|
||||||
|
@ -17,9 +17,9 @@ import { useIntl } from 'react-intl';
|
|||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import adminPermissions from '../../../permissions';
|
import adminPermissions from '../../../permissions';
|
||||||
|
|
||||||
import Table from './Table';
|
import Table from './Table';
|
||||||
import fetchData from './utils/api';
|
import fetchData from './utils/api';
|
||||||
|
import tableHeaders from './utils/tableHeaders';
|
||||||
|
|
||||||
const ListPage = () => {
|
const ListPage = () => {
|
||||||
const {
|
const {
|
||||||
@ -269,7 +269,10 @@ const ListPage = () => {
|
|||||||
canCreate={canCreate}
|
canCreate={canCreate}
|
||||||
canDelete={canDelete}
|
canDelete={canDelete}
|
||||||
canUpdate={canUpdate}
|
canUpdate={canUpdate}
|
||||||
|
headers={tableHeaders}
|
||||||
rows={data?.results}
|
rows={data?.results}
|
||||||
|
withBulkActions
|
||||||
|
withMainAction={canDelete}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</CustomContentLayout>
|
</CustomContentLayout>
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Text, Row } from '@strapi/parts';
|
||||||
|
import { Status } from '@strapi/helper-plugin';
|
||||||
|
|
||||||
|
const tableHeaders = [
|
||||||
|
{
|
||||||
|
name: 'firstname',
|
||||||
|
key: 'firstname',
|
||||||
|
metadatas: { label: 'Name', sortable: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'email',
|
||||||
|
name: 'email',
|
||||||
|
metadatas: { label: 'Email', sortable: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'roles',
|
||||||
|
name: 'roles',
|
||||||
|
metadatas: { label: 'Roles', sortable: false },
|
||||||
|
/* eslint-disable react/prop-types */
|
||||||
|
cellFormatter: ({ roles }) => {
|
||||||
|
return <Text textColor="neutral800">{roles.map(role => role.name).join(',\n')}</Text>;
|
||||||
|
},
|
||||||
|
/* eslint-enable react/prop-types */
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'username',
|
||||||
|
name: 'username',
|
||||||
|
metadatas: { label: 'Username', sortable: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'isActive',
|
||||||
|
name: 'isActive',
|
||||||
|
metadatas: { label: 'Active User', sortable: false },
|
||||||
|
// eslint-disable-next-line react/prop-types
|
||||||
|
cellFormatter: ({ isActive }) => {
|
||||||
|
return (
|
||||||
|
<Row>
|
||||||
|
<Status isActive={isActive} variant={isActive ? 'success' : 'danger'} />
|
||||||
|
<Text textColor="neutral800">{isActive ? 'Active' : 'Inactive'}</Text>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default tableHeaders;
|
Loading…
x
Reference in New Issue
Block a user