import { PageContent } from 'component/common/PageContent/PageContent'; import { PageHeader } from 'component/common/PageHeader/PageHeader'; import { TablePlaceholder, VirtualizedTable } from 'component/common/Table'; import { SortingRule, useFlexLayout, useRowSelect, useSortBy, useTable, } from 'react-table'; import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext'; import { Checkbox, useMediaQuery } from '@mui/material'; import { sortTypes } from 'utils/sortTypes'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { HighlightCell } from 'component/common/Table/cells/HighlightCell/HighlightCell'; import { DateCell } from 'component/common/Table/cells/DateCell/DateCell'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { Search } from 'component/common/Search/Search'; import { FeatureTypeCell } from 'component/common/Table/cells/FeatureTypeCell/FeatureTypeCell'; import { FeatureSeenCell } from 'component/common/Table/cells/FeatureSeenCell/FeatureSeenCell'; import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell'; import { FeatureStaleCell } from 'component/feature/FeatureToggleList/FeatureStaleCell/FeatureStaleCell'; import { ArchivedFeatureActionCell } from 'component/archive/ArchiveTable/ArchivedFeatureActionCell/ArchivedFeatureActionCell'; import { featuresPlaceholder } from 'component/feature/FeatureToggleList/FeatureToggleListTable'; import theme from 'themes/theme'; import { FeatureSchema } from 'openapi'; import { useFeatureArchiveApi } from 'hooks/api/actions/useFeatureArchiveApi/useReviveFeatureApi'; import useToast from 'hooks/useToast'; import { formatUnknownError } from 'utils/formatUnknownError'; import { useSearch } from 'hooks/useSearch'; import { FeatureArchivedCell } from './FeatureArchivedCell/FeatureArchivedCell'; import { useSearchParams } from 'react-router-dom'; import { ArchivedFeatureDeleteConfirm } from './ArchivedFeatureActionCell/ArchivedFeatureDeleteConfirm/ArchivedFeatureDeleteConfirm'; import { IFeatureToggle } from 'interfaces/featureToggle'; import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColumns'; import { RowSelectCell } from '../../project/Project/ProjectFeatureToggles/RowSelectCell/RowSelectCell'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import { BatchSelectionActionsBar } from '../../common/BatchSelectionActionsBar/BatchSelectionActionsBar'; import { ArchiveBatchActions } from './ArchiveBatchActions'; export interface IFeaturesArchiveTableProps { archivedFeatures: FeatureSchema[]; title: string; refetch: () => void; loading: boolean; storedParams: SortingRule; setStoredParams: ( newValue: | SortingRule | ((prev: SortingRule) => SortingRule) ) => SortingRule; projectId?: string; } export const ArchiveTable = ({ archivedFeatures = [], loading, refetch, storedParams, setStoredParams, title, projectId, }: IFeaturesArchiveTableProps) => { const isSmallScreen = useMediaQuery(theme.breakpoints.down('md')); const isMediumScreen = useMediaQuery(theme.breakpoints.down('lg')); const { setToastData, setToastApiError } = useToast(); const { uiConfig } = useUiConfig(); const [deleteModalOpen, setDeleteModalOpen] = useState(false); const [deletedFeature, setDeletedFeature] = useState(); const [searchParams, setSearchParams] = useSearchParams(); const { reviveFeature } = useFeatureArchiveApi(); const [searchValue, setSearchValue] = useState( searchParams.get('search') || '' ); const onRevive = useCallback( async (feature: string) => { try { await reviveFeature(feature); await refetch(); setToastData({ type: 'success', title: "And we're back!", text: 'The feature toggle has been revived.', }); } catch (e: unknown) { setToastApiError(formatUnknownError(e)); } }, [refetch, reviveFeature, setToastApiError, setToastData] ); const columns = useMemo( () => [ { id: 'Select', Header: ({ getToggleAllRowsSelectedProps }: any) => ( ), Cell: ({ row }: any) => ( ), maxWidth: 50, disableSortBy: true, hideInMenu: true, }, { Header: 'Seen', width: 85, canSort: true, Cell: FeatureSeenCell, accessor: 'lastSeenAt', align: 'center', }, { Header: 'Type', accessor: 'type', width: 85, canSort: true, Cell: FeatureTypeCell, align: 'center', }, { Header: 'Name', accessor: 'name', searchable: true, minWidth: 100, Cell: ({ value, row: { original } }: any) => ( ), sortType: 'alphanumeric', }, { Header: 'Created', accessor: 'createdAt', width: 150, Cell: DateCell, sortType: 'date', }, { Header: 'Archived', accessor: 'archivedAt', width: 150, Cell: FeatureArchivedCell, sortType: 'date', }, ...(!projectId ? [ { Header: 'Project ID', accessor: 'project', sortType: 'alphanumeric', filterName: 'project', searchable: true, maxWidth: 170, Cell: ({ value }: any) => ( ), }, ] : []), { Header: 'State', accessor: 'stale', Cell: FeatureStaleCell, sortType: 'boolean', maxWidth: 120, filterName: 'state', filterParsing: (value: any) => (value ? 'stale' : 'active'), }, { Header: 'Actions', id: 'Actions', align: 'center', maxWidth: 120, canSort: false, Cell: ({ row: { original: feature } }: any) => ( onRevive(feature.name)} onDelete={() => { setDeletedFeature(feature); setDeleteModalOpen(true); }} /> ), }, // Always hidden -- for search { accessor: 'description', header: 'Description', searchable: true, }, ], //eslint-disable-next-line [projectId] ); const { data: searchedData, getSearchText, getSearchContext, } = useSearch(columns, searchValue, archivedFeatures); const data = useMemo( () => (loading ? featuresPlaceholder : searchedData), [searchedData, loading] ); const [initialState] = useState(() => ({ sortBy: [ { id: searchParams.get('sort') || storedParams.id, desc: searchParams.has('order') ? searchParams.get('order') === 'desc' : storedParams.desc, }, ], hiddenColumns: ['description'], selectedRowIds: {}, })); const getRowId = useCallback((row: any) => row.name, []); const { headerGroups, rows, state: { sortBy, selectedRowIds }, prepareRow, setHiddenColumns, } = useTable( { columns: columns as any[], // TODO: fix after `react-table` v8 update data, initialState, sortTypes, autoResetHiddenColumns: false, disableSortRemove: true, autoResetSortBy: false, getRowId, }, useFlexLayout, useSortBy, useRowSelect ); useConditionallyHiddenColumns( [ { condition: isSmallScreen, columns: ['type', 'createdAt'], }, { condition: isMediumScreen, columns: ['lastSeenAt', 'stale'], }, ], setHiddenColumns, columns ); useEffect(() => { if (loading) { return; } const tableState: Record = {}; tableState.sort = sortBy[0].id; if (sortBy[0].desc) { tableState.order = 'desc'; } if (searchValue) { tableState.search = searchValue; } setSearchParams(tableState, { replace: true, }); setStoredParams({ id: sortBy[0].id, desc: sortBy[0].desc || false }); }, [loading, sortBy, searchValue]); // eslint-disable-line react-hooks/exhaustive-deps return ( <> } /> } > ( 0} show={ No feature toggles found matching “ {searchValue}” } elseShow={ None of the feature toggles were archived yet. } /> )} /> } /> ); };