Skip to content

Commit 800a2e2

Browse files
committed
fix(auth): resolve review findings and complete layout isolation
1 parent 38e886d commit 800a2e2

17 files changed

Lines changed: 177 additions & 123 deletions

File tree

‎apps/sim/app/(auth)/components/auth-navigation.tsx‎

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import { AuthLinkButton } from '@sim/auth-ui'
44
import { usePathname, useSearchParams } from 'next/navigation'
5-
import { getEnv, isFalsy } from '@/lib/core/config/env'
65
import { validateCallbackUrl } from '@/lib/core/security/input-validation'
76
import { buildAuthCrossLink, resolveAuthRedirect } from '@/app/(auth)/auth-redirect'
87

@@ -15,11 +14,7 @@ export function AuthNavigation({ registrationDisabled }: AuthNavigationProps) {
1514
const pathname = usePathname()
1615
const params = useSearchParams()
1716
if (pathname !== '/login' && pathname !== '/signup') return null
18-
if (
19-
pathname === '/login' &&
20-
(registrationDisabled || isFalsy(getEnv('NEXT_PUBLIC_EMAIL_PASSWORD_SIGNUP_ENABLED')))
21-
)
22-
return null
17+
if (pathname === '/login' && registrationDisabled) return null
2318
const { rawCallbackUrl: callback, isInviteFlow } = resolveAuthRedirect({
2419
redirect: params.get('redirect'),
2520
callbackUrl: params.get('callbackUrl'),

‎apps/sim/app/(auth)/components/social-login-buttons.tsx‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,17 @@ export function SocialLoginButtons({
5858
setError(null)
5959
setLoadingProvider(provider)
6060
try {
61-
await startSocialSignIn({ provider, view, surface: 'auth_page', callbackURL })
61+
const result = await startSocialSignIn({
62+
provider,
63+
view,
64+
surface: 'auth_page',
65+
callbackURL,
66+
})
67+
if (result.error) {
68+
throw new Error(
69+
result.error.message || `Could not start ${label} sign-in. Please try again.`
70+
)
71+
}
6272
} catch (err) {
6373
logger.error(`${label} sign-in failed`, { error: getErrorMessage(err) })
6474
setError(getErrorMessage(err, `Could not start ${label} sign-in. Please try again.`))

‎apps/sim/app/cli/auth/cli-auth-view.tsx‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,9 @@ export function CliAuthView() {
8989
const suggested = workspaces.data?.workspaces.some((w) => w.id === request.suggestedWorkspaceId)
9090
? request.suggestedWorkspaceId
9191
: null
92-
const workspaceId = selected ?? suggested ?? workspaces.data?.lastActiveWorkspaceId ?? null
92+
const workspaceId = workspaces.isError
93+
? null
94+
: (selected ?? suggested ?? workspaces.data?.lastActiveWorkspaceId ?? null)
9395
const chosen = workspaces.data?.workspaces.find((w) => w.id === workspaceId)
9496

9597
return (

‎apps/sim/app/cli/auth/loading.tsx‎

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,18 @@
1-
import { AuthSkeleton as Skeleton } from '@sim/auth-ui'
1+
import { AuthStack, AuthSkeleton as Skeleton } from '@sim/auth-ui'
22
import { AuthShell } from '@/app/(auth)/components'
33

4-
/**
5-
* Consent-card skeleton, shared by the route fallback and `page.tsx`'s Suspense.
6-
*
7-
* Bars mirror the card's boxes so hydration doesn't shift the layout: a
8-
* one-line heading, a description that wraps to two in the 400px column, then
9-
* the 70px pairing panel (28px type + `py-5` + 1px border) and the `h-9` button.
10-
*/
4+
/** Mirrors the terminal consent header, pairing code, and approval control. */
115
export function CliAuthLoading() {
126
return (
13-
<div className='au:flex au:w-full au:flex-col au:items-center'>
14-
<Skeleton className='au:h-[38px] au:w-[280px] au:rounded-[4px]' />
15-
<Skeleton className='au:mt-1 au:h-[23px] au:w-[360px] au:rounded-[4px]' />
16-
<Skeleton className='au:h-[23px] au:w-[240px] au:rounded-[4px]' />
17-
<Skeleton className='au:h-[70px] au:w-full au:rounded-[10px]' />
18-
<Skeleton className='au:h-9 au:w-full au:rounded-[10px]' />
19-
</div>
7+
<AuthStack>
8+
<div className='au:space-y-1'>
9+
<Skeleton shape='title' className='au:w-[280px]' />
10+
<Skeleton shape='label' className='au:w-[360px]' />
11+
<Skeleton shape='label' className='au:w-[240px]' />
12+
</div>
13+
<Skeleton />
14+
<Skeleton />
15+
</AuthStack>
2016
)
2117
}
2218

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
import { AuthSkeleton as Skeleton } from '@sim/auth-ui'
1+
import { AuthStack, AuthSkeleton as Skeleton } from '@sim/auth-ui'
22

33
export default function InviteLoading() {
44
return (
5-
<div className='au:flex au:flex-col au:items-center au:justify-center'>
6-
<Skeleton className='au:size-[48px] au:rounded-[12px]' />
7-
<Skeleton className='au:mt-[16px] au:h-[24px] au:w-[200px] au:rounded-[4px]' />
8-
<Skeleton className='au:mt-[8px] au:h-[14px] au:w-[280px] au:rounded-[4px]' />
9-
<Skeleton className='au:mt-[4px] au:h-[14px] au:w-[240px] au:rounded-[4px]' />
10-
<Skeleton className='au:mt-[24px] au:h-[44px] au:w-[200px] au:rounded-[10px]' />
11-
</div>
5+
<AuthStack>
6+
<div className='au:space-y-1'>
7+
<Skeleton shape='title' className='au:w-[200px]' />
8+
<Skeleton shape='label' className='au:w-[280px]' />
9+
<Skeleton shape='label' className='au:w-[240px]' />
10+
</div>
11+
<Skeleton />
12+
</AuthStack>
1213
)
1314
}
Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
import { Skeleton } from '@sim/emcn'
1+
import { AuthSkeleton, AuthStack } from '@sim/auth-ui'
2+
import { InviteLayout } from '@/app/invite/components'
23

34
export default function UnsubscribeLoading() {
45
return (
5-
<div className='flex flex-col items-center justify-center'>
6-
<Skeleton className='size-[48px] rounded-[12px]' />
7-
<Skeleton className='mt-[16px] h-[24px] w-[180px] rounded-[4px]' />
8-
<Skeleton className='mt-[8px] h-[14px] w-[300px] rounded-[4px]' />
9-
<Skeleton className='mt-[4px] h-[14px] w-[260px] rounded-[4px]' />
10-
<Skeleton className='mt-[24px] h-[44px] w-[200px] rounded-[10px]' />
11-
</div>
6+
<InviteLayout>
7+
<AuthStack>
8+
<div className='au:space-y-1'>
9+
<AuthSkeleton shape='title' className='au:w-[180px]' />
10+
<AuthSkeleton shape='label' className='au:w-[300px]' />
11+
</div>
12+
<AuthSkeleton />
13+
</AuthStack>
14+
</InviteLayout>
1215
)
1316
}

‎apps/sim/app/unsubscribe/unsubscribe.tsx‎

Lines changed: 37 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
'use client'
22

33
import { Suspense } from 'react'
4-
import { Chip, cn, Loader } from '@sim/emcn'
4+
import { AuthHeader, AuthButton as Chip, AuthLoader as Loader } from '@sim/auth-ui'
55
import { getErrorMessage } from '@sim/utils/errors'
66
import { useSearchParams } from 'next/navigation'
77
import type { UnsubscribeType } from '@/lib/api/contracts/user'
88
import { AuthSubmitButton } from '@/app/(auth)/components'
9-
import { AUTH_BUTTON_CLASS } from '@/app/(auth)/components/constants'
109
import { InviteLayout } from '@/app/invite/components'
1110
import { useUnsubscribe, useUnsubscribeMutation } from '@/hooks/queries/unsubscribe'
1211

@@ -39,12 +38,9 @@ function UnsubscribeContent() {
3938
if (loading) {
4039
return (
4140
<InviteLayout>
42-
<div className='space-y-1 text-center'>
43-
<h1 className={'text-[32px] text-[var(--text-primary)] tracking-tight'}>Loading</h1>
44-
<p className={'text-[var(--text-muted)] text-md'}>Validating your unsubscribe link…</p>
45-
</div>
46-
<div className={'mt-8 flex w-full items-center justify-center py-8'}>
47-
<Loader className='size-8 text-[var(--text-muted)]' animate />
41+
<AuthHeader title='Loading' description='Validating your unsubscribe link…' />
42+
<div className='au:mt-[var(--auth-group-gap)] au:flex au:w-full au:items-center au:justify-center au:py-8'>
43+
<Loader className='au:size-8 au:text-[var(--auth-secondary)]' animate />
4844
</div>
4945
</InviteLayout>
5046
)
@@ -53,16 +49,11 @@ function UnsubscribeContent() {
5349
if (error) {
5450
return (
5551
<InviteLayout>
56-
<div className='space-y-1 text-center'>
57-
<h1 className={'text-[32px] text-[var(--text-primary)] tracking-tight'}>
58-
Invalid Unsubscribe Link
59-
</h1>
60-
<p className={'text-[var(--text-muted)] text-md'}>{error}</p>
61-
</div>
52+
<AuthHeader title='Invalid unsubscribe link' description={error} />
6253

63-
<div className={'mt-8 w-full max-w-[410px] space-y-3'}>
54+
<div className='au:mt-[var(--auth-group-gap)] au:w-full au:space-y-3'>
6455
<AuthSubmitButton type='button' onClick={() => window.history.back()} loadingLabel=''>
65-
Go Back
56+
Go back
6657
</AuthSubmitButton>
6758
</div>
6859
</InviteLayout>
@@ -72,17 +63,12 @@ function UnsubscribeContent() {
7263
if (data?.isTransactional) {
7364
return (
7465
<InviteLayout>
75-
<div className='space-y-1 text-center'>
76-
<h1 className={'text-[32px] text-[var(--text-primary)] tracking-tight'}>
77-
Important Account Emails
78-
</h1>
79-
<p className={'text-[var(--text-muted)] text-md'}>
80-
Transactional emails like password resets, account confirmations, and security alerts
81-
cannot be unsubscribed from as they contain essential information for your account.
82-
</p>
83-
</div>
66+
<AuthHeader
67+
title='Important account emails'
68+
description='Transactional emails like password resets, account confirmations, and security alerts cannot be unsubscribed from as they contain essential information for your account.'
69+
/>
8470

85-
<div className={'mt-8 w-full max-w-[410px] space-y-3'}>
71+
<div className='au:mt-[var(--auth-group-gap)] au:w-full au:space-y-3'>
8672
<AuthSubmitButton type='button' onClick={() => window.close()} loadingLabel=''>
8773
Close
8874
</AuthSubmitButton>
@@ -94,17 +80,12 @@ function UnsubscribeContent() {
9480
if (unsubscribed) {
9581
return (
9682
<InviteLayout>
97-
<div className='space-y-1 text-center'>
98-
<h1 className={'text-[32px] text-[var(--text-primary)] tracking-tight'}>
99-
Successfully Unsubscribed
100-
</h1>
101-
<p className={'text-[var(--text-muted)] text-md'}>
102-
You have been unsubscribed from our emails. You will stop receiving emails within 48
103-
hours.
104-
</p>
105-
</div>
83+
<AuthHeader
84+
title='Successfully unsubscribed'
85+
description='You have been unsubscribed from our emails. You will stop receiving emails within 48 hours.'
86+
/>
10687

107-
<div className={'mt-8 w-full max-w-[410px] space-y-3'}>
88+
<div className='au:mt-[var(--auth-group-gap)] au:w-full au:space-y-3'>
10889
<AuthSubmitButton type='button' onClick={() => window.close()} loadingLabel=''>
10990
Close
11091
</AuthSubmitButton>
@@ -117,17 +98,17 @@ function UnsubscribeContent() {
11798

11899
return (
119100
<InviteLayout>
120-
<div className='space-y-1 text-center'>
121-
<h1 className={'text-[32px] text-[var(--text-primary)] tracking-tight'}>
122-
Email Preferences
123-
</h1>
124-
<p className={'text-[var(--text-muted)] text-md'}>
125-
Choose which emails you'd like to stop receiving.
101+
<div className='au:space-y-1'>
102+
<AuthHeader
103+
title='Email preferences'
104+
description="Choose which emails you'd like to stop receiving."
105+
/>
106+
<p className='au:break-all au:text-[var(--auth-secondary)] au:text-control'>
107+
{data?.email}
126108
</p>
127-
<p className={'text-[var(--text-muted)] text-sm'}>{data?.email}</p>
128109
</div>
129110

130-
<div className={'mt-8 w-full max-w-[410px] space-y-3'}>
111+
<div className='au:mt-[var(--auth-group-gap)] au:w-full au:space-y-3'>
131112
<AuthSubmitButton
132113
type='button'
133114
onClick={() => handleUnsubscribe('all')}
@@ -140,8 +121,10 @@ function UnsubscribeContent() {
140121
: 'Unsubscribe from All Marketing Emails'}
141122
</AuthSubmitButton>
142123

143-
<div className='py-2 text-center'>
144-
<span className={'text-[var(--text-muted)] text-sm'}>or choose specific types</span>
124+
<div className='au:py-2 au:text-center'>
125+
<span className='au:text-[var(--auth-secondary)] au:text-caption'>
126+
or choose specific types
127+
</span>
145128
</div>
146129

147130
<Chip
@@ -152,7 +135,7 @@ function UnsubscribeContent() {
152135
isAlreadyUnsubscribedFromAll ||
153136
data?.currentPreferences.unsubscribeMarketing
154137
}
155-
className={cn(AUTH_BUTTON_CLASS, 'border border-[var(--border-1)]')}
138+
variant='outline'
156139
>
157140
{data?.currentPreferences.unsubscribeMarketing
158141
? 'Unsubscribed from Marketing'
@@ -167,7 +150,7 @@ function UnsubscribeContent() {
167150
isAlreadyUnsubscribedFromAll ||
168151
data?.currentPreferences.unsubscribeUpdates
169152
}
170-
className={cn(AUTH_BUTTON_CLASS, 'border border-[var(--border-1)]')}
153+
variant='outline'
171154
>
172155
{data?.currentPreferences.unsubscribeUpdates
173156
? 'Unsubscribed from Updates'
@@ -182,16 +165,16 @@ function UnsubscribeContent() {
182165
isAlreadyUnsubscribedFromAll ||
183166
data?.currentPreferences.unsubscribeNotifications
184167
}
185-
className={cn(AUTH_BUTTON_CLASS, 'border border-[var(--border-1)]')}
168+
variant='outline'
186169
>
187170
{data?.currentPreferences.unsubscribeNotifications
188171
? 'Unsubscribed from Notifications'
189172
: 'Unsubscribe from Notifications'}
190173
</Chip>
191174
</div>
192175

193-
<div className={'mt-6 max-w-[410px] text-center'}>
194-
<p className='text-[var(--text-muted)] text-small'>
176+
<div className='au:mt-[var(--auth-group-gap)]'>
177+
<p className='au:text-[var(--auth-secondary)] au:text-caption'>
195178
You'll continue receiving important account emails like password resets and security
196179
alerts.
197180
</p>
@@ -205,12 +188,9 @@ export default function Unsubscribe() {
205188
<Suspense
206189
fallback={
207190
<InviteLayout>
208-
<div className='space-y-1 text-center'>
209-
<h1 className={'text-[32px] text-[var(--text-primary)] tracking-tight'}>Loading</h1>
210-
<p className={'text-[var(--text-muted)] text-md'}>Validating your unsubscribe link…</p>
211-
</div>
212-
<div className={'mt-8 flex w-full items-center justify-center py-8'}>
213-
<Loader className='size-8 text-[var(--text-muted)]' animate />
191+
<AuthHeader title='Loading' description='Validating your unsubscribe link…' />
192+
<div className='au:mt-[var(--auth-group-gap)] au:flex au:w-full au:items-center au:justify-center au:py-8'>
193+
<Loader className='au:size-8 au:text-[var(--auth-secondary)]' animate />
214194
</div>
215195
</InviteLayout>
216196
}

‎apps/sim/components/branded-layout.tsx‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,20 @@ export function BrandedLayout({ children }: BrandedLayoutProps) {
1616
useEffect(() => {
1717
const config = getBrandConfig()
1818

19-
// Update document title
2019
if (config.name !== 'Sim') {
2120
document.title = config.name
2221
}
2322

24-
// Update favicon
2523
if (config.faviconUrl) {
2624
const faviconLink = document.querySelector("link[rel*='icon']") as HTMLLinkElement
2725
if (faviconLink) {
2826
faviconLink.href = config.faviconUrl
2927
}
3028
}
29+
}, [])
3130

31+
useEffect(() => {
32+
const config = getBrandConfig()
3233
// Public resource routes can move between an Auth gate and their branded viewer.
3334
const resourceRoute = pathname.startsWith('/f/') || pathname.startsWith('/chat/')
3435
const syncCustomCss = () => {

‎apps/sim/lib/navigation/auth-surfaces.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const AUTH_SURFACE_ROOTS = [
99
'/oauth',
1010
'/oauth-error',
1111
'/invite',
12+
'/unsubscribe',
1213
'/desktop/auth',
1314
'/desktop/done',
1415
'/desktop/connect',

‎apps/sim/package.json‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,6 @@
217217
"https-proxy-agent": "7.0.6",
218218
"idb-keyval": "6.3.0",
219219
"imapflow": "1.7.8",
220-
"input-otp": "^1.5.0",
221220
"ioredis": "^5.6.0",
222221
"isolated-vm": "6.2.0",
223222
"jose": "6.2.3",

0 commit comments

Comments
 (0)