Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Firebase Sign In #5

Merged
merged 50 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
2727801
Install firebase
arkadiuszbachorski Jun 6, 2024
62f6026
Add global styles
arkadiuszbachorski Jun 6, 2024
0cafd11
Remove components import from design-system index
arkadiuszbachorski Jun 6, 2024
ca3cea6
Create Separator component
arkadiuszbachorski Jun 6, 2024
2f756e9
Add surface-primary color
arkadiuszbachorski Jun 7, 2024
ef52573
Add outlineBg button variant
arkadiuszbachorski Jun 7, 2024
edb386d
Configure intl
arkadiuszbachorski Jun 10, 2024
a0d0030
Create Card component
arkadiuszbachorski Jun 10, 2024
ed773d3
Create Error component
arkadiuszbachorski Jun 10, 2024
dd49bd6
Create Input component
arkadiuszbachorski Jun 10, 2024
d94dced
Create Label component
arkadiuszbachorski Jun 10, 2024
148fedd
Register new components
arkadiuszbachorski Jun 10, 2024
9844209
Configure sign-in
arkadiuszbachorski Jun 10, 2024
a1b8dc2
Fix
arkadiuszbachorski Jun 10, 2024
595c945
Add missing compliance
arkadiuszbachorski Jun 10, 2024
0efeb2d
Add missing compliance v2
arkadiuszbachorski Jun 10, 2024
1591e9c
Fix Card tests
arkadiuszbachorski Jun 10, 2024
a43adb1
Fix Card test
arkadiuszbachorski Jun 11, 2024
63513ef
Fix build
arkadiuszbachorski Jun 11, 2024
cf8a677
Update envs
arkadiuszbachorski Jun 11, 2024
0a8b8e4
Merge branch 'main' into arek/add-firebase
PSchmiedmayer Jun 12, 2024
ed77b63
Improve layout components
arkadiuszbachorski Jun 12, 2024
870d80b
Implement reusable guards
arkadiuszbachorski Jun 14, 2024
09c8744
Make SignIn form reusable
arkadiuszbachorski Jun 14, 2024
6077e2c
Move getSeverApp
arkadiuszbachorski Jun 14, 2024
bf97a38
Modularize other elements
arkadiuszbachorski Jun 14, 2024
7279384
Fix build step
arkadiuszbachorski Jun 14, 2024
a62b77e
Fix compliance
arkadiuszbachorski Jun 14, 2024
107ce22
Fix compliance v2
arkadiuszbachorski Jun 14, 2024
4045f93
Ignore dist files locally
arkadiuszbachorski Jun 15, 2024
f7adf9f
Test and document useForm
arkadiuszbachorski Jun 15, 2024
1ad50ef
Add Field test and stories
arkadiuszbachorski Jun 17, 2024
2f969fb
Fix missing compliance
arkadiuszbachorski Jun 17, 2024
900a49f
Convert RootLayout to arrow function
arkadiuszbachorski Jun 17, 2024
182f9be
Create MessagesProvider and move messages as separate module
arkadiuszbachorski Jun 17, 2024
56d9141
Test modules/auth/SignInForm components
arkadiuszbachorski Jun 17, 2024
d332297
Add missing compliance
arkadiuszbachorski Jun 17, 2024
ae35df2
Forward isSubmittign to EmailPasswordForm
arkadiuszbachorski Jun 17, 2024
9c47f2c
Merge branch 'main' into arek/add-firebase
PSchmiedmayer Jun 17, 2024
328bf64
Merge branch 'refs/heads/main' into arek/add-firebase
arkadiuszbachorski Jul 3, 2024
cbc2289
Make engage logo easier to read
arkadiuszbachorski Jul 3, 2024
8af31d5
Improve error spacing
arkadiuszbachorski Jul 3, 2024
7b8b6ce
Add build service worker to dev script
arkadiuszbachorski Jul 3, 2024
5d2a5ab
Remove TODO from code
arkadiuszbachorski Jul 4, 2024
26ba9cd
Add env variables config
arkadiuszbachorski Jul 4, 2024
41c9524
Fix compliance
arkadiuszbachorski Jul 4, 2024
31254a7
Prefix variables with webFrontend_
arkadiuszbachorski Jul 9, 2024
629548d
Fix SSO
arkadiuszbachorski Jul 9, 2024
5ef0cff
Fix ESLint config
arkadiuszbachorski Jul 10, 2024
1cbbedc
Update README
PSchmiedmayer Jul 15, 2024
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
9 changes: 9 additions & 0 deletions .github/workflows/build-and-test.yml
arkadiuszbachorski marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,12 @@ jobs:
with:
dockerComposeFile: docker-compose-development.yml
testscript: test.sh
secrets:
ENV_FILE: |
NEXT_PUBLIC_FIREBASE_API_KEY='${{ secrets.NEXT_PUBLIC_FIREBASE_API_KEY }}'
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN='${{ secrets.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN }}'
NEXT_PUBLIC_FIREBASE_PROJECT_ID='${{ secrets.NEXT_PUBLIC_FIREBASE_PROJECT_ID }}'
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET='${{ secrets.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET }}'
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID='${{ secrets.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID }}'
NEXT_PUBLIC_FIREBASE_APP_ID='${{ secrets.NEXT_PUBLIC_FIREBASE_APP_ID }}'

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

# production
/build
/public/authServiceWorker.js

# misc
.DS_Store
Expand Down
7 changes: 7 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ COPY . .
# Uncomment the following line in case you want to disable telemetry during the build.
ENV NEXT_TELEMETRY_DISABLED 1

ARG NEXT_PUBLIC_FIREBASE_API_KEY
ARG NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN
ARG NEXT_PUBLIC_FIREBASE_PROJECT_ID
ARG NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET
ARG NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID
ARG NEXT_PUBLIC_FIREBASE_APP_ID

RUN npm run build

# Production image, copy all the files and run next
Expand Down
21 changes: 21 additions & 0 deletions app/(dashboard)/SignOutButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// This source file is part of the Stanford Biodesign Digital Health ENGAGE-HF open-source project
//
// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//
'use client'
import { Button } from '@stanfordbdhg/design-system/components/Button'
import { auth } from '../../modules/firebase/clientApp'

export const SignOutButton = () => (
<Button
variant="link"
onClick={async () => {
await auth.signOut()
}}
>
Sign out
</Button>
)
27 changes: 27 additions & 0 deletions app/(dashboard)/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// This source file is part of the Stanford Biodesign Digital Health ENGAGE-HF open-source project
//
// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//
import { redirect } from 'next/navigation'
import { SignOutButton } from './SignOutButton'
import { getServerApp } from '../../modules/firebase/serverApp'
import { routes } from '../../modules/routes'

export const dynamic = 'force-dynamic'

const DashboardPage = async () => {
const { currentUser } = await getServerApp()
if (!currentUser) redirect(routes.signIn)
arkadiuszbachorski marked this conversation as resolved.
Show resolved Hide resolved

return (
<div className="grid gap-6 p-10 text-center">
<h1 className="text-2xl">Dashboard</h1>
<SignOutButton />
</div>
)
}

export default DashboardPage
51 changes: 51 additions & 0 deletions app/RegisterWorker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// This source file is part of the Stanford Biodesign Digital Health ENGAGE-HF open-source project
//
// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//
'use client'
import { type User } from '@firebase/auth-types'
import { onAuthStateChanged } from 'firebase/auth'
import { useRouter } from 'next/navigation'
import { useEffect, useState } from 'react'
import { auth } from '../modules/firebase/clientApp'
import { firebaseConfig } from '../modules/firebase/config'
import { routes } from '../modules/routes'

export const RegisterWorker = () => {
const router = useRouter()
const [user, setUser] = useState<User | null>()

useEffect(() => {
if ('serviceWorker' in navigator) {
const serializedFirebaseConfig = encodeURIComponent(
JSON.stringify(firebaseConfig),
)
const serviceWorkerUrl = `/authServiceWorker.js?firebaseConfig=${serializedFirebaseConfig}`

void navigator.serviceWorker.register(serviceWorkerUrl)
}
}, [])

useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
// @ts-expect-error Nested methods are not used anyway
setUser(user)
})
return () => unsubscribe()
}, [])

useEffect(() => {
// TODO: This is not ideal, results with double redirect. To investigate
arkadiuszbachorski marked this conversation as resolved.
Show resolved Hide resolved
const isSignIn = window.location.pathname === '/sign-in'
if (isSignIn && user) {
window.location.assign(routes.home)
} else if (!isSignIn && user === null) {
window.location.assign(routes.signIn)
}
}, [router, user])

return null
}
26 changes: 26 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,29 @@ SPDX-License-Identifier: MIT
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
*::selection {
@apply bg-primary/30;
}

body {
@apply bg-surface text-foreground;
}
}

@layer components {
.focus-ring {
@apply ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2;
}
}

@layer utilities {
.flex-center {
@apply flex items-center justify-center;
}

.interactive-opacity {
@apply focus-ring transition-opacity hover:opacity-60;
}
}
22 changes: 17 additions & 5 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@
// SPDX-License-Identifier: MIT
//
import type { Metadata } from 'next'
import { NextIntlClientProvider } from 'next-intl'
import { getLocale, getMessages } from 'next-intl/server'
import type { ReactNode } from 'react'
import './globals.css'
import '@stanfordbdhg/design-system/main.css'
import { themeToCSSVariables, lightTheme } from '@stanfordbdhg/design-system'
import { RegisterWorker } from './RegisterWorker'
import './globals.css'

export const metadata: Metadata = {
title: 'ENGAGE-HF Web Frontend',
Expand All @@ -20,18 +23,27 @@ interface RootLayoutProps {
children: ReactNode
}

export default function RootLayout({ children }: RootLayoutProps) {
export const dynamic = 'force-dynamic'

export default async function RootLayout({ children }: RootLayoutProps) {
const locale = await getLocale()
const messages = await getMessages()

return (
<html lang="en">
<html lang={locale}>
<head>
<style>
{`
:root { ${themeToCSSVariables(lightTheme)} }
`}
</style>
</head>

<body>{children}</body>
<body>
<RegisterWorker />
<NextIntlClientProvider messages={messages}>
{children}
</NextIntlClientProvider>
</body>
</html>
)
}
34 changes: 0 additions & 34 deletions app/page.test.tsx

This file was deleted.

27 changes: 0 additions & 27 deletions app/page.tsx

This file was deleted.

88 changes: 88 additions & 0 deletions app/sign-in/SignInForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//
// This source file is part of the Stanford Biodesign Digital Health ENGAGE-HF open-source project
//
// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//
'use client'
import { signInWithEmailAndPassword, signInWithPopup } from 'firebase/auth'
import { useTranslations } from 'next-intl'
import { z } from 'zod'
import { Button } from '@stanfordbdhg/design-system/components/Button'
import { Input } from '@stanfordbdhg/design-system/components/Input'
import {
Separator,
SeparatorText,
} from '@stanfordbdhg/design-system/components/Separator'
import { Field } from '@stanfordbdhg/design-system/forms/Field'
import { useForm } from '@stanfordbdhg/design-system/forms/useForm'
import { auth, authProvider } from '../../modules/firebase/clientApp'

const formSchema = z.object({
email: z.string().min(1, 'Email is required'),
password: z.string().min(1, 'Password is required'),
})

export const SignInForm = () => {
const t = useTranslations()
const form = useForm({
formSchema,
defaultValues: { email: '', password: '' },
})

const handleSubmit = form.handleSubmit(async ({ email, password }) => {
try {
await signInWithEmailAndPassword(auth, email, password)
} catch (error) {
if (
error instanceof Error &&
'code' in error &&
error.code === 'auth/invalid-credential'
) {
form.setFormError(t('signIn_formError_invalidCredentials'))
} else {
form.setFormError(t('signIn_formError_unknown'))
}
}
})

return (
<div className="mx-auto grid w-[350px] gap-4">
arkadiuszbachorski marked this conversation as resolved.
Show resolved Hide resolved
<h1 className="mb-4 text-center text-2xl font-bold">{t('signIn')}</h1>
<Button
variant="outlineBg"
onClick={() => signInWithPopup(auth, authProvider.apple)}
>
{t('signIn_apple')}
</Button>
<Button
variant="outlineBg"
onClick={() => signInWithPopup(auth, authProvider.stanford)}
>
{t('signIn_stanford')}
</Button>
<Separator className="my-5">
<SeparatorText>{t('signIn_separator')}</SeparatorText>
</Separator>
<form className="grid" onSubmit={handleSubmit}>
<Field
control={form.control}
name="email"
label={t('signIn_field_email')}
render={({ field }) => (
<Input type="email" placeholder="[email protected]" {...field} />
)}
/>
<Field
control={form.control}
name="password"
label={t('signIn_field_password')}
render={({ field }) => <Input type="password" {...field} />}
error={form.formError}
/>
<Button type="submit">{t('signIn_submit')}</Button>
arkadiuszbachorski marked this conversation as resolved.
Show resolved Hide resolved
</form>
</div>
)
}
Loading
Loading