enh
This commit is contained in:
parent
ca8297af9b
commit
001dd62aa5
2 changed files with 75 additions and 16 deletions
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState } from 'react';
|
||||
import { View, Alert, ScrollView } from 'react-native';
|
||||
import { View, ScrollView } from 'react-native';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { Formik } from 'formik';
|
||||
import * as Yup from 'yup';
|
||||
|
|
@ -30,39 +30,44 @@ const AddressForm: React.FC<AddressFormProps> = ({ onSuccess, initialValues, isE
|
|||
const [locationLoading, setLocationLoading] = useState(false);
|
||||
const [locationError, setLocationError] = useState<string | null>(null);
|
||||
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 [locationSuccess, setLocationSuccess] = useState(false);
|
||||
|
||||
const createAddressMutation = trpc.user.address.createAddress.useMutation({
|
||||
onSuccess: (data) => {
|
||||
setIsSubmitting(false);
|
||||
const addressId = data?.data?.id;
|
||||
setTimeout(() => onSuccess(addressId), 100);
|
||||
// Delay to allow modal to close animation
|
||||
setTimeout(() => onSuccess(addressId), 350);
|
||||
},
|
||||
onError: (error: any) => {
|
||||
setIsSubmitting(false);
|
||||
Alert.alert('Error', error.message || 'Failed to save address');
|
||||
setSubmitError(error.message || 'Failed to save address');
|
||||
},
|
||||
});
|
||||
|
||||
const updateAddressMutation = trpc.user.address.updateAddress.useMutation({
|
||||
onSuccess: () => {
|
||||
setIsSubmitting(false);
|
||||
setTimeout(() => onSuccess(), 100);
|
||||
// Delay to allow modal to close animation
|
||||
setTimeout(() => onSuccess(), 350);
|
||||
},
|
||||
onError: (error: any) => {
|
||||
setIsSubmitting(false);
|
||||
Alert.alert('Error', error.message || 'Failed to update address');
|
||||
setSubmitError(error.message || 'Failed to update address');
|
||||
},
|
||||
});
|
||||
|
||||
const attachCurrentLocation = async () => {
|
||||
setLocationLoading(true);
|
||||
setLocationError(null);
|
||||
setLocationSuccess(false);
|
||||
|
||||
try {
|
||||
const { status } = await Location.requestForegroundPermissionsAsync();
|
||||
|
|
@ -78,7 +83,9 @@ const AddressForm: React.FC<AddressFormProps> = ({ onSuccess, initialValues, isE
|
|||
|
||||
const { latitude, longitude } = location.coords;
|
||||
setCurrentLocation({ latitude, longitude });
|
||||
Alert.alert('Success', 'Location attached successfully');
|
||||
setLocationSuccess(true);
|
||||
// Clear success message after 3 seconds
|
||||
setTimeout(() => setLocationSuccess(false), 3000);
|
||||
} catch (error) {
|
||||
setLocationError('Unable to fetch location. Please check your GPS settings.');
|
||||
} finally {
|
||||
|
|
@ -98,8 +105,19 @@ const AddressForm: React.FC<AddressFormProps> = ({ onSuccess, initialValues, isE
|
|||
});
|
||||
|
||||
return (
|
||||
<ScrollView style={tw`p-4`}>
|
||||
<ScrollView
|
||||
style={tw`p-4`}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
keyboardDismissMode="on-drag"
|
||||
>
|
||||
<MyText style={tw`text-xl font-bold mb-4`}>{isEdit ? 'Edit Address' : 'Add Address'}</MyText>
|
||||
|
||||
{/* 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: '',
|
||||
|
|
@ -116,9 +134,10 @@ const AddressForm: React.FC<AddressFormProps> = ({ onSuccess, initialValues, isE
|
|||
}}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={(values) => {
|
||||
setIsSubmitting(true);
|
||||
const payload = {
|
||||
...values,
|
||||
setIsSubmitting(true);
|
||||
setSubmitError(null);
|
||||
const payload = {
|
||||
...values,
|
||||
latitude: currentLocation?.latitude,
|
||||
longitude: currentLocation?.longitude,
|
||||
googleMapsUrl: values.googleMapsUrl || undefined,
|
||||
|
|
@ -203,9 +222,24 @@ const AddressForm: React.FC<AddressFormProps> = ({ onSuccess, initialValues, isE
|
|||
<MyText style={tw`text-blue-500 text-sm mb-2`}>Fetching location...</MyText>
|
||||
) : locationError ? (
|
||||
<MyText style={tw`text-red-500 text-sm mb-2`}>{locationError}</MyText>
|
||||
) : locationSuccess ? (
|
||||
<View style={tw`flex-row items-center mb-4`}>
|
||||
<View style={tw`bg-green-100 px-3 py-1 rounded-full`}>
|
||||
<MyText style={tw`text-green-600 text-sm font-medium`}>✓ Location Attached</MyText>
|
||||
</View>
|
||||
<MyTouchableOpacity
|
||||
onPress={() => attachCurrentLocation()}
|
||||
disabled={locationLoading}
|
||||
style={tw`ml-4`}
|
||||
>
|
||||
<MyText style={tw`text-blue-500 text-sm font-medium`}>Attach Current</MyText>
|
||||
</MyTouchableOpacity>
|
||||
</View>
|
||||
) : currentLocation ? (
|
||||
<View style={tw`flex-row items-center mb-4`}>
|
||||
<MyText style={tw`text-green-600 text-sm font-medium`}>Location Attached</MyText>
|
||||
<View style={tw`bg-green-100 px-3 py-1 rounded-full`}>
|
||||
<MyText style={tw`text-green-600 text-sm font-medium`}>✓ Location Attached</MyText>
|
||||
</View>
|
||||
<MyTouchableOpacity
|
||||
onPress={() => attachCurrentLocation()}
|
||||
disabled={locationLoading}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { ReactNode, useState } from 'react';
|
||||
import { Modal, View, TouchableOpacity, StyleSheet, Animated, Easing, Dimensions, TextInput } from 'react-native';
|
||||
import { Modal, View, TouchableOpacity, StyleSheet, Animated, Easing, Dimensions, TextInput, KeyboardAvoidingView, Platform, ScrollView } from 'react-native';
|
||||
import MyText from './text';
|
||||
import { MyButton } from 'common-ui';
|
||||
|
||||
|
|
@ -53,7 +53,20 @@ export const BottomDialog: React.FC<DialogProps> = ({ open, onClose, children, e
|
|||
]}
|
||||
>
|
||||
<View style={styles.handle} />
|
||||
{children}
|
||||
<KeyboardAvoidingView
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
||||
style={styles.keyboardAvoidingContainer}
|
||||
keyboardVerticalOffset={Platform.OS === 'ios' ? 0 : 0}
|
||||
>
|
||||
<ScrollView
|
||||
style={styles.scrollContainer}
|
||||
contentContainerStyle={styles.scrollContent}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
showsVerticalScrollIndicator={true}
|
||||
>
|
||||
{children}
|
||||
</ScrollView>
|
||||
</KeyboardAvoidingView>
|
||||
</Animated.View>
|
||||
</Modal>
|
||||
);
|
||||
|
|
@ -71,19 +84,18 @@ const styles = StyleSheet.create({
|
|||
position: 'absolute',
|
||||
left: 0,
|
||||
right: 0,
|
||||
// top: SCREEN_HEIGHT * 0.3,
|
||||
bottom: 0,
|
||||
backgroundColor: '#fff',
|
||||
borderTopLeftRadius: 18,
|
||||
borderTopRightRadius: 18,
|
||||
paddingHorizontal: 20,
|
||||
// paddingTop: 16,
|
||||
paddingBottom: 0,
|
||||
elevation: 8,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: -2 },
|
||||
shadowOpacity: 0.2,
|
||||
shadowRadius: 8,
|
||||
maxHeight: SCREEN_HEIGHT * 0.85, // Limit max height so it doesn't cover entire screen
|
||||
},
|
||||
handle: {
|
||||
width: 40,
|
||||
|
|
@ -91,7 +103,20 @@ const styles = StyleSheet.create({
|
|||
borderRadius: 3,
|
||||
backgroundColor: '#ccc',
|
||||
alignSelf: 'center',
|
||||
// marginBottom: 12,
|
||||
marginTop: 12,
|
||||
marginBottom: 8,
|
||||
},
|
||||
keyboardAvoidingContainer: {
|
||||
flex: 1,
|
||||
width: '100%',
|
||||
},
|
||||
scrollContainer: {
|
||||
flex: 1,
|
||||
width: '100%',
|
||||
},
|
||||
scrollContent: {
|
||||
flexGrow: 1,
|
||||
paddingBottom: Platform.OS === 'ios' ? 40 : 20, // Extra padding for iOS keyboard
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue