enh
This commit is contained in:
parent
e546c52c05
commit
83e733fdd1
10 changed files with 212 additions and 1144 deletions
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
ENV_MODE=PROD
|
||||
# DATABASE_URL=postgresql://postgres:meatfarmer_master_password@57.128.212.174:7447/meatfarmer #technocracy
|
||||
DATABASE_URL=postgres://postgres:meatfarmer_master_password@5.223.55.14:7447/meatfarmer #hetzner
|
||||
DATABASE_URL=postgresql://postgres:meatfarmer_master_password@57.128.212.174:7447/meatfarmer #technocracy
|
||||
# DATABASE_URL=postgres://postgres:meatfarmer_master_password@5.223.55.14:7447/meatfarmer #hetzner
|
||||
PHONE_PE_BASE_URL=https://api-preprod.phonepe.com/
|
||||
PHONE_PE_CLIENT_ID=TEST-M23F2IGP34ZAR_25090
|
||||
PHONE_PE_CLIENT_VERSION=1
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
0
apps/backend/demo.json
Normal file
0
apps/backend/demo.json
Normal file
|
|
@ -14,7 +14,7 @@ export const computeConstants = async (): Promise<void> => {
|
|||
for (const constant of constants) {
|
||||
const redisKey = `${CONST_REDIS_PREFIX}${constant.key}`;
|
||||
const value = JSON.stringify(constant.value);
|
||||
console.log({redisKey, value})
|
||||
// console.log({redisKey, value})
|
||||
|
||||
await redisClient.set(redisKey, value);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { db } from '../db/db_index';
|
|||
import { deliverySlotInfo, productSlots, productInfo, units } from '../db/schema';
|
||||
import { eq, and, gt, asc } from 'drizzle-orm';
|
||||
import { generateSignedUrlsFromS3Urls } from 'src/lib/s3-client';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
// Define the structure for slot with products
|
||||
interface SlotWithProducts {
|
||||
|
|
@ -58,6 +59,20 @@ export async function initializeSlotStore(): Promise<void> {
|
|||
orderBy: asc(deliverySlotInfo.deliveryTime),
|
||||
});
|
||||
|
||||
const allSlots = await db.query.deliverySlotInfo.findMany({
|
||||
where: and(
|
||||
eq(deliverySlotInfo.isActive, true),
|
||||
gt(deliverySlotInfo.deliveryTime, now), // Only future slots
|
||||
)
|
||||
})
|
||||
|
||||
console.log(allSlots)
|
||||
|
||||
slots.forEach(slot => {
|
||||
console.log({id: slot.id, delivery: dayjs(slot.deliveryTime).format('MM-DD-YY hh:mm:ss a')})
|
||||
})
|
||||
|
||||
|
||||
// Transform data for storage
|
||||
const slotsWithProducts = await Promise.all(
|
||||
slots.map(async (slot) => ({
|
||||
|
|
|
|||
|
|
@ -238,7 +238,7 @@ export const slotsRouter = router({
|
|||
throw new ApiError("Delivery time and orders close time are required", 400);
|
||||
}
|
||||
|
||||
return await db.transaction(async (tx) => {
|
||||
const result = await db.transaction(async (tx) => {
|
||||
// Create slot
|
||||
const [newSlot] = await tx
|
||||
.insert(deliverySlotInfo)
|
||||
|
|
@ -290,15 +290,17 @@ export const slotsRouter = router({
|
|||
}
|
||||
}
|
||||
|
||||
// Reinitialize stores to reflect changes
|
||||
await initializeAllStores();
|
||||
|
||||
return {
|
||||
slot: newSlot,
|
||||
createdSnippets,
|
||||
message: "Slot created successfully",
|
||||
};
|
||||
});
|
||||
|
||||
// Reinitialize stores to reflect changes (outside transaction)
|
||||
await initializeAllStores();
|
||||
|
||||
return result;
|
||||
}),
|
||||
|
||||
getSlots: protectedProcedure.query(async ({ ctx }) => {
|
||||
|
|
@ -384,7 +386,7 @@ export const slotsRouter = router({
|
|||
validGroupIds = existingGroups.map(g => g.id);
|
||||
}
|
||||
|
||||
return await db.transaction(async (tx) => {
|
||||
const result = await db.transaction(async (tx) => {
|
||||
const [updatedSlot] = await tx
|
||||
.update(deliverySlotInfo)
|
||||
.set({
|
||||
|
|
@ -447,15 +449,17 @@ export const slotsRouter = router({
|
|||
}
|
||||
}
|
||||
|
||||
// Reinitialize stores to reflect changes
|
||||
await initializeAllStores();
|
||||
|
||||
return {
|
||||
slot: updatedSlot,
|
||||
createdSnippets,
|
||||
message: "Slot updated successfully",
|
||||
};
|
||||
});
|
||||
|
||||
// Reinitialize stores to reflect changes (outside transaction)
|
||||
await initializeAllStores();
|
||||
|
||||
return result;
|
||||
}
|
||||
catch(e) {
|
||||
console.log(e)
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ export const storeRouter = router({
|
|||
.mutation(async ({ input, ctx }) => {
|
||||
const { storeId } = input;
|
||||
|
||||
return await db.transaction(async (tx) => {
|
||||
const result = await db.transaction(async (tx) => {
|
||||
// First, update all products of this store to set storeId to null
|
||||
await tx
|
||||
.update(productInfo)
|
||||
|
|
@ -196,12 +196,14 @@ export const storeRouter = router({
|
|||
throw new ApiError("Store not found", 404);
|
||||
}
|
||||
|
||||
// Reinitialize stores to reflect changes
|
||||
await initializeAllStores();
|
||||
|
||||
return {
|
||||
message: "Store deleted successfully",
|
||||
};
|
||||
});
|
||||
|
||||
// Reinitialize stores to reflect changes (outside transaction)
|
||||
await initializeAllStores();
|
||||
|
||||
return result;
|
||||
}),
|
||||
});
|
||||
|
|
@ -3,9 +3,9 @@ import { View, ScrollView } from 'react-native';
|
|||
import { useMutation } from '@tanstack/react-query';
|
||||
import { Formik } from 'formik';
|
||||
import * as Yup from 'yup';
|
||||
import * as Location from 'expo-location';
|
||||
import { tw, MyText, MyTouchableOpacity , Checkbox , MyTextInput , LoadingDialog } from 'common-ui';
|
||||
import { trpc } from '../trpc-client';
|
||||
import LocationAttacher from './LocationAttacher';
|
||||
|
||||
interface AddressFormProps {
|
||||
onSuccess: (addressId?: number) => void;
|
||||
|
|
@ -27,8 +27,6 @@ interface AddressFormProps {
|
|||
}
|
||||
|
||||
const AddressForm: React.FC<AddressFormProps> = ({ onSuccess, initialValues, isEdit = false }) => {
|
||||
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);
|
||||
|
|
@ -37,7 +35,6 @@ const AddressForm: React.FC<AddressFormProps> = ({ onSuccess, initialValues, isE
|
|||
? { latitude: initialValues.latitude, longitude: initialValues.longitude }
|
||||
: null
|
||||
);
|
||||
const [locationSuccess, setLocationSuccess] = useState(false);
|
||||
|
||||
const createAddressMutation = trpc.user.address.createAddress.useMutation({
|
||||
onSuccess: (data) => {
|
||||
|
|
@ -64,35 +61,6 @@ const AddressForm: React.FC<AddressFormProps> = ({ onSuccess, initialValues, isE
|
|||
},
|
||||
});
|
||||
|
||||
const attachCurrentLocation = async () => {
|
||||
setLocationLoading(true);
|
||||
setLocationError(null);
|
||||
setLocationSuccess(false);
|
||||
|
||||
try {
|
||||
const { status } = await Location.requestForegroundPermissionsAsync();
|
||||
|
||||
if (status !== 'granted') {
|
||||
setLocationError('Location Permission denied');
|
||||
return;
|
||||
}
|
||||
|
||||
const location = await Location.getCurrentPositionAsync({
|
||||
accuracy: Location.Accuracy.High,
|
||||
});
|
||||
|
||||
const { latitude, longitude } = location.coords;
|
||||
setCurrentLocation({ latitude, longitude });
|
||||
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 {
|
||||
setLocationLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
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'),
|
||||
|
|
@ -228,58 +196,27 @@ const AddressForm: React.FC<AddressFormProps> = ({ onSuccess, initialValues, isE
|
|||
/>
|
||||
{touched.pincode && errors.pincode && <MyText style={tw`text-red-500 mb-2`}>{errors.pincode}</MyText>}
|
||||
|
||||
{locationLoading ? (
|
||||
<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`}>
|
||||
<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>
|
||||
) : (
|
||||
<LocationAttacher
|
||||
initialLocation={currentLocation || undefined}
|
||||
initialGoogleMapsUrl={values.googleMapsUrl}
|
||||
googleMapsUrl={values.googleMapsUrl}
|
||||
onGoogleMapsUrlChange={(url) => setFieldValue('googleMapsUrl', url)}
|
||||
onLocationChange={(location) => setCurrentLocation(location)}
|
||||
disabled={false}
|
||||
/>
|
||||
|
||||
{!showGoogleMapsField && (
|
||||
<MyTouchableOpacity
|
||||
onPress={() => attachCurrentLocation()}
|
||||
disabled={locationLoading}
|
||||
style={tw`mb-4`}
|
||||
onPress={() => setShowGoogleMapsField(true)}
|
||||
disabled={false}
|
||||
style={tw`mb-1`}
|
||||
>
|
||||
<MyText style={tw`text-blue-500 text-sm font-medium`}>
|
||||
Attach Current Location
|
||||
Attach with Google Maps
|
||||
</MyText>
|
||||
</MyTouchableOpacity>
|
||||
)}
|
||||
|
||||
<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`}>
|
||||
|
|
|
|||
158
apps/user-ui/src/components/LocationAttacher.tsx
Normal file
158
apps/user-ui/src/components/LocationAttacher.tsx
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
import React, { useState } from 'react';
|
||||
import { View } from 'react-native';
|
||||
import * as Location from 'expo-location';
|
||||
import { tw, MyText, MyTouchableOpacity } from 'common-ui';
|
||||
|
||||
interface LocationAttacherProps {
|
||||
onLocationChange?: (location: { latitude: number; longitude: number } | null) => void;
|
||||
initialLocation?: { latitude: number; longitude: number };
|
||||
initialGoogleMapsUrl?: string;
|
||||
showGoogleMapsOption?: boolean;
|
||||
googleMapsUrl?: string;
|
||||
onGoogleMapsUrlChange?: (url: string) => void;
|
||||
disabled?: boolean;
|
||||
containerStyle?: any;
|
||||
attachButtonText?: string;
|
||||
googleMapsButtonText?: string;
|
||||
}
|
||||
|
||||
export const LocationAttacher: React.FC<LocationAttacherProps> = ({
|
||||
onLocationChange,
|
||||
initialLocation,
|
||||
initialGoogleMapsUrl = '',
|
||||
showGoogleMapsOption = true,
|
||||
googleMapsUrl: controlledGoogleMapsUrl,
|
||||
onGoogleMapsUrlChange,
|
||||
disabled = false,
|
||||
containerStyle,
|
||||
attachButtonText = 'Attach Current Location',
|
||||
googleMapsButtonText = 'Attach with Google Maps',
|
||||
}) => {
|
||||
const [locationLoading, setLocationLoading] = useState(false);
|
||||
const [locationError, setLocationError] = useState<string | null>(null);
|
||||
const [locationSuccess, setLocationSuccess] = useState(false);
|
||||
const [currentLocation, setCurrentLocation] = useState<{ latitude: number; longitude: number } | null>(
|
||||
initialLocation || null
|
||||
);
|
||||
const [showGoogleMapsField, setShowGoogleMapsField] = useState(!!initialGoogleMapsUrl);
|
||||
const [internalGoogleMapsUrl, setInternalGoogleMapsUrl] = useState(initialGoogleMapsUrl);
|
||||
|
||||
// Use controlled or uncontrolled Google Maps URL
|
||||
const googleMapsUrl = controlledGoogleMapsUrl !== undefined ? controlledGoogleMapsUrl : internalGoogleMapsUrl;
|
||||
|
||||
const handleGoogleMapsUrlChange = (url: string) => {
|
||||
if (onGoogleMapsUrlChange) {
|
||||
onGoogleMapsUrlChange(url);
|
||||
} else {
|
||||
setInternalGoogleMapsUrl(url);
|
||||
}
|
||||
};
|
||||
|
||||
const attachCurrentLocation = async () => {
|
||||
if (disabled) return;
|
||||
|
||||
setLocationLoading(true);
|
||||
setLocationError(null);
|
||||
setLocationSuccess(false);
|
||||
|
||||
try {
|
||||
const { status } = await Location.requestForegroundPermissionsAsync();
|
||||
|
||||
if (status !== 'granted') {
|
||||
setLocationError('Location Permission denied');
|
||||
return;
|
||||
}
|
||||
|
||||
const location = await Location.getCurrentPositionAsync({
|
||||
accuracy: Location.Accuracy.High,
|
||||
});
|
||||
|
||||
const { latitude, longitude } = location.coords;
|
||||
const newLocation = { latitude, longitude };
|
||||
setCurrentLocation(newLocation);
|
||||
setLocationSuccess(true);
|
||||
onLocationChange?.(newLocation);
|
||||
// Clear success message after 3 seconds
|
||||
setTimeout(() => setLocationSuccess(false), 3000);
|
||||
} catch (error) {
|
||||
setLocationError('Unable to fetch location. Please check your GPS settings.');
|
||||
} finally {
|
||||
setLocationLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={containerStyle}>
|
||||
{/* Location Status */}
|
||||
{locationLoading ? (
|
||||
<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 || disabled}
|
||||
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`}>
|
||||
<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 || disabled}
|
||||
style={tw`ml-4`}
|
||||
>
|
||||
<MyText style={tw`text-blue-500 text-sm font-medium`}>Attach Current</MyText>
|
||||
</MyTouchableOpacity>
|
||||
</View>
|
||||
) : (
|
||||
<MyTouchableOpacity
|
||||
onPress={() => attachCurrentLocation()}
|
||||
disabled={locationLoading || disabled}
|
||||
style={tw`mb-4`}
|
||||
>
|
||||
<MyText style={tw`text-blue-500 text-sm font-medium`}>
|
||||
{attachButtonText}
|
||||
</MyText>
|
||||
</MyTouchableOpacity>
|
||||
)}
|
||||
|
||||
{/* Google Maps Option - Only show button if not in controlled mode */}
|
||||
{showGoogleMapsOption && onGoogleMapsUrlChange === undefined && (
|
||||
<>
|
||||
<MyTouchableOpacity
|
||||
onPress={() => setShowGoogleMapsField(true)}
|
||||
disabled={disabled}
|
||||
style={tw`mb-1`}
|
||||
>
|
||||
<MyText style={tw`text-blue-500 text-sm font-medium`}>
|
||||
{googleMapsButtonText}
|
||||
</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>
|
||||
</View>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default LocationAttacher;
|
||||
1052
package-lock.json
generated
1052
package-lock.json
generated
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue