Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 16 additions & 19 deletions components/Alert.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,29 @@
import React from 'react'
import { CheckCircleIcon, XCircleIcon } from '@heroicons/react/24/solid'

type AlertProps = {
success: boolean | null
message: string | null
}

export default class Alert extends React.Component<AlertProps> {
override render() {
const icon = this.props.success ? (
<CheckCircleIcon className="h-5 w-5 text-green-400" />
) : (
<XCircleIcon className="h-5 w-5 text-red-400" />
)
export default function Alert({ success, message }: AlertProps) {
const icon = success ? (
<CheckCircleIcon className="h-5 w-5 text-green-400" />
) : (
<XCircleIcon className="h-5 w-5 text-red-400" />
)

const color = this.props.success ? 'green' : 'red'
const color = success ? 'green' : 'red'

return (
<div className={this.props.message ? 'block' : 'hidden'}>
<div className={`mb-6 rounded-md bg-${color}-50 p-4`}>
<div className="flex">
<div className="flex-shrink-0">{icon}</div>
<div className="ml-3">
<p className={`text-sm font-medium text-${color}-800`}>{this.props.message}</p>
</div>
return (
<div className={message ? 'block' : 'hidden'}>
<div className={`mb-6 rounded-md bg-${color}-50 p-4`}>
<div className="flex">
<div className="flex-shrink-0">{icon}</div>
<div className="ml-3">
<p className={`text-sm font-medium text-${color}-800`}>{message}</p>
</div>
</div>
</div>
)
}
</div>
)
}
95 changes: 46 additions & 49 deletions components/LoginWithEmail.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react'
import Link from 'next/link'
import { EnvelopeIcon } from '@heroicons/react/24/solid'
import Alert from './Alert'
Expand All @@ -9,58 +8,56 @@ type LoginWithEmailProps = {
message: string | null
}

export default class LoginWithEmail extends React.Component<LoginWithEmailProps> {
override render() {
return (
<div className="min-h-screen bg-gray-50 flex flex-col justify-center py-12 sm:px-6 lg:px-8">
<div className="sm:mx-auto sm:w-full sm:max-w-md">
<img className="mx-auto h-20 w-auto" src="/favicon.png" alt="SuperApp" />
<h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">
Log in to Super App
</h2>
</div>
export default function LoginWithEmail({ onSubmit, success, message }: LoginWithEmailProps) {
return (
<div className="min-h-screen bg-gray-50 flex flex-col justify-center py-12 sm:px-6 lg:px-8">
<div className="sm:mx-auto sm:w-full sm:max-w-md">
<img className="mx-auto h-20 w-auto" src="/favicon.png" alt="SuperApp" />
<h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">
Log in to Super App
</h2>
</div>

<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
<Alert success={this.props.success} message={this.props.message} />
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
<Alert success={success} message={message} />

<form onSubmit={this.props.onSubmit} className="space-y-6">
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700">
Email address
</label>
<div className="mt-1">
<input
id="email"
name="email"
type="email"
autoComplete="email"
placeholder="james@bond.com"
required
className="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
/>
</div>
<form onSubmit={onSubmit} className="space-y-6">
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700">
Email address
</label>
<div className="mt-1">
<input
id="email"
name="email"
type="email"
autoComplete="email"
placeholder="james@bond.com"
required
className="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
/>
</div>
</div>

<div>
<button
type="submit"
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
<EnvelopeIcon className="-ml-1 mr-2 h-5 w-5" aria-hidden="true" />
Send Magic Link
</button>
</div>
</form>
</div>
<p className="mt-4 text-center text-sm text-gray-600">
Or{' '}
<Link href="/app/sso" className="font-medium text-blue-600 hover:text-blue-500">
continue with SAML SSO
</Link>
</p>
<div>
<button
type="submit"
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
<EnvelopeIcon className="-ml-1 mr-2 h-5 w-5" aria-hidden="true" />
Send Magic Link
</button>
</div>
</form>
</div>
<p className="mt-4 text-center text-sm text-gray-600">
Or{' '}
<Link href="/app/sso" className="font-medium text-blue-600 hover:text-blue-500">
continue with SAML SSO
</Link>
</p>
</div>
)
}
</div>
)
}
93 changes: 45 additions & 48 deletions components/LoginWithSSO.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react'
import Link from 'next/link'
import { LockClosedIcon } from '@heroicons/react/24/solid'
import Alert from './Alert'
Expand All @@ -9,57 +8,55 @@ type LoginWithSSOProps = {
message: string | null
}

export default class LoginWithSSO extends React.Component<LoginWithSSOProps> {
override render() {
return (
<div className="min-h-screen bg-gray-50 flex flex-col justify-center py-12 sm:px-6 lg:px-8">
<div className="sm:mx-auto sm:w-full sm:max-w-md">
<img className="mx-auto h-20 w-auto" src="/favicon.png" alt="SuperApp" />
<h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">
Log in with SSO to Super App
</h2>
</div>
export default function LoginWithSSO({ onSubmit, success, message }: LoginWithSSOProps) {
return (
<div className="min-h-screen bg-gray-50 flex flex-col justify-center py-12 sm:px-6 lg:px-8">
<div className="sm:mx-auto sm:w-full sm:max-w-md">
<img className="mx-auto h-20 w-auto" src="/favicon.png" alt="SuperApp" />
<h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">
Log in with SSO to Super App
</h2>
</div>

<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
<Alert success={this.props.success} message={this.props.message} />
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
<Alert success={success} message={message} />

<form onSubmit={this.props.onSubmit} className="space-y-6">
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700">
Email Address
</label>
<div className="mt-1">
<input
id="email"
name="email"
type="text"
placeholder="Enter email address"
required
className="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
/>
</div>
<form onSubmit={onSubmit} className="space-y-6">
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700">
Email Address
</label>
<div className="mt-1">
<input
id="email"
name="email"
type="text"
placeholder="Enter email address"
required
className="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
/>
</div>
</div>

<div>
<button
type="submit"
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
<LockClosedIcon className="-ml-1 mr-2 h-5 w-5" aria-hidden="true" />
Login with SAML SSO
</button>
</div>
</form>
</div>
<p className="mt-4 text-center text-sm text-gray-600">
Or{' '}
<Link href="/app/login" className="font-medium text-blue-600 hover:text-blue-500">
continue with Magic Link
</Link>
</p>
<div>
<button
type="submit"
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
<LockClosedIcon className="-ml-1 mr-2 h-5 w-5" aria-hidden="true" />
Login with SAML SSO
</button>
</div>
</form>
</div>
<p className="mt-4 text-center text-sm text-gray-600">
Or{' '}
<Link href="/app/login" className="font-medium text-blue-600 hover:text-blue-500">
continue with Magic Link
</Link>
</p>
</div>
)
}
</div>
)
}
27 changes: 12 additions & 15 deletions pages/app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
import React from 'react'
import Head from 'next/head'
import Home from '../../components/Home'
import Layout from '../../components/Layout'

export default class extends React.Component {
override render() {
return (
<main>
<Head>
<title>Super App | Home</title>
<link href="/favicon.png" rel="shortcut icon" />
</Head>
export default function HomePage() {
return (
<main>
<Head>
<title>Super App | Home</title>
<link href="/favicon.png" rel="shortcut icon" />
</Head>

<Layout>
<Home />
</Layout>
</main>
)
}
<Layout>
<Home />
</Layout>
</main>
)
}
57 changes: 18 additions & 39 deletions pages/app/login.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,12 @@
import React from 'react'
import { useState } from 'react'
import Head from 'next/head'
import LoginWithEmail from '../../components/LoginWithEmail'

type State = {
success: boolean | null
message: string | null
}

export default class extends React.Component<{}, State> {
constructor(props: {}) {
super(props)

this.state = {
success: null,
message: null,
}
}
export default function LoginPage() {
const [success, setSuccess] = useState<boolean | null>(null)
const [message, setMessage] = useState<string | null>(null)

onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()

try {
Expand All @@ -38,32 +27,22 @@ export default class extends React.Component<{}, State> {
throw new Error(data.message)
}

this.setState({
success: true,
message: 'We just sent a magic link to your email.',
})
setSuccess(true)
setMessage('We just sent a magic link to your email.')
} catch (err) {
this.setState({
success: false,
message: err instanceof Error ? err.message : String(err),
})
setSuccess(false)
setMessage(err instanceof Error ? err.message : String(err))
}
}

override render() {
return (
<main>
<Head>
<title>Super App | Log in with Email</title>
<link href="/favicon.png" rel="shortcut icon" />
</Head>
return (
<main>
<Head>
<title>Super App | Log in with Email</title>
<link href="/favicon.png" rel="shortcut icon" />
</Head>

<LoginWithEmail
onSubmit={this.onSubmit}
success={this.state.success}
message={this.state.message}
/>
</main>
)
}
<LoginWithEmail onSubmit={onSubmit} success={success} message={message} />
</main>
)
}
Loading
Loading