Skip to content

Commit

Permalink
Account activation page (#117)
Browse files Browse the repository at this point in the history
* fix: host database

* fix: email link to activate account

* feat: account activation page

* chore: change error function name
  • Loading branch information
isaqueveras committed May 28, 2023
1 parent b45679e commit 825d14e
Show file tree
Hide file tree
Showing 28 changed files with 231 additions and 50 deletions.
4 changes: 2 additions & 2 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@
"srf": true,
"debug": false,
"start_http": true,
"start_grpc": true,
"start_grpc": false,
"access_log_directory": "/var/log/powersso/access.log",
"error_log_directory": "/var/log/powersso/error.log",
"permission_base": "github.com/isaqueveras/power-sso"
},
"database": {
"host": "postgesql",
"host": "localhost",
"port": 5432,
"user": "postgres",
"password": "postgres",
Expand Down
2 changes: 1 addition & 1 deletion internal/infrastructure/auth/mailer.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (ma *mailerAuth) sendMailActivationAccount(email *string, token *string) er
"Activate your "+ma.cfg.Meta.ProjectName+" registration",
`Click on the link below to activate your `+ma.cfg.Meta.ProjectName+` registration:
`+ma.cfg.Meta.ProjectURL+`/activate?token=`+*token+`
<a href="`+ma.cfg.Meta.ProjectURL+`/auth/activation/`+*token+`">`+ma.cfg.Meta.ProjectURL+`/auth/activation/`+*token+`</a>
If you have not made this request, please ignore this email.
Expand Down
98 changes: 80 additions & 18 deletions ui/package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
"eject": "react-scripts eject",
"lint": "eslint './**/*.{js,jsx,ts,tsx}'"
},
"eslintConfig": {
"extends": [
Expand Down
1 change: 1 addition & 0 deletions ui/src/data/usecases/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './remote-authentication'
export * from './remote-create-account'
export * from './remote-activation'
18 changes: 18 additions & 0 deletions ui/src/data/usecases/remote-activation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { HttpClient, HttpStatusCode } from '../protocols/http'
import { Activation } from '../../domain/usecases'
import { Err } from '../../domain/errors'

export class RemoteActivation implements Activation {
constructor (
private readonly url: string,
private readonly httpClient: HttpClient<Activation.Model>
) {}

async activate (params: Activation.Params): Promise<Activation.Model> {
const httpResponse = await this.httpClient.request({ url: this.url, method: 'post', body: params })
switch (httpResponse.statusCode) {
case HttpStatusCode.ok: return httpResponse.body
default: throw new Err(httpResponse.body.message)
}
}
}
4 changes: 2 additions & 2 deletions ui/src/data/usecases/remote-authentication.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { HttpClient, HttpStatusCode } from '../../data/protocols/http'
import { Authentication } from '../../domain/usecases'
import { Oops } from '../../domain/errors'
import { Err } from '../../domain/errors'

export class RemoteAuthentication implements Authentication {
constructor (
Expand All @@ -12,7 +12,7 @@ export class RemoteAuthentication implements Authentication {
const httpResponse = await this.httpClient.request({ url: this.url, method: 'post', body: params })
switch (httpResponse.statusCode) {
case HttpStatusCode.ok: return httpResponse.body
default: throw new Oops(httpResponse.body.message)
default: throw new Err(httpResponse.body.message)
}
}
}
4 changes: 2 additions & 2 deletions ui/src/data/usecases/remote-create-account.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { HttpClient, HttpStatusCode } from '../../data/protocols/http'
import { CreateAccount } from '../../domain/usecases'
import { Oops } from '../../domain/errors'
import { Err } from '../../domain/errors'

export class RemoteCreateAccount implements CreateAccount {
constructor (
Expand All @@ -12,7 +12,7 @@ export class RemoteCreateAccount implements CreateAccount {
const httpResponse = await this.httpClient.request({ url: this.url, method: 'post', body: params })
switch (httpResponse.statusCode) {
case HttpStatusCode.ok: return httpResponse.body
default: throw new Oops(httpResponse.body.message)
default: throw new Err(httpResponse.body.message)
}
}
}
4 changes: 2 additions & 2 deletions ui/src/domain/errors/oops.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export class Oops extends Error {
export class Err extends Error {
constructor (message: string) {
super(message)
this.name = 'OopsError'
this.name = 'ErrError'
}
}
13 changes: 13 additions & 0 deletions ui/src/domain/usecases/activation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Err } from "@/domain/models"

export interface Activation {
activate: (params: Activation.Params) => Promise<Activation.Model>
}

export namespace Activation {
export type Params = {
id: string
}

export type Model = Err
}
1 change: 1 addition & 0 deletions ui/src/domain/usecases/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './authentication'
export * from './create-account'
export * from './activation'
10 changes: 10 additions & 0 deletions ui/src/main/factories/pages/activation-factory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from 'react'
import { useParams } from 'react-router-dom'

import { ActivationPage } from '../../../presentation/pages'
import { makeRemoteActivation } from '../usecases'

export const makeActivationPage: React.FC = () => {
const { token } = useParams<{ token: string }>()
return <ActivationPage usecase={makeRemoteActivation(token)}/>
}
2 changes: 1 addition & 1 deletion ui/src/main/factories/pages/create-account-factory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { CreateAccountPage } from '../../../presentation/pages'
import { makeRemoteCreateAccount } from '../usecases'
import { makeCreateAccountValidation } from '../validation'

export const makeCreateAccount: React.FC = () => {
export const makeCreateAccountPage: React.FC = () => {
return (
<CreateAccountPage
usecase={makeRemoteCreateAccount()}
Expand Down
2 changes: 1 addition & 1 deletion ui/src/main/factories/pages/home-factory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { currentAccountState } from '../../../presentation/components'
import React from 'react'
import { useRecoilValue } from 'recoil'

export const makeHome: React.FC = () => {
export const makeHomePage: React.FC = () => {
const { getCurrentAccount } = useRecoilValue(currentAccountState)
const name = getCurrentAccount().first_name
return <h1>Hello, {name}!</h1>
Expand Down
1 change: 1 addition & 0 deletions ui/src/main/factories/pages/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './login-factory'
export * from './home-factory'
export * from './create-account-factory'
export * from './activation-factory'
2 changes: 1 addition & 1 deletion ui/src/main/factories/pages/login-factory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { makeLoginValidation } from '../../../main/factories/validation'
import { makeRemoteAuthentication } from '../../../main/factories/usecases'
import { LoginPage } from '../../../presentation/pages'

export const makeLogin: React.FC = () => {
export const makeLoginPage: React.FC = () => {
return (
<LoginPage
usecase={makeRemoteAuthentication()}
Expand Down
6 changes: 6 additions & 0 deletions ui/src/main/factories/usecases/activation-factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { makeApiUrl, makeAxiosHttpClient } from '../http'
import { RemoteActivation } from '../../../data/usecases'
import { Activation } from '../../../domain/usecases'

export const makeRemoteActivation = (token: string): Activation =>
new RemoteActivation(makeApiUrl(`auth/activation/${token}`), makeAxiosHttpClient())
1 change: 1 addition & 0 deletions ui/src/main/factories/usecases/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './remote-authentication-factory'
export * from './remote-register-factory'
export * from './activation-factory'
11 changes: 6 additions & 5 deletions ui/src/main/routes/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import React from 'react'
import { BrowserRouter, Route, Switch } from 'react-router-dom'
import { RecoilRoot } from 'recoil'

import { PrivateRoute } from '../proxies'
import { setCurrentAccountAdapter, getCurrentAccountAdapter } from '../../main/adapters'
import { currentAccountState } from '../../presentation/components'
import { makeLogin, makeHome, makeCreateAccount } from '../../main/factories/pages'
import { PrivateRoute } from '../proxies'
import { makeLoginPage, makeHomePage, makeCreateAccountPage, makeActivationPage } from '../../main/factories/pages'

const Router: React.FC = () => {
const state = {
Expand All @@ -17,9 +17,10 @@ const Router: React.FC = () => {
<div className='h-screen'>
<BrowserRouter>
<Switch>
<Route path="/auth/login" exact component={makeLogin} />
<Route path="/auth/register" exact component={makeCreateAccount} />
<PrivateRoute path="/" exact component={makeHome} />
<Route path="/auth/login" exact component={makeLoginPage} />
<Route path="/auth/register" exact component={makeCreateAccountPage} />
<Route path="/auth/activation/:token" exact component={makeActivationPage} />
<PrivateRoute path="/" exact component={makeHomePage} />
</Switch>
</BrowserRouter>
</div>
Expand Down
4 changes: 2 additions & 2 deletions ui/src/presentation/components/form-status/form-status.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React from 'react'

type Props = {
messageError: string
errorMessage: string
}

const FormStatus: React.FC<Props> = ({ messageError }: Props) => {
const FormStatus: React.FC<Props> = ({ errorMessage: messageError }: Props) => {
return (
<>
{messageError && (
Expand Down
14 changes: 7 additions & 7 deletions ui/src/presentation/pages/Projects.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import Sidebar from "../components/sidebar";
import Sidebar from '../components/sidebar'

const Projects = () => {
return (
<div className="flex flex-row dark:bg-black/95 w-screen">
<div className="w-64">
<div className='flex flex-row dark:bg-black/95 w-screen'>
<div className='w-64'>
<Sidebar />
</div>
<div className="w-full">
<div className="border-b border-gray-200 dark:border-black/30 h-16">
<div className="h-16 px-4 flex flex-row justify-between items-center">
<h1 className="text-2xl text-gray-900 font-medium dark:text-white">Projects</h1>
<div className='w-full'>
<div className='border-b border-gray-200 dark:border-black/30 h-16'>
<div className='h-16 px-4 flex flex-row justify-between items-center'>
<h1 className='text-2xl text-gray-900 font-medium dark:text-white'>Projects</h1>
</div>
</div>
</div>
Expand Down
9 changes: 9 additions & 0 deletions ui/src/presentation/pages/activation/components/atoms.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { atom } from 'recoil'

export const activationState = atom({
key: 'activationState',
default: {
isLoading: false,
errorMessage: ''
}
})
12 changes: 12 additions & 0 deletions ui/src/presentation/pages/activation/components/form-status.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react'
import { useRecoilValue } from 'recoil'

import { activationState } from './atoms'
import { FormStatusBase } from '../../../../presentation/components'

const FormStatus: React.FC = () => {
const state = useRecoilValue(activationState)
return <FormStatusBase errorMessage={state.errorMessage} />
}

export default FormStatus
44 changes: 44 additions & 0 deletions ui/src/presentation/pages/activation/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React, { useEffect } from 'react'
import { Activation } from '@/domain/usecases/activation'
import { useRecoilState, useResetRecoilState } from 'recoil'
import { activationState } from './components/atoms'
import FormStatus from './components/form-status'
import { Link } from 'react-router-dom'

type Props = {
usecase: Activation
}

const ActivationPage: React.FC<Props> = ({ usecase }: Props) => {
const resetActivationState = useResetRecoilState(activationState)
const [state, setState] = useRecoilState(activationState)

useEffect(() => {
resetActivationState()
handleSubmit()
}, [])

const handleSubmit = async (): Promise<void> => {
try {
if (state.isLoading) return
setState((old) => ({ ...old, isLoading: true }))
await usecase.activate({ id: 'asdasdasd' })
} catch (error: any) {
setState((old) => ({ ...old, isLoading: false, errorMessage: error.message }))
}
}

return (
<section className='h-screen flex justify-center items-center bg-pink-50'>
{state.errorMessage === '' && (
<div className='space-y-6 bg-white p-12 rounded-sm shadow-sm'>
<p className='mb-4 text-base'>Activation successful! now you can login.</p>
<Link to='/auth/login'><p className='text-blue-500'>Login</p></Link>
</div>
)}
<FormStatus />
</section>
)
}

export default ActivationPage
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { FormStatusBase } from '../../../../presentation/components'

const FormStatus: React.FC = () => {
const state = useRecoilValue(createAccountState)
return <FormStatusBase messageError={state.messageError} />
return <FormStatusBase errorMessage={state.messageError} />
}

export default FormStatus
3 changes: 2 additions & 1 deletion ui/src/presentation/pages/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { default as LoginPage } from './login/login'
export { default as LoginPage } from './login'
export { default as CreateAccountPage } from './create-account'
export { default as ActivationPage } from './activation'
2 changes: 1 addition & 1 deletion ui/src/presentation/pages/login/components/form-status.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { FormStatusBase } from '../../../../presentation/components'

const FormStatus: React.FC = () => {
const state = useRecoilValue(loginState)
return <FormStatusBase messageError={state.messageError} />
return <FormStatusBase errorMessage={state.messageError} />
}

export default FormStatus
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type Props = {
usecase: Authentication
}

const Login: React.FC<Props> = ({ validation, usecase }: Props) => {
const LoginPage: React.FC<Props> = ({ validation, usecase }: Props) => {
const resetLoginState = useResetRecoilState(loginState)
const [state, setState] = useRecoilState(loginState)
const { setCurrentAccount } = useRecoilValue(currentAccountState)
Expand Down Expand Up @@ -68,4 +68,4 @@ const Login: React.FC<Props> = ({ validation, usecase }: Props) => {
)
}

export default Login
export default LoginPage

1 comment on commit 825d14e

@vercel
Copy link

@vercel vercel bot commented on 825d14e May 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

power-sso – ./

power-sso.vercel.app
power-sso-isaqueveras.vercel.app
power-sso-git-main-isaqueveras.vercel.app

Please sign in to comment.