From a1a24ea0b14b26bbca97aadf48d1fbe6e221178a Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Wed, 25 Sep 2024 11:11:30 +0200 Subject: [PATCH] feat: flag exposure in personal dashboard (#8247) --- .../FeatureLifecycle/FeatureLifecycle.tsx | 1 + .../FeatureLifecycleTooltip.test.tsx | 28 +++----- .../FeatureLifecycleTooltip.tsx | 47 ++++++++---- .../FeatureLifecycle/FlagExposure.tsx | 72 +++++++++++++++++++ .../personalDashboard/PersonalDashboard.tsx | 19 ++++- 5 files changed, 132 insertions(+), 35 deletions(-) create mode 100644 frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FlagExposure.tsx diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycle.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycle.tsx index 995b468bcd..3df3893dce 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycle.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycle.tsx @@ -43,6 +43,7 @@ export const FeatureLifecycle: FC<{ return currentStage ? ( { render( - - - child - - } - /> - , + + child + , { - route: '/projects/default', permissions: [ { permission: DELETE_FEATURE }, { permission: UPDATE_FEATURE }, diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycleTooltip.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycleTooltip.tsx index 46d597c98d..cfd8e6e4b4 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycleTooltip.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycleTooltip.tsx @@ -25,7 +25,6 @@ import { isSafeToArchive } from './isSafeToArchive'; import { useLocationSettings } from 'hooks/useLocationSettings'; import { formatDateYMDHMS } from 'utils/formatDate'; import { formatDistanceToNow, parseISO } from 'date-fns'; -import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; const TimeLabel = styled('span')(({ theme }) => ({ color: theme.palette.text.secondary, @@ -96,7 +95,7 @@ const StageBox = styled(Box, { ...(active && { backgroundColor: theme.palette.primary.light, color: theme.palette.primary.contrastText, - fontWeight: theme.fontWeight.bold, + fontWeight: theme.typography.fontWeightBold, borderRadius: theme.spacing(0.5), }), }, @@ -247,17 +246,16 @@ const PreLiveStageDescription: FC<{ children?: React.ReactNode }> = ({ const BoldTitle = styled(Typography)(({ theme }) => ({ marginTop: theme.spacing(1), marginBottom: theme.spacing(1), - fontSize: theme.fontSizes.smallBody, - fontWeight: theme.fontWeight.bold, + fontSize: theme.typography.body2.fontSize, + fontWeight: theme.typography.fontWeightBold, })); const LiveStageDescription: FC<{ onComplete: () => void; loading: boolean; children?: React.ReactNode; -}> = ({ children, onComplete, loading }) => { - const projectId = useRequiredPathParam('projectId'); - + project: string; +}> = ({ children, onComplete, loading, project }) => { return ( <> Is this feature complete? @@ -276,7 +274,7 @@ const LiveStageDescription: FC<{ size='small' onClick={onComplete} disabled={loading} - projectId={projectId} + projectId={project} > Mark completed @@ -294,9 +292,8 @@ const SafeToArchive: FC<{ onArchive: () => void; onUncomplete: () => void; loading: boolean; -}> = ({ onArchive, onUncomplete, loading }) => { - const projectId = useRequiredPathParam('projectId'); - + project: string; +}> = ({ onArchive, onUncomplete, loading, project }) => { return ( <> Safe to archive @@ -324,7 +321,7 @@ const SafeToArchive: FC<{ size='small' onClick={onUncomplete} disabled={loading} - projectId={projectId} + projectId={project} > Revert to live @@ -335,7 +332,7 @@ const SafeToArchive: FC<{ size='small' sx={{ mb: 2 }} onClick={onArchive} - projectId={projectId} + projectId={project} > Archive feature @@ -393,7 +390,15 @@ const CompletedStageDescription: FC<{ lastSeenAt: string; }>; children?: React.ReactNode; -}> = ({ children, environments, onArchive, onUncomplete, loading }) => { + project: string; +}> = ({ + children, + environments, + onArchive, + onUncomplete, + loading, + project, +}) => { return ( } elseShow={ @@ -432,11 +438,20 @@ const FormatElapsedTime: FC<{ export const FeatureLifecycleTooltip: FC<{ children: React.ReactElement; stage: LifecycleStage; + project: string; onArchive: () => void; onComplete: () => void; onUncomplete: () => void; loading: boolean; -}> = ({ children, stage, onArchive, onComplete, onUncomplete, loading }) => ( +}> = ({ + children, + stage, + project, + onArchive, + onComplete, + onUncomplete, + loading, +}) => ( @@ -492,6 +508,7 @@ export const FeatureLifecycleTooltip: FC<{ onArchive={onArchive} onUncomplete={onUncomplete} loading={loading} + project={project} > diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FlagExposure.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FlagExposure.tsx new file mode 100644 index 0000000000..7c1283b7f7 --- /dev/null +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FlagExposure.tsx @@ -0,0 +1,72 @@ +import { type FC, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { useFeature } from 'hooks/api/getters/useFeature/useFeature'; +import type { ILastSeenEnvironments } from 'interfaces/featureToggle'; +import { Box } from '@mui/material'; +import { FeatureEnvironmentSeen } from '../../FeatureEnvironmentSeen/FeatureEnvironmentSeen'; +import { FeatureLifecycle } from './FeatureLifecycle'; +import { FeatureArchiveNotAllowedDialog } from 'component/common/FeatureArchiveDialog/FeatureArchiveNotAllowedDialog'; +import { FeatureArchiveDialog } from 'component/common/FeatureArchiveDialog/FeatureArchiveDialog'; +import { MarkCompletedDialogue } from './MarkCompletedDialogue'; + +export const FlagExposure: FC<{ + project: string; + flagName: string; + onArchive: () => void; +}> = ({ project, flagName, onArchive }) => { + const navigate = useNavigate(); + const { feature, refetchFeature } = useFeature(project, flagName); + const lastSeenEnvironments: ILastSeenEnvironments[] = + feature.environments?.map((env) => ({ + name: env.name, + lastSeenAt: env.lastSeenAt, + enabled: env.enabled, + yes: env.yes, + no: env.no, + })); + const [showDelDialog, setShowDelDialog] = useState(false); + const [showMarkCompletedDialogue, setShowMarkCompletedDialogue] = + useState(false); + + return ( + + + setShowDelDialog(true)} + onComplete={() => setShowMarkCompletedDialogue(true)} + onUncomplete={refetchFeature} + /> + + {feature.children.length > 0 ? ( + setShowDelDialog(false)} + /> + ) : ( + setShowDelDialog(false)} + projectId={project} + featureIds={[flagName]} + /> + )} + + {feature.project ? ( + + ) : null} + + ); +}; diff --git a/frontend/src/component/personalDashboard/PersonalDashboard.tsx b/frontend/src/component/personalDashboard/PersonalDashboard.tsx index ff40f972e3..3dcf55fc5e 100644 --- a/frontend/src/component/personalDashboard/PersonalDashboard.tsx +++ b/frontend/src/component/personalDashboard/PersonalDashboard.tsx @@ -24,6 +24,7 @@ import { ProjectSetupComplete } from './ProjectSetupComplete'; import { usePersonalDashboard } from 'hooks/api/getters/usePersonalDashboard/usePersonalDashboard'; import { getFeatureTypeIcons } from 'utils/getFeatureTypeIcons'; import type { PersonalDashboardSchema } from '../../openapi'; +import { FlagExposure } from 'component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FlagExposure'; const ScreenExplanation = styled(Typography)(({ theme }) => ({ marginTop: theme.spacing(1), @@ -177,7 +178,8 @@ export const PersonalDashboard = () => { const { projects, activeProject, setActiveProject } = useProjects(); - const { personalDashboard } = usePersonalDashboard(); + const { personalDashboard, refetch: refetchDashboard } = + usePersonalDashboard(); const [activeFlag, setActiveFlag] = useState< PersonalDashboardSchema['flags'][0] | null >(null); @@ -298,7 +300,20 @@ export const PersonalDashboard = () => { My feature flags - + + {activeFlag ? ( + + ) : null} + {personalDashboard && personalDashboard.flags.length > 0 ? (