Merge pull request #280 from authorizerdev/feat/user-roles-multi-select

feat: add user roles multi select input
This commit is contained in:
Lakhan Samani 2022-10-25 08:19:08 +05:30 committed by GitHub
commit 3b196f074b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 912 additions and 795 deletions

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import {
Button,
Center,
@ -20,13 +20,14 @@ import { useClient } from 'urql';
import { FaSave } from 'react-icons/fa';
import InputField from './InputField';
import {
ArrayInputType,
DateInputType,
MultiSelectInputType,
SelectInputType,
TextInputType,
} from '../constants';
import { getObjectDiff } from '../utils';
import { UpdateUser } from '../graphql/mutation';
import { GetAvailableRolesQuery } from '../graphql/queries';
const GenderTypes = {
Undisclosed: null,
@ -57,8 +58,9 @@ const EditUserModal = ({
}) => {
const client = useClient();
const toast = useToast();
const [availableRoles, setAvailableRoles] = useState<string[]>([]);
const { isOpen, onOpen, onClose } = useDisclosure();
const [userData, setUserData] = React.useState<userDataTypes>({
const [userData, setUserData] = useState<userDataTypes>({
id: '',
email: '',
given_name: '',
@ -73,7 +75,17 @@ const EditUserModal = ({
});
React.useEffect(() => {
setUserData(user);
fetchAvailableRoles();
}, []);
const fetchAvailableRoles = async () => {
const res = await client.query(GetAvailableRolesQuery).toPromise();
if (res.data?._env?.ROLES && res.data?._env?.PROTECTED_ROLES) {
setAvailableRoles([
...res.data._env.ROLES,
...res.data._env.PROTECTED_ROLES,
]);
}
};
const saveHandler = async () => {
const diff = getObjectDiff(user, userData);
const updatedUserData = diff.reduce(
@ -221,7 +233,8 @@ const EditUserModal = ({
<InputField
variables={userData}
setVariables={setUserData}
inputType={ArrayInputType.USER_ROLES}
availableRoles={availableRoles}
inputType={MultiSelectInputType.USER_ROLES}
/>
</Center>
</Flex>

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import {
Box,
Flex,
@ -13,6 +13,12 @@ import {
Textarea,
Switch,
Text,
MenuButton,
MenuList,
MenuItemOption,
MenuOptionGroup,
Button,
Menu,
} from '@chakra-ui/react';
import {
FaRegClone,
@ -20,6 +26,7 @@ import {
FaRegEyeSlash,
FaPlus,
FaTimes,
FaAngleDown,
} from 'react-icons/fa';
import {
ArrayInputOperations,
@ -30,6 +37,7 @@ import {
TextAreaInputType,
SwitchInputType,
DateInputType,
MultiSelectInputType,
} from '../constants';
import { copyTextToClipboard } from '../utils';
@ -39,13 +47,16 @@ const InputField = ({
setVariables,
fieldVisibility,
setFieldVisibility,
availableRoles,
...downshiftProps
}: any) => {
const props = {
size: 'sm',
...downshiftProps,
};
const [inputFieldVisibility, setInputFieldVisibility] = React.useState<
const [availableUserRoles, setAvailableUserRoles] =
useState<string[]>(availableRoles);
const [inputFieldVisibility, setInputFieldVisibility] = useState<
Record<string, boolean>
>({
ROLES: false,
@ -54,7 +65,7 @@ const InputField = ({
ALLOWED_ORIGINS: false,
roles: false,
});
const [inputData, setInputData] = React.useState<Record<string, string>>({
const [inputData, setInputData] = useState<Record<string, string>>({
ROLES: '',
DEFAULT_ROLES: '',
PROTECTED_ROLES: '',
@ -116,7 +127,7 @@ const InputField = ({
<InputGroup size="sm">
<Input
{...props}
value={variables[inputType] ?? ''}
value={variables[inputType] || ''}
onChange={(
event: Event & {
target: HTMLInputElement;
@ -221,7 +232,7 @@ const InputField = ({
size="xs"
minW="150px"
placeholder="add a new value"
value={inputData[inputType] ?? ''}
value={inputData[inputType] || ''}
onChange={(e: any) => {
setInputData({ ...inputData, [inputType]: e.target.value });
}}
@ -278,6 +289,87 @@ const InputField = ({
</Select>
);
}
if (Object.values(MultiSelectInputType).includes(inputType)) {
return (
<Flex w="100%" style={{ position: 'relative' }}>
<Flex
border="1px solid #e2e8f0"
w="100%"
borderRadius="var(--chakra-radii-sm)"
p="1% 0 0 2.5%"
overflowX={variables[inputType].length > 3 ? 'scroll' : 'hidden'}
overflowY="hidden"
justifyContent="space-between"
alignItems="center"
>
<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"
variant="outline"
colorScheme="gray"
minW="fit-content"
>
<TagLabel cursor="default">{role}</TagLabel>
<TagRightIcon
boxSize="12px"
as={FaTimes}
display="none"
cursor="pointer"
_groupHover={{ display: 'block' }}
onClick={() =>
updateInputHandler(
inputType,
ArrayInputOperations.REMOVE,
role,
)
}
/>
</Tag>
</Box>
))}
</Flex>
<Menu matchWidth={true}>
<MenuButton px="10px" py="7.5px">
<FaAngleDown />
</MenuButton>
<MenuList
position="absolute"
top="0"
right="0"
zIndex="10"
maxH="150"
overflowX="scroll"
>
<MenuOptionGroup
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

View File

@ -48,7 +48,6 @@ export const ArrayInputType = {
DEFAULT_ROLES: 'DEFAULT_ROLES',
PROTECTED_ROLES: 'PROTECTED_ROLES',
ALLOWED_ORIGINS: 'ALLOWED_ORIGINS',
USER_ROLES: 'roles',
};
export const SelectInputType = {
@ -56,6 +55,10 @@ export const SelectInputType = {
GENDER: 'gender',
};
export const MultiSelectInputType = {
USER_ROLES: 'roles',
};
export const TextAreaInputType = {
CUSTOM_ACCESS_TOKEN_SCRIPT: 'CUSTOM_ACCESS_TOKEN_SCRIPT',
JWT_PRIVATE_KEY: 'JWT_PRIVATE_KEY',

View File

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