Files
Cibello-app/apps/mobile/src/app/(auth)/login.tsx
T
Sven (AAMOS AI) 5f9526cb07
CI / Typecheck, test & build (push) Successful in 1m40s
fix(mobile): keyboard-avoidance i Screen så formulär inte göms bakom tangentbordet (FAS 35)
2026-08-14 04:01:44 +07:00

77 lines
2.4 KiB
TypeScript

import { useState } from "react";
import { Text, View } from "react-native";
import { Link, Redirect, router } from "expo-router";
import { api } from "@/lib/api";
import { useAuth, type AuthUser } from "@/lib/auth";
import { t } from "@/lib/i18n";
import { Body, Button, Input, Screen, Spacer, Title } from "@/components/ui";
import { colors, spacing } from "@/lib/theme";
import { BRAND } from "@/lib/brand";
interface LoginResponse {
user: AuthUser;
accessToken: string;
refreshToken: string;
}
export default function LoginScreen() {
const { accessToken, setSession } = useAuth();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState<string | null>(null);
const [busy, setBusy] = useState(false);
if (accessToken) return <Redirect href="/(tabs)" />;
const submit = async () => {
setBusy(true);
setError(null);
try {
const result = await api<LoginResponse>("/v1/auth/login", {
method: "POST",
body: { email, password },
});
await setSession(
{ accessToken: result.accessToken, refreshToken: result.refreshToken },
result.user,
);
router.replace("/(tabs)");
} catch (err) {
setError(err instanceof Error ? err.message : t("common.error"));
} finally {
setBusy(false);
}
};
return (
<Screen style={{ flexGrow: 1, justifyContent: "center", gap: spacing.md }}>
<View style={{ alignItems: "center", marginBottom: spacing.lg }}>
<Title>{BRAND.name}</Title>
<Body muted>Hushållets mat-OS</Body>
</View>
<Input
placeholder={t("auth.email")}
autoCapitalize="none"
keyboardType="email-address"
value={email}
onChangeText={setEmail}
/>
<Input
placeholder={t("auth.password")}
secureTextEntry
value={password}
onChangeText={setPassword}
/>
{error && <Text style={{ color: colors.danger }}>{error}</Text>}
<Button label={t("auth.login")} onPress={() => void submit()} loading={busy} />
<Spacer size={spacing.sm} />
<Link href="/(auth)/register" style={{ textAlign: "center", color: colors.primaryDark }}>
{t("auth.noAccount")}
</Link>
<Link href="/(auth)/forgot-password" style={{ textAlign: "center", color: colors.textMuted }}>
{t("auth.forgotLink")}
</Link>
</Screen>
);
}