1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

feat: new tag types table (#1014)

* feat: new tag types table

* fix: update loader text

* fix: update header

* fix: regenerator runtime

* fix: update snapshot
This commit is contained in:
Fredrik Strand Oseberg 2022-05-25 11:12:53 +02:00 committed by GitHub
parent 91a825792e
commit c073908027
5 changed files with 1235 additions and 171 deletions

View File

@ -0,0 +1,36 @@
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
import { UPDATE_TAG_TYPE } from 'component/providers/AccessProvider/permissions';
import useMediaQuery from '@mui/material/useMediaQuery';
import { useNavigate } from 'react-router-dom';
import { Add } from '@mui/icons-material';
export const AddTagTypeButton = () => {
const navigate = useNavigate();
const smallScreen = useMediaQuery('(max-width:700px)');
return (
<ConditionallyRender
condition={smallScreen}
show={
<PermissionIconButton
onClick={() => navigate('/tag-types/create')}
size="large"
permission={UPDATE_TAG_TYPE}
>
<Add />
</PermissionIconButton>
}
elseShow={
<PermissionButton
permission={UPDATE_TAG_TYPE}
onClick={() => navigate('/tag-types/create')}
>
New tag type
</PermissionButton>
}
/>
);
};

View File

@ -1,50 +0,0 @@
.select {
min-width: 100px;
}
.icon {
fill: #757575;
}
.textfield {
margin-left: 15px;
}
.header {
padding: var(--card-header-padding);
word-break: break-all;
border-bottom: var(--default-border);
display: flex;
align-items: center;
justify-content: space-between;
}
.header h1 {
font-size: var(--h1-size);
}
.container {
padding: var(--card-padding);
}
.formButtons {
padding-top: 1rem;
}
.tagListItem {
padding: 0;
}
.tagListItem * > a {
text-decoration: none;
color: inherit;
}
.tagTypeContainer {
max-width: 350px;
}
.addTagTypeForm {
display: flex;
flex-direction: column;
}

View File

@ -1,15 +1,16 @@
import { useContext, useState } from 'react'; import { useState, useMemo } from 'react';
import { Link, useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { Box } from '@mui/material';
import { import {
Button, Table,
IconButton, SortableTableHeader,
List, TableBody,
ListItem, TableCell,
ListItemIcon, TableRow,
ListItemText, TablePlaceholder,
Tooltip, TableSearch,
} from '@mui/material'; } from 'component/common/Table';
import { Add, Delete, Edit, Label } from '@mui/icons-material'; import { Delete, Edit, Label } from '@mui/icons-material';
import { PageHeader } from 'component/common/PageHeader/PageHeader'; import { PageHeader } from 'component/common/PageHeader/PageHeader';
import { PageContent } from 'component/common/PageContent/PageContent'; import { PageContent } from 'component/common/PageContent/PageContent';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
@ -18,28 +19,152 @@ import {
UPDATE_TAG_TYPE, UPDATE_TAG_TYPE,
} from 'component/providers/AccessProvider/permissions'; } from 'component/providers/AccessProvider/permissions';
import { Dialogue } from 'component/common/Dialogue/Dialogue'; import { Dialogue } from 'component/common/Dialogue/Dialogue';
import useMediaQuery from '@mui/material/useMediaQuery';
import styles from './TagTypeList.module.scss';
import AccessContext from 'contexts/AccessContext';
import useTagTypesApi from 'hooks/api/actions/useTagTypesApi/useTagTypesApi'; import useTagTypesApi from 'hooks/api/actions/useTagTypesApi/useTagTypesApi';
import useTagTypes from 'hooks/api/getters/useTagTypes/useTagTypes'; import useTagTypes from 'hooks/api/getters/useTagTypes/useTagTypes';
import useToast from 'hooks/useToast'; import useToast from 'hooks/useToast';
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton'; import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
import { formatUnknownError } from 'utils/formatUnknownError'; import { formatUnknownError } from 'utils/formatUnknownError';
import { ITagType } from 'interfaces/tags'; import { useTable, useGlobalFilter, useSortBy } from 'react-table';
import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell';
import { sortTypes } from 'utils/sortTypes';
import { AddTagTypeButton } from './AddTagTypeButton/AddTagTypeButton';
export const TagTypeList = () => { export const TagTypeList = () => {
const { hasAccess } = useContext(AccessContext);
const [deletion, setDeletion] = useState<{ const [deletion, setDeletion] = useState<{
open: boolean; open: boolean;
name?: string; name?: string;
}>({ open: false }); }>({ open: false });
const navigate = useNavigate(); const navigate = useNavigate();
const smallScreen = useMediaQuery('(max-width:700px)');
const { deleteTagType } = useTagTypesApi(); const { deleteTagType } = useTagTypesApi();
const { tagTypes, refetch } = useTagTypes(); const { tagTypes, refetch, loading } = useTagTypes();
const { setToastData, setToastApiError } = useToast(); const { setToastData, setToastApiError } = useToast();
const data = useMemo(() => {
if (loading) {
return Array(5).fill({
name: 'Tag type name',
description: 'Tag type description when loading',
});
}
return tagTypes.map(({ name, description }) => ({
name,
description,
}));
}, [tagTypes, loading]);
const columns = useMemo(
() => [
{
id: 'Icon',
Cell: () => (
<Box
data-loading
sx={{
pl: 2,
pr: 1,
display: 'flex',
alignItems: 'center',
}}
>
<Label color="disabled" />
</Box>
),
},
{
Header: 'Name',
accessor: 'name',
width: '90%',
Cell: ({
row: {
original: { name, description },
},
}: any) => {
return (
<LinkCell
data-loading
title={name}
subtitle={description}
/>
);
},
sortType: 'alphanumeric',
},
{
Header: 'Actions',
id: 'Actions',
align: 'center',
Cell: ({ row: { original } }: any) => (
<Box
sx={{ display: 'flex', justifyContent: 'flex-end' }}
data-loading
>
<PermissionIconButton
onClick={() =>
navigate(`/tag-types/edit/${original.name}`)
}
permission={UPDATE_TAG_TYPE}
tooltipProps={{ title: 'Edit tag type' }}
>
<Edit />
</PermissionIconButton>
<PermissionIconButton
permission={DELETE_TAG_TYPE}
tooltipProps={{ title: 'Delete tag type' }}
onClick={() =>
setDeletion({
open: true,
name: original.name,
})
}
>
<Delete />
</PermissionIconButton>
</Box>
),
width: 150,
disableSortBy: true,
},
{
accessor: 'description',
disableSortBy: true,
},
],
[]
);
const initialState = useMemo(
() => ({
sortBy: [{ id: 'name', desc: false }],
hiddenColumns: ['description'],
}),
[]
);
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
state: { globalFilter },
setGlobalFilter,
} = useTable(
{
columns: columns as any[], // TODO: fix after `react-table` v8 update
data,
initialState,
sortTypes,
autoResetGlobalFilter: false,
autoResetSortBy: false,
disableSortRemove: true,
},
useGlobalFilter,
useSortBy
);
const deleteTag = async () => { const deleteTag = async () => {
try { try {
if (deletion.name) { if (deletion.name) {
@ -57,100 +182,64 @@ export const TagTypeList = () => {
} }
}; };
let header = ( return (
<PageHeader <PageContent
title="Tag Types" isLoading={loading}
actions={ header={
<ConditionallyRender <PageHeader
condition={hasAccess(UPDATE_TAG_TYPE)} title="Tag types"
show={ actions={
<ConditionallyRender <>
condition={smallScreen} <TableSearch
show={ initialValue={globalFilter}
<Tooltip title="Add tag type" arrow> onChange={setGlobalFilter}
<IconButton />
onClick={() => <PageHeader.Divider />
navigate('/tag-types/create') <AddTagTypeButton />
} </>
size="large"
>
<Add />
</IconButton>
</Tooltip>
}
elseShow={
<Button
variant="contained"
color="primary"
onClick={() =>
navigate('/tag-types/create')
}
>
New tag type
</Button>
}
/>
} }
/> />
} }
/> >
); <SearchHighlightProvider value={globalFilter}>
<Table {...getTableProps()}>
const renderTagType = (tagType: ITagType) => { <SortableTableHeader headerGroups={headerGroups} />
let link = ( <TableBody {...getTableBodyProps()}>
<Link to={`/tag-types/edit/${tagType.name}`}> {rows.map(row => {
<strong>{tagType.name}</strong> prepareRow(row);
</Link> return (
); <TableRow hover {...row.getRowProps()}>
let deleteButton = ( {row.cells.map(cell => (
<Tooltip title={`Delete ${tagType.name}`} arrow> <TableCell {...cell.getCellProps()}>
<IconButton {cell.render('Cell')}
onClick={() => </TableCell>
setDeletion({ ))}
open: true, </TableRow>
name: tagType.name, );
}) })}
} </TableBody>
size="large" </Table>
> </SearchHighlightProvider>
<Delete /> <ConditionallyRender
</IconButton> condition={rows.length === 0}
</Tooltip> show={
); <ConditionallyRender
condition={globalFilter?.length > 0}
return ( show={
<ListItem <TablePlaceholder>
key={`${tagType.name}`} No tags found matching &ldquo;
classes={{ root: styles.tagListItem }} {globalFilter}
> &rdquo;
<ListItemIcon> </TablePlaceholder>
<Label /> }
</ListItemIcon> elseShow={
<ListItemText primary={link} secondary={tagType.description} /> <TablePlaceholder>
<PermissionIconButton No tags available. Get started by adding one.
permission={UPDATE_TAG_TYPE} </TablePlaceholder>
component={Link} }
tooltipProps={{ title: 'Edit tag type' }} />
to={`/tag-types/edit/${tagType.name}`} }
> />
<Edit className={styles.icon} />
</PermissionIconButton>
<ConditionallyRender
condition={hasAccess(DELETE_TAG_TYPE)}
show={deleteButton}
/>
</ListItem>
);
};
return (
<PageContent header={header}>
<List>
<ConditionallyRender
condition={tagTypes.length > 0}
show={tagTypes.map(tagType => renderTagType(tagType))}
elseShow={<ListItem>No entries</ListItem>}
/>
</List>
<Dialogue <Dialogue
title="Really delete Tag type?" title="Really delete Tag type?"
open={deletion.open} open={deletion.open}

View File

@ -1,4 +1,5 @@
import '@testing-library/jest-dom'; import '@testing-library/jest-dom';
import 'whatwg-fetch'; import 'whatwg-fetch';
import 'regenerator-runtime';
process.env.TZ = 'UTC'; process.env.TZ = 'UTC';