Compare commits
4 commits
d234c8a00f
...
d599c2e004
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d599c2e004 | ||
|
|
55c41fa0af | ||
|
|
d1d7db55a0 | ||
|
|
78e90fd398 |
34 changed files with 599 additions and 82 deletions
File diff suppressed because one or more lines are too long
6
apps/admin-ui/.expo/types/router.d.ts
vendored
6
apps/admin-ui/.expo/types/router.d.ts
vendored
File diff suppressed because one or more lines are too long
|
|
@ -17,6 +17,13 @@ export default function Layout() {
|
|||
headerShown: false,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="all-items-order"
|
||||
options={{
|
||||
title: "All Items Order",
|
||||
headerShown: false,
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
391
apps/admin-ui/app/(drawer)/customize-app/all-items-order.tsx
Normal file
391
apps/admin-ui/app/(drawer)/customize-app/all-items-order.tsx
Normal file
|
|
@ -0,0 +1,391 @@
|
|||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import {
|
||||
View,
|
||||
Alert,
|
||||
ActivityIndicator,
|
||||
Dimensions,
|
||||
StyleSheet,
|
||||
} from "react-native";
|
||||
import { TouchableOpacity } from "react-native-gesture-handler";
|
||||
import { Image } from "expo-image";
|
||||
import DraggableFlatList, {
|
||||
ScaleDecorator,
|
||||
} from "react-native-draggable-flatlist";
|
||||
import {
|
||||
AppContainer,
|
||||
MyText,
|
||||
tw,
|
||||
MyTouchableOpacity,
|
||||
} from "common-ui";
|
||||
import { useRouter } from "expo-router";
|
||||
import { trpc } from "../../../src/trpc-client";
|
||||
import MaterialIcons from "@expo/vector-icons/MaterialIcons";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
const { width: screenWidth } = Dimensions.get("window");
|
||||
// Item takes full width minus padding
|
||||
const itemWidth = screenWidth - 48; // 24px padding each side
|
||||
const itemHeight = 80;
|
||||
|
||||
interface Product {
|
||||
id: number;
|
||||
name: string;
|
||||
images: string[];
|
||||
isOutOfStock: boolean;
|
||||
}
|
||||
|
||||
interface ProductItemProps {
|
||||
item: Product;
|
||||
drag: () => void;
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
const ProductItem: React.FC<ProductItemProps> = ({
|
||||
item,
|
||||
drag,
|
||||
isActive,
|
||||
}) => {
|
||||
return (
|
||||
<ScaleDecorator>
|
||||
<TouchableOpacity
|
||||
onLongPress={drag}
|
||||
activeOpacity={1}
|
||||
style={[
|
||||
styles.item,
|
||||
isActive && styles.activeItem,
|
||||
item.isOutOfStock && styles.outOfStock,
|
||||
]}
|
||||
>
|
||||
{/* Drag Handle */}
|
||||
<View style={styles.dragHandle}>
|
||||
<MaterialIcons
|
||||
name="drag-indicator"
|
||||
size={24}
|
||||
color={isActive ? "#3b82f6" : "#9ca3af"}
|
||||
/>
|
||||
</View>
|
||||
|
||||
{/* Product Image */}
|
||||
{item.images?.[0] ? (
|
||||
<Image
|
||||
source={{ uri: item.images[0] }}
|
||||
style={styles.image}
|
||||
resizeMode="cover"
|
||||
/>
|
||||
) : (
|
||||
<View style={styles.placeholderImage}>
|
||||
<MaterialIcons name="image" size={24} color="#9ca3af" />
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* Product Info */}
|
||||
<View style={styles.info}>
|
||||
<MyText style={styles.name} numberOfLines={1}>
|
||||
{item.name.length > 30 ? item.name.substring(0, 30) + '...' : item.name}
|
||||
</MyText>
|
||||
|
||||
{item.isOutOfStock && (
|
||||
<MaterialIcons name="remove-circle" size={16} color="#dc2626" />
|
||||
)}
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</ScaleDecorator>
|
||||
);
|
||||
};
|
||||
|
||||
export default function AllItemsOrder() {
|
||||
const router = useRouter();
|
||||
const queryClient = useQueryClient();
|
||||
const [products, setProducts] = useState<Product[]>([]);
|
||||
const [hasChanges, setHasChanges] = useState(false);
|
||||
|
||||
// Get current order from constants
|
||||
const { data: constants, isLoading: isLoadingConstants, error: constantsError } = trpc.admin.const.getConstants.useQuery();
|
||||
const { data: allProducts, isLoading: isLoadingProducts, error: productsError } = trpc.common.product.getAllProductsSummary.useQuery({});
|
||||
const updateConstants = trpc.admin.const.updateConstants.useMutation();
|
||||
|
||||
// Initialize products from constants
|
||||
useEffect(() => {
|
||||
if (allProducts?.products) {
|
||||
const allItemsOrderConstant = constants?.find(c => c.key === 'allItemsOrder');
|
||||
|
||||
let orderedIds: number[] = [];
|
||||
|
||||
if (allItemsOrderConstant) {
|
||||
const value = allItemsOrderConstant.value;
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
orderedIds = value.map((id: any) => parseInt(id));
|
||||
} else if (typeof value === 'string') {
|
||||
orderedIds = value.split(',').map((id: string) => parseInt(id.trim())).filter(id => !isNaN(id));
|
||||
}
|
||||
}
|
||||
|
||||
// Create product map for quick lookup
|
||||
const productMap = new Map(allProducts.products.map(p => [p.id, p]));
|
||||
|
||||
// Sort products based on order, products not in order go to end
|
||||
const sortedProducts: Product[] = [];
|
||||
|
||||
// First add products in the specified order
|
||||
for (const id of orderedIds) {
|
||||
const product = productMap.get(id);
|
||||
if (product) {
|
||||
sortedProducts.push({
|
||||
id: product.id,
|
||||
name: product.name,
|
||||
images: product.images || [],
|
||||
isOutOfStock: product.isOutOfStock || false,
|
||||
});
|
||||
productMap.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
// Then add remaining products (not in order yet)
|
||||
for (const product of productMap.values()) {
|
||||
sortedProducts.push({
|
||||
id: product.id,
|
||||
name: product.name,
|
||||
images: product.images || [],
|
||||
isOutOfStock: product.isOutOfStock || false,
|
||||
});
|
||||
}
|
||||
|
||||
setProducts(sortedProducts);
|
||||
}
|
||||
}, [constants, allProducts]);
|
||||
|
||||
const handleDragEnd = useCallback(({ data }: { data: Product[] }) => {
|
||||
setProducts(data);
|
||||
setHasChanges(true);
|
||||
}, []);
|
||||
|
||||
const renderItem = useCallback(({ item, drag, isActive }: { item: Product; drag: () => void; isActive: boolean }) => {
|
||||
return (
|
||||
<ProductItem
|
||||
item={item}
|
||||
drag={drag}
|
||||
isActive={isActive}
|
||||
/>
|
||||
);
|
||||
}, []);
|
||||
|
||||
const handleSave = () => {
|
||||
const productIds = products.map(p => p.id);
|
||||
|
||||
updateConstants.mutate(
|
||||
{
|
||||
constants: [{
|
||||
key: 'allItemsOrder',
|
||||
value: productIds
|
||||
}]
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
setHasChanges(false);
|
||||
Alert.alert('Success', 'All items order updated successfully!');
|
||||
queryClient.invalidateQueries({ queryKey: ['const.getConstants'] });
|
||||
},
|
||||
onError: (error) => {
|
||||
Alert.alert('Error', 'Failed to update items order. Please try again.');
|
||||
console.error('Update all items order error:', error);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
// Show loading state while data is being fetched
|
||||
if (isLoadingConstants || isLoadingProducts) {
|
||||
return (
|
||||
<AppContainer>
|
||||
<View style={tw`flex-1 bg-gray-50`}>
|
||||
<View style={tw`bg-white px-4 py-4 border-b border-gray-200 flex-row items-center justify-between`}>
|
||||
<TouchableOpacity
|
||||
onPress={() => router.back()}
|
||||
style={tw`p-2 -ml-4`}
|
||||
>
|
||||
<MaterialIcons name="chevron-left" size={24} color="#374151" />
|
||||
</TouchableOpacity>
|
||||
<MyText style={tw`text-xl font-bold text-gray-900`}>All Items Order</MyText>
|
||||
<View style={tw`w-16`} />
|
||||
</View>
|
||||
<View style={tw`flex-1 justify-center items-center p-8`}>
|
||||
<ActivityIndicator size="large" color="#3b82f6" />
|
||||
<MyText style={tw`text-gray-500 mt-4 text-center`}>
|
||||
{isLoadingConstants ? 'Loading order...' : 'Loading products...'}
|
||||
</MyText>
|
||||
</View>
|
||||
</View>
|
||||
</AppContainer>
|
||||
);
|
||||
}
|
||||
|
||||
// Show error state if queries failed
|
||||
if (constantsError || productsError) {
|
||||
return (
|
||||
<AppContainer>
|
||||
<View style={tw`flex-1 bg-gray-50`}>
|
||||
<View style={tw`bg-white px-4 py-4 border-b border-gray-200 flex-row items-center justify-between`}>
|
||||
<TouchableOpacity
|
||||
onPress={() => router.back()}
|
||||
style={tw`p-2 -ml-4`}
|
||||
>
|
||||
<MaterialIcons name="chevron-left" size={24} color="#374151" />
|
||||
</TouchableOpacity>
|
||||
<MyText style={tw`text-xl font-bold text-gray-900`}>All Items Order</MyText>
|
||||
<View style={tw`w-16`} />
|
||||
</View>
|
||||
<View style={tw`flex-1 justify-center items-center p-8`}>
|
||||
<MaterialIcons name="error-outline" size={64} color="#ef4444" />
|
||||
<MyText style={tw`text-gray-900 text-lg font-bold mt-4`}>Error</MyText>
|
||||
<MyText style={tw`text-gray-500 mt-2 text-center`}>
|
||||
{constantsError ? 'Failed to load order' : 'Failed to load products'}
|
||||
</MyText>
|
||||
<TouchableOpacity
|
||||
onPress={() => router.back()}
|
||||
style={tw`mt-6 bg-blue-600 px-6 py-3 rounded-full`}
|
||||
>
|
||||
<MyText style={tw`text-white font-semibold`}>Go Back</MyText>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</AppContainer>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={tw`flex-1 bg-gray-50`}>
|
||||
{/* Header */}
|
||||
<View style={tw`bg-white px-4 py-4 border-b border-gray-200 flex-row items-center justify-between`}>
|
||||
<MyTouchableOpacity
|
||||
onPress={() => router.back()}
|
||||
style={tw`p-2 -ml-4`}
|
||||
>
|
||||
<MaterialIcons name="chevron-left" size={24} color="#374151" />
|
||||
</MyTouchableOpacity>
|
||||
|
||||
<MyText style={tw`text-xl font-bold text-gray-900`}>All Items Order</MyText>
|
||||
|
||||
<MyTouchableOpacity
|
||||
onPress={handleSave}
|
||||
disabled={!hasChanges || updateConstants.isPending}
|
||||
style={tw`px-4 py-2 rounded-lg ${
|
||||
hasChanges && !updateConstants.isPending
|
||||
? 'bg-blue-600'
|
||||
: 'bg-gray-300'
|
||||
}`}
|
||||
>
|
||||
<MyText style={tw`${
|
||||
hasChanges && !updateConstants.isPending
|
||||
? 'text-white'
|
||||
: 'text-gray-500'
|
||||
} font-semibold`}>
|
||||
{updateConstants.isPending ? 'Saving...' : 'Save'}
|
||||
</MyText>
|
||||
</MyTouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* Content */}
|
||||
{products.length === 0 ? (
|
||||
<View style={tw`flex-1 justify-center items-center p-8`}>
|
||||
<MaterialIcons name="inventory" size={64} color="#e5e7eb" />
|
||||
<MyText style={tw`text-gray-500 mt-4 text-center text-lg`}>
|
||||
No products available
|
||||
</MyText>
|
||||
</View>
|
||||
) : (
|
||||
<View style={tw`flex-1`}>
|
||||
<View style={tw`bg-blue-50 px-4 py-2 mb-2 mt-2 mx-4 rounded-lg`}>
|
||||
<MyText style={tw`text-blue-700 text-xs text-center`}>
|
||||
Long press and drag to reorder • {products.length} items
|
||||
</MyText>
|
||||
</View>
|
||||
|
||||
<View style={tw`flex-1 px-3`}>
|
||||
<DraggableFlatList
|
||||
data={products}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={(item) => item.id.toString()}
|
||||
onDragEnd={handleDragEnd}
|
||||
showsVerticalScrollIndicator={true}
|
||||
contentContainerStyle={{ paddingBottom: 20 }}
|
||||
containerStyle={tw`flex-1`}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
// Enable auto-scroll during drag
|
||||
activationDistance={10}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
item: {
|
||||
width: itemWidth,
|
||||
height: 60,
|
||||
backgroundColor: 'white',
|
||||
borderRadius: 8,
|
||||
borderWidth: 1,
|
||||
borderColor: '#e5e7eb',
|
||||
padding: 10,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 2,
|
||||
elevation: 2,
|
||||
marginVertical: 4,
|
||||
},
|
||||
activeItem: {
|
||||
shadowColor: '#3b82f6',
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: 0.3,
|
||||
shadowRadius: 8,
|
||||
elevation: 8,
|
||||
borderColor: '#3b82f6',
|
||||
transform: [{ scale: 1.02 }],
|
||||
},
|
||||
outOfStock: {
|
||||
opacity: 0.6,
|
||||
},
|
||||
dragHandle: {
|
||||
marginRight: 8,
|
||||
padding: 2,
|
||||
},
|
||||
image: {
|
||||
width: 30,
|
||||
height: 30,
|
||||
borderRadius: 6,
|
||||
marginRight: 10,
|
||||
},
|
||||
placeholderImage: {
|
||||
width: 30,
|
||||
height: 30,
|
||||
borderRadius: 6,
|
||||
backgroundColor: '#f3f4f6',
|
||||
marginRight: 10,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
info: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
name: {
|
||||
fontSize: 13,
|
||||
color: '#111827',
|
||||
fontWeight: '500',
|
||||
flex: 1,
|
||||
marginRight: 4,
|
||||
},
|
||||
orderNumber: {
|
||||
fontSize: 11,
|
||||
color: '#9ca3af',
|
||||
marginLeft: 8,
|
||||
},
|
||||
});
|
||||
|
|
@ -31,6 +31,7 @@ const CONST_LABELS: Record<string, string> = {
|
|||
playStoreUrl: 'Play Store URL',
|
||||
appStoreUrl: 'App Store URL',
|
||||
popularItems: 'Popular Items',
|
||||
allItemsOrder: 'All Items Order',
|
||||
isFlashDeliveryEnabled: 'Enable Flash Delivery',
|
||||
supportMobile: 'Support Mobile',
|
||||
supportEmail: 'Support Email',
|
||||
|
|
@ -48,6 +49,7 @@ const ConstantInput: React.FC<ConstantInputProps> = ({ constant, setFieldValue,
|
|||
|
||||
// Special handling for popularItems - show navigation button instead of input
|
||||
if (constant.key === 'popularItems') {
|
||||
console.log('key is allItemsOrder')
|
||||
return (
|
||||
<View>
|
||||
<MyText style={tw`text-sm font-medium text-gray-700 mb-2`}>
|
||||
|
|
@ -67,6 +69,28 @@ const ConstantInput: React.FC<ConstantInputProps> = ({ constant, setFieldValue,
|
|||
);
|
||||
}
|
||||
|
||||
// Special handling for allItemsOrder - show navigation button instead of input
|
||||
if (constant.key === 'allItemsOrder') {
|
||||
|
||||
return (
|
||||
<View>
|
||||
<MyText style={tw`text-sm font-medium text-gray-700 mb-2`}>
|
||||
{CONST_LABELS[constant.key] || constant.key}
|
||||
</MyText>
|
||||
<MyTouchableOpacity
|
||||
onPress={() => router.push('/(drawer)/customize-app/all-items-order')}
|
||||
style={tw`bg-green-50 border-2 border-dashed border-green-200 p-4 rounded-lg flex-row items-center justify-center`}
|
||||
>
|
||||
<MaterialIcons name="reorder" size={20} color="#16a34a" style={tw`mr-2`} />
|
||||
<MyText style={tw`text-green-700 font-medium`}>
|
||||
Manage All Visible Items ({Array.isArray(constant.value) ? constant.value.length : 0} items)
|
||||
</MyText>
|
||||
<MaterialIcons name="chevron-right" size={20} color="#16a34a" style={tw`ml-2`} />
|
||||
</MyTouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
// Handle boolean values - show checkbox
|
||||
if (typeof constant.value === 'boolean') {
|
||||
return (
|
||||
|
|
@ -134,6 +158,7 @@ export default function CustomizeApp() {
|
|||
const { data: constants, isLoading: isLoadingConstants, refetch } = trpc.admin.const.getConstants.useQuery();
|
||||
const { mutate: updateConstants, isPending: isUpdating } = trpc.admin.const.updateConstants.useMutation();
|
||||
|
||||
|
||||
const handleSubmit = (values: ConstantFormData) => {
|
||||
// Filter out constants that haven't changed
|
||||
const changedConstants = values.constants.filter((constant, index) => {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import React, { useState, useEffect, useMemo } from "react";
|
||||
import {
|
||||
View,
|
||||
TouchableOpacity,
|
||||
Alert,
|
||||
ActivityIndicator,
|
||||
ScrollView,
|
||||
} from "react-native";
|
||||
import { TouchableOpacity } from "react-native-gesture-handler";
|
||||
import { Image } from "expo-image";
|
||||
import DraggableFlatList, {
|
||||
RenderItemParams,
|
||||
|
|
@ -16,6 +17,7 @@ import {
|
|||
tw,
|
||||
BottomDialog,
|
||||
BottomDropdown,
|
||||
MyTouchableOpacity,
|
||||
} from "common-ui";
|
||||
import ProductsSelector from "../../../components/ProductsSelector";
|
||||
import { useRouter } from "expo-router";
|
||||
|
|
@ -27,8 +29,8 @@ interface PopularProduct {
|
|||
id: number;
|
||||
name: string;
|
||||
shortDescription: string | null;
|
||||
price: string;
|
||||
marketPrice: string | null;
|
||||
price: number;
|
||||
marketPrice: number | null;
|
||||
unit: string;
|
||||
incrementStep: number;
|
||||
productQuantity: number;
|
||||
|
|
@ -119,7 +121,7 @@ export default function CustomizePopularItems() {
|
|||
const [popularProducts, setPopularProducts] = useState<PopularProduct[]>([]);
|
||||
const [hasChanges, setHasChanges] = useState(false);
|
||||
const [showAddDialog, setShowAddDialog] = useState(false);
|
||||
const [selectedProductId, setSelectedProductId] = useState<number | null>(null);
|
||||
const [selectedProductIds, setSelectedProductIds] = useState<number[]>([]);
|
||||
|
||||
// Get current popular items from constants
|
||||
const { data: constants, isLoading: isLoadingConstants, error: constantsError } = trpc.admin.const.getConstants.useQuery();
|
||||
|
|
@ -182,14 +184,20 @@ export default function CustomizePopularItems() {
|
|||
};
|
||||
|
||||
const handleAddProduct = () => {
|
||||
if (selectedProductId) {
|
||||
const product = allProducts?.products.find(p => p.id === selectedProductId);
|
||||
if (product && !popularProducts.find(p => p.id === product.id)) {
|
||||
setPopularProducts(prev => [...prev, product as PopularProduct]);
|
||||
if (selectedProductIds.length > 0) {
|
||||
const newProducts = selectedProductIds
|
||||
.map(id => allProducts?.products.find(p => p.id === id))
|
||||
.filter((product): product is NonNullable<typeof product> =>
|
||||
product !== undefined && !popularProducts.find(p => p.id === product.id)
|
||||
);
|
||||
|
||||
if (newProducts.length > 0) {
|
||||
setPopularProducts(prev => [...prev, ...newProducts as PopularProduct[]]);
|
||||
setHasChanges(true);
|
||||
setSelectedProductId(null);
|
||||
setShowAddDialog(false);
|
||||
}
|
||||
|
||||
setSelectedProductIds([]);
|
||||
setShowAddDialog(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -293,20 +301,19 @@ export default function CustomizePopularItems() {
|
|||
}
|
||||
|
||||
return (
|
||||
<AppContainer>
|
||||
<View style={tw`flex-1 bg-gray-50`}>
|
||||
<View style={[tw`flex-1 bg-gray-50 relative`]}>
|
||||
{/* Header */}
|
||||
<View style={tw`bg-white px-4 py-4 border-b border-gray-200 flex-row items-center justify-between`}>
|
||||
<TouchableOpacity
|
||||
<MyTouchableOpacity
|
||||
onPress={() => router.back()}
|
||||
style={tw`p-2 -ml-4`}
|
||||
>
|
||||
<MaterialIcons name="chevron-left" size={24} color="#374151" />
|
||||
</TouchableOpacity>
|
||||
</MyTouchableOpacity>
|
||||
|
||||
<MyText style={tw`text-xl font-bold text-gray-900`}>Popular Items</MyText>
|
||||
|
||||
<TouchableOpacity
|
||||
<MyTouchableOpacity
|
||||
onPress={handleSave}
|
||||
disabled={!hasChanges || updateConstants.isPending}
|
||||
style={tw`px-4 py-2 rounded-lg ${
|
||||
|
|
@ -322,7 +329,7 @@ export default function CustomizePopularItems() {
|
|||
} font-semibold`}>
|
||||
{updateConstants.isPending ? 'Saving...' : 'Save'}
|
||||
</MyText>
|
||||
</TouchableOpacity>
|
||||
</MyTouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* Content */}
|
||||
|
|
@ -356,35 +363,41 @@ export default function CustomizePopularItems() {
|
|||
)}
|
||||
keyExtractor={(item) => item.id.toString()}
|
||||
onDragEnd={handleDragEnd}
|
||||
showsVerticalScrollIndicator={false}
|
||||
contentContainerStyle={tw`pb-8`}
|
||||
showsVerticalScrollIndicator={true}
|
||||
scrollEnabled={true}
|
||||
contentContainerStyle={{ paddingBottom: 80 }}
|
||||
containerStyle={tw`flex-1`}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* FAB for Add Product */}
|
||||
<View style={tw`absolute bottom-4 right-4`}>
|
||||
<TouchableOpacity
|
||||
{/* FAB for Add Product - Fixed position */}
|
||||
<View style={tw`absolute bottom-12 right-6 z-50`}>
|
||||
<MyTouchableOpacity
|
||||
onPress={() => setShowAddDialog(true)}
|
||||
style={tw`bg-blue-600 p-4 rounded-full shadow-lg`}
|
||||
style={tw`bg-blue-600 p-4 rounded-full shadow-lg elevation-5`}
|
||||
>
|
||||
<MaterialIcons name="add" size={24} color="white" />
|
||||
</TouchableOpacity>
|
||||
</MyTouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* Add Product Dialog */}
|
||||
<BottomDialog
|
||||
open={showAddDialog}
|
||||
onClose={() => setShowAddDialog(false)}
|
||||
onClose={() => {
|
||||
setShowAddDialog(false);
|
||||
setSelectedProductIds([]);
|
||||
}}
|
||||
>
|
||||
<View style={tw`pb-8 pt-2 px-4`}>
|
||||
<View style={tw`items-center mb-6`}>
|
||||
<View style={tw`w-12 h-1.5 bg-gray-200 rounded-full mb-4`} />
|
||||
<MyText style={tw`text-lg font-bold text-gray-900`}>
|
||||
Add Popular Item
|
||||
Add Popular Items
|
||||
</MyText>
|
||||
<MyText style={tw`text-sm text-gray-500`}>
|
||||
Select a product to add to popular items
|
||||
Select products to add to popular items
|
||||
</MyText>
|
||||
</View>
|
||||
|
||||
|
|
@ -398,41 +411,43 @@ export default function CustomizePopularItems() {
|
|||
) : (
|
||||
<>
|
||||
<ProductsSelector
|
||||
value={selectedProductId || 0}
|
||||
onChange={(val) => setSelectedProductId(val as number)}
|
||||
multiple={false}
|
||||
label="Select Product"
|
||||
placeholder="Choose a product..."
|
||||
value={selectedProductIds}
|
||||
onChange={(val) => setSelectedProductIds(val as number[])}
|
||||
multiple={true}
|
||||
label="Select Products"
|
||||
placeholder="Choose products..."
|
||||
labelFormat={(product) => `${product.name} - ₹${product.price}`}
|
||||
/>
|
||||
|
||||
<View style={tw`flex-row gap-3 mt-6`}>
|
||||
<TouchableOpacity
|
||||
<MyTouchableOpacity
|
||||
onPress={() => setShowAddDialog(false)}
|
||||
style={tw`flex-1 bg-gray-100 p-3 rounded-lg`}
|
||||
>
|
||||
<MyText style={tw`text-gray-700 text-center font-semibold`}>
|
||||
Cancel
|
||||
</MyText>
|
||||
</TouchableOpacity>
|
||||
</MyTouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
<MyTouchableOpacity
|
||||
onPress={handleAddProduct}
|
||||
disabled={!selectedProductId}
|
||||
disabled={selectedProductIds.length === 0}
|
||||
style={tw`flex-1 ${
|
||||
selectedProductId ? 'bg-blue-600' : 'bg-gray-300'
|
||||
selectedProductIds.length > 0 ? 'bg-blue-600' : 'bg-gray-300'
|
||||
} p-3 rounded-lg`}
|
||||
>
|
||||
<MyText style={tw`text-white text-center font-semibold`}>
|
||||
Add Product
|
||||
{selectedProductIds.length > 0
|
||||
? `Add ${selectedProductIds.length} Product${selectedProductIds.length > 1 ? 's' : ''}`
|
||||
: 'Add Products'}
|
||||
</MyText>
|
||||
</TouchableOpacity>
|
||||
</MyTouchableOpacity>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
</BottomDialog>
|
||||
|
||||
</View>
|
||||
</AppContainer>
|
||||
);
|
||||
}
|
||||
|
|
@ -44,7 +44,6 @@ export default function EditProduct() {
|
|||
tagIds: values.tagIds,
|
||||
};
|
||||
|
||||
console.log({payload})
|
||||
|
||||
const formData = new FormData();
|
||||
Object.entries(payload).forEach(([key, value]) => {
|
||||
|
|
|
|||
|
|
@ -63,7 +63,6 @@ const VendorSnippetForm: React.FC<VendorSnippetFormProps> = ({
|
|||
},
|
||||
onSubmit: async (values) => {
|
||||
try {
|
||||
console.log({values})
|
||||
|
||||
const submitData = {
|
||||
snippetCode: values.snippetCode,
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -19,7 +19,7 @@ import { seed } from 'src/db/seed';
|
|||
import './src/jobs/jobs-index';
|
||||
import { startAutomatedJobs } from './src/lib/automatedJobs';
|
||||
|
||||
// seed()
|
||||
seed()
|
||||
initFunc()
|
||||
startAutomatedJobs()
|
||||
|
||||
|
|
|
|||
|
|
@ -124,7 +124,6 @@ export const updateProduct = async (req: Request, res: Response) => {
|
|||
const { id } = req.params;
|
||||
const { name, shortDescription, longDescription, unitId, storeId, price, marketPrice, incrementStep, productQuantity, isSuspended, isFlashAvailable, flashPrice, deals:dealsRaw, imagesToDelete:imagesToDeleteRaw, tagIds } = req.body;
|
||||
|
||||
console.log({productQuantity})
|
||||
|
||||
const deals = dealsRaw ? JSON.parse(dealsRaw) : null;
|
||||
const imagesToDelete = imagesToDeleteRaw ? JSON.parse(imagesToDeleteRaw) : [];
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ export async function seed() {
|
|||
{ key: CONST_KEYS.flashFreeDeliveryThreshold, value: 500 },
|
||||
{ key: CONST_KEYS.flashDeliveryCharge, value: 69 },
|
||||
{ key: CONST_KEYS.popularItems, value: [] },
|
||||
{ key: CONST_KEYS.allItemsOrder, value: [] },
|
||||
{ key: CONST_KEYS.versionNum, value: '1.1.0' },
|
||||
{ key: CONST_KEYS.playStoreUrl, value: 'https://play.google.com/store/apps/details?id=in.freshyo.app' },
|
||||
{ key: CONST_KEYS.appStoreUrl, value: 'https://play.google.com/store/apps/details?id=in.freshyo.app' },
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ export const CONST_KEYS = {
|
|||
flashDeliveryCharge: 'flashDeliveryCharge',
|
||||
platformFeePercent: 'platformFeePercent',
|
||||
taxRate: 'taxRate',
|
||||
tester: 'tester',
|
||||
minOrderAmountForCoupon: 'minOrderAmountForCoupon',
|
||||
maxCouponDiscount: 'maxCouponDiscount',
|
||||
flashDeliverySlotId: 'flashDeliverySlotId',
|
||||
|
|
@ -14,6 +15,7 @@ export const CONST_KEYS = {
|
|||
playStoreUrl: 'playStoreUrl',
|
||||
appStoreUrl: 'appStoreUrl',
|
||||
popularItems: 'popularItems',
|
||||
allItemsOrder: 'allItemsOrder',
|
||||
isFlashDeliveryEnabled: 'isFlashDeliveryEnabled',
|
||||
supportMobile: 'supportMobile',
|
||||
supportEmail: 'supportEmail',
|
||||
|
|
@ -27,6 +29,7 @@ export const CONST_LABELS: Record<ConstKey, string> = {
|
|||
flashDeliveryCharge: 'Flash Delivery Charge',
|
||||
platformFeePercent: 'Platform Fee Percent',
|
||||
taxRate: 'Tax Rate',
|
||||
tester: 'Tester',
|
||||
minOrderAmountForCoupon: 'Minimum Order Amount for Coupon',
|
||||
maxCouponDiscount: 'Maximum Coupon Discount',
|
||||
flashDeliverySlotId: 'Flash Delivery Slot ID',
|
||||
|
|
@ -35,6 +38,7 @@ export const CONST_LABELS: Record<ConstKey, string> = {
|
|||
playStoreUrl: 'Play Store URL',
|
||||
appStoreUrl: 'App Store URL',
|
||||
popularItems: 'Popular Items',
|
||||
allItemsOrder: 'All Items Order',
|
||||
isFlashDeliveryEnabled: 'Enable Flash Delivery',
|
||||
supportMobile: 'Support Mobile',
|
||||
supportEmail: 'Support Email',
|
||||
|
|
|
|||
|
|
@ -431,7 +431,6 @@ export const orderRouter = router({
|
|||
.input(updateOrderItemPackagingSchema)
|
||||
.mutation(async ({ input }) => {
|
||||
const { orderItemId, isPackaged, isPackageVerified } = input;
|
||||
console.log({ orderItemId, isPackaged, isPackageVerified });
|
||||
|
||||
// Validate that orderItem exists
|
||||
const orderItem = await db.query.orderItems.findFirst({
|
||||
|
|
|
|||
|
|
@ -543,9 +543,6 @@ export const slotsRouter = router({
|
|||
|
||||
const { id, deliverySequence } = input;
|
||||
|
||||
|
||||
console.log({deliverySequence})
|
||||
|
||||
const [updatedSlot] = await db
|
||||
.update(deliverySlotInfo)
|
||||
.set({ deliverySequence })
|
||||
|
|
|
|||
|
|
@ -130,7 +130,6 @@ export const vendorSnippetsRouter = router({
|
|||
.input(updateSnippetSchema)
|
||||
.mutation(async ({ input }) => {
|
||||
const { id, updates } = input;
|
||||
console.log({updates})
|
||||
|
||||
// Check if snippet exists
|
||||
const existingSnippet = await db.query.vendorSnippets.findFirst({
|
||||
|
|
|
|||
|
|
@ -29,8 +29,6 @@ export const bannerRouter = router({
|
|||
}
|
||||
})
|
||||
);
|
||||
|
||||
console.log({bannersWithSignedUrls})
|
||||
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -713,8 +713,6 @@ export const orderRouter = router({
|
|||
const userId = ctx.user.userId;
|
||||
const { id, reason } = input;
|
||||
|
||||
console.log({id, reason})
|
||||
|
||||
// Check if order exists and belongs to user
|
||||
const order = await db.query.orders.findFirst({
|
||||
where: eq(orders.id, Number(id)),
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ export const userRouter = router({
|
|||
.mutation(async ({ input, ctx }) => {
|
||||
const userId = ctx.user.userId;
|
||||
const { token } = input;
|
||||
|
||||
|
||||
if (!userId) {
|
||||
throw new ApiError('User not authenticated', 401);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,8 +137,6 @@ function ErrorPage() {
|
|||
export function UserHomeRoute() {
|
||||
const { user } = useUserStore()
|
||||
|
||||
console.log({user})
|
||||
|
||||
return (
|
||||
<div className="max-w-6xl mx-auto space-y-6">
|
||||
{(user?.role?.name === 'admin' || user?.role?.name === 'super_admin') && <AdminDashboard />}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"expo": {
|
||||
"name": "Freshyo",
|
||||
"slug": "freshyo",
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.0",
|
||||
"orientation": "portrait",
|
||||
"icon": "./assets/images/freshyo-logo.png",
|
||||
"scheme": "freshyo",
|
||||
|
|
@ -67,7 +67,8 @@
|
|||
"backgroundColor": "#fff0f6"
|
||||
},
|
||||
"edgeToEdgeEnabled": true,
|
||||
"package": "in.freshyo.app"
|
||||
"package": "in.freshyo.app",
|
||||
"googleServicesFile": "./google-services.json"
|
||||
},
|
||||
"web": {
|
||||
"bundler": "metro",
|
||||
|
|
@ -85,7 +86,8 @@
|
|||
"backgroundColor": "#ffffff"
|
||||
}
|
||||
],
|
||||
"expo-secure-store"
|
||||
"expo-secure-store",
|
||||
"expo-notifications"
|
||||
],
|
||||
"experiments": {
|
||||
"typedRoutes": true
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import MaterialIcons from "@expo/vector-icons/MaterialIcons";
|
|||
import Ionicons from '@expo/vector-icons/Ionicons';
|
||||
import dayjs from "dayjs";
|
||||
import ComplaintForm from "@/components/ComplaintForm";
|
||||
import { orderStatusManipulator } from "@/src/lib/string-manipulators";
|
||||
|
||||
export default function OrderDetails() {
|
||||
const { id } = useLocalSearchParams<{ id: string }>();
|
||||
|
|
@ -145,7 +146,7 @@ export default function OrderDetails() {
|
|||
<View style={tw`flex-row items-center gap-2`}>
|
||||
<View style={[tw`px-3 py-1 rounded-full`, { backgroundColor: statusConfig.color + '10' }]}>
|
||||
<MyText style={[tw`text-[10px] font-bold uppercase`, { color: statusConfig.color }]}>
|
||||
{statusConfig.label}
|
||||
{orderStatusManipulator(statusConfig.label)}
|
||||
</MyText>
|
||||
</View>
|
||||
{order.isFlashDelivery && (
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { trpc } from '@/src/trpc-client';
|
|||
// import RazorpayCheckout from 'react-native-razorpay';
|
||||
import OrderMenu from '@/components/OrderMenu';
|
||||
import dayjs from 'dayjs';
|
||||
import { orderStatusManipulator } from '@/src/lib/string-manipulators';
|
||||
|
||||
// Type definitions
|
||||
interface OrderItem {
|
||||
|
|
@ -136,9 +137,9 @@ const OrderItem: React.FC<OrderItemProps> = ({
|
|||
{item.orderStatus.toLowerCase() === 'cancelled' && (
|
||||
<View style={[tw`flex-row items-center px-3 py-1.5 rounded-full border ${mainStatus.bg} ${mainStatus.border}`]}>
|
||||
<MaterialIcons name={mainStatus.icon as any} size={14} color={mainStatus.color} />
|
||||
<MyText style={[tw`text-[11px] font-bold ml-1.5 uppercase tracking-wide`, { color: mainStatus.color }]}>
|
||||
{mainStatus.label}
|
||||
</MyText>
|
||||
<MyText style={[tw`text-[11px] font-bold ml-1.5 uppercase tracking-wide`, { color: mainStatus.color }]}>
|
||||
{orderStatusManipulator(mainStatus.label)}
|
||||
</MyText>
|
||||
</View>
|
||||
)}
|
||||
{item.isFlashDelivery && (
|
||||
|
|
@ -265,7 +266,7 @@ const OrderItem: React.FC<OrderItemProps> = ({
|
|||
{item.isFlashDelivery ? "1 Hr Delivery:" : "Shipping Status:"}
|
||||
</MyText>
|
||||
<MyText style={[tw`text-xs font-bold`, item.isFlashDelivery ? tw`text-amber-700` : { color: deliveryStatus.color }]} numberOfLines={1}>
|
||||
{item.deliveryStatus}
|
||||
{orderStatusManipulator(item.deliveryStatus)}
|
||||
</MyText>
|
||||
</View>
|
||||
{item.isFlashDelivery && (
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ export default function RootLayout() {
|
|||
SpaceMono: require("../assets/fonts/SpaceMono-Regular.ttf"),
|
||||
});
|
||||
|
||||
console.log('from layout')
|
||||
|
||||
React.useEffect(() => {
|
||||
Appearance.setColorScheme('light')
|
||||
}, []);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { MaterialIcons, Ionicons } from '@expo/vector-icons';
|
|||
import dayjs from 'dayjs';
|
||||
import { trpc } from '@/src/trpc-client';
|
||||
import { Image } from 'expo-image';
|
||||
import { orderStatusManipulator } from '@/src/lib/string-manipulators';
|
||||
|
||||
interface OrderItem {
|
||||
productName: string;
|
||||
|
|
@ -126,7 +127,7 @@ export default function NextOrderGlimpse() {
|
|||
</MyText>
|
||||
</View>
|
||||
<View style={tw`bg-white px-2 py-1 rounded-lg border border-amber-200`}>
|
||||
<MyText style={tw`text-[10px] font-bold text-amber-700 uppercase`}>{nextOrder.deliveryStatus}</MyText>
|
||||
<MyText style={tw`text-[10px] font-bold text-amber-700 uppercase`}>{orderStatusManipulator(nextOrder.deliveryStatus)}</MyText>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
|
|
|
|||
|
|
@ -88,6 +88,10 @@ const ProductCard: React.FC<ProductCardProps> = ({
|
|||
const slotId = getQuickestSlot(item.id);
|
||||
const displayIsOutOfStock = item.isOutOfStock || !slotId;
|
||||
|
||||
// if(item.name.startsWith('Mutton Curry Cut')) {
|
||||
// console.log({slotId, displayIsOutOfStock})
|
||||
// }
|
||||
|
||||
// Return null if nullIfNotAvailable is true and the product is out of stock
|
||||
if (nullIfNotAvailable && displayIsOutOfStock) {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -397,7 +397,14 @@ export function SlotProducts({ slotId:slotIdParent, storeId:storeIdParent, baseU
|
|||
);
|
||||
}
|
||||
|
||||
const filteredProducts: any[] = storeIdNum ? productsQuery?.data?.filter(p => p.store?.id === storeIdNum) || [] : slotQuery.data.products;
|
||||
// Create a Set of product IDs from slot data for O(1) lookup
|
||||
const slotProductIds = new Set(slotQuery.data.products?.map((p: any) => p.id) || []);
|
||||
|
||||
const filteredProducts: any[] = storeIdNum
|
||||
? productsQuery?.data?.filter(p =>
|
||||
p.store?.id === storeIdNum && slotProductIds.has(p.id)
|
||||
) || []
|
||||
: slotQuery.data.products;
|
||||
|
||||
return (
|
||||
<View style={tw`flex-1`}>
|
||||
|
|
@ -481,11 +488,18 @@ export function FlashDeliveryProducts({ storeId:storeIdParent, baseUrl, onProduc
|
|||
// Filter products to only include those eligible for flash delivery
|
||||
let flashProducts: any[] = [];
|
||||
if (storeIdNum) {
|
||||
// Filter by store and flash availability
|
||||
flashProducts = productsQuery?.data?.filter(p => p.store?.id === storeIdNum && p.isFlashAvailable) || [];
|
||||
// Filter by store, flash availability, and stock status
|
||||
flashProducts = productsQuery?.data?.filter(p =>
|
||||
p.store?.id === storeIdNum &&
|
||||
p.isFlashAvailable &&
|
||||
!p.isOutOfStock
|
||||
) || [];
|
||||
} else {
|
||||
// Show all flash-available products (no slot filtering)
|
||||
flashProducts = productsQuery?.data?.filter(p => p.isFlashAvailable) || [];
|
||||
// Show all flash-available products that are in stock
|
||||
flashProducts = productsQuery?.data?.filter(p =>
|
||||
p.isFlashAvailable &&
|
||||
!p.isOutOfStock
|
||||
) || [];
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
29
apps/user-ui/google-services.json
Normal file
29
apps/user-ui/google-services.json
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"project_info": {
|
||||
"project_number": "535753078248",
|
||||
"project_id": "freshyo-cefb2",
|
||||
"storage_bucket": "freshyo-cefb2.firebasestorage.app"
|
||||
},
|
||||
"client": [
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:535753078248:android:d00803550e29eb3238605d",
|
||||
"android_client_info": {
|
||||
"package_name": "in.freshyo.app"
|
||||
}
|
||||
},
|
||||
"oauth_client": [],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": "AIzaSyDW4w6vGDrZnkf8vgwdQObBLnT_iucs2H8"
|
||||
}
|
||||
],
|
||||
"services": {
|
||||
"appinvite_service": {
|
||||
"other_platform_oauth_client": []
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"configuration_version": "1"
|
||||
}
|
||||
|
|
@ -3,7 +3,8 @@ import dayjs from 'dayjs';
|
|||
|
||||
export function useProductSlotIdentifier() {
|
||||
// Fetch all slots with products
|
||||
const { data: slotsData } = trpc.user.slots.getSlotsWithProducts.useQuery();
|
||||
const { data: slotsData, isLoading: isProductsLoading } = trpc.user.slots.getSlotsWithProducts.useQuery();
|
||||
|
||||
|
||||
const productSlotsMap = new Map<number, number[]>();
|
||||
|
||||
|
|
@ -24,16 +25,18 @@ export function useProductSlotIdentifier() {
|
|||
}
|
||||
|
||||
const getQuickestSlot = (productId: number): number | null => {
|
||||
|
||||
if (!slotsData?.slots) return null;
|
||||
|
||||
|
||||
const now = dayjs();
|
||||
|
||||
|
||||
// Find slots that contain this product and have future delivery time
|
||||
const availableSlots = slotsData.slots.filter(slot =>
|
||||
slot.products.some(product => product.id === productId) &&
|
||||
dayjs(slot.deliveryTime).isAfter(now)
|
||||
);
|
||||
|
||||
// if(productId === 98)
|
||||
// console.log(JSON.stringify(slotsData))
|
||||
if (availableSlots.length === 0) return null;
|
||||
|
||||
// Return earliest slot ID (sorted by delivery time)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ function NotifChecker(props: Props) {
|
|||
const { notifPermission, expoPushToken } = useNotification();
|
||||
|
||||
React.useEffect(() => {
|
||||
console.log({isAuthenticated, expoPushToken, notifPermission});
|
||||
if (isAuthenticated && expoPushToken && notifPermission === 'granted') {
|
||||
savePushTokenMutation.mutate(
|
||||
{ token: expoPushToken },
|
||||
|
|
|
|||
|
|
@ -53,10 +53,13 @@ export const NotificationProvider: React.FC<NotificationProviderProps> = ({
|
|||
useEffect(() => {
|
||||
registerForPushNotificationsAsync()
|
||||
.then((token) => {
|
||||
|
||||
|
||||
setExpoPushToken(token);
|
||||
setNotifPermission("granted");
|
||||
})
|
||||
.catch((errorRaw) => {
|
||||
|
||||
|
||||
const err = String(errorRaw).slice(7); //remove the "Error: " string component in beginning
|
||||
|
||||
|
|
|
|||
27
apps/user-ui/src/lib/string-manipulators.ts
Normal file
27
apps/user-ui/src/lib/string-manipulators.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* String manipulation utilities for the user UI
|
||||
*
|
||||
* This file contains helper functions for transforming and formatting
|
||||
* strings throughout the application. These utilities ensure consistent
|
||||
* display formatting across all components.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Transforms delivery status for display.
|
||||
*
|
||||
* Why: The term "Pending" can be confusing for users as it implies
|
||||
* uncertainty. "Confirmed" provides clearer communication that the
|
||||
* order has been received and acknowledged by the system.
|
||||
*
|
||||
* @param status - The raw delivery status from API
|
||||
* @returns User-friendly status label
|
||||
* @example
|
||||
* orderStatusManipulator('pending') // returns 'Confirmed'
|
||||
* orderStatusManipulator('packaged') // returns 'packaged' (unchanged)
|
||||
*/
|
||||
export const orderStatusManipulator = (status: string): string => {
|
||||
if (status.toLowerCase() === 'pending') {
|
||||
return 'Confirmed';
|
||||
}
|
||||
return status;
|
||||
};
|
||||
|
|
@ -63,8 +63,8 @@ const isDevMode = Constants.executionEnvironment !== "standalone";
|
|||
// const BASE_API_URL = API_URL;
|
||||
// const BASE_API_URL = 'http://10.0.2.2:4000';
|
||||
// const BASE_API_URL = 'http://192.168.100.101:4000';
|
||||
// const BASE_API_URL = 'http://192.168.1.3:4000';
|
||||
let BASE_API_URL = "https://mf.freshyo.in";
|
||||
const BASE_API_URL = 'http://192.168.1.3:4000';
|
||||
// let BASE_API_URL = "https://mf.freshyo.in";
|
||||
// let BASE_API_URL = 'http://192.168.100.104:4000';
|
||||
// let BASE_API_URL = 'http://192.168.29.176:4000';
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue