mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
Revert "Refactor front end code pt 1 (#8438)"
This reverts commit 8c2ed5dc30
.
This commit is contained in:
parent
8c2ed5dc30
commit
5a036997dc
@ -121,12 +121,10 @@ const useFlagMetrics = (
|
||||
environment: string | null,
|
||||
hoursBack: number,
|
||||
) => {
|
||||
const {
|
||||
featureMetrics: metrics = [],
|
||||
loading,
|
||||
error,
|
||||
} = useFeatureMetricsRaw(flagName, hoursBack);
|
||||
|
||||
const { featureMetrics: metrics = [], loading } = useFeatureMetricsRaw(
|
||||
flagName,
|
||||
hoursBack,
|
||||
);
|
||||
const sortedMetrics = useMemo(() => {
|
||||
return [...metrics].sort((metricA, metricB) => {
|
||||
return metricA.timestamp.localeCompare(metricB.timestamp);
|
||||
@ -153,7 +151,7 @@ const useFlagMetrics = (
|
||||
return createBarChartOptions(theme, hoursBack, locationSettings);
|
||||
}, [theme, hoursBack, locationSettings]);
|
||||
|
||||
return { data, options, loading, error };
|
||||
return { data, options, loading };
|
||||
};
|
||||
|
||||
const EnvironmentSelect: FC<{
|
||||
@ -224,22 +222,11 @@ export const FlagMetricsChart: FC<{
|
||||
const { environment, setEnvironment, activeEnvironments } =
|
||||
useMetricsEnvironments(flag.project, flag.name);
|
||||
|
||||
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 { data, options, loading } = useFlagMetrics(
|
||||
flag.name,
|
||||
environment,
|
||||
hoursBack,
|
||||
);
|
||||
|
||||
const noData = data.datasets[0].data.length === 0;
|
||||
|
||||
|
@ -58,7 +58,7 @@ export const FlagGrid = styled(ContentGrid)(
|
||||
);
|
||||
|
||||
export const GridItem = styled('div', {
|
||||
shouldForwardProp: (prop) => !['gridArea'].includes(prop.toString()),
|
||||
shouldForwardProp: (prop) => !['gridArea', 'sx'].includes(prop.toString()),
|
||||
})<{ gridArea: string }>(({ theme, gridArea }) => ({
|
||||
padding: theme.spacing(2, 4),
|
||||
maxHeight: '100%',
|
||||
@ -113,20 +113,3 @@ 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',
|
||||
}),
|
||||
);
|
||||
|
@ -1,180 +0,0 @@
|
||||
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,
|
||||
})),
|
||||
);
|
@ -13,6 +13,7 @@ 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,
|
||||
@ -27,7 +28,6 @@ import {
|
||||
GridItem,
|
||||
SpacedGridItem,
|
||||
StyledList,
|
||||
StyledCardTitle,
|
||||
} from './Grid';
|
||||
import { ContactAdmins, DataError } from './ProjectDetailsError';
|
||||
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
||||
|
@ -3,23 +3,201 @@ import {
|
||||
Accordion,
|
||||
AccordionDetails,
|
||||
AccordionSummary,
|
||||
Alert,
|
||||
Button,
|
||||
IconButton,
|
||||
Link,
|
||||
ListItem,
|
||||
ListItemButton,
|
||||
styled,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import React, { type FC, useEffect, useRef } from 'react';
|
||||
import LinkIcon from '@mui/icons-material/ArrowForward';
|
||||
import { WelcomeDialog } from './WelcomeDialog';
|
||||
import { useLocalStorageState } from 'hooks/useLocalStorageState';
|
||||
import { usePersonalDashboard } from 'hooks/api/getters/usePersonalDashboard/usePersonalDashboard';
|
||||
import { getFeatureTypeIcons } from 'utils/getFeatureTypeIcons';
|
||||
import type {
|
||||
PersonalDashboardSchemaFlagsItem,
|
||||
PersonalDashboardSchemaProjectsItem,
|
||||
} from '../../openapi';
|
||||
import { usePersonalDashboardProjectDetails } from 'hooks/api/getters/usePersonalDashboard/usePersonalDashboardProjectDetails';
|
||||
import useLoading from '../../hooks/useLoading';
|
||||
import { MyProjects } from './MyProjects';
|
||||
import {
|
||||
ContentGridContainer,
|
||||
FlagGrid,
|
||||
ListItemBox,
|
||||
listItemStyle,
|
||||
SpacedGridItem,
|
||||
StyledList,
|
||||
} from './Grid';
|
||||
import { ContentGridNoProjects } from './ContentGridNoProjects';
|
||||
import ExpandMore from '@mui/icons-material/ExpandMore';
|
||||
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
||||
import useSplashApi from 'hooks/api/actions/useSplashApi/useSplashApi';
|
||||
import { useAuthSplash } from 'hooks/api/getters/useAuth/useAuthSplash';
|
||||
import { useDashboardState } from './useDashboardState';
|
||||
import { MyFlags } from './MyFlags';
|
||||
|
||||
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',
|
||||
}),
|
||||
);
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
||||
// todo: move into own file
|
||||
const useDashboardState = (
|
||||
projects: PersonalDashboardSchemaProjectsItem[],
|
||||
flags: PersonalDashboardSchemaFlagsItem[],
|
||||
) => {
|
||||
type State = {
|
||||
activeProject: string | undefined;
|
||||
activeFlag: PersonalDashboardSchemaFlagsItem | undefined;
|
||||
expandProjects: boolean;
|
||||
expandFlags: boolean;
|
||||
};
|
||||
|
||||
const defaultState: State = {
|
||||
activeProject: undefined,
|
||||
activeFlag: undefined,
|
||||
expandProjects: true,
|
||||
expandFlags: true,
|
||||
};
|
||||
|
||||
const [state, setState] = useLocalStorageState<State>(
|
||||
'personal-dashboard:v1',
|
||||
defaultState,
|
||||
);
|
||||
|
||||
const updateState = (newState: Partial<State>) =>
|
||||
setState({ ...defaultState, ...state, ...newState });
|
||||
|
||||
useEffect(() => {
|
||||
const updates: Partial<State> = {};
|
||||
const setDefaultFlag =
|
||||
flags.length &&
|
||||
(!state.activeFlag ||
|
||||
!flags.some((flag) => flag.name === state.activeFlag?.name));
|
||||
|
||||
if (setDefaultFlag) {
|
||||
updates.activeFlag = flags[0];
|
||||
}
|
||||
|
||||
const setDefaultProject =
|
||||
projects.length &&
|
||||
(!state.activeProject ||
|
||||
!projects.some(
|
||||
(project) => project.id === state.activeProject,
|
||||
));
|
||||
|
||||
if (setDefaultProject) {
|
||||
updates.activeProject = projects[0].id;
|
||||
}
|
||||
|
||||
if (Object.keys(updates).length) {
|
||||
updateState(updates);
|
||||
}
|
||||
}, [
|
||||
JSON.stringify(projects, null, 2),
|
||||
JSON.stringify(flags, null, 2),
|
||||
JSON.stringify(state, null, 2),
|
||||
]);
|
||||
|
||||
const { activeFlag, activeProject } = state;
|
||||
|
||||
const setActiveFlag = (flag: PersonalDashboardSchemaFlagsItem) => {
|
||||
updateState({
|
||||
activeFlag: flag,
|
||||
});
|
||||
};
|
||||
|
||||
const setActiveProject = (projectId: string) => {
|
||||
updateState({
|
||||
activeProject: projectId,
|
||||
});
|
||||
};
|
||||
|
||||
const toggleSectionState = (section: 'flags' | 'projects') => {
|
||||
const property = section === 'flags' ? 'expandFlags' : 'expandProjects';
|
||||
updateState({
|
||||
[property]: !(state[property] ?? true),
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
activeFlag,
|
||||
setActiveFlag,
|
||||
activeProject,
|
||||
setActiveProject,
|
||||
expandFlags: state.expandFlags ?? true,
|
||||
expandProjects: state.expandProjects ?? true,
|
||||
toggleSectionState,
|
||||
};
|
||||
};
|
||||
|
||||
const WelcomeSection = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
@ -86,28 +264,6 @@ const NoActiveFlagsInfo = styled('div')(({ theme }) => ({
|
||||
gap: theme.spacing(2),
|
||||
}));
|
||||
|
||||
type DashboardState =
|
||||
| {
|
||||
state: 'flags and projects';
|
||||
// regular state; show everything
|
||||
activeFlag: any;
|
||||
activeProject: any;
|
||||
}
|
||||
| {
|
||||
state: 'projects, no flags';
|
||||
// show projects as normal, tell the user to create a flag
|
||||
activeProject: any;
|
||||
}
|
||||
| {
|
||||
state: 'no projects, no flags';
|
||||
// no projects and no flags; show information about admins, project owners, and tell the user to join a project to create a flags
|
||||
}
|
||||
| {
|
||||
state: 'flags, no projects';
|
||||
// show info about admins + project owners, regular flags
|
||||
activeFlag: any;
|
||||
};
|
||||
|
||||
export const PersonalDashboard = () => {
|
||||
const { user } = useAuthUser();
|
||||
const { trackEvent } = usePlausibleTracker();
|
||||
@ -116,11 +272,8 @@ export const PersonalDashboard = () => {
|
||||
|
||||
const name = user?.name;
|
||||
|
||||
const {
|
||||
personalDashboard,
|
||||
refetch: refetchDashboard,
|
||||
loading: personalDashboardLoading,
|
||||
} = usePersonalDashboard();
|
||||
const { personalDashboard, refetch: refetchDashboard } =
|
||||
usePersonalDashboard();
|
||||
|
||||
const projects = personalDashboard?.projects || [];
|
||||
|
||||
@ -232,22 +385,70 @@ export const PersonalDashboard = () => {
|
||||
</Typography>
|
||||
</StyledAccordionSummary>
|
||||
<StyledAccordionDetails>
|
||||
<MyFlags
|
||||
hasProjects={projects?.length > 0}
|
||||
flagData={
|
||||
personalDashboard &&
|
||||
personalDashboard.flags.length &&
|
||||
activeFlag
|
||||
? {
|
||||
state: 'flags' as const,
|
||||
activeFlag,
|
||||
flags: personalDashboard.flags,
|
||||
}
|
||||
: { state: 'no flags' as const }
|
||||
}
|
||||
setActiveFlag={setActiveFlag}
|
||||
refetchDashboard={refetchDashboard}
|
||||
/>
|
||||
<ContentGridContainer>
|
||||
<FlagGrid>
|
||||
<SpacedGridItem gridArea='flags'>
|
||||
{personalDashboard &&
|
||||
personalDashboard.flags.length > 0 ? (
|
||||
<StyledList
|
||||
disablePadding={true}
|
||||
sx={{
|
||||
height: '100%',
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
{personalDashboard.flags.map((flag) => (
|
||||
<FlagListItem
|
||||
key={flag.name}
|
||||
flag={flag}
|
||||
selected={
|
||||
flag.name ===
|
||||
activeFlag?.name
|
||||
}
|
||||
onClick={() =>
|
||||
setActiveFlag(flag)
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</StyledList>
|
||||
) : activeProject ? (
|
||||
<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'>
|
||||
{activeFlag ? (
|
||||
<FlagMetricsChart
|
||||
flag={activeFlag}
|
||||
onArchive={refetchDashboard}
|
||||
/>
|
||||
) : (
|
||||
<PlaceholderFlagMetricsChart
|
||||
label={
|
||||
'Metrics for your feature flags will be shown here'
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</SpacedGridItem>
|
||||
</FlagGrid>
|
||||
</ContentGridContainer>
|
||||
</StyledAccordionDetails>
|
||||
</SectionAccordion>
|
||||
<WelcomeDialog
|
||||
@ -260,3 +461,14 @@ export const PersonalDashboard = () => {
|
||||
</MainContent>
|
||||
);
|
||||
};
|
||||
|
||||
const FlagMetricsChart = React.lazy(() =>
|
||||
import('./FlagMetricsChart').then((module) => ({
|
||||
default: module.FlagMetricsChart,
|
||||
})),
|
||||
);
|
||||
const PlaceholderFlagMetricsChart = React.lazy(() =>
|
||||
import('./FlagMetricsChart').then((module) => ({
|
||||
default: module.PlaceholderFlagMetricsChartWithWrapper,
|
||||
})),
|
||||
);
|
||||
|
@ -1,95 +0,0 @@
|
||||
import { useLocalStorageState } from 'hooks/useLocalStorageState';
|
||||
import type {
|
||||
PersonalDashboardSchemaFlagsItem,
|
||||
PersonalDashboardSchemaProjectsItem,
|
||||
} from 'openapi';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export const useDashboardState = (
|
||||
projects: PersonalDashboardSchemaProjectsItem[],
|
||||
flags: PersonalDashboardSchemaFlagsItem[],
|
||||
) => {
|
||||
type State = {
|
||||
activeProject: string | undefined;
|
||||
activeFlag: PersonalDashboardSchemaFlagsItem | undefined;
|
||||
expandProjects: boolean;
|
||||
expandFlags: boolean;
|
||||
};
|
||||
|
||||
const defaultState: State = {
|
||||
activeProject: undefined,
|
||||
activeFlag: undefined,
|
||||
expandProjects: true,
|
||||
expandFlags: true,
|
||||
};
|
||||
|
||||
const [state, setState] = useLocalStorageState<State>(
|
||||
'personal-dashboard:v1',
|
||||
defaultState,
|
||||
);
|
||||
|
||||
const updateState = (newState: Partial<State>) =>
|
||||
setState({ ...defaultState, ...state, ...newState });
|
||||
|
||||
useEffect(() => {
|
||||
const updates: Partial<State> = {};
|
||||
const setDefaultFlag =
|
||||
flags.length &&
|
||||
(!state.activeFlag ||
|
||||
!flags.some((flag) => flag.name === state.activeFlag?.name));
|
||||
|
||||
if (setDefaultFlag) {
|
||||
updates.activeFlag = flags[0];
|
||||
}
|
||||
|
||||
const setDefaultProject =
|
||||
projects.length &&
|
||||
(!state.activeProject ||
|
||||
!projects.some(
|
||||
(project) => project.id === state.activeProject,
|
||||
));
|
||||
|
||||
if (setDefaultProject) {
|
||||
updates.activeProject = projects[0].id;
|
||||
}
|
||||
|
||||
if (Object.keys(updates).length) {
|
||||
updateState(updates);
|
||||
}
|
||||
}, [
|
||||
JSON.stringify(projects, null, 2),
|
||||
JSON.stringify(flags, null, 2),
|
||||
JSON.stringify(state, null, 2),
|
||||
]);
|
||||
|
||||
const { activeFlag, activeProject } = state;
|
||||
|
||||
const setActiveFlag = (flag: PersonalDashboardSchemaFlagsItem) => {
|
||||
updateState({
|
||||
activeFlag: flag,
|
||||
});
|
||||
};
|
||||
|
||||
const setActiveProject = (projectId: string) => {
|
||||
updateState({
|
||||
activeProject: projectId,
|
||||
});
|
||||
};
|
||||
|
||||
const toggleSectionState = (section: 'flags' | 'projects') => {
|
||||
const property = section === 'flags' ? 'expandFlags' : 'expandProjects';
|
||||
updateState({
|
||||
[property]: !(state[property] ?? true),
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
activeFlag,
|
||||
setActiveFlag,
|
||||
activeProject,
|
||||
setActiveProject,
|
||||
expandFlags: state.expandFlags ?? true,
|
||||
expandProjects: state.expandProjects ?? true,
|
||||
toggleSectionState,
|
||||
};
|
||||
};
|
Loading…
Reference in New Issue
Block a user