diff --git a/frontend/src/component/personalDashboard/ActionBox.tsx b/frontend/src/component/personalDashboard/ActionBox.tsx new file mode 100644 index 0000000000..eb9bb2be45 --- /dev/null +++ b/frontend/src/component/personalDashboard/ActionBox.tsx @@ -0,0 +1,35 @@ +import { styled } from '@mui/material'; +import type { FC, PropsWithChildren, ReactNode } from 'react'; + +const Container = styled('article')(({ theme }) => ({ + padding: theme.spacing(4, 2), + display: 'flex', + gap: theme.spacing(3), + flexDirection: 'column', +})); + +const TitleContainer = styled('div')(({ theme }) => ({ + display: 'flex', + flexDirection: 'row', + gap: theme.spacing(2), + alignItems: 'center', + fontSize: theme.spacing(1.75), + fontWeight: 'bold', +})); + +type Props = { + title?: string | ReactNode; +}; + +export const ActionBox: FC> = ({ + title, + children, +}) => { + return ( + + {title ? {title} : null} + + {children} + + ); +}; diff --git a/frontend/src/component/personalDashboard/ConnectSDK.tsx b/frontend/src/component/personalDashboard/ConnectSDK.tsx index 99e0149e9e..815947357f 100644 --- a/frontend/src/component/personalDashboard/ConnectSDK.tsx +++ b/frontend/src/component/personalDashboard/ConnectSDK.tsx @@ -1,15 +1,8 @@ import { Button, styled, Typography } from '@mui/material'; import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; import type { FC } from 'react'; - -const TitleContainer = styled('div')(({ theme }) => ({ - display: 'flex', - flexDirection: 'row', - gap: theme.spacing(2), - alignItems: 'center', - fontSize: theme.spacing(1.75), - fontWeight: 'bold', -})); +import { ActionBox } from './ActionBox'; +import { Link } from 'react-router-dom'; const NeutralCircleContainer = styled('span')(({ theme }) => ({ width: '28px', @@ -37,22 +30,18 @@ const SuccessContainer = styled('div')(({ theme }) => ({ padding: theme.spacing(2, 2, 2, 2), })); -const ActionBox = styled('div')(({ theme }) => ({ - flexBasis: '50%', - padding: theme.spacing(4, 2), - display: 'flex', - gap: theme.spacing(3), - flexDirection: 'column', -})); - export const CreateFlag: FC<{ project: string }> = ({ project }) => { const { trackEvent } = usePlausibleTracker(); return ( - - - 1 - Create a feature flag - + + 1 + Create a feature flag + + } + >

The project currently holds no feature flags.

Create one to get started.

@@ -78,11 +67,14 @@ export const CreateFlag: FC<{ project: string }> = ({ project }) => { export const ExistingFlag: FC<{ project: string }> = ({ project }) => { return ( - - - - Create a feature flag - + + 1 + Create a feature flag + + } + > You have created your first flag @@ -92,7 +84,11 @@ export const ExistingFlag: FC<{ project: string }> = ({ project }) => {
-
@@ -102,11 +98,15 @@ export const ExistingFlag: FC<{ project: string }> = ({ project }) => { export const ConnectSDK: FC<{ project: string }> = ({ project }) => { return ( - - - 2 - Connect an SDK - + + 2 + Connect an SDK + + } + >

Your project is not yet connected to any SDK.

@@ -115,7 +115,11 @@ export const ConnectSDK: FC<{ project: string }> = ({ project }) => {

-
diff --git a/frontend/src/component/personalDashboard/LatestProjectEvents.tsx b/frontend/src/component/personalDashboard/LatestProjectEvents.tsx index fdb04de313..3f91aa2352 100644 --- a/frontend/src/component/personalDashboard/LatestProjectEvents.tsx +++ b/frontend/src/component/personalDashboard/LatestProjectEvents.tsx @@ -5,6 +5,7 @@ import { UserAvatar } from '../common/UserAvatar/UserAvatar'; import { Typography, styled } from '@mui/material'; import { formatDateYMDHM } from 'utils/formatDate'; import { useLocationSettings } from 'hooks/useLocationSettings'; +import { ActionBox } from './ActionBox'; const Events = styled('ul')(({ theme }) => ({ padding: 0, @@ -33,13 +34,6 @@ const TitleContainer = styled('div')(({ theme }) => ({ alignItems: 'center', })); -const ActionBox = styled('article')(({ theme }) => ({ - padding: theme.spacing(0, 2), - display: 'flex', - gap: theme.spacing(3), - flexDirection: 'column', -})); - const Timestamp = styled('time')(({ theme }) => ({ color: theme.palette.text.secondary, fontSize: theme.typography.fontSize, @@ -55,8 +49,8 @@ export const LatestProjectEvents: FC<{ }> = ({ latestEvents }) => { const { locationSettings } = useLocationSettings(); return ( - - + Latest events - + } + > {latestEvents.map((event) => { return ( diff --git a/frontend/src/component/personalDashboard/MyProjects.tsx b/frontend/src/component/personalDashboard/MyProjects.tsx index 3a2d20c1be..e325b79330 100644 --- a/frontend/src/component/personalDashboard/MyProjects.tsx +++ b/frontend/src/component/personalDashboard/MyProjects.tsx @@ -1,7 +1,6 @@ import { Box, IconButton, - Link, ListItem, ListItemButton, Typography, @@ -16,6 +15,7 @@ import { forwardRef, useEffect, useRef, type FC } from 'react'; import type { PersonalDashboardProjectDetailsSchema, PersonalDashboardSchemaAdminsItem, + PersonalDashboardSchemaProjectOwnersItem, PersonalDashboardSchemaProjectsItem, } from '../../openapi'; import { @@ -31,6 +31,8 @@ import { } from './SharedComponents'; import { ContactAdmins, DataError } from './ProjectDetailsError'; import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; +import { Link } from 'react-router-dom'; +import { ActionBox } from './ActionBox'; const ActiveProjectDetails: FC<{ project: PersonalDashboardSchemaProjectsItem; @@ -98,7 +100,7 @@ const ProjectListItem: FC<{ {project.name} { @@ -118,6 +120,8 @@ const ProjectListItem: FC<{ ); }; +type MyProjectsState = 'no projects' | 'projects' | 'projects with error'; + export const MyProjects = forwardRef< HTMLDivElement, { @@ -126,6 +130,7 @@ export const MyProjects = forwardRef< activeProject: string; setActiveProject: (project: string) => void; admins: PersonalDashboardSchemaAdminsItem[]; + owners: PersonalDashboardSchemaProjectOwnersItem[]; } >( ( @@ -138,6 +143,12 @@ export const MyProjects = forwardRef< }, ref, ) => { + const state: MyProjectsState = projects.length + ? personalDashboardProjectDetails + ? 'projects' + : 'projects with error' + : 'no projects'; + const activeProjectStage = personalDashboardProjectDetails?.onboardingStatus.status ?? 'loading'; @@ -145,10 +156,11 @@ export const MyProjects = forwardRef< activeProjectStage === 'onboarding-started' || activeProjectStage === 'first-flag-created'; - const error = personalDashboardProjectDetails === undefined; - const box1Content = () => { - if (error) { + if (state === 'no projects') { + } + + if (state === 'projects with error') { return ; } @@ -173,7 +185,7 @@ export const MyProjects = forwardRef< }; const box2Content = () => { - if (error) { + if (state === 'projects with error') { return ; } @@ -195,20 +207,45 @@ export const MyProjects = forwardRef< } }; + const projectListContent = () => { + if (state === 'no projects') { + return ( + + + You don't currently have access to any projects in + the system. + + + To get started, you can{' '} + + create your own project + + . Alternatively, you can review the available + projects in the system and ask the owner for access. + + + ); + } + + return ( + + {projects.map((project) => ( + setActiveProject(project.id)} + /> + ))} + + ); + }; + return ( - - {projects.map((project) => ( - setActiveProject(project.id)} - /> - ))} - + {projectListContent()} {box1Content()} diff --git a/frontend/src/component/personalDashboard/ProjectDetailsError.tsx b/frontend/src/component/personalDashboard/ProjectDetailsError.tsx index 6ce4c99f61..22a3da5728 100644 --- a/frontend/src/component/personalDashboard/ProjectDetailsError.tsx +++ b/frontend/src/component/personalDashboard/ProjectDetailsError.tsx @@ -1,32 +1,14 @@ -import { styled } from '@mui/material'; import type { PersonalDashboardSchemaAdminsItem } from 'openapi'; import type { FC } from 'react'; import { YourAdmins } from './YourAdmins'; - -const TitleContainer = styled('div')(({ theme }) => ({ - display: 'flex', - flexDirection: 'row', - gap: theme.spacing(2), - alignItems: 'center', - fontSize: theme.spacing(1.75), - fontWeight: 'bold', -})); - -const ActionBox = styled('div')(({ theme }) => ({ - flexBasis: '50%', - padding: theme.spacing(4, 2), - display: 'flex', - gap: theme.spacing(3), - flexDirection: 'column', -})); +import { ActionBox } from './ActionBox'; export const DataError: FC<{ project: string }> = ({ project }) => { return ( - - - Couldn't fetch data for project "{project}". - - +

The API request to get data for this project returned with an error. @@ -44,10 +26,7 @@ export const ContactAdmins: FC<{ admins: PersonalDashboardSchemaAdminsItem[]; }> = ({ admins }) => { return ( - - - Consider contacting one of your Unleash admins for help. - + ); diff --git a/frontend/src/component/personalDashboard/ProjectSetupComplete.tsx b/frontend/src/component/personalDashboard/ProjectSetupComplete.tsx index 58ab79f34b..db8be8b7ee 100644 --- a/frontend/src/component/personalDashboard/ProjectSetupComplete.tsx +++ b/frontend/src/component/personalDashboard/ProjectSetupComplete.tsx @@ -3,20 +3,7 @@ import type { FC } from 'react'; import { Link } from 'react-router-dom'; import Lightbulb from '@mui/icons-material/LightbulbOutlined'; import type { PersonalDashboardProjectDetailsSchemaInsights } from '../../openapi'; - -const TitleContainer = styled('div')(({ theme }) => ({ - display: 'flex', - flexDirection: 'row', - gap: theme.spacing(2), - alignItems: 'center', -})); - -const ActionBox = styled('article')(({ theme }) => ({ - padding: theme.spacing(0, 2), - display: 'flex', - gap: theme.spacing(3), - flexDirection: 'column', -})); +import { ActionBox } from './ActionBox'; const PercentageScore = styled('span')(({ theme }) => ({ fontWeight: theme.typography.fontWeightBold, @@ -145,14 +132,16 @@ export const ProjectSetupComplete: FC<{ const projectHealthTrend = determineProjectHealthTrend(insights); return ( - - - - - Project health - - - + + + + Project health + + + } + >