This commit is contained in:
shafi54 2026-02-12 01:49:36 +05:30
parent e546c52c05
commit 83e733fdd1
10 changed files with 212 additions and 1144 deletions

View file

@ -1,7 +1,7 @@
ENV_MODE=PROD ENV_MODE=PROD
# DATABASE_URL=postgresql://postgres:meatfarmer_master_password@57.128.212.174:7447/meatfarmer #technocracy 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=postgres://postgres:meatfarmer_master_password@5.223.55.14:7447/meatfarmer #hetzner
PHONE_PE_BASE_URL=https://api-preprod.phonepe.com/ PHONE_PE_BASE_URL=https://api-preprod.phonepe.com/
PHONE_PE_CLIENT_ID=TEST-M23F2IGP34ZAR_25090 PHONE_PE_CLIENT_ID=TEST-M23F2IGP34ZAR_25090
PHONE_PE_CLIENT_VERSION=1 PHONE_PE_CLIENT_VERSION=1

File diff suppressed because one or more lines are too long

0
apps/backend/demo.json Normal file
View file

View file

@ -14,7 +14,7 @@ export const computeConstants = async (): Promise<void> => {
for (const constant of constants) { for (const constant of constants) {
const redisKey = `${CONST_REDIS_PREFIX}${constant.key}`; const redisKey = `${CONST_REDIS_PREFIX}${constant.key}`;
const value = JSON.stringify(constant.value); const value = JSON.stringify(constant.value);
console.log({redisKey, value}) // console.log({redisKey, value})
await redisClient.set(redisKey, value); await redisClient.set(redisKey, value);
} }

View file

@ -3,6 +3,7 @@ import { db } from '../db/db_index';
import { deliverySlotInfo, productSlots, productInfo, units } from '../db/schema'; import { deliverySlotInfo, productSlots, productInfo, units } from '../db/schema';
import { eq, and, gt, asc } from 'drizzle-orm'; import { eq, and, gt, asc } from 'drizzle-orm';
import { generateSignedUrlsFromS3Urls } from 'src/lib/s3-client'; import { generateSignedUrlsFromS3Urls } from 'src/lib/s3-client';
import dayjs from 'dayjs';
// Define the structure for slot with products // Define the structure for slot with products
interface SlotWithProducts { interface SlotWithProducts {
@ -58,6 +59,20 @@ export async function initializeSlotStore(): Promise<void> {
orderBy: asc(deliverySlotInfo.deliveryTime), 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 // Transform data for storage
const slotsWithProducts = await Promise.all( const slotsWithProducts = await Promise.all(
slots.map(async (slot) => ({ slots.map(async (slot) => ({

View file

@ -238,7 +238,7 @@ export const slotsRouter = router({
throw new ApiError("Delivery time and orders close time are required", 400); 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 // Create slot
const [newSlot] = await tx const [newSlot] = await tx
.insert(deliverySlotInfo) .insert(deliverySlotInfo)
@ -290,15 +290,17 @@ export const slotsRouter = router({
} }
} }
// Reinitialize stores to reflect changes
await initializeAllStores();
return { return {
slot: newSlot, slot: newSlot,
createdSnippets, createdSnippets,
message: "Slot created successfully", message: "Slot created successfully",
}; };
}); });
// Reinitialize stores to reflect changes (outside transaction)
await initializeAllStores();
return result;
}), }),
getSlots: protectedProcedure.query(async ({ ctx }) => { getSlots: protectedProcedure.query(async ({ ctx }) => {
@ -384,7 +386,7 @@ export const slotsRouter = router({
validGroupIds = existingGroups.map(g => g.id); validGroupIds = existingGroups.map(g => g.id);
} }
return await db.transaction(async (tx) => { const result = await db.transaction(async (tx) => {
const [updatedSlot] = await tx const [updatedSlot] = await tx
.update(deliverySlotInfo) .update(deliverySlotInfo)
.set({ .set({
@ -447,15 +449,17 @@ export const slotsRouter = router({
} }
} }
// Reinitialize stores to reflect changes
await initializeAllStores();
return { return {
slot: updatedSlot, slot: updatedSlot,
createdSnippets, createdSnippets,
message: "Slot updated successfully", message: "Slot updated successfully",
}; };
}); });
// Reinitialize stores to reflect changes (outside transaction)
await initializeAllStores();
return result;
} }
catch(e) { catch(e) {
console.log(e) console.log(e)

View file

@ -179,7 +179,7 @@ export const storeRouter = router({
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const { storeId } = input; 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 // First, update all products of this store to set storeId to null
await tx await tx
.update(productInfo) .update(productInfo)
@ -196,12 +196,14 @@ export const storeRouter = router({
throw new ApiError("Store not found", 404); throw new ApiError("Store not found", 404);
} }
// Reinitialize stores to reflect changes
await initializeAllStores();
return { return {
message: "Store deleted successfully", message: "Store deleted successfully",
}; };
}); });
// Reinitialize stores to reflect changes (outside transaction)
await initializeAllStores();
return result;
}), }),
}); });

View file

@ -3,9 +3,9 @@ import { View, ScrollView } from 'react-native';
import { useMutation } from '@tanstack/react-query'; import { useMutation } from '@tanstack/react-query';
import { Formik } from 'formik'; import { Formik } from 'formik';
import * as Yup from 'yup'; import * as Yup from 'yup';
import * as Location from 'expo-location';
import { tw, MyText, MyTouchableOpacity , Checkbox , MyTextInput , LoadingDialog } from 'common-ui'; import { tw, MyText, MyTouchableOpacity , Checkbox , MyTextInput , LoadingDialog } from 'common-ui';
import { trpc } from '../trpc-client'; import { trpc } from '../trpc-client';
import LocationAttacher from './LocationAttacher';
interface AddressFormProps { interface AddressFormProps {
onSuccess: (addressId?: number) => void; onSuccess: (addressId?: number) => void;
@ -27,8 +27,6 @@ interface AddressFormProps {
} }
const AddressForm: React.FC<AddressFormProps> = ({ onSuccess, initialValues, isEdit = false }) => { 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 [isSubmitting, setIsSubmitting] = useState(false);
const [submitError, setSubmitError] = useState<string | null>(null); const [submitError, setSubmitError] = useState<string | null>(null);
const [showGoogleMapsField, setShowGoogleMapsField] = useState(!!initialValues?.googleMapsUrl); const [showGoogleMapsField, setShowGoogleMapsField] = useState(!!initialValues?.googleMapsUrl);
@ -37,7 +35,6 @@ const AddressForm: React.FC<AddressFormProps> = ({ onSuccess, initialValues, isE
? { latitude: initialValues.latitude, longitude: initialValues.longitude } ? { latitude: initialValues.latitude, longitude: initialValues.longitude }
: null : null
); );
const [locationSuccess, setLocationSuccess] = useState(false);
const createAddressMutation = trpc.user.address.createAddress.useMutation({ const createAddressMutation = trpc.user.address.createAddress.useMutation({
onSuccess: (data) => { 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({ const validationSchema = Yup.object({
name: Yup.string().required('Name is required'), name: Yup.string().required('Name is required'),
phone: Yup.string().required('Phone is required').matches(/^\d{10}$/, 'Phone must be 10 digits'), phone: Yup.string().required('Phone is required').matches(/^\d{10}$/, 'Phone must be 10 digits'),
@ -228,48 +196,16 @@ const AddressForm: React.FC<AddressFormProps> = ({ onSuccess, initialValues, isE
/> />
{touched.pincode && errors.pincode && <MyText style={tw`text-red-500 mb-2`}>{errors.pincode}</MyText>} {touched.pincode && errors.pincode && <MyText style={tw`text-red-500 mb-2`}>{errors.pincode}</MyText>}
{locationLoading ? ( <LocationAttacher
<MyText style={tw`text-blue-500 text-sm mb-2`}>Fetching location...</MyText> initialLocation={currentLocation || undefined}
) : locationError ? ( initialGoogleMapsUrl={values.googleMapsUrl}
<MyText style={tw`text-red-500 text-sm mb-2`}>{locationError}</MyText> googleMapsUrl={values.googleMapsUrl}
) : locationSuccess ? ( onGoogleMapsUrlChange={(url) => setFieldValue('googleMapsUrl', url)}
<View style={tw`flex-row items-center mb-4`}> onLocationChange={(location) => setCurrentLocation(location)}
<View style={tw`bg-green-100 px-3 py-1 rounded-full`}> disabled={false}
<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>
) : (
<MyTouchableOpacity
onPress={() => attachCurrentLocation()}
disabled={locationLoading}
style={tw`mb-4`}
>
<MyText style={tw`text-blue-500 text-sm font-medium`}>
Attach Current Location
</MyText>
</MyTouchableOpacity>
)}
{!showGoogleMapsField && (
<MyTouchableOpacity <MyTouchableOpacity
onPress={() => setShowGoogleMapsField(true)} onPress={() => setShowGoogleMapsField(true)}
disabled={false} disabled={false}
@ -279,6 +215,7 @@ const AddressForm: React.FC<AddressFormProps> = ({ onSuccess, initialValues, isE
Attach with Google Maps Attach with Google Maps
</MyText> </MyText>
</MyTouchableOpacity> </MyTouchableOpacity>
)}
{showGoogleMapsField && ( {showGoogleMapsField && (
<View style={tw`mb-2`}> <View style={tw`mb-2`}>

View 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

File diff suppressed because it is too large Load diff