import { useState, useMemo, useCallback, FC } from 'react'; import { useNavigate } from 'react-router-dom'; import { Box, Link, Typography, styled } from '@mui/material'; import { Extension } from '@mui/icons-material'; import { Table, SortableTableHeader, TableBody, TableCell, TableRow, TablePlaceholder, } from 'component/common/Table'; import { ActionCell } from 'component/common/Table/cells/ActionCell/ActionCell'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { PageContent } from 'component/common/PageContent/PageContent'; import { PageHeader } from 'component/common/PageHeader/PageHeader'; import { Dialogue } from 'component/common/Dialogue/Dialogue'; import { formatStrategyName } from 'utils/strategyNames'; import { useStrategies } from 'hooks/api/getters/useStrategies/useStrategies'; import useStrategiesApi from 'hooks/api/actions/useStrategiesApi/useStrategiesApi'; import useToast from 'hooks/useToast'; import { formatUnknownError } from 'utils/formatUnknownError'; import { IStrategy } from 'interfaces/strategy'; 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 { StrategySwitch } from './StrategySwitch/StrategySwitch'; import { StrategyEditButton } from './StrategyEditButton/StrategyEditButton'; import { StrategyDeleteButton } from './StrategyDeleteButton/StrategyDeleteButton'; import { Search } from 'component/common/Search/Search'; import { Badge } from 'component/common/Badge/Badge'; import { HelpIcon } from 'component/common/HelpIcon/HelpIcon'; import { CustomStrategyInfo } from '../CustomStrategyInfo/CustomStrategyInfo'; interface IDialogueMetaData { show: boolean; title: string; onConfirm: () => void; } const StyledBadge = styled(Badge)(({ theme }) => ({ marginLeft: theme.spacing(1), display: 'inline-block', })); const Subtitle: FC<{ title: string; description: string; link: string; }> = ({ title, description, link }) => ( {title} ({ marginBottom: theme.spacing(1) })} > {description} Read more in the documentation } /> ); const PredefinedStrategyTitle = () => ( ({ marginBottom: theme.spacing(1.5) })}> ); const CustomStrategyTitle: FC = () => ( ({ display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: theme.spacing(1.5), })} > ); export const StrategiesList = () => { const navigate = useNavigate(); const [dialogueMetaData, setDialogueMetaData] = useState( { show: false, title: '', onConfirm: () => {}, } ); const { strategies, refetchStrategies, loading } = useStrategies(); const { removeStrategy, deprecateStrategy, reactivateStrategy } = useStrategiesApi(); const { setToastData, setToastApiError } = useToast(); const data = useMemo(() => { if (loading) { const mock = Array(5).fill({ name: 'Context name', description: 'Context description when loading', }); return { all: mock, predefined: mock, custom: mock, }; } const all = strategies.map( ({ name, description, editable, deprecated }) => ({ name, description, editable, deprecated, }) ); return { all, predefined: all.filter(strategy => !strategy.editable), custom: all.filter(strategy => strategy.editable), }; }, [strategies, loading]); const onToggle = useCallback( (strategy: IStrategy) => (deprecated: boolean) => { if (deprecated) { setDialogueMetaData({ show: true, title: 'Really reactivate strategy?', onConfirm: async () => { try { await reactivateStrategy(strategy); refetchStrategies(); setToastData({ type: 'success', title: 'Success', text: 'Strategy reactivated successfully', }); } catch (error: unknown) { setToastApiError(formatUnknownError(error)); } }, }); } else { setDialogueMetaData({ show: true, title: 'Really deprecate strategy?', onConfirm: async () => { try { await deprecateStrategy(strategy); refetchStrategies(); setToastData({ type: 'success', title: 'Success', text: 'Strategy deprecated successfully', }); } catch (error: unknown) { setToastApiError(formatUnknownError(error)); } }, }); } }, [ deprecateStrategy, reactivateStrategy, refetchStrategies, setToastApiError, setToastData, ] ); const onDeleteStrategy = useCallback( (strategy: IStrategy) => { setDialogueMetaData({ show: true, title: 'Really delete strategy?', onConfirm: async () => { try { await removeStrategy(strategy); refetchStrategies(); setToastData({ type: 'success', title: 'Success', text: 'Strategy deleted successfully', }); } catch (error: unknown) { setToastApiError(formatUnknownError(error)); } }, }); }, [removeStrategy, refetchStrategies, setToastApiError, setToastData] ); const onEditStrategy = useCallback( (strategy: IStrategy) => { navigate(`/strategies/${strategy.name}/edit`); }, [navigate] ); const columns = useMemo( () => [ { id: 'Icon', Cell: () => ( ), disableGlobalFilter: true, }, { id: 'Name', Header: 'Name', accessor: (row: any) => formatStrategyName(row.name), width: '90%', Cell: ({ row: { original: { name, description, deprecated }, }, }: any) => { return ( ( Disabled )} /> ); }, sortType: 'alphanumeric', }, { Header: 'Actions', id: 'Actions', align: 'center', Cell: ({ row: { original } }: any) => ( onEditStrategy(original)} /> onDeleteStrategy(original) } /> } /> ), width: 150, minWidth: 120, disableGlobalFilter: true, disableSortBy: true, }, { accessor: 'description', disableSortBy: true, }, { accessor: 'sortOrder', disableGlobalFilter: true, sortType: 'number', }, ], [onToggle, onEditStrategy, onDeleteStrategy] ); 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: data.predefined, initialState, sortTypes, autoResetGlobalFilter: false, autoResetSortBy: false, disableSortRemove: true, }, useGlobalFilter, useSortBy ); const { getTableProps: customGetTableProps, getTableBodyProps: customGetTableBodyProps, headerGroups: customHeaderGroups, rows: customRows, prepareRow: customPrepareRow, setGlobalFilter: customSetGlobalFilter, } = useTable( { columns: columns as any[], // TODO: fix after `react-table` v8 update data: data.custom, initialState, sortTypes, autoResetGlobalFilter: false, autoResetSortBy: false, disableSortRemove: true, }, useGlobalFilter, useSortBy ); const onDialogConfirm = () => { dialogueMetaData?.onConfirm(); setDialogueMetaData((prev: IDialogueMetaData) => ({ ...prev, show: false, })); }; let strategyTypeCount = rows.length; return ( { setGlobalFilter(...props); customSetGlobalFilter(...props); }} /> } /> } > ({ paddingBottom: theme.spacing(4) })}> {rows.map(row => { prepareRow(row); return ( {row.cells.map(cell => ( {cell.render('Cell')} ))} ); })}
0} show={ No predefined strategies found matching “ {globalFilter} ” } elseShow={ No strategies available. } /> } />
{customRows.map(row => { customPrepareRow(row); return ( {row.cells.map(cell => ( {cell.render('Cell')} ))} ); })}
0} show={ No custom strategies found matching “ {globalFilter} ” } elseShow={} /> } />
setDialogueMetaData((prev: IDialogueMetaData) => ({ ...prev, show: false, })) } />
); };