Skip to content

Commit

Permalink
feat(envited.ascs.digital): add login buttons (#46)
Browse files Browse the repository at this point in the history
  • Loading branch information
royscheeren authored Jan 11, 2024
2 parents 1a8725f + 328b0bd commit e70a1e8
Show file tree
Hide file tree
Showing 17 changed files with 200 additions and 103 deletions.
57 changes: 1 addition & 56 deletions apps/envited.ascs.digital/app/api/auth/[...nextauth]/route.ts
Original file line number Diff line number Diff line change
@@ -1,61 +1,6 @@
import NextAuth from 'next-auth'
import type { NextAuthOptions } from 'next-auth'
import CredentialsProvider from 'next-auth/providers/credentials'

export const authOptions: NextAuthOptions = {
providers: [
CredentialsProvider({
// The name to display on the sign in form (e.g. 'Sign in with...')
name: 'Sign in with Your Credentials',
// The credentials is used to generate a suitable form on the sign in page.
// You can specify whatever fields you are expecting to be submitted.
// e.g. domain, username, password, 2FA token, etc.
// You can pass any HTML attribute to the <input> tag through the object.
credentials: {
pkh: { label: 'Address', type: 'text', placeholder: 'tz...' },
},
async authorize(credentials) {
if (!credentials) {
return {
id: '',
pkh: '',
memberId: '',
role: '',
}
}
const { pkh } = credentials

return {
id: '',
pkh,
memberId: '',
role: '',
}
},
}),
],
session: {
strategy: 'jwt',
},
callbacks: {
async jwt({ token, user }) {
if (user) {
token.user = user
}

return token
},
async session({ session, token }: { session: any; token: any }) {
session.user.pkh = token.user.pkh
session.user.role = token.user.role
session.user.memberId = token.user.memberId
session.user.id = token.sub
session.user.email = undefined
session.user.image = undefined
return session
},
},
}
import { authOptions } from '../../../../common/auth/auth'

const handler = NextAuth(authOptions)

Expand Down
12 changes: 10 additions & 2 deletions apps/envited.ascs.digital/app/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import { getServerSession } from '../../common/session'
import { getServerSession } from '../../common/auth'
import { Dashboard } from '../../modules/Dashboard'
import { Header } from '../../modules/Header'

export default async function Index() {
const session = await getServerSession()

return (
<>
<Header />
<main>
<div className="mx-auto max-w-6xl">{session ? JSON.stringify(session) : 'No session'}</div>
<div className="mx-auto max-w-6xl">
{session ? (
<Dashboard id={session?.user?.id} address={session?.user?.pkh} role={session?.user?.role} />
) : (
<div>Not logged in</div>
)}
</div>
</main>
</>
)
Expand Down
19 changes: 19 additions & 0 deletions apps/envited.ascs.digital/common/auth/auth.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { _signIn } from './auth'

describe('common/auth/auth', () => {
describe('signIn', () => {
it('should call the sign in method with the expected parameters', async () => {
// when ... we want to sign a user in
// then ... it should call the sign in method with the expected parameters
const NASignIn = jest.fn().mockResolvedValue('SIGNED_IN')
const pkh = 'PKH'

const session = await _signIn(NASignIn)({ pkh })
expect(NASignIn).toHaveBeenCalledWith('credentials', {
pkh,
callbackUrl: '/dashboard',
})
expect(session).toEqual('SIGNED_IN')
})
})
})
86 changes: 86 additions & 0 deletions apps/envited.ascs.digital/common/auth/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import type { NextAuthOptions } from 'next-auth'
import CredentialsProvider from 'next-auth/providers/credentials'
import { signIn as NASignIn, signOut as NASignOut } from 'next-auth/react'
import { match } from 'ts-pattern'

import { Role } from '../types'

export const authOptions: NextAuthOptions = {
pages: {
error: '/',
signIn: '/',
},
providers: [
CredentialsProvider({
// The name to display on the sign in form (e.g. 'Sign in with...')
name: 'Sign in with Your Credentials',
// The credentials is used to generate a suitable form on the sign in page.
// You can specify whatever fields you are expecting to be submitted.
// e.g. domain, username, password, 2FA token, etc.
// You can pass any HTML attribute to the <input> tag through the object.
credentials: {
pkh: { label: 'Address', type: 'text', placeholder: 'tz...' },
},

async authorize(credentials) {
if (!credentials) {
return {
id: '',
pkh: '',
role: '',
}
}
const { pkh } = credentials

return match(pkh)
.with('tz1USER', () => ({
id: '1',
pkh: 'tz1USER',
role: Role.user,
}))
.with('tz1PRINCIPAL', () => ({
id: '1',
pkh: 'tz1PRINCIPAL',
role: Role.principal,
}))
.with('tz1NO_USER', () => null)
.otherwise(() => null)
},
}),
],
session: {
strategy: 'jwt',
},
callbacks: {
async jwt({ token, user }) {
if (user) {
token.user = user
}

return token
},
async session({ session, token }: { session: any; token: any }) {
session.user.pkh = token.user.pkh
session.user.role = token.user.role
session.user.id = token.sub
session.user.email = undefined
session.user.image = undefined
return session
},
},
}

export const _signIn =
(NASignIn: any) =>
({ pkh }: { pkh: string }) =>
NASignIn('credentials', {
pkh,
callbackUrl: '/dashboard',
})

export const signIn = _signIn(NASignIn)

export const signOut = () =>
NASignOut({
callbackUrl: '/',
})
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { getServerSession } from './session'
export { signIn, signOut } from './auth'
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { _getServerSession } from './session'

describe('common/session', () => {
describe('common/auth/session', () => {
describe('getServerSession', () => {
it('should should fetch a server session with the correct parameters', async () => {
it('should fetch a server session with the correct parameters', async () => {
// when ... we want to get the current session server side
// then ... it should call the getServerSession function with the correct parameters
const authOptions = 'AUTH_OPTIONS' as any
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getServerSession as NAGetServerSession, NextAuthOptions } from 'next-auth'

import { authOptions } from '../../app/api/auth/[...nextauth]/route'
import { authOptions } from './auth'

export const _getServerSession = (NAGetServerSession: any) => (authOptions: NextAuthOptions) => () =>
NAGetServerSession(authOptions)
Expand Down
2 changes: 1 addition & 1 deletion apps/envited.ascs.digital/common/database/database.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ describe('common/database', () => {
it('should should setup a connection with a local database', async () => {
// when ... we want to make a connection with a local db
// then ... we should get a connection as expected
process.env.ENV = 'local'
process.env.ENV = 'development'
process.env.POSTGRES_DATABASE_NAME = 'DB_NAME'
process.env.POSTGRES_DATABASE_USER = 'DB_USER'
process.env.POSTGRES_DATABASE_PASSWORD = 'DB_PASSWORD'
Expand Down
12 changes: 2 additions & 10 deletions apps/envited.ascs.digital/common/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,2 @@
export {
Language,
Columns,
Size,
ColorScheme,
} from './types'
export type {
Action,
Obj,
} from './types'
export { Language, Columns, Size, ColorScheme, Role } from './types'
export type { Action, Obj } from './types'
30 changes: 30 additions & 0 deletions apps/envited.ascs.digital/modules/Dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use client'

import { Button } from '@envited-marketplace/design-system'
import React, { FC } from 'react'

import { signOut } from '../../common/auth'
import { Role } from '../../common/types'

interface DashboardProps {
id: string
address: string
role: Role
}

export const Dashboard: FC<DashboardProps> = ({ id, address, role }) => {
return (
<div>
<h1 className="text-3xl font-bold mb-5">You are logged in:</h1>
<dl className="mb-10">
<dt className="font-bold">ID</dt>
<dd className="ml-5 italic">{id}</dd>
<dt className="font-bold">Address</dt>
<dd className="ml-5 italic">{address}</dd>
<dt className="font-bold">Role</dt>
<dd className="ml-5 italic">{role}</dd>
</dl>
<Button onClick={signOut}>Sign out</Button>
</div>
)
}
1 change: 1 addition & 0 deletions apps/envited.ascs.digital/modules/Dashboard/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Dashboard } from './Dashboard'
7 changes: 5 additions & 2 deletions apps/envited.ascs.digital/modules/Header/Header.test.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import '@testing-library/jest-dom'
import { render } from '@testing-library/react'

import { Header } from './Header'

describe('Header', () => {
it('should render successfully', () => {
const { baseElement } = render(<Header />)
expect(baseElement).toBeTruthy()
// when ... rendering component
// then ... should render as expected
const { getByText } = render(<Header />)
expect(getByText('Connect')).toBeInTheDocument()
})
})
43 changes: 19 additions & 24 deletions apps/envited.ascs.digital/modules/HeroHeader/HeroHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,37 @@
'use client'

import { Button, Card, Grid, GridRow, Heading, HeadingWithTooltip, Tooltip } from '@envited-marketplace/design-system'
import { Button, Grid, GridRow } from '@envited-marketplace/design-system'
import { useSearchParams } from 'next/navigation'
import React, { FC } from 'react'

import { signIn } from '../../common/auth'
import { useTranslation } from '../../common/i18n'

export const HeroHeader: FC = () => {
const { t } = useTranslation('HeroHeader')
const searchParams = useSearchParams()

return (
<div className="mx-auto max-w-6xl py-32 sm:py-48 lg:py-56">
<div className="mx-auto max-w-6xl">
{searchParams.has('error') ? (
<div className="w-full text-center text-orange text-2xl">Could not sign you in</div>
) : null}
<Grid>
<GridRow columns={`four` as any}>
<Card>
<Heading importance="h1">Hello</Heading>
<HeadingWithTooltip heading={<Heading importance="h3">Hello</Heading>} tooltip={<Tooltip>Test</Tooltip>} />
</Card>
<Card>
<Heading importance="h1">Hello</Heading>
<HeadingWithTooltip heading={<Heading importance="h3">Hello</Heading>} tooltip={<Tooltip>Test</Tooltip>} />
</Card>
<Card>
<Heading importance="h1">Hello</Heading>
<HeadingWithTooltip heading={<Heading importance="h3">Hello</Heading>} tooltip={<Tooltip>Test</Tooltip>} />
</Card>
<Card>
<Heading importance="h1">Hello</Heading>
<HeadingWithTooltip heading={<Heading importance="h3">Hello</Heading>} tooltip={<Tooltip>Test</Tooltip>} />
</Card>
<GridRow columns={`three` as any}>
<Button onClick={() => signIn({ pkh: 'tz1USER' })}>
<span>Login as User</span>
</Button>
<Button onClick={() => signIn({ pkh: 'tz1PRINCIPAL' })}>
<span>Login as Principal</span>
</Button>
<Button onClick={() => signIn({ pkh: 'tz1NO_USER' })}>
<span>Test Failed Login</span>
</Button>
</GridRow>
</Grid>
<div className="text-center">
<div className="flex justify-center items-center"></div>
<div className="mt-10 flex items-center justify-center gap-x-6">
<Button onClick={() => {}}>
<span>{t('[Button] title')}</span>
</Button>
</div>
<div className="mt-10 flex items-center justify-center gap-x-6"></div>
<div className="mt-10">
<h2 className="text-lg font-bold tracking-tight text-gray-900">{t('[Heading] why')}</h2>
<p className="mt-2 text-md leading-8 text-gray-600">{t('[Description] why')}</p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ jest.mock('next/navigation', () => ({
},
}))

jest.mock('next/navigation', () => ({
useSearchParams: () => ({
has: jest.fn(),
}),
}))

describe('modules/HeroHeader', () => {
describe('render', () => {
it('should return as expected', async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import '@testing-library/jest-dom'
import { render } from '@testing-library/react'
import React from 'react'
import TestRenderer from 'react-test-renderer'

import ThemeToggle from './ThemeToggle'

describe('modules/ThemeToggle', () => {
describe('render', () => {
it('should render as expected', async () => {
// when ... rendering component
const component = TestRenderer.create(<ThemeToggle />)

// then ... should render with expected element type
const tree = component.toJSON() as any
expect(tree.children[0].type).toEqual('svg')

const { getByRole } = render(<ThemeToggle />)
expect(getByRole('button')).toBeInTheDocument()
})
})
})
Loading

0 comments on commit e70a1e8

Please sign in to comment.