230 lines
8.3 KiB
TypeScript
230 lines
8.3 KiB
TypeScript
import { useState } from "react";
|
||
import {
|
||
View,
|
||
Text,
|
||
KeyboardAvoidingView,
|
||
Platform,
|
||
ScrollView,
|
||
Alert,
|
||
Pressable,
|
||
} from "react-native";
|
||
import { useRouter } from "expo-router";
|
||
import { useMutation, useAction } from "convex/react";
|
||
import { api } from "../../convex/_generated/api";
|
||
import { Input } from "../../components/ui/input";
|
||
import { Button } from "../../components/ui/button";
|
||
import { useTheme } from "../../components/theme";
|
||
import { useAuth } from "../_layout";
|
||
|
||
type Role = "handyman" | "customer";
|
||
|
||
export default function RegisterScreen() {
|
||
const theme = useTheme();
|
||
const router = useRouter();
|
||
const { login } = useAuth();
|
||
const registerMutation = useMutation(api.auth.register);
|
||
const hashPassword = useAction(api.auth.hashPassword);
|
||
|
||
const [name, setName] = useState("");
|
||
const [email, setEmail] = useState("");
|
||
const [phone, setPhone] = useState("");
|
||
const [password, setPassword] = useState("");
|
||
const [role, setRole] = useState<Role>("customer");
|
||
const [loading, setLoading] = useState(false);
|
||
|
||
async function handleRegister() {
|
||
if (!name || !email || !password) {
|
||
Alert.alert("Грешка", "Пополнете ги сите задолжителни полиња");
|
||
return;
|
||
}
|
||
if (password.length < 8) {
|
||
Alert.alert("Грешка", "Лозинката мора да има најмалку 8 карактери");
|
||
return;
|
||
}
|
||
|
||
setLoading(true);
|
||
try {
|
||
const passwordHash = await hashPassword({ password });
|
||
const result = await registerMutation({
|
||
name,
|
||
email,
|
||
passwordHash,
|
||
role,
|
||
phone: phone || undefined,
|
||
});
|
||
await login(result.token, result.user!._id);
|
||
router.replace("/(tabs)/(home)");
|
||
} catch (e: any) {
|
||
Alert.alert("Регистрација неуспешна", e.message || "Обидете се повторно");
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}
|
||
|
||
return (
|
||
<KeyboardAvoidingView
|
||
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
||
style={{ flex: 1, backgroundColor: theme.colors.background }}
|
||
>
|
||
<ScrollView
|
||
contentContainerStyle={{ flexGrow: 1, padding: theme.spacing.lg }}
|
||
keyboardShouldPersistTaps="handled"
|
||
>
|
||
<View style={{
|
||
backgroundColor: theme.colors.card,
|
||
borderRadius: theme.radius.xl,
|
||
padding: theme.spacing.lg,
|
||
borderWidth: 1,
|
||
borderColor: theme.colors.borderLight,
|
||
...theme.shadows.md,
|
||
gap: theme.spacing.md,
|
||
}}>
|
||
<View style={{ alignItems: "center", marginBottom: theme.spacing.sm }}>
|
||
<View
|
||
style={{
|
||
width: 64,
|
||
height: 64,
|
||
borderRadius: 32,
|
||
backgroundColor: theme.colors.primaryLight,
|
||
alignItems: "center",
|
||
justifyContent: "center",
|
||
marginBottom: theme.spacing.md,
|
||
}}
|
||
>
|
||
<Text style={{ fontSize: 32 }}>👤</Text>
|
||
</View>
|
||
<Text style={{ ...theme.typography.h1, color: theme.colors.text }}>
|
||
Креирајте профил
|
||
</Text>
|
||
<Text selectable style={{ ...theme.typography.body, color: theme.colors.textSecondary, marginTop: theme.spacing.xs }}>
|
||
Изберете ја вашата улога
|
||
</Text>
|
||
</View>
|
||
|
||
<View style={{ flexDirection: "row", gap: theme.spacing.md }}>
|
||
<Pressable
|
||
accessible
|
||
accessibilityLabel="Изберете улога: Клиент"
|
||
accessibilityRole="button"
|
||
onPress={() => setRole("customer")}
|
||
style={{
|
||
flex: 1,
|
||
paddingVertical: theme.spacing.lg,
|
||
paddingHorizontal: theme.spacing.sm,
|
||
borderRadius: theme.radius.md,
|
||
borderWidth: 2,
|
||
borderColor: role === "customer" ? theme.colors.primary : theme.colors.borderLight,
|
||
backgroundColor: role === "customer" ? theme.colors.primaryLight : theme.colors.surface,
|
||
alignItems: "center",
|
||
gap: theme.spacing.sm,
|
||
}}
|
||
>
|
||
<View
|
||
style={{
|
||
width: 48,
|
||
height: 48,
|
||
borderRadius: 24,
|
||
backgroundColor: role === "customer" ? theme.colors.primary + "20" : theme.colors.surfaceAlt,
|
||
alignItems: "center",
|
||
justifyContent: "center",
|
||
}}
|
||
>
|
||
<Text style={{ fontSize: 24 }}>🏠</Text>
|
||
</View>
|
||
<Text style={{ fontWeight: "600", fontSize: 15, color: theme.colors.text }}>Клиент</Text>
|
||
<Text style={{ fontSize: 12, color: theme.colors.textSecondary, textAlign: "center" }}>
|
||
Барајте мајстори
|
||
</Text>
|
||
</Pressable>
|
||
|
||
<Pressable
|
||
accessible
|
||
accessibilityLabel="Изберете улога: Мајстор"
|
||
accessibilityRole="button"
|
||
onPress={() => setRole("handyman")}
|
||
style={{
|
||
flex: 1,
|
||
paddingVertical: theme.spacing.lg,
|
||
paddingHorizontal: theme.spacing.sm,
|
||
borderRadius: theme.radius.md,
|
||
borderWidth: 2,
|
||
borderColor: role === "handyman" ? theme.colors.primary : theme.colors.borderLight,
|
||
backgroundColor: role === "handyman" ? theme.colors.primaryLight : theme.colors.surface,
|
||
alignItems: "center",
|
||
gap: theme.spacing.sm,
|
||
}}
|
||
>
|
||
<View
|
||
style={{
|
||
width: 48,
|
||
height: 48,
|
||
borderRadius: 24,
|
||
backgroundColor: role === "handyman" ? theme.colors.primary + "20" : theme.colors.surfaceAlt,
|
||
alignItems: "center",
|
||
justifyContent: "center",
|
||
}}
|
||
>
|
||
<Text style={{ fontSize: 24 }}>🔧</Text>
|
||
</View>
|
||
<Text style={{ fontWeight: "600", fontSize: 15, color: theme.colors.text }}>Мајстор</Text>
|
||
<Text style={{ fontSize: 12, color: theme.colors.textSecondary, textAlign: "center" }}>
|
||
Нудете услуги
|
||
</Text>
|
||
</Pressable>
|
||
</View>
|
||
|
||
<Input
|
||
placeholder="Име и презиме"
|
||
value={name}
|
||
onChangeText={setName}
|
||
accessibilityLabel="Име и презиме"
|
||
label="Име и презиме"
|
||
/>
|
||
<Input
|
||
placeholder="your@email.com"
|
||
value={email}
|
||
onChangeText={setEmail}
|
||
autoCapitalize="none"
|
||
keyboardType="email-address"
|
||
accessibilityLabel="Е-пошта"
|
||
label="Е-пошта"
|
||
/>
|
||
<Input
|
||
placeholder="+389 70 123 456"
|
||
value={phone}
|
||
onChangeText={setPhone}
|
||
keyboardType="phone-pad"
|
||
accessibilityLabel="Телефонски број"
|
||
label="Телефон (незадолжително)"
|
||
/>
|
||
<Input
|
||
placeholder="Минимум 8 карактери"
|
||
value={password}
|
||
onChangeText={setPassword}
|
||
secureTextEntry
|
||
accessibilityLabel="Лозинка"
|
||
label="Лозинка"
|
||
/>
|
||
|
||
<Button loading={loading} onPress={handleRegister} accessibilityLabel="Регистрирај се" size="lg" fullWidth>
|
||
Регистрирај се
|
||
</Button>
|
||
|
||
<View style={{ flexDirection: "row", justifyContent: "center", marginTop: theme.spacing.sm }}>
|
||
<Text style={{ color: theme.colors.textSecondary }}>Веќе имате профил? </Text>
|
||
<Text
|
||
accessible
|
||
accessibilityLabel="Најавете се"
|
||
accessibilityRole="link"
|
||
style={{ color: theme.colors.primary, fontWeight: "600" }}
|
||
onPress={() => router.back()}
|
||
>
|
||
Најавете се
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
</ScrollView>
|
||
</KeyboardAvoidingView>
|
||
);
|
||
}
|