diff --git a/frontend/src/component/common/Table/cells/FeatureSegmentCell/FeatureSegmentCell.tsx b/frontend/src/component/common/Table/cells/FeatureSegmentCell/FeatureSegmentCell.tsx new file mode 100644 index 000000000000..df667093ed40 --- /dev/null +++ b/frontend/src/component/common/Table/cells/FeatureSegmentCell/FeatureSegmentCell.tsx @@ -0,0 +1,54 @@ +import { VFC } from 'react'; +import { FeatureSchema, FeatureSearchResponseSchema } from 'openapi'; +import { styled, Typography } from '@mui/material'; +import { TextCell } from '../TextCell/TextCell'; +import { Highlighter } from 'component/common/Highlighter/Highlighter'; +import { useSearchHighlightContext } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext'; +import { TooltipLink } from 'component/common/TooltipLink/TooltipLink'; + +const StyledTag = styled(Typography)(({ theme }) => ({ + fontSize: theme.fontSizes.smallerBody, +})); + +interface IFeatureSegmentCellProps { + row: { + original: FeatureSearchResponseSchema; + }; + value: string; +} + +export const FeatureSegmentCell: VFC = ({ + row, + value, +}) => { + const { searchQuery } = useSearchHighlightContext(); + + if (!row.original.segments || row.original.segments.length === 0) + return ; + + return ( + + 0 && + value.toLowerCase().includes(searchQuery.toLowerCase()) + } + tooltip={ + <> + {row.original.segments?.map((segment) => ( + + + {segment} + + + ))} + + } + > + {row.original.segments?.length === 1 + ? '1 segment' + : `${row.original.segments?.length} segments`} + + + ); +}; diff --git a/frontend/src/component/feature/FeatureToggleList/FeatureToggleListTable.tsx b/frontend/src/component/feature/FeatureToggleList/FeatureToggleListTable.tsx index 5ee6c4a44256..9e92d9cf3cb0 100644 --- a/frontend/src/component/feature/FeatureToggleList/FeatureToggleListTable.tsx +++ b/frontend/src/component/feature/FeatureToggleList/FeatureToggleListTable.tsx @@ -17,7 +17,7 @@ import { FeatureTypeCell } from 'component/common/Table/cells/FeatureTypeCell/Fe import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { PageContent } from 'component/common/PageContent/PageContent'; import { PageHeader } from 'component/common/PageHeader/PageHeader'; -import { FeatureSchema } from 'openapi'; +import { FeatureSchema, FeatureSearchResponseSchema } from 'openapi'; import { CreateFeatureButton } from '../CreateFeatureButton/CreateFeatureButton'; import { FeatureStaleCell } from './FeatureStaleCell/FeatureStaleCell'; import { Search } from 'component/common/Search/Search'; @@ -49,6 +49,8 @@ import { } 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'; export const featuresPlaceholder = Array(15).fill({ name: 'Name of the feature', @@ -58,7 +60,7 @@ export const featuresPlaceholder = Array(15).fill({ project: 'projectID', }); -const columnHelper = createColumnHelper(); +const columnHelper = createColumnHelper(); export const FeatureToggleListTable: VFC = () => { const theme = useTheme(); @@ -168,18 +170,24 @@ export const FeatureToggleListTable: VFC = () => { /> ), }), - // columnHelper.accessor( - // (row) => - // row.tags - // ?.map(({ type, value }) => `${type}:${value}`) - // .join('\n') || '', - // { - // header: 'Tags', - // cell: ({ getValue, row }) => ( - // - // ), - // }, - // ), + columnHelper.accessor((row) => row.segments?.join('\n') || '', { + header: 'Segments', + cell: ({ getValue, row }) => ( + + ), + }), + columnHelper.accessor( + (row) => + row.tags + ?.map(({ type, value }) => `${type}:${value}`) + .join('\n') || '', + { + header: 'Tags', + cell: ({ getValue, row }) => ( + + ), + }, + ), columnHelper.accessor('createdAt', { header: 'Created', cell: ({ getValue }) => , diff --git a/frontend/src/openapi/models/featureSearchResponseSchema.ts b/frontend/src/openapi/models/featureSearchResponseSchema.ts new file mode 100644 index 000000000000..d11d5ac8264a --- /dev/null +++ b/frontend/src/openapi/models/featureSearchResponseSchema.ts @@ -0,0 +1,63 @@ +/** + * Generated by Orval + * Do not edit manually. + * See `gen:api` script in package.json + */ +import type { FeatureSearchResponseSchemaDependenciesItem } from './featureSearchResponseSchemaDependenciesItem'; +import type { FeatureEnvironmentSchema } from './featureEnvironmentSchema'; +import type { FeatureSearchResponseSchemaStrategiesItem } from './featureSearchResponseSchemaStrategiesItem'; +import type { TagSchema } from './tagSchema'; +import type { VariantSchema } from './variantSchema'; + +/** + * A feature toggle definition + */ +export interface FeatureSearchResponseSchema { + /** `true` if the feature is archived */ + archived?: boolean; + /** The date the feature was archived */ + archivedAt?: string | null; + /** The list of child feature names. This is an experimental field and may change. */ + children?: string[]; + /** The date the feature was created */ + createdAt?: string | null; + /** The list of parent dependencies. This is an experimental field and may change. */ + dependencies?: FeatureSearchResponseSchemaDependenciesItem[]; + /** Detailed description of the feature */ + description?: string | null; + /** `true` if the feature is enabled, otherwise `false`. */ + enabled?: boolean; + /** The list of environments where the feature can be used */ + environments?: FeatureEnvironmentSchema[]; + /** `true` if the feature was favorited, otherwise `false`. */ + favorite?: boolean; + /** `true` if the impression data collection is enabled for the feature, otherwise `false`. */ + impressionData?: boolean; + /** + * The date when metrics where last collected for the feature. This field is deprecated, use the one in featureEnvironmentSchema + * @deprecated + */ + lastSeenAt?: string | null; + /** Unique feature name */ + name: string; + /** Name of the project the feature belongs to */ + project?: string; + /** The list of segments the feature is enabled for. */ + segments?: string[]; + /** `true` if the feature is stale based on the age and feature type, otherwise `false`. */ + stale?: boolean; + /** + * This is a legacy field that will be deprecated + * @deprecated + */ + strategies?: FeatureSearchResponseSchemaStrategiesItem[]; + /** The list of feature tags */ + tags?: TagSchema[] | null; + /** Type of the toggle e.g. experiment, kill-switch, release, operational, permission */ + type?: string; + /** + * The list of feature variants + * @deprecated + */ + variants?: VariantSchema[]; +} diff --git a/frontend/src/openapi/models/featureSearchResponseSchemaDependenciesItem.ts b/frontend/src/openapi/models/featureSearchResponseSchemaDependenciesItem.ts new file mode 100644 index 000000000000..f8f3e92885ff --- /dev/null +++ b/frontend/src/openapi/models/featureSearchResponseSchemaDependenciesItem.ts @@ -0,0 +1,14 @@ +/** + * Generated by Orval + * Do not edit manually. + * See `gen:api` script in package.json + */ + +export type FeatureSearchResponseSchemaDependenciesItem = { + /** Whether the parent feature is enabled or not */ + enabled?: boolean; + /** The name of the parent feature */ + feature: string; + /** The list of variants the parent feature should resolve to. Only valid when feature is enabled. */ + variants?: string[]; +}; diff --git a/frontend/src/openapi/models/featureSearchResponseSchemaStrategiesItem.ts b/frontend/src/openapi/models/featureSearchResponseSchemaStrategiesItem.ts new file mode 100644 index 000000000000..106ce402ec4f --- /dev/null +++ b/frontend/src/openapi/models/featureSearchResponseSchemaStrategiesItem.ts @@ -0,0 +1,7 @@ +/** + * Generated by Orval + * Do not edit manually. + * See `gen:api` script in package.json + */ + +export type FeatureSearchResponseSchemaStrategiesItem = { [key: string]: any }; diff --git a/frontend/src/openapi/models/index.ts b/frontend/src/openapi/models/index.ts index 9636eddab482..0c15dd279209 100644 --- a/frontend/src/openapi/models/index.ts +++ b/frontend/src/openapi/models/index.ts @@ -486,6 +486,9 @@ export * from './featureMetricsSchema'; export * from './featureSchema'; export * from './featureSchemaDependenciesItem'; export * from './featureSchemaStrategiesItem'; +export * from './featureSearchResponseSchema'; +export * from './featureSearchResponseSchemaDependenciesItem'; +export * from './featureSearchResponseSchemaStrategiesItem'; export * from './featureStrategySchema'; export * from './featureStrategySegmentSchema'; export * from './featureTagSchema'; diff --git a/frontend/src/openapi/models/searchFeaturesSchema.ts b/frontend/src/openapi/models/searchFeaturesSchema.ts index 286ce8d91dea..52683917e39a 100644 --- a/frontend/src/openapi/models/searchFeaturesSchema.ts +++ b/frontend/src/openapi/models/searchFeaturesSchema.ts @@ -3,14 +3,14 @@ * Do not edit manually. * See `gen:api` script in package.json */ -import type { FeatureSchema } from './featureSchema'; +import type { FeatureSearchResponseSchema } from './featureSearchResponseSchema'; /** * A list of features matching search and filter criteria. */ export interface SearchFeaturesSchema { - /** The full list of features in this project (excluding archived features) */ - features: FeatureSchema[]; + /** The full list of features in this project matching search and filter criteria. */ + features: FeatureSearchResponseSchema[]; /** Total count of the features matching search and filter criteria */ total?: number; }