BaaS_Driver_Android_App/app/payments/selectAmount.tsx

446 lines
13 KiB
TypeScript

import React, { useState } from "react";
import {
View,
Text,
StyleSheet,
TouchableOpacity,
TextInput,
ScrollView,
Platform,
KeyboardAvoidingView,
Alert,
} from "react-native";
import { useDispatch, useSelector } from "react-redux";
import { Formik } from "formik";
import * as Yup from "yup";
import Header from "../../components/common/Header";
import { RootState } from "@/store/rootReducer";
import { BASE_URL, payments } from "@/constants/config";
import api from "@/services/axiosClient";
import { setPaymentOrder } from "@/store/paymentSlice";
import { Overlay } from "@/components/common/Overlay";
import { useRouter } from "expo-router";
import { useSocket } from "@/contexts/SocketContext";
// Validation schema
const validationSchema = Yup.object().shape({
paymentType: Yup.string().required("Please select a payment option"),
customAmount: Yup.string().when("paymentType", {
is: "custom",
then: (schema) =>
schema
.required("Amount is required")
.test("valid-number", "Please enter a valid amount", (value) => {
const numValue = parseFloat(value);
return !isNaN(numValue) && numValue > 0;
})
.test(
"min-amount",
`Minimum amount is ₹${payments.MIN_AMOUNT}`,
(value) => {
const numValue = parseFloat(value);
return !isNaN(numValue) && numValue >= payments.MIN_AMOUNT;
}
),
otherwise: (schema) => schema.notRequired(),
}),
});
const SelectAmountScreen = () => {
// Fetch due amount from Redux
const dueAmount = useSelector(
(state: RootState) => state.payments?.due_amount || 0
);
const router = useRouter();
const [isFetching, setIsFetching] = useState<boolean>(false);
const { registerTransaction } = useSocket();
const quickAmounts = [50, 100, 500, 1000];
const initialValues = {
paymentType: "due",
customAmount: "",
};
const dispatch = useDispatch();
const handleSubmit = async (values: any) => {
setIsFetching(true);
const paymentAmount =
values.paymentType === "due"
? dueAmount
: parseFloat(values.customAmount);
console.log(values, "values");
try {
const res = await api.post(`/api/v1/create-order`, {
amount: paymentAmount,
});
console.log(res.data, "response from select amount");
if (res.data && res.data.success) {
dispatch(setPaymentOrder(res.data.data));
try {
await registerTransaction(res.data.data.transaction_id);
console.log("Transaction registered successfully");
// Navigate to payment screen
router.push("/payments/payEmi");
} catch (socketError) {
throw socketError;
}
} else {
throw new Error("Failed to create order");
}
} catch (err) {
console.error(err, "Error in creating order.");
} finally {
setIsFetching(false);
}
console.log("Payment Amount:", paymentAmount);
};
return (
<>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleSubmit}
>
{({
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
setFieldValue,
isValid,
dirty,
}) => {
const handleQuickAmountPress = (amount: number) => {
const currentAmount = values.customAmount
? parseFloat(values.customAmount)
: 0;
const newAmount = currentAmount + amount;
setFieldValue("customAmount", newAmount.toString());
};
const getPaymentAmount = () => {
if (values.paymentType === "due") {
return dueAmount;
}
return values.customAmount ? parseFloat(values.customAmount) : 0;
};
const isPayButtonEnabled = () => {
if (values.paymentType === "due") return true;
return isValid && dirty && values.customAmount;
};
return (
<KeyboardAvoidingView
style={styles.container}
behavior={Platform.OS === "ios" ? "padding" : "height"}
keyboardVerticalOffset={Platform.OS === "ios" ? 0 : 20}
>
<Header title="Select Amount" showBackButton={true} />
<ScrollView
style={styles.content}
showsVerticalScrollIndicator={false}
>
<View style={styles.selectAmountContainer}>
<TouchableOpacity
style={[
styles.option,
values.paymentType === "due" && styles.selectedOption,
]}
onPress={() => setFieldValue("paymentType", "due")}
>
<View style={styles.radioContainer}>
<View
style={[
styles.radioDot,
values.paymentType === "due" &&
styles.selectedRadioDot,
]}
>
{values.paymentType === "due" && (
<View style={styles.radioInner} />
)}
</View>
<Text style={styles.radioLabel}>Pay amount due</Text>
</View>
<Text style={styles.amountText}>
{dueAmount?.toFixed(2)}
</Text>
</TouchableOpacity>
<View
style={[
styles.customOption,
values.paymentType === "custom" && styles.selectedOption,
touched.customAmount &&
errors.customAmount &&
styles.errorOption,
]}
>
<TouchableOpacity
style={styles.radioContainer}
onPress={() => setFieldValue("paymentType", "custom")}
>
<View
style={[
styles.radioDot,
values.paymentType === "custom" &&
styles.selectedRadioDot,
]}
>
{values.paymentType === "custom" && (
<View style={styles.radioInner} />
)}
</View>
<Text style={styles.radioLabel}>Enter custom amount</Text>
</TouchableOpacity>
<View style={styles.inputContainer}>
<TextInput
style={[
styles.textInput,
touched.customAmount &&
errors.customAmount &&
styles.errorInput,
]}
value={values.customAmount}
onChangeText={(text) => {
handleChange("customAmount")(text);
setFieldValue("paymentType", "custom");
}}
onBlur={handleBlur("customAmount")}
placeholder="₹"
placeholderTextColor="#94A3B8"
keyboardType="numeric"
onFocus={() => setFieldValue("paymentType", "custom")}
/>
<View style={styles.helperContainer}>
<Text
style={[
styles.helperText,
touched.customAmount &&
errors.customAmount &&
styles.errorText,
]}
>
{touched.customAmount && errors.customAmount
? errors.customAmount
: `Minimum: ₹${payments.MIN_AMOUNT}`}
</Text>
</View>
</View>
<View style={styles.chipsContainer}>
{quickAmounts.map((amount) => (
<TouchableOpacity
key={amount}
style={styles.chip}
onPress={() => handleQuickAmountPress(amount)}
>
<Text style={styles.chipText}>+{amount}</Text>
</TouchableOpacity>
))}
</View>
</View>
{/* General form error */}
{touched.paymentType && errors.paymentType && (
<Text style={styles.generalErrorText}>
{errors.paymentType}
</Text>
)}
</View>
</ScrollView>
{/* Pay Button */}
<View style={styles.buttonContainer}>
<TouchableOpacity
style={[
styles.payButton,
!isPayButtonEnabled() && styles.disabledButton,
]}
onPress={() => handleSubmit()}
disabled={!isPayButtonEnabled()}
>
{getPaymentAmount() < payments.MIN_AMOUNT ? (
<Text style={styles.payButtonText}>Select Amount</Text>
) : (
<Text style={styles.payButtonText}>
Pay {getPaymentAmount().toFixed(2)}
</Text>
)}
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
);
}}
</Formik>
<Overlay isUploading={isFetching} />
</>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#F3F4F6",
},
content: {
flex: 1,
paddingHorizontal: 16,
},
selectAmountContainer: {
paddingTop: 16,
gap: 16,
},
option: {
backgroundColor: "#FCFCFC",
borderRadius: 8,
padding: 16,
borderWidth: 1,
borderColor: "transparent",
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
minHeight: 56,
},
selectedOption: {
borderColor: "#009E71",
},
errorOption: {
borderColor: "#EF4444",
},
customOption: {
backgroundColor: "#FCFCFC",
borderRadius: 8,
padding: 16,
borderWidth: 1,
borderColor: "transparent",
minHeight: 180,
},
radioContainer: {
flexDirection: "row",
alignItems: "center",
flex: 1,
marginBottom: 16,
},
radioDot: {
width: 18,
height: 18,
borderRadius: 9,
borderWidth: 2,
borderColor: "#D1D5DB",
alignItems: "center",
justifyContent: "center",
marginRight: 12,
},
selectedRadioDot: {
borderColor: "#009E71",
},
radioInner: {
width: 8,
height: 8,
borderRadius: 4,
backgroundColor: "#009E71",
},
radioLabel: {
fontSize: 14,
color: "#252936",
fontWeight: "400",
},
amountText: {
fontSize: 14,
color: "#252936",
fontWeight: "400",
},
inputContainer: {
marginBottom: 16,
},
textInput: {
backgroundColor: "#FFFFFF",
borderWidth: 1,
borderColor: "#D8DDE7",
borderRadius: 4,
paddingHorizontal: 8,
paddingVertical: 10,
fontSize: 14,
color: "#252936",
height: 40,
},
errorInput: {
borderColor: "#EF4444",
},
helperContainer: {
marginTop: 4,
},
helperText: {
fontSize: 14,
color: "#565F70",
},
errorText: {
color: "#EF4444",
},
generalErrorText: {
fontSize: 14,
color: "#EF4444",
textAlign: "center",
marginTop: 8,
},
chipsContainer: {
flexDirection: "row",
gap: 8,
flexWrap: "wrap",
},
chip: {
backgroundColor: "#F3F4F6",
borderWidth: 1,
borderColor: "#D8DDE7",
borderRadius: 4,
paddingHorizontal: 8,
paddingVertical: 4,
minWidth: 68,
alignItems: "center",
justifyContent: "center",
height: 28,
},
chipText: {
fontSize: 14,
color: "#252936",
fontWeight: "500",
},
buttonContainer: {
padding: 16,
backgroundColor: "#F3F4F6",
},
payButton: {
backgroundColor: "#009E71",
borderRadius: 4,
paddingVertical: 8,
paddingHorizontal: 16,
alignItems: "center",
justifyContent: "center",
height: 40,
},
disabledButton: {
backgroundColor: "#94A3B8",
},
payButtonText: {
fontSize: 14,
color: "#FCFCFC",
fontWeight: "500",
},
});
export default SelectAmountScreen;