Merge branch 'development' of https://github.com/authorizerdev/authorizer into development

This commit is contained in:
Lakhan Samani 2022-10-25 08:21:32 +05:30
commit 9ce53eb8e8
4 changed files with 914 additions and 797 deletions

View File

@ -1,250 +1,263 @@
import React from 'react'; import React, { useState } from 'react';
import { import {
Button, Button,
Center, Center,
Flex, Flex,
MenuItem, MenuItem,
Modal, Modal,
ModalBody, ModalBody,
ModalCloseButton, ModalCloseButton,
ModalContent, ModalContent,
ModalFooter, ModalFooter,
ModalHeader, ModalHeader,
ModalOverlay, ModalOverlay,
Stack, Stack,
useDisclosure, useDisclosure,
Text, Text,
useToast, useToast,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { useClient } from 'urql'; import { useClient } from 'urql';
import { FaSave } from 'react-icons/fa'; import { FaSave } from 'react-icons/fa';
import InputField from './InputField'; import InputField from './InputField';
import { import {
ArrayInputType, DateInputType,
DateInputType, MultiSelectInputType,
SelectInputType, SelectInputType,
TextInputType, TextInputType,
} from '../constants'; } from '../constants';
import { getObjectDiff } from '../utils'; import { getObjectDiff } from '../utils';
import { UpdateUser } from '../graphql/mutation'; import { UpdateUser } from '../graphql/mutation';
import { GetAvailableRolesQuery } from '../graphql/queries';
const GenderTypes = { const GenderTypes = {
Undisclosed: null, Undisclosed: null,
Male: 'Male', Male: 'Male',
Female: 'Female', Female: 'Female',
}; };
interface userDataTypes { interface userDataTypes {
id: string; id: string;
email: string; email: string;
given_name: string; given_name: string;
family_name: string; family_name: string;
middle_name: string; middle_name: string;
nickname: string; nickname: string;
gender: string; gender: string;
birthdate: string; birthdate: string;
phone_number: string; phone_number: string;
picture: string; picture: string;
roles: [string] | []; roles: [string] | [];
} }
const EditUserModal = ({ const EditUserModal = ({
user, user,
updateUserList, updateUserList,
}: { }: {
user: userDataTypes; user: userDataTypes;
updateUserList: Function; updateUserList: Function;
}) => { }) => {
const client = useClient(); const client = useClient();
const toast = useToast(); const toast = useToast();
const { isOpen, onOpen, onClose } = useDisclosure(); const [availableRoles, setAvailableRoles] = useState<string[]>([]);
const [userData, setUserData] = React.useState<userDataTypes>({ const { isOpen, onOpen, onClose } = useDisclosure();
id: '', const [userData, setUserData] = useState<userDataTypes>({
email: '', id: '',
given_name: '', email: '',
family_name: '', given_name: '',
middle_name: '', family_name: '',
nickname: '', middle_name: '',
gender: '', nickname: '',
birthdate: '', gender: '',
phone_number: '', birthdate: '',
picture: '', phone_number: '',
roles: [], picture: '',
}); roles: [],
React.useEffect(() => { });
setUserData(user); React.useEffect(() => {
}, []); setUserData(user);
const saveHandler = async () => { fetchAvailableRoles();
const diff = getObjectDiff(user, userData); }, []);
const updatedUserData = diff.reduce( const fetchAvailableRoles = async () => {
(acc: any, property: string) => ({ const res = await client.query(GetAvailableRolesQuery).toPromise();
...acc, if (res.data?._env?.ROLES && res.data?._env?.PROTECTED_ROLES) {
// @ts-ignore setAvailableRoles([
[property]: userData[property], ...res.data._env.ROLES,
}), ...res.data._env.PROTECTED_ROLES,
{}, ]);
); }
const res = await client };
.mutation(UpdateUser, { params: { ...updatedUserData, id: userData.id } }) const saveHandler = async () => {
.toPromise(); const diff = getObjectDiff(user, userData);
if (res.error) { const updatedUserData = diff.reduce(
toast({ (acc: any, property: string) => ({
title: 'User data update failed', ...acc,
isClosable: true, // @ts-ignore
status: 'error', [property]: userData[property],
position: 'bottom-right', }),
}); {},
} else if (res.data?._update_user?.id) { );
toast({ const res = await client
title: 'User data update successful', .mutation(UpdateUser, { params: { ...updatedUserData, id: userData.id } })
isClosable: true, .toPromise();
status: 'success', if (res.error) {
position: 'bottom-right', toast({
}); title: 'User data update failed',
} isClosable: true,
onClose(); status: 'error',
updateUserList(); position: 'bottom-right',
}; });
return ( } else if (res.data?._update_user?.id) {
<> toast({
<MenuItem onClick={onOpen}>Edit User Details</MenuItem> title: 'User data update successful',
<Modal isOpen={isOpen} onClose={onClose}> isClosable: true,
<ModalOverlay /> status: 'success',
<ModalContent> position: 'bottom-right',
<ModalHeader>Edit User Details</ModalHeader> });
<ModalCloseButton /> }
<ModalBody> onClose();
<Stack> updateUserList();
<Flex> };
<Flex w="30%" justifyContent="start" alignItems="center"> return (
<Text fontSize="sm">Given Name:</Text> <>
</Flex> <MenuItem onClick={onOpen}>Edit User Details</MenuItem>
<Center w="70%"> <Modal isOpen={isOpen} onClose={onClose}>
<InputField <ModalOverlay />
variables={userData} <ModalContent>
setVariables={setUserData} <ModalHeader>Edit User Details</ModalHeader>
inputType={TextInputType.GIVEN_NAME} <ModalCloseButton />
/> <ModalBody>
</Center> <Stack>
</Flex> <Flex>
<Flex> <Flex w="30%" justifyContent="start" alignItems="center">
<Flex w="30%" justifyContent="start" alignItems="center"> <Text fontSize="sm">Given Name:</Text>
<Text fontSize="sm">Middle Name:</Text> </Flex>
</Flex> <Center w="70%">
<Center w="70%"> <InputField
<InputField variables={userData}
variables={userData} setVariables={setUserData}
setVariables={setUserData} inputType={TextInputType.GIVEN_NAME}
inputType={TextInputType.MIDDLE_NAME} />
/> </Center>
</Center> </Flex>
</Flex> <Flex>
<Flex> <Flex w="30%" justifyContent="start" alignItems="center">
<Flex w="30%" justifyContent="start" alignItems="center"> <Text fontSize="sm">Middle Name:</Text>
<Text fontSize="sm">Family Name:</Text> </Flex>
</Flex> <Center w="70%">
<Center w="70%"> <InputField
<InputField variables={userData}
variables={userData} setVariables={setUserData}
setVariables={setUserData} inputType={TextInputType.MIDDLE_NAME}
inputType={TextInputType.FAMILY_NAME} />
/> </Center>
</Center> </Flex>
</Flex> <Flex>
<Flex> <Flex w="30%" justifyContent="start" alignItems="center">
<Flex w="30%" justifyContent="start" alignItems="center"> <Text fontSize="sm">Family Name:</Text>
<Text fontSize="sm">Birth Date:</Text> </Flex>
</Flex> <Center w="70%">
<Center w="70%"> <InputField
<InputField variables={userData}
variables={userData} setVariables={setUserData}
setVariables={setUserData} inputType={TextInputType.FAMILY_NAME}
inputType={DateInputType.BIRTHDATE} />
/> </Center>
</Center> </Flex>
</Flex> <Flex>
<Flex> <Flex w="30%" justifyContent="start" alignItems="center">
<Flex w="30%" justifyContent="start" alignItems="center"> <Text fontSize="sm">Birth Date:</Text>
<Text fontSize="sm">Nickname:</Text> </Flex>
</Flex> <Center w="70%">
<Center w="70%"> <InputField
<InputField variables={userData}
variables={userData} setVariables={setUserData}
setVariables={setUserData} inputType={DateInputType.BIRTHDATE}
inputType={TextInputType.NICKNAME} />
/> </Center>
</Center> </Flex>
</Flex> <Flex>
<Flex> <Flex w="30%" justifyContent="start" alignItems="center">
<Flex w="30%" justifyContent="start" alignItems="center"> <Text fontSize="sm">Nickname:</Text>
<Text fontSize="sm">Gender:</Text> </Flex>
</Flex> <Center w="70%">
<Center w="70%"> <InputField
<InputField variables={userData}
variables={userData} setVariables={setUserData}
setVariables={setUserData} inputType={TextInputType.NICKNAME}
inputType={SelectInputType.GENDER} />
value={userData.gender} </Center>
options={GenderTypes} </Flex>
/> <Flex>
</Center> <Flex w="30%" justifyContent="start" alignItems="center">
</Flex> <Text fontSize="sm">Gender:</Text>
<Flex> </Flex>
<Flex w="30%" justifyContent="start" alignItems="center"> <Center w="70%">
<Text fontSize="sm">Phone Number:</Text> <InputField
</Flex> variables={userData}
<Center w="70%"> setVariables={setUserData}
<InputField inputType={SelectInputType.GENDER}
variables={userData} value={userData.gender}
setVariables={setUserData} options={GenderTypes}
inputType={TextInputType.PHONE_NUMBER} />
/> </Center>
</Center> </Flex>
</Flex> <Flex>
<Flex> <Flex w="30%" justifyContent="start" alignItems="center">
<Flex w="30%" justifyContent="start" alignItems="center"> <Text fontSize="sm">Phone Number:</Text>
<Text fontSize="sm">Picture:</Text> </Flex>
</Flex> <Center w="70%">
<Center w="70%"> <InputField
<InputField variables={userData}
variables={userData} setVariables={setUserData}
setVariables={setUserData} inputType={TextInputType.PHONE_NUMBER}
inputType={TextInputType.PICTURE} />
/> </Center>
</Center> </Flex>
</Flex> <Flex>
<Flex> <Flex w="30%" justifyContent="start" alignItems="center">
<Flex w="30%" justifyContent="start" alignItems="center"> <Text fontSize="sm">Picture:</Text>
<Text fontSize="sm">Roles:</Text> </Flex>
</Flex> <Center w="70%">
<Center w="70%"> <InputField
<InputField variables={userData}
variables={userData} setVariables={setUserData}
setVariables={setUserData} inputType={TextInputType.PICTURE}
inputType={ArrayInputType.USER_ROLES} />
/> </Center>
</Center> </Flex>
</Flex> <Flex>
</Stack> <Flex w="30%" justifyContent="start" alignItems="center">
</ModalBody> <Text fontSize="sm">Roles:</Text>
</Flex>
<Center w="70%">
<InputField
variables={userData}
setVariables={setUserData}
availableRoles={availableRoles}
inputType={MultiSelectInputType.USER_ROLES}
/>
</Center>
</Flex>
</Stack>
</ModalBody>
<ModalFooter> <ModalFooter>
<Button <Button
leftIcon={<FaSave />} leftIcon={<FaSave />}
colorScheme="blue" colorScheme="blue"
variant="solid" variant="solid"
onClick={saveHandler} onClick={saveHandler}
isDisabled={false} isDisabled={false}
> >
<Center h="100%" pt="5%"> <Center h="100%" pt="5%">
Save Save
</Center> </Center>
</Button> </Button>
</ModalFooter> </ModalFooter>
</ModalContent> </ModalContent>
</Modal> </Modal>
</> </>
); );
}; };
export default EditUserModal; export default EditUserModal;

View File

@ -1,340 +1,432 @@
import React from 'react'; import React, { useState } from 'react';
import { import {
Box, Box,
Flex, Flex,
Input, Input,
Center, Center,
InputGroup, InputGroup,
InputRightElement, InputRightElement,
Tag, Tag,
TagLabel, TagLabel,
TagRightIcon, TagRightIcon,
Select, Select,
Textarea, Textarea,
Switch, Switch,
Text, Text,
MenuButton,
MenuList,
MenuItemOption,
MenuOptionGroup,
Button,
Menu,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { import {
FaRegClone, FaRegClone,
FaRegEye, FaRegEye,
FaRegEyeSlash, FaRegEyeSlash,
FaPlus, FaPlus,
FaTimes, FaTimes,
FaAngleDown,
} from 'react-icons/fa'; } from 'react-icons/fa';
import { import {
ArrayInputOperations, ArrayInputOperations,
ArrayInputType, ArrayInputType,
SelectInputType, SelectInputType,
HiddenInputType, HiddenInputType,
TextInputType, TextInputType,
TextAreaInputType, TextAreaInputType,
SwitchInputType, SwitchInputType,
DateInputType, DateInputType,
MultiSelectInputType,
} from '../constants'; } from '../constants';
import { copyTextToClipboard } from '../utils'; import { copyTextToClipboard } from '../utils';
const InputField = ({ const InputField = ({
inputType, inputType,
variables, variables,
setVariables, setVariables,
fieldVisibility, fieldVisibility,
setFieldVisibility, setFieldVisibility,
...downshiftProps availableRoles,
...downshiftProps
}: any) => { }: any) => {
const props = { const props = {
size: 'sm', size: 'sm',
...downshiftProps, ...downshiftProps,
}; };
const [inputFieldVisibility, setInputFieldVisibility] = React.useState< const [availableUserRoles, setAvailableUserRoles] =
Record<string, boolean> useState<string[]>(availableRoles);
>({ const [inputFieldVisibility, setInputFieldVisibility] = useState<
ROLES: false, Record<string, boolean>
DEFAULT_ROLES: false, >({
PROTECTED_ROLES: false, ROLES: false,
ALLOWED_ORIGINS: false, DEFAULT_ROLES: false,
roles: false, PROTECTED_ROLES: false,
}); ALLOWED_ORIGINS: false,
const [inputData, setInputData] = React.useState<Record<string, string>>({ roles: false,
ROLES: '', });
DEFAULT_ROLES: '', const [inputData, setInputData] = useState<Record<string, string>>({
PROTECTED_ROLES: '', ROLES: '',
ALLOWED_ORIGINS: '', DEFAULT_ROLES: '',
roles: '', PROTECTED_ROLES: '',
}); ALLOWED_ORIGINS: '',
const updateInputHandler = ( roles: '',
type: string, });
operation: any, const updateInputHandler = (
role: string = '', type: string,
) => { operation: any,
if (operation === ArrayInputOperations.APPEND) { role: string = '',
if (inputData[type] !== '') { ) => {
setVariables({ if (operation === ArrayInputOperations.APPEND) {
...variables, if (inputData[type] !== '') {
[type]: [...variables[type], inputData[type]], setVariables({
}); ...variables,
setInputData({ ...inputData, [type]: '' }); [type]: [...variables[type], inputData[type]],
} });
setInputFieldVisibility({ ...inputFieldVisibility, [type]: false }); setInputData({ ...inputData, [type]: '' });
} }
if (operation === ArrayInputOperations.REMOVE) { setInputFieldVisibility({ ...inputFieldVisibility, [type]: false });
let updatedEnvVars = variables[type].filter( }
(item: string) => item !== role, if (operation === ArrayInputOperations.REMOVE) {
); let updatedEnvVars = variables[type].filter(
setVariables({ (item: string) => item !== role,
...variables, );
[type]: updatedEnvVars, setVariables({
}); ...variables,
} [type]: updatedEnvVars,
}; });
if (Object.values(TextInputType).includes(inputType)) { }
return ( };
<InputGroup size="sm"> if (Object.values(TextInputType).includes(inputType)) {
<Input return (
{...props} <InputGroup size="sm">
value={variables[inputType] ? variables[inputType] : ''} <Input
onChange={( {...props}
event: Event & { value={variables[inputType] ? variables[inputType] : ''}
target: HTMLInputElement; onChange={(
}, event: Event & {
) => target: HTMLInputElement;
setVariables({ },
...variables, ) =>
[inputType]: event.target.value, setVariables({
}) ...variables,
} [inputType]: event.target.value,
/> })
<InputRightElement }
children={<FaRegClone color="#bfbfbf" />} />
cursor="pointer" <InputRightElement
onClick={() => copyTextToClipboard(variables[inputType])} children={<FaRegClone color="#bfbfbf" />}
/> cursor="pointer"
</InputGroup> onClick={() => copyTextToClipboard(variables[inputType])}
); />
} </InputGroup>
if (Object.values(HiddenInputType).includes(inputType)) { );
return ( }
<InputGroup size="sm"> if (Object.values(HiddenInputType).includes(inputType)) {
<Input return (
{...props} <InputGroup size="sm">
value={variables[inputType] ?? ''} <Input
onChange={( {...props}
event: Event & { value={variables[inputType] || ''}
target: HTMLInputElement; onChange={(
}, event: Event & {
) => target: HTMLInputElement;
setVariables({ },
...variables, ) =>
[inputType]: event.target.value, setVariables({
}) ...variables,
} [inputType]: event.target.value,
type={!fieldVisibility[inputType] ? 'password' : 'text'} })
/> }
<InputRightElement type={!fieldVisibility[inputType] ? 'password' : 'text'}
right="15px" />
children={ <InputRightElement
<Flex> right="15px"
{fieldVisibility[inputType] ? ( children={
<Center <Flex>
w="25px" {fieldVisibility[inputType] ? (
margin="0 1.5%" <Center
cursor="pointer" w="25px"
onClick={() => margin="0 1.5%"
setFieldVisibility({ cursor="pointer"
...fieldVisibility, onClick={() =>
[inputType]: false, setFieldVisibility({
}) ...fieldVisibility,
} [inputType]: false,
> })
<FaRegEyeSlash color="#bfbfbf" /> }
</Center> >
) : ( <FaRegEyeSlash color="#bfbfbf" />
<Center </Center>
w="25px" ) : (
margin="0 1.5%" <Center
cursor="pointer" w="25px"
onClick={() => margin="0 1.5%"
setFieldVisibility({ cursor="pointer"
...fieldVisibility, onClick={() =>
[inputType]: true, setFieldVisibility({
}) ...fieldVisibility,
} [inputType]: true,
> })
<FaRegEye color="#bfbfbf" /> }
</Center> >
)} <FaRegEye color="#bfbfbf" />
<Center </Center>
w="25px" )}
margin="0 1.5%" <Center
cursor="pointer" w="25px"
onClick={() => copyTextToClipboard(variables[inputType])} margin="0 1.5%"
> cursor="pointer"
<FaRegClone color="#bfbfbf" /> onClick={() => copyTextToClipboard(variables[inputType])}
</Center> >
</Flex> <FaRegClone color="#bfbfbf" />
} </Center>
/> </Flex>
</InputGroup> }
); />
} </InputGroup>
if (Object.values(ArrayInputType).includes(inputType)) { );
return ( }
<Flex if (Object.values(ArrayInputType).includes(inputType)) {
border="1px solid #e2e8f0" return (
w="100%" <Flex
borderRadius={5} border="1px solid #e2e8f0"
paddingTop="0.5%" w="100%"
overflowX={variables[inputType].length > 3 ? 'scroll' : 'hidden'} borderRadius={5}
overflowY="hidden" paddingTop="0.5%"
justifyContent="start" overflowX={variables[inputType].length > 3 ? 'scroll' : 'hidden'}
alignItems="center" overflowY="hidden"
> justifyContent="start"
{variables[inputType].map((role: string, index: number) => ( alignItems="center"
<Box key={index} margin="0.5%" role="group"> >
<Tag {variables[inputType].map((role: string, index: number) => (
size="sm" <Box key={index} margin="0.5%" role="group">
variant="outline" <Tag
colorScheme="gray" size="sm"
minW="fit-content" variant="outline"
> colorScheme="gray"
<TagLabel cursor="default">{role}</TagLabel> minW="fit-content"
<TagRightIcon >
boxSize="12px" <TagLabel cursor="default">{role}</TagLabel>
as={FaTimes} <TagRightIcon
display="none" boxSize="12px"
cursor="pointer" as={FaTimes}
_groupHover={{ display: 'block' }} display="none"
onClick={() => cursor="pointer"
updateInputHandler( _groupHover={{ display: 'block' }}
inputType, onClick={() =>
ArrayInputOperations.REMOVE, updateInputHandler(
role, inputType,
) ArrayInputOperations.REMOVE,
} role,
/> )
</Tag> }
</Box> />
))} </Tag>
{inputFieldVisibility[inputType] ? ( </Box>
<Box ml="1%" mb="0.75%"> ))}
<Input {inputFieldVisibility[inputType] ? (
type="text" <Box ml="1%" mb="0.75%">
size="xs" <Input
minW="150px" type="text"
placeholder="add a new value" size="xs"
value={inputData[inputType] ?? ''} minW="150px"
onChange={(e: any) => { placeholder="add a new value"
setInputData({ ...inputData, [inputType]: e.target.value }); value={inputData[inputType] || ''}
}} onChange={(e: any) => {
onBlur={() => setInputData({ ...inputData, [inputType]: e.target.value });
updateInputHandler(inputType, ArrayInputOperations.APPEND) }}
} onBlur={() =>
onKeyPress={(event) => { updateInputHandler(inputType, ArrayInputOperations.APPEND)
if (event.key === 'Enter') { }
updateInputHandler(inputType, ArrayInputOperations.APPEND); onKeyPress={(event) => {
} if (event.key === 'Enter') {
}} updateInputHandler(inputType, ArrayInputOperations.APPEND);
/> }
</Box> }}
) : ( />
<Box </Box>
marginLeft="0.5%" ) : (
cursor="pointer" <Box
onClick={() => marginLeft="0.5%"
setInputFieldVisibility({ cursor="pointer"
...inputFieldVisibility, onClick={() =>
[inputType]: true, setInputFieldVisibility({
}) ...inputFieldVisibility,
} [inputType]: true,
> })
<Tag }
size="sm" >
variant="outline" <Tag
colorScheme="gray" size="sm"
minW="fit-content" variant="outline"
> colorScheme="gray"
<FaPlus /> minW="fit-content"
</Tag> >
</Box> <FaPlus />
)} </Tag>
</Flex> </Box>
); )}
} </Flex>
if (Object.values(SelectInputType).includes(inputType)) { );
const { options, ...rest } = props; }
return ( if (Object.values(SelectInputType).includes(inputType)) {
<Select const { options, ...rest } = props;
size="sm" return (
{...rest} <Select
value={variables[inputType] ? variables[inputType] : ''} size="sm"
onChange={(e) => {...rest}
setVariables({ ...variables, [inputType]: e.target.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} {Object.entries(options).map(([key, value]: any) => (
</option> <option value={value} key={key}>
))} {key}
</Select> </option>
); ))}
} </Select>
if (Object.values(TextAreaInputType).includes(inputType)) { );
return ( }
<Textarea if (Object.values(MultiSelectInputType).includes(inputType)) {
{...props} return (
size="lg" <Flex w="100%" style={{ position: 'relative' }}>
fontSize={14} <Flex
value={variables[inputType] ? variables[inputType] : ''} border="1px solid #e2e8f0"
onChange={( w="100%"
event: Event & { borderRadius="var(--chakra-radii-sm)"
target: HTMLInputElement; p="1% 0 0 2.5%"
}, overflowX={variables[inputType].length > 3 ? 'scroll' : 'hidden'}
) => overflowY="hidden"
setVariables({ justifyContent="space-between"
...variables, alignItems="center"
[inputType]: event.target.value, >
}) <Flex justifyContent="start" alignItems="center" w="100%" wrap="wrap">
} {variables[inputType].map((role: string, index: number) => (
/> <Box key={index} margin="0.5%" role="group">
); <Tag
} size="sm"
if (Object.values(SwitchInputType).includes(inputType)) { variant="outline"
return ( colorScheme="gray"
<Flex w="25%" justifyContent="space-between"> minW="fit-content"
<Text h="75%" fontWeight="bold" marginRight="2"> >
Off <TagLabel cursor="default">{role}</TagLabel>
</Text> <TagRightIcon
<Switch boxSize="12px"
size="md" as={FaTimes}
isChecked={variables[inputType]} display="none"
onChange={() => { cursor="pointer"
setVariables({ _groupHover={{ display: 'block' }}
...variables, onClick={() =>
[inputType]: !variables[inputType], updateInputHandler(
}); inputType,
}} ArrayInputOperations.REMOVE,
/> role,
<Text h="75%" fontWeight="bold" marginLeft="2"> )
On }
</Text> />
</Flex> </Tag>
); </Box>
} ))}
if (Object.values(DateInputType).includes(inputType)) { </Flex>
return ( <Menu matchWidth={true}>
<Flex border="1px solid #e2e8f0" w="100%" h="33px" padding="1%"> <MenuButton px="10px" py="7.5px">
<input <FaAngleDown />
type="date" </MenuButton>
style={{ width: '100%', paddingLeft: '2.5%' }} <MenuList
value={variables[inputType] ? variables[inputType] : ''} position="absolute"
onChange={(e) => top="0"
setVariables({ ...variables, [inputType]: e.target.value }) right="0"
} zIndex="10"
/> maxH="150"
</Flex> overflowX="scroll"
); >
} <MenuOptionGroup
return null; title={undefined}
value={variables[inputType]}
type="checkbox"
onChange={(values: string[] | string) => {
setVariables({
...variables,
[inputType]: values,
});
}}
>
{availableUserRoles.map((role) => {
return (
<MenuItemOption
key={`multiselect-menu-${role}`}
value={role}
>
{role}
</MenuItemOption>
);
})}
</MenuOptionGroup>
</MenuList>
</Menu>
</Flex>
</Flex>
);
}
if (Object.values(TextAreaInputType).includes(inputType)) {
return (
<Textarea
{...props}
size="lg"
fontSize={14}
value={variables[inputType] ? variables[inputType] : ''}
onChange={(
event: Event & {
target: HTMLInputElement;
},
) =>
setVariables({
...variables,
[inputType]: event.target.value,
})
}
/>
);
}
if (Object.values(SwitchInputType).includes(inputType)) {
return (
<Flex w="25%" justifyContent="space-between">
<Text h="75%" fontWeight="bold" marginRight="2">
Off
</Text>
<Switch
size="md"
isChecked={variables[inputType]}
onChange={() => {
setVariables({
...variables,
[inputType]: !variables[inputType],
});
}}
/>
<Text h="75%" fontWeight="bold" marginLeft="2">
On
</Text>
</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;
}; };
export default InputField; export default InputField;

View File

@ -1,311 +1,314 @@
export const LOGO_URL = export const LOGO_URL =
'https://user-images.githubusercontent.com/6964334/147834043-fc384cab-e7ca-40f8-9663-38fc25fd5f3a.png'; 'https://user-images.githubusercontent.com/6964334/147834043-fc384cab-e7ca-40f8-9663-38fc25fd5f3a.png';
export const TextInputType = { export const TextInputType = {
ACCESS_TOKEN_EXPIRY_TIME: 'ACCESS_TOKEN_EXPIRY_TIME', ACCESS_TOKEN_EXPIRY_TIME: 'ACCESS_TOKEN_EXPIRY_TIME',
CLIENT_ID: 'CLIENT_ID', CLIENT_ID: 'CLIENT_ID',
GOOGLE_CLIENT_ID: 'GOOGLE_CLIENT_ID', GOOGLE_CLIENT_ID: 'GOOGLE_CLIENT_ID',
GITHUB_CLIENT_ID: 'GITHUB_CLIENT_ID', GITHUB_CLIENT_ID: 'GITHUB_CLIENT_ID',
FACEBOOK_CLIENT_ID: 'FACEBOOK_CLIENT_ID', FACEBOOK_CLIENT_ID: 'FACEBOOK_CLIENT_ID',
LINKEDIN_CLIENT_ID: 'LINKEDIN_CLIENT_ID', LINKEDIN_CLIENT_ID: 'LINKEDIN_CLIENT_ID',
APPLE_CLIENT_ID: 'APPLE_CLIENT_ID', APPLE_CLIENT_ID: 'APPLE_CLIENT_ID',
TWITTER_CLIENT_ID: 'TWITTER_CLIENT_ID', TWITTER_CLIENT_ID: 'TWITTER_CLIENT_ID',
JWT_ROLE_CLAIM: 'JWT_ROLE_CLAIM', JWT_ROLE_CLAIM: 'JWT_ROLE_CLAIM',
REDIS_URL: 'REDIS_URL', REDIS_URL: 'REDIS_URL',
SMTP_HOST: 'SMTP_HOST', SMTP_HOST: 'SMTP_HOST',
SMTP_PORT: 'SMTP_PORT', SMTP_PORT: 'SMTP_PORT',
SMTP_USERNAME: 'SMTP_USERNAME', SMTP_USERNAME: 'SMTP_USERNAME',
SMTP_LOCAL_NAME: 'SMTP_LOCAL_NAME', SMTP_LOCAL_NAME: 'SMTP_LOCAL_NAME',
SENDER_EMAIL: 'SENDER_EMAIL', SENDER_EMAIL: 'SENDER_EMAIL',
ORGANIZATION_NAME: 'ORGANIZATION_NAME', ORGANIZATION_NAME: 'ORGANIZATION_NAME',
ORGANIZATION_LOGO: 'ORGANIZATION_LOGO', ORGANIZATION_LOGO: 'ORGANIZATION_LOGO',
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', GIVEN_NAME: 'given_name',
MIDDLE_NAME: 'middle_name', MIDDLE_NAME: 'middle_name',
FAMILY_NAME: 'family_name', FAMILY_NAME: 'family_name',
NICKNAME: 'nickname', NICKNAME: 'nickname',
PHONE_NUMBER: 'phone_number', PHONE_NUMBER: 'phone_number',
PICTURE: 'picture', PICTURE: 'picture',
}; };
export const HiddenInputType = { export const HiddenInputType = {
CLIENT_SECRET: 'CLIENT_SECRET', CLIENT_SECRET: 'CLIENT_SECRET',
GOOGLE_CLIENT_SECRET: 'GOOGLE_CLIENT_SECRET', GOOGLE_CLIENT_SECRET: 'GOOGLE_CLIENT_SECRET',
GITHUB_CLIENT_SECRET: 'GITHUB_CLIENT_SECRET', GITHUB_CLIENT_SECRET: 'GITHUB_CLIENT_SECRET',
FACEBOOK_CLIENT_SECRET: 'FACEBOOK_CLIENT_SECRET', FACEBOOK_CLIENT_SECRET: 'FACEBOOK_CLIENT_SECRET',
LINKEDIN_CLIENT_SECRET: 'LINKEDIN_CLIENT_SECRET', LINKEDIN_CLIENT_SECRET: 'LINKEDIN_CLIENT_SECRET',
APPLE_CLIENT_SECRET: 'APPLE_CLIENT_SECRET', APPLE_CLIENT_SECRET: 'APPLE_CLIENT_SECRET',
TWITTER_CLIENT_SECRET: 'TWITTER_CLIENT_SECRET', TWITTER_CLIENT_SECRET: 'TWITTER_CLIENT_SECRET',
JWT_SECRET: 'JWT_SECRET', JWT_SECRET: 'JWT_SECRET',
SMTP_PASSWORD: 'SMTP_PASSWORD', SMTP_PASSWORD: 'SMTP_PASSWORD',
ADMIN_SECRET: 'ADMIN_SECRET', ADMIN_SECRET: 'ADMIN_SECRET',
OLD_ADMIN_SECRET: 'OLD_ADMIN_SECRET', OLD_ADMIN_SECRET: 'OLD_ADMIN_SECRET',
}; };
export const ArrayInputType = { export const ArrayInputType = {
ROLES: 'ROLES', ROLES: 'ROLES',
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', GENDER: 'gender',
};
export const MultiSelectInputType = {
USER_ROLES: 'roles',
}; };
export const TextAreaInputType = { export const TextAreaInputType = {
CUSTOM_ACCESS_TOKEN_SCRIPT: 'CUSTOM_ACCESS_TOKEN_SCRIPT', CUSTOM_ACCESS_TOKEN_SCRIPT: 'CUSTOM_ACCESS_TOKEN_SCRIPT',
JWT_PRIVATE_KEY: 'JWT_PRIVATE_KEY', JWT_PRIVATE_KEY: 'JWT_PRIVATE_KEY',
JWT_PUBLIC_KEY: 'JWT_PUBLIC_KEY', JWT_PUBLIC_KEY: 'JWT_PUBLIC_KEY',
}; };
export const SwitchInputType = { export const SwitchInputType = {
APP_COOKIE_SECURE: 'APP_COOKIE_SECURE', APP_COOKIE_SECURE: 'APP_COOKIE_SECURE',
ADMIN_COOKIE_SECURE: 'ADMIN_COOKIE_SECURE', ADMIN_COOKIE_SECURE: 'ADMIN_COOKIE_SECURE',
DISABLE_LOGIN_PAGE: 'DISABLE_LOGIN_PAGE', DISABLE_LOGIN_PAGE: 'DISABLE_LOGIN_PAGE',
DISABLE_MAGIC_LINK_LOGIN: 'DISABLE_MAGIC_LINK_LOGIN', DISABLE_MAGIC_LINK_LOGIN: 'DISABLE_MAGIC_LINK_LOGIN',
DISABLE_EMAIL_VERIFICATION: 'DISABLE_EMAIL_VERIFICATION', DISABLE_EMAIL_VERIFICATION: 'DISABLE_EMAIL_VERIFICATION',
DISABLE_BASIC_AUTHENTICATION: 'DISABLE_BASIC_AUTHENTICATION', DISABLE_BASIC_AUTHENTICATION: 'DISABLE_BASIC_AUTHENTICATION',
DISABLE_SIGN_UP: 'DISABLE_SIGN_UP', DISABLE_SIGN_UP: 'DISABLE_SIGN_UP',
DISABLE_REDIS_FOR_ENV: 'DISABLE_REDIS_FOR_ENV', DISABLE_REDIS_FOR_ENV: 'DISABLE_REDIS_FOR_ENV',
DISABLE_STRONG_PASSWORD: 'DISABLE_STRONG_PASSWORD', DISABLE_STRONG_PASSWORD: 'DISABLE_STRONG_PASSWORD',
DISABLE_MULTI_FACTOR_AUTHENTICATION: 'DISABLE_MULTI_FACTOR_AUTHENTICATION', DISABLE_MULTI_FACTOR_AUTHENTICATION: 'DISABLE_MULTI_FACTOR_AUTHENTICATION',
ENFORCE_MULTI_FACTOR_AUTHENTICATION: 'ENFORCE_MULTI_FACTOR_AUTHENTICATION', ENFORCE_MULTI_FACTOR_AUTHENTICATION: 'ENFORCE_MULTI_FACTOR_AUTHENTICATION',
}; };
export const DateInputType = { export const DateInputType = {
BIRTHDATE: 'birthdate', BIRTHDATE: 'birthdate',
}; };
export const ArrayInputOperations = { export const ArrayInputOperations = {
APPEND: 'APPEND', APPEND: 'APPEND',
REMOVE: 'REMOVE', REMOVE: 'REMOVE',
}; };
export const HMACEncryptionType = { export const HMACEncryptionType = {
HS256: 'HS256', HS256: 'HS256',
HS384: 'HS384', HS384: 'HS384',
HS512: 'HS512', HS512: 'HS512',
}; };
export const RSAEncryptionType = { export const RSAEncryptionType = {
RS256: 'RS256', RS256: 'RS256',
RS384: 'RS384', RS384: 'RS384',
RS512: 'RS512', RS512: 'RS512',
}; };
export const ECDSAEncryptionType = { export const ECDSAEncryptionType = {
ES256: 'ES256', ES256: 'ES256',
ES384: 'ES384', ES384: 'ES384',
ES512: 'ES512', ES512: 'ES512',
}; };
export interface envVarTypes { export interface envVarTypes {
GOOGLE_CLIENT_ID: string; GOOGLE_CLIENT_ID: string;
GOOGLE_CLIENT_SECRET: string; GOOGLE_CLIENT_SECRET: string;
GITHUB_CLIENT_ID: string; GITHUB_CLIENT_ID: string;
GITHUB_CLIENT_SECRET: string; GITHUB_CLIENT_SECRET: string;
FACEBOOK_CLIENT_ID: string; FACEBOOK_CLIENT_ID: string;
FACEBOOK_CLIENT_SECRET: string; FACEBOOK_CLIENT_SECRET: string;
LINKEDIN_CLIENT_ID: string; LINKEDIN_CLIENT_ID: string;
LINKEDIN_CLIENT_SECRET: string; LINKEDIN_CLIENT_SECRET: string;
APPLE_CLIENT_ID: string; APPLE_CLIENT_ID: string;
APPLE_CLIENT_SECRET: string; APPLE_CLIENT_SECRET: string;
TWITTER_CLIENT_ID: string; TWITTER_CLIENT_ID: string;
TWITTER_CLIENT_SECRET: string; TWITTER_CLIENT_SECRET: string;
ROLES: [string] | []; ROLES: [string] | [];
DEFAULT_ROLES: [string] | []; DEFAULT_ROLES: [string] | [];
PROTECTED_ROLES: [string] | []; PROTECTED_ROLES: [string] | [];
JWT_TYPE: string; JWT_TYPE: string;
JWT_SECRET: string; JWT_SECRET: string;
JWT_ROLE_CLAIM: string; JWT_ROLE_CLAIM: string;
JWT_PRIVATE_KEY: string; JWT_PRIVATE_KEY: string;
JWT_PUBLIC_KEY: string; JWT_PUBLIC_KEY: string;
REDIS_URL: string; REDIS_URL: string;
SMTP_HOST: string; SMTP_HOST: string;
SMTP_PORT: string; SMTP_PORT: string;
SMTP_USERNAME: string; SMTP_USERNAME: string;
SMTP_PASSWORD: string; SMTP_PASSWORD: string;
SMTP_LOCAL_NAME: string; SMTP_LOCAL_NAME: string;
SENDER_EMAIL: string; SENDER_EMAIL: string;
ALLOWED_ORIGINS: [string] | []; ALLOWED_ORIGINS: [string] | [];
ORGANIZATION_NAME: string; ORGANIZATION_NAME: string;
ORGANIZATION_LOGO: string; ORGANIZATION_LOGO: string;
CUSTOM_ACCESS_TOKEN_SCRIPT: string; CUSTOM_ACCESS_TOKEN_SCRIPT: string;
ADMIN_SECRET: string; ADMIN_SECRET: string;
APP_COOKIE_SECURE: boolean; APP_COOKIE_SECURE: boolean;
ADMIN_COOKIE_SECURE: boolean; ADMIN_COOKIE_SECURE: boolean;
DISABLE_LOGIN_PAGE: boolean; DISABLE_LOGIN_PAGE: boolean;
DISABLE_MAGIC_LINK_LOGIN: boolean; DISABLE_MAGIC_LINK_LOGIN: boolean;
DISABLE_EMAIL_VERIFICATION: boolean; DISABLE_EMAIL_VERIFICATION: boolean;
DISABLE_BASIC_AUTHENTICATION: boolean; DISABLE_BASIC_AUTHENTICATION: boolean;
DISABLE_SIGN_UP: boolean; DISABLE_SIGN_UP: boolean;
DISABLE_STRONG_PASSWORD: boolean; DISABLE_STRONG_PASSWORD: boolean;
OLD_ADMIN_SECRET: string; OLD_ADMIN_SECRET: string;
DATABASE_NAME: string; DATABASE_NAME: string;
DATABASE_TYPE: string; DATABASE_TYPE: string;
DATABASE_URL: string; DATABASE_URL: string;
ACCESS_TOKEN_EXPIRY_TIME: string; ACCESS_TOKEN_EXPIRY_TIME: string;
DISABLE_MULTI_FACTOR_AUTHENTICATION: boolean; DISABLE_MULTI_FACTOR_AUTHENTICATION: boolean;
ENFORCE_MULTI_FACTOR_AUTHENTICATION: boolean; ENFORCE_MULTI_FACTOR_AUTHENTICATION: boolean;
} }
export const envSubViews = { export const envSubViews = {
INSTANCE_INFO: 'instance-info', INSTANCE_INFO: 'instance-info',
ROLES: 'roles', ROLES: 'roles',
JWT_CONFIG: 'jwt-config', JWT_CONFIG: 'jwt-config',
SESSION_STORAGE: 'session-storage', SESSION_STORAGE: 'session-storage',
EMAIL_CONFIG: 'email-config', EMAIL_CONFIG: 'email-config',
WHITELIST_VARIABLES: 'whitelist-variables', WHITELIST_VARIABLES: 'whitelist-variables',
ORGANIZATION_INFO: 'organization-info', ORGANIZATION_INFO: 'organization-info',
ACCESS_TOKEN: 'access-token', ACCESS_TOKEN: 'access-token',
FEATURES: 'features', FEATURES: 'features',
ADMIN_SECRET: 'admin-secret', ADMIN_SECRET: 'admin-secret',
DB_CRED: 'db-cred', DB_CRED: 'db-cred',
}; };
export enum WebhookInputDataFields { export enum WebhookInputDataFields {
ID = 'id', ID = 'id',
EVENT_NAME = 'event_name', EVENT_NAME = 'event_name',
ENDPOINT = 'endpoint', ENDPOINT = 'endpoint',
ENABLED = 'enabled', ENABLED = 'enabled',
HEADERS = 'headers', HEADERS = 'headers',
} }
export enum EmailTemplateInputDataFields { export enum EmailTemplateInputDataFields {
ID = 'id', ID = 'id',
EVENT_NAME = 'event_name', EVENT_NAME = 'event_name',
SUBJECT = 'subject', SUBJECT = 'subject',
CREATED_AT = 'created_at', CREATED_AT = 'created_at',
TEMPLATE = 'template', TEMPLATE = 'template',
DESIGN = 'design', DESIGN = 'design',
} }
export enum WebhookInputHeaderFields { export enum WebhookInputHeaderFields {
KEY = 'key', KEY = 'key',
VALUE = 'value', VALUE = 'value',
} }
export enum UpdateModalViews { export enum UpdateModalViews {
ADD = 'add', ADD = 'add',
Edit = 'edit', Edit = 'edit',
} }
export const pageLimits: number[] = [5, 10, 15]; export const pageLimits: number[] = [5, 10, 15];
export const webhookEventNames = { export const webhookEventNames = {
'User signup': 'user.signup', 'User signup': 'user.signup',
'User created': 'user.created', 'User created': 'user.created',
'User login': 'user.login', 'User login': 'user.login',
'User deleted': 'user.deleted', 'User deleted': 'user.deleted',
'User access enabled': 'user.access_enabled', 'User access enabled': 'user.access_enabled',
'User access revoked': 'user.access_revoked', 'User access revoked': 'user.access_revoked',
}; };
export const emailTemplateEventNames = { export const emailTemplateEventNames = {
Signup: 'basic_auth_signup', Signup: 'basic_auth_signup',
'Magic Link Login': 'magic_link_login', 'Magic Link Login': 'magic_link_login',
'Update Email': 'update_email', 'Update Email': 'update_email',
'Forgot Password': 'forgot_password', 'Forgot Password': 'forgot_password',
'Verify Otp': 'verify_otp', 'Verify Otp': 'verify_otp',
'Invite member': 'invite_member', 'Invite member': 'invite_member',
}; };
export enum webhookVerifiedStatus { export enum webhookVerifiedStatus {
VERIFIED = 'verified', VERIFIED = 'verified',
NOT_VERIFIED = 'not_verified', NOT_VERIFIED = 'not_verified',
PENDING = 'verification_pending', PENDING = 'verification_pending',
} }
export const emailTemplateVariables = { export const emailTemplateVariables = {
'user.id': { 'user.id': {
description: `User identifier`, description: `User identifier`,
value: '{.user.id}}', value: '{.user.id}}',
}, },
'user.email': { 'user.email': {
description: 'User email address', description: 'User email address',
value: '{.user.email}}', value: '{.user.email}}',
}, },
'user.given_name': { 'user.given_name': {
description: `User first name`, description: `User first name`,
value: '{.user.given_name}}', value: '{.user.given_name}}',
}, },
'user.family_name': { 'user.family_name': {
description: `User last name`, description: `User last name`,
value: '{.user.family_name}}', value: '{.user.family_name}}',
}, },
'user.middle_name': { 'user.middle_name': {
description: `Middle name of user`, description: `Middle name of user`,
value: '{.user.middle_name}}', value: '{.user.middle_name}}',
}, },
'user.nickname': { 'user.nickname': {
description: `Nick name of user`, description: `Nick name of user`,
value: '{.user.nickname}}', value: '{.user.nickname}}',
}, },
'user.preferred_username': { 'user.preferred_username': {
description: `Username, by default it is email`, description: `Username, by default it is email`,
value: '{.user.preferred_username}}', value: '{.user.preferred_username}}',
}, },
'user.signup_methods': { 'user.signup_methods': {
description: `Comma separated list of methods using which user has signed up`, description: `Comma separated list of methods using which user has signed up`,
value: '{.user.signup_methods}}', value: '{.user.signup_methods}}',
}, },
'user.email_verified': { 'user.email_verified': {
description: `Whether email is verified or not`, description: `Whether email is verified or not`,
value: '{.user.email_verified}}', value: '{.user.email_verified}}',
}, },
'user.picture': { 'user.picture': {
description: `URL of the user profile picture`, description: `URL of the user profile picture`,
value: '{.user.picture}}', value: '{.user.picture}}',
}, },
'user.roles': { 'user.roles': {
description: `Comma separated list of roles assigned to user`, description: `Comma separated list of roles assigned to user`,
value: '{.user.roles}}', value: '{.user.roles}}',
}, },
'user.gender': { 'user.gender': {
description: `Gender of user`, description: `Gender of user`,
value: '{.user.gender}}', value: '{.user.gender}}',
}, },
'user.birthdate': { 'user.birthdate': {
description: `BirthDate of user`, description: `BirthDate of user`,
value: '{.user.birthdate}}', value: '{.user.birthdate}}',
}, },
'user.phone_number': { 'user.phone_number': {
description: `Phone number of user`, description: `Phone number of user`,
value: '{.user.phone_number}}', value: '{.user.phone_number}}',
}, },
'user.phone_number_verified': { 'user.phone_number_verified': {
description: `Whether phone number is verified or not`, description: `Whether phone number is verified or not`,
value: '{.user.phone_number_verified}}', value: '{.user.phone_number_verified}}',
}, },
'user.created_at': { 'user.created_at': {
description: `User created at time`, description: `User created at time`,
value: '{.user.created_at}}', value: '{.user.created_at}}',
}, },
'user.updated_at': { 'user.updated_at': {
description: `Last updated time at user`, description: `Last updated time at user`,
value: '{.user.updated_at}}', value: '{.user.updated_at}}',
}, },
'organization.name': { 'organization.name': {
description: `Organization name`, description: `Organization name`,
value: '{.organization.name}}', value: '{.organization.name}}',
}, },
'organization.logo': { 'organization.logo': {
description: `Organization logo`, description: `Organization logo`,
value: '{.organization.logo}}', value: '{.organization.logo}}',
}, },
verification_url: { verification_url: {
description: `Verification URL in case of events other than verify otp`, description: `Verification URL in case of events other than verify otp`,
value: '{.verification_url}}', value: '{.verification_url}}',
}, },
otp: { otp: {
description: `OTP sent during login with Multi factor authentication`, description: `OTP sent during login with Multi factor authentication`,
value: '{.otp}}', value: '{.otp}}',
}, },
}; };
export const webhookPayloadExample: string = `{ export const webhookPayloadExample: string = `{

View File

@ -170,3 +170,12 @@ export const WebhookLogsQuery = `
} }
} }
`; `;
export const GetAvailableRolesQuery = `
query {
_env {
ROLES
PROTECTED_ROLES
}
}
`;