diff --git a/components/ProfileSettings.tsx b/components/ProfileSettings.tsx index 0cac415..e66496d 100644 --- a/components/ProfileSettings.tsx +++ b/components/ProfileSettings.tsx @@ -4,11 +4,15 @@ import { Button } from './ui/button'; import { Card, CardContent, CardHeader, CardTitle } from './ui/card'; import { Input } from './ui/input'; import { Label } from './ui/label'; -import { useSession, signOut } from '../lib/auth-client'; +import { useSession, signOut, updateUser } from '../lib/auth-client'; import { validatePhoneOrEmail } from '../lib/validators'; export function ProfileSettings() { const { data: session } = useSession(); + const [userName, setUserName] = useState(''); + const [savingName, setSavingName] = useState(false); + const [nameSaved, setNameSaved] = useState(false); + const [nameError, setNameError] = useState(''); const [piFirstName, setPiFirstName] = useState(''); const [bldgCode, setBldgCode] = useState(''); const [lab, setLab] = useState(''); @@ -21,6 +25,10 @@ export function ProfileSettings() { const [deleting, setDeleting] = useState(false); const [deleteError, setDeleteError] = useState(''); + useEffect(() => { + if (session?.user.name) setUserName(session.user.name); + }, [session?.user.name]); + useEffect(() => { fetch('/api/profile', { credentials: 'include' }) .then(r => (r.ok ? r.json() : null)) @@ -35,6 +43,25 @@ export function ProfileSettings() { .finally(() => setLoading(false)); }, []); + async function handleSaveName() { + setNameError(''); + setNameSaved(false); + const trimmed = userName.trim(); + if (!trimmed) { + setNameError('Name is required.'); + return; + } + setSavingName(true); + const { error: err } = await updateUser({ name: trimmed }); + setSavingName(false); + if (err) { + setNameError(err.message || 'Failed to save name.'); + } else { + setNameSaved(true); + setTimeout(() => setNameSaved(false), 3000); + } + } + async function handleSubmit(e: React.FormEvent) { e.preventDefault(); setError(''); @@ -93,15 +120,31 @@ export function ProfileSettings() { Account - -
- -

{session?.user.name || '—'}

+ +
+ + setUserName(e.target.value)} + placeholder="Your name" + />

{session?.user.email || '—'}

+ {nameError &&

{nameError}

} + {nameSaved && ( +

+ Name saved +

+ )} +
+ +
diff --git a/components/auth/LoginForm.tsx b/components/auth/LoginForm.tsx index e8500a8..553978c 100644 --- a/components/auth/LoginForm.tsx +++ b/components/auth/LoginForm.tsx @@ -11,6 +11,7 @@ import { Camera, AlertTriangle, ChevronDown, + Loader2, } from 'lucide-react'; const logo = '/logo.png'; @@ -52,6 +53,7 @@ export function LoginForm({ onSignUp, onForgotPassword }: Props) { const [password, setPassword] = useState(''); const [error, setError] = useState(''); const [loading, setLoading] = useState(false); + const [appleLoading, setAppleLoading] = useState(false); const [resendCooldown, setResendCooldown] = useState(0); const [resendLoading, setResendLoading] = useState(false); const aboutRef = useRef(null); @@ -90,7 +92,9 @@ export function LoginForm({ onSignUp, onForgotPassword }: Props) { } async function handleApple() { - await signIn.social({ provider: 'apple', callbackURL: window.location.origin, errorCallbackURL: window.location.origin }); + setAppleLoading(true); + const res = await signIn.social({ provider: 'apple', callbackURL: window.location.origin, errorCallbackURL: window.location.origin }); + if (res?.error) setAppleLoading(false); } function scrollToAbout() { @@ -212,11 +216,16 @@ export function LoginForm({ onSignUp, onForgotPassword }: Props) { variant="outline" className="w-full flex items-center gap-3" onClick={handleApple} + disabled={appleLoading} > - - - - Sign in with Apple + {appleLoading ? ( + + ) : ( + + + + )} + {appleLoading ? 'Signing in…' : 'Sign in with Apple'}
diff --git a/lib/auth-client.ts b/lib/auth-client.ts index 0daea0f..51e8a43 100644 --- a/lib/auth-client.ts +++ b/lib/auth-client.ts @@ -9,6 +9,7 @@ export const { signOut, signUp, useSession, + updateUser, requestPasswordReset, resetPassword, sendVerificationEmail,