import { useState, useMemo, useCallback, type FC } from 'react'; import { useNavigate } from 'react-router-dom'; import { Alert, Box, Link, Typography, styled } from '@mui/material'; import Extension from '@mui/icons-material/Extension'; 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 type { IStrategy } from 'interfaces/strategy'; import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell'; import { sortTypes } from 'utils/sortTypes'; import { useTable, useSortBy } from 'react-table'; import { StrategySwitch } from './StrategySwitch/StrategySwitch'; import { StrategyEditButton } from './StrategyEditButton/StrategyEditButton'; import { StrategyDeleteButton } from './StrategyDeleteButton/StrategyDeleteButton'; import { Badge } from 'component/common/Badge/Badge'; import { HelpIcon } from 'component/common/HelpIcon/HelpIcon'; import { CustomStrategyInfo } from '../CustomStrategyInfo/CustomStrategyInfo'; import { AddStrategyButton } from './AddStrategyButton/AddStrategyButton'; interface IDialogueMetaData { show: boolean; title: string; onConfirm: () => void; } const StyledBox = styled(Box)(({ theme }) => ({ display: 'flex', flexDirection: 'column', gap: theme.spacing(2), })); const StyledBadge = styled(Badge)(({ theme }) => ({ marginLeft: theme.spacing(1), display: 'inline-block', })); const StyledTypography = styled(Typography)(({ theme }) => ({ display: 'flex', fontSize: theme.fontSizes.mainHeader, })); const Subtitle: FC<{ title: string; description: string; link: string; }> = ({ title, description, link }) => ( {title} ({ marginBottom: theme.spacing(1) })} > {description} Read more in the documentation } /> ); const CustomStrategyTitle: FC = () => ( ({ display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: theme.spacing(1.5), })} > ); const PredefinedStrategyTitle = () => ( ); const StrategyDeprecationWarning = () => ( Custom strategies are deprecated and may be removed in a future major version. We recommend not using custom strategies going forward and instead using the predefined strategies with{' '} constraints . If you have a need for custom strategies that you cannot support with constraints, please reach out to us. ); 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: () => ( ), }, { 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, disableSortBy: true, }, { accessor: 'description', disableSortBy: true, }, { accessor: 'sortOrder', sortType: 'number', }, ], [onToggle, onEditStrategy, onDeleteStrategy], ); const initialState = useMemo( () => ({ sortBy: [{ id: 'Name', desc: false }], hiddenColumns: ['description', 'sortOrder'], }), [], ); const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } = useTable( { columns: columns as any[], // TODO: fix after `react-table` v8 update data: data.predefined, initialState, sortTypes, autoResetSortBy: false, disableSortRemove: true, autoResetHiddenColumns: false, }, useSortBy, ); const { getTableProps: customGetTableProps, getTableBodyProps: customGetTableBodyProps, headerGroups: customHeaderGroups, rows: customRows, prepareRow: customPrepareRow, } = useTable( { columns: columns as any[], // TODO: fix after `react-table` v8 update data: data.custom, initialState, sortTypes, autoResetSortBy: false, disableSortRemove: true, autoResetHiddenColumns: false, }, useSortBy, ); const onDialogConfirm = () => { dialogueMetaData?.onConfirm(); setDialogueMetaData((prev: IDialogueMetaData) => ({ ...prev, show: false, })); }; return ( } > {rows.map((row) => { prepareRow(row); return ( {row.cells.map((cell) => ( {cell.render('Cell')} ))} ); })}
No strategies available. } />
setDialogueMetaData((prev: IDialogueMetaData) => ({ ...prev, show: false, })) } />
} > {customRows.map((row) => { customPrepareRow(row); return ( {row.cells.map((cell) => ( {cell.render('Cell')} ))} ); })}
} />
setDialogueMetaData((prev: IDialogueMetaData) => ({ ...prev, show: false, })) } />
); };