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

New sonner #334

Merged
merged 13 commits into from
Jan 14, 2025
5 changes: 5 additions & 0 deletions apps/masterbots.ai/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
@tailwind components;
@tailwind utilities;

@layer components {
.icon-sonner-dark path {
stroke: white;
}
}
@layer base {
:root {
--background: 0 0% 100%;
Expand Down
12 changes: 6 additions & 6 deletions apps/masterbots.ai/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { GeistMono } from 'geist/font/mono'
import { GeistSans } from 'geist/font/sans'
import { Toaster } from 'react-hot-toast'

import '@/app/globals.css'
import { Header } from '@/components/layout/header/header'
Expand All @@ -9,6 +8,7 @@ import { cn } from '@/lib/utils'
import { GoogleAnalytics } from '@next/third-parties/google'
import { Metadata } from 'next'
import NextTopLoader from 'nextjs-toploader'
import { Toaster } from '@/components/ui/sonner'

export default function RootLayout({ children }: RootLayoutProps) {
return (
Expand All @@ -20,10 +20,8 @@ export default function RootLayout({ children }: RootLayoutProps) {
GeistMono.variable
)}
>
<NextTopLoader color="#1ED761" initialPosition={0.20} />
<Toaster toastOptions={{
className: 'bg-background text-background-foreground',
}} />
<NextTopLoader color="#1ED761" initialPosition={0.2} />
<Toaster />
<Providers
attribute="class"
defaultTheme="system"
Expand All @@ -32,7 +30,9 @@ export default function RootLayout({ children }: RootLayoutProps) {
>
<div className="flex flex-col min-h-screen">
<Header />
<main className="relative flex flex-col flex-1 bg-muted/50">{children}</main>
<main className="relative flex flex-col flex-1 bg-muted/50">
{children}
</main>
</div>
{/* <TailwindIndicator /> */}
<GoogleAnalytics gaId="G-N135BF99HS" />
Expand Down
9 changes: 5 additions & 4 deletions apps/masterbots.ai/components/auth/forgot-password-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { useSonner } from '@/lib/hooks/useSonner'
import { validateEmail } from '@/lib/utils'
import type React from 'react'
import { useState } from 'react'
import { toast } from 'react-hot-toast'

export default function ForgotPasswordForm() {
const [email, setEmail] = useState('')
const [isLoading, setIsLoading] = useState(false)
const [emailError, setEmailError] = useState('')
const { customSonner } = useSonner()

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
Expand All @@ -34,14 +35,14 @@ export default function ForgotPasswordForm() {
const data = await response.json()

if (response.ok) {
toast.success(data.message)
customSonner({ type: 'success', text: data.message })
setEmail('')
} else {
toast.error(data.error || 'An error occurred')
customSonner({ type: 'error', text: data.error || 'An error occurred' })
}
} catch (error) {
console.error('Error:', error)
toast.error('An unexpected error occurred')
customSonner({ type: 'error', text: 'An unexpected error occurred' })
} finally {
setIsLoading(false)
}
Expand Down
13 changes: 7 additions & 6 deletions apps/masterbots.ai/components/auth/reset-password-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { isPasswordStrong } from '@/lib/password'
import { Eye, EyeOff } from 'lucide-react'
import { useRouter } from 'next/navigation'
import type React from 'react'
import { toast } from 'react-hot-toast'
import { useSetState } from 'react-use'
import { useSonner } from '@/lib/hooks/useSonner'

interface FormState {
password: string
Expand All @@ -29,6 +29,7 @@ export default function ResetPasswordForm({ token }: { token: string }) {
showPassword: false,
showConfirmPassword: false
})
const { customSonner } = useSonner()

const router = useRouter()

Expand All @@ -48,13 +49,13 @@ export default function ResetPasswordForm({ token }: { token: string }) {
setState({ isLoading: true })

if (!isPasswordStrong(state.password)) {
toast.error('Please choose a stronger password')
customSonner({ type: 'error', text: 'Please choose a stronger password' })
setState({ isLoading: false })
return
}

if (state.password !== state.confirmPassword) {
toast.error('Passwords do not match')
customSonner({ type: 'error', text: 'Passwords do not match' })
setState({ isLoading: false })
return
}
Expand All @@ -69,14 +70,14 @@ export default function ResetPasswordForm({ token }: { token: string }) {
const data = await response.json()

if (response.ok) {
toast.success(data.message)
customSonner({ type: 'success', text: data.message })
router.push('/auth/signin')
} else {
toast.error(data.error || 'An error occurred')
customSonner({ type: 'error', text: data.error || 'An error occurred' })
}
} catch (error) {
console.error('Error:', error)
toast.error('An unexpected error occurred')
customSonner({ type: 'error', text: 'An unexpected error occurred' })
} finally {
setState({ isLoading: false })
}
Expand Down
84 changes: 46 additions & 38 deletions apps/masterbots.ai/components/auth/signup-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,31 @@ import { PasswordStrengthMeter } from '@/components/shared/password-strength-met
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { useSonner } from '@/lib/hooks/useSonner'
import { isPasswordStrong, verifyPassword } from '@/lib/password'
import { Eye, EyeOff } from 'lucide-react'
import { useRouter } from 'next/navigation'
import type React from 'react'
import { useState } from 'react'
import toast from 'react-hot-toast'

interface SignupState {
email: string;
password: string;
username: string;
passwordVerify: string;
isLoading: boolean;
showVerificationNotice: boolean;
showPassword: boolean;
showPasswordVerify: boolean;
email: string
password: string
username: string
passwordVerify: string
isLoading: boolean
showVerificationNotice: boolean
showPassword: boolean
showPasswordVerify: boolean
}

interface SignupState {
email: string;
password: string;
username: string;
passwordVerify: string;
isLoading: boolean;
showVerificationNotice: boolean;
email: string
password: string
username: string
passwordVerify: string
isLoading: boolean
showVerificationNotice: boolean
}

export default function SignUpForm() {
Expand All @@ -43,21 +43,21 @@ export default function SignUpForm() {
showPassword: false,
showPasswordVerify: false
})

const { customSonner } = useSonner()
const router = useRouter()

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setState(prev => ({ ...prev, isLoading: true }))

if (state.password !== state.passwordVerify) {
toast.error('Passwords do not match')
customSonner({ type: 'error', text: 'Passwords do not match' })
setState(prev => ({ ...prev, isLoading: false }))
return
}

if (!isPasswordStrong(state.password)) {
toast.error('Please choose a stronger password')
customSonner({ type: 'error', text: 'Please choose a stronger password' })
setState(prev => ({ ...prev, isLoading: false }))
return
}
Expand All @@ -77,13 +77,16 @@ export default function SignUpForm() {

if (response.ok) {
setState(prev => ({ ...prev, showVerificationNotice: true }))
toast.success('Account created successfully! Please check your email to verify your account.')
customSonner({
type: 'success',
text: 'Account created successfully! Please check your email to verify your account.'
})
} else {
toast.error(data.error || 'Failed to sign up')
customSonner({ type: 'error', text: data.error || 'Failed to sign up' })
}
} catch (error) {
console.error(error)
toast.error('An unexpected error occurred')
customSonner({ type: 'error', text: 'An unexpected error occurred' })
} finally {
setState(prev => ({ ...prev, isLoading: false }))
}
Expand All @@ -94,7 +97,9 @@ export default function SignUpForm() {
setState(prev => ({ ...prev, [name]: value }))
}

const togglePasswordVisibility = (field: 'showPassword' | 'showPasswordVerify') => {
const togglePasswordVisibility = (
field: 'showPassword' | 'showPasswordVerify'
) => {
setState(prev => ({ ...prev, [field]: !prev[field] }))
}

Expand All @@ -106,14 +111,11 @@ export default function SignUpForm() {
We&apos;ve sent a verification link to <strong>{state.email}</strong>
</p>
<p className="text-sm text-gray-600">
Please check your email and click the verification link to activate your account.
The verification link will expire in 15 days.
Please check your email and click the verification link to activate
your account. The verification link will expire in 15 days.
</p>
<div className="pt-4">
<Button
variant="outline"
onClick={() => router.push('/auth/signin')}
>
<Button variant="outline" onClick={() => router.push('/auth/signin')}>
Go to Sign In
</Button>
</div>
Expand All @@ -135,7 +137,9 @@ export default function SignUpForm() {
/>
</div>
<div className="space-y-2">
<Label htmlFor="email" variant="required">Email</Label>
<Label htmlFor="email" variant="required">
Email
</Label>
<Input
id="email"
name="email"
Expand All @@ -147,7 +151,9 @@ export default function SignUpForm() {
/>
</div>
<div className="space-y-2">
<Label htmlFor="password" variant="required">Password</Label>
<Label htmlFor="password" variant="required">
Password
</Label>
<div className="relative">
<Input
id="password"
Expand All @@ -170,7 +176,9 @@ export default function SignUpForm() {
<PasswordStrengthMeter password={state.password} />
</div>
<div className="space-y-2">
<Label htmlFor="passwordVerify" variant="required">Verify Password</Label>
<Label htmlFor="passwordVerify" variant="required">
Verify Password
</Label>
<div className="relative">
<Input
id="passwordVerify"
Expand All @@ -188,17 +196,17 @@ export default function SignUpForm() {
onClick={() => togglePasswordVisibility('showPasswordVerify')}
className="absolute inset-y-0 right-0 flex items-center pr-3 text-gray-400 hover:text-gray-600"
>
{state.showPasswordVerify ? <EyeOff size={20} /> : <Eye size={20} />}
{state.showPasswordVerify ? (
<EyeOff size={20} />
) : (
<Eye size={20} />
)}
</button>
</div>
</div>
<Button
type="submit"
className="w-full"
disabled={state.isLoading}
>
<Button type="submit" className="w-full" disabled={state.isLoading}>
{state.isLoading ? 'Signing up...' : 'Sign Up'}
</Button>
</form>
)
}
}
18 changes: 18 additions & 0 deletions apps/masterbots.ai/components/icons/closeIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const CloseIcon = (props: React.ComponentPropsWithoutRef<'svg'>) => (
<svg width={19} height={18} viewBox="0 0 19 18" fill="none" {...props}>
<path
d="M14 4.5L5 13.5"
stroke="#09090B"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M5 4.5L14 13.5"
stroke="#09090B"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
)

export default CloseIcon
13 changes: 13 additions & 0 deletions apps/masterbots.ai/components/icons/errorIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const ErrorIcon = (props: React.ComponentPropsWithoutRef<'svg'>) => (
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" {...props}>
<path
d="M8.99983 6.74992V9.74992M8.99983 12.7499H9.00733M16.2973 13.4999L10.2973 2.99992C10.1665 2.76908 9.97678 2.57707 9.74752 2.44348C9.51826 2.30989 9.25767 2.2395 8.99232 2.2395C8.72698 2.2395 8.46639 2.30989 8.23713 2.44348C8.00787 2.57707 7.81815 2.76908 7.68733 2.99992L1.68733 13.4999C1.55509 13.7289 1.48575 13.9889 1.48633 14.2533C1.48692 14.5178 1.55741 14.7774 1.69066 15.0058C1.82391 15.2342 2.01519 15.4234 2.2451 15.554C2.47501 15.6847 2.73538 15.7523 2.99983 15.7499H14.9998C15.263 15.7497 15.5215 15.6802 15.7493 15.5484C15.9771 15.4166 16.1662 15.2272 16.2977 14.9993C16.4292 14.7713 16.4984 14.5127 16.4983 14.2495C16.4982 13.9864 16.4289 13.7278 16.2973 13.4999Z"
stroke="#F93333"
strokeWidth="1.125"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
)

export default ErrorIcon
4 changes: 4 additions & 0 deletions apps/masterbots.ai/components/icons/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { default as SuccessIcon } from './successIcon'
export { default as InfoIcon } from './infoIcon'
export { default as ErrorIcon } from './errorIcon'
export { default as CloseIcon } from './closeIcon'
20 changes: 20 additions & 0 deletions apps/masterbots.ai/components/icons/infoIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const InfoIcon = (props: React.ComponentPropsWithoutRef<'svg'>) => (
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" {...props}>
<g clipPath="url(#clip0_2431_25609)">
<path
d="M9 12V9M9 6H9.0075M16.5 9C16.5 13.1421 13.1421 16.5 9 16.5C4.85786 16.5 1.5 13.1421 1.5 9C1.5 4.85786 4.85786 1.5 9 1.5C13.1421 1.5 16.5 4.85786 16.5 9Z"
stroke="#388DE2"
strokeWidth="1.125"
strokeLinecap="round"
strokeLinejoin="round"
/>
</g>
<defs>
<clipPath id="clip0_2431_25609">
<rect width="18" height="18" fill="white" />
</clipPath>
</defs>
</svg>
)

export default InfoIcon
12 changes: 12 additions & 0 deletions apps/masterbots.ai/components/icons/successIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const SuccessIcon = (props: React.ComponentPropsWithoutRef<'svg'>) => (
<svg width="14" height="10" viewBox="0 0 14 10" fill="none" {...props}>
<path
d="M1 5L5 9L13 1"
stroke="#83E56A"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
)

export default SuccessIcon
Loading