Skip to content
Closed
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
4 changes: 4 additions & 0 deletions apps/sim/app/(auth)/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import AuthBackground from './auth-background/auth-background'
import OauthButtons from './oauth-buttons/oauth-buttons'

export { OauthButtons, AuthBackground }
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
'use client'

import { useEffect, useState } from 'react'
import { GithubIcon, GoogleIcon } from '@/components/icons'
import { GithubIcon, GoogleIcon } from '@/components/icons/icons'
import { Button } from '@/components/ui/button'
import { client } from '@/lib/auth-client'
import { inter } from '@/app/fonts/inter'
import { inter } from '@/app/styles/fonts/inter'

interface SocialLoginButtonsProps {
interface OauthButtonsProps {
githubAvailable: boolean
googleAvailable: boolean
callbackURL?: string
isProduction: boolean
}

export function SocialLoginButtons({
export default function OauthButtons({
githubAvailable,
googleAvailable,
callbackURL = '/workspace',
isProduction,
}: SocialLoginButtonsProps) {
}: OauthButtonsProps) {
const [isGithubLoading, setIsGithubLoading] = useState(false)
const [isGoogleLoading, setIsGoogleLoading] = useState(false)
const [mounted, setMounted] = useState(false)
Expand Down
12 changes: 0 additions & 12 deletions apps/sim/app/(auth)/components/oauth-provider-checker.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion apps/sim/app/(auth)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { useEffect } from 'react'
import Nav from '@/app/(landing)/components/nav/nav'
import AuthBackground from './components/auth-background'
import AuthBackground from './components/auth-background/auth-background'

// Helper to detect if a color is dark
function isColorDark(hexColor: string): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import { client } from '@/lib/auth-client'
import { quickValidateEmail } from '@/lib/email/validation'
import { createLogger } from '@/lib/logs/console/logger'
import { cn } from '@/lib/utils'
import { SocialLoginButtons } from '@/app/(auth)/components/social-login-buttons'
import { inter } from '@/app/fonts/inter'
import { soehne } from '@/app/fonts/soehne/soehne'
import { OauthButtons } from '@/app/(auth)/components'
import { inter } from '@/app/styles/fonts/inter'
import { soehne } from '@/app/styles/fonts/soehne/soehne'

const logger = createLogger('LoginForm')

Expand Down Expand Up @@ -85,7 +85,7 @@ const validatePassword = (passwordValue: string): string[] => {
return errors
}

export default function LoginPage({
function LoginFormContent({
githubAvailable,
googleAvailable,
isProduction,
Expand Down Expand Up @@ -347,12 +347,13 @@ export default function LoginPage({

setResetStatus({
type: 'success',
message: 'Password reset link sent to your email',
message: '',
})

setTimeout(() => {
setForgotPasswordOpen(false)
setResetStatus({ type: null, message: '' })
setForgotPasswordEmail('')
}, 2000)
} catch (error) {
logger.error('Error requesting password reset:', { error })
Expand Down Expand Up @@ -476,7 +477,7 @@ export default function LoginPage({
</div>
)}

<SocialLoginButtons
<OauthButtons
googleAvailable={googleAvailable}
githubAvailable={githubAvailable}
isProduction={isProduction}
Expand Down Expand Up @@ -551,22 +552,41 @@ export default function LoginPage({
</div>
)}
</div>
{resetStatus.type === 'success' && (
<div className='mt-1 space-y-1 text-[#4CAF50] text-xs'>
<p>{resetStatus.message}</p>
</div>
)}
<Button
type='button'
onClick={handleForgotPassword}
className={`${buttonClass} w-full rounded-[10px] border font-medium text-[15px] text-white transition-all duration-200`}
disabled={isSubmittingReset}
disabled={isSubmittingReset || resetStatus.type === 'success'}
>
{isSubmittingReset ? 'Sending...' : 'Send Reset Link'}
{isSubmittingReset
? 'Sending...'
: resetStatus.type === 'success'
? 'Sent!'
: 'Send Reset Link'}
</Button>
</div>
</DialogContent>
</Dialog>
</>
)
}

interface LoginFormProps {
githubAvailable: boolean
googleAvailable: boolean
isProduction: boolean
}

export default function LoginForm({
githubAvailable,
googleAvailable,
isProduction,
}: LoginFormProps) {
return (
<LoginFormContent
githubAvailable={githubAvailable}
googleAvailable={googleAvailable}
isProduction={isProduction}
/>
)
}
20 changes: 20 additions & 0 deletions apps/sim/app/(auth)/login/login-server.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { env } from '@/lib/env'
import { isProd } from '@/lib/environment'
import LoginForm from '@/app/(auth)/login/login-client'

/**
* Server component wrapper for login page
* Handles server-side OAuth provider checks
*/
export default function LoginServerWrapper() {
const githubAvailable = !!(env.GITHUB_CLIENT_ID && env.GITHUB_CLIENT_SECRET)
const googleAvailable = !!(env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET)

return (
<LoginForm
githubAvailable={githubAvailable}
googleAvailable={googleAvailable}
isProduction={isProd}
/>
)
}
17 changes: 1 addition & 16 deletions apps/sim/app/(auth)/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,2 @@
import { getOAuthProviderStatus } from '@/app/(auth)/components/oauth-provider-checker'
import LoginForm from '@/app/(auth)/login/login-form'

// Force dynamic rendering to avoid prerender errors with search params
export { default } from '@/app/(auth)/login/login-server'
export const dynamic = 'force-dynamic'

export default async function LoginPage() {
const { githubAvailable, googleAvailable, isProduction } = await getOAuthProviderStatus()

return (
<LoginForm
githubAvailable={githubAvailable}
googleAvailable={googleAvailable}
isProduction={isProduction}
/>
)
}
119 changes: 2 additions & 117 deletions apps/sim/app/(auth)/reset-password/page.tsx
Original file line number Diff line number Diff line change
@@ -1,117 +1,2 @@
'use client'

import { Suspense, useEffect, useState } from 'react'
import Link from 'next/link'
import { useRouter, useSearchParams } from 'next/navigation'
import { createLogger } from '@/lib/logs/console/logger'
import { SetNewPasswordForm } from '@/app/(auth)/reset-password/reset-password-form'
import { inter } from '@/app/fonts/inter'
import { soehne } from '@/app/fonts/soehne/soehne'

const logger = createLogger('ResetPasswordPage')

function ResetPasswordContent() {
const router = useRouter()
const searchParams = useSearchParams()
const token = searchParams.get('token')

const [isSubmitting, setIsSubmitting] = useState(false)
const [statusMessage, setStatusMessage] = useState<{
type: 'success' | 'error' | null
text: string
}>({
type: null,
text: '',
})

useEffect(() => {
if (!token) {
setStatusMessage({
type: 'error',
text: 'Invalid or missing reset token. Please request a new password reset link.',
})
}
}, [token])

const handleResetPassword = async (password: string) => {
try {
setIsSubmitting(true)
setStatusMessage({ type: null, text: '' })

const response = await fetch('/api/auth/reset-password', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
token,
newPassword: password,
}),
})

if (!response.ok) {
const errorData = await response.json()
throw new Error(errorData.message || 'Failed to reset password')
}

setStatusMessage({
type: 'success',
text: 'Password reset successful! Redirecting to login...',
})

setTimeout(() => {
router.push('/login?resetSuccess=true')
}, 1500)
} catch (error) {
logger.error('Error resetting password:', { error })
setStatusMessage({
type: 'error',
text: error instanceof Error ? error.message : 'Failed to reset password',
})
} finally {
setIsSubmitting(false)
}
}

return (
<>
<div className='space-y-1 text-center'>
<h1 className={`${soehne.className} font-medium text-[32px] text-black tracking-tight`}>
Reset your password
</h1>
<p className={`${inter.className} font-[380] text-[16px] text-muted-foreground`}>
Enter a new password for your account
</p>
</div>

<div className={`${inter.className} mt-8`}>
<SetNewPasswordForm
token={token}
onSubmit={handleResetPassword}
isSubmitting={isSubmitting}
statusType={statusMessage.type}
statusMessage={statusMessage.text}
/>
</div>

<div className={`${inter.className} pt-6 text-center font-light text-[14px]`}>
<Link
href='/login'
className='font-medium text-[var(--brand-accent-hex)] underline-offset-4 transition hover:text-[var(--brand-accent-hover-hex)] hover:underline'
>
Back to login
</Link>
</div>
</>
)
}

export default function ResetPasswordPage() {
return (
<Suspense
fallback={<div className='flex h-screen items-center justify-center'>Loading...</div>}
>
<ResetPasswordContent />
</Suspense>
)
}
export { default } from '@/app/(auth)/reset-password/reset-password'
export const dynamic = 'force-dynamic'
Loading