Skip to content

Commit

Permalink
feat: project members widget (#6628)
Browse files Browse the repository at this point in the history
  • Loading branch information
sjaanus committed Mar 20, 2024
1 parent f0e5d07 commit 1becfc0
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 1 deletion.
Expand Up @@ -13,6 +13,9 @@ interface IProjectMembersWidgetProps {
change?: number;
}

/**
* @deprecated in favor of ProjectMembers.tsx
*/
export const ProjectMembersWidget = ({
projectId,
memberCount,
Expand Down
Expand Up @@ -7,6 +7,7 @@ import { ProjectInsightsStats } from './ProjectInsightsStats/ProjectInsightsStat
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { useProjectInsights } from 'hooks/api/getters/useProjectInsights/useProjectInsights';
import useLoading from 'hooks/useLoading';
import { ProjectMembers } from './ProjectMembers/ProjectMembers';

const Container = styled(Box)(({ theme }) => ({
backgroundColor: theme.palette.background.paper,
Expand Down Expand Up @@ -56,7 +57,9 @@ export const ProjectInsights = () => {
<NarrowContainer>
<FlagTypesUsed featureTypeCounts={data.featureTypeCounts} />
</NarrowContainer>
<NarrowContainer>Project members</NarrowContainer>
<NarrowContainer>
<ProjectMembers projectId={projectId} members={data.members} />
</NarrowContainer>
<WideContainer>
{data.changeRequests && (
<ChangeRequests changeRequests={data.changeRequests} />
Expand Down
@@ -0,0 +1,18 @@
import { screen } from '@testing-library/react';
import { render } from 'utils/testRenderer';
import { ProjectMembers } from './ProjectMembers';

test('Show outdated project members', async () => {
const members = {
active: 10,
totalPreviousMonth: 2,
inactive: 5,
};

render(<ProjectMembers projectId={'default'} members={members} />);

await screen.findByText('15');
await screen.findByText('+13');
await screen.findByText('10');
await screen.findByText('5');
});
@@ -0,0 +1,149 @@
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { Box, styled, Typography, useTheme } from '@mui/material';
import { StatusBox } from '../ProjectInsightsStats/StatusBox';
import KeyboardArrowRight from '@mui/icons-material/KeyboardArrowRight';
import { Link } from 'react-router-dom';
import type { ProjectInsightsSchemaMembers } from '../../../../../openapi';

interface IProjectMembersProps {
members: ProjectInsightsSchemaMembers;
projectId: string;
}

const NavigationBar = styled(Link)(({ theme }) => ({
display: 'flex',
justifyContent: 'space-between',
textDecoration: 'none',
color: theme.palette.text.primary,
}));

export const StyledProjectInfoWidgetContainer = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(2.5),
}));

export const BarContainer = styled('div')(({ theme }) => ({
width: '100%',
height: '20px',
display: 'flex',
}));

const ActiveBar = styled('span', {
shouldForwardProp: (prop) => prop !== 'percentage',
})<{
percentage: number;
}>(({ theme, percentage }) => ({
width: `${percentage - 1}%`,
backgroundColor: theme.palette.success.border,
borderRadius: theme.shape.borderRadius,
}));

const InactiveBar = styled('span', {
shouldForwardProp: (prop) => prop !== 'percentage',
})<{
percentage: number;
}>(({ theme, percentage }) => ({
width: `${percentage - 1}%`,
backgroundColor: theme.palette.warning.border,
marginLeft: 'auto',
borderRadius: theme.shape.borderRadius,
}));

export const CountContainer = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(1.5),
}));

export const CountRow = styled(NavigationBar)(({ theme }) => ({
display: 'flex',
padding: theme.spacing(0.5, 1, 0, 2),
alignItems: 'center',
gap: theme.spacing(3),
alignSelf: 'stretch',
borderRadius: theme.shape.borderRadiusMedium,
background: '#F7F7FA',
}));

const StatusWithDot = styled(Box)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
gap: theme.spacing(1),
}));

const Dot = styled('span', {
shouldForwardProp: (prop) => prop !== 'color',
})<{
color?: string;
}>(({ theme, color }) => ({
height: '15px',
width: '15px',
borderRadius: '50%',
display: 'inline-block',
backgroundColor: color,
}));

export const StyledCount = styled('span')(({ theme }) => ({
fontSize: theme.typography.h1.fontSize,
fontWeight: theme.typography.fontWeightRegular,
color: theme.palette.text.primary,
}));

export const ProjectMembers = ({
members,
projectId,
}: IProjectMembersProps) => {
const { uiConfig } = useUiConfig();
const theme = useTheme();

const link = uiConfig?.versionInfo?.current?.enterprise
? `/projects/${projectId}/settings/access`
: `/admin/users`;

const { active, totalPreviousMonth, inactive } = members;

const currentMembers = active + inactive;
const change = currentMembers - (totalPreviousMonth || 0);

const activePercentage = (active / currentMembers) * 100;
const inactivePercentage = (inactive / currentMembers) * 100;

return (
<StyledProjectInfoWidgetContainer>
<NavigationBar to={link}>
<Typography variant='h3'>Project members</Typography>
<KeyboardArrowRight />
</NavigationBar>
<Box
sx={{
display: 'flex',
}}
>
<StatusBox boxText={`${currentMembers}`} change={change} />
</Box>
<BarContainer>
<ActiveBar percentage={activePercentage} />
<InactiveBar percentage={inactivePercentage} />
</BarContainer>
<CountContainer>
<CountRow to={link}>
<StatusWithDot>
<Dot color={theme.palette.success.border} />
<Box>Active</Box>
<StyledCount>{active}</StyledCount>
</StatusWithDot>
<KeyboardArrowRight />
</CountRow>
<CountRow to={link}>
<StatusWithDot>
<Dot color={theme.palette.warning.border} />
<Box>Inactive</Box>
<StyledCount>{inactive}</StyledCount>
</StatusWithDot>
<KeyboardArrowRight />
</CountRow>
</CountContainer>
</StyledProjectInfoWidgetContainer>
);
};

0 comments on commit 1becfc0

Please sign in to comment.