Skip to content

Commit

Permalink
feat(platform): Add Google OAuth (#689)
Browse files Browse the repository at this point in the history
  • Loading branch information
rajdip-b authored Feb 6, 2025
1 parent 110e265 commit ad3a3d2
Show file tree
Hide file tree
Showing 12 changed files with 553 additions and 278 deletions.
14 changes: 4 additions & 10 deletions apps/api/src/common/redirect.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
import { User } from '@prisma/client'
import { Response } from 'express'

const platformFrontendUrl = process.env.PLATFORM_FRONTEND_URL
const platformOAuthSuccessRedirectPath =
process.env.PLATFORM_OAUTH_SUCCESS_REDIRECT_PATH
const platformOAuthFailureRedirectPath =
process.env.PLATFORM_OAUTH_FAILURE_REDIRECT_PATH
const platformOAuthSuccessRedirectUrl = `${platformFrontendUrl}${platformOAuthSuccessRedirectPath}`
const platformOAuthFailureRedirectUrl = `${platformFrontendUrl}${platformOAuthFailureRedirectPath}`

/* istanbul ignore next */
export function sendOAuthFailureRedirect(response: Response, reason: string) {
response
.status(302)
.redirect(`${platformOAuthFailureRedirectUrl}?reason=${reason}`)
.redirect(
`${process.env.PLATFORM_FRONTEND_URL}${process.env.PLATFORM_OAUTH_FAILURE_REDIRECT_PATH}?reason=${reason}`
)
}

/* istanbul ignore next */
export function sendOAuthSuccessRedirect(response: Response, user: User) {
response
.status(302)
.redirect(
`${platformOAuthSuccessRedirectUrl}?data=${encodeURIComponent(
`${process.env.PLATFORM_FRONTEND_URL}${process.env.PLATFORM_OAUTH_SUCCESS_REDIRECT_PATH}?data=${encodeURIComponent(
JSON.stringify(user)
)}`
)
Expand Down
76 changes: 41 additions & 35 deletions apps/platform/src/app/(main)/(settings)/settings/@profile/page.tsx
Original file line number Diff line number Diff line change
@@ -1,61 +1,67 @@
'use client'
import React, { useCallback, useEffect, useState } from 'react'
import React, { useCallback, useState } from 'react'
import { toast } from 'sonner'
import { useAtom } from 'jotai'
import InputLoading from './loading'
import { Input } from '@/components/ui/input'
import { Separator } from '@/components/ui/separator'
import ControllerInstance from '@/lib/controller-instance'
import { Button } from '@/components/ui/button'
import { userAtom } from '@/store'

function ProfilePage(): React.JSX.Element {
const [user, setUser] = useAtom(userAtom)

const [isLoading, setIsLoading] = useState<boolean>(true)
const [userData, setUserData] = useState({
email: '',
name: '',
profilePictureUrl: ''
})
const [isModified, setIsModified] = useState<boolean>(false)
const [email, setEmail] = useState<string>('')

const updateSelf = useCallback(async () => {
toast.loading('Updating profile...')
setIsLoading(true)
try {
await ControllerInstance.getInstance().userController.updateSelf(
{
name: userData.name,
email: userData.email === email ? undefined : email
},
{}
)
toast.success('Profile updated successfully')
const { success, error, data } =
await ControllerInstance.getInstance().userController.updateSelf({
name: userData.name === user?.name ? undefined : userData.name,
email: userData.email === user?.email ? undefined : userData.email
})

toast.dismiss()

if (success && data) {
toast.success('Profile updated successfully!')
setUser(data)
} else {
toast.error('Something went wrong', {
description: (
<p className="text-xs text-red-300">
Something went wrong updating the profile. Check console for more
info.
</p>
)
})
// eslint-disable-next-line no-console -- we need to log the error
console.error(error)
}
} catch (error) {
toast.error('Something went wrong', {
description: (
<p className="text-xs text-red-300">
Something went wrong updating the profile. Check console for more
info.
</p>
)
})
// eslint-disable-next-line no-console -- we need to log the error
console.error(error)
}
setIsModified(false)
}, [userData, email])

useEffect(() => {
ControllerInstance.getInstance()
.userController.getSelf()
.then(({ data, success, error }) => {
if (success && data) {
setUserData({
email: data.email,
name: data.name,
profilePictureUrl: data.profilePictureUrl || ''
})
setEmail(data.email)
setIsLoading(false)
} else {
// eslint-disable-next-line no-console -- we need to log the error
console.error(error)
}
})
.catch((error) => {
// eslint-disable-next-line no-console -- we need to log the error
console.error(error)
})
}, [])
setIsLoading(false)
}, [userData.name, userData.email, user?.name, user?.email, setUser])

return (
<main className="flex flex-col gap-y-10">
Expand Down Expand Up @@ -109,7 +115,7 @@ function ProfilePage(): React.JSX.Element {
// setEmail(e.target.value)
// }}
placeholder="email"
value={email}
value={user?.email}
/>
)}
</div>
Expand Down
26 changes: 24 additions & 2 deletions apps/platform/src/app/(main)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@ import {
selectedWorkspaceAtom,
projectsOfWorkspaceAtom,
deleteProjectOpenAtom,
selectedProjectAtom
selectedProjectAtom,
userAtom
} from '@/store'
import EditProjectSheet from '@/components/dashboard/project/editProjectSheet'
import { Button } from '@/components/ui/button'
import ConfirmDeleteProject from '@/components/dashboard/project/confirmDeleteProject'

export default function Index(): JSX.Element {
const [loading, setLoading] = useState<boolean>(false)
const setUser = useSetAtom(userAtom)

const setIsCreateProjectDialogOpen = useSetAtom(createProjectOpenAtom)
const selectedWorkspace = useAtomValue(selectedWorkspaceAtom)
Expand Down Expand Up @@ -60,9 +62,29 @@ export default function Index(): JSX.Element {
setLoading(false)
}, [selectedWorkspace, setProjects])

const getSelf = useCallback(async () => {
const { success, error, data } =
await ControllerInstance.getInstance().userController.getSelf()
if (success && data) {
setUser(data)
} else {
toast.error('Something went wrong!', {
description: (
<p className="text-xs text-red-300">
Something went wrong while fetching user. Check console for more
info.
</p>
)
})
// eslint-disable-next-line no-console -- we need to log the error
console.error(error)
}
}, [setUser])

useEffect(() => {
getAllProjects()
}, [getAllProjects, selectedWorkspace, setProjects])
getSelf()
}, [getAllProjects, selectedWorkspace, setProjects, getSelf])

return (
<div className="flex flex-col gap-4">
Expand Down
91 changes: 53 additions & 38 deletions apps/platform/src/app/auth/account-details/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,85 @@
import { useEffect, useState } from 'react'
import { z } from 'zod'
import { useRouter } from 'next/navigation'
import { useAtomValue } from 'jotai'
import { useAtom } from 'jotai'
import Cookies from 'js-cookie'
import { LoadingSVG } from '@public/svg/shared'
import { KeyshadeBigSVG } from '@public/svg/auth'
import { toast } from 'sonner'
import { GeistSansFont, NunitoSansFont } from '@/fonts'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { authEmailAtom } from '@/store'
import { userAtom } from '@/store'
import ControllerInstance from '@/lib/controller-instance'

export default function AuthDetailsPage(): React.JSX.Element {
const email = useAtomValue(authEmailAtom)
const [name, setName] = useState<string>('')
const [user, setUser] = useAtom(userAtom)

const [name, setName] = useState<string>(user?.name ?? '')
const [isLoading, setIsLoading] = useState<boolean>(false)
const router = useRouter()

useEffect(() => {
if (email === '') {
if (!user?.email) {
router.push('/auth')
}
}, [email, router])

const handleDoneUpdateSubmit = async (userName: string): Promise<void> => {
const resultName = z.string().safeParse(userName)
setName(user?.name ?? '')
}, [router, user?.email, user?.name])

const handleDoneUpdateSubmit = async (): Promise<void> => {
const resultName = z.string().safeParse(name)

if (!resultName.success) {
setIsLoading(false)
return
}
setIsLoading(true)

toast.loading('Updating profile details...')
try {
const response = await fetch(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/api/user`,
{
method: 'PUT',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ name: userName, isOnboardingFinished: true })
}
)
if (response.status === 200) {
Cookies.set('isOnboardingFinished', 'true')
router.push('/')
const { success, error, data } =
await ControllerInstance.getInstance().userController.updateSelf({
name,
isOnboardingFinished: true
})

toast.dismiss()
if (success && data) {
toast.success('Profile details successfully updated')

Cookies.set(
'isOnboardingFinished',
data.isOnboardingFinished ? 'true' : 'false'
)

setUser(data)
window.location.reload()
} else {
toast.error('Something went wrong!', {
description: (
<p className="text-xs text-red-300">
Something went wrong updating your profile details. Check console
for more info.
</p>
)
})
// eslint-disable-next-line no-console -- we need to log the error
console.error(error)
}
} catch (error) {
toast.error('Something went wrong!', {
description: (
<p className="text-xs text-red-300">
Something went wrong updating your profile details. Check console
for more info.
</p>
)
})
// eslint-disable-next-line no-console -- we need to log the error
console.error(`Failed to update user details: ${error}`)
setIsLoading(false)
}
setIsLoading(false)
}

return (
Expand Down Expand Up @@ -83,27 +111,14 @@ export default function AuthDetailsPage(): React.JSX.Element {
setName(e.target.value)
}}
placeholder="Enter your name"
value={name}
/>
</div>
{/* <div>
<span className={`${NunitoSansFont.className} text-sm`}>
Your Workspace Name
</span>
<Input placeholder="Enter your workspace name" />
</div> */}
{/* <div>
<span className={`${NunitoSansFont.className} text-sm`}>
Organization Mail
</span>
<Input placeholder="Enter your mail" type="email" />
</div> */}
</div>
<Button
className="w-full"
disabled={isLoading}
onClick={() => {
void handleDoneUpdateSubmit(name)
}}
onClick={handleDoneUpdateSubmit}
>
{isLoading ? <LoadingSVG className="w-10" /> : 'Done'}
</Button>
Expand Down
Loading

0 comments on commit ad3a3d2

Please sign in to comment.