2022-05-24 10:58:06 +02:00
|
|
|
import { useState, useMemo } from 'react';
|
|
|
|
import { useNavigate } from 'react-router-dom';
|
|
|
|
import { IconButton, Tooltip, Box } from '@mui/material';
|
2021-11-29 14:29:58 +01:00
|
|
|
import {
|
|
|
|
Delete,
|
2022-03-04 23:39:41 +01:00
|
|
|
Edit,
|
2021-11-29 14:29:58 +01:00
|
|
|
Extension,
|
|
|
|
Visibility,
|
|
|
|
VisibilityOff,
|
2022-05-02 15:52:41 +02:00
|
|
|
} from '@mui/icons-material';
|
2021-11-29 14:29:58 +01:00
|
|
|
import {
|
|
|
|
DELETE_STRATEGY,
|
|
|
|
UPDATE_STRATEGY,
|
2022-03-28 10:49:59 +02:00
|
|
|
} from 'component/providers/AccessProvider/permissions';
|
2022-05-24 10:58:06 +02:00
|
|
|
import {
|
|
|
|
Table,
|
|
|
|
SortableTableHeader,
|
|
|
|
TableBody,
|
|
|
|
TableCell,
|
|
|
|
TableRow,
|
|
|
|
TablePlaceholder,
|
|
|
|
TableSearch,
|
|
|
|
} from 'component/common/Table';
|
2022-05-02 12:52:33 +02:00
|
|
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
2022-05-09 14:38:12 +02:00
|
|
|
import { PageContent } from 'component/common/PageContent/PageContent';
|
|
|
|
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
2022-05-02 12:52:33 +02:00
|
|
|
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
2022-03-04 23:39:41 +01:00
|
|
|
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
2022-03-25 12:34:20 +01:00
|
|
|
import { formatStrategyName } from 'utils/strategyNames';
|
2022-03-09 14:59:24 +01:00
|
|
|
import { useStrategies } from 'hooks/api/getters/useStrategies/useStrategies';
|
2022-03-04 23:39:41 +01:00
|
|
|
import useStrategiesApi from 'hooks/api/actions/useStrategiesApi/useStrategiesApi';
|
|
|
|
import useToast from 'hooks/useToast';
|
2022-03-25 12:34:20 +01:00
|
|
|
import { formatUnknownError } from 'utils/formatUnknownError';
|
2022-05-04 15:16:34 +02:00
|
|
|
import { IStrategy } from 'interfaces/strategy';
|
2022-05-24 10:58:06 +02:00
|
|
|
import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell';
|
|
|
|
import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
|
|
|
|
import { sortTypes } from 'utils/sortTypes';
|
|
|
|
import { useTable, useGlobalFilter, useSortBy } from 'react-table';
|
|
|
|
import { AddStrategyButton } from './AddStrategyButton/AddStrategyButton';
|
|
|
|
import { PredefinedBadge } from './PredefinedBadge/PredefinedBadge';
|
2022-02-11 00:08:55 +01:00
|
|
|
interface IDialogueMetaData {
|
|
|
|
show: boolean;
|
|
|
|
title: string;
|
|
|
|
onConfirm: () => void;
|
|
|
|
}
|
2021-03-30 15:14:02 +02:00
|
|
|
|
2022-02-11 00:08:55 +01:00
|
|
|
export const StrategiesList = () => {
|
2022-05-05 13:42:18 +02:00
|
|
|
const navigate = useNavigate();
|
2022-02-11 00:08:55 +01:00
|
|
|
const [dialogueMetaData, setDialogueMetaData] = useState<IDialogueMetaData>(
|
2022-02-25 10:55:39 +01:00
|
|
|
{
|
|
|
|
show: false,
|
|
|
|
title: '',
|
|
|
|
onConfirm: () => {},
|
|
|
|
}
|
2022-02-11 00:08:55 +01:00
|
|
|
);
|
2022-05-24 10:58:06 +02:00
|
|
|
|
|
|
|
const { strategies, refetchStrategies, loading } = useStrategies();
|
2022-02-11 00:08:55 +01:00
|
|
|
const { removeStrategy, deprecateStrategy, reactivateStrategy } =
|
|
|
|
useStrategiesApi();
|
|
|
|
const { setToastData, setToastApiError } = useToast();
|
2021-03-30 15:14:02 +02:00
|
|
|
|
2022-05-24 10:58:06 +02:00
|
|
|
const data = useMemo(() => {
|
|
|
|
if (loading) {
|
|
|
|
return Array(5).fill({
|
|
|
|
name: 'Context name',
|
|
|
|
description: 'Context description when loading',
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return strategies.map(
|
|
|
|
({ name, description, editable, deprecated }) => ({
|
|
|
|
name,
|
|
|
|
description,
|
|
|
|
editable,
|
|
|
|
deprecated,
|
|
|
|
})
|
|
|
|
);
|
|
|
|
}, [strategies, loading]);
|
|
|
|
|
|
|
|
const columns = useMemo(
|
|
|
|
() => [
|
|
|
|
{
|
|
|
|
id: 'Icon',
|
|
|
|
Cell: () => (
|
|
|
|
<Box
|
|
|
|
data-loading
|
|
|
|
sx={{
|
|
|
|
pl: 2,
|
|
|
|
pr: 1,
|
|
|
|
display: 'flex',
|
|
|
|
alignItems: 'center',
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Extension color="disabled" />
|
|
|
|
</Box>
|
|
|
|
),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Header: 'Name',
|
|
|
|
accessor: 'name',
|
|
|
|
width: '90%',
|
|
|
|
Cell: ({
|
|
|
|
row: {
|
|
|
|
original: { name, description, deprecated, editable },
|
|
|
|
},
|
|
|
|
}: any) => {
|
|
|
|
const subTitleText = deprecated
|
|
|
|
? `${description} (deprecated)`
|
|
|
|
: description;
|
|
|
|
return (
|
|
|
|
<LinkCell
|
|
|
|
data-loading
|
|
|
|
title={formatStrategyName(name)}
|
|
|
|
subtitle={subTitleText}
|
|
|
|
to={`/strategies/${name}`}
|
2021-04-23 15:21:24 +02:00
|
|
|
>
|
2022-05-24 10:58:06 +02:00
|
|
|
<ConditionallyRender
|
|
|
|
condition={!editable}
|
|
|
|
show={() => <PredefinedBadge />}
|
|
|
|
/>
|
|
|
|
</LinkCell>
|
|
|
|
);
|
|
|
|
},
|
|
|
|
sortType: 'alphanumeric',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Header: 'Actions',
|
|
|
|
id: 'Actions',
|
|
|
|
align: 'center',
|
|
|
|
Cell: ({ row: { original } }: any) => (
|
|
|
|
<Box
|
|
|
|
sx={{ display: 'flex', justifyContent: 'flex-end' }}
|
|
|
|
data-loading
|
|
|
|
>
|
|
|
|
<ConditionallyRender
|
|
|
|
condition={original.deprecated}
|
|
|
|
show={reactivateButton(original)}
|
|
|
|
elseShow={deprecateButton(original)}
|
|
|
|
/>
|
|
|
|
{editButton(original)}
|
|
|
|
{deleteButton(original)}
|
|
|
|
</Box>
|
|
|
|
),
|
|
|
|
width: 150,
|
|
|
|
disableSortBy: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
accessor: 'description',
|
|
|
|
disableSortBy: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
accessor: 'sortOrder',
|
|
|
|
sortType: 'number',
|
|
|
|
},
|
|
|
|
],
|
|
|
|
[]
|
2021-03-30 15:14:02 +02:00
|
|
|
);
|
|
|
|
|
2022-05-24 10:58:06 +02:00
|
|
|
const initialState = useMemo(
|
|
|
|
() => ({
|
|
|
|
sortBy: [{ id: 'name', desc: false }],
|
|
|
|
hiddenColumns: ['description', 'sortOrder'],
|
|
|
|
}),
|
|
|
|
[]
|
|
|
|
);
|
|
|
|
|
|
|
|
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
|
2021-03-30 15:14:02 +02:00
|
|
|
);
|
|
|
|
|
2022-05-04 15:16:34 +02:00
|
|
|
const onReactivateStrategy = (strategy: IStrategy) => {
|
2022-02-11 00:08:55 +01:00
|
|
|
setDialogueMetaData({
|
|
|
|
show: true,
|
|
|
|
title: 'Really reactivate strategy?',
|
|
|
|
onConfirm: async () => {
|
|
|
|
try {
|
|
|
|
await reactivateStrategy(strategy);
|
|
|
|
refetchStrategies();
|
|
|
|
setToastData({
|
|
|
|
type: 'success',
|
|
|
|
title: 'Success',
|
|
|
|
text: 'Strategy reactivated successfully',
|
|
|
|
});
|
2022-02-25 10:55:39 +01:00
|
|
|
} catch (error: unknown) {
|
|
|
|
setToastApiError(formatUnknownError(error));
|
2022-02-11 00:08:55 +01:00
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2022-05-04 15:16:34 +02:00
|
|
|
const onDeprecateStrategy = (strategy: IStrategy) => {
|
2022-02-11 00:08:55 +01:00
|
|
|
setDialogueMetaData({
|
|
|
|
show: true,
|
|
|
|
title: 'Really deprecate strategy?',
|
|
|
|
onConfirm: async () => {
|
|
|
|
try {
|
|
|
|
await deprecateStrategy(strategy);
|
|
|
|
refetchStrategies();
|
|
|
|
setToastData({
|
|
|
|
type: 'success',
|
|
|
|
title: 'Success',
|
|
|
|
text: 'Strategy deprecated successfully',
|
|
|
|
});
|
2022-02-25 10:55:39 +01:00
|
|
|
} catch (error: unknown) {
|
|
|
|
setToastApiError(formatUnknownError(error));
|
2022-02-11 00:08:55 +01:00
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2022-05-04 15:16:34 +02:00
|
|
|
const onDeleteStrategy = (strategy: IStrategy) => {
|
2022-02-11 00:08:55 +01:00
|
|
|
setDialogueMetaData({
|
|
|
|
show: true,
|
|
|
|
title: 'Really delete strategy?',
|
|
|
|
onConfirm: async () => {
|
|
|
|
try {
|
|
|
|
await removeStrategy(strategy);
|
|
|
|
refetchStrategies();
|
|
|
|
setToastData({
|
|
|
|
type: 'success',
|
|
|
|
title: 'Success',
|
|
|
|
text: 'Strategy deleted successfully',
|
|
|
|
});
|
2022-02-25 10:55:39 +01:00
|
|
|
} catch (error: unknown) {
|
|
|
|
setToastApiError(formatUnknownError(error));
|
2022-02-11 00:08:55 +01:00
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2022-05-04 15:16:34 +02:00
|
|
|
const reactivateButton = (strategy: IStrategy) => (
|
2022-04-21 08:26:49 +02:00
|
|
|
<PermissionIconButton
|
|
|
|
onClick={() => onReactivateStrategy(strategy)}
|
|
|
|
permission={UPDATE_STRATEGY}
|
2022-05-09 15:17:20 +02:00
|
|
|
tooltipProps={{ title: 'Reactivate activation strategy' }}
|
2022-04-21 08:26:49 +02:00
|
|
|
>
|
|
|
|
<VisibilityOff />
|
|
|
|
</PermissionIconButton>
|
2021-03-30 15:14:02 +02:00
|
|
|
);
|
|
|
|
|
2022-05-04 15:16:34 +02:00
|
|
|
const deprecateButton = (strategy: IStrategy) => (
|
2021-03-30 15:14:02 +02:00
|
|
|
<ConditionallyRender
|
|
|
|
condition={strategy.name === 'default'}
|
|
|
|
show={
|
2022-05-24 10:58:06 +02:00
|
|
|
<Tooltip title="You cannot deprecate the default strategy">
|
2021-03-30 15:14:02 +02:00
|
|
|
<div>
|
2022-05-02 15:52:41 +02:00
|
|
|
<IconButton disabled size="large">
|
2022-03-23 09:12:10 +01:00
|
|
|
<Visibility titleAccess="Deprecate strategy" />
|
2021-03-30 15:14:02 +02:00
|
|
|
</IconButton>
|
|
|
|
</div>
|
|
|
|
</Tooltip>
|
|
|
|
}
|
|
|
|
elseShow={
|
2021-10-21 13:25:39 +02:00
|
|
|
<div>
|
|
|
|
<PermissionIconButton
|
2022-02-11 00:08:55 +01:00
|
|
|
onClick={() => onDeprecateStrategy(strategy)}
|
2021-10-21 13:25:39 +02:00
|
|
|
permission={UPDATE_STRATEGY}
|
2022-05-09 15:17:20 +02:00
|
|
|
tooltipProps={{ title: 'Deprecate strategy' }}
|
2021-10-21 13:25:39 +02:00
|
|
|
>
|
2022-04-21 08:26:49 +02:00
|
|
|
<Visibility />
|
2021-10-21 13:25:39 +02:00
|
|
|
</PermissionIconButton>
|
|
|
|
</div>
|
2021-03-30 15:14:02 +02:00
|
|
|
}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
|
2022-05-04 15:16:34 +02:00
|
|
|
const editButton = (strategy: IStrategy) => (
|
2021-03-30 15:14:02 +02:00
|
|
|
<ConditionallyRender
|
2022-03-04 23:39:41 +01:00
|
|
|
condition={strategy?.editable}
|
|
|
|
show={
|
|
|
|
<PermissionIconButton
|
|
|
|
onClick={() =>
|
2022-05-05 13:42:18 +02:00
|
|
|
navigate(`/strategies/${strategy?.name}/edit`)
|
2022-03-04 23:39:41 +01:00
|
|
|
}
|
|
|
|
permission={UPDATE_STRATEGY}
|
2022-05-09 15:17:20 +02:00
|
|
|
tooltipProps={{ title: 'Edit strategy' }}
|
2022-03-04 23:39:41 +01:00
|
|
|
>
|
2022-04-21 08:26:49 +02:00
|
|
|
<Edit />
|
2022-03-04 23:39:41 +01:00
|
|
|
</PermissionIconButton>
|
|
|
|
}
|
|
|
|
elseShow={
|
2022-05-24 10:58:06 +02:00
|
|
|
<Tooltip title="You cannot edit a built-in strategy" arrow>
|
2022-03-04 23:39:41 +01:00
|
|
|
<div>
|
2022-05-02 15:52:41 +02:00
|
|
|
<IconButton disabled size="large">
|
2022-03-04 23:39:41 +01:00
|
|
|
<Edit titleAccess="Edit strategy" />
|
|
|
|
</IconButton>
|
|
|
|
</div>
|
|
|
|
</Tooltip>
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
|
2022-05-04 15:16:34 +02:00
|
|
|
const deleteButton = (strategy: IStrategy) => (
|
2022-03-04 23:39:41 +01:00
|
|
|
<ConditionallyRender
|
|
|
|
condition={strategy?.editable}
|
2021-03-30 15:14:02 +02:00
|
|
|
show={
|
2021-11-29 14:29:58 +01:00
|
|
|
<PermissionIconButton
|
2022-02-11 00:08:55 +01:00
|
|
|
onClick={() => onDeleteStrategy(strategy)}
|
2021-11-29 14:29:58 +01:00
|
|
|
permission={DELETE_STRATEGY}
|
2022-05-09 15:17:20 +02:00
|
|
|
tooltipProps={{ title: 'Delete strategy' }}
|
2021-11-29 14:29:58 +01:00
|
|
|
>
|
|
|
|
<Delete />
|
|
|
|
</PermissionIconButton>
|
2021-03-30 15:14:02 +02:00
|
|
|
}
|
|
|
|
elseShow={
|
2022-05-09 15:17:20 +02:00
|
|
|
<Tooltip title="You cannot delete a built-in strategy" arrow>
|
2021-03-30 15:14:02 +02:00
|
|
|
<div>
|
2022-05-02 15:52:41 +02:00
|
|
|
<IconButton disabled size="large">
|
2022-04-21 08:26:49 +02:00
|
|
|
<Delete titleAccess="Delete strategy" />
|
2021-03-30 15:14:02 +02:00
|
|
|
</IconButton>
|
|
|
|
</div>
|
|
|
|
</Tooltip>
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
|
2021-05-10 13:22:22 +02:00
|
|
|
const onDialogConfirm = () => {
|
|
|
|
dialogueMetaData?.onConfirm();
|
2022-05-24 10:58:06 +02:00
|
|
|
setDialogueMetaData((prev: IDialogueMetaData) => ({
|
|
|
|
...prev,
|
|
|
|
show: false,
|
|
|
|
}));
|
2021-05-10 13:22:22 +02:00
|
|
|
};
|
|
|
|
|
2021-03-30 15:14:02 +02:00
|
|
|
return (
|
2021-04-23 15:21:24 +02:00
|
|
|
<PageContent
|
2022-05-24 10:58:06 +02:00
|
|
|
isLoading={loading}
|
|
|
|
header={
|
|
|
|
<PageHeader
|
|
|
|
title="Strategies"
|
|
|
|
actions={
|
|
|
|
<>
|
|
|
|
<TableSearch
|
|
|
|
initialValue={globalFilter}
|
|
|
|
onChange={setGlobalFilter}
|
|
|
|
/>
|
|
|
|
<PageHeader.Divider />
|
|
|
|
<AddStrategyButton />
|
|
|
|
</>
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
}
|
2021-04-23 15:21:24 +02:00
|
|
|
>
|
2022-05-24 10:58:06 +02:00
|
|
|
<SearchHighlightProvider value={globalFilter}>
|
|
|
|
<Table {...getTableProps()}>
|
|
|
|
<SortableTableHeader headerGroups={headerGroups} />
|
|
|
|
<TableBody {...getTableBodyProps()}>
|
|
|
|
{rows.map(row => {
|
|
|
|
prepareRow(row);
|
|
|
|
return (
|
|
|
|
<TableRow hover {...row.getRowProps()}>
|
|
|
|
{row.cells.map(cell => (
|
|
|
|
<TableCell {...cell.getCellProps()}>
|
|
|
|
{cell.render('Cell')}
|
|
|
|
</TableCell>
|
|
|
|
))}
|
|
|
|
</TableRow>
|
|
|
|
);
|
|
|
|
})}
|
|
|
|
</TableBody>
|
|
|
|
</Table>
|
|
|
|
</SearchHighlightProvider>
|
|
|
|
<ConditionallyRender
|
|
|
|
condition={rows.length === 0}
|
|
|
|
show={
|
|
|
|
<ConditionallyRender
|
|
|
|
condition={globalFilter?.length > 0}
|
|
|
|
show={
|
|
|
|
<TablePlaceholder>
|
|
|
|
No strategies found matching “
|
|
|
|
{globalFilter}
|
|
|
|
”
|
|
|
|
</TablePlaceholder>
|
|
|
|
}
|
|
|
|
elseShow={
|
|
|
|
<TablePlaceholder>
|
|
|
|
No strategies available. Get started by adding
|
|
|
|
one.
|
|
|
|
</TablePlaceholder>
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
|
2021-05-10 13:22:22 +02:00
|
|
|
<Dialogue
|
|
|
|
open={dialogueMetaData.show}
|
|
|
|
onClick={onDialogConfirm}
|
|
|
|
title={dialogueMetaData?.title}
|
|
|
|
onClose={() =>
|
2022-05-24 10:58:06 +02:00
|
|
|
setDialogueMetaData((prev: IDialogueMetaData) => ({
|
|
|
|
...prev,
|
|
|
|
show: false,
|
|
|
|
}))
|
2021-05-10 13:22:22 +02:00
|
|
|
}
|
|
|
|
/>
|
2021-03-30 15:14:02 +02:00
|
|
|
</PageContent>
|
|
|
|
);
|
|
|
|
};
|