Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9b5b416
Link userList to userPage through an onClick function in each box of …
peacheym Apr 3, 2020
3c4256b
Move queries and mutations to frontend/src/graphql.
peacheym Apr 3, 2020
1c43766
Remove redundant <BOX> and update test to include barebone mocked use…
peacheym Apr 3, 2020
d10b99a
Add Mocked Data test and expectations. Optimize most imports.
peacheym Apr 6, 2020
bdfb713
Add barebones UserPage test.
peacheym Apr 6, 2020
8830909
Update docs to maintain eslint
peacheym Apr 6, 2020
220da1b
Add a redirect test for UserList.
peacheym Apr 6, 2020
bee8cb2
Remove unnecessary console log that was included in the '.then()' fun…
peacheym Apr 6, 2020
28078ed
Add badge rendering tests.
peacheym Apr 6, 2020
185f794
Disable functions on userPage when accessed from userList.
peacheym Apr 7, 2020
392fc52
Auto-release multiple images
Apr 3, 2020
8e77464
Auto-release gcr.io/track-compliance/api:master-4301b10
Apr 6, 2020
3e525fc
Add custom type for userList
peacheym Apr 7, 2020
c95f27f
Add custom faker types for userPage and userList.
peacheym Apr 8, 2020
1efeeb7
Update schema.faker.graphql to use a UserItemEdges.
peacheym Apr 8, 2020
fe81ba4
Add faker values for custom types
peacheym Apr 8, 2020
ef99a72
Use custom object query.
peacheym Apr 8, 2020
96a17d9
Add prop type string in userPage
peacheym Apr 9, 2020
15ca9eb
Make additional subtype for admin and org.
peacheym Apr 9, 2020
af4a3f8
Refactor userCard into its own component.
peacheym Apr 14, 2020
59e9947
Merge branch 'master' into create-custom-graphql-types
peacheym Apr 14, 2020
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
77 changes: 77 additions & 0 deletions frontend/schema.faker.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,16 @@ type Query {
An api endpoint that will send a verification email to a given email address.
"""
sendValidationEmail(email: EmailAddress!): NotificationEmail

"""
An api endpoint that will be used to populate a userList component in the front end.
"""
userList(organizaion: Acronym!): UserList

"""
An api endpoint that will be used to populate a userPage component in the front end.
"""
userPage(userName: EmailAddress!): UserPage

queryDmarcReport(reportId: String!): QueryDmarcReport
}
Expand Down Expand Up @@ -1093,6 +1103,73 @@ type WWWScanEdge {
cursor: String!
}

"""
This custom object is used to populate the userList componenet in the front end.
"""
type UserList {
""" Indicates which organization this list is being queried for."""
organization: Acronym!

"""Pagination data for this connection."""
pageInfo: PageInfo!

"""Contains the nodes in this connection. Aka: A list of userItems that will make up the list on the front end."""
edges: [UserItemEdge]!
}

type UserListItem implements Node {
"""The ID of the object."""
id: ID!

""" The users email address or userName """
userName: EmailAddress! @fake(type: email)

""" The users display name"""
displayName: String! @fake(type: firstName)

""" Indicates wether or not this user has enabled two factor authentication"""
tfa: Boolean! @examples(values: [true, false])

""" Indicates if this user is an admin of the organization specified in UserList query."""
admin: Boolean! @examples(values: [true, false])
}

"""A Relay edge containing a `UserItem` and its cursor."""
type UserItemEdge {
"""The item at the end of the edge"""
node: UserListItem!

"""A cursor for use in pagination"""
cursor: String!
}

"""
This custom gql object is used to populate a userPage component in the front end.
"""
type UserPage{
""" The users email address or userName"""
userName: EmailAddress! @fake(type: email)

""" The users display name"""
displayName: String! @fake(type: firstName)

""" Indicates the preferred language of this user."""
lang: String! @examples(values: ["English, "French])

""" Indicates wether or not this user has enabled two factor authentication"""
tfa: Boolean! @examples(values: [true, false])

""" Indicates if this user is an admin of the organization specified."""
Comment thread
nsdeschenes marked this conversation as resolved.
userAffiliations: [UserPageAffiliations]!
}

type UserPageAffiliations{
""" Indicates if this user is an admin of the organization"""
admin: Boolean! @examples(values: [true, false])

""" Indicates which organization this users data is being displayed for."""
organization: Acronym! @examples(values: ["GC", "ABC", "ASDF", "NSTIR", "BC"])

"""A custom type used to query a DmarcReport for the front end."""

type QueryDmarcReport {
Expand Down
58 changes: 58 additions & 0 deletions frontend/src/UserCard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React from 'react'

import { Badge, Box, Text, PseudoBox } from '@chakra-ui/core'
import { Trans } from '@lingui/macro'

import { useHistory } from 'react-router-dom'

import { bool, string } from 'prop-types'

export function UserCard(props) {
const history = useHistory()
return (
<PseudoBox
width="100%"
display={{ md: 'flex' }}
alignItems="center"
onClick={() => {
history.push({
pathname: '/user',
state: { detail: props.userName },
})
}}
_hover={{ borderColor: 'gray.200', bg: 'gray.200' }}
p="30px"
>
<Box flexShrink="0" minW="15%">
<Text mt={1} fontSize="lg" fontWeight="semibold">
{props.displayName}
</Text>
</Box>
<Box flexShrink="0" ml={{ md: 4 }} mr={{ md: 4 }} minW="35%">
<Text fontSize="lg" minW="10%">
{props.userName}
</Text>
</Box>
<Box flexShrink="0" ml={{ md: 4 }} mr={{ md: 4 }} minW="15%">
<Badge variantColor={props.tfa ? 'green' : 'red'} minW="15%">
<Trans>TwoFactor</Trans>
</Badge>
<Badge
variantColor={props.admin ? 'green' : 'red'}
ml="10px"
mr={{ md: 4 }}
>
<Trans>Admin</Trans>
</Badge>
</Box>
<Box flexShrink="0" ml={{ md: 4 }} mr={{ md: 4 }} mt={2} minW="15%"></Box>
</PseudoBox>
)
}

UserCard.propTypes = {
displayName: string.isRequired,
userName: string.isRequired,
admin: bool.isRequired,
tfa: bool.isRequired,
}
197 changes: 40 additions & 157 deletions frontend/src/UserList.js
Original file line number Diff line number Diff line change
@@ -1,81 +1,30 @@
import React from 'react'

import {
Badge,
Stack,
SimpleGrid,
Divider,
Box,
Text,
Button,
Icon,
InputGroup,
InputLeftElement,
Input,
PseudoBox,
} from '@chakra-ui/core'

import { Trans } from '@lingui/macro'
import gql from 'graphql-tag'
import { QUERY_USERLIST } from './graphql/queries'
import { useQuery } from '@apollo/react-hooks'
import { PaginationButtons } from './PaginationButtons'
import { UserCard } from './UserCard'

export function UserList() {
// This function generates the URL when the page loads
const { loading, error, data } = useQuery(
gql`
{
user {
affiliations {
edges {
node {
organization {
acronym
affiliatedUsers {
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
edges {
node {
id
user {
userName
displayName
tfa
affiliations {
edges {
node {
id
organization {
acronym
}
permission
}
}
}
}
}
}
}
}
}
}
}
}
}
`,
)
const { loading, error, data } = useQuery(QUERY_USERLIST)
if (loading) {
return <p>Loading...</p>
}
if (error) {
console.log(error)
}
if (data) {
console.log(data)
return <p>Error :(</p>
}

return (
Expand All @@ -99,112 +48,46 @@ export function UserList() {
</Button>
</SimpleGrid>
<Divider />

{data
? data.user.affiliations.edges[0].node.organization.affiliatedUsers.edges.map(
edge => {
return (
<Box key={edge.node.id} width="100%">
<PseudoBox
display={{ md: 'flex' }}
alignItems="center"
onClick={() => {
window.alert('clicked box')
}}
_hover={{ borderColor: 'gray.200', bg: 'gray.200' }}
p="30px"
>
<Box flexShrink="0" minW="15%">
<Text mt={1} fontSize="lg" fontWeight="semibold">
{edge.node.user.displayName}
</Text>
</Box>
<Box
flexShrink="0"
ml={{ md: 4 }}
mr={{ md: 4 }}
minW="35%"
>
<Text fontSize="lg" minW="10%">
{edge.node.user.userName}
</Text>
</Box>
<Box
flexShrink="0"
ml={{ md: 4 }}
mr={{ md: 4 }}
minW="25%"
>
<Box mt={2} color="gray.500">
Orgs:&nbsp;
{// Populate the user-orgs list.
edge.node.user.affiliations.edges.map(
(edge, i, arr) => {
if (arr.length - 1 === i) {
return (
<Text display="inline" key={edge.node.id + i}>
{edge.node.organization.acronym}
</Text>
)
}
return (
<Text display="inline" key={edge.node.id + i}>
{edge.node.organization.acronym + ' | '}
</Text>
)
},
)}
</Box>
</Box>
<Box
flexShrink="0"
ml={{ md: 4 }}
mr={{ md: 4 }}
minW="15%"
>
<Badge
variantColor={edge.node.user.tfa ? 'green' : 'red'}
minW="15%"
>
<Trans>TwoFactor</Trans>
</Badge>
<Badge
variantColor={
edge.node.permission === 'ADMIN' ||
edge.node.permission === 'SUPER_ADMIN'
? 'green'
: 'red'
}
ml="10px"
mr={{ md: 4 }}
>
<Trans>Admin</Trans>
</Badge>
</Box>
<Box
flexShrink="0"
ml={{ md: 4 }}
mr={{ md: 4 }}
mt={2}
minW="15%"
></Box>
</PseudoBox>
</Box>
)
},
)
? data.userList.edges.map((edge) => {
return (
<UserCard
key={edge.node.id}
userName={edge.node.userName}
tfa={edge.node.tfa}
admin={edge.node.admin}
displayName={edge.node.displayName}
/>
)
})
: null}

<PaginationButtons
next={
data.user.affiliations.edges[0].node.organization.affiliatedUsers
.pageInfo.hasNextPage
}
previous={
data.user.affiliations.edges[0].node.organization.affiliatedUsers
.pageInfo.hasPreviousPage
}
next={data.userList.pageInfo.hasNextPage}
previous={data.userList.pageInfo.hasPreviousPage}
/>
</Stack>
)
}

/* -- Source code for adding organizations, not being used. --

<Box flexShrink="0" ml={{ md: 4 }} mr={{ md: 4 }} minW="25%">
<Box mt={2} color="gray.500">
Orgs:&nbsp;
{// Populate the user-orgs list.
edge.node.user.affiliations.edges.map((edge, i, arr) => {
if (arr.length - 1 === i) {
return (
<Text display="inline" key={edge.node.id + i}>
{edge.node.organization.acronym}
</Text>
)
}
return (
<Text display="inline" key={edge.node.id + i}>
{edge.node.organization.acronym + ' | '}
</Text>
)
})}
</Box>
</Box>*/
Loading