Merge pull request #111 from anik-ghosh-au7/feat/manage-users
Feat/manage users
This commit is contained in:
commit
ea14cc1743
2315
dashboard/package-lock.json
generated
2315
dashboard/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -17,6 +17,7 @@
|
||||||
"@types/react": "^17.0.38",
|
"@types/react": "^17.0.38",
|
||||||
"@types/react-dom": "^17.0.11",
|
"@types/react-dom": "^17.0.11",
|
||||||
"@types/react-router-dom": "^5.3.2",
|
"@types/react-router-dom": "^5.3.2",
|
||||||
|
"dayjs": "^1.10.7",
|
||||||
"esbuild": "^0.14.9",
|
"esbuild": "^0.14.9",
|
||||||
"framer-motion": "^5.5.5",
|
"framer-motion": "^5.5.5",
|
||||||
"graphql": "^16.2.0",
|
"graphql": "^16.2.0",
|
||||||
|
|
250
dashboard/src/components/EditUserModal.tsx
Normal file
250
dashboard/src/components/EditUserModal.tsx
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Center,
|
||||||
|
Flex,
|
||||||
|
MenuItem,
|
||||||
|
Modal,
|
||||||
|
ModalBody,
|
||||||
|
ModalCloseButton,
|
||||||
|
ModalContent,
|
||||||
|
ModalFooter,
|
||||||
|
ModalHeader,
|
||||||
|
ModalOverlay,
|
||||||
|
Stack,
|
||||||
|
useDisclosure,
|
||||||
|
Text,
|
||||||
|
useToast,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { useClient } from 'urql';
|
||||||
|
import { FaSave } from 'react-icons/fa';
|
||||||
|
import InputField from './InputField';
|
||||||
|
import {
|
||||||
|
ArrayInputType,
|
||||||
|
DateInputType,
|
||||||
|
SelectInputType,
|
||||||
|
TextInputType,
|
||||||
|
} from '../constants';
|
||||||
|
import { getObjectDiff } from '../utils';
|
||||||
|
import { UpdateUser } from '../graphql/mutation';
|
||||||
|
|
||||||
|
const GenderTypes = {
|
||||||
|
Undisclosed: null,
|
||||||
|
Male: 'Male',
|
||||||
|
Female: 'Female',
|
||||||
|
};
|
||||||
|
|
||||||
|
interface userDataTypes {
|
||||||
|
id: string;
|
||||||
|
email: string;
|
||||||
|
given_name: string;
|
||||||
|
family_name: string;
|
||||||
|
middle_name: string;
|
||||||
|
nickname: string;
|
||||||
|
gender: string;
|
||||||
|
birthdate: string;
|
||||||
|
phone_number: string;
|
||||||
|
picture: string;
|
||||||
|
roles: [string] | [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const EditUserModal = ({
|
||||||
|
user,
|
||||||
|
updateUserList,
|
||||||
|
}: {
|
||||||
|
user: userDataTypes;
|
||||||
|
updateUserList: Function;
|
||||||
|
}) => {
|
||||||
|
const client = useClient();
|
||||||
|
const toast = useToast();
|
||||||
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
|
const [userData, setUserData] = React.useState<userDataTypes>({
|
||||||
|
id: '',
|
||||||
|
email: '',
|
||||||
|
given_name: '',
|
||||||
|
family_name: '',
|
||||||
|
middle_name: '',
|
||||||
|
nickname: '',
|
||||||
|
gender: '',
|
||||||
|
birthdate: '',
|
||||||
|
phone_number: '',
|
||||||
|
picture: '',
|
||||||
|
roles: [],
|
||||||
|
});
|
||||||
|
React.useEffect(() => {
|
||||||
|
setUserData(user);
|
||||||
|
}, []);
|
||||||
|
const saveHandler = async () => {
|
||||||
|
const diff = getObjectDiff(user, userData);
|
||||||
|
const updatedUserData = diff.reduce(
|
||||||
|
(acc: any, property: string) => ({
|
||||||
|
...acc,
|
||||||
|
// @ts-ignore
|
||||||
|
[property]: userData[property],
|
||||||
|
}),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
const res = await client
|
||||||
|
.mutation(UpdateUser, { params: { ...updatedUserData, id: userData.id } })
|
||||||
|
.toPromise();
|
||||||
|
if (res.error) {
|
||||||
|
toast({
|
||||||
|
title: 'User data update failed',
|
||||||
|
isClosable: true,
|
||||||
|
status: 'error',
|
||||||
|
position: 'bottom-right',
|
||||||
|
});
|
||||||
|
} else if (res.data?._update_user?.id) {
|
||||||
|
toast({
|
||||||
|
title: 'User data update successful',
|
||||||
|
isClosable: true,
|
||||||
|
status: 'success',
|
||||||
|
position: 'bottom-right',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
onClose();
|
||||||
|
updateUserList();
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<MenuItem onClick={onOpen}>Edit User Details</MenuItem>
|
||||||
|
<Modal isOpen={isOpen} onClose={onClose}>
|
||||||
|
<ModalOverlay />
|
||||||
|
<ModalContent>
|
||||||
|
<ModalHeader>Edit User Details</ModalHeader>
|
||||||
|
<ModalCloseButton />
|
||||||
|
<ModalBody>
|
||||||
|
<Stack>
|
||||||
|
<Flex>
|
||||||
|
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||||
|
<Text fontSize="sm">Given Name:</Text>
|
||||||
|
</Flex>
|
||||||
|
<Center w="70%">
|
||||||
|
<InputField
|
||||||
|
variables={userData}
|
||||||
|
setVariables={setUserData}
|
||||||
|
inputType={TextInputType.GIVEN_NAME}
|
||||||
|
/>
|
||||||
|
</Center>
|
||||||
|
</Flex>
|
||||||
|
<Flex>
|
||||||
|
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||||
|
<Text fontSize="sm">Middle Name:</Text>
|
||||||
|
</Flex>
|
||||||
|
<Center w="70%">
|
||||||
|
<InputField
|
||||||
|
variables={userData}
|
||||||
|
setVariables={setUserData}
|
||||||
|
inputType={TextInputType.MIDDLE_NAME}
|
||||||
|
/>
|
||||||
|
</Center>
|
||||||
|
</Flex>
|
||||||
|
<Flex>
|
||||||
|
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||||
|
<Text fontSize="sm">Family Name:</Text>
|
||||||
|
</Flex>
|
||||||
|
<Center w="70%">
|
||||||
|
<InputField
|
||||||
|
variables={userData}
|
||||||
|
setVariables={setUserData}
|
||||||
|
inputType={TextInputType.FAMILY_NAME}
|
||||||
|
/>
|
||||||
|
</Center>
|
||||||
|
</Flex>
|
||||||
|
<Flex>
|
||||||
|
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||||
|
<Text fontSize="sm">Birth Date:</Text>
|
||||||
|
</Flex>
|
||||||
|
<Center w="70%">
|
||||||
|
<InputField
|
||||||
|
variables={userData}
|
||||||
|
setVariables={setUserData}
|
||||||
|
inputType={DateInputType.BIRTHDATE}
|
||||||
|
/>
|
||||||
|
</Center>
|
||||||
|
</Flex>
|
||||||
|
<Flex>
|
||||||
|
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||||
|
<Text fontSize="sm">Nickname:</Text>
|
||||||
|
</Flex>
|
||||||
|
<Center w="70%">
|
||||||
|
<InputField
|
||||||
|
variables={userData}
|
||||||
|
setVariables={setUserData}
|
||||||
|
inputType={TextInputType.NICKNAME}
|
||||||
|
/>
|
||||||
|
</Center>
|
||||||
|
</Flex>
|
||||||
|
<Flex>
|
||||||
|
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||||
|
<Text fontSize="sm">Gender:</Text>
|
||||||
|
</Flex>
|
||||||
|
<Center w="70%">
|
||||||
|
<InputField
|
||||||
|
variables={userData}
|
||||||
|
setVariables={setUserData}
|
||||||
|
inputType={SelectInputType.GENDER}
|
||||||
|
value={userData.gender}
|
||||||
|
options={GenderTypes}
|
||||||
|
/>
|
||||||
|
</Center>
|
||||||
|
</Flex>
|
||||||
|
<Flex>
|
||||||
|
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||||
|
<Text fontSize="sm">Phone Number:</Text>
|
||||||
|
</Flex>
|
||||||
|
<Center w="70%">
|
||||||
|
<InputField
|
||||||
|
variables={userData}
|
||||||
|
setVariables={setUserData}
|
||||||
|
inputType={TextInputType.PHONE_NUMBER}
|
||||||
|
/>
|
||||||
|
</Center>
|
||||||
|
</Flex>
|
||||||
|
<Flex>
|
||||||
|
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||||
|
<Text fontSize="sm">Picture:</Text>
|
||||||
|
</Flex>
|
||||||
|
<Center w="70%">
|
||||||
|
<InputField
|
||||||
|
variables={userData}
|
||||||
|
setVariables={setUserData}
|
||||||
|
inputType={TextInputType.PICTURE}
|
||||||
|
/>
|
||||||
|
</Center>
|
||||||
|
</Flex>
|
||||||
|
<Flex>
|
||||||
|
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||||
|
<Text fontSize="sm">Roles:</Text>
|
||||||
|
</Flex>
|
||||||
|
<Center w="70%">
|
||||||
|
<InputField
|
||||||
|
variables={userData}
|
||||||
|
setVariables={setUserData}
|
||||||
|
inputType={ArrayInputType.USER_ROLES}
|
||||||
|
/>
|
||||||
|
</Center>
|
||||||
|
</Flex>
|
||||||
|
</Stack>
|
||||||
|
</ModalBody>
|
||||||
|
|
||||||
|
<ModalFooter>
|
||||||
|
<Button
|
||||||
|
leftIcon={<FaSave />}
|
||||||
|
colorScheme="blue"
|
||||||
|
variant="solid"
|
||||||
|
onClick={saveHandler}
|
||||||
|
isDisabled={false}
|
||||||
|
>
|
||||||
|
<Center h="100%" pt="5%">
|
||||||
|
Save
|
||||||
|
</Center>
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EditUserModal;
|
|
@ -29,13 +29,14 @@ import {
|
||||||
TextInputType,
|
TextInputType,
|
||||||
TextAreaInputType,
|
TextAreaInputType,
|
||||||
SwitchInputType,
|
SwitchInputType,
|
||||||
|
DateInputType,
|
||||||
} from '../constants';
|
} from '../constants';
|
||||||
import { copyTextToClipboard } from '../utils';
|
import { copyTextToClipboard } from '../utils';
|
||||||
|
|
||||||
const InputField = ({
|
const InputField = ({
|
||||||
inputType,
|
inputType,
|
||||||
envVariables,
|
variables,
|
||||||
setEnvVariables,
|
setVariables,
|
||||||
fieldVisibility,
|
fieldVisibility,
|
||||||
setFieldVisibility,
|
setFieldVisibility,
|
||||||
...downshiftProps
|
...downshiftProps
|
||||||
|
@ -51,12 +52,14 @@ const InputField = ({
|
||||||
DEFAULT_ROLES: false,
|
DEFAULT_ROLES: false,
|
||||||
PROTECTED_ROLES: false,
|
PROTECTED_ROLES: false,
|
||||||
ALLOWED_ORIGINS: false,
|
ALLOWED_ORIGINS: false,
|
||||||
|
roles: false,
|
||||||
});
|
});
|
||||||
const [inputData, setInputData] = React.useState<Record<string, string>>({
|
const [inputData, setInputData] = React.useState<Record<string, string>>({
|
||||||
ROLES: '',
|
ROLES: '',
|
||||||
DEFAULT_ROLES: '',
|
DEFAULT_ROLES: '',
|
||||||
PROTECTED_ROLES: '',
|
PROTECTED_ROLES: '',
|
||||||
ALLOWED_ORIGINS: '',
|
ALLOWED_ORIGINS: '',
|
||||||
|
roles: '',
|
||||||
});
|
});
|
||||||
const updateInputHandler = (
|
const updateInputHandler = (
|
||||||
type: string,
|
type: string,
|
||||||
|
@ -65,20 +68,20 @@ const InputField = ({
|
||||||
) => {
|
) => {
|
||||||
if (operation === ArrayInputOperations.APPEND) {
|
if (operation === ArrayInputOperations.APPEND) {
|
||||||
if (inputData[type] !== '') {
|
if (inputData[type] !== '') {
|
||||||
setEnvVariables({
|
setVariables({
|
||||||
...envVariables,
|
...variables,
|
||||||
[type]: [...envVariables[type], inputData[type]],
|
[type]: [...variables[type], inputData[type]],
|
||||||
});
|
});
|
||||||
setInputData({ ...inputData, [type]: '' });
|
setInputData({ ...inputData, [type]: '' });
|
||||||
}
|
}
|
||||||
setInputFieldVisibility({ ...inputFieldVisibility, [type]: false });
|
setInputFieldVisibility({ ...inputFieldVisibility, [type]: false });
|
||||||
}
|
}
|
||||||
if (operation === ArrayInputOperations.REMOVE) {
|
if (operation === ArrayInputOperations.REMOVE) {
|
||||||
let updatedEnvVars = envVariables[type].filter(
|
let updatedEnvVars = variables[type].filter(
|
||||||
(item: string) => item !== role
|
(item: string) => item !== role
|
||||||
);
|
);
|
||||||
setEnvVariables({
|
setVariables({
|
||||||
...envVariables,
|
...variables,
|
||||||
[type]: updatedEnvVars,
|
[type]: updatedEnvVars,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -88,14 +91,14 @@ const InputField = ({
|
||||||
<InputGroup size="sm">
|
<InputGroup size="sm">
|
||||||
<Input
|
<Input
|
||||||
{...props}
|
{...props}
|
||||||
value={envVariables[inputType]}
|
value={variables[inputType] ? variables[inputType] : ''}
|
||||||
onChange={(
|
onChange={(
|
||||||
event: Event & {
|
event: Event & {
|
||||||
target: HTMLInputElement;
|
target: HTMLInputElement;
|
||||||
}
|
}
|
||||||
) =>
|
) =>
|
||||||
setEnvVariables({
|
setVariables({
|
||||||
...envVariables,
|
...variables,
|
||||||
[inputType]: event.target.value,
|
[inputType]: event.target.value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -103,7 +106,7 @@ const InputField = ({
|
||||||
<InputRightElement
|
<InputRightElement
|
||||||
children={<FaRegClone color="#bfbfbf" />}
|
children={<FaRegClone color="#bfbfbf" />}
|
||||||
cursor="pointer"
|
cursor="pointer"
|
||||||
onClick={() => copyTextToClipboard(envVariables[inputType])}
|
onClick={() => copyTextToClipboard(variables[inputType])}
|
||||||
/>
|
/>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
);
|
);
|
||||||
|
@ -113,14 +116,14 @@ const InputField = ({
|
||||||
<InputGroup size="sm">
|
<InputGroup size="sm">
|
||||||
<Input
|
<Input
|
||||||
{...props}
|
{...props}
|
||||||
value={envVariables[inputType]}
|
value={variables[inputType]}
|
||||||
onChange={(
|
onChange={(
|
||||||
event: Event & {
|
event: Event & {
|
||||||
target: HTMLInputElement;
|
target: HTMLInputElement;
|
||||||
}
|
}
|
||||||
) =>
|
) =>
|
||||||
setEnvVariables({
|
setVariables({
|
||||||
...envVariables,
|
...variables,
|
||||||
[inputType]: event.target.value,
|
[inputType]: event.target.value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -163,7 +166,7 @@ const InputField = ({
|
||||||
w="25px"
|
w="25px"
|
||||||
margin="0 1.5%"
|
margin="0 1.5%"
|
||||||
cursor="pointer"
|
cursor="pointer"
|
||||||
onClick={() => copyTextToClipboard(envVariables[inputType])}
|
onClick={() => copyTextToClipboard(variables[inputType])}
|
||||||
>
|
>
|
||||||
<FaRegClone color="#bfbfbf" />
|
<FaRegClone color="#bfbfbf" />
|
||||||
</Center>
|
</Center>
|
||||||
|
@ -178,13 +181,14 @@ const InputField = ({
|
||||||
<Flex
|
<Flex
|
||||||
border="1px solid #e2e8f0"
|
border="1px solid #e2e8f0"
|
||||||
w="100%"
|
w="100%"
|
||||||
h="45px"
|
paddingTop="0.5%"
|
||||||
wrap="wrap"
|
overflowX="scroll"
|
||||||
overflow="scroll"
|
overflowY="hidden"
|
||||||
padding="1%"
|
justifyContent="start"
|
||||||
|
alignItems="center"
|
||||||
>
|
>
|
||||||
{envVariables[inputType].map((role: string, index: number) => (
|
{variables[inputType].map((role: string, index: number) => (
|
||||||
<Box key={index} margin="1" role="group">
|
<Box key={index} margin="0.5%" role="group">
|
||||||
<Tag
|
<Tag
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
@ -210,10 +214,11 @@ const InputField = ({
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
{inputFieldVisibility[inputType] ? (
|
{inputFieldVisibility[inputType] ? (
|
||||||
<Box ml="1.15%">
|
<Box ml="1%" mb="0.75%">
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
size="xs"
|
size="xs"
|
||||||
|
minW="150px"
|
||||||
placeholder="add a new value"
|
placeholder="add a new value"
|
||||||
value={inputData[inputType]}
|
value={inputData[inputType]}
|
||||||
onChange={(e: any) => {
|
onChange={(e: any) => {
|
||||||
|
@ -231,7 +236,7 @@ const InputField = ({
|
||||||
</Box>
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
<Box
|
<Box
|
||||||
margin="1"
|
marginLeft="0.5%"
|
||||||
cursor="pointer"
|
cursor="pointer"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setInputFieldVisibility({
|
setInputFieldVisibility({
|
||||||
|
@ -254,11 +259,30 @@ const InputField = ({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (Object.values(SelectInputType).includes(inputType)) {
|
if (Object.values(SelectInputType).includes(inputType)) {
|
||||||
|
if (inputType === SelectInputType.JWT_TYPE) {
|
||||||
|
return (
|
||||||
|
<Select size="sm" {...props}>
|
||||||
|
{[variables[inputType]].map((value: string) => (
|
||||||
|
<option value="value" key={value}>
|
||||||
|
{value}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const { options, ...rest } = props;
|
||||||
return (
|
return (
|
||||||
<Select size="sm" {...props}>
|
<Select
|
||||||
{[envVariables[inputType]].map((value: string) => (
|
size="sm"
|
||||||
<option value="value" key={value}>
|
{...rest}
|
||||||
{value}
|
value={variables[inputType] ? variables[inputType] : ''}
|
||||||
|
onChange={(e) =>
|
||||||
|
setVariables({ ...variables, [inputType]: e.target.value })
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{Object.entries(options).map(([key, value]: any) => (
|
||||||
|
<option value={value} key={key}>
|
||||||
|
{key}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
|
@ -282,11 +306,11 @@ const InputField = ({
|
||||||
<Code h="75%">Off</Code>
|
<Code h="75%">Off</Code>
|
||||||
<Switch
|
<Switch
|
||||||
size="md"
|
size="md"
|
||||||
isChecked={envVariables[inputType]}
|
isChecked={variables[inputType]}
|
||||||
onChange={() => {
|
onChange={() => {
|
||||||
setEnvVariables({
|
setVariables({
|
||||||
...envVariables,
|
...variables,
|
||||||
[inputType]: !envVariables[inputType],
|
[inputType]: !variables[inputType],
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -294,6 +318,20 @@ const InputField = ({
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (Object.values(DateInputType).includes(inputType)) {
|
||||||
|
return (
|
||||||
|
<Flex border="1px solid #e2e8f0" w="100%" h="33px" padding="1%">
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
style={{ width: '100%', paddingLeft: '2.5%' }}
|
||||||
|
value={variables[inputType] ? variables[inputType] : ''}
|
||||||
|
onChange={(e) =>
|
||||||
|
setVariables({ ...variables, [inputType]: e.target.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,12 @@ export const TextInputType = {
|
||||||
DATABASE_NAME: 'DATABASE_NAME',
|
DATABASE_NAME: 'DATABASE_NAME',
|
||||||
DATABASE_TYPE: 'DATABASE_TYPE',
|
DATABASE_TYPE: 'DATABASE_TYPE',
|
||||||
DATABASE_URL: 'DATABASE_URL',
|
DATABASE_URL: 'DATABASE_URL',
|
||||||
|
GIVEN_NAME: 'given_name',
|
||||||
|
MIDDLE_NAME: 'middle_name',
|
||||||
|
FAMILY_NAME: 'family_name',
|
||||||
|
NICKNAME: 'nickname',
|
||||||
|
PHONE_NUMBER: 'phone_number',
|
||||||
|
PICTURE: 'picture',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const HiddenInputType = {
|
export const HiddenInputType = {
|
||||||
|
@ -33,10 +39,12 @@ export const ArrayInputType = {
|
||||||
DEFAULT_ROLES: 'DEFAULT_ROLES',
|
DEFAULT_ROLES: 'DEFAULT_ROLES',
|
||||||
PROTECTED_ROLES: 'PROTECTED_ROLES',
|
PROTECTED_ROLES: 'PROTECTED_ROLES',
|
||||||
ALLOWED_ORIGINS: 'ALLOWED_ORIGINS',
|
ALLOWED_ORIGINS: 'ALLOWED_ORIGINS',
|
||||||
|
USER_ROLES: 'roles',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SelectInputType = {
|
export const SelectInputType = {
|
||||||
JWT_TYPE: 'JWT_TYPE',
|
JWT_TYPE: 'JWT_TYPE',
|
||||||
|
GENDER: 'gender',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TextAreaInputType = {
|
export const TextAreaInputType = {
|
||||||
|
@ -50,6 +58,10 @@ export const SwitchInputType = {
|
||||||
DISABLE_BASIC_AUTHENTICATION: 'DISABLE_BASIC_AUTHENTICATION',
|
DISABLE_BASIC_AUTHENTICATION: 'DISABLE_BASIC_AUTHENTICATION',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const DateInputType = {
|
||||||
|
BIRTHDATE: 'birthdate',
|
||||||
|
};
|
||||||
|
|
||||||
export const ArrayInputOperations = {
|
export const ArrayInputOperations = {
|
||||||
APPEND: 'APPEND',
|
APPEND: 'APPEND',
|
||||||
REMOVE: 'REMOVE',
|
REMOVE: 'REMOVE',
|
||||||
|
|
|
@ -31,3 +31,13 @@ export const UpdateEnvVariables = `
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const UpdateUser = `
|
||||||
|
mutation updateUser(
|
||||||
|
$params: UpdateUserInput!
|
||||||
|
) {
|
||||||
|
_update_user(params: $params) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
|
@ -42,3 +42,31 @@ export const EnvVariablesQuery = `
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const UserDetailsQuery = `
|
||||||
|
query($params: PaginatedInput) {
|
||||||
|
_users(params: $params) {
|
||||||
|
pagination {
|
||||||
|
limit
|
||||||
|
page
|
||||||
|
offset
|
||||||
|
total
|
||||||
|
}
|
||||||
|
users {
|
||||||
|
id
|
||||||
|
email
|
||||||
|
email_verified
|
||||||
|
given_name
|
||||||
|
family_name
|
||||||
|
middle_name
|
||||||
|
nickname
|
||||||
|
gender
|
||||||
|
birthdate
|
||||||
|
phone_number
|
||||||
|
picture
|
||||||
|
roles
|
||||||
|
created_at
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
|
@ -249,16 +249,16 @@ export default function Environment() {
|
||||||
</Center>
|
</Center>
|
||||||
<Center w="45%" marginRight="1.5%">
|
<Center w="45%" marginRight="1.5%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
inputType={TextInputType.GOOGLE_CLIENT_ID}
|
inputType={TextInputType.GOOGLE_CLIENT_ID}
|
||||||
placeholder="Google Client ID"
|
placeholder="Google Client ID"
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
<Center w="45%">
|
<Center w="45%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
fieldVisibility={fieldVisibility}
|
fieldVisibility={fieldVisibility}
|
||||||
setFieldVisibility={setFieldVisibility}
|
setFieldVisibility={setFieldVisibility}
|
||||||
inputType={HiddenInputType.GOOGLE_CLIENT_SECRET}
|
inputType={HiddenInputType.GOOGLE_CLIENT_SECRET}
|
||||||
|
@ -277,16 +277,16 @@ export default function Environment() {
|
||||||
</Center>
|
</Center>
|
||||||
<Center w="45%" marginRight="1.5%">
|
<Center w="45%" marginRight="1.5%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
inputType={TextInputType.GITHUB_CLIENT_ID}
|
inputType={TextInputType.GITHUB_CLIENT_ID}
|
||||||
placeholder="Github Client ID"
|
placeholder="Github Client ID"
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
<Center w="45%">
|
<Center w="45%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
fieldVisibility={fieldVisibility}
|
fieldVisibility={fieldVisibility}
|
||||||
setFieldVisibility={setFieldVisibility}
|
setFieldVisibility={setFieldVisibility}
|
||||||
inputType={HiddenInputType.GITHUB_CLIENT_SECRET}
|
inputType={HiddenInputType.GITHUB_CLIENT_SECRET}
|
||||||
|
@ -305,16 +305,16 @@ export default function Environment() {
|
||||||
</Center>
|
</Center>
|
||||||
<Center w="45%" marginRight="1.5%">
|
<Center w="45%" marginRight="1.5%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
inputType={TextInputType.FACEBOOK_CLIENT_ID}
|
inputType={TextInputType.FACEBOOK_CLIENT_ID}
|
||||||
placeholder="Facebook Client ID"
|
placeholder="Facebook Client ID"
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
<Center w="45%">
|
<Center w="45%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
fieldVisibility={fieldVisibility}
|
fieldVisibility={fieldVisibility}
|
||||||
setFieldVisibility={setFieldVisibility}
|
setFieldVisibility={setFieldVisibility}
|
||||||
inputType={HiddenInputType.FACEBOOK_CLIENT_SECRET}
|
inputType={HiddenInputType.FACEBOOK_CLIENT_SECRET}
|
||||||
|
@ -334,8 +334,8 @@ export default function Environment() {
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center w="70%">
|
<Center w="70%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
inputType={ArrayInputType.ROLES}
|
inputType={ArrayInputType.ROLES}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
|
@ -346,8 +346,8 @@ export default function Environment() {
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center w="70%">
|
<Center w="70%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
inputType={ArrayInputType.DEFAULT_ROLES}
|
inputType={ArrayInputType.DEFAULT_ROLES}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
|
@ -358,8 +358,8 @@ export default function Environment() {
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center w="70%">
|
<Center w="70%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
inputType={ArrayInputType.PROTECTED_ROLES}
|
inputType={ArrayInputType.PROTECTED_ROLES}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
|
@ -378,8 +378,8 @@ export default function Environment() {
|
||||||
<Flex w="100%" justifyContent="space-between">
|
<Flex w="100%" justifyContent="space-between">
|
||||||
<Flex flex="2">
|
<Flex flex="2">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
inputType={SelectInputType.JWT_TYPE}
|
inputType={SelectInputType.JWT_TYPE}
|
||||||
isDisabled={true}
|
isDisabled={true}
|
||||||
defaultValue={SelectInputType.JWT_TYPE}
|
defaultValue={SelectInputType.JWT_TYPE}
|
||||||
|
@ -399,8 +399,8 @@ export default function Environment() {
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center w="70%">
|
<Center w="70%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
fieldVisibility={fieldVisibility}
|
fieldVisibility={fieldVisibility}
|
||||||
setFieldVisibility={setFieldVisibility}
|
setFieldVisibility={setFieldVisibility}
|
||||||
inputType={HiddenInputType.JWT_SECRET}
|
inputType={HiddenInputType.JWT_SECRET}
|
||||||
|
@ -413,8 +413,8 @@ export default function Environment() {
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center w="70%">
|
<Center w="70%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
inputType={TextInputType.JWT_ROLE_CLAIM}
|
inputType={TextInputType.JWT_ROLE_CLAIM}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
|
@ -431,8 +431,8 @@ export default function Environment() {
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center w="70%">
|
<Center w="70%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
inputType={TextInputType.REDIS_URL}
|
inputType={TextInputType.REDIS_URL}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
|
@ -449,8 +449,8 @@ export default function Environment() {
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center w="70%">
|
<Center w="70%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
inputType={TextInputType.SMTP_HOST}
|
inputType={TextInputType.SMTP_HOST}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
|
@ -461,8 +461,8 @@ export default function Environment() {
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center w="70%">
|
<Center w="70%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
inputType={TextInputType.SMTP_PORT}
|
inputType={TextInputType.SMTP_PORT}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
|
@ -473,8 +473,8 @@ export default function Environment() {
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center w="70%">
|
<Center w="70%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
inputType={TextInputType.SMTP_USERNAME}
|
inputType={TextInputType.SMTP_USERNAME}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
|
@ -485,8 +485,8 @@ export default function Environment() {
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center w="70%">
|
<Center w="70%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
fieldVisibility={fieldVisibility}
|
fieldVisibility={fieldVisibility}
|
||||||
setFieldVisibility={setFieldVisibility}
|
setFieldVisibility={setFieldVisibility}
|
||||||
inputType={HiddenInputType.SMTP_PASSWORD}
|
inputType={HiddenInputType.SMTP_PASSWORD}
|
||||||
|
@ -499,8 +499,8 @@ export default function Environment() {
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center w="70%">
|
<Center w="70%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
inputType={TextInputType.SENDER_EMAIL}
|
inputType={TextInputType.SENDER_EMAIL}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
|
@ -517,8 +517,8 @@ export default function Environment() {
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center w="70%">
|
<Center w="70%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
inputType={ArrayInputType.ALLOWED_ORIGINS}
|
inputType={ArrayInputType.ALLOWED_ORIGINS}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
|
@ -535,8 +535,8 @@ export default function Environment() {
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center w="70%">
|
<Center w="70%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
inputType={TextInputType.ORGANIZATION_NAME}
|
inputType={TextInputType.ORGANIZATION_NAME}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
|
@ -547,8 +547,8 @@ export default function Environment() {
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center w="70%">
|
<Center w="70%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
inputType={TextInputType.ORGANIZATION_LOGO}
|
inputType={TextInputType.ORGANIZATION_LOGO}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
|
@ -562,8 +562,8 @@ export default function Environment() {
|
||||||
<Flex>
|
<Flex>
|
||||||
<Center w="100%">
|
<Center w="100%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
inputType={TextAreaInputType.CUSTOM_ACCESS_TOKEN_SCRIPT}
|
inputType={TextAreaInputType.CUSTOM_ACCESS_TOKEN_SCRIPT}
|
||||||
placeholder="Add script here"
|
placeholder="Add script here"
|
||||||
minH="25vh"
|
minH="25vh"
|
||||||
|
@ -582,8 +582,8 @@ export default function Environment() {
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex justifyContent="start" w="70%">
|
<Flex justifyContent="start" w="70%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
inputType={SwitchInputType.DISABLE_LOGIN_PAGE}
|
inputType={SwitchInputType.DISABLE_LOGIN_PAGE}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -594,8 +594,8 @@ export default function Environment() {
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex justifyContent="start" w="70%">
|
<Flex justifyContent="start" w="70%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
inputType={SwitchInputType.DISABLE_EMAIL_VERIFICATION}
|
inputType={SwitchInputType.DISABLE_EMAIL_VERIFICATION}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -606,8 +606,8 @@ export default function Environment() {
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex justifyContent="start" w="70%">
|
<Flex justifyContent="start" w="70%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
inputType={SwitchInputType.DISABLE_MAGIC_LINK_LOGIN}
|
inputType={SwitchInputType.DISABLE_MAGIC_LINK_LOGIN}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -618,8 +618,8 @@ export default function Environment() {
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex justifyContent="start" w="70%">
|
<Flex justifyContent="start" w="70%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
inputType={SwitchInputType.DISABLE_BASIC_AUTHENTICATION}
|
inputType={SwitchInputType.DISABLE_BASIC_AUTHENTICATION}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -647,8 +647,8 @@ export default function Environment() {
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center w="70%">
|
<Center w="70%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
inputType={TextInputType.DATABASE_NAME}
|
inputType={TextInputType.DATABASE_NAME}
|
||||||
isDisabled={true}
|
isDisabled={true}
|
||||||
/>
|
/>
|
||||||
|
@ -660,8 +660,8 @@ export default function Environment() {
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center w="70%">
|
<Center w="70%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
inputType={TextInputType.DATABASE_TYPE}
|
inputType={TextInputType.DATABASE_TYPE}
|
||||||
isDisabled={true}
|
isDisabled={true}
|
||||||
/>
|
/>
|
||||||
|
@ -673,8 +673,8 @@ export default function Environment() {
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center w="70%">
|
<Center w="70%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
inputType={TextInputType.DATABASE_URL}
|
inputType={TextInputType.DATABASE_URL}
|
||||||
isDisabled={true}
|
isDisabled={true}
|
||||||
/>
|
/>
|
||||||
|
@ -743,8 +743,8 @@ export default function Environment() {
|
||||||
</Flex>
|
</Flex>
|
||||||
<Center w="70%">
|
<Center w="70%">
|
||||||
<InputField
|
<InputField
|
||||||
envVariables={envVariables}
|
variables={envVariables}
|
||||||
setEnvVariables={setEnvVariables}
|
setVariables={setEnvVariables}
|
||||||
inputType={HiddenInputType.ADMIN_SECRET}
|
inputType={HiddenInputType.ADMIN_SECRET}
|
||||||
fieldVisibility={fieldVisibility}
|
fieldVisibility={fieldVisibility}
|
||||||
setFieldVisibility={setFieldVisibility}
|
setFieldVisibility={setFieldVisibility}
|
||||||
|
|
|
@ -1,6 +1,363 @@
|
||||||
import { Box } from '@chakra-ui/react';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useClient } from 'urql';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Flex,
|
||||||
|
IconButton,
|
||||||
|
NumberDecrementStepper,
|
||||||
|
NumberIncrementStepper,
|
||||||
|
NumberInput,
|
||||||
|
NumberInputField,
|
||||||
|
NumberInputStepper,
|
||||||
|
Select,
|
||||||
|
Table,
|
||||||
|
Tag,
|
||||||
|
Tbody,
|
||||||
|
Td,
|
||||||
|
Text,
|
||||||
|
TableCaption,
|
||||||
|
Th,
|
||||||
|
Thead,
|
||||||
|
Tooltip,
|
||||||
|
Tr,
|
||||||
|
Button,
|
||||||
|
Center,
|
||||||
|
Menu,
|
||||||
|
MenuButton,
|
||||||
|
MenuList,
|
||||||
|
MenuItem,
|
||||||
|
useToast,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import {
|
||||||
|
FaAngleLeft,
|
||||||
|
FaAngleRight,
|
||||||
|
FaAngleDoubleLeft,
|
||||||
|
FaAngleDoubleRight,
|
||||||
|
FaExclamationCircle,
|
||||||
|
FaAngleDown,
|
||||||
|
} from 'react-icons/fa';
|
||||||
|
import { UserDetailsQuery } from '../graphql/queries';
|
||||||
|
import { UpdateUser } from '../graphql/mutation';
|
||||||
|
import EditUserModal from '../components/EditUserModal';
|
||||||
|
|
||||||
|
interface paginationPropTypes {
|
||||||
|
limit: number;
|
||||||
|
page: number;
|
||||||
|
offset: number;
|
||||||
|
total: number;
|
||||||
|
maxPages: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface userDataTypes {
|
||||||
|
id: string;
|
||||||
|
email: string;
|
||||||
|
email_verified: boolean;
|
||||||
|
given_name: string;
|
||||||
|
family_name: string;
|
||||||
|
middle_name: string;
|
||||||
|
nickname: string;
|
||||||
|
gender: string;
|
||||||
|
birthdate: string;
|
||||||
|
phone_number: string;
|
||||||
|
picture: string;
|
||||||
|
roles: [string];
|
||||||
|
created_at: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getMaxPages = (pagination: paginationPropTypes) => {
|
||||||
|
const { limit, total } = pagination;
|
||||||
|
if (total > 1) {
|
||||||
|
return total % limit === 0
|
||||||
|
? total / limit
|
||||||
|
: parseInt(`${total / limit}`) + 1;
|
||||||
|
} else return 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getLimits = (pagination: paginationPropTypes) => {
|
||||||
|
const { total } = pagination;
|
||||||
|
const limits = [5];
|
||||||
|
if (total > 10) {
|
||||||
|
for (let i = 10; i <= total && limits.length <= 10; i += 5) {
|
||||||
|
limits.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return limits;
|
||||||
|
};
|
||||||
|
|
||||||
export default function Users() {
|
export default function Users() {
|
||||||
return <Box>Welcome to Users Page</Box>;
|
const client = useClient();
|
||||||
|
const toast = useToast();
|
||||||
|
const [paginationProps, setPaginationProps] =
|
||||||
|
React.useState<paginationPropTypes>({
|
||||||
|
limit: 5,
|
||||||
|
page: 1,
|
||||||
|
offset: 0,
|
||||||
|
total: 0,
|
||||||
|
maxPages: 1,
|
||||||
|
});
|
||||||
|
const [userList, setUserList] = React.useState<userDataTypes[]>([]);
|
||||||
|
const updateUserList = async () => {
|
||||||
|
const { data } = await client
|
||||||
|
.query(UserDetailsQuery, {
|
||||||
|
params: {
|
||||||
|
pagination: {
|
||||||
|
limit: paginationProps.limit,
|
||||||
|
page: paginationProps.page,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.toPromise();
|
||||||
|
if (data?._users) {
|
||||||
|
const { pagination, users } = data._users;
|
||||||
|
const maxPages = getMaxPages(pagination);
|
||||||
|
setPaginationProps({ ...paginationProps, ...pagination, maxPages });
|
||||||
|
setUserList(users);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
React.useEffect(() => {
|
||||||
|
updateUserList();
|
||||||
|
}, []);
|
||||||
|
React.useEffect(() => {
|
||||||
|
updateUserList();
|
||||||
|
}, [paginationProps.page, paginationProps.limit]);
|
||||||
|
|
||||||
|
const paginationHandler = (value: Record<string, number>) => {
|
||||||
|
setPaginationProps({ ...paginationProps, ...value });
|
||||||
|
};
|
||||||
|
|
||||||
|
const userVerificationHandler = async (user: userDataTypes) => {
|
||||||
|
const { id, email } = user;
|
||||||
|
const res = await client
|
||||||
|
.mutation(UpdateUser, {
|
||||||
|
params: {
|
||||||
|
id,
|
||||||
|
email,
|
||||||
|
email_verified: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.toPromise();
|
||||||
|
if (res.error) {
|
||||||
|
toast({
|
||||||
|
title: 'User verification failed',
|
||||||
|
isClosable: true,
|
||||||
|
status: 'error',
|
||||||
|
position: 'bottom-right',
|
||||||
|
});
|
||||||
|
} else if (res.data?._update_user?.id) {
|
||||||
|
toast({
|
||||||
|
title: 'User verification successful',
|
||||||
|
isClosable: true,
|
||||||
|
status: 'success',
|
||||||
|
position: 'bottom-right',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
updateUserList();
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Box m="5" py="5" px="10" bg="white" rounded="md">
|
||||||
|
<Flex margin="2% 0" justifyContent="space-between" alignItems="center">
|
||||||
|
<Text fontSize="md" fontWeight="bold">
|
||||||
|
Users
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
{userList.length > 0 ? (
|
||||||
|
<Table variant="simple">
|
||||||
|
<Thead>
|
||||||
|
<Tr>
|
||||||
|
<Th>Email</Th>
|
||||||
|
<Th>Created At</Th>
|
||||||
|
<Th>Verified</Th>
|
||||||
|
<Th>Actions</Th>
|
||||||
|
</Tr>
|
||||||
|
</Thead>
|
||||||
|
<Tbody>
|
||||||
|
{userList.map((user: userDataTypes) => {
|
||||||
|
const { email_verified, created_at, ...rest }: any = user;
|
||||||
|
return (
|
||||||
|
<Tr key={user.id} style={{ fontSize: 14 }}>
|
||||||
|
<Td>{user.email}</Td>
|
||||||
|
<Td>{dayjs(user.created_at).format('MMM DD, YYYY')}</Td>
|
||||||
|
<Td>
|
||||||
|
<Tag
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
colorScheme={user.email_verified ? 'green' : 'yellow'}
|
||||||
|
>
|
||||||
|
{user.email_verified.toString()}
|
||||||
|
</Tag>
|
||||||
|
</Td>
|
||||||
|
<Td>
|
||||||
|
<Menu>
|
||||||
|
<MenuButton as={Button} variant="unstyled" size="sm">
|
||||||
|
<Flex
|
||||||
|
justifyContent="space-between"
|
||||||
|
alignItems="center"
|
||||||
|
>
|
||||||
|
<Text fontSize="sm" fontWeight="light">
|
||||||
|
Menu
|
||||||
|
</Text>
|
||||||
|
<FaAngleDown style={{ marginLeft: 10 }} />
|
||||||
|
</Flex>
|
||||||
|
</MenuButton>
|
||||||
|
<MenuList>
|
||||||
|
<EditUserModal
|
||||||
|
user={rest}
|
||||||
|
updateUserList={updateUserList}
|
||||||
|
/>
|
||||||
|
{!user.email_verified && (
|
||||||
|
<MenuItem
|
||||||
|
onClick={() => userVerificationHandler(user)}
|
||||||
|
>
|
||||||
|
Verify User
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
|
</MenuList>
|
||||||
|
</Menu>
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Tbody>
|
||||||
|
{paginationProps.maxPages > 1 && (
|
||||||
|
<TableCaption>
|
||||||
|
<Flex justifyContent="space-between" alignItems="center" m="2% 0">
|
||||||
|
<Flex flex="1">
|
||||||
|
<Tooltip label="First Page">
|
||||||
|
<IconButton
|
||||||
|
aria-label="icon button"
|
||||||
|
onClick={() =>
|
||||||
|
paginationHandler({
|
||||||
|
page: 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
isDisabled={paginationProps.page <= 1}
|
||||||
|
mr={4}
|
||||||
|
icon={<FaAngleDoubleLeft />}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip label="Previous Page">
|
||||||
|
<IconButton
|
||||||
|
aria-label="icon button"
|
||||||
|
onClick={() =>
|
||||||
|
paginationHandler({
|
||||||
|
page: paginationProps.page - 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
isDisabled={paginationProps.page <= 1}
|
||||||
|
icon={<FaAngleLeft />}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</Flex>
|
||||||
|
<Flex
|
||||||
|
flex="8"
|
||||||
|
justifyContent="space-evenly"
|
||||||
|
alignItems="center"
|
||||||
|
>
|
||||||
|
<Text mr={8}>
|
||||||
|
Page{' '}
|
||||||
|
<Text fontWeight="bold" as="span">
|
||||||
|
{paginationProps.page}
|
||||||
|
</Text>{' '}
|
||||||
|
of{' '}
|
||||||
|
<Text fontWeight="bold" as="span">
|
||||||
|
{paginationProps.maxPages}
|
||||||
|
</Text>
|
||||||
|
</Text>
|
||||||
|
<Flex alignItems="center">
|
||||||
|
<Text flexShrink="0">Go to page:</Text>{' '}
|
||||||
|
<NumberInput
|
||||||
|
ml={2}
|
||||||
|
mr={8}
|
||||||
|
w={28}
|
||||||
|
min={1}
|
||||||
|
max={paginationProps.maxPages}
|
||||||
|
onChange={(value) =>
|
||||||
|
paginationHandler({
|
||||||
|
page: parseInt(value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
value={paginationProps.page}
|
||||||
|
>
|
||||||
|
<NumberInputField />
|
||||||
|
<NumberInputStepper>
|
||||||
|
<NumberIncrementStepper />
|
||||||
|
<NumberDecrementStepper />
|
||||||
|
</NumberInputStepper>
|
||||||
|
</NumberInput>
|
||||||
|
</Flex>
|
||||||
|
<Select
|
||||||
|
w={32}
|
||||||
|
value={paginationProps.limit}
|
||||||
|
onChange={(e) =>
|
||||||
|
paginationHandler({
|
||||||
|
page: 1,
|
||||||
|
limit: parseInt(e.target.value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{getLimits(paginationProps).map((pageSize) => (
|
||||||
|
<option key={pageSize} value={pageSize}>
|
||||||
|
Show {pageSize}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Flex>
|
||||||
|
<Flex flex="1">
|
||||||
|
<Tooltip label="Next Page">
|
||||||
|
<IconButton
|
||||||
|
aria-label="icon button"
|
||||||
|
onClick={() =>
|
||||||
|
paginationHandler({
|
||||||
|
page: paginationProps.page + 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
isDisabled={
|
||||||
|
paginationProps.page >= paginationProps.maxPages
|
||||||
|
}
|
||||||
|
icon={<FaAngleRight />}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip label="Last Page">
|
||||||
|
<IconButton
|
||||||
|
aria-label="icon button"
|
||||||
|
onClick={() =>
|
||||||
|
paginationHandler({
|
||||||
|
page: paginationProps.maxPages,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
isDisabled={
|
||||||
|
paginationProps.page >= paginationProps.maxPages
|
||||||
|
}
|
||||||
|
ml={4}
|
||||||
|
icon={<FaAngleDoubleRight />}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</TableCaption>
|
||||||
|
)}
|
||||||
|
</Table>
|
||||||
|
) : (
|
||||||
|
<Flex
|
||||||
|
flexDirection="column"
|
||||||
|
minH="25vh"
|
||||||
|
justifyContent="center"
|
||||||
|
alignItems="center"
|
||||||
|
>
|
||||||
|
<Center w="50px" marginRight="1.5%">
|
||||||
|
<FaExclamationCircle style={{ color: '#f0f0f0', fontSize: 70 }} />
|
||||||
|
</Center>
|
||||||
|
<Text
|
||||||
|
fontSize="2xl"
|
||||||
|
paddingRight="1%"
|
||||||
|
fontWeight="bold"
|
||||||
|
color="#d9d9d9"
|
||||||
|
>
|
||||||
|
No Data
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,11 @@ export const getObjectDiff = (obj1: any, obj2: any) => {
|
||||||
const diff = Object.keys(obj1).reduce((result, key) => {
|
const diff = Object.keys(obj1).reduce((result, key) => {
|
||||||
if (!obj2.hasOwnProperty(key)) {
|
if (!obj2.hasOwnProperty(key)) {
|
||||||
result.push(key);
|
result.push(key);
|
||||||
} else if (_.isEqual(obj1[key], obj2[key])) {
|
} else if (
|
||||||
|
_.isEqual(obj1[key], obj2[key]) ||
|
||||||
|
(obj1[key] === null && obj2[key] === '') ||
|
||||||
|
(obj1[key] === [] && obj2[key] === null)
|
||||||
|
) {
|
||||||
const resultKeyIndex = result.indexOf(key);
|
const resultKeyIndex = result.indexOf(key);
|
||||||
result.splice(resultKeyIndex, 1);
|
result.splice(resultKeyIndex, 1);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user