diff --git a/packages/smb/src/components/pages/users/__tests__/users.test.tsx b/packages/smb/src/components/pages/users/__tests__/users.test.tsx new file mode 100644 index 0000000000..22af5fa725 --- /dev/null +++ b/packages/smb/src/components/pages/users/__tests__/users.test.tsx @@ -0,0 +1,21 @@ +import * as React from 'react' +import { mount } from 'enzyme' +import { MockedProvider } from '@apollo/react-testing' +import { BrowserRouter as Router } from 'react-router-dom' +import { Users, UsersProps } from '../users' + +describe('Users', () => { + describe('Users', () => { + it('should match a snapshot', () => { + const mockProps: UsersProps = {} + const wrapper = mount( + + + + + , + ) + expect(wrapper).toMatchSnapshot() + }) + }) +}) diff --git a/packages/smb/src/components/pages/users/index.ts b/packages/smb/src/components/pages/users/index.ts new file mode 100644 index 0000000000..7d740a8781 --- /dev/null +++ b/packages/smb/src/components/pages/users/index.ts @@ -0,0 +1,2 @@ +import Users from './users' +export default Users diff --git a/packages/smb/src/components/pages/users/users.tsx b/packages/smb/src/components/pages/users/users.tsx new file mode 100644 index 0000000000..1027d1394c --- /dev/null +++ b/packages/smb/src/components/pages/users/users.tsx @@ -0,0 +1,18 @@ +import * as React from 'react' +import { H3, FlexContainerBasic, FlexContainerResponsive } from '@reapit/elements' +import UserList from '@/components/ui/user-list' + +export type UsersProps = {} + +export const Users: React.FC = () => { + return ( + + +

Users

+ +
+
+ ) +} + +export default Users diff --git a/packages/smb/src/components/ui/user-list/__mocks__/users.ts b/packages/smb/src/components/ui/user-list/__mocks__/users.ts new file mode 100644 index 0000000000..05131360b2 --- /dev/null +++ b/packages/smb/src/components/ui/user-list/__mocks__/users.ts @@ -0,0 +1,81 @@ +import { UsersQueryResponse } from '../user-list' + +export const users: UsersQueryResponse = { + GetNegotiators: { + _embedded: [ + { + id: 'MGL', + created: '2014-12-26T12:22:01.0000000Z', + modified: '2016-11-29T09:57:32.0000000Z', + name: 'Abel Robertson', + active: true, + officeId: 'NPG', + email: 'abel.robertson@reapitestates.net', + metadata: {}, + _eTag: '"10109C0209C684789B72FFC53730E31C"', + _links: { + self: { + href: '/negotiators/MGL', + }, + office: { + href: '/offices/NPG', + }, + }, + }, + { + id: 'RPA', + created: '2017-02-07T12:08:53.0000000Z', + modified: '2017-02-07T12:09:41.0000000Z', + name: 'Accounts User', + active: true, + officeId: 'MCL', + _eTag: '"85A9DB571DB893A5FB734105AFF6B464"', + _links: { + self: { + href: '/negotiators/RPA', + }, + office: { + href: '/offices/MCL', + }, + }, + }, + { + id: 'KLB', + created: '2010-11-15T14:53:08.0000000Z', + modified: '2017-03-10T00:02:08.0000000Z', + name: 'Adele Small', + active: true, + officeId: 'NGL', + email: 'adele.small@reapitestates.net', + metadata: {}, + _eTag: '"B28EE5CFF654DB22BF3247CA028C10C3"', + _links: { + self: { + href: '/negotiators/KLB', + }, + office: { + href: '/offices/NGL', + }, + }, + }, + ], + pageNumber: 1, + pageSize: 3, + pageCount: 3, + totalCount: 3, + _links: { + self: { + href: '/negotiators/?PageNumber=1&PageSize=3', + }, + first: { + href: '/negotiators/?PageNumber=1&PageSize=3', + }, + next: { + href: '/negotiators/?PageNumber=2&PageSize=3', + }, + last: { + href: '/negotiators/?PageNumber=3&PageSize=3', + }, + }, + }, +} diff --git a/packages/smb/src/components/ui/user-list/__tests__/user-list.test.tsx b/packages/smb/src/components/ui/user-list/__tests__/user-list.test.tsx new file mode 100644 index 0000000000..7770a18cae --- /dev/null +++ b/packages/smb/src/components/ui/user-list/__tests__/user-list.test.tsx @@ -0,0 +1,96 @@ +import * as React from 'react' +import { mount, shallow } from 'enzyme' +import { MockedProvider } from '@apollo/react-testing' +import { BrowserRouter as Router } from 'react-router-dom' +import { + UserList, + UserListProps, + renderUserList, + RenderUserListParams, + getDataTable, + tableHeaders, + handleChangePage, +} from '../user-list' +import { GetUsers } from '../user-list.graphql' +import { users } from '../__mocks__/users' +import { error } from '@/graphql/__mocks__/error' +import { getMockRouterProps } from '@/core/__mocks__/mock-router' + +const mockQueries = { + request: { + query: GetUsers, + variables: { pageSize: 100, pageNumber: 1 }, + }, + result: { + data: users, + }, +} + +describe('UserList', () => { + describe('UserList', () => { + it('should match a snapshot', () => { + const mockProps: UserListProps = getMockRouterProps({ params: {}, search: '?page=1' }) + const wrapper = mount( + + + + + , + ) + expect(wrapper).toMatchSnapshot() + }) + }) + + describe('renderUserList', () => { + it('should match snapshot', () => { + const mockParams: RenderUserListParams = { + loading: true, + error: undefined, + handleChangePage: jest.fn(), + dataTable: [], + } + const wrapper = shallow(
{renderUserList(mockParams)}
) + expect(wrapper).toMatchSnapshot() + }) + + it('should match snapshot', () => { + const mockParams: RenderUserListParams = { + loading: false, + error, + handleChangePage: jest.fn(), + dataTable: [], + } + const wrapper = shallow(
{renderUserList(mockParams)}
) + expect(wrapper).toMatchSnapshot() + }) + + it('should match snapshot', () => { + const mockParams: RenderUserListParams = { + loading: false, + error: undefined, + handleChangePage: jest.fn(), + dataTable: getDataTable(users), + } + const wrapper = shallow(
{renderUserList(mockParams)}
) + expect(wrapper).toMatchSnapshot() + }) + + describe('getDataTable', () => { + it('should run correctly', () => { + const dataTable = getDataTable(users) + expect(Array.isArray(dataTable)).toBe(true) + expect(dataTable.length).toBe(3) + expect(dataTable[0]).toEqual(tableHeaders) + }) + }) + + describe('handleChangePage', () => { + it('should run correctly', () => { + const mockParams = { history: { push: jest.fn() } } + const fn = handleChangePage(mockParams) + fn(2) + expect(mockParams.history.push).toBeCalledWith({ search: 'page=2' }) + }) + }) + }) +}) diff --git a/packages/smb/src/components/ui/user-list/index.ts b/packages/smb/src/components/ui/user-list/index.ts new file mode 100644 index 0000000000..0ff3ee6ef5 --- /dev/null +++ b/packages/smb/src/components/ui/user-list/index.ts @@ -0,0 +1,2 @@ +import UserList from './user-list' +export default UserList diff --git a/packages/smb/src/components/ui/user-list/user-list.graphql b/packages/smb/src/components/ui/user-list/user-list.graphql new file mode 100644 index 0000000000..349c5b1f3f --- /dev/null +++ b/packages/smb/src/components/ui/user-list/user-list.graphql @@ -0,0 +1,21 @@ +#import "../../../graphql/fragments/negotiator.graphql" + +query GetUsers( + $id: [String!], + $name: String, + $officeId: [String!], + $pageSize: Int, + $pageNumber: Int, + $sortBy: String + ) { + GetNegotiators(id: $id, name: $name, officeId: $officeId, pageSize: $pageSize, pageNumber: $pageNumber, sortBy: $sortBy) { + _embedded { + ...Negotiator + } + pageNumber + pageSize + pageCount + totalCount + _links + } +} diff --git a/packages/smb/src/components/ui/user-list/user-list.tsx b/packages/smb/src/components/ui/user-list/user-list.tsx new file mode 100644 index 0000000000..8503d31315 --- /dev/null +++ b/packages/smb/src/components/ui/user-list/user-list.tsx @@ -0,0 +1,156 @@ +import * as React from 'react' +import { useLocation, useHistory } from 'react-router-dom' +import { useQuery } from '@apollo/react-hooks' +import { QueryResult } from '@apollo/react-common' +import { ApolloError } from 'apollo-boost' +import { Loader, Cell, Alert, Section, Pagination, Spreadsheet, Checkbox } from '@reapit/elements' +import { getParamsFromPath, stringifyObjectIntoQueryString } from '@/utils/client-url-params' +import { GetUsers } from './user-list.graphql' +import { + PagedResultNegotiatorModel_, + NegotiatorModel, +} from '@reapit/elements/node_modules/@reapit/foundations-ts-definitions/types' +import { USERS_PER_PAGE } from '@/constants/paginators' + +export const tableHeaders: DataTableRow[] = [ + { readOnly: true, value: 'Username' }, + { readOnly: true, value: 'Job Title' }, + { readOnly: true, value: 'Email Address' }, + { readOnly: true, value: 'Telephone' }, + { readOnly: true, value: 'Office' }, + { readOnly: true, value: 'Status' }, +] + +export type DataTableRow = { + value?: string | boolean + readOnly?: boolean +} + +export type UserListProps = {} + +export interface UsersQueryParams { + pageSize: number + pageNumber: number + sortBy?: string + id?: string[] + officeId?: string[] + name?: string +} + +export type UsersQueryResponse = { + GetNegotiators?: PagedResultNegotiatorModel_ +} + +export type RenderUserListParams = { + loading: boolean + error?: ApolloError + pageNumber?: number + pageSize?: number + totalCount?: number + dataTable: DataTableRow[][] + handleChangePage: (page: number) => void +} + +const CustomComponent = ({ cellRenderProps }) => { + const { + row, + col, + cell: { value }, + } = cellRenderProps + const checkBoxId = `${row}-${col}` + return ( +
+ + +
+ ) +} + +export function getDataTable(data: UsersQueryResponse): DataTableRow[][] { + let dataTable: DataTableRow[][] = [tableHeaders] + const users: NegotiatorModel[] = data.GetNegotiators?._embedded || [] + const dataRows: DataTableRow[][] = users.map((user: NegotiatorModel) => [ + { value: user.name }, + { value: user.jobTitle }, + { value: user.email }, + { value: user.mobilePhone }, + { value: user.officeId }, + { value: user.active, CustomComponent }, + ]) + dataTable = [tableHeaders, ...dataRows] + return dataTable +} + +export const handleChangePage = ({ history }) => (pageNumber: number) => { + const searchParams = stringifyObjectIntoQueryString({ + page: pageNumber, + }) + history.push({ + search: searchParams, + }) +} + +export const renderUserList = ({ + loading, + error, + dataTable, + pageNumber = 0, + pageSize = 0, + totalCount = 0, + handleChangePage, +}: RenderUserListParams) => { + if (loading) { + return + } + if (error) { + return + } + return ( + + +

+ Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the + industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type + and scrambled it to make a type specimen book. +

+

+ } + /> +
+ +
+
+ ) +} + +export const UserList: React.FC = () => { + const location = useLocation() + const history = useHistory() + const params = getParamsFromPath(location?.search) + const page = Number(params?.page) || 1 + const { loading, error, data } = useQuery(GetUsers, { + variables: { pageSize: USERS_PER_PAGE, pageNumber: page }, + fetchPolicy: 'network-only', + }) as QueryResult + const dataTable = getDataTable(data || { GetNegotiators: { _embedded: [] } }) + return ( +
+ {renderUserList({ + loading, + error, + dataTable, + pageNumber: data?.GetNegotiators?.pageNumber, + pageSize: data?.GetNegotiators?.pageSize, + totalCount: data?.GetNegotiators?.totalCount, + handleChangePage: handleChangePage({ history }), + })} +
+ ) +} + +export default UserList diff --git a/packages/smb/src/constants/paginators.ts b/packages/smb/src/constants/paginators.ts index 19cf7ba2a9..d2c7b85adb 100644 --- a/packages/smb/src/constants/paginators.ts +++ b/packages/smb/src/constants/paginators.ts @@ -1 +1,2 @@ export const OFFICES_PER_PAGE = 12 +export const USERS_PER_PAGE = 12 diff --git a/packages/smb/src/core/router.tsx b/packages/smb/src/core/router.tsx index c28951c893..40b425162a 100644 --- a/packages/smb/src/core/router.tsx +++ b/packages/smb/src/core/router.tsx @@ -10,6 +10,7 @@ export const history = createBrowserHistory() const LoginPage = React.lazy(() => import('../components/pages/login')) const Home = React.lazy(() => import('../components/pages/home')) const OfficesPage = React.lazy(() => import('../components/pages/offices')) +const UsersPage = React.lazy(() => import('../components/pages/users')) const GetStartedPage = React.lazy(() => import('../components/pages/get-started')) const Router = () => { @@ -22,6 +23,7 @@ const Router = () => { + diff --git a/packages/smb/src/graphql/fragments/negotiator.graphql b/packages/smb/src/graphql/fragments/negotiator.graphql new file mode 100644 index 0000000000..686fd84de4 --- /dev/null +++ b/packages/smb/src/graphql/fragments/negotiator.graphql @@ -0,0 +1,16 @@ +fragment Negotiator on NegotiatorModel { + id + created + modified + name + jobTitle + active + officeId + workPhone + mobilePhone + email + metadata + _eTag + _links + _embedded +}