229 lines
7.0 KiB
TypeScript
229 lines
7.0 KiB
TypeScript
import React, { useState } from "react";
|
|
import {
|
|
View,
|
|
Text,
|
|
StyleSheet,
|
|
ScrollView,
|
|
TouchableOpacity,
|
|
ToastAndroid,
|
|
} from "react-native";
|
|
import LucideIcon from '../../components/common/LucideIcon';
|
|
import { useNavigation } from "@react-navigation/native";
|
|
import { responsiveFontSize } from "react-native-responsive-dimensions";
|
|
|
|
import { CustomInput, CustomButton } from "../../components/common/Custom";
|
|
import { useTheme } from "../../hooks/ui/useTheme";
|
|
import { useCreate } from "../../hooks/core/useCreate";
|
|
import AppView from "../../components/AppView";
|
|
import FontsFamily from "../../constents/FontsFamily";
|
|
import { APPLY_ORGANIZER_URL } from "../../utils/ApiUrls";
|
|
|
|
type OrganizerApplyErrors = {
|
|
idName?: string;
|
|
idNumber?: string;
|
|
issueDate?: string;
|
|
};
|
|
|
|
const isValidIdType = (value: string) => /^[A-Za-z][A-Za-z\s-]{1,}$/.test(value.trim());
|
|
const isValidIdNumber = (value: string) => /^[A-Za-z0-9-]{6,24}$/.test(value.trim());
|
|
const formatIssueDateInput = (value: string) => {
|
|
const digits = value.replace(/\D/g, "").slice(0, 8);
|
|
|
|
if (digits.length <= 2) return digits;
|
|
if (digits.length <= 4) return `${digits.slice(0, 2)}-${digits.slice(2)}`;
|
|
return `${digits.slice(0, 2)}-${digits.slice(2, 4)}-${digits.slice(4)}`;
|
|
};
|
|
|
|
const isValidIssueDate = (value: string) => {
|
|
if (!/^\d{2}-\d{2}-\d{4}$/.test(value)) return false;
|
|
const [day, month, year] = value.split("-").map(Number);
|
|
const parsed = new Date(Date.UTC(year, month - 1, day));
|
|
const isRealDate =
|
|
parsed.getUTCFullYear() === year &&
|
|
parsed.getUTCMonth() + 1 === month &&
|
|
parsed.getUTCDate() === day;
|
|
if (!isRealDate) return false;
|
|
|
|
const today = new Date();
|
|
const todayUtc = Date.UTC(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
|
|
return parsed.getTime() <= todayUtc;
|
|
};
|
|
|
|
const BecomeOrg = () => {
|
|
const theme = useTheme();
|
|
const navigation = useNavigation<any>();
|
|
|
|
const [idName, setIdName] = useState("");
|
|
const [idNumber, setIdNumber] = useState("");
|
|
const [issueDate, setIssueDate] = useState("");
|
|
const [errors, setErrors] = useState<OrganizerApplyErrors>({});
|
|
|
|
const { mutate: applyOrganizer, isPending: loading } = useCreate<any, any>(APPLY_ORGANIZER_URL, {
|
|
onSuccess: (res) => {
|
|
console.log("api res",res)
|
|
if (res.status === "success") {
|
|
ToastAndroid.show("Application submitted successfully!", ToastAndroid.SHORT);
|
|
navigation.navigate("Home");
|
|
} else {
|
|
ToastAndroid.show(res.message || "Failed to submit application", ToastAndroid.SHORT);
|
|
}
|
|
},
|
|
onError: (err: any) => {
|
|
ToastAndroid.show(err.message || "An error occurred", ToastAndroid.SHORT);
|
|
}
|
|
});
|
|
|
|
const handleChange = (field: keyof OrganizerApplyErrors, value: string) => {
|
|
if (field === "idName") setIdName(value);
|
|
if (field === "idNumber") setIdNumber(value);
|
|
if (field === "issueDate") setIssueDate(value);
|
|
if (errors[field]) {
|
|
setErrors((prev) => ({ ...prev, [field]: "" }));
|
|
}
|
|
};
|
|
|
|
const validateForm = () => {
|
|
const nextErrors: OrganizerApplyErrors = {};
|
|
|
|
if (!isValidIdType(idName)) {
|
|
nextErrors.idName = "Enter a valid ID type (min 2 letters).";
|
|
}
|
|
if (!isValidIdNumber(idNumber)) {
|
|
nextErrors.idNumber = "ID number must be 6-24 chars (A-Z, 0-9, -).";
|
|
}
|
|
if (!isValidIssueDate(issueDate)) {
|
|
nextErrors.issueDate = "Use valid date DD-MM-YYYY (not future).";
|
|
}
|
|
|
|
setErrors(nextErrors);
|
|
return Object.keys(nextErrors).length === 0;
|
|
};
|
|
|
|
const handleSubmit = () => {
|
|
if (!validateForm()) {
|
|
ToastAndroid.show("Please fix the highlighted fields.", ToastAndroid.SHORT);
|
|
return;
|
|
}
|
|
applyOrganizer({
|
|
idName: idName.trim(),
|
|
idNumber: idNumber.trim(),
|
|
issueDate: issueDate.trim(),
|
|
});
|
|
};
|
|
|
|
return (
|
|
<AppView keyboardVerticalOffset={60} dismissKeyboardOnTap={false}>
|
|
<View style={styles.header}>
|
|
<TouchableOpacity onPress={() => navigation.goBack()} style={styles.backButton}>
|
|
<LucideIcon name="arrow-back" size={24} color={theme.colors.textPrimary} />
|
|
</TouchableOpacity>
|
|
<Text style={[styles.headerTitle, { color: theme.colors.textPrimary }]}>Become Organizer</Text>
|
|
</View>
|
|
<ScrollView
|
|
style={styles.scrollView}
|
|
contentContainerStyle={styles.scrollContent}
|
|
keyboardShouldPersistTaps="handled"
|
|
keyboardDismissMode="on-drag"
|
|
showsVerticalScrollIndicator={false}
|
|
>
|
|
<View style={[styles.card, { backgroundColor: theme.colors.surface, borderColor: theme.colors.border }]}>
|
|
<Text style={[styles.title, { color: theme.colors.textPrimary }]}>ID Verification</Text>
|
|
<Text style={[styles.subtitle, { color: theme.colors.textSecondary }]}>
|
|
Please provide your official identification details to become a verified organizer.
|
|
</Text>
|
|
|
|
<CustomInput
|
|
label="ID Type"
|
|
placeholder="e.g., National ID"
|
|
value={idName}
|
|
onChangeText={(val) => handleChange("idName", val)}
|
|
icon="person-outline"
|
|
error={errors.idName}
|
|
/>
|
|
|
|
<CustomInput
|
|
label="ID Number"
|
|
placeholder="Enter your ID number"
|
|
value={idNumber}
|
|
onChangeText={(val) => handleChange("idNumber", val.toUpperCase())}
|
|
autoCapitalize="characters"
|
|
icon="card-outline"
|
|
error={errors.idNumber}
|
|
/>
|
|
|
|
<CustomInput
|
|
label="Issue Date"
|
|
placeholder="DD-MM-YYYY"
|
|
value={issueDate}
|
|
onChangeText={(val) => handleChange("issueDate", formatIssueDateInput(val))}
|
|
icon="calendar-outline"
|
|
error={errors.issueDate}
|
|
/>
|
|
|
|
<CustomButton
|
|
title="Submit Application"
|
|
onPress={handleSubmit}
|
|
loading={loading}
|
|
style={styles.submitBtn}
|
|
icon="send-outline"
|
|
/>
|
|
</View>
|
|
</ScrollView>
|
|
</AppView>
|
|
);
|
|
};
|
|
|
|
const styles = StyleSheet.create({
|
|
header: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
paddingHorizontal: 24,
|
|
paddingVertical: 16,
|
|
},
|
|
backButton: {
|
|
marginRight: 16,
|
|
padding: 4,
|
|
},
|
|
headerTitle: {
|
|
fontSize: responsiveFontSize(2.4),
|
|
fontFamily: FontsFamily.nunitoBold,
|
|
},
|
|
scrollView: {
|
|
flex: 1,
|
|
},
|
|
scrollContent: {
|
|
flexGrow: 1,
|
|
paddingHorizontal: 20,
|
|
paddingTop: 10,
|
|
justifyContent: "flex-start",
|
|
paddingBottom: 40,
|
|
},
|
|
card: {
|
|
padding: 24,
|
|
borderRadius: 24,
|
|
borderWidth: 1,
|
|
elevation: 2,
|
|
shadowColor: '#000',
|
|
shadowOpacity: 0.04,
|
|
shadowRadius: 12,
|
|
shadowOffset: { width: 0, height: 4 },
|
|
},
|
|
title: {
|
|
fontSize: responsiveFontSize(2.6),
|
|
fontFamily: FontsFamily.nunitoBold,
|
|
marginBottom: 8,
|
|
textAlign: "center",
|
|
},
|
|
subtitle: {
|
|
fontSize: responsiveFontSize(1.6),
|
|
fontFamily: FontsFamily.nunitoRegular,
|
|
textAlign: "center",
|
|
marginBottom: 30,
|
|
},
|
|
submitBtn: {
|
|
marginTop: 12,
|
|
}
|
|
});
|
|
|
|
export default BecomeOrg;
|