theme update latest project
This commit is contained in:
+160
-126
@@ -3,192 +3,226 @@ import {
|
||||
View,
|
||||
Text,
|
||||
StyleSheet,
|
||||
Alert,
|
||||
ActivityIndicator,
|
||||
ScrollView,
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
TouchableOpacity,
|
||||
ToastAndroid,
|
||||
} from "react-native";
|
||||
import * as Keychain from "react-native-keychain";
|
||||
import { CustomInput, CustomButton } from "../../components/common/Custom";
|
||||
import colors from "../../components/colors";
|
||||
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 { Ionicons } from "@react-native-vector-icons/ionicons";
|
||||
import { BASE_URL } from "../../utils/ApiUrls";
|
||||
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 [loading, setLoading] = useState(false);
|
||||
const [errors, setErrors] = useState<OrganizerApplyErrors>({});
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!idName || !idNumber || !issueDate) {
|
||||
ToastAndroid.show("Please fill all fields", ToastAndroid.SHORT);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
const credentials = await Keychain.getGenericPassword();
|
||||
if (!credentials) {
|
||||
ToastAndroid.show("You are not logged in. Please log in to continue.", ToastAndroid.SHORT);
|
||||
setLoading(false);
|
||||
// Optionally navigate to login screen
|
||||
// navigation.navigate("Login");
|
||||
return;
|
||||
}
|
||||
const token = credentials.password;
|
||||
|
||||
const response = await fetch(
|
||||
`${BASE_URL}organizer/registration/apply`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
idName,
|
||||
idNumber,
|
||||
issueDate,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result.status === "success") {
|
||||
ToastAndroid.show("Organizer registration application submitted successfully!", ToastAndroid.SHORT);
|
||||
setIdName("");
|
||||
setIdNumber("");
|
||||
setIssueDate("");
|
||||
navigation.navigate("Home" as never);
|
||||
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(result.message || "Failed to submit application", ToastAndroid.SHORT);
|
||||
ToastAndroid.show(res.message || "Failed to submit application", ToastAndroid.SHORT);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ Registration error:", error);
|
||||
ToastAndroid.show("An unexpected error occurred. Please try again.", ToastAndroid.SHORT);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
},
|
||||
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 barStyle="dark-content">
|
||||
<AppView keyboardVerticalOffset={60} dismissKeyboardOnTap={false}>
|
||||
<View style={styles.header}>
|
||||
<TouchableOpacity onPress={() => navigation.goBack()} style={styles.backButton}>
|
||||
<Ionicons name="arrow-back" size={24} color={'#000'} />
|
||||
<LucideIcon name="arrow-back" size={24} color={theme.colors.textPrimary} />
|
||||
</TouchableOpacity>
|
||||
<Text style={styles.headerTitle}>Become an Organizer</Text>
|
||||
<View style={styles.placeholder} />
|
||||
<Text style={[styles.headerTitle, { color: theme.colors.textPrimary }]}>Become Organizer</Text>
|
||||
</View>
|
||||
<KeyboardAvoidingView
|
||||
style={{ flex: 1 }}
|
||||
behavior={Platform.OS === "ios" ? "padding" : undefined}
|
||||
keyboardVerticalOffset={Platform.OS === "ios" ? 60 : 0}
|
||||
<ScrollView
|
||||
style={styles.scrollView}
|
||||
contentContainerStyle={styles.scrollContent}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
keyboardDismissMode="on-drag"
|
||||
showsVerticalScrollIndicator={false}
|
||||
>
|
||||
<ScrollView
|
||||
contentContainerStyle={styles.container}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
>
|
||||
<Text style={styles.title}>Submit Your ID for Verification</Text>
|
||||
<Text style={styles.subtitle}>
|
||||
<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
|
||||
placeholder="ID Name (e.g., Aadhaar, PAN Card)"
|
||||
label="ID Type"
|
||||
placeholder="e.g., Aadhaar, PAN Card"
|
||||
value={idName}
|
||||
onChangeText={setIdName}
|
||||
onChangeText={(val) => handleChange("idName", val)}
|
||||
icon="person-outline"
|
||||
error={""}
|
||||
error={errors.idName}
|
||||
/>
|
||||
|
||||
<CustomInput
|
||||
placeholder="ID Number"
|
||||
label="ID Number"
|
||||
placeholder="Enter your ID number"
|
||||
value={idNumber}
|
||||
onChangeText={setIdNumber}
|
||||
onChangeText={(val) => handleChange("idNumber", val.toUpperCase())}
|
||||
autoCapitalize="characters"
|
||||
icon="card-outline"
|
||||
error=""
|
||||
error={errors.idNumber}
|
||||
/>
|
||||
|
||||
<CustomInput
|
||||
placeholder="Issue Date (YYYY-MM-DD)"
|
||||
label="Issue Date"
|
||||
placeholder="DD-MM-YYYY"
|
||||
value={issueDate}
|
||||
onChangeText={setIssueDate}
|
||||
onChangeText={(val) => handleChange("issueDate", formatIssueDateInput(val))}
|
||||
icon="calendar-outline"
|
||||
error=""
|
||||
error={errors.issueDate}
|
||||
/>
|
||||
|
||||
<CustomButton
|
||||
title={loading ? "Submitting..." : "Request Organizer Role"}
|
||||
title="Submit Application"
|
||||
onPress={handleSubmit}
|
||||
|
||||
loading={loading}
|
||||
style={styles.submitBtn}
|
||||
icon="send-outline"
|
||||
buttonBackground={colors.pastelBlue}
|
||||
style={{ opacity: loading ? 0.6 : 1, marginTop: 20 }}
|
||||
/>
|
||||
|
||||
{loading && (
|
||||
<ActivityIndicator
|
||||
style={{ marginTop: 15 }}
|
||||
size="large"
|
||||
color={colors.primary}
|
||||
/>
|
||||
)}
|
||||
</ScrollView>
|
||||
</KeyboardAvoidingView>
|
||||
</View>
|
||||
</ScrollView>
|
||||
</AppView>
|
||||
);
|
||||
};
|
||||
|
||||
export default BecomeOrg;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flexGrow: 1,
|
||||
padding: 20,
|
||||
justifyContent: "center",
|
||||
},
|
||||
title: {
|
||||
fontSize: 24,
|
||||
fontWeight: "700",
|
||||
marginBottom: 10,
|
||||
textAlign: "center",
|
||||
color: colors.textDark,
|
||||
},
|
||||
subtitle: {
|
||||
fontSize: 16,
|
||||
textAlign: "center",
|
||||
color: colors.textDark,
|
||||
marginBottom: 30,
|
||||
},
|
||||
header: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 12,
|
||||
backgroundColor: '#FFF',
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: '#E0E0E0',
|
||||
paddingHorizontal: 24,
|
||||
paddingVertical: 16,
|
||||
},
|
||||
backButton: {
|
||||
padding: 5,
|
||||
marginRight: 16,
|
||||
padding: 4,
|
||||
},
|
||||
headerTitle: {
|
||||
fontSize: responsiveFontSize(2.4),
|
||||
fontFamily: FontsFamily.nunitoBold,
|
||||
},
|
||||
scrollView: {
|
||||
flex: 1,
|
||||
fontSize: 20,
|
||||
fontWeight: "600",
|
||||
color: "#000",
|
||||
},
|
||||
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",
|
||||
},
|
||||
placeholder: {
|
||||
width: 24, // to balance the back button
|
||||
subtitle: {
|
||||
fontSize: responsiveFontSize(1.6),
|
||||
fontFamily: FontsFamily.nunitoRegular,
|
||||
textAlign: "center",
|
||||
marginBottom: 30,
|
||||
},
|
||||
submitBtn: {
|
||||
marginTop: 12,
|
||||
}
|
||||
});
|
||||
|
||||
export default BecomeOrg;
|
||||
|
||||
Reference in New Issue
Block a user