251 lines
No EOL
9.5 KiB
TypeScript
251 lines
No EOL
9.5 KiB
TypeScript
import React, { useState } from 'react';
|
||
import { View, ScrollView, Dimensions } from 'react-native';
|
||
import { useMutation } from '@tanstack/react-query';
|
||
import { Formik } from 'formik';
|
||
import * as Yup from 'yup';
|
||
import { tw, MyText, MyTouchableOpacity , Checkbox , MyTextInput , LoadingDialog } from 'common-ui';
|
||
import { trpc } from '../trpc-client';
|
||
import LocationAttacher from './LocationAttacher';
|
||
import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view';
|
||
import type { AddressFormProps } from '@packages/shared';
|
||
|
||
const AddressForm: React.FC<AddressFormProps> = ({ onSuccess, initialValues, isEdit = false }) => {
|
||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||
const [submitError, setSubmitError] = useState<string | null>(null);
|
||
const [showGoogleMapsField, setShowGoogleMapsField] = useState(!!initialValues?.googleMapsUrl);
|
||
const [currentLocation, setCurrentLocation] = useState<{ latitude: number; longitude: number } | null>(
|
||
initialValues?.latitude && initialValues?.longitude
|
||
? { latitude: initialValues.latitude, longitude: initialValues.longitude }
|
||
: null
|
||
);
|
||
|
||
const createAddressMutation = trpc.user.address.createAddress.useMutation({
|
||
onSuccess: (data) => {
|
||
setIsSubmitting(false);
|
||
const addressId = data?.data?.id;
|
||
// Delay to allow modal to close animation
|
||
setTimeout(() => onSuccess(addressId), 350);
|
||
},
|
||
onError: (error: any) => {
|
||
setIsSubmitting(false);
|
||
setSubmitError(error.message || 'Failed to save address');
|
||
},
|
||
});
|
||
|
||
const updateAddressMutation = trpc.user.address.updateAddress.useMutation({
|
||
onSuccess: () => {
|
||
setIsSubmitting(false);
|
||
// Delay to allow modal to close animation
|
||
setTimeout(() => onSuccess(), 350);
|
||
},
|
||
onError: (error: any) => {
|
||
setIsSubmitting(false);
|
||
setSubmitError(error.message || 'Failed to update address');
|
||
},
|
||
});
|
||
|
||
const validationSchema = Yup.object({
|
||
name: Yup.string().required('Name is required'),
|
||
phone: Yup.string().required('Phone is required').matches(/^\d{10}$/, 'Phone must be 10 digits'),
|
||
addressLine1: Yup.string().required('Address Line 1 is required'),
|
||
addressLine2: Yup.string(),
|
||
city: Yup.string().required('City is required'),
|
||
state: Yup.string().required('State is required'),
|
||
pincode: Yup.string().required('Pincode is required').matches(/^\d{6}$/, 'Pincode must be 6 digits'),
|
||
isDefault: Yup.boolean(),
|
||
});
|
||
|
||
return (
|
||
<KeyboardAwareScrollView
|
||
style={[tw`px-4 pt-4 pb-32 bg-white`, {maxHeight: Dimensions.get('window').height * 0.7}]}
|
||
keyboardShouldPersistTaps="always"
|
||
keyboardDismissMode="on-drag"
|
||
>
|
||
<MyText style={tw`text-xl font-bold mb-4`}>{isEdit ? 'Edit Address' : 'Add Address'}</MyText>
|
||
|
||
{/* Service Area Notice */}
|
||
<View style={tw`bg-amber-50 border border-amber-200 rounded-lg p-3 mb-4`}>
|
||
<View style={tw`flex-row items-center`}>
|
||
<MyText style={tw`text-amber-600 mr-2`}>ℹ️</MyText>
|
||
<MyText style={tw`text-amber-800 text-sm flex-1`}>
|
||
We currently serve only in Mahabubnagar town
|
||
</MyText>
|
||
</View>
|
||
</View>
|
||
|
||
{/* Submit Error Message */}
|
||
{submitError && (
|
||
<View style={tw`bg-red-50 border border-red-200 rounded-lg p-3 mb-4`}>
|
||
<MyText style={tw`text-red-600 text-sm`}>{submitError}</MyText>
|
||
</View>
|
||
)}
|
||
<Formik
|
||
initialValues={initialValues || {
|
||
name: '',
|
||
phone: '',
|
||
addressLine1: '',
|
||
addressLine2: '',
|
||
city: 'Mahabubnagar',
|
||
state: 'Telangana',
|
||
pincode: '509001',
|
||
isDefault: false,
|
||
latitude: undefined,
|
||
longitude: undefined,
|
||
googleMapsUrl: '',
|
||
}}
|
||
validationSchema={validationSchema}
|
||
onSubmit={(values) => {
|
||
setIsSubmitting(true);
|
||
setSubmitError(null);
|
||
const payload = {
|
||
...values,
|
||
latitude: currentLocation?.latitude,
|
||
longitude: currentLocation?.longitude,
|
||
googleMapsUrl: values.googleMapsUrl || undefined,
|
||
};
|
||
if (isEdit && initialValues?.id) {
|
||
updateAddressMutation.mutate({ id: initialValues.id, ...payload });
|
||
} else {
|
||
createAddressMutation.mutate(payload);
|
||
}
|
||
}}
|
||
>
|
||
{({ handleChange, handleBlur, handleSubmit, values, errors, touched, setFieldValue }) => (
|
||
<View style={tw`flex-col gap-2`}>
|
||
<MyTextInput
|
||
placeholder="Name"
|
||
shrunkPadding={true}
|
||
onChangeText={handleChange('name')}
|
||
onBlur={handleBlur('name')}
|
||
value={values.name}
|
||
/>
|
||
{touched.name && errors.name && <MyText style={tw`text-red-500 mb-2`}>{errors.name}</MyText>}
|
||
|
||
<MyTextInput
|
||
placeholder="Phone"
|
||
shrunkPadding={true}
|
||
keyboardType="phone-pad"
|
||
onChangeText={handleChange('phone')}
|
||
onBlur={handleBlur('phone')}
|
||
value={values.phone}
|
||
/>
|
||
{touched.phone && errors.phone && <MyText style={tw`text-red-500 mb-2`}>{errors.phone}</MyText>}
|
||
|
||
<MyTextInput
|
||
placeholder="Address Line 1"
|
||
shrunkPadding={true}
|
||
onChangeText={handleChange('addressLine1')}
|
||
onBlur={handleBlur('addressLine1')}
|
||
value={values.addressLine1}
|
||
/>
|
||
{touched.addressLine1 && errors.addressLine1 && <MyText style={tw`text-red-500 mb-2`}>{errors.addressLine1}</MyText>}
|
||
|
||
<MyTextInput
|
||
placeholder="Address Line 2 (Optional)"
|
||
shrunkPadding={true}
|
||
onChangeText={handleChange('addressLine2')}
|
||
onBlur={handleBlur('addressLine2')}
|
||
value={values.addressLine2}
|
||
/>
|
||
|
||
<MyTextInput
|
||
placeholder="City"
|
||
shrunkPadding={true}
|
||
onChangeText={handleChange('city')}
|
||
onBlur={handleBlur('city')}
|
||
value={values.city}
|
||
disabled={true}
|
||
/>
|
||
{touched.city && errors.city && <MyText style={tw`text-red-500 mb-2`}>{errors.city}</MyText>}
|
||
|
||
<MyTextInput
|
||
placeholder="State"
|
||
shrunkPadding={true}
|
||
onChangeText={handleChange('state')}
|
||
onBlur={handleBlur('state')}
|
||
value={values.state}
|
||
disabled={true}
|
||
/>
|
||
{touched.state && errors.state && <MyText style={tw`text-red-500 mb-2`}>{errors.state}</MyText>}
|
||
|
||
<MyTextInput
|
||
placeholder="Pincode"
|
||
shrunkPadding={true}
|
||
keyboardType="numeric"
|
||
onChangeText={handleChange('pincode')}
|
||
onBlur={handleBlur('pincode')}
|
||
value={values.pincode}
|
||
disabled={true}
|
||
/>
|
||
{touched.pincode && errors.pincode && <MyText style={tw`text-red-500 mb-2`}>{errors.pincode}</MyText>}
|
||
|
||
<LocationAttacher
|
||
initialLocation={currentLocation || undefined}
|
||
initialGoogleMapsUrl={values.googleMapsUrl}
|
||
googleMapsUrl={values.googleMapsUrl}
|
||
onGoogleMapsUrlChange={(url) => setFieldValue('googleMapsUrl', url)}
|
||
onLocationChange={(location) => setCurrentLocation(location)}
|
||
disabled={false}
|
||
/>
|
||
|
||
{!showGoogleMapsField && (
|
||
<MyTouchableOpacity
|
||
onPress={() => setShowGoogleMapsField(true)}
|
||
disabled={false}
|
||
style={tw`mb-1`}
|
||
>
|
||
<MyText style={tw`text-blue-500 text-sm font-medium`}>
|
||
Attach with Google Maps
|
||
</MyText>
|
||
</MyTouchableOpacity>
|
||
)}
|
||
|
||
{showGoogleMapsField && (
|
||
<View style={tw`mb-2`}>
|
||
<MyText style={tw`text-gray-500 text-xs mb-2`}>
|
||
1. Open Google Maps and Find location{'\n'}
|
||
2. Long press the desired location{'\n'}
|
||
3. Click on Share and Click on Copy{'\n'}
|
||
4. Paste the copied url here in the field.
|
||
</MyText>
|
||
<MyTextInput
|
||
placeholder="Google Maps Shared URL"
|
||
shrunkPadding={true}
|
||
onChangeText={handleChange('googleMapsUrl')}
|
||
onBlur={handleBlur('googleMapsUrl')}
|
||
value={values.googleMapsUrl}
|
||
/>
|
||
</View>
|
||
)}
|
||
|
||
<View style={tw`flex-row items-center mb-4`}>
|
||
<Checkbox
|
||
checked={values.isDefault}
|
||
onPress={() => setFieldValue('isDefault', !values.isDefault)}
|
||
/>
|
||
<MyText style={tw`ml-2`}>Set as default address</MyText>
|
||
</View>
|
||
|
||
<MyTouchableOpacity
|
||
style={tw`bg-indigo-600 p-3 rounded ${isSubmitting ? 'opacity-50' : ''}`}
|
||
onPress={() => handleSubmit()}
|
||
disabled={isSubmitting}
|
||
>
|
||
<MyText style={tw`text-white text-center font-bold`}>
|
||
{isSubmitting ? (isEdit ? 'Updating...' : 'Adding...') : (isEdit ? 'Update Address' : 'Add Address')}
|
||
</MyText>
|
||
</MyTouchableOpacity>
|
||
<View style={tw`h-8`}></View>
|
||
</View>
|
||
)}
|
||
</Formik>
|
||
|
||
<LoadingDialog
|
||
open={isSubmitting}
|
||
message={isEdit ? "Updating address..." : "Adding address..."}
|
||
/>
|
||
</KeyboardAwareScrollView>
|
||
);
|
||
};
|
||
|
||
export default AddressForm; |