Skip to content

Commit 5eaf5b0

Browse files
authored
Merge pull request #1705 from SeedCompany/user-status
2 parents 69eb1bc + 105bb08 commit 5eaf5b0

File tree

7 files changed

+129
-107
lines changed

7 files changed

+129
-107
lines changed

src/common/fragments/user.graphql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ fragment DisplayUser on User {
5454
canEdit
5555
value
5656
}
57+
status {
58+
canRead
59+
canEdit
60+
value
61+
}
5762
fullName
5863
timezone {
5964
canRead

src/components/UserDataGrid/UserColumns.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,15 @@ import {
44
GridToolbarColumnsButton,
55
GridToolbarFilterButton,
66
} from '@mui/x-data-grid-pro';
7-
import { RoleLabels, RoleList } from '~/api/schema.graphql';
7+
import {
8+
RoleLabels,
9+
RoleList,
10+
UserStatusLabels,
11+
UserStatusList,
12+
} from '~/api/schema.graphql';
813
import {
914
booleanColumn,
15+
enumColumn,
1016
getInitialVisibility,
1117
multiEnumColumn,
1218
QuickFilterButton,
@@ -43,6 +49,12 @@ export const UserColumns: Array<GridColDef<User>> = [
4349
return roles.value;
4450
},
4551
},
52+
{
53+
headerName: 'Status',
54+
field: 'status',
55+
...enumColumn(UserStatusList, UserStatusLabels),
56+
valueGetter: (_, row) => row.status.value,
57+
},
4658
{
4759
headerName: 'Pinned',
4860
field: 'pinned',

src/components/UserDataGrid/userDataGridRow.graphql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,8 @@ fragment userDataGridRow on User {
1010
email {
1111
value
1212
}
13+
status {
14+
value
15+
}
1316
...TogglePin
1417
}

src/scenes/Users/Create/CreateUser.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
type SubmitResult = CreatePersonMutation['createPerson']['user'];
1414
export type CreateUserProps = Except<
1515
UserFormProps<CreatePersonInput, SubmitResult>,
16-
'prefix' | 'onSubmit'
16+
'fieldsPrefix' | 'onSubmit'
1717
>;
1818

1919
export const CreateUser = (props: CreateUserProps) => {
@@ -39,7 +39,7 @@ export const CreateUser = (props: CreateUserProps) => {
3939
});
4040
}}
4141
{...props}
42-
prefix="person"
42+
fieldsPrefix="person"
4343
onSubmit={async (input) => {
4444
const { data } = await createPerson({
4545
variables: { input },

src/scenes/Users/Detail/UserDetail.tsx

Lines changed: 34 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import { useQuery } from '@apollo/client';
22
import { Edit } from '@mui/icons-material';
3-
import { Skeleton, Tooltip, Typography } from '@mui/material';
3+
import { Box, Skeleton, Stack, Tooltip, Typography } from '@mui/material';
44
import { useInterval } from 'ahooks';
55
import { DateTime } from 'luxon';
66
import { useState } from 'react';
77
import { Helmet } from 'react-helmet-async';
88
import { useParams } from 'react-router-dom';
9-
import { makeStyles } from 'tss-react/mui';
109
import { PartialDeep } from 'type-fest';
1110
import { RoleLabels } from '~/api/schema.graphql';
1211
import { canEditAny, labelsFrom } from '~/common';
@@ -26,34 +25,7 @@ import { UsersQueryVariables } from '../List/users.graphql';
2625
import { ImpersonationToggle } from './ImpersonationToggle';
2726
import { UserDocument } from './UserDetail.graphql';
2827

29-
const useStyles = makeStyles()(({ spacing, breakpoints }) => ({
30-
root: {
31-
overflowY: 'auto',
32-
padding: spacing(4),
33-
'& > *:not(:last-child)': {
34-
marginBottom: spacing(3),
35-
},
36-
maxWidth: breakpoints.values.md,
37-
},
38-
header: {
39-
flex: 1,
40-
display: 'flex',
41-
gap: spacing(1),
42-
},
43-
name: {
44-
marginRight: spacing(2), // a little extra between text and buttons
45-
lineHeight: 'inherit', // centers text with buttons better
46-
},
47-
partnersContainer: {
48-
marginTop: spacing(1),
49-
},
50-
partner: {
51-
marginBottom: spacing(2),
52-
},
53-
}));
54-
5528
export const UserDetail = () => {
56-
const { classes } = useStyles();
5729
const { userId = '' } = useParams();
5830
const { data, error } = useQuery(UserDocument, {
5931
variables: { userId },
@@ -67,14 +39,34 @@ export const UserDetail = () => {
6739
const canEditAnyFields = canEditAny(user);
6840

6941
return (
70-
<main className={classes.root}>
42+
<Stack
43+
component="main"
44+
sx={{
45+
overflowY: 'auto',
46+
p: 4,
47+
gap: 3,
48+
maxWidth: (theme) => theme.breakpoints.values.md,
49+
}}
50+
>
7151
<Helmet title={user?.fullName ?? undefined} />
7252
{error ? (
7353
<Typography variant="h4">Error loading person</Typography>
7454
) : (
7555
<>
76-
<div className={classes.header}>
77-
<Typography variant="h2" className={classes.name}>
56+
<Box
57+
sx={{
58+
flex: 1,
59+
display: 'flex',
60+
gap: 1,
61+
}}
62+
>
63+
<Typography
64+
variant="h2"
65+
sx={{
66+
mr: 2, // a little extra between text and buttons
67+
lineHeight: 'inherit', // centers text with buttons better
68+
}}
69+
>
7870
{!user ? (
7971
<Skeleton width="20ch" />
8072
) : (
@@ -103,7 +95,12 @@ export const UserDetail = () => {
10395
/>
10496
<ToggleCommentsButton loading={!user} />
10597
<ImpersonationToggle user={user} />
106-
</div>
98+
</Box>
99+
<DisplayProperty
100+
label="Status"
101+
value={user?.status.value}
102+
loading={!user}
103+
/>
107104
<DisplayProperty
108105
label="Email"
109106
value={user?.email.value}
@@ -143,20 +140,16 @@ export const UserDetail = () => {
143140
{!!user?.partners.items.length && (
144141
<>
145142
<Typography variant="h3">Partners</Typography>
146-
<div className={classes.partnersContainer}>
143+
<Stack sx={{ mt: 1, gap: 2 }}>
147144
{user.partners.items.map((item) => (
148-
<PartnerListItemCard
149-
key={item.id}
150-
partner={item}
151-
className={classes.partner}
152-
/>
145+
<PartnerListItemCard key={item.id} partner={item} />
153146
))}
154-
</div>
147+
</Stack>
155148
</>
156149
)}
157150
</>
158151
)}
159-
</main>
152+
</Stack>
160153
);
161154
};
162155

src/scenes/Users/Edit/EditUser.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { UpdateUserDocument } from './EditUser.graphql';
77

88
export type EditUserProps = Except<
99
UserFormProps<UpdateUserInput>,
10-
'prefix' | 'onSubmit' | 'initialValues'
10+
'fieldsPrefix' | 'onSubmit' | 'initialValues'
1111
>;
1212

1313
export const EditUser = (props: EditUserProps) => {
@@ -30,6 +30,7 @@ export const EditUser = (props: EditUserProps) => {
3030
email: user.email.value,
3131
title: user.title.value,
3232
roles: user.roles.value,
33+
status: user.status.value,
3334
},
3435
}
3536
: undefined,
@@ -40,7 +41,7 @@ export const EditUser = (props: EditUserProps) => {
4041
<UserForm<UpdateUserInput & { user: { email?: Maybe<string> } }>
4142
title="Edit Person"
4243
{...props}
43-
prefix="user"
44+
fieldsPrefix="user"
4445
initialValues={initialValues}
4546
onSubmit={async ({ user }) => {
4647
await updateUser({
Lines changed: 69 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1-
import { Grid } from '@mui/material';
1+
import { Stack } from '@mui/material';
22
import { memoize } from 'lodash';
3-
import { RoleLabels } from '~/api/schema.graphql';
3+
import { SetRequired } from 'type-fest';
4+
import {
5+
RoleLabels,
6+
UserStatusLabels,
7+
UserStatusList,
8+
} from '~/api/schema.graphql';
49
import { labelFrom } from '~/common';
510
import {
611
DialogForm,
712
DialogFormProps,
813
} from '../../../components/Dialog/DialogForm';
914
import {
1015
EmailField,
11-
FieldGroup,
16+
EnumField,
1217
matchFieldIfSame,
1318
SecuredField,
1419
SubmitError,
@@ -18,10 +23,12 @@ import { AutocompleteField } from '../../../components/form/AutocompleteField';
1823
import { useSession } from '../../../components/Session';
1924
import { UserFormFragment } from './UserForm.graphql';
2025

21-
export type UserFormProps<T, R = void> = DialogFormProps<T, R> & {
26+
export type UserFormProps<T, R = void> = SetRequired<
27+
DialogFormProps<T, R>,
28+
'fieldsPrefix'
29+
> & {
2230
/** The pre-existing user to edit */
2331
user?: UserFormFragment;
24-
prefix: string;
2532
};
2633

2734
const decorators = memoize((prefix: string) => [
@@ -32,7 +39,6 @@ const decorators = memoize((prefix: string) => [
3239

3340
export const UserForm = <T, R = void>({
3441
user,
35-
prefix,
3642
...rest
3743
}: UserFormProps<T, R>) => {
3844
const { session } = useSession();
@@ -42,62 +48,64 @@ export const UserForm = <T, R = void>({
4248
maxWidth: 'sm',
4349
}}
4450
{...rest}
45-
decorators={decorators(prefix)}
51+
decorators={decorators(rest.fieldsPrefix)}
4652
>
4753
<SubmitError />
48-
<FieldGroup prefix={prefix}>
49-
<Grid container spacing={2}>
50-
<Grid item xs>
51-
<SecuredField obj={user} name="realFirstName">
52-
{(props) => (
53-
<TextField
54-
label="First Name"
55-
placeholder="Enter First Name"
56-
required
57-
{...props}
58-
/>
59-
)}
60-
</SecuredField>
61-
</Grid>
62-
<Grid item xs>
63-
<SecuredField obj={user} name="realLastName">
64-
{(props) => (
65-
<TextField
66-
label="Last Name"
67-
placeholder="Enter Last Name"
68-
required
69-
{...props}
70-
/>
71-
)}
72-
</SecuredField>
73-
</Grid>
74-
</Grid>
75-
<Grid container spacing={2}>
76-
<Grid item xs>
77-
<SecuredField obj={user} name="displayFirstName">
78-
{(props) => (
79-
<TextField
80-
label="Public First Name"
81-
placeholder="Enter Public First Name"
82-
required
83-
{...props}
84-
/>
85-
)}
86-
</SecuredField>
87-
</Grid>
88-
<Grid item xs>
89-
<SecuredField obj={user} name="displayLastName">
90-
{(props) => (
91-
<TextField
92-
label="Public Last Name"
93-
placeholder="Enter Public Last Name"
94-
required
95-
{...props}
96-
/>
97-
)}
98-
</SecuredField>
99-
</Grid>
100-
</Grid>
54+
<Stack>
55+
<Stack direction="row" spacing={2}>
56+
<SecuredField obj={user} name="realFirstName">
57+
{(props) => (
58+
<TextField
59+
label="First Name"
60+
placeholder="Enter First Name"
61+
required
62+
{...props}
63+
/>
64+
)}
65+
</SecuredField>
66+
<SecuredField obj={user} name="realLastName">
67+
{(props) => (
68+
<TextField
69+
label="Last Name"
70+
placeholder="Enter Last Name"
71+
required
72+
{...props}
73+
/>
74+
)}
75+
</SecuredField>
76+
</Stack>
77+
<Stack direction="row" spacing={2}>
78+
<SecuredField obj={user} name="displayFirstName">
79+
{(props) => (
80+
<TextField
81+
label="Public First Name"
82+
placeholder="Enter Public First Name"
83+
required
84+
{...props}
85+
/>
86+
)}
87+
</SecuredField>
88+
<SecuredField obj={user} name="displayLastName">
89+
{(props) => (
90+
<TextField
91+
label="Public Last Name"
92+
placeholder="Enter Public Last Name"
93+
required
94+
{...props}
95+
/>
96+
)}
97+
</SecuredField>
98+
</Stack>
99+
<SecuredField obj={user} name="status">
100+
{(props) => (
101+
<EnumField
102+
label="Status"
103+
options={UserStatusList}
104+
getLabel={(value) => UserStatusLabels[value]}
105+
{...props}
106+
/>
107+
)}
108+
</SecuredField>
101109
<SecuredField obj={user} name="email">
102110
{(props) => <EmailField {...props} required={false} />}
103111
</SecuredField>
@@ -149,7 +157,7 @@ export const UserForm = <T, R = void>({
149157
/>
150158
)}
151159
</SecuredField>
152-
</FieldGroup>
160+
</Stack>
153161
</DialogForm>
154162
);
155163
};

0 commit comments

Comments
 (0)