Skip to content

Commit e2ccefb

Browse files
authored
improvement(ui): use BrandedButton and BrandedLink components (#2930)
- Refactor auth forms to use BrandedButton component - Add BrandedLink component for changelog page - Reduce code duplication in login, signup, reset-password forms - Update star count default value
1 parent 103b31a commit e2ccefb

File tree

8 files changed

+78
-109
lines changed

8 files changed

+78
-109
lines changed

apps/sim/app/(auth)/login/login-form.tsx

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22

33
import { useEffect, useState } from 'react'
44
import { createLogger } from '@sim/logger'
5-
import { ArrowRight, ChevronRight, Eye, EyeOff } from 'lucide-react'
5+
import { Eye, EyeOff } from 'lucide-react'
66
import Link from 'next/link'
77
import { useRouter, useSearchParams } from 'next/navigation'
8-
import { Button } from '@/components/ui/button'
98
import {
109
Dialog,
1110
DialogContent,
@@ -22,6 +21,7 @@ import { getBaseUrl } from '@/lib/core/utils/urls'
2221
import { quickValidateEmail } from '@/lib/messaging/email/validation'
2322
import { inter } from '@/app/_styles/fonts/inter/inter'
2423
import { soehne } from '@/app/_styles/fonts/soehne/soehne'
24+
import { BrandedButton } from '@/app/(auth)/components/branded-button'
2525
import { SocialLoginButtons } from '@/app/(auth)/components/social-login-buttons'
2626
import { SSOLoginButton } from '@/app/(auth)/components/sso-login-button'
2727
import { useBrandedButtonClass } from '@/hooks/use-branded-button-class'
@@ -107,15 +107,13 @@ export default function LoginPage({
107107
const [passwordErrors, setPasswordErrors] = useState<string[]>([])
108108
const [showValidationError, setShowValidationError] = useState(false)
109109
const buttonClass = useBrandedButtonClass()
110-
const [isButtonHovered, setIsButtonHovered] = useState(false)
111110

112111
const [callbackUrl, setCallbackUrl] = useState('/workspace')
113112
const [isInviteFlow, setIsInviteFlow] = useState(false)
114113

115114
const [forgotPasswordOpen, setForgotPasswordOpen] = useState(false)
116115
const [forgotPasswordEmail, setForgotPasswordEmail] = useState('')
117116
const [isSubmittingReset, setIsSubmittingReset] = useState(false)
118-
const [isResetButtonHovered, setIsResetButtonHovered] = useState(false)
119117
const [resetStatus, setResetStatus] = useState<{
120118
type: 'success' | 'error' | null
121119
message: string
@@ -491,24 +489,14 @@ export default function LoginPage({
491489
</div>
492490
</div>
493491

494-
<Button
492+
<BrandedButton
495493
type='submit'
496-
onMouseEnter={() => setIsButtonHovered(true)}
497-
onMouseLeave={() => setIsButtonHovered(false)}
498-
className='group inline-flex w-full items-center justify-center gap-2 rounded-[10px] border border-[#6F3DFA] bg-gradient-to-b from-[#8357FF] to-[#6F3DFA] py-[6px] pr-[10px] pl-[12px] text-[15px] text-white shadow-[inset_0_2px_4px_0_#9B77FF] transition-all'
499494
disabled={isLoading}
495+
loading={isLoading}
496+
loadingText='Signing in'
500497
>
501-
<span className='flex items-center gap-1'>
502-
{isLoading ? 'Signing in...' : 'Sign in'}
503-
<span className='inline-flex transition-transform duration-200 group-hover:translate-x-0.5'>
504-
{isButtonHovered ? (
505-
<ArrowRight className='h-4 w-4' aria-hidden='true' />
506-
) : (
507-
<ChevronRight className='h-4 w-4' aria-hidden='true' />
508-
)}
509-
</span>
510-
</span>
511-
</Button>
498+
Sign in
499+
</BrandedButton>
512500
</form>
513501
)}
514502

@@ -619,25 +607,15 @@ export default function LoginPage({
619607
<p>{resetStatus.message}</p>
620608
</div>
621609
)}
622-
<Button
610+
<BrandedButton
623611
type='button'
624612
onClick={handleForgotPassword}
625-
onMouseEnter={() => setIsResetButtonHovered(true)}
626-
onMouseLeave={() => setIsResetButtonHovered(false)}
627-
className='group inline-flex w-full items-center justify-center gap-2 rounded-[10px] border border-[#6F3DFA] bg-gradient-to-b from-[#8357FF] to-[#6F3DFA] py-[6px] pr-[10px] pl-[12px] text-[15px] text-white shadow-[inset_0_2px_4px_0_#9B77FF] transition-all'
628613
disabled={isSubmittingReset}
614+
loading={isSubmittingReset}
615+
loadingText='Sending'
629616
>
630-
<span className='flex items-center gap-1'>
631-
{isSubmittingReset ? 'Sending...' : 'Send Reset Link'}
632-
<span className='inline-flex transition-transform duration-200 group-hover:translate-x-0.5'>
633-
{isResetButtonHovered ? (
634-
<ArrowRight className='h-4 w-4' aria-hidden='true' />
635-
) : (
636-
<ChevronRight className='h-4 w-4' aria-hidden='true' />
637-
)}
638-
</span>
639-
</span>
640-
</Button>
617+
Send Reset Link
618+
</BrandedButton>
641619
</div>
642620
</DialogContent>
643621
</Dialog>

apps/sim/app/(auth)/reset-password/reset-password-form.tsx

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

33
import { useState } from 'react'
4-
import { ArrowRight, ChevronRight, Eye, EyeOff } from 'lucide-react'
5-
import { Button } from '@/components/ui/button'
4+
import { Eye, EyeOff } from 'lucide-react'
65
import { Input } from '@/components/ui/input'
76
import { Label } from '@/components/ui/label'
87
import { cn } from '@/lib/core/utils/cn'
98
import { inter } from '@/app/_styles/fonts/inter/inter'
10-
import { useBrandedButtonClass } from '@/hooks/use-branded-button-class'
9+
import { BrandedButton } from '@/app/(auth)/components/branded-button'
1110

1211
interface RequestResetFormProps {
1312
email: string
@@ -28,9 +27,6 @@ export function RequestResetForm({
2827
statusMessage,
2928
className,
3029
}: RequestResetFormProps) {
31-
const buttonClass = useBrandedButtonClass()
32-
const [isButtonHovered, setIsButtonHovered] = useState(false)
33-
3430
const handleSubmit = async (e: React.FormEvent) => {
3531
e.preventDefault()
3632
onSubmit(email)
@@ -68,24 +64,14 @@ export function RequestResetForm({
6864
)}
6965
</div>
7066

71-
<Button
67+
<BrandedButton
7268
type='submit'
7369
disabled={isSubmitting}
74-
onMouseEnter={() => setIsButtonHovered(true)}
75-
onMouseLeave={() => setIsButtonHovered(false)}
76-
className='group inline-flex w-full items-center justify-center gap-2 rounded-[10px] border border-[#6F3DFA] bg-gradient-to-b from-[#8357FF] to-[#6F3DFA] py-[6px] pr-[10px] pl-[12px] text-[15px] text-white shadow-[inset_0_2px_4px_0_#9B77FF] transition-all'
70+
loading={isSubmitting}
71+
loadingText='Sending'
7772
>
78-
<span className='flex items-center gap-1'>
79-
{isSubmitting ? 'Sending...' : 'Send Reset Link'}
80-
<span className='inline-flex transition-transform duration-200 group-hover:translate-x-0.5'>
81-
{isButtonHovered ? (
82-
<ArrowRight className='h-4 w-4' aria-hidden='true' />
83-
) : (
84-
<ChevronRight className='h-4 w-4' aria-hidden='true' />
85-
)}
86-
</span>
87-
</span>
88-
</Button>
73+
Send Reset Link
74+
</BrandedButton>
8975
</form>
9076
)
9177
}
@@ -112,8 +98,6 @@ export function SetNewPasswordForm({
11298
const [validationMessage, setValidationMessage] = useState('')
11399
const [showPassword, setShowPassword] = useState(false)
114100
const [showConfirmPassword, setShowConfirmPassword] = useState(false)
115-
const buttonClass = useBrandedButtonClass()
116-
const [isButtonHovered, setIsButtonHovered] = useState(false)
117101

118102
const handleSubmit = async (e: React.FormEvent) => {
119103
e.preventDefault()
@@ -243,24 +227,14 @@ export function SetNewPasswordForm({
243227
)}
244228
</div>
245229

246-
<Button
247-
disabled={isSubmitting || !token}
230+
<BrandedButton
248231
type='submit'
249-
onMouseEnter={() => setIsButtonHovered(true)}
250-
onMouseLeave={() => setIsButtonHovered(false)}
251-
className='group inline-flex w-full items-center justify-center gap-2 rounded-[10px] border border-[#6F3DFA] bg-gradient-to-b from-[#8357FF] to-[#6F3DFA] py-[6px] pr-[10px] pl-[12px] text-[15px] text-white shadow-[inset_0_2px_4px_0_#9B77FF] transition-all'
232+
disabled={isSubmitting || !token}
233+
loading={isSubmitting}
234+
loadingText='Resetting'
252235
>
253-
<span className='flex items-center gap-1'>
254-
{isSubmitting ? 'Resetting...' : 'Reset Password'}
255-
<span className='inline-flex transition-transform duration-200 group-hover:translate-x-0.5'>
256-
{isButtonHovered ? (
257-
<ArrowRight className='h-4 w-4' aria-hidden='true' />
258-
) : (
259-
<ChevronRight className='h-4 w-4' aria-hidden='true' />
260-
)}
261-
</span>
262-
</span>
263-
</Button>
236+
Reset Password
237+
</BrandedButton>
264238
</form>
265239
)
266240
}

apps/sim/app/(auth)/signup/signup-form.tsx

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22

33
import { Suspense, useEffect, useState } from 'react'
44
import { createLogger } from '@sim/logger'
5-
import { ArrowRight, ChevronRight, Eye, EyeOff } from 'lucide-react'
5+
import { Eye, EyeOff } from 'lucide-react'
66
import Link from 'next/link'
77
import { useRouter, useSearchParams } from 'next/navigation'
8-
import { Button } from '@/components/ui/button'
98
import { Input } from '@/components/ui/input'
109
import { Label } from '@/components/ui/label'
1110
import { client, useSession } from '@/lib/auth/auth-client'
@@ -14,6 +13,7 @@ import { cn } from '@/lib/core/utils/cn'
1413
import { quickValidateEmail } from '@/lib/messaging/email/validation'
1514
import { inter } from '@/app/_styles/fonts/inter/inter'
1615
import { soehne } from '@/app/_styles/fonts/soehne/soehne'
16+
import { BrandedButton } from '@/app/(auth)/components/branded-button'
1717
import { SocialLoginButtons } from '@/app/(auth)/components/social-login-buttons'
1818
import { SSOLoginButton } from '@/app/(auth)/components/sso-login-button'
1919
import { useBrandedButtonClass } from '@/hooks/use-branded-button-class'
@@ -97,7 +97,6 @@ function SignupFormContent({
9797
const [redirectUrl, setRedirectUrl] = useState('')
9898
const [isInviteFlow, setIsInviteFlow] = useState(false)
9999
const buttonClass = useBrandedButtonClass()
100-
const [isButtonHovered, setIsButtonHovered] = useState(false)
101100

102101
const [name, setName] = useState('')
103102
const [nameErrors, setNameErrors] = useState<string[]>([])
@@ -476,24 +475,14 @@ function SignupFormContent({
476475
</div>
477476
</div>
478477

479-
<Button
478+
<BrandedButton
480479
type='submit'
481-
onMouseEnter={() => setIsButtonHovered(true)}
482-
onMouseLeave={() => setIsButtonHovered(false)}
483-
className='group inline-flex w-full items-center justify-center gap-2 rounded-[10px] border border-[#6F3DFA] bg-gradient-to-b from-[#8357FF] to-[#6F3DFA] py-[6px] pr-[10px] pl-[12px] text-[15px] text-white shadow-[inset_0_2px_4px_0_#9B77FF] transition-all'
484480
disabled={isLoading}
481+
loading={isLoading}
482+
loadingText='Creating account'
485483
>
486-
<span className='flex items-center gap-1'>
487-
{isLoading ? 'Creating account' : 'Create account'}
488-
<span className='inline-flex transition-transform duration-200 group-hover:translate-x-0.5'>
489-
{isButtonHovered ? (
490-
<ArrowRight className='h-4 w-4' aria-hidden='true' />
491-
) : (
492-
<ChevronRight className='h-4 w-4' aria-hidden='true' />
493-
)}
494-
</span>
495-
</span>
496-
</Button>
484+
Create account
485+
</BrandedButton>
497486
</form>
498487
)}
499488

apps/sim/app/(landing)/careers/page.tsx

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { useRef, useState } from 'react'
44
import { createLogger } from '@sim/logger'
55
import { X } from 'lucide-react'
66
import { Textarea } from '@/components/emcn'
7-
import { Button } from '@/components/ui/button'
87
import { Input } from '@/components/ui/input'
98
import { Label } from '@/components/ui/label'
109
import {
@@ -18,6 +17,7 @@ import { isHosted } from '@/lib/core/config/feature-flags'
1817
import { cn } from '@/lib/core/utils/cn'
1918
import { quickValidateEmail } from '@/lib/messaging/email/validation'
2019
import { soehne } from '@/app/_styles/fonts/soehne/soehne'
20+
import { BrandedButton } from '@/app/(auth)/components/branded-button'
2121
import Footer from '@/app/(landing)/components/footer/footer'
2222
import Nav from '@/app/(landing)/components/nav/nav'
2323

@@ -493,18 +493,17 @@ export default function CareersPage() {
493493

494494
{/* Submit Button */}
495495
<div className='flex justify-end pt-2'>
496-
<Button
496+
<BrandedButton
497497
type='submit'
498498
disabled={isSubmitting || submitStatus === 'success'}
499-
className='min-w-[200px] rounded-[10px] border border-[#6F3DFA] bg-gradient-to-b from-[#8357FF] to-[#6F3DFA] text-white shadow-[inset_0_2px_4px_0_#9B77FF] transition-all duration-300 hover:opacity-90 disabled:opacity-50'
500-
size='lg'
499+
loading={isSubmitting}
500+
loadingText='Submitting'
501+
showArrow={false}
502+
fullWidth={false}
503+
className='min-w-[200px]'
501504
>
502-
{isSubmitting
503-
? 'Submitting...'
504-
: submitStatus === 'success'
505-
? 'Submitted'
506-
: 'Submit Application'}
507-
</Button>
505+
{submitStatus === 'success' ? 'Submitted' : 'Submit Application'}
506+
</BrandedButton>
508507
</div>
509508
</form>
510509
</section>

apps/sim/app/(landing)/components/nav/nav.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { useBrandConfig } from '@/lib/branding/branding'
1111
import { isHosted } from '@/lib/core/config/feature-flags'
1212
import { soehne } from '@/app/_styles/fonts/soehne/soehne'
1313
import { getFormattedGitHubStars } from '@/app/(landing)/actions/github'
14+
import { useBrandedButtonClass } from '@/hooks/use-branded-button-class'
1415

1516
const logger = createLogger('nav')
1617

@@ -20,11 +21,12 @@ interface NavProps {
2021
}
2122

2223
export default function Nav({ hideAuthButtons = false, variant = 'landing' }: NavProps = {}) {
23-
const [githubStars, setGithubStars] = useState('25.1k')
24+
const [githubStars, setGithubStars] = useState('25.8k')
2425
const [isHovered, setIsHovered] = useState(false)
2526
const [isLoginHovered, setIsLoginHovered] = useState(false)
2627
const router = useRouter()
2728
const brand = useBrandConfig()
29+
const buttonClass = useBrandedButtonClass()
2830

2931
useEffect(() => {
3032
if (variant !== 'landing') return
@@ -183,7 +185,7 @@ export default function Nav({ hideAuthButtons = false, variant = 'landing' }: Na
183185
href='/signup'
184186
onMouseEnter={() => setIsHovered(true)}
185187
onMouseLeave={() => setIsHovered(false)}
186-
className='group inline-flex items-center justify-center gap-2 rounded-[10px] border border-[#6F3DFA] bg-gradient-to-b from-[#8357FF] to-[#6F3DFA] py-[6px] pr-[10px] pl-[12px] text-[14px] text-white shadow-[inset_0_2px_4px_0_#9B77FF] transition-all sm:text-[16px]'
188+
className={`${buttonClass} group inline-flex items-center justify-center gap-2 rounded-[10px] py-[6px] pr-[10px] pl-[12px] text-[15px] text-white transition-all`}
187189
aria-label='Get started with Sim - Sign up for free'
188190
prefetch={true}
189191
>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
'use client'
2+
3+
import Link from 'next/link'
4+
import { useBrandedButtonClass } from '@/hooks/use-branded-button-class'
5+
6+
interface BrandedLinkProps {
7+
href: string
8+
children: React.ReactNode
9+
className?: string
10+
target?: string
11+
rel?: string
12+
}
13+
14+
export function BrandedLink({ href, children, className = '', target, rel }: BrandedLinkProps) {
15+
const buttonClass = useBrandedButtonClass()
16+
17+
return (
18+
<Link
19+
href={href}
20+
target={target}
21+
rel={rel}
22+
className={`${buttonClass} group inline-flex items-center justify-center gap-2 rounded-[10px] py-[6px] pr-[10px] pl-[12px] text-[15px] text-white transition-all ${className}`}
23+
>
24+
{children}
25+
</Link>
26+
)
27+
}

apps/sim/app/changelog/components/changelog-content.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { BookOpen, Github, Rss } from 'lucide-react'
22
import Link from 'next/link'
33
import { inter } from '@/app/_styles/fonts/inter/inter'
44
import { soehne } from '@/app/_styles/fonts/soehne/soehne'
5+
import { BrandedLink } from '@/app/changelog/components/branded-link'
56
import ChangelogList from '@/app/changelog/components/timeline-list'
67

78
export interface ChangelogEntry {
@@ -66,25 +67,24 @@ export default async function ChangelogContent() {
6667
<hr className='mt-6 border-border' />
6768

6869
<div className='mt-6 flex flex-wrap items-center gap-3 text-sm'>
69-
<Link
70+
<BrandedLink
7071
href='https://github.com/simstudioai/sim/releases'
7172
target='_blank'
7273
rel='noopener noreferrer'
73-
className='group inline-flex items-center justify-center gap-2 rounded-[10px] border border-[#6F3DFA] bg-gradient-to-b from-[#8357FF] to-[#6F3DFA] py-[6px] pr-[10px] pl-[12px] text-[14px] text-white shadow-[inset_0_2px_4px_0_#9B77FF] transition-all sm:text-[16px]'
7474
>
7575
<Github className='h-4 w-4' />
7676
View on GitHub
77-
</Link>
77+
</BrandedLink>
7878
<Link
7979
href='https://docs.sim.ai'
80-
className='inline-flex items-center gap-2 rounded-md border border-border px-3 py-1.5 hover:bg-muted'
80+
className='inline-flex items-center gap-2 rounded-[10px] border border-border py-[6px] pr-[10px] pl-[12px] text-[15px] transition-all hover:bg-muted'
8181
>
8282
<BookOpen className='h-4 w-4' />
8383
Documentation
8484
</Link>
8585
<Link
8686
href='/changelog.xml'
87-
className='inline-flex items-center gap-2 rounded-md border border-border px-3 py-1.5 hover:bg-muted'
87+
className='inline-flex items-center gap-2 rounded-[10px] border border-border py-[6px] pr-[10px] pl-[12px] text-[15px] transition-all hover:bg-muted'
8888
>
8989
<Rss className='h-4 w-4' />
9090
RSS Feed

apps/sim/app/chat/[identifier]/chat.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ export default function ChatClient({ identifier }: { identifier: string }) {
117117
const [error, setError] = useState<string | null>(null)
118118
const messagesEndRef = useRef<HTMLDivElement>(null)
119119
const messagesContainerRef = useRef<HTMLDivElement>(null)
120-
const [starCount, setStarCount] = useState('25.1k')
120+
const [starCount, setStarCount] = useState('25.8k')
121121
const [conversationId, setConversationId] = useState('')
122122

123123
const [showScrollButton, setShowScrollButton] = useState(false)

0 commit comments

Comments
 (0)