Skip to content

Commit

Permalink
feat: Lifecycle in project overview (#7024)
Browse files Browse the repository at this point in the history
  • Loading branch information
kwasniew authored May 9, 2024
1 parent 476959d commit 3fc7714
Show file tree
Hide file tree
Showing 12 changed files with 136 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React, { type VFC } from 'react';
import { FeatureEnvironmentSeen } from 'component/feature/FeatureView/FeatureEnvironmentSeen/FeatureEnvironmentSeen';
import type { FeatureSearchEnvironmentSchema } from 'openapi';
import { FeatureLifecycle } from 'component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycle';
import { Box } from '@mui/material';

interface IFeatureSeenCellProps {
feature: {
Expand All @@ -26,6 +28,46 @@ export const FeatureEnvironmentSeenCell: VFC<IFeatureSeenCellProps> = ({
);
};

interface IFeatureLifecycleProps {
feature: {
environments?: FeatureSearchEnvironmentSchema[];
lastSeenAt?: string | null;
project: string;
name: string;
};
onComplete: () => void;
onUncomplete: () => void;
onArchive: () => void;
}

export const FeatureLifecycleCell: VFC<IFeatureLifecycleProps> = ({
feature,
onComplete,
onUncomplete,
onArchive,
...rest
}) => {
const environments = feature.environments
? Object.values(feature.environments)
: [];

return (
<Box sx={{ display: 'flex' }}>
<FeatureEnvironmentSeen
featureLastSeen={feature.lastSeenAt || undefined}
environments={environments}
{...rest}
/>
<FeatureLifecycle
onArchive={onArchive}
onComplete={onComplete}
onUncomplete={onUncomplete}
feature={feature}
/>
</Box>
);
};

export const MemoizedFeatureEnvironmentSeenCell = React.memo(
FeatureEnvironmentSeenCell,
);
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,25 @@ import { FeatureLifecycleStageIcon } from './FeatureLifecycleStageIcon';
import { FeatureLifecycleTooltip } from './FeatureLifecycleTooltip';
import useFeatureLifecycleApi from 'hooks/api/actions/useFeatureLifecycleApi/useFeatureLifecycleApi';
import { populateCurrentStage } from './populateCurrentStage';
import type { IFeatureToggle } from 'interfaces/featureToggle';
import type { FC } from 'react';
import type { Lifecycle } from 'interfaces/featureToggle';

export interface LifecycleFeature {
lifecycle?: Lifecycle;
project: string;
name: string;
environments?: Array<{
type: string;
name: string;
lastSeenAt?: string | null;
}>;
}

export const FeatureLifecycle: FC<{
onArchive: () => void;
onComplete: () => void;
onUncomplete: () => void;
feature: Pick<
IFeatureToggle,
'lifecycle' | 'environments' | 'project' | 'name'
>;
feature: LifecycleFeature;
}> = ({ feature, onComplete, onUncomplete, onArchive }) => {
const currentStage = populateCurrentStage(feature);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,6 @@ export const FeatureLifecycleTooltip: FC<{
</Box>
}
>
<Box>{children}</Box>
<CenteredBox>{children}</CenteredBox>
</HtmlTooltip>
);
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import type { IFeatureToggle } from 'interfaces/featureToggle';
import type { LifecycleStage } from './LifecycleStage';
import type { LifecycleFeature } from './FeatureLifecycle';

export const populateCurrentStage = (
feature: Pick<IFeatureToggle, 'lifecycle' | 'environments'>,
feature: Pick<LifecycleFeature, 'lifecycle' | 'environments'>,
): LifecycleStage | undefined => {
if (!feature.lifecycle) return undefined;

const getFilteredEnvironments = (condition: (type: string) => boolean) => {
return feature.environments
return (feature.environments || [])
.filter((env) => condition(env.type) && Boolean(env.lastSeenAt))
.map((env) => ({
name: env.name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ import { useFavoriteFeaturesApi } from 'hooks/api/actions/useFavoriteFeaturesApi
import { MemoizedRowSelectCell } from '../ProjectFeatureToggles/RowSelectCell/RowSelectCell';
import { BatchSelectionActionsBar } from 'component/common/BatchSelectionActionsBar/BatchSelectionActionsBar';
import { ProjectFeaturesBatchActions } from '../ProjectFeatureToggles/ProjectFeaturesBatchActions/ProjectFeaturesBatchActions';
import { MemoizedFeatureEnvironmentSeenCell } from 'component/common/Table/cells/FeatureSeenCell/FeatureEnvironmentSeenCell';
import {
FeatureLifecycleCell,
MemoizedFeatureEnvironmentSeenCell,
} from 'component/common/Table/cells/FeatureSeenCell/FeatureEnvironmentSeenCell';
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
import { useFeatureToggleSwitch } from '../ProjectFeatureToggles/FeatureToggleSwitch/useFeatureToggleSwitch';
import useLoading from 'hooks/useLoading';
Expand Down Expand Up @@ -48,6 +51,7 @@ import { TableEmptyState } from './TableEmptyState/TableEmptyState';
import { useRowActions } from './hooks/useRowActions';
import { useSelectedData } from './hooks/useSelectedData';
import { FeatureOverviewCell } from '../../../common/Table/cells/FeatureOverviewCell/FeatureOverviewCell';
import { useUiFlag } from 'hooks/useUiFlag';

interface IPaginatedProjectFeatureTogglesProps {
environments: string[];
Expand Down Expand Up @@ -126,6 +130,8 @@ export const ProjectFeatureToggles = ({

const isPlaceholder = Boolean(initialLoad || (loading && total));

const featureLifecycleEnabled = useUiFlag('featureLifecycle');

const columns = useMemo(
() => [
columnHelper.display({
Expand Down Expand Up @@ -195,9 +201,25 @@ export const ProjectFeatureToggles = ({
id: 'lastSeenAt',
header: 'Last seen',
cell: ({ row: { original } }) => (
<MemoizedFeatureEnvironmentSeenCell
feature={original}
data-loading
<ConditionallyRender
condition={featureLifecycleEnabled}
show={
<FeatureLifecycleCell
feature={original}
onComplete={refetch}
onUncomplete={refetch}
onArchive={() =>
setFeatureArchiveState(original.name)
}
data-loading
/>
}
elseShow={
<MemoizedFeatureEnvironmentSeenCell
feature={original}
data-loading
/>
}
/>
),
size: 50,
Expand Down Expand Up @@ -308,10 +330,12 @@ export const ProjectFeatureToggles = ({
{
name: 'development',
enabled: false,
type: 'development',
},
{
name: 'production',
enabled: false,
type: 'production',
},
],
})),
Expand Down
10 changes: 6 additions & 4 deletions frontend/src/interfaces/featureToggle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ export type ILastSeenEnvironments = Pick<
'name' | 'enabled' | 'lastSeenAt' | 'yes' | 'no'
>;

export type Lifecycle = {
stage: 'initial' | 'pre-live' | 'live' | 'completed' | 'archived';
enteredStageAt: string;
};

export interface IFeatureToggle {
stale: boolean;
archived: boolean;
Expand All @@ -49,10 +54,7 @@ export interface IFeatureToggle {
impressionData: boolean;
strategies?: IFeatureStrategy[];
dependencies: Array<IDependency>;
lifecycle?: {
stage: 'initial' | 'pre-live' | 'live' | 'completed' | 'archived';
enteredStageAt: string;
};
lifecycle?: Lifecycle;
children: Array<string>;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export interface FeatureSearchEnvironmentSchema {
/** A list of activation strategies for the feature environment */
strategies?: FeatureStrategySchema[];
/** The type of the environment */
type?: string;
type: string;
/** The number of defined variants */
variantCount?: number;
/** A list of variants for the feature environment */
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/openapi/models/featureSearchResponseSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
import type { FeatureSearchResponseSchemaDependencyType } from './featureSearchResponseSchemaDependencyType';
import type { FeatureSearchEnvironmentSchema } from './featureSearchEnvironmentSchema';
import type { FeatureSearchResponseSchemaLifecycle } from './featureSearchResponseSchemaLifecycle';
import type { FeatureSearchResponseSchemaStrategiesItem } from './featureSearchResponseSchemaStrategiesItem';
import type { TagSchema } from './tagSchema';
import type { VariantSchema } from './variantSchema';
Expand Down Expand Up @@ -47,6 +48,8 @@ export interface FeatureSearchResponseSchema {
* @nullable
*/
lastSeenAt?: string | null;
/** Current lifecycle stage of the feature */
lifecycle?: FeatureSearchResponseSchemaLifecycle;
/** Unique feature name */
name: string;
/** Name of the project the feature belongs to */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Generated by Orval
* Do not edit manually.
* See `gen:api` script in package.json
*/
import type { FeatureSearchResponseSchemaLifecycleStage } from './featureSearchResponseSchemaLifecycleStage';

/**
* Current lifecycle stage of the feature
*/
export type FeatureSearchResponseSchemaLifecycle = {
/** When the feature entered this stage */
enteredStageAt: string;
/** The name of the current lifecycle stage */
stage: FeatureSearchResponseSchemaLifecycleStage;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Generated by Orval
* Do not edit manually.
* See `gen:api` script in package.json
*/

/**
* The name of the current lifecycle stage
*/
export type FeatureSearchResponseSchemaLifecycleStage =
(typeof FeatureSearchResponseSchemaLifecycleStage)[keyof typeof FeatureSearchResponseSchemaLifecycleStage];

// eslint-disable-next-line @typescript-eslint/no-redeclare
export const FeatureSearchResponseSchemaLifecycleStage = {
initial: 'initial',
'pre-live': 'pre-live',
live: 'live',
completed: 'completed',
archived: 'archived',
} as const;
2 changes: 2 additions & 0 deletions frontend/src/openapi/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,8 @@ export * from './featureSchemaStrategiesItem';
export * from './featureSearchEnvironmentSchema';
export * from './featureSearchResponseSchema';
export * from './featureSearchResponseSchemaDependencyType';
export * from './featureSearchResponseSchemaLifecycle';
export * from './featureSearchResponseSchemaLifecycleStage';
export * from './featureSearchResponseSchemaStrategiesItem';
export * from './featureStrategySchema';
export * from './featureStrategySegmentSchema';
Expand Down
2 changes: 1 addition & 1 deletion src/lib/openapi/spec/feature-search-environment-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const featureSearchEnvironmentSchema = {
$id: '#/components/schemas/featureSearchEnvironmentSchema',
type: 'object',
additionalProperties: false,
required: ['name', 'enabled'],
required: ['name', 'enabled', 'type'],
description: 'A detailed description of the feature environment',
properties: {
...featureEnvironmentSchema.properties,
Expand Down

0 comments on commit 3fc7714

Please sign in to comment.