Files
beenvoice-app/app/(auth)/forgot-password.tsx
T
soconnor 6d2711e36e Polish mobile app for App Store review and expand CRUD.
Default to beenvoice.soconnor.dev with server settings hidden behind Advanced; add Entities tab with clients/businesses, invoice creation, UI fixes for dashboard layout, date fields, FAB position, and card-matched button radius.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-17 23:14:58 -04:00

132 lines
3.8 KiB
TypeScript

import { router } from "expo-router";
import { useState } from "react";
import {
KeyboardAvoidingView,
Platform,
Pressable,
ScrollView,
StyleSheet,
Text,
View,
} from "react-native";
import { FullScreen } from "@/components/Screen";
import { AuthBackground } from "@/components/AppBackground";
import { HeadingText, Logo } from "@/components/Logo";
import { Button } from "@/components/ui/Button";
import { Card } from "@/components/ui/Card";
import { Input } from "@/components/ui/Input";
import { fonts, spacing } from "@/constants/theme";
import { useAppTheme } from "@/contexts/ThemeContext";
import { requestPasswordReset } from "@/lib/auth-api";
export default function ForgotPasswordScreen() {
const { colors } = useAppTheme();
const [email, setEmail] = useState("");
const [message, setMessage] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
async function handleSubmit() {
setError(null);
setMessage(null);
setLoading(true);
try {
const result = await requestPasswordReset(email.trim());
setMessage(result);
} catch (err) {
setError(err instanceof Error ? err.message : "Request failed");
} finally {
setLoading(false);
}
}
return (
<AuthBackground>
<FullScreen style={styles.safe}>
<KeyboardAvoidingView
behavior={Platform.OS === "ios" ? "padding" : undefined}
style={styles.flex}
>
<ScrollView contentContainerStyle={styles.container}>
<Pressable onPress={() => router.back()}>
<Text style={[styles.back, { color: colors.mutedForeground }]}> Back</Text>
</Pressable>
<Card style={styles.card}>
<View style={styles.header}>
<Logo size="md" />
<HeadingText style={styles.title}>Reset password</HeadingText>
<Text style={[styles.subtitle, { color: colors.mutedForeground }]}>
Enter your email and we&apos;ll send reset instructions if an account exists.
</Text>
</View>
<View style={styles.form}>
<Input
label="Email"
autoCapitalize="none"
autoComplete="email"
keyboardType="email-address"
value={email}
onChangeText={setEmail}
placeholder="you@example.com"
/>
{error ? (
<Text style={[styles.error, { color: colors.destructive }]}>{error}</Text>
) : null}
{message ? (
<Text style={[styles.success, { color: colors.foreground }]}>{message}</Text>
) : null}
<Button title="Send reset link" loading={loading} onPress={handleSubmit} />
<Button
title="Have a reset token?"
variant="ghost"
onPress={() => router.push("/(auth)/reset-password")}
/>
</View>
</Card>
</ScrollView>
</KeyboardAvoidingView>
</FullScreen>
</AuthBackground>
);
}
const styles = StyleSheet.create({
safe: { flex: 1 },
flex: { flex: 1 },
container: {
flexGrow: 1,
padding: spacing.lg,
paddingBottom: spacing.md,
gap: spacing.md,
justifyContent: "center",
},
back: {
fontFamily: fonts.bodyMedium,
fontSize: 16,
marginBottom: spacing.sm,
},
card: { gap: spacing.lg },
header: { gap: spacing.sm },
title: { fontSize: 28 },
subtitle: {
fontSize: 14,
fontFamily: fonts.body,
lineHeight: 20,
},
form: { gap: spacing.md },
error: {
fontSize: 14,
fontFamily: fonts.body,
},
success: {
fontSize: 14,
fontFamily: fonts.body,
lineHeight: 20,
},
});