Skip to content

Commit

Permalink
Refactor front end code pt 1 (#8438)
Browse files Browse the repository at this point in the history
This PR is the first in the front end code refactoring. It moves My
Flags out into a separate file and includes some extra error handling
(such as if the name of the flag causes problems for the API).


![image](https://github.com/user-attachments/assets/5aec8f0c-de79-4b7d-b56b-42297b872ec5)
  • Loading branch information
thomasheartman authored Oct 14, 2024
1 parent d8ddb57 commit 8c2ed5d
Show file tree
Hide file tree
Showing 6 changed files with 362 additions and 269 deletions.
33 changes: 23 additions & 10 deletions frontend/src/component/personalDashboard/FlagMetricsChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,12 @@ const useFlagMetrics = (
environment: string | null,
hoursBack: number,
) => {
const { featureMetrics: metrics = [], loading } = useFeatureMetricsRaw(
flagName,
hoursBack,
);
const {
featureMetrics: metrics = [],
loading,
error,
} = useFeatureMetricsRaw(flagName, hoursBack);

const sortedMetrics = useMemo(() => {
return [...metrics].sort((metricA, metricB) => {
return metricA.timestamp.localeCompare(metricB.timestamp);
Expand All @@ -151,7 +153,7 @@ const useFlagMetrics = (
return createBarChartOptions(theme, hoursBack, locationSettings);
}, [theme, hoursBack, locationSettings]);

return { data, options, loading };
return { data, options, loading, error };
};

const EnvironmentSelect: FC<{
Expand Down Expand Up @@ -222,11 +224,22 @@ export const FlagMetricsChart: FC<{
const { environment, setEnvironment, activeEnvironments } =
useMetricsEnvironments(flag.project, flag.name);

const { data, options, loading } = useFlagMetrics(
flag.name,
environment,
hoursBack,
);
const {
data,
options,
loading,
error: metricsError,
} = useFlagMetrics(flag.name, environment, hoursBack);

if (metricsError) {
return (
<ChartContainer>
<PlaceholderFlagMetricsChart
label={`Couldn't fetch metrics for the current flag. This may be a transient error, or your flag name ("${flag.name}") may be causing issues.`}
/>
</ChartContainer>
);
}

const noData = data.datasets[0].data.length === 0;

Expand Down
19 changes: 18 additions & 1 deletion frontend/src/component/personalDashboard/Grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export const FlagGrid = styled(ContentGrid)(
);

export const GridItem = styled('div', {
shouldForwardProp: (prop) => !['gridArea', 'sx'].includes(prop.toString()),
shouldForwardProp: (prop) => !['gridArea'].includes(prop.toString()),
})<{ gridArea: string }>(({ theme, gridArea }) => ({
padding: theme.spacing(2, 4),
maxHeight: '100%',
Expand Down Expand Up @@ -113,3 +113,20 @@ export const StyledList = styled(List)(({ theme }) => ({
maxHeight: '100%',
})({ theme }),
}));

export const StyledCardTitle = styled('div')<{ lines?: number }>(
({ theme, lines = 2 }) => ({
fontWeight: theme.typography.fontWeightRegular,
fontSize: theme.typography.body1.fontSize,
lineClamp: `${lines}`,
WebkitLineClamp: lines,
lineHeight: '1.2',
display: '-webkit-box',
boxOrient: 'vertical',
textOverflow: 'ellipsis',
overflow: 'hidden',
alignItems: 'flex-start',
WebkitBoxOrient: 'vertical',
wordBreak: 'break-word',
}),
);
180 changes: 180 additions & 0 deletions frontend/src/component/personalDashboard/MyFlags.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import { type FC, useEffect, useRef } from 'react';
import {
ContentGridContainer,
FlagGrid,
ListItemBox,
SpacedGridItem,
StyledCardTitle,
StyledList,
listItemStyle,
} from './Grid';
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
import { getFeatureTypeIcons } from 'utils/getFeatureTypeIcons';
import {
Alert,
IconButton,
Link,
ListItem,
ListItemButton,
Typography,
styled,
} from '@mui/material';
import LinkIcon from '@mui/icons-material/ArrowForward';
import React from 'react';
import type { PersonalDashboardSchemaFlagsItem } from 'openapi';

const NoActiveFlagsInfo = styled('div')(({ theme }) => ({
display: 'flex',
flexFlow: 'column',
gap: theme.spacing(2),
}));

const FlagListItem: FC<{
flag: { name: string; project: string; type: string };
selected: boolean;
onClick: () => void;
}> = ({ flag, selected, onClick }) => {
const activeFlagRef = useRef<HTMLLIElement>(null);
const { trackEvent } = usePlausibleTracker();

useEffect(() => {
if (activeFlagRef.current) {
activeFlagRef.current.scrollIntoView({
block: 'nearest',
inline: 'start',
});
}
}, []);
const IconComponent = getFeatureTypeIcons(flag.type);
const flagLink = `projects/${flag.project}/features/${flag.name}`;
return (
<ListItem
key={flag.name}
disablePadding={true}
sx={{ mb: 1 }}
ref={selected ? activeFlagRef : null}
>
<ListItemButton
sx={listItemStyle}
selected={selected}
onClick={onClick}
>
<ListItemBox>
<IconComponent color='primary' />
<StyledCardTitle>{flag.name}</StyledCardTitle>
<IconButton
component={Link}
href={flagLink}
onClick={() => {
trackEvent('personal-dashboard', {
props: {
eventType: `Go to flag from list`,
},
});
}}
size='small'
sx={{ ml: 'auto' }}
>
<LinkIcon titleAccess={flagLink} />
</IconButton>
</ListItemBox>
</ListItemButton>
</ListItem>
);
};

type FlagData =
| {
state: 'flags';
flags: PersonalDashboardSchemaFlagsItem[];
activeFlag: PersonalDashboardSchemaFlagsItem;
}
| {
state: 'no flags';
};

type Props = {
hasProjects: boolean;
flagData: FlagData;
setActiveFlag: (flag: PersonalDashboardSchemaFlagsItem) => void;
refetchDashboard: () => void;
};

export const MyFlags: FC<Props> = ({
hasProjects,
flagData,
setActiveFlag,
refetchDashboard,
}) => {
return (
<ContentGridContainer>
<FlagGrid>
<SpacedGridItem gridArea='flags'>
{flagData.state === 'flags' ? (
<StyledList
disablePadding={true}
sx={{
height: '100%',
overflow: 'auto',
}}
>
{flagData.flags.map((flag) => (
<FlagListItem
key={flag.name}
flag={flag}
selected={
flag.name === flagData.activeFlag.name
}
onClick={() => setActiveFlag(flag)}
/>
))}
</StyledList>
) : hasProjects ? (
<NoActiveFlagsInfo>
<Typography>
You have not created or favorited any feature
flags. Once you do, they will show up here.
</Typography>
<Typography>
To create a new flag, go to one of your
projects.
</Typography>
</NoActiveFlagsInfo>
) : (
<Alert severity='info'>
You need to create or join a project to be able to
add a flag, or you must be given the rights by your
admin to add feature flags.
</Alert>
)}
</SpacedGridItem>

<SpacedGridItem gridArea='chart'>
{flagData.state === 'flags' ? (
<FlagMetricsChart
flag={flagData.activeFlag}
onArchive={refetchDashboard}
/>
) : (
<PlaceholderFlagMetricsChart
label={
'Metrics for your feature flags will be shown here'
}
/>
)}
</SpacedGridItem>
</FlagGrid>
</ContentGridContainer>
);
};

const FlagMetricsChart = React.lazy(() =>
import('./FlagMetricsChart').then((module) => ({
default: module.FlagMetricsChart,
})),
);
const PlaceholderFlagMetricsChart = React.lazy(() =>
import('./FlagMetricsChart').then((module) => ({
default: module.PlaceholderFlagMetricsChartWithWrapper,
})),
);
2 changes: 1 addition & 1 deletion frontend/src/component/personalDashboard/MyProjects.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { ConnectSDK, CreateFlag, ExistingFlag } from './ConnectSDK';
import { LatestProjectEvents } from './LatestProjectEvents';
import { RoleAndOwnerInfo } from './RoleAndOwnerInfo';
import { forwardRef, useEffect, useRef, type FC } from 'react';
import { StyledCardTitle } from './PersonalDashboard';
import type {
PersonalDashboardProjectDetailsSchema,
PersonalDashboardSchemaAdminsItem,
Expand All @@ -28,6 +27,7 @@ import {
GridItem,
SpacedGridItem,
StyledList,
StyledCardTitle,
} from './Grid';
import { ContactAdmins, DataError } from './ProjectDetailsError';
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
Expand Down
Loading

0 comments on commit 8c2ed5d

Please sign in to comment.