Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User edit fixes #335

Merged
merged 18 commits into from
Aug 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/components/UI/Form/AutoComplete/AutoComplete.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from 'react';
import styles from './AutoComplete.module.css';
import TextField from '@material-ui/core/TextField';
import Autocomplete from '@material-ui/lab/Autocomplete';
import { Chip } from '@material-ui/core';
import { getIn } from 'formik';
import styles from './AutoComplete.module.css';

export interface AutocompleteProps {
options: any;
Expand Down Expand Up @@ -33,12 +33,12 @@ export const AutoComplete: React.SFC<AutocompleteProps> = ({
<Autocomplete
className={styles.Input}
multiple
id="checkboxes-tags-demo"
data-testid="autocomplete-element"
options={optionValue}
getOptionLabel={(option: any) => option[optionLabel]}
onChange={(event, Value: any) => {
if (Value) {
setFieldValue(field.name, Value);
onChange={(event, value: any) => {
if (value) {
setFieldValue(field.name, value);
}
}}
value={field.value}
Expand Down
3 changes: 2 additions & 1 deletion src/components/UI/Form/Dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React from 'react';
import MenuItem from '@material-ui/core/MenuItem';
import styles from './Dropdown.module.css';
import { Select, FormControl, InputLabel, FormHelperText } from '@material-ui/core';

import styles from './Dropdown.module.css';

export interface DropdownProps {
type?: any;
field: any;
Expand Down
28 changes: 8 additions & 20 deletions src/containers/Collection/Collection.test.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import React from 'react';
import { shallow } from 'enzyme';
import { Collection } from './Collection';
import gqlClient from '../../config/apolloclient';
import { render, wait, within, fireEvent } from '@testing-library/react';
import { BrowserRouter as Router } from 'react-router-dom';
import { render, wait } from '@testing-library/react';
import { MockedProvider } from '@apollo/client/testing';
import { Route } from 'react-router-dom';
import { CollectionList } from './CollectionList/CollectionList';

import { Collection } from './Collection';
import { LIST_ITEM_MOCKS } from './Collection.test.helper';

const mocks = LIST_ITEM_MOCKS;

const wrapper = shallow(
<MockedProvider mocks={[]}>
<MockedProvider mocks={mocks} addTypename={false}>
<Collection />
</MockedProvider>
);
Expand All @@ -23,22 +20,13 @@ describe('<Collection />', () => {
});
});

test('cancel button should redirect to collectionlist page', async () => {
const { container, getByText, unmount } = render(
test('should load the collection edit', async () => {
const { getByText } = render(
<MockedProvider mocks={mocks} addTypename={false}>
<Router>
<Collection match={{ params: { id: 1 } }} />
<Route path="/collection" exact component={CollectionList} />
</Router>
<Collection match={{ params: { id: 1 } }} />
</MockedProvider>
);
await wait();
const { queryByText } = within(container.querySelector('form'));
const button = queryByText('Cancel');

fireEvent.click(button);
expect(getByText('Loading...')).toBeInTheDocument();
await wait();
expect(getByText('Collections')).toBeInTheDocument();
unmount();
expect(getByText('Edit Collection')).toBeInTheDocument();
});
2 changes: 1 addition & 1 deletion src/containers/Collection/Collection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ export const Collection: React.SFC<CollectionProps> = ({ match }) => {
setStates={setStates}
setPayload={setPayload}
validationSchema={FormSchema}
listItemName="collection"
listItemName="Collection"
dialogMessage={dialogMessage}
formFields={formFields}
redirectionLink="collection"
Expand Down
22 changes: 12 additions & 10 deletions src/containers/Form/FormLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ import React, { useState } from 'react';
import { Redirect } from 'react-router-dom';
import { Formik, Form, Field } from 'formik';
import { useApolloClient, DocumentNode, ApolloError } from '@apollo/client';
import styles from './FormLayout.module.css';
import { useQuery, useMutation } from '@apollo/client';
import { Typography, IconButton } from '@material-ui/core';

import { Button } from '../../components/UI/Form/Button/Button';
import { Dropdown } from '../../components/UI/Form/Dropdown/Dropdown';
import { DialogBox } from '../../components/UI/DialogBox/DialogBox';
import { Loading } from '../../components/UI/Layout/Loading/Loading';
import { GET_LANGUAGES } from '../../graphql/queries/List';
import { setNotification, setErrorMessage } from '../../common/notification';
import { ReactComponent as DeleteIcon } from '../../assets/images/icons/Delete/White.svg';
import { DialogBox } from '../../components/UI/DialogBox/DialogBox';
import { setNotification, setErrorMessage } from '../../common/notification';
import { GET_LANGUAGES } from '../../graphql/queries/List';
import styles from './FormLayout.module.css';

export interface FormLayoutProps {
match: any;
Expand Down Expand Up @@ -65,7 +66,6 @@ export const FormLayout: React.SFC<FormLayoutProps> = ({
const [formCancelled, setFormCancelled] = useState(false);
const [action, setAction] = useState(false);
const [link, setLink] = useState(undefined);
const [groupsID, setGroupsID] = useState();

const languages = useQuery(GET_LANGUAGES, {
onCompleted: (data) => {
Expand All @@ -83,9 +83,6 @@ export const FormLayout: React.SFC<FormLayoutProps> = ({
setLink(data[listItem][listItem][linkParameter]);
setStates(item);
setLanguageId(languageSupport ? item.language.id : null);
if (data.user && data.user.user) {
setGroupsID(data.user.user.groups === undefined ? null : data.user.user.groups);
}
}
},
});
Expand Down Expand Up @@ -131,14 +128,19 @@ export const FormLayout: React.SFC<FormLayoutProps> = ({
payload = setPayload(payload);
}

// remove fields from the payload that marked as skipPayload = true
formFields.map((field: any) => {
if (field.skipPayload) {
delete payload[field.name];
}
});

let message;

if (itemId) {
console.log(payload);
updateItem({
variables: {
id: itemId,
groupIds: groupsID,
input: payload,
},
});
Expand Down
34 changes: 33 additions & 1 deletion src/containers/StaffManagement/StaffManagement.test.helper.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { GET_USERS_QUERY, FILTER_USERS, USER_COUNT } from '../../graphql/queries/StaffManagement';
import {
GET_USERS_QUERY,
FILTER_USERS,
USER_COUNT,
GET_USER_ROLES,
} from '../../graphql/queries/User';
import { GET_LANGUAGES } from '../../graphql/queries/List';
import { GET_GROUPS } from '../../graphql/queries/Group';

Expand Down Expand Up @@ -92,4 +97,31 @@ export const STAFF_MANAGEMENT_MOCKS = [
},
},
},
{
request: {
query: GET_USER_ROLES,
},
result: {
data: {
roles: [
{
id: 1,
label: 'none',
},
{
id: 2,
label: 'staff',
},
{
id: 3,
label: 'manager',
},
{
id: 4,
label: 'admin',
},
],
},
},
},
];
97 changes: 71 additions & 26 deletions src/containers/StaffManagement/StaffManagement.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import React, { useState } from 'react';
import * as Yup from 'yup';
import { useQuery } from '@apollo/client';

import { Input } from '../../components/UI/Form/Input/Input';
import { GET_USERS_QUERY } from '../../graphql/queries/StaffManagement';
import { GET_GROUPS } from '../../graphql/queries/Group';
import { UPDATE_USER, DELETE_USER } from '../../graphql/mutations/StaffManagement';
import { CREATE_TEMPLATE } from '../../graphql/mutations/Template';
import { ReactComponent as StaffManagementIcon } from '../../assets/images/icons/StaffManagement/Active.svg';
import { useQuery, useMutation } from '@apollo/client';
import { FormLayout } from '../Form/FormLayout';
import { AutoComplete } from '../../components/UI/Form/AutoComplete/AutoComplete';
import { Dropdown } from '../../components/UI/Form/Dropdown/Dropdown';
import { GET_USERS_QUERY, GET_USER_ROLES } from '../../graphql/queries/User';
import { UPDATE_USER, DELETE_USER } from '../../graphql/mutations/User';
import { ReactComponent as StaffManagementIcon } from '../../assets/images/icons/StaffManagement/Active.svg';
import { GET_GROUPS } from '../../graphql/queries/Group';
import { Loading } from '../../components/UI/Layout/Loading/Loading';

export interface StaffManagementProps {
match: any;
Expand All @@ -21,61 +21,80 @@ const staffManagementIcon = <StaffManagementIcon />;

const queries = {
getItemQuery: GET_USERS_QUERY,
createItemQuery: CREATE_TEMPLATE,
createItemQuery: UPDATE_USER,
updateItemQuery: UPDATE_USER,
deleteItemQuery: DELETE_USER,
};

export const StaffManagement: React.SFC<StaffManagementProps> = ({ match }) => {
const [name, setName] = useState('');
const [phone, setPhone] = useState('');
const [roles, setRoles] = useState('');
const [roles, setRoles] = useState([]);
const [groups, setGroups] = useState([]);

const states = { name, phone, roles };
const states = { name, phone, roles, groups };
const setStates = ({ name, phone, roles, groups }: any) => {
setName(name);
setPhone(phone);
setRoles(roles);

// let' format the roles so that it is displayed correctly in the UI
if (roles) {
let defaultRoles: any = [];
roles.map((role: any) => {
defaultRoles.push({ id: role, label: role });
});
setRoles(defaultRoles);
}
setGroups(groups);
};

useQuery(GET_GROUPS, {
onCompleted: (data) => {
setGroups(data.groups);
},
});
const { loading: loadingRoles, data: roleData } = useQuery(GET_USER_ROLES);

const { loading, data } = useQuery(GET_GROUPS);

if (loading || loadingRoles) return <Loading />;

if (!data.groups || !roleData.roles) {
return null;
}

let rolesList: any = [];
if (roleData.roles) {
roleData.roles.map((role: any) => {
rolesList.push({ id: role, label: role });
});
}

const formFields = [
{
component: Input,
name: 'name',
type: 'text',
placeholder: 'Full Name',
query: true,
select: false,
},
{
component: Input,
name: 'phone',
placeholder: 'Phone Number',
query: false,
select: false,
disabled: true,
skipPayload: true,
},
{
component: Dropdown,
component: AutoComplete,
name: 'roles',
placeholder: 'Roles',
options: [
{ value: 'admin', name: 'Admin' },
{ value: 'basic', name: 'Basic' },
],
options: rolesList,
optionLabel: 'label',
textFieldProps: {
label: 'Roles',
variant: 'outlined',
},
},
{
component: AutoComplete,
name: 'groups',
placeholder: 'Groups',
options: groups,
options: data.groups,
optionLabel: 'label',
textFieldProps: {
label: 'Groups',
Expand All @@ -89,12 +108,38 @@ export const StaffManagement: React.SFC<StaffManagementProps> = ({ match }) => {
phone: Yup.string().required('Phone is required'),
});

const setPayload = (payload: any) => {
// let's build the groupIds, as backend expects the array of group ids
let groupIds = payload.groups.map((group: any) => {
return group.id;
});

// remove groups from the payload
delete payload['groups'];

// let's rebuild roles, as per backend
let roleIds = payload.roles.map((role: any) => {
return role.id;
});

// delete current roles from the payload
delete payload['roles'];

// return modified payload
return {
...payload,
groupIds: groupIds,
roles: roleIds,
};
};

return (
<FormLayout
{...queries}
match={match}
states={states}
setStates={setStates}
setPayload={setPayload}
validationSchema={FormSchema}
listItemName="User"
dialogMessage={dialogMessage}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { USER_COUNT, FILTER_USERS } from '../../../graphql/queries/StaffManagement';
import { DELETE_USER } from '../../../graphql/mutations/StaffManagement';
import { USER_COUNT, FILTER_USERS } from '../../../graphql/queries/User';
import { DELETE_USER } from '../../../graphql/mutations/User';
import styles from './StaffManagementList.module.css';
import { ReactComponent as StaffIcon } from '../../../assets/images/icons/StaffManagement/Active.svg';
import { List } from '../../List/List';
Expand Down Expand Up @@ -32,8 +32,11 @@ export const StaffManagementList: React.SFC<StaffManagementProps> = () => {
return <p className={styles.TableText}>{text}</p>;
};

const getGroups = (text: string) => {
return <p className={styles.TableText}>{text}</p>;
const getGroups = (groupList: any) => {
const groups = groupList.map((group: any) => {
return group.label;
});
return <p className={styles.TableText}>{groups.join(', ')}</p>;
};

const dialogMessage = ' Once deleted this action cannot be undone.';
Expand Down
Loading