Teams |
diff --git a/web/src/components/Admin/User/UserFormTeams/UserFormTeams.test.tsx b/web/src/components/Admin/User/UserFormTeams/UserFormTeams.test.tsx
index 182baad..30b7d0d 100644
--- a/web/src/components/Admin/User/UserFormTeams/UserFormTeams.test.tsx
+++ b/web/src/components/Admin/User/UserFormTeams/UserFormTeams.test.tsx
@@ -3,12 +3,12 @@ import userEvent from '@testing-library/user-event'
import { Form } from '@redwoodjs/forms'
import { render, screen, within } from '@redwoodjs/testing/web'
-import { standard } from '../UserFormTeamsCell/UserFormTeamsCell.mocks'
+import { standard } from '../UserFormTeamsCell/UserFormTeamsCell.mock'
import { UserFormTeams } from './UserFormTeams'
-const [firstTeam] = standard().userFormTeams.teams
-const [firstRole, secondRole] = standard().userFormTeams.roles
+const [firstTeam] = standard().teams
+const [firstRole, secondRole] = standard().roles
describe('UserFormTeams', () => {
describe('when a value is not selected', () => {
@@ -17,10 +17,10 @@ describe('UserFormTeams', () => {
)
@@ -31,7 +31,7 @@ describe('UserFormTeams', () => {
it('renders team name successfully', () => {
renderComponent()
- const [firstTeam, secondTeam] = standard().userFormTeams.teams
+ const [firstTeam, secondTeam] = standard().teams
const firstElement = firstTeam.name
const secondElement = secondTeam.name
userEvent.selectOptions(
@@ -63,9 +63,9 @@ describe('UserFormTeams', () => {
diff --git a/web/src/components/Admin/User/UserFormTeamsCell/UserFormTeamsCell.mock.ts b/web/src/components/Admin/User/UserFormTeamsCell/UserFormTeamsCell.mock.ts
new file mode 100644
index 0000000..e664770
--- /dev/null
+++ b/web/src/components/Admin/User/UserFormTeamsCell/UserFormTeamsCell.mock.ts
@@ -0,0 +1,22 @@
+export const standard = () => ({
+ teams: [
+ {
+ id: '1',
+ name: 'team1',
+ },
+ {
+ id: '2',
+ name: 'team2',
+ },
+ ],
+ roles: [
+ {
+ id: '3',
+ name: 'foo_role',
+ },
+ {
+ id: '4',
+ name: 'admin',
+ },
+ ],
+})
diff --git a/web/src/components/Admin/User/UserFormTeamsCell/UserFormTeamsCell.mocks.ts b/web/src/components/Admin/User/UserFormTeamsCell/UserFormTeamsCell.mocks.ts
deleted file mode 100644
index d55e04e..0000000
--- a/web/src/components/Admin/User/UserFormTeamsCell/UserFormTeamsCell.mocks.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-export const standard = () => ({
- userFormTeams: {
- teams: [
- {
- id: '1',
- name: 'team1',
- },
- {
- id: '2',
- name: 'team2',
- },
- ],
- roles: [
- {
- id: '3',
- name: 'foo_role',
- },
- {
- id: '4',
- name: 'admin',
- },
- ],
- },
-})
diff --git a/web/src/components/Admin/User/UserFormTeamsCell/UserFormTeamsCell.test.tsx b/web/src/components/Admin/User/UserFormTeamsCell/UserFormTeamsCell.test.tsx
index 4f7ac56..a48f92a 100644
--- a/web/src/components/Admin/User/UserFormTeamsCell/UserFormTeamsCell.test.tsx
+++ b/web/src/components/Admin/User/UserFormTeamsCell/UserFormTeamsCell.test.tsx
@@ -1,7 +1,7 @@
import { render } from '@redwoodjs/testing/web'
import { Loading, Failure, Success } from './UserFormTeamsCell'
-import { standard } from './UserFormTeamsCell.mocks'
+import { standard } from './UserFormTeamsCell.mock'
jest.mock('../UserFormTeams/UserFormTeams', () => ({
UserFormTeams: () => {
@@ -24,7 +24,7 @@ describe('UserFormTeamsCell', () => {
it('renders Success successfully', async () => {
expect(() => {
- render()
+ render()
}).not.toThrow()
})
})
diff --git a/web/src/components/Admin/User/Users/Users.tsx b/web/src/components/Admin/User/Users/Users.tsx
index 124c38f..e3991c9 100644
--- a/web/src/components/Admin/User/Users/Users.tsx
+++ b/web/src/components/Admin/User/Users/Users.tsx
@@ -70,6 +70,7 @@ const Users = ({ users }) => {
| Pronouns |
Active |
Admin |
+ Verified |
Updated at |
Created at |
Actions |
diff --git a/web/src/components/Navigation/Navigation.test.tsx b/web/src/components/Navigation/Navigation.test.tsx
index b04c0b3..c73c7f2 100644
--- a/web/src/components/Navigation/Navigation.test.tsx
+++ b/web/src/components/Navigation/Navigation.test.tsx
@@ -11,10 +11,6 @@ import { Navigation } from './Navigation'
const renderComponent = (props = {}) => render()
describe('Navigation', () => {
- afterEach(() => {
- jest.clearAllMocks()
- })
-
it('renders navigation component', () => {
renderComponent()
expect(screen.getByTestId('nav')).toBeVisible()
@@ -36,6 +32,7 @@ describe('Navigation', () => {
it('shows logout when authenticated', async () => {
mockCurrentUser({ id: 'foobar' })
+
renderComponent()
await waitFor(() => {
expect(screen.getByText('Logout')).toBeInTheDocument()
diff --git a/web/src/components/Verification/Verification.mock.ts b/web/src/components/Verification/Verification.mock.ts
new file mode 100644
index 0000000..b615c8f
--- /dev/null
+++ b/web/src/components/Verification/Verification.mock.ts
@@ -0,0 +1 @@
+mockGraphQLMutation('VerificationMutation', { verifyUser: true })
diff --git a/web/src/components/Verification/Verification.stories.tsx b/web/src/components/Verification/Verification.stories.tsx
new file mode 100644
index 0000000..b085612
--- /dev/null
+++ b/web/src/components/Verification/Verification.stories.tsx
@@ -0,0 +1,12 @@
+import type { ComponentMeta } from '@storybook/react'
+
+import { Verification } from './Verification'
+
+export const generated = () => {
+ return
+}
+
+export default {
+ title: 'Components/Verification',
+ component: Verification,
+} as ComponentMeta
diff --git a/web/src/components/Verification/Verification.test.tsx b/web/src/components/Verification/Verification.test.tsx
new file mode 100644
index 0000000..e4b238d
--- /dev/null
+++ b/web/src/components/Verification/Verification.test.tsx
@@ -0,0 +1,11 @@
+import { render } from '@redwoodjs/testing/web'
+
+import { Verification } from './Verification'
+
+describe('Verification', () => {
+ it('renders successfully', () => {
+ expect(() => {
+ render()
+ }).not.toThrow()
+ })
+})
diff --git a/web/src/components/Verification/Verification.tsx b/web/src/components/Verification/Verification.tsx
new file mode 100644
index 0000000..ae1e0c9
--- /dev/null
+++ b/web/src/components/Verification/Verification.tsx
@@ -0,0 +1,45 @@
+import { useEffect } from 'react'
+
+import { navigate, routes } from '@redwoodjs/router'
+import { MetaTags, useMutation } from '@redwoodjs/web'
+import { toast } from '@redwoodjs/web/dist/toast'
+
+const VERIFY_TOKEN_MUTATION = gql`
+ mutation VerificationMutation($token: String!) {
+ verifyUser: verifyUser(token: $token)
+ }
+`
+const Verification = ({ token }) => {
+ const [verifyUser, { loading, error }] = useMutation(VERIFY_TOKEN_MUTATION, {
+ onCompleted: () => {
+ toast.success('Account Verified')
+ navigate(routes.login())
+ },
+ onError: (error) => {
+ toast.error(error.message)
+ },
+ })
+
+ useEffect(() => {
+ verifyUser({ variables: { token } })
+ }, [verifyUser, token])
+
+ return (
+ <>
+
+
+
+
+
+ {loading && 'Loading...'}
+ {error && 'Error, unable to verify account'}
+
+
+
+ >
+ )
+}
+
+export { Verification }
diff --git a/web/src/pages/LoginPage/LoginPage.tsx b/web/src/pages/LoginPage/LoginPage.tsx
index dcdafd2..ce2bb9b 100644
--- a/web/src/pages/LoginPage/LoginPage.tsx
+++ b/web/src/pages/LoginPage/LoginPage.tsx
@@ -34,6 +34,9 @@ const LoginPage = () => {
if (response.message) {
toast(response.message)
} else if (response.error) {
+ if (response.error === 'User not Verified') {
+ navigate(routes.verificationReset({ email: data.username }))
+ }
toast.error(response.error)
} else {
toast.success('Welcome back!')
diff --git a/web/src/pages/VerificationPage/VerificationPage.stories.tsx b/web/src/pages/VerificationPage/VerificationPage.stories.tsx
new file mode 100644
index 0000000..e54f1db
--- /dev/null
+++ b/web/src/pages/VerificationPage/VerificationPage.stories.tsx
@@ -0,0 +1,7 @@
+import { VerificationPage } from './VerificationPage'
+
+export const generated = (args) => {
+ return
+}
+
+export default { title: 'Pages/VerificationPage' }
diff --git a/web/src/pages/VerificationPage/VerificationPage.test.tsx b/web/src/pages/VerificationPage/VerificationPage.test.tsx
new file mode 100644
index 0000000..aa427d7
--- /dev/null
+++ b/web/src/pages/VerificationPage/VerificationPage.test.tsx
@@ -0,0 +1,14 @@
+import { render } from '@redwoodjs/testing/web'
+
+import VerificationPage from './VerificationPage'
+
+// Improve this test with help from the Redwood Testing Doc:
+// https://redwoodjs.com/docs/testing#testing-pages-layouts
+
+describe('VerificationPage', () => {
+ it('renders successfully', () => {
+ expect(() => {
+ render()
+ }).not.toThrow()
+ })
+})
diff --git a/web/src/pages/VerificationPage/VerificationPage.tsx b/web/src/pages/VerificationPage/VerificationPage.tsx
new file mode 100644
index 0000000..84f666d
--- /dev/null
+++ b/web/src/pages/VerificationPage/VerificationPage.tsx
@@ -0,0 +1,7 @@
+import { Verification } from 'src/components/Verification'
+
+const VerificationPage = ({ verifyToken }) => (
+
+)
+
+export default VerificationPage
diff --git a/web/src/pages/VerificationResetPage/VerificationResetPage.stories.tsx b/web/src/pages/VerificationResetPage/VerificationResetPage.stories.tsx
new file mode 100644
index 0000000..ce83284
--- /dev/null
+++ b/web/src/pages/VerificationResetPage/VerificationResetPage.stories.tsx
@@ -0,0 +1,12 @@
+import type { ComponentMeta } from '@storybook/react'
+
+import VerificationResetPage from './VerificationResetPage'
+
+export const generated = () => {
+ return
+}
+
+export default {
+ title: 'Pages/VerificationResetPage',
+ component: VerificationResetPage,
+} as ComponentMeta
diff --git a/web/src/pages/VerificationResetPage/VerificationResetPage.test.tsx b/web/src/pages/VerificationResetPage/VerificationResetPage.test.tsx
new file mode 100644
index 0000000..07b728b
--- /dev/null
+++ b/web/src/pages/VerificationResetPage/VerificationResetPage.test.tsx
@@ -0,0 +1,14 @@
+import { render } from '@redwoodjs/testing/web'
+
+import VerificationResetPage from './VerificationResetPage'
+
+// Improve this test with help from the Redwood Testing Doc:
+// https://redwoodjs.com/docs/testing#testing-pages-layouts
+
+describe('VerificationResetPage', () => {
+ it('renders successfully', () => {
+ expect(() => {
+ render()
+ }).not.toThrow()
+ })
+})
diff --git a/web/src/pages/VerificationResetPage/VerificationResetPage.tsx b/web/src/pages/VerificationResetPage/VerificationResetPage.tsx
new file mode 100644
index 0000000..dcc08f9
--- /dev/null
+++ b/web/src/pages/VerificationResetPage/VerificationResetPage.tsx
@@ -0,0 +1,93 @@
+import { useEffect, useRef } from 'react'
+
+import { Form, Label, TextField, Submit, FieldError } from '@redwoodjs/forms'
+import { navigate, routes } from '@redwoodjs/router'
+import { MetaTags, useMutation } from '@redwoodjs/web'
+import { toast, Toaster } from '@redwoodjs/web/toast'
+
+const VERIFY_RESET_MUTATION = gql`
+ mutation VerificationResetMutation($email: String!) {
+ email: verifyReset(email: $email)
+ }
+`
+
+const VerificationResetPage = ({ email }) => {
+ const [verifyReset, { loading }] = useMutation(VERIFY_RESET_MUTATION, {
+ onCompleted: (response) => {
+ toast.success(
+ 'A link to verify your account was sent to ' + response.email
+ )
+ navigate(routes.login())
+ },
+ onError: (error) => {
+ toast.error(error.message)
+ },
+ })
+
+ const emailRef = useRef()
+ useEffect(() => {
+ emailRef.current.focus()
+ }, [])
+
+ const onSubmit = async (data) => {
+ verifyReset({ variables: { email: data.email } })
+ }
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+ Resend Verification Email
+
+
+
+
+
+
+
+ >
+ )
+}
+
+export default VerificationResetPage
diff --git a/yarn.lock b/yarn.lock
index 3d89174..7a923f6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7260,6 +7260,7 @@ __metadata:
"@redwoodjs/graphql-server": ^3.2.0
"@types/chance": ^1.1.3
chance: ^1.1.8
+ nodemailer: ^6.7.8
languageName: unknown
linkType: soft
@@ -17631,6 +17632,13 @@ __metadata:
languageName: node
linkType: hard
+"nodemailer@npm:^6.7.8":
+ version: 6.8.0
+ resolution: "nodemailer@npm:6.8.0"
+ checksum: 6f8fd051ff33e1330903b0013f65a14a672731de8506a931a7012c582b1ea507257186244213108b966e30d41a297f8de9e295ace61a8f0a70222913255ad667
+ languageName: node
+ linkType: hard
+
"nodemon@npm:2.0.20":
version: 2.0.20
resolution: "nodemon@npm:2.0.20"
@@ -23257,7 +23265,7 @@ __metadata:
languageName: node
linkType: hard
-"undici@npm:5.10.0, undici@npm:^5.5.1, undici@npm:^5.8.0":
+"undici@npm:5.10.0, undici@npm:^5.5.1":
version: 5.10.0
resolution: "undici@npm:5.10.0"
checksum: c67eec014c92d40b27a271d0127d0297299a0507feb67e581a0bd9fb5ce32974d3a05eba2bd19357d6231a7c2bbbc15ed4cef43c2738b5210b275e585547b09c
@@ -23271,6 +23279,13 @@ __metadata:
languageName: node
linkType: hard
+"undici@npm:^5.8.0":
+ version: 5.8.1
+ resolution: "undici@npm:5.8.1"
+ checksum: 9f1285950f153c28ab9b7e39cf3d4b8d1701c8e2037bde8460adc5f1fa3d3d5ab4336994310fccc9ac6679f1dacdca016b760ea2328cdab41e5e340faa684fd7
+ languageName: node
+ linkType: hard
+
"unfetch@npm:^4.2.0":
version: 4.2.0
resolution: "unfetch@npm:4.2.0"