Yaltopia-Tickets-App/app/user/create.tsx

258 lines
8.5 KiB
TypeScript

import React, { useState } from "react";
import {
View,
ScrollView,
TextInput,
ActivityIndicator,
KeyboardAvoidingView,
Platform,
Pressable,
useColorScheme,
} from "react-native";
import { useSirouRouter } from "@sirou/react-native";
import { AppRoutes } from "@/lib/routes";
import { Stack } from "expo-router";
import { Text } from "@/components/ui/text";
import { Button } from "@/components/ui/button";
import {
Mail,
Lock,
User,
Phone,
ArrowRight,
ShieldCheck,
ChevronDown,
} from "@/lib/icons";
import { ScreenWrapper } from "@/components/ScreenWrapper";
import { StandardHeader } from "@/components/StandardHeader";
import { api } from "@/lib/api";
import { toast } from "@/lib/toast-store";
import { PickerModal, SelectOption } from "@/components/PickerModal";
const ROLES = ["VIEWER", "EMPLOYEE", "ACCOUNTANT", "CUSTOMER_SERVICE"];
export default function CreateUserScreen() {
const nav = useSirouRouter<AppRoutes>();
const colorScheme = useColorScheme();
const isDark = colorScheme === "dark";
const [form, setForm] = useState({
firstName: "",
lastName: "",
email: "",
phone: "",
password: "",
role: "VIEWER",
});
const [showRolePicker, setShowRolePicker] = useState(false);
const [loading, setLoading] = useState(false);
const handleCreate = async () => {
const { firstName, lastName, email, phone, password, role } = form;
if (!firstName || !lastName || !email || !phone || !password) {
toast.error("Required Fields", "Please fill in all fields");
return;
}
setLoading(true);
try {
// Prepend +251 if not present
const formattedPhone = phone.startsWith("+") ? phone : `+251${phone}`;
await api.users.create({
body: {
...form,
phone: formattedPhone,
},
});
toast.success(
"User Created!",
`${firstName} has been added to the system.`,
);
nav.back();
} catch (err: any) {
toast.error(
"Creation Failed",
err.message || "Failed to create user account",
);
} finally {
setLoading(false);
}
};
const updateForm = (key: keyof typeof form, val: string) =>
setForm((prev) => ({ ...prev, [key]: val }));
return (
<ScreenWrapper className="bg-background">
<Stack.Screen options={{ headerShown: false }} />
<StandardHeader title="Add New User" showBack />
<KeyboardAvoidingView
behavior={Platform.OS === "ios" ? "padding" : "height"}
className="flex-1"
>
<ScrollView
className="flex-1"
contentContainerStyle={{ padding: 24, paddingBottom: 60 }}
keyboardShouldPersistTaps="handled"
>
<View className="mb-8">
<Text variant="h3" className="font-bold text-foreground">
User Details
</Text>
<Text variant="muted" className="mt-1">
Configure credentials and system access
</Text>
</View>
<View className="gap-5">
{/* Identity Group */}
<View className="flex-row gap-4">
<View className="flex-1">
<Text variant="small" className="font-semibold mb-2 ml-1">
First Name
</Text>
<View className="bg-secondary/30 rounded-xl px-4 border border-border h-12 justify-center">
<TextInput
className="text-foreground"
placeholder="First Name"
placeholderTextColor={isDark ? "#475569" : "#94a3b8"}
value={form.firstName}
onChangeText={(v) => updateForm("firstName", v)}
/>
</View>
</View>
<View className="flex-1">
<Text variant="small" className="font-semibold mb-2 ml-1">
Last Name
</Text>
<View className="bg-secondary/30 rounded-xl px-4 border border-border h-12 justify-center">
<TextInput
className="text-foreground"
placeholder="Last Name"
placeholderTextColor={isDark ? "#475569" : "#94a3b8"}
value={form.lastName}
onChangeText={(v) => updateForm("lastName", v)}
/>
</View>
</View>
</View>
{/* Email */}
<View>
<Text variant="small" className="font-semibold mb-2 ml-1">
Email Address
</Text>
<View className="flex-row items-center bg-secondary/30 rounded-xl px-4 border border-border h-12">
<Mail size={18} color={isDark ? "#94a3b8" : "#64748b"} />
<TextInput
className="flex-1 ml-3 text-foreground"
placeholder="email@company.com"
placeholderTextColor={isDark ? "#475569" : "#94a3b8"}
value={form.email}
onChangeText={(v) => updateForm("email", v)}
autoCapitalize="none"
keyboardType="email-address"
/>
</View>
</View>
{/* Phone */}
<View>
<Text variant="small" className="font-semibold mb-2 ml-1">
Phone Number
</Text>
<View className="flex-row items-center bg-secondary/30 rounded-xl px-4 border border-border h-12">
<Phone size={18} color={isDark ? "#94a3b8" : "#64748b"} />
<TextInput
className="flex-1 ml-3 text-foreground"
placeholder="911 234 567"
placeholderTextColor={isDark ? "#475569" : "#94a3b8"}
value={form.phone}
onChangeText={(v) => updateForm("phone", v)}
keyboardType="phone-pad"
/>
</View>
</View>
{/* Role - Dropdown */}
<View>
<Text variant="small" className="font-semibold mb-2 ml-1">
System Role
</Text>
<Pressable
onPress={() => setShowRolePicker(true)}
className="flex-row items-center bg-secondary/30 rounded-xl px-4 border border-border h-12"
>
<ShieldCheck size={18} color={isDark ? "#94a3b8" : "#64748b"} />
<Text className="flex-1 ml-3 text-foreground font-medium">
{form.role}
</Text>
<ChevronDown size={18} color={isDark ? "#94a3b8" : "#64748b"} />
</Pressable>
</View>
{/* Password */}
<View>
<Text variant="small" className="font-semibold mb-2 ml-1">
Initial Password
</Text>
<View className="flex-row items-center bg-secondary/30 rounded-xl px-4 border border-border h-12">
<Lock size={18} color={isDark ? "#94a3b8" : "#64748b"} />
<TextInput
className="flex-1 ml-3 text-foreground"
placeholder="••••••••"
placeholderTextColor={isDark ? "#475569" : "#94a3b8"}
value={form.password}
onChangeText={(v) => updateForm("password", v)}
secureTextEntry
/>
</View>
</View>
<Button
className="h-14 bg-primary rounded-[10px] shadow-lg shadow-primary/30 mt-6"
onPress={handleCreate}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="white" />
) : (
<>
<Text className="text-white font-bold text-base mr-2">
Create User
</Text>
<ArrowRight color="white" size={18} strokeWidth={2.5} />
</>
)}
</Button>
</View>
</ScrollView>
</KeyboardAvoidingView>
<PickerModal
visible={showRolePicker}
onClose={() => setShowRolePicker(false)}
title="Select System Role"
>
{ROLES.map((role) => (
<SelectOption
key={role}
label={role}
value={role}
selected={form.role === role}
onSelect={(v) => {
updateForm("role", v);
setShowRolePicker(false);
}}
/>
))}
</PickerModal>
</ScreenWrapper>
);
}