-
-
Notifications
You must be signed in to change notification settings - Fork 730
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Personal flags UI component #8221
Changes from 3 commits
8715e45
6a97d19
a7534ad
65429cc
3ce3f81
f06263e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -22,6 +22,8 @@ import { WelcomeDialog } from './WelcomeDialog'; | |||||||||||||||||||||
import { useLocalStorageState } from 'hooks/useLocalStorageState'; | ||||||||||||||||||||||
import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectOverview'; | ||||||||||||||||||||||
import { ProjectSetupComplete } from './ProjectSetupComplete'; | ||||||||||||||||||||||
import { usePersonalDashboard } from 'hooks/api/getters/usePersonalDashboard/usePersonalDashboard'; | ||||||||||||||||||||||
import { getFeatureTypeIcons } from 'utils/getFeatureTypeIcons'; | ||||||||||||||||||||||
|
||||||||||||||||||||||
const ScreenExplanation = styled(Typography)(({ theme }) => ({ | ||||||||||||||||||||||
marginTop: theme.spacing(1), | ||||||||||||||||||||||
|
@@ -136,13 +138,56 @@ const useProjects = () => { | |||||||||||||||||||||
return { projects, activeProject, setActiveProject }; | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
|
||||||||||||||||||||||
const FlagListItem: FC<{ | ||||||||||||||||||||||
flag: { name: string; project: string; type: string }; | ||||||||||||||||||||||
selected: boolean; | ||||||||||||||||||||||
onClick: () => void; | ||||||||||||||||||||||
}> = ({ flag, selected, onClick }) => { | ||||||||||||||||||||||
const IconComponent = getFeatureTypeIcons(flag.type); | ||||||||||||||||||||||
return ( | ||||||||||||||||||||||
<ListItem key={flag.name} disablePadding={true} sx={{ mb: 1 }}> | ||||||||||||||||||||||
<ListItemButton | ||||||||||||||||||||||
sx={projectStyle} | ||||||||||||||||||||||
selected={selected} | ||||||||||||||||||||||
onClick={onClick} | ||||||||||||||||||||||
> | ||||||||||||||||||||||
<ProjectBox> | ||||||||||||||||||||||
<IconComponent color='primary' /> | ||||||||||||||||||||||
<StyledCardTitle>{flag.name}</StyledCardTitle> | ||||||||||||||||||||||
<IconButton | ||||||||||||||||||||||
component={Link} | ||||||||||||||||||||||
href={`projects/${flag.project}/features/${flag.name}`} | ||||||||||||||||||||||
size='small' | ||||||||||||||||||||||
sx={{ ml: 'auto' }} | ||||||||||||||||||||||
> | ||||||||||||||||||||||
<LinkIcon | ||||||||||||||||||||||
titleAccess={`projects/${flag.project}/features/${flag.name}`} | ||||||||||||||||||||||
/> | ||||||||||||||||||||||
</IconButton> | ||||||||||||||||||||||
</ProjectBox> | ||||||||||||||||||||||
</ListItemButton> | ||||||||||||||||||||||
</ListItem> | ||||||||||||||||||||||
); | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
|
||||||||||||||||||||||
export const PersonalDashboard = () => { | ||||||||||||||||||||||
const { user } = useAuthUser(); | ||||||||||||||||||||||
|
||||||||||||||||||||||
const name = user?.name; | ||||||||||||||||||||||
|
||||||||||||||||||||||
const { projects, activeProject, setActiveProject } = useProjects(); | ||||||||||||||||||||||
|
||||||||||||||||||||||
const { personalDashboard } = usePersonalDashboard(); | ||||||||||||||||||||||
const [activeFlag, setActiveFlag] = useState<string | null>(null); | ||||||||||||||||||||||
useEffect(() => { | ||||||||||||||||||||||
if ( | ||||||||||||||||||||||
personalDashboard?.flags.length && | ||||||||||||||||||||||
personalDashboard?.flags.length > 0 | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this meant to be more like this (though both will work)?
Suggested change
We should also be able to simplify it:
Suggested change
though TS might require us to do
Suggested change
at which point ... maybe? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||||||
) { | ||||||||||||||||||||||
setActiveFlag(personalDashboard.flags[0].name); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
}, [JSON.stringify(personalDashboard)]); | ||||||||||||||||||||||
|
||||||||||||||||||||||
const { project: activeProjectOverview, loading } = | ||||||||||||||||||||||
useProjectOverview(activeProject); | ||||||||||||||||||||||
|
||||||||||||||||||||||
|
@@ -255,11 +300,28 @@ export const PersonalDashboard = () => { | |||||||||||||||||||||
<Typography variant='h3'>My feature flags</Typography> | ||||||||||||||||||||||
</SpacedGridItem> | ||||||||||||||||||||||
<SpacedGridItem item lg={8} md={1} /> | ||||||||||||||||||||||
<SpacedGridItem item lg={4} md={1}> | ||||||||||||||||||||||
<Typography> | ||||||||||||||||||||||
You have not created or favorited any feature flags. | ||||||||||||||||||||||
Once you do, the will show up here. | ||||||||||||||||||||||
</Typography> | ||||||||||||||||||||||
<SpacedGridItem | ||||||||||||||||||||||
item | ||||||||||||||||||||||
lg={4} | ||||||||||||||||||||||
md={1} | ||||||||||||||||||||||
sx={{ maxHeight: '400px', overflow: 'auto' }} | ||||||||||||||||||||||
> | ||||||||||||||||||||||
{personalDashboard && personalDashboard.flags.length > 0 ? ( | ||||||||||||||||||||||
personalDashboard.flags.map((flag) => { | ||||||||||||||||||||||
return ( | ||||||||||||||||||||||
<FlagListItem | ||||||||||||||||||||||
flag={flag} | ||||||||||||||||||||||
selected={flag.name === activeFlag} | ||||||||||||||||||||||
onClick={() => setActiveFlag(flag.name)} | ||||||||||||||||||||||
/> | ||||||||||||||||||||||
); | ||||||||||||||||||||||
}) | ||||||||||||||||||||||
) : ( | ||||||||||||||||||||||
<Typography> | ||||||||||||||||||||||
You have not created or favorited any feature flags. | ||||||||||||||||||||||
Once you do, the will show up here. | ||||||||||||||||||||||
</Typography> | ||||||||||||||||||||||
)} | ||||||||||||||||||||||
</SpacedGridItem> | ||||||||||||||||||||||
<SpacedGridItem item lg={8} md={1}> | ||||||||||||||||||||||
<Typography sx={{ mb: 4 }}>Feature flag metrics</Typography> | ||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import useSWR from 'swr'; | ||
import { formatApiPath } from 'utils/formatPath'; | ||
import handleErrorResponses from '../httpErrorResponseHandler'; | ||
import type { PersonalDashboardSchema } from 'openapi'; | ||
|
||
export interface IPersonalDashboardOutput { | ||
personalDashboard?: PersonalDashboardSchema; | ||
refetch: () => void; | ||
loading: boolean; | ||
error?: Error; | ||
} | ||
|
||
export const usePersonalDashboard = (): IPersonalDashboardOutput => { | ||
const { data, error, mutate } = useSWR( | ||
formatApiPath('api/admin/personal-dashboard'), | ||
fetcher, | ||
); | ||
|
||
return { | ||
personalDashboard: data, | ||
loading: !error && !data, | ||
refetch: () => mutate(), | ||
error, | ||
}; | ||
}; | ||
|
||
const fetcher = (path: string) => { | ||
return fetch(path) | ||
.then(handleErrorResponses('Personal Dashboard')) | ||
.then((res) => res.json()); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,12 @@ | ||
import type { IPersonalDashboardReadModel } from './personal-dashboard-read-model-type'; | ||
import type { | ||
IPersonalDashboardReadModel, | ||
PersonalFeature, | ||
} from './personal-dashboard-read-model-type'; | ||
|
||
export class FakePersonalDashboardReadModel | ||
implements IPersonalDashboardReadModel | ||
{ | ||
async getPersonalFeatures(userId: number): Promise<{ name: string }[]> { | ||
async getPersonalFeatures(userId: number): Promise<PersonalFeature[]> { | ||
return []; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
export type PersonalFeature = { name: string; type: string; project: string }; | ||
|
||
export interface IPersonalDashboardReadModel { | ||
getPersonalFeatures(userId: number): Promise<{ name: string }[]>; | ||
getPersonalFeatures(userId: number): Promise<PersonalFeature[]>; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will extract this code into a custom hook once we find shared items between flags and projects