Merge pull request #280 from authorizerdev/feat/user-roles-multi-select
feat: add user roles multi select input
This commit is contained in:
commit
3b196f074b
|
@ -1,4 +1,4 @@
|
||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Center,
|
Center,
|
||||||
|
@ -20,13 +20,14 @@ 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,
|
||||||
|
@ -57,8 +58,9 @@ const EditUserModal = ({
|
||||||
}) => {
|
}) => {
|
||||||
const client = useClient();
|
const client = useClient();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
const [availableRoles, setAvailableRoles] = useState<string[]>([]);
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
const [userData, setUserData] = React.useState<userDataTypes>({
|
const [userData, setUserData] = useState<userDataTypes>({
|
||||||
id: '',
|
id: '',
|
||||||
email: '',
|
email: '',
|
||||||
given_name: '',
|
given_name: '',
|
||||||
|
@ -73,7 +75,17 @@ const EditUserModal = ({
|
||||||
});
|
});
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
setUserData(user);
|
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 saveHandler = async () => {
|
||||||
const diff = getObjectDiff(user, userData);
|
const diff = getObjectDiff(user, userData);
|
||||||
const updatedUserData = diff.reduce(
|
const updatedUserData = diff.reduce(
|
||||||
|
@ -221,7 +233,8 @@ const EditUserModal = ({
|
||||||
<InputField
|
<InputField
|
||||||
variables={userData}
|
variables={userData}
|
||||||
setVariables={setUserData}
|
setVariables={setUserData}
|
||||||
inputType={ArrayInputType.USER_ROLES}
|
availableRoles={availableRoles}
|
||||||
|
inputType={MultiSelectInputType.USER_ROLES}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Flex,
|
Flex,
|
||||||
|
@ -13,6 +13,12 @@ import {
|
||||||
Textarea,
|
Textarea,
|
||||||
Switch,
|
Switch,
|
||||||
Text,
|
Text,
|
||||||
|
MenuButton,
|
||||||
|
MenuList,
|
||||||
|
MenuItemOption,
|
||||||
|
MenuOptionGroup,
|
||||||
|
Button,
|
||||||
|
Menu,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import {
|
import {
|
||||||
FaRegClone,
|
FaRegClone,
|
||||||
|
@ -20,6 +26,7 @@ import {
|
||||||
FaRegEyeSlash,
|
FaRegEyeSlash,
|
||||||
FaPlus,
|
FaPlus,
|
||||||
FaTimes,
|
FaTimes,
|
||||||
|
FaAngleDown,
|
||||||
} from 'react-icons/fa';
|
} from 'react-icons/fa';
|
||||||
import {
|
import {
|
||||||
ArrayInputOperations,
|
ArrayInputOperations,
|
||||||
|
@ -30,6 +37,7 @@ import {
|
||||||
TextAreaInputType,
|
TextAreaInputType,
|
||||||
SwitchInputType,
|
SwitchInputType,
|
||||||
DateInputType,
|
DateInputType,
|
||||||
|
MultiSelectInputType,
|
||||||
} from '../constants';
|
} from '../constants';
|
||||||
import { copyTextToClipboard } from '../utils';
|
import { copyTextToClipboard } from '../utils';
|
||||||
|
|
||||||
|
@ -39,13 +47,16 @@ const InputField = ({
|
||||||
setVariables,
|
setVariables,
|
||||||
fieldVisibility,
|
fieldVisibility,
|
||||||
setFieldVisibility,
|
setFieldVisibility,
|
||||||
|
availableRoles,
|
||||||
...downshiftProps
|
...downshiftProps
|
||||||
}: any) => {
|
}: any) => {
|
||||||
const props = {
|
const props = {
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
...downshiftProps,
|
...downshiftProps,
|
||||||
};
|
};
|
||||||
const [inputFieldVisibility, setInputFieldVisibility] = React.useState<
|
const [availableUserRoles, setAvailableUserRoles] =
|
||||||
|
useState<string[]>(availableRoles);
|
||||||
|
const [inputFieldVisibility, setInputFieldVisibility] = useState<
|
||||||
Record<string, boolean>
|
Record<string, boolean>
|
||||||
>({
|
>({
|
||||||
ROLES: false,
|
ROLES: false,
|
||||||
|
@ -54,7 +65,7 @@ const InputField = ({
|
||||||
ALLOWED_ORIGINS: false,
|
ALLOWED_ORIGINS: false,
|
||||||
roles: false,
|
roles: false,
|
||||||
});
|
});
|
||||||
const [inputData, setInputData] = React.useState<Record<string, string>>({
|
const [inputData, setInputData] = useState<Record<string, string>>({
|
||||||
ROLES: '',
|
ROLES: '',
|
||||||
DEFAULT_ROLES: '',
|
DEFAULT_ROLES: '',
|
||||||
PROTECTED_ROLES: '',
|
PROTECTED_ROLES: '',
|
||||||
|
@ -116,7 +127,7 @@ const InputField = ({
|
||||||
<InputGroup size="sm">
|
<InputGroup size="sm">
|
||||||
<Input
|
<Input
|
||||||
{...props}
|
{...props}
|
||||||
value={variables[inputType] ?? ''}
|
value={variables[inputType] || ''}
|
||||||
onChange={(
|
onChange={(
|
||||||
event: Event & {
|
event: Event & {
|
||||||
target: HTMLInputElement;
|
target: HTMLInputElement;
|
||||||
|
@ -221,7 +232,7 @@ const InputField = ({
|
||||||
size="xs"
|
size="xs"
|
||||||
minW="150px"
|
minW="150px"
|
||||||
placeholder="add a new value"
|
placeholder="add a new value"
|
||||||
value={inputData[inputType] ?? ''}
|
value={inputData[inputType] || ''}
|
||||||
onChange={(e: any) => {
|
onChange={(e: any) => {
|
||||||
setInputData({ ...inputData, [inputType]: e.target.value });
|
setInputData({ ...inputData, [inputType]: e.target.value });
|
||||||
}}
|
}}
|
||||||
|
@ -278,6 +289,87 @@ const InputField = ({
|
||||||
</Select>
|
</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)) {
|
if (Object.values(TextAreaInputType).includes(inputType)) {
|
||||||
return (
|
return (
|
||||||
<Textarea
|
<Textarea
|
||||||
|
|
|
@ -48,7 +48,6 @@ 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 = {
|
||||||
|
@ -56,6 +55,10 @@ export const SelectInputType = {
|
||||||
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',
|
||||||
|
|
|
@ -169,3 +169,12 @@ export const WebhookLogsQuery = `
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const GetAvailableRolesQuery = `
|
||||||
|
query {
|
||||||
|
_env {
|
||||||
|
ROLES
|
||||||
|
PROTECTED_ROLES
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user