mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-23 00:22:19 +01:00
feat: use actionable change request data in UI (#8613)
This PR hooks up the actionable change request data to the counter in the UI. It: - creates a getter for the data. It only exposes data. We don't really care about error or loading for this (it's not an important piece of data), so we don't expose that just yet. - Adds orval-generated schema - Uses the hook in the UI. It also stwitches the previous "notification badge" for MUI's built-in badge. We already use that badge component for the event timeline, so I thought it would make sense to do it here too. Overall, the effect is pretty good, but there's a few kinks we might wanna work out. I'll make a follow-up for that (worked out in this PR after all)
This commit is contained in:
parent
88e1ec5dc4
commit
dc1847420c
@ -14,10 +14,22 @@ import {
|
||||
StyledTabContainer,
|
||||
StyledTopRow,
|
||||
} from './Project.styles';
|
||||
import { Box, Paper, Tabs, Typography, styled } from '@mui/material';
|
||||
import {
|
||||
Badge as CounterBadge,
|
||||
Box,
|
||||
Paper,
|
||||
Tabs,
|
||||
Typography,
|
||||
styled,
|
||||
} from '@mui/material';
|
||||
import useToast from 'hooks/useToast';
|
||||
import useQueryParams from 'hooks/useQueryParams';
|
||||
import { useEffect, useState } from 'react';
|
||||
import {
|
||||
type PropsWithChildren,
|
||||
useEffect,
|
||||
useState,
|
||||
type ReactNode,
|
||||
} from 'react';
|
||||
import ProjectEnvironment from '../ProjectEnvironment/ProjectEnvironment';
|
||||
import { ProjectFeaturesArchive } from './ProjectFeaturesArchive/ProjectFeaturesArchive';
|
||||
import ProjectFlags from './ProjectFlags';
|
||||
@ -45,8 +57,8 @@ import { ProjectInsights } from './ProjectInsights/ProjectInsights';
|
||||
import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectOverview';
|
||||
import { ProjectArchived } from './ArchiveProject/ProjectArchived';
|
||||
import { usePlausibleTracker } from '../../../hooks/usePlausibleTracker';
|
||||
import { ScreenReaderOnly } from 'component/common/ScreenReaderOnly/ScreenReaderOnly';
|
||||
import { useUiFlag } from 'hooks/useUiFlag';
|
||||
import { useActionableChangeRequests } from 'hooks/api/getters/useActionableChangeRequests/useActionableChangeRequests';
|
||||
|
||||
const StyledBadge = styled(Badge)(({ theme }) => ({
|
||||
position: 'absolute',
|
||||
@ -64,46 +76,32 @@ interface ITab {
|
||||
flag?: keyof UiFlags;
|
||||
new?: boolean;
|
||||
isEnterprise?: boolean;
|
||||
label?: () => ReactNode;
|
||||
}
|
||||
|
||||
const CircleContainer = styled('div')(({ theme }) => ({
|
||||
position: 'absolute',
|
||||
width: theme.spacing(2.5),
|
||||
height: theme.spacing(2.5),
|
||||
display: 'grid',
|
||||
placeItems: 'center',
|
||||
borderRadius: '50%',
|
||||
|
||||
background: theme.palette.background.alternative,
|
||||
color: theme.palette.primary.contrastText,
|
||||
fontSize: theme.typography.body2.fontSize,
|
||||
|
||||
// todo: revisit these values later
|
||||
top: 10,
|
||||
right: 0,
|
||||
[theme.breakpoints.down('md')]: {
|
||||
top: 2,
|
||||
const StyledCounterBadge = styled(CounterBadge)(({ theme }) => ({
|
||||
'.MuiBadge-badge': {
|
||||
backgroundColor: theme.palette.background.alternative,
|
||||
right: '2px',
|
||||
},
|
||||
flex: 'auto',
|
||||
justifyContent: 'center',
|
||||
minHeight: '1.5em',
|
||||
alignItems: 'center',
|
||||
}));
|
||||
|
||||
const ActionableChangeRequestsIndicator = () => {
|
||||
// todo: useSWR for this instead (maybe conditional)
|
||||
const count = 0;
|
||||
const TabText = styled('span')(({ theme }) => ({
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
if (count <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const renderedCount = count > 9 ? '9+' : count;
|
||||
const ChangeRequestsLabel = () => {
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const { total } = useActionableChangeRequests(projectId);
|
||||
|
||||
return (
|
||||
<CircleContainer>
|
||||
<ScreenReaderOnly>You can move</ScreenReaderOnly>
|
||||
{renderedCount}
|
||||
<ScreenReaderOnly>
|
||||
change requests into their next phase.
|
||||
</ScreenReaderOnly>
|
||||
</CircleContainer>
|
||||
<StyledCounterBadge badgeContent={total ?? 0} color='primary'>
|
||||
<TabText>Change requests</TabText>
|
||||
</StyledCounterBadge>
|
||||
);
|
||||
};
|
||||
|
||||
@ -160,6 +158,7 @@ export const Project = () => {
|
||||
path: `${basePath}/change-requests`,
|
||||
name: 'change-request',
|
||||
isEnterprise: true,
|
||||
label: simplifyProjectOverview ? ChangeRequestsLabel : undefined,
|
||||
},
|
||||
{
|
||||
title: 'Applications',
|
||||
@ -300,7 +299,7 @@ export const Project = () => {
|
||||
<StyledTab
|
||||
data-loading-project
|
||||
key={tab.title}
|
||||
label={tab.title}
|
||||
label={tab.label ? tab.label() : tab.title}
|
||||
value={tab.path}
|
||||
onClick={() => {
|
||||
if (tab.title !== 'Flags') {
|
||||
@ -329,11 +328,6 @@ export const Project = () => {
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
{simplifyProjectOverview &&
|
||||
tab.name ===
|
||||
'change-request' && (
|
||||
<ActionableChangeRequestsIndicator />
|
||||
)}
|
||||
{(tab.isEnterprise &&
|
||||
isPro() &&
|
||||
enterpriseIcon) ||
|
||||
|
@ -0,0 +1,30 @@
|
||||
import { formatApiPath } from 'utils/formatPath';
|
||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||
import type { ActionableChangeRequestsSchema } from 'openapi/models/actionableChangeRequestsSchema';
|
||||
import { useEnterpriseSWR } from '../useEnterpriseSWR/useEnterpriseSWR';
|
||||
|
||||
interface IUseActionableChangeRequestsOutput {
|
||||
total?: number;
|
||||
}
|
||||
|
||||
export const useActionableChangeRequests = (
|
||||
projectId: string,
|
||||
): IUseActionableChangeRequestsOutput => {
|
||||
const { data } = useEnterpriseSWR<ActionableChangeRequestsSchema>(
|
||||
{ total: 0 },
|
||||
formatApiPath(
|
||||
`api/admin/projects/${projectId}/change-requests/actionable`,
|
||||
),
|
||||
fetcher,
|
||||
);
|
||||
|
||||
return {
|
||||
total: data?.total,
|
||||
};
|
||||
};
|
||||
|
||||
const fetcher = (path: string) => {
|
||||
return fetch(path)
|
||||
.then(handleErrorResponses('Actionable change requests'))
|
||||
.then((res) => res.json());
|
||||
};
|
@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Generated by Orval
|
||||
* Do not edit manually.
|
||||
* See `gen:api` script in package.json
|
||||
*/
|
||||
|
||||
/**
|
||||
* Data related to actionable change requests in a project.
|
||||
*/
|
||||
export interface ActionableChangeRequestsSchema {
|
||||
/**
|
||||
* The number of actionable change requests in the project.
|
||||
* @minimum 0
|
||||
*/
|
||||
total: number;
|
||||
}
|
Loading…
Reference in New Issue
Block a user