QualificationsPage: add a new input to the FormDialog ('Professions')

This commit is contained in:
Dawid Wysokiński 2021-03-13 13:09:48 +01:00
parent 2d2ab01497
commit 4bdd2fc68a
6 changed files with 461 additions and 239 deletions

View File

@ -200,7 +200,7 @@ const QualificationsPage = () => {
open={
dialogType === DialogType.Create || dialogType === DialogType.Edit
}
qualification={qualificationBeingEdited as QualificationInput}
qualification={qualificationBeingEdited}
onSubmit={handleFormDialogSubmit}
onClose={() => setDialogType(DialogType.None)}
/>

View File

@ -1,12 +1,15 @@
import { useState } from 'react';
import { useForm, Controller } from 'react-hook-form';
import { pick } from 'lodash';
import { MAX_NAME_LENGTH, FORMULAS } from './constants';
import { QualificationInput } from 'libs/graphql/types';
import { useEffect, useMemo } from 'react';
import { Controller, useFieldArray, useForm } from 'react-hook-form';
import { omit, pick } from 'lodash';
import useProfessionAutocomplete from './FormDialog.useProfessionAutocomplete.js';
import { FORMULAS, MAX_NAME_LENGTH } from './constants';
import { Maybe, Qualification, QualificationInput } from 'libs/graphql/types';
import { ExtendedProfession, Input } from './types';
import { makeStyles } from '@material-ui/core/styles';
import {
Button,
CircularProgress,
Dialog,
DialogActions,
DialogContent,
@ -15,9 +18,10 @@ import {
MenuItem,
TextField,
} from '@material-ui/core';
import { Autocomplete } from '@material-ui/lab';
export interface FormDialogProps extends Pick<DialogProps, 'open'> {
qualification?: QualificationInput;
qualification?: Maybe<Qualification>;
onClose: () => void;
onSubmit: (input: QualificationInput) => Promise<boolean> | boolean;
}
@ -34,20 +38,78 @@ const FormDialog = ({
handleSubmit,
errors,
control,
} = useForm<QualificationInput>({});
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
reset,
setValue,
formState: { isSubmitting },
} = useForm<Input>({});
const { fields: selectedProfessions } = useFieldArray<
ExtendedProfession,
'key'
>({
control,
name: 'professions',
keyName: 'key',
});
const {
professions,
loading,
isLoadingSuggestions,
suggestions,
setSearch,
} = useProfessionAutocomplete({
qualificationID: qualification?.id,
omit: selectedProfessions.map(profession => profession?.id ?? 0),
});
const classes = useStyles();
const _onSubmit = async (data: QualificationInput) => {
setIsSubmitting(true);
const filtered = editMode
? pick(
data,
Object.keys(data).filter(key => data[key as keyof QualificationInput])
const autocompleteOptions:
| typeof selectedProfessions
| typeof suggestions = useMemo(() => {
return [
...suggestions
.filter(
profession =>
!selectedProfessions.some(
otherProfession => otherProfession.id === profession.id
)
)
: data;
const success = await onSubmit(filtered);
setIsSubmitting(false);
.map(p => ({ ...p, disabled: false })),
...selectedProfessions.map(p => ({
...p,
disabled: true,
})),
];
}, [suggestions, selectedProfessions]);
useEffect(() => {
reset({
professions,
});
}, [professions, reset]);
const prepateDataBeforeSave = (data: Input): QualificationInput => {
return {
...pick(data, ['name', 'description', 'formula', 'code']),
associateProfession: data.professions
.filter(
profession =>
!professions.some(
otherProfession => otherProfession.id === profession.id
)
)
.map(profession => profession.id),
dissociateProfession: professions
.filter(
profession =>
!data.professions.some(
otherProfession => otherProfession.id === profession.id
)
)
.map(profession => profession.id),
};
};
const _onSubmit = async (data: Input) => {
const success = await onSubmit(prepateDataBeforeSave(data));
if (success) {
onClose();
}
@ -113,6 +175,56 @@ const FormDialog = ({
inputRef={register}
multiline
/>
{!loading && (
<Autocomplete
multiple
options={autocompleteOptions}
getOptionLabel={option => option?.name ?? ''}
loading={isLoadingSuggestions}
value={selectedProfessions}
getOptionDisabled={option => !!option.disabled}
onChange={(_, opts, reason) => {
setValue(
'professions',
opts.map(profession => omit(profession, 'key'))
);
}}
getOptionSelected={(option, value) =>
option && value && option.id === value.id
}
renderInput={params => (
<TextField
{...params}
variant="standard"
label="Zawody"
onChange={e => {
setSearch(e.target.value);
}}
InputProps={{
...params.InputProps,
endAdornment: (
<>
{isLoadingSuggestions ? (
<CircularProgress color="inherit" size={20} />
) : null}
</>
),
}}
/>
)}
/>
)}
{selectedProfessions.map((profession, index) => {
return (
<input
type="hidden"
key={profession.id}
name={`professions[${index}].id`}
ref={register({ valueAsNumber: true })}
defaultValue={profession.id}
/>
);
})}
</DialogContent>
<DialogActions>
<Button

View File

@ -0,0 +1,80 @@
import { useMemo, useState } from 'react';
import { useApolloClient, useQuery } from '@apollo/client';
import { useDebounce } from 'react-use';
import { QUERY_PROFESSIONS } from './queries';
import {
Maybe,
Query,
QueryProfessionsArgs,
Scalars,
} from 'libs/graphql/types';
import { ExtendedProfession } from './types';
export interface Options {
qualificationID?: Maybe<Scalars['ID']>;
omit?: Scalars['ID'][];
}
const useProfessionAutocomplete = ({ qualificationID, omit = [] }: Options) => {
const [suggestions, setSuggestions] = useState<ExtendedProfession[]>([]);
const [isLoadingSuggestions, setIsLoadingSuggestions] = useState<boolean>(
false
);
const [search, setSearch] = useState<string>('');
const client = useApolloClient();
const { data, loading } = useQuery<
Pick<Query, 'professions'>,
QueryProfessionsArgs
>(QUERY_PROFESSIONS, {
fetchPolicy: 'cache-and-network',
skip: !qualificationID,
variables: {
filter: {
qualificationID: [qualificationID ?? 0],
},
},
});
const professions = useMemo(() => data?.professions.items ?? [], [data]);
const loadSuggestions = async (search: string) => {
setIsLoadingSuggestions(true);
try {
const { data } = await client.query<
Pick<Query, 'professions'>,
QueryProfessionsArgs
>({
query: QUERY_PROFESSIONS,
fetchPolicy: 'no-cache',
variables: {
filter: { nameIEQ: '%' + search + '%', idNEQ: omit },
limit: 10,
},
});
if (data.professions?.items) {
setSuggestions(data.professions.items);
}
} catch (e) {}
setIsLoadingSuggestions(false);
};
useDebounce(
() => {
loadSuggestions(search);
},
500,
[search]
);
return {
professions,
get loading() {
return this.professions.length === 0 && loading;
},
isLoadingSuggestions,
suggestions,
setSearch,
search,
};
};
export default useProfessionAutocomplete;

View File

@ -0,0 +1,17 @@
import { gql } from '@apollo/client';
export const QUERY_PROFESSIONS = gql`
query professions(
$offset: Int
$limit: Int
$filter: ProfessionFilter
$sort: [String!]
) {
professions(offset: $offset, limit: $limit, filter: $filter, sort: $sort) {
items {
id
name
}
}
}
`;

View File

@ -0,0 +1,12 @@
import { Profession, QualificationInput } from 'libs/graphql/types';
export type ExtendedProfession = Profession & {
disabled?: boolean;
};
export type Input = Omit<
QualificationInput,
'associateProfession' | 'dissociateProfession'
> & {
professions: ExtendedProfession[];
};

View File

@ -9,35 +9,67 @@ export type Scalars = {
Boolean: boolean;
Int: number;
Float: number;
Upload: any;
Time: any;
Upload: any;
};
export type UpdateManyUsersInput = {
role?: Maybe<Role>;
activated?: Maybe<Scalars['Boolean']>;
export type UserWithToken = {
token: Scalars['String'];
user: User;
};
export type QualificationFilterOr = {
nameMatch?: Maybe<Scalars['String']>;
nameIEQ?: Maybe<Scalars['String']>;
codeMatch?: Maybe<Scalars['String']>;
codeIEQ?: Maybe<Scalars['String']>;
export type ProfessionInput = {
name?: Maybe<Scalars['String']>;
description?: Maybe<Scalars['String']>;
};
export type QuestionList = {
export type QualificationList = {
total: Scalars['Int'];
items?: Maybe<Array<Question>>;
items?: Maybe<Array<Qualification>>;
};
export type ProfessionList = {
total: Scalars['Int'];
items?: Maybe<Array<Profession>>;
export enum Role {
Admin = 'admin',
User = 'user'
}
export type User = {
id: Scalars['ID'];
displayName: Scalars['String'];
role: Role;
email: Scalars['String'];
activated: Scalars['Boolean'];
createdAt: Scalars['Time'];
};
export type UserFilterOr = {
displayNameIEQ?: Maybe<Scalars['String']>;
displayNameMATCH?: Maybe<Scalars['String']>;
emailIEQ?: Maybe<Scalars['String']>;
emailMATCH?: Maybe<Scalars['String']>;
};
export type Qualification = {
id: Scalars['ID'];
slug: Scalars['String'];
name: Scalars['String'];
code: Scalars['String'];
formula?: Maybe<Scalars['String']>;
description?: Maybe<Scalars['String']>;
createdAt: Scalars['Time'];
};
export enum Answer {
A = 'a',
B = 'b',
C = 'c',
D = 'd'
}
export type QuestionInput = {
content?: Maybe<Scalars['String']>;
from?: Maybe<Scalars['String']>;
@ -60,89 +92,6 @@ export type QuestionInput = {
deleteAnswerDImage?: Maybe<Scalars['Boolean']>;
};
export type UserFilter = {
id?: Maybe<Array<Scalars['ID']>>;
idNEQ?: Maybe<Array<Scalars['ID']>>;
activated?: Maybe<Scalars['Boolean']>;
displayName?: Maybe<Array<Scalars['String']>>;
displayNameNEQ?: Maybe<Array<Scalars['String']>>;
displayNameIEQ?: Maybe<Scalars['String']>;
displayNameMATCH?: Maybe<Scalars['String']>;
email?: Maybe<Array<Scalars['String']>>;
emailNEQ?: Maybe<Array<Scalars['String']>>;
emailIEQ?: Maybe<Scalars['String']>;
emailMATCH?: Maybe<Scalars['String']>;
role?: Maybe<Array<Role>>;
roleNEQ?: Maybe<Array<Role>>;
createdAt?: Maybe<Scalars['Time']>;
createdAtGT?: Maybe<Scalars['Time']>;
createdAtGTE?: Maybe<Scalars['Time']>;
createdAtLT?: Maybe<Scalars['Time']>;
createdAtLTE?: Maybe<Scalars['Time']>;
or?: Maybe<UserFilterOr>;
};
export type Profession = {
id: Scalars['ID'];
slug: Scalars['String'];
name: Scalars['String'];
description?: Maybe<Scalars['String']>;
createdAt: Scalars['Time'];
};
export enum Answer {
A = 'a',
B = 'b',
C = 'c',
D = 'd'
}
export type QuestionFilter = {
id?: Maybe<Array<Scalars['ID']>>;
idNEQ?: Maybe<Array<Scalars['ID']>>;
from?: Maybe<Array<Scalars['String']>>;
contentIEQ?: Maybe<Scalars['String']>;
contentMATCH?: Maybe<Scalars['String']>;
qualificationID?: Maybe<Array<Scalars['Int']>>;
qualificationIDNEQ?: Maybe<Array<Scalars['Int']>>;
qualificationFilter?: Maybe<QualificationFilter>;
createdAt?: Maybe<Scalars['Time']>;
createdAtGT?: Maybe<Scalars['Time']>;
createdAtGTE?: Maybe<Scalars['Time']>;
createdAtLT?: Maybe<Scalars['Time']>;
createdAtLTE?: Maybe<Scalars['Time']>;
};
export type UserFilterOr = {
displayNameIEQ?: Maybe<Scalars['String']>;
displayNameMATCH?: Maybe<Scalars['String']>;
emailIEQ?: Maybe<Scalars['String']>;
emailMATCH?: Maybe<Scalars['String']>;
};
export type ProfessionFilter = {
id?: Maybe<Array<Scalars['ID']>>;
idNEQ?: Maybe<Array<Scalars['ID']>>;
slug?: Maybe<Array<Scalars['String']>>;
slugNEQ?: Maybe<Array<Scalars['String']>>;
name?: Maybe<Array<Scalars['String']>>;
nameNEQ?: Maybe<Array<Scalars['String']>>;
nameIEQ?: Maybe<Scalars['String']>;
nameMATCH?: Maybe<Scalars['String']>;
descriptionIEQ?: Maybe<Scalars['String']>;
descriptionMATCH?: Maybe<Scalars['String']>;
createdAt?: Maybe<Scalars['Time']>;
createdAtGT?: Maybe<Scalars['Time']>;
createdAtGTE?: Maybe<Scalars['Time']>;
createdAtLT?: Maybe<Scalars['Time']>;
createdAtLTE?: Maybe<Scalars['Time']>;
};
export type QualificationList = {
total: Scalars['Int'];
items?: Maybe<Array<Qualification>>;
};
export type Question = {
id: Scalars['ID'];
from?: Maybe<Scalars['String']>;
@ -163,81 +112,44 @@ export type Question = {
updatedAt: Scalars['Time'];
};
export type UserList = {
export type ProfessionList = {
total: Scalars['Int'];
items?: Maybe<Array<User>>;
items?: Maybe<Array<Profession>>;
};
export type UserWithToken = {
token: Scalars['String'];
user: User;
export type QualificationInput = {
name?: Maybe<Scalars['String']>;
description?: Maybe<Scalars['String']>;
code?: Maybe<Scalars['String']>;
formula?: Maybe<Scalars['String']>;
associateProfession?: Maybe<Array<Scalars['Int']>>;
dissociateProfession?: Maybe<Array<Scalars['Int']>>;
};
export type Query = {
professions: ProfessionList;
profession?: Maybe<Profession>;
qualifications: QualificationList;
qualification?: Maybe<Qualification>;
questions: QuestionList;
generateTest?: Maybe<Array<Question>>;
users: UserList;
user?: Maybe<User>;
me?: Maybe<User>;
};
export type QueryProfessionsArgs = {
filter?: Maybe<ProfessionFilter>;
limit?: Maybe<Scalars['Int']>;
offset?: Maybe<Scalars['Int']>;
sort?: Maybe<Array<Scalars['String']>>;
};
export type QueryProfessionArgs = {
id?: Maybe<Scalars['Int']>;
slug?: Maybe<Scalars['String']>;
};
export type QueryQualificationsArgs = {
filter?: Maybe<QualificationFilter>;
limit?: Maybe<Scalars['Int']>;
offset?: Maybe<Scalars['Int']>;
sort?: Maybe<Array<Scalars['String']>>;
};
export type QueryQualificationArgs = {
id?: Maybe<Scalars['Int']>;
slug?: Maybe<Scalars['String']>;
};
export type QueryQuestionsArgs = {
filter?: Maybe<QuestionFilter>;
limit?: Maybe<Scalars['Int']>;
offset?: Maybe<Scalars['Int']>;
sort?: Maybe<Array<Scalars['String']>>;
};
export type QueryGenerateTestArgs = {
qualificationIDs: Array<Scalars['ID']>;
limit?: Maybe<Scalars['Int']>;
};
export type QueryUsersArgs = {
filter?: Maybe<UserFilter>;
limit?: Maybe<Scalars['Int']>;
offset?: Maybe<Scalars['Int']>;
sort?: Maybe<Array<Scalars['String']>>;
};
export type QueryUserArgs = {
id: Scalars['Int'];
export type QualificationFilter = {
id?: Maybe<Array<Scalars['ID']>>;
idNEQ?: Maybe<Array<Scalars['ID']>>;
slug?: Maybe<Array<Scalars['String']>>;
slugNEQ?: Maybe<Array<Scalars['String']>>;
formula?: Maybe<Array<Scalars['String']>>;
formulaNEQ?: Maybe<Array<Scalars['String']>>;
name?: Maybe<Array<Scalars['String']>>;
nameNEQ?: Maybe<Array<Scalars['String']>>;
nameIEQ?: Maybe<Scalars['String']>;
nameMATCH?: Maybe<Scalars['String']>;
code?: Maybe<Array<Scalars['String']>>;
codeNEQ?: Maybe<Array<Scalars['String']>>;
codeIEQ?: Maybe<Scalars['String']>;
codeMATCH?: Maybe<Scalars['String']>;
descriptionIEQ?: Maybe<Scalars['String']>;
descriptionMATCH?: Maybe<Scalars['String']>;
professionID?: Maybe<Array<Scalars['Int']>>;
createdAt?: Maybe<Scalars['Time']>;
createdAtGT?: Maybe<Scalars['Time']>;
createdAtGTE?: Maybe<Scalars['Time']>;
createdAtLT?: Maybe<Scalars['Time']>;
createdAtLTE?: Maybe<Scalars['Time']>;
or?: Maybe<QualificationFilterOr>;
};
export type Mutation = {
@ -334,25 +246,145 @@ export type MutationSignInArgs = {
staySignedIn?: Maybe<Scalars['Boolean']>;
};
export type ProfessionInput = {
name?: Maybe<Scalars['String']>;
description?: Maybe<Scalars['String']>;
export type UserFilter = {
id?: Maybe<Array<Scalars['ID']>>;
idNEQ?: Maybe<Array<Scalars['ID']>>;
activated?: Maybe<Scalars['Boolean']>;
displayName?: Maybe<Array<Scalars['String']>>;
displayNameNEQ?: Maybe<Array<Scalars['String']>>;
displayNameIEQ?: Maybe<Scalars['String']>;
displayNameMATCH?: Maybe<Scalars['String']>;
email?: Maybe<Array<Scalars['String']>>;
emailNEQ?: Maybe<Array<Scalars['String']>>;
emailIEQ?: Maybe<Scalars['String']>;
emailMATCH?: Maybe<Scalars['String']>;
role?: Maybe<Array<Role>>;
roleNEQ?: Maybe<Array<Role>>;
createdAt?: Maybe<Scalars['Time']>;
createdAtGT?: Maybe<Scalars['Time']>;
createdAtGTE?: Maybe<Scalars['Time']>;
createdAtLT?: Maybe<Scalars['Time']>;
createdAtLTE?: Maybe<Scalars['Time']>;
or?: Maybe<UserFilterOr>;
};
export type QualificationInput = {
name?: Maybe<Scalars['String']>;
export type Profession = {
id: Scalars['ID'];
slug: Scalars['String'];
name: Scalars['String'];
description?: Maybe<Scalars['String']>;
code?: Maybe<Scalars['String']>;
formula?: Maybe<Scalars['String']>;
associateProfession?: Maybe<Array<Scalars['Int']>>;
dissociateProfession?: Maybe<Array<Scalars['Int']>>;
createdAt: Scalars['Time'];
};
export enum Role {
Admin = 'admin',
User = 'user'
}
export type QuestionFilter = {
id?: Maybe<Array<Scalars['ID']>>;
idNEQ?: Maybe<Array<Scalars['ID']>>;
from?: Maybe<Array<Scalars['String']>>;
contentIEQ?: Maybe<Scalars['String']>;
contentMATCH?: Maybe<Scalars['String']>;
qualificationID?: Maybe<Array<Scalars['Int']>>;
qualificationIDNEQ?: Maybe<Array<Scalars['Int']>>;
qualificationFilter?: Maybe<QualificationFilter>;
createdAt?: Maybe<Scalars['Time']>;
createdAtGT?: Maybe<Scalars['Time']>;
createdAtGTE?: Maybe<Scalars['Time']>;
createdAtLT?: Maybe<Scalars['Time']>;
createdAtLTE?: Maybe<Scalars['Time']>;
};
export type Query = {
professions: ProfessionList;
profession?: Maybe<Profession>;
qualifications: QualificationList;
qualification?: Maybe<Qualification>;
questions: QuestionList;
generateTest?: Maybe<Array<Question>>;
users: UserList;
user?: Maybe<User>;
me?: Maybe<User>;
};
export type QueryProfessionsArgs = {
filter?: Maybe<ProfessionFilter>;
limit?: Maybe<Scalars['Int']>;
offset?: Maybe<Scalars['Int']>;
sort?: Maybe<Array<Scalars['String']>>;
};
export type QueryProfessionArgs = {
id?: Maybe<Scalars['Int']>;
slug?: Maybe<Scalars['String']>;
};
export type QueryQualificationsArgs = {
filter?: Maybe<QualificationFilter>;
limit?: Maybe<Scalars['Int']>;
offset?: Maybe<Scalars['Int']>;
sort?: Maybe<Array<Scalars['String']>>;
};
export type QueryQualificationArgs = {
id?: Maybe<Scalars['Int']>;
slug?: Maybe<Scalars['String']>;
};
export type QueryQuestionsArgs = {
filter?: Maybe<QuestionFilter>;
limit?: Maybe<Scalars['Int']>;
offset?: Maybe<Scalars['Int']>;
sort?: Maybe<Array<Scalars['String']>>;
};
export type QueryGenerateTestArgs = {
qualificationIDs: Array<Scalars['ID']>;
limit?: Maybe<Scalars['Int']>;
};
export type QueryUsersArgs = {
filter?: Maybe<UserFilter>;
limit?: Maybe<Scalars['Int']>;
offset?: Maybe<Scalars['Int']>;
sort?: Maybe<Array<Scalars['String']>>;
};
export type QueryUserArgs = {
id: Scalars['Int'];
};
export type ProfessionFilter = {
id?: Maybe<Array<Scalars['ID']>>;
idNEQ?: Maybe<Array<Scalars['ID']>>;
slug?: Maybe<Array<Scalars['String']>>;
slugNEQ?: Maybe<Array<Scalars['String']>>;
name?: Maybe<Array<Scalars['String']>>;
nameNEQ?: Maybe<Array<Scalars['String']>>;
nameIEQ?: Maybe<Scalars['String']>;
nameMATCH?: Maybe<Scalars['String']>;
descriptionIEQ?: Maybe<Scalars['String']>;
descriptionMATCH?: Maybe<Scalars['String']>;
qualificationID?: Maybe<Array<Scalars['ID']>>;
createdAt?: Maybe<Scalars['Time']>;
createdAtGT?: Maybe<Scalars['Time']>;
createdAtGTE?: Maybe<Scalars['Time']>;
createdAtLT?: Maybe<Scalars['Time']>;
createdAtLTE?: Maybe<Scalars['Time']>;
};
export type QualificationFilterOr = {
nameMatch?: Maybe<Scalars['String']>;
nameIEQ?: Maybe<Scalars['String']>;
codeMatch?: Maybe<Scalars['String']>;
codeIEQ?: Maybe<Scalars['String']>;
};
export type UserInput = {
displayName?: Maybe<Scalars['String']>;
@ -362,48 +394,17 @@ export type UserInput = {
activated?: Maybe<Scalars['Boolean']>;
};
export type Qualification = {
id: Scalars['ID'];
slug: Scalars['String'];
name: Scalars['String'];
code: Scalars['String'];
formula?: Maybe<Scalars['String']>;
description?: Maybe<Scalars['String']>;
createdAt: Scalars['Time'];
export type QuestionList = {
total: Scalars['Int'];
items?: Maybe<Array<Question>>;
};
export type QualificationFilter = {
id?: Maybe<Array<Scalars['ID']>>;
idNEQ?: Maybe<Array<Scalars['ID']>>;
slug?: Maybe<Array<Scalars['String']>>;
slugNEQ?: Maybe<Array<Scalars['String']>>;
formula?: Maybe<Array<Scalars['String']>>;
formulaNEQ?: Maybe<Array<Scalars['String']>>;
name?: Maybe<Array<Scalars['String']>>;
nameNEQ?: Maybe<Array<Scalars['String']>>;
nameIEQ?: Maybe<Scalars['String']>;
nameMATCH?: Maybe<Scalars['String']>;
code?: Maybe<Array<Scalars['String']>>;
codeNEQ?: Maybe<Array<Scalars['String']>>;
codeIEQ?: Maybe<Scalars['String']>;
codeMATCH?: Maybe<Scalars['String']>;
descriptionIEQ?: Maybe<Scalars['String']>;
descriptionMATCH?: Maybe<Scalars['String']>;
professionID?: Maybe<Array<Scalars['Int']>>;
createdAt?: Maybe<Scalars['Time']>;
createdAtGT?: Maybe<Scalars['Time']>;
createdAtGTE?: Maybe<Scalars['Time']>;
createdAtLT?: Maybe<Scalars['Time']>;
createdAtLTE?: Maybe<Scalars['Time']>;
or?: Maybe<QualificationFilterOr>;
export type UserList = {
total: Scalars['Int'];
items?: Maybe<Array<User>>;
};
export type User = {
id: Scalars['ID'];
displayName: Scalars['String'];
role: Role;
email: Scalars['String'];
activated: Scalars['Boolean'];
createdAt: Scalars['Time'];
export type UpdateManyUsersInput = {
role?: Maybe<Role>;
activated?: Maybe<Scalars['Boolean']>;
};