feature: add user project count to Admin component

This commit is contained in:
Jordan Knott
2020-07-17 19:40:05 -05:00
parent ccaa97e2bb
commit 68fa7aef94
23 changed files with 1140 additions and 140 deletions

View File

@ -1,18 +1,18 @@
import React, {useRef} from 'react';
import React, { useRef } from 'react';
import Admin from '.';
import {theme} from 'App/ThemeStyles';
import { theme } from 'App/ThemeStyles';
import NormalizeStyles from 'App/NormalizeStyles';
import BaseStyles from 'App/BaseStyles';
import {ThemeProvider} from 'styled-components';
import {action} from '@storybook/addon-actions';
import { ThemeProvider } from 'styled-components';
import { action } from '@storybook/addon-actions';
export default {
component: Admin,
title: 'Admin',
parameters: {
backgrounds: [
{name: 'gray', value: '#f8f8f8', default: true},
{name: 'white', value: '#ffffff'},
{ name: 'gray', value: '#f8f8f8', default: true },
{ name: 'white', value: '#ffffff' },
],
},
};
@ -33,13 +33,21 @@ export const Default = () => {
id: '1',
username: 'jordanthedev',
email: 'jordan@jordanthedev.com',
role: {code: 'admin', name: 'Admin'},
role: { code: 'admin', name: 'Admin' },
fullName: 'Jordan Knott',
profileIcon: {
bgColor: '#fff',
initials: 'JK',
url: null,
},
owned: {
teams: [{ id: '1', name: 'Team' }],
projects: [{ id: '2', name: 'Project' }],
},
member: {
teams: [],
projects: [],
},
},
]}
onAddUser={action('add user')}

View File

@ -509,7 +509,7 @@ const ListTable: React.FC<ListTableProps> = ({ users, onDeleteUser }) => {
rowSelection="multiple"
defaultColDef={data.defaultColDef}
columnDefs={data.columnDefs}
rowData={users.map(u => ({ ...u, roleName: u.role.name }))}
rowData={users.map(u => ({ ...u, roleName: 'member' }))}
frameworkComponents={data.frameworkComponents}
onFirstDataRendered={params => {
params.api.sizeColumnsToFit();
@ -717,46 +717,49 @@ const Admin: React.FC<AdminProps> = ({
</ListActions>
</MemberListHeader>
<MemberList>
{users.map(member => (
<MemberListItem>
<MemberProfile showRoleIcons size={32} onMemberProfile={() => {}} member={member} />
<MemberListItemDetails>
<MemberItemName>{member.fullName}</MemberItemName>
<MemberItemUsername>{`@${member.username}`}</MemberItemUsername>
</MemberListItemDetails>
<MemberItemOptions>
<MemberItemOption variant="flat">On 6 projects</MemberItemOption>
<MemberItemOption
variant="outline"
onClick={$target => {
showPopup(
$target,
<TeamRoleManagerPopup
user={member}
warning={member.role && member.role.code === 'owner' ? warning : null}
updateUserPassword={(user, password) => {
onUpdateUserPassword(user, password);
}}
canChangeRole={member.role && member.role.code !== 'owner'}
onChangeRole={roleCode => {
updateUserRole({ variables: { userID: member.id, roleCode } });
}}
onRemoveFromTeam={
member.role && member.role.code === 'owner'
? undefined
: () => {
hidePopup();
}
}
/>,
);
}}
>
Manage
</MemberItemOption>
</MemberItemOptions>
</MemberListItem>
))}
{users.map(member => {
const projectTotal = member.owned.projects.length + member.member.projects.length;
return (
<MemberListItem>
<MemberProfile showRoleIcons size={32} onMemberProfile={() => {}} member={member} />
<MemberListItemDetails>
<MemberItemName>{member.fullName}</MemberItemName>
<MemberItemUsername>{`@${member.username}`}</MemberItemUsername>
</MemberListItemDetails>
<MemberItemOptions>
<MemberItemOption variant="flat">{`On ${projectTotal} projects`}</MemberItemOption>
<MemberItemOption
variant="outline"
onClick={$target => {
showPopup(
$target,
<TeamRoleManagerPopup
user={member}
warning={member.role && member.role.code === 'owner' ? warning : null}
updateUserPassword={(user, password) => {
onUpdateUserPassword(user, password);
}}
canChangeRole={(member.role && member.role.code !== 'owner') ?? false}
onChangeRole={roleCode => {
updateUserRole({ variables: { userID: member.id, roleCode } });
}}
onRemoveFromTeam={
member.role && member.role.code === 'owner'
? undefined
: () => {
hidePopup();
}
}
/>,
);
}}
>
Manage
</MemberItemOption>
</MemberItemOptions>
</MemberListItem>
);
})}
</MemberList>
</MemberListWrapper>
</TabContent>

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useRef } from 'react';
import styled, { css } from 'styled-components/macro';
const InputWrapper = styled.div<{ width: string }>`
@ -85,6 +85,8 @@ type InputProps = {
icon?: JSX.Element;
type?: string;
autocomplete?: boolean;
autoFocus?: boolean;
autoSelect?: boolean;
id?: string;
name?: string;
className?: string;
@ -92,12 +94,32 @@ type InputProps = {
onClick?: (e: React.MouseEvent<HTMLInputElement>) => void;
};
function useCombinedRefs(...refs: any) {
const targetRef = React.useRef();
React.useEffect(() => {
refs.forEach((ref: any) => {
if (!ref) return;
if (typeof ref === 'function') {
ref(targetRef.current);
} else {
ref.current = targetRef.current;
}
});
}, [refs]);
return targetRef;
}
const Input = React.forwardRef(
(
{
width = 'auto',
variant = 'normal',
type = 'text',
autoFocus = false,
autoSelect = false,
autocomplete,
label,
placeholder,
@ -111,9 +133,25 @@ const Input = React.forwardRef(
}: InputProps,
$ref: any,
) => {
const [hasValue, setHasValue] = useState(false);
const [hasValue, setHasValue] = useState(defaultValue !== '');
const borderColor = variant === 'normal' ? 'rgba(0, 0, 0, 0.2)' : '#414561';
const focusBg = variant === 'normal' ? 'rgba(38, 44, 73, )' : 'rgba(16, 22, 58, 1)';
// Merge forwarded ref and internal ref in order to be able to access the ref in the useEffect
// The forwarded ref is not accessible by itself, which is what the innerRef & combined ref is for
// TODO(jordanknott): This is super ugly, find a better approach?
const $innerRef = React.useRef<HTMLInputElement>(null);
const combinedRef: any = useCombinedRefs($ref, $innerRef);
useEffect(() => {
if (combinedRef && combinedRef.current) {
if (autoFocus) {
combinedRef.current.focus();
}
if (autoSelect) {
combinedRef.current.select();
}
}
}, []);
return (
<InputWrapper className={className} width={width}>
<InputInput
@ -121,7 +159,7 @@ const Input = React.forwardRef(
setHasValue((e.currentTarget.value !== '' || floatingLabel) ?? false);
}}
hasValue={hasValue}
ref={$ref}
ref={combinedRef}
id={id}
type={type}
name={name}

View File

@ -67,7 +67,8 @@ export type Member = {
fullName: Scalars['String'];
username: Scalars['String'];
profileIcon: ProfileIcon;
owned?: Maybe<OwnersList>;
owned: OwnedList;
member: MemberList;
};
export type RefreshToken = {
@ -84,6 +85,18 @@ export type Role = {
name: Scalars['String'];
};
export type OwnedList = {
__typename?: 'OwnedList';
teams: Array<Team>;
projects: Array<Project>;
};
export type MemberList = {
__typename?: 'MemberList';
teams: Array<Team>;
projects: Array<Project>;
};
export type UserAccount = {
__typename?: 'UserAccount';
id: Scalars['ID'];
@ -94,6 +107,8 @@ export type UserAccount = {
role: Role;
username: Scalars['String'];
profileIcon: ProfileIcon;
owned: OwnedList;
member: MemberList;
};
export type Team = {
@ -1096,6 +1111,24 @@ export type FindProjectQuery = (
), profileIcon: (
{ __typename?: 'ProfileIcon' }
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
), owned: (
{ __typename?: 'OwnedList' }
& { teams: Array<(
{ __typename?: 'Team' }
& Pick<Team, 'id' | 'name'>
)>, projects: Array<(
{ __typename?: 'Project' }
& Pick<Project, 'id' | 'name'>
)> }
), member: (
{ __typename?: 'MemberList' }
& { teams: Array<(
{ __typename?: 'Team' }
& Pick<Team, 'id' | 'name'>
)>, projects: Array<(
{ __typename?: 'Project' }
& Pick<Project, 'id' | 'name'>
)> }
) }
)> }
);
@ -1611,12 +1644,27 @@ export type GetTeamQuery = (
& { role: (
{ __typename?: 'Role' }
& Pick<Role, 'code' | 'name'>
), owned?: Maybe<(
{ __typename?: 'OwnersList' }
& Pick<OwnersList, 'projects' | 'teams'>
)>, profileIcon: (
), profileIcon: (
{ __typename?: 'ProfileIcon' }
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
), owned: (
{ __typename?: 'OwnedList' }
& { teams: Array<(
{ __typename?: 'Team' }
& Pick<Team, 'id' | 'name'>
)>, projects: Array<(
{ __typename?: 'Project' }
& Pick<Project, 'id' | 'name'>
)> }
), member: (
{ __typename?: 'MemberList' }
& { teams: Array<(
{ __typename?: 'Team' }
& Pick<Team, 'id' | 'name'>
)>, projects: Array<(
{ __typename?: 'Project' }
& Pick<Project, 'id' | 'name'>
)> }
) }
)> }
), projects: Array<(
@ -1635,6 +1683,24 @@ export type GetTeamQuery = (
), profileIcon: (
{ __typename?: 'ProfileIcon' }
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
), owned: (
{ __typename?: 'OwnedList' }
& { teams: Array<(
{ __typename?: 'Team' }
& Pick<Team, 'id' | 'name'>
)>, projects: Array<(
{ __typename?: 'Project' }
& Pick<Project, 'id' | 'name'>
)> }
), member: (
{ __typename?: 'MemberList' }
& { teams: Array<(
{ __typename?: 'Team' }
& Pick<Team, 'id' | 'name'>
)>, projects: Array<(
{ __typename?: 'Project' }
& Pick<Project, 'id' | 'name'>
)> }
) }
)> }
);
@ -1876,6 +1942,24 @@ export type UsersQuery = (
), profileIcon: (
{ __typename?: 'ProfileIcon' }
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
), owned: (
{ __typename?: 'OwnedList' }
& { teams: Array<(
{ __typename?: 'Team' }
& Pick<Team, 'id' | 'name'>
)>, projects: Array<(
{ __typename?: 'Project' }
& Pick<Project, 'id' | 'name'>
)> }
), member: (
{ __typename?: 'MemberList' }
& { teams: Array<(
{ __typename?: 'Team' }
& Pick<Team, 'id' | 'name'>
)>, projects: Array<(
{ __typename?: 'Project' }
& Pick<Project, 'id' | 'name'>
)> }
) }
)> }
);
@ -2278,6 +2362,26 @@ export const FindProjectDocument = gql`
initials
bgColor
}
owned {
teams {
id
name
}
projects {
id
name
}
}
member {
teams {
id
name
}
projects {
id
name
}
}
}
}
${TaskFieldsFragmentDoc}`;
@ -3288,15 +3392,31 @@ export const GetTeamDocument = gql`
code
name
}
owned {
projects
teams
}
profileIcon {
url
initials
bgColor
}
owned {
teams {
id
name
}
projects {
id
name
}
}
member {
teams {
id
name
}
projects {
id
name
}
}
}
}
projects(input: {teamID: $teamID}) {
@ -3321,6 +3441,26 @@ export const GetTeamDocument = gql`
initials
bgColor
}
owned {
teams {
id
name
}
projects {
id
name
}
}
member {
teams {
id
name
}
projects {
id
name
}
}
}
}
`;
@ -3834,6 +3974,26 @@ export const UsersDocument = gql`
initials
bgColor
}
owned {
teams {
id
name
}
projects {
id
name
}
}
member {
teams {
id
name
}
projects {
id
name
}
}
}
}
`;

View File

@ -59,6 +59,26 @@ query findProject($projectId: String!) {
initials
bgColor
}
owned {
teams {
id
name
}
projects {
id
name
}
}
member {
teams {
id
name
}
projects {
id
name
}
}
}
${TASK_FRAGMENT}
}

View File

@ -14,15 +14,31 @@ export const GET_TEAM_QUERY = gql`
code
name
}
owned {
projects
teams
}
profileIcon {
url
initials
bgColor
}
owned {
teams {
id
name
}
projects {
id
name
}
}
member {
teams {
id
name
}
projects {
id
name
}
}
}
}
projects(input: { teamID: $teamID }) {
@ -47,6 +63,26 @@ export const GET_TEAM_QUERY = gql`
initials
bgColor
}
owned {
teams {
id
name
}
projects {
id
name
}
}
member {
teams {
id
name
}
projects {
id
name
}
}
}
}
`;

View File

@ -13,6 +13,26 @@ query users {
initials
bgColor
}
owned {
teams {
id
name
}
projects {
id
name
}
}
member {
teams {
id
name
}
projects {
id
name
}
}
}
}