import { useCallback, useEffect, useMemo, useState, type VFC } from 'react'; import { Box, Button, IconButton, Link, Tooltip, useMediaQuery, useTheme, } from '@mui/material'; import { Link as RouterLink } from 'react-router-dom'; import { createColumnHelper, useReactTable } from '@tanstack/react-table'; import { PaginatedTable, TablePlaceholder } from 'component/common/Table'; import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext'; import { DateCell } from 'component/common/Table/cells/DateCell/DateCell'; import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell'; import { FeatureTypeCell } from 'component/common/Table/cells/FeatureTypeCell/FeatureTypeCell'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { PageContent } from 'component/common/PageContent/PageContent'; import { PageHeader } from 'component/common/PageHeader/PageHeader'; import type { FeatureSchema, FeatureSearchResponseSchema } from 'openapi'; import { FeatureStaleCell } from './FeatureStaleCell/FeatureStaleCell'; import { Search } from 'component/common/Search/Search'; import { useFavoriteFeaturesApi } from 'hooks/api/actions/useFavoriteFeaturesApi/useFavoriteFeaturesApi'; import { FavoriteIconCell } from 'component/common/Table/cells/FavoriteIconCell/FavoriteIconCell'; import { FavoriteIconHeader } from 'component/common/Table/FavoriteIconHeader/FavoriteIconHeader'; import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments'; import { ExportDialog } from './ExportDialog'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import { focusable } from 'themes/themeStyles'; import { FeatureEnvironmentSeenCell } from 'component/common/Table/cells/FeatureSeenCell/FeatureEnvironmentSeenCell'; import useToast from 'hooks/useToast'; import { FeatureToggleFilters } from './FeatureToggleFilters/FeatureToggleFilters'; import { DEFAULT_PAGE_LIMIT, useFeatureSearch, } from 'hooks/api/getters/useFeatureSearch/useFeatureSearch'; import mapValues from 'lodash.mapvalues'; import { BooleansStringParam, FilterItemParam, } from 'utils/serializeQueryParams'; import { encodeQueryParams, NumberParam, StringParam, withDefault, } from 'use-query-params'; import { withTableState } from 'utils/withTableState'; import { usePersistentTableState } from 'hooks/usePersistentTableState'; import { FeatureTagCell } from 'component/common/Table/cells/FeatureTagCell/FeatureTagCell'; import { FeatureSegmentCell } from 'component/common/Table/cells/FeatureSegmentCell/FeatureSegmentCell'; import { useUiFlag } from 'hooks/useUiFlag'; import { FeatureToggleListActions } from './FeatureToggleListActions/FeatureToggleListActions'; import useLoading from 'hooks/useLoading'; import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; import { useFeedback } from '../../feedbackNew/useFeedback'; import ReviewsOutlined from '@mui/icons-material/ReviewsOutlined'; export const featuresPlaceholder = Array(15).fill({ name: 'Name of the feature', description: 'Short description of the feature', type: '-', createdAt: new Date(2022, 1, 1), project: 'projectID', }); const columnHelper = createColumnHelper(); const feedbackCategory = 'search'; export const FeatureToggleListTable: VFC = () => { const theme = useTheme(); const featureSearchFeedback = useUiFlag('featureSearchFeedback'); const { trackEvent } = usePlausibleTracker(); const { environments } = useEnvironments(); const enabledEnvironments = environments .filter((env) => env.enabled) .map((env) => env.name); const isSmallScreen = useMediaQuery(theme.breakpoints.down('md')); const isMediumScreen = useMediaQuery(theme.breakpoints.down('lg')); const [showExportDialog, setShowExportDialog] = useState(false); const { setToastApiError } = useToast(); const { uiConfig } = useUiConfig(); const variant = featureSearchFeedback !== false ? featureSearchFeedback?.name ?? '' : ''; const { openFeedback } = useFeedback( feedbackCategory, 'automatic', variant, ); const stateConfig = { offset: withDefault(NumberParam, 0), limit: withDefault(NumberParam, DEFAULT_PAGE_LIMIT), query: StringParam, favoritesFirst: withDefault(BooleansStringParam, true), sortBy: withDefault(StringParam, 'createdAt'), sortOrder: withDefault(StringParam, 'desc'), project: FilterItemParam, tag: FilterItemParam, state: FilterItemParam, segment: FilterItemParam, createdAt: FilterItemParam, }; const [tableState, setTableState] = usePersistentTableState( 'features-list-table', stateConfig, ); const { offset, limit, query, favoritesFirst, sortBy, sortOrder, ...filterState } = tableState; const { features = [], total, loading, refetch: refetchFeatures, initialLoad, } = useFeatureSearch( mapValues(encodeQueryParams(stateConfig, tableState), (value) => value ? `${value}` : undefined, ), ); const bodyLoadingRef = useLoading(loading); const { favorite, unfavorite } = useFavoriteFeaturesApi(); const onFavorite = useCallback( async (feature: FeatureSchema) => { try { if (feature?.favorite) { await unfavorite(feature.project!, feature.name); } else { await favorite(feature.project!, feature.name); } refetchFeatures(); } catch (error) { setToastApiError( 'Something went wrong, could not update favorite', ); } }, [favorite, refetchFeatures, unfavorite, setToastApiError], ); const columns = useMemo( () => [ columnHelper.accessor('favorite', { header: () => ( setTableState({ favoritesFirst: !favoritesFirst, }) } /> ), cell: ({ getValue, row }) => ( <> onFavorite(row.original)} /> ), enableSorting: false, meta: { width: '1%', }, }), columnHelper.accessor('lastSeenAt', { header: 'Seen', cell: ({ row }) => ( ), meta: { align: 'center', width: '1%', }, }), columnHelper.accessor('type', { header: 'Type', cell: ({ getValue }) => , meta: { align: 'center', width: '1%', }, }), columnHelper.accessor('name', { header: 'Name', // cell: (cell) => , cell: ({ row }) => ( ), meta: { width: '50%', }, }), columnHelper.accessor((row) => row.segments?.join('\n') || '', { header: 'Segments', cell: ({ getValue, row }) => ( ), enableSorting: false, meta: { width: '1%', }, }), columnHelper.accessor( (row) => row.tags ?.map(({ type, value }) => `${type}:${value}`) .join('\n') || '', { header: 'Tags', cell: FeatureTagCell, enableSorting: false, meta: { width: '1%', }, }, ), columnHelper.accessor('createdAt', { header: 'Created', cell: ({ getValue }) => , meta: { width: '1%', }, }), columnHelper.accessor('project', { header: 'Project ID', cell: ({ getValue }) => ( ), meta: { width: '1%', }, }), columnHelper.accessor('stale', { header: 'State', cell: ({ getValue }) => , meta: { width: '1%', }, }), ], [favoritesFirst], ); const data = useMemo( () => features?.length === 0 && loading ? featuresPlaceholder : features, [initialLoad, features, loading], ); const table = useReactTable( withTableState(tableState, setTableState, { columns, data, }), ); useEffect(() => { if (isSmallScreen) { table.setColumnVisibility({ type: false, createdAt: false, tags: false, lastSeenAt: false, stale: false, }); } else if (isMediumScreen) { table.setColumnVisibility({ lastSeenAt: false, stale: false, }); } else { table.setColumnVisibility({}); } }, [isSmallScreen, isMediumScreen]); const setSearchValue = (query = '') => { setTableState({ query }); trackEvent('search-bar', { props: { screen: 'features', length: query.length, }, }); }; const rows = table.getRowModel().rows; if (!(environments.length > 0)) { return null; } const createFeedbackContext = () => { openFeedback({ title: 'How easy was it to use search and filters?', positiveLabel: 'What do you like most about search and filters?', areasForImprovementsLabel: 'What should be improved in search and filters page?', }); }; return ( } /> { trackEvent('search-feature-buttons', { props: { action: 'archive', }, }); }} > View archive setShowExportDialog(true)} /> {featureSearchFeedback !== false && featureSearchFeedback?.enabled && ( <> } /> } onClick={ createFeedbackContext } > Provide feedback } />{' '} } onClick={ createFeedbackContext } variant='outlined' > Provide feedback } /> )} } > } /> } >
({ padding: theme.spacing(0, 2, 2) })}> 0} show={ No feature toggles found matching “ {query} ” } elseShow={ No feature toggles found matching your criteria. Get started by adding a new feature toggle. } /> } /> setShowExportDialog(false)} environments={enabledEnvironments} /> } />
); };