Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
35 changes: 15 additions & 20 deletions frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ import { TopBanner } from './TopBanner'
import { PhaseBanner } from './PhaseBanner'
import { Footer } from './Footer'
import { Navigation } from './Navigation'
import { Flex, Link, CSSReset, useToast } from '@chakra-ui/core'
import { Flex, Link, CSSReset } from '@chakra-ui/core'
import { SkipLink } from './SkipLink'
import { useQuery, useApolloClient } from '@apollo/react-hooks'
import { useQuery } from '@apollo/react-hooks'
import gql from 'graphql-tag'
import { TwoFactorNotificationBar } from './TwoFactorNotificationBar'
import { UserPage } from './UserPage'
import { UserList } from './UserList'
import { DmarcReportPage } from './DmarcReportPage'

Expand All @@ -29,12 +30,15 @@ export default function App() {
{
jwt @client
tfa @client
userName @client
}
`
const { i18n } = useLingui()
const toast = useToast()
const client = useApolloClient()
const { data } = useQuery(GET_JWT_TOKEN)
const { data, loading } = useQuery(GET_JWT_TOKEN)

if (loading) {
return <p>Loading...</p>
}

return (
<>
Expand Down Expand Up @@ -68,21 +72,8 @@ export default function App() {
<Trans>Sign In</Trans>
</Link>
) : (
<Link
to="/"
onClick={() => {
// This clears the JWT, essentially logging the user out in one go
client.writeData({ data: { jwt: null } }) // How is this done?
toast({
title: 'Sign Out.',
description: 'You have successfully been signed out.',
status: 'success',
duration: 9000,
isClosable: true,
})
}}
>
<Trans>Sign Out</Trans>
<Link to="/user">
<Trans>User Profile</Trans>
</Link>
)}
<Link to="/user-list">
Expand All @@ -103,6 +94,10 @@ export default function App() {
<DomainsPage />
</Route>

<Route path="/user">
<UserPage userName={data && data.userName ? data.userName : null} />
</Route>

<Route path="/sign-in">
<SignInPage />
</Route>
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/DomainsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ export function DomainsPage() {
<Trans>This is the full list of domains</Trans>
</Text>
<List>
{data.domains.map((domain, i) => {
{data.domains.edges.map((edge, i) => {
return (
<ListItem key={domain.url + i}>
<Text>{domain.url}</Text>
<ListItem key={edge.node.url + i}>
<Text>{edge.node.url}</Text>
</ListItem>
)
})}
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/SignInPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ export function SignInPage() {
cache.writeData({
data: {
jwt: signIn.authToken,
tfa: signIn.user.tfaValidated,
tfa: signIn.user.tfa,
userName: signIn.user.userName,
},
})
},
Expand Down Expand Up @@ -92,7 +93,7 @@ export function SignInPage() {
{(props) => (
// Needed for testing library
// eslint-disable-next-line jsx-a11y/no-redundant-roles
<form onSubmit={props.handleSubmit} role="form">
<form onSubmit={props.handleSubmit} role="form" aria-label="form" name="form">
<Field name="email">
{({ field, form }) => (
<FormControl
Expand Down
255 changes: 255 additions & 0 deletions frontend/src/UserPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
/* eslint react/prop-types: 0 */
import React from 'react'

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

import { string } from 'prop-types'

import {
Stack,
SimpleGrid,
Button,
Text,
Select,
Input,
Divider,
Checkbox,
CheckboxGroup,
useToast,
} from '@chakra-ui/core'
import { useApolloClient, useMutation, useQuery } from '@apollo/react-hooks'
import { PasswordConfirmation } from './PasswordConfirmation'
import gql from 'graphql-tag'

export function UserPage({ userName }) {
// TODO: Move to mutations folder
const UPDATE_PASSWORD = gql`
mutation UpdatePassword(
$userName: EmailAddress!
$password: String!
$confirmPassword: String!
) {
updatePassword(
userName: $userName
password: $password
confirmPassword: $confirmPassword
) {
user {
userName
}
}
}
`

const QUERY_USER = gql`
query User($userName: EmailAddress!) {
user(userName: $userName) {
userName
displayName
lang
}
}
`

const client = useApolloClient()
const toast = useToast()
const history = useHistory()

const [
updatePassword,
{
loading: updatePasswordLoading,
error: updatePasswordError,
data: updatePasswordData,
},
] = useMutation(UPDATE_PASSWORD)

const {
loading: queryUserLoading,
error: queryUserError,
data: queryUserData,
} = useQuery(QUERY_USER, {
variables: { userName: userName },
})

if (updatePasswordLoading || queryUserLoading) {
return <p>Loading...</p>
}

if (queryUserError || updatePasswordError) {
return <p>Error</p>
}

return (
<SimpleGrid columns={{ md: 1, lg: 2 }} spacing="60px" width="100%">
<Formik
initialValues={{
email: userName, // Taken from prop or query, does not matter.
lang: queryUserData.user.lang,
displayName: queryUserData.user.displayName,
}}
onSubmit={(values, actions) => {
window.alert('coming soon!!\n' + JSON.stringify(values, null, 2))
actions.setSubmitting(false)
}}
>
{(props) => (
<form onSubmit={props.handleSubmit}>
<Stack p={25} spacing={4}>
<Text fontSize="2xl" fontWeight="bold" textAlign="center">
User Profile
</Text>

<Stack mt="20px">
<Text fontSize="xl">Display Name:</Text>
<Input
id="displayName"
name="displayName"
type="text"
onChange={props.handleChange}
value={props.values.displayName}
/>
</Stack>

<Stack>
<Text fontSize="xl">Email:</Text>
<Input
id="email"
name="email"
type="email"
onChange={props.handleChange}
value={props.values.email}
/>
</Stack>

<Stack>
<Text fontSize="xl">Language:</Text>
<Select
id="lang"
name="lang"
type="text"
placeholder="Select option"
onChange={props.handleChange}
value={props.values.lang}
>
<option value="en">English</option>
<option value="fr">French</option>
</Select>
</Stack>
<Button type="submit" variantColor="teal" w={'50%'} mt={5}>
Save Changes
</Button>
</Stack>
</form>
)}
</Formik>

<Stack Stack p={25} spacing={4}>
<Text fontSize="2xl" fontWeight="bold" textAlign="center">
Account Details
</Text>
<CheckboxGroup
mt="20px"
variantColor="teal"
defaultValue={['admin', 'active']}
>
<Checkbox value="admin">Administrative Account</Checkbox>
<Checkbox value="active">Account Active</Checkbox>
</CheckboxGroup>
<Divider />
<Stack isInline>
<Button
leftIcon="lock"
variantColor="blue"
onClick={() => {
history.push('/two-factor-code')
}}
>
Enable 2FA
</Button>
<Button
leftIcon="edit"
variantColor="teal"
onClick={() => {
window.alert('coming soon')
}}
>
Manage API keys
</Button>
</Stack>
<Button
variantColor="teal"
w={'50%'}
onClick={() => {
// This clears the JWT, essentially logging the user out in one go
client.writeData({ data: { jwt: null, tfa: null, userName: null } }) // How is this done?
history.push('/')
toast({
title: 'Sign Out.',
description: 'You have successfully been signed out.',
status: 'success',
duration: 9000,
isClosable: true,
})
}}
>
Sign Out
</Button>
</Stack>
<Stack p={25} spacing={4}>
<Text fontSize="2xl" fontWeight="bold" textAlign="center">
Change Password
</Text>
<Text>
Change your password below by entering and confirming a new password.
</Text>

<Formik
initialValues={{ password: '', confirmPassword: '' }}
onSubmit={async (values) => {
// Submit GraphQL mutation
console.log(values)
await updatePassword({
variables: {
userName: 'testuser@test.ca', // This needs to be retreived from a seperate GQL query or props that will populate this entire page with data.
password: values.password,
confirmPassword: values.confirmPassword,
},
})

if (!updatePasswordError) {
console.log(updatePasswordData)
toast({
title: 'Password Updated.',
description: 'You have successfully changed your password.',
status: 'success',
duration: 9000,
isClosable: true,
})
}
}}
>
{({ handleSubmit, isSubmitting }) => (
<form id="form" onSubmit={handleSubmit}>
<PasswordConfirmation />

<Stack mt={6} spacing={4} isInline>
<Button
variantColor="teal"
isLoading={isSubmitting}
type="submit"
id="submitBtn"
>
Change Password
</Button>
</Stack>
</form>
)}
</Formik>
</Stack>
</SimpleGrid>
)
}

UserPage.propTypes = { userName: string.isRequired }
28 changes: 19 additions & 9 deletions frontend/src/__tests__/App.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,32 @@ const mocks = [
query: gql`
{
domains(organization: BOC) {
url
edges {
node {
url
}
}
}
}
`,
variables: {},
},
result: {
data: {
domains: [
{
url: 'canada.ca',
},
{
url: 'alpha.canada.ca',
},
],
domains: {
edges: [
{
node: {
url: 'canada.ca',
},
},
{
node: {
url: 'alpha.canada.ca',
},
},
],
},
},
},
},
Expand Down
Loading