* chore: run Tailwind v4 upgrade tool (Phase 1) - Upgraded tailwindcss v3 → v4.2.1, postcss plugin to @tailwindcss/postcss - Deleted tailwind.config.js, migrated theme to CSS @theme block in index.css - Replaced @tailwind directives with @import 'tailwindcss' - Added @custom-variant dark, @utility blocks for custom utilities - Updated class names across 128 files (shadow-sm → shadow-xs, etc.) - Removed autoprefixer (built into v4) - Added migration plan doc Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: switch from @tailwindcss/postcss to @tailwindcss/vite (Phase 2) - Replaced @tailwindcss/postcss with @tailwindcss/vite plugin - Deleted postcss.config.js (no longer needed) - Tailwind now runs as a native Vite plugin for faster HMR Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: convert to OKLCH colors, move keyframes into @theme (Phase 3-4) - Replaced all HSL color indirection with direct OKLCH values in @theme - Moved all keyframes inside @theme block (v4 pattern) - Eliminated hsl(var(--x)) double-indirection across 17 component files - Replaced hsl() inline styles with var(--color-*) theme references - Cleaned up redundant rdp-* utility blocks - Fixed @custom-variant dark syntax to use :where() - Added sidebar/glass/shadow vars as OKLCH in :root Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
58 lines
1.8 KiB
TypeScript
58 lines
1.8 KiB
TypeScript
import { useState, useEffect } from 'react'
|
|
import { AlertTriangle, X, Loader2 } from 'lucide-react'
|
|
import { authApi } from '@/api/auth'
|
|
import { useAuthStore } from '@/store/authStore'
|
|
import { cn } from '@/lib/utils'
|
|
import { toast } from '@/lib/toast'
|
|
|
|
export function EmailVerificationBanner() {
|
|
const user = useAuthStore((s) => s.user)
|
|
const [dismissed, setDismissed] = useState(false)
|
|
const [isSending, setIsSending] = useState(false)
|
|
const [verificationEnabled, setVerificationEnabled] = useState(true)
|
|
|
|
useEffect(() => {
|
|
authApi.getVerificationStatus()
|
|
.then((data) => setVerificationEnabled(data.enabled))
|
|
.catch(() => {})
|
|
}, [])
|
|
|
|
if (!user || user.email_verified_at || dismissed || !verificationEnabled) return null
|
|
|
|
const handleResend = async () => {
|
|
setIsSending(true)
|
|
try {
|
|
await authApi.sendVerificationEmail()
|
|
toast.success('Verification email sent')
|
|
} catch {
|
|
toast.error('Failed to send verification email')
|
|
} finally {
|
|
setIsSending(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="flex items-center gap-3 border-b border-amber-400/20 bg-amber-400/5 px-4 py-2 text-sm">
|
|
<AlertTriangle className="h-4 w-4 shrink-0 text-amber-400" />
|
|
<span className="text-amber-200">
|
|
Your email is not verified.
|
|
</span>
|
|
<button
|
|
onClick={handleResend}
|
|
disabled={isSending}
|
|
className={cn(
|
|
'text-amber-400 underline hover:text-amber-300 disabled:opacity-50'
|
|
)}
|
|
>
|
|
{isSending ? <Loader2 className="inline h-3 w-3 animate-spin" /> : 'Resend verification email'}
|
|
</button>
|
|
<button
|
|
onClick={() => setDismissed(true)}
|
|
className="ml-auto text-muted-foreground hover:text-foreground"
|
|
>
|
|
<X className="h-4 w-4" />
|
|
</button>
|
|
</div>
|
|
)
|
|
}
|