This commit is contained in:
shafi54 2026-02-08 01:45:50 +05:30
parent d1d7db55a0
commit 55c41fa0af
8 changed files with 93 additions and 59 deletions

View file

@ -1,10 +1,11 @@
import React, { useState, useEffect, useMemo } from "react"; import React, { useState, useEffect, useMemo } from "react";
import { import {
View, View,
TouchableOpacity,
Alert, Alert,
ActivityIndicator, ActivityIndicator,
ScrollView,
} from "react-native"; } from "react-native";
import { TouchableOpacity } from "react-native-gesture-handler";
import { Image } from "expo-image"; import { Image } from "expo-image";
import DraggableFlatList, { import DraggableFlatList, {
RenderItemParams, RenderItemParams,
@ -16,6 +17,7 @@ import {
tw, tw,
BottomDialog, BottomDialog,
BottomDropdown, BottomDropdown,
MyTouchableOpacity,
} from "common-ui"; } from "common-ui";
import ProductsSelector from "../../../components/ProductsSelector"; import ProductsSelector from "../../../components/ProductsSelector";
import { useRouter } from "expo-router"; import { useRouter } from "expo-router";
@ -27,8 +29,8 @@ interface PopularProduct {
id: number; id: number;
name: string; name: string;
shortDescription: string | null; shortDescription: string | null;
price: string; price: number;
marketPrice: string | null; marketPrice: number | null;
unit: string; unit: string;
incrementStep: number; incrementStep: number;
productQuantity: number; productQuantity: number;
@ -119,7 +121,7 @@ export default function CustomizePopularItems() {
const [popularProducts, setPopularProducts] = useState<PopularProduct[]>([]); const [popularProducts, setPopularProducts] = useState<PopularProduct[]>([]);
const [hasChanges, setHasChanges] = useState(false); const [hasChanges, setHasChanges] = useState(false);
const [showAddDialog, setShowAddDialog] = 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 // Get current popular items from constants
const { data: constants, isLoading: isLoadingConstants, error: constantsError } = trpc.admin.const.getConstants.useQuery(); const { data: constants, isLoading: isLoadingConstants, error: constantsError } = trpc.admin.const.getConstants.useQuery();
@ -182,14 +184,20 @@ export default function CustomizePopularItems() {
}; };
const handleAddProduct = () => { const handleAddProduct = () => {
if (selectedProductId) { if (selectedProductIds.length > 0) {
const product = allProducts?.products.find(p => p.id === selectedProductId); const newProducts = selectedProductIds
if (product && !popularProducts.find(p => p.id === product.id)) { .map(id => allProducts?.products.find(p => p.id === id))
setPopularProducts(prev => [...prev, product as PopularProduct]); .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); setHasChanges(true);
setSelectedProductId(null);
setShowAddDialog(false);
} }
setSelectedProductIds([]);
setShowAddDialog(false);
} }
}; };
@ -293,20 +301,19 @@ export default function CustomizePopularItems() {
} }
return ( return (
<AppContainer> <View style={[tw`flex-1 bg-gray-50 relative`]}>
<View style={tw`flex-1 bg-gray-50`}>
{/* Header */} {/* Header */}
<View style={tw`bg-white px-4 py-4 border-b border-gray-200 flex-row items-center justify-between`}> <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()} onPress={() => router.back()}
style={tw`p-2 -ml-4`} style={tw`p-2 -ml-4`}
> >
<MaterialIcons name="chevron-left" size={24} color="#374151" /> <MaterialIcons name="chevron-left" size={24} color="#374151" />
</TouchableOpacity> </MyTouchableOpacity>
<MyText style={tw`text-xl font-bold text-gray-900`}>Popular Items</MyText> <MyText style={tw`text-xl font-bold text-gray-900`}>Popular Items</MyText>
<TouchableOpacity <MyTouchableOpacity
onPress={handleSave} onPress={handleSave}
disabled={!hasChanges || updateConstants.isPending} disabled={!hasChanges || updateConstants.isPending}
style={tw`px-4 py-2 rounded-lg ${ style={tw`px-4 py-2 rounded-lg ${
@ -322,7 +329,7 @@ export default function CustomizePopularItems() {
} font-semibold`}> } font-semibold`}>
{updateConstants.isPending ? 'Saving...' : 'Save'} {updateConstants.isPending ? 'Saving...' : 'Save'}
</MyText> </MyText>
</TouchableOpacity> </MyTouchableOpacity>
</View> </View>
{/* Content */} {/* Content */}
@ -356,35 +363,41 @@ export default function CustomizePopularItems() {
)} )}
keyExtractor={(item) => item.id.toString()} keyExtractor={(item) => item.id.toString()}
onDragEnd={handleDragEnd} onDragEnd={handleDragEnd}
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={true}
contentContainerStyle={tw`pb-8`} scrollEnabled={true}
contentContainerStyle={{ paddingBottom: 80 }}
containerStyle={tw`flex-1`}
keyboardShouldPersistTaps="handled"
/> />
</View> </View>
)} )}
{/* FAB for Add Product */} {/* FAB for Add Product - Fixed position */}
<View style={tw`absolute bottom-4 right-4`}> <View style={tw`absolute bottom-12 right-6 z-50`}>
<TouchableOpacity <MyTouchableOpacity
onPress={() => setShowAddDialog(true)} 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" /> <MaterialIcons name="add" size={24} color="white" />
</TouchableOpacity> </MyTouchableOpacity>
</View> </View>
{/* Add Product Dialog */} {/* Add Product Dialog */}
<BottomDialog <BottomDialog
open={showAddDialog} open={showAddDialog}
onClose={() => setShowAddDialog(false)} onClose={() => {
setShowAddDialog(false);
setSelectedProductIds([]);
}}
> >
<View style={tw`pb-8 pt-2 px-4`}> <View style={tw`pb-8 pt-2 px-4`}>
<View style={tw`items-center mb-6`}> <View style={tw`items-center mb-6`}>
<View style={tw`w-12 h-1.5 bg-gray-200 rounded-full mb-4`} /> <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`}> <MyText style={tw`text-lg font-bold text-gray-900`}>
Add Popular Item Add Popular Items
</MyText> </MyText>
<MyText style={tw`text-sm text-gray-500`}> <MyText style={tw`text-sm text-gray-500`}>
Select a product to add to popular items Select products to add to popular items
</MyText> </MyText>
</View> </View>
@ -398,41 +411,43 @@ export default function CustomizePopularItems() {
) : ( ) : (
<> <>
<ProductsSelector <ProductsSelector
value={selectedProductId || 0} value={selectedProductIds}
onChange={(val) => setSelectedProductId(val as number)} onChange={(val) => setSelectedProductIds(val as number[])}
multiple={false} multiple={true}
label="Select Product" label="Select Products"
placeholder="Choose a product..." placeholder="Choose products..."
labelFormat={(product) => `${product.name} - ₹${product.price}`} labelFormat={(product) => `${product.name} - ₹${product.price}`}
/> />
<View style={tw`flex-row gap-3 mt-6`}> <View style={tw`flex-row gap-3 mt-6`}>
<TouchableOpacity <MyTouchableOpacity
onPress={() => setShowAddDialog(false)} onPress={() => setShowAddDialog(false)}
style={tw`flex-1 bg-gray-100 p-3 rounded-lg`} style={tw`flex-1 bg-gray-100 p-3 rounded-lg`}
> >
<MyText style={tw`text-gray-700 text-center font-semibold`}> <MyText style={tw`text-gray-700 text-center font-semibold`}>
Cancel Cancel
</MyText> </MyText>
</TouchableOpacity> </MyTouchableOpacity>
<TouchableOpacity <MyTouchableOpacity
onPress={handleAddProduct} onPress={handleAddProduct}
disabled={!selectedProductId} disabled={selectedProductIds.length === 0}
style={tw`flex-1 ${ style={tw`flex-1 ${
selectedProductId ? 'bg-blue-600' : 'bg-gray-300' selectedProductIds.length > 0 ? 'bg-blue-600' : 'bg-gray-300'
} p-3 rounded-lg`} } p-3 rounded-lg`}
> >
<MyText style={tw`text-white text-center font-semibold`}> <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> </MyText>
</TouchableOpacity> </MyTouchableOpacity>
</View> </View>
</> </>
)} )}
</View> </View>
</BottomDialog> </BottomDialog>
</View> </View>
</AppContainer>
); );
} }

File diff suppressed because one or more lines are too long

View file

@ -8,6 +8,7 @@ import MaterialIcons from "@expo/vector-icons/MaterialIcons";
import Ionicons from '@expo/vector-icons/Ionicons'; import Ionicons from '@expo/vector-icons/Ionicons';
import dayjs from "dayjs"; import dayjs from "dayjs";
import ComplaintForm from "@/components/ComplaintForm"; import ComplaintForm from "@/components/ComplaintForm";
import { orderStatusManipulator } from "@/src/lib/string-manipulators";
export default function OrderDetails() { export default function OrderDetails() {
const { id } = useLocalSearchParams<{ id: string }>(); 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`flex-row items-center gap-2`}>
<View style={[tw`px-3 py-1 rounded-full`, { backgroundColor: statusConfig.color + '10' }]}> <View style={[tw`px-3 py-1 rounded-full`, { backgroundColor: statusConfig.color + '10' }]}>
<MyText style={[tw`text-[10px] font-bold uppercase`, { color: statusConfig.color }]}> <MyText style={[tw`text-[10px] font-bold uppercase`, { color: statusConfig.color }]}>
{statusConfig.label} {orderStatusManipulator(statusConfig.label)}
</MyText> </MyText>
</View> </View>
{order.isFlashDelivery && ( {order.isFlashDelivery && (

View file

@ -9,6 +9,7 @@ import { trpc } from '@/src/trpc-client';
// import RazorpayCheckout from 'react-native-razorpay'; // import RazorpayCheckout from 'react-native-razorpay';
import OrderMenu from '@/components/OrderMenu'; import OrderMenu from '@/components/OrderMenu';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { orderStatusManipulator } from '@/src/lib/string-manipulators';
// Type definitions // Type definitions
interface OrderItem { interface OrderItem {
@ -136,9 +137,9 @@ const OrderItem: React.FC<OrderItemProps> = ({
{item.orderStatus.toLowerCase() === 'cancelled' && ( {item.orderStatus.toLowerCase() === 'cancelled' && (
<View style={[tw`flex-row items-center px-3 py-1.5 rounded-full border ${mainStatus.bg} ${mainStatus.border}`]}> <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} /> <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 }]}> <MyText style={[tw`text-[11px] font-bold ml-1.5 uppercase tracking-wide`, { color: mainStatus.color }]}>
{mainStatus.label} {orderStatusManipulator(mainStatus.label)}
</MyText> </MyText>
</View> </View>
)} )}
{item.isFlashDelivery && ( {item.isFlashDelivery && (
@ -265,7 +266,7 @@ const OrderItem: React.FC<OrderItemProps> = ({
{item.isFlashDelivery ? "1 Hr Delivery:" : "Shipping Status:"} {item.isFlashDelivery ? "1 Hr Delivery:" : "Shipping Status:"}
</MyText> </MyText>
<MyText style={[tw`text-xs font-bold`, item.isFlashDelivery ? tw`text-amber-700` : { color: deliveryStatus.color }]} numberOfLines={1}> <MyText style={[tw`text-xs font-bold`, item.isFlashDelivery ? tw`text-amber-700` : { color: deliveryStatus.color }]} numberOfLines={1}>
{item.deliveryStatus} {orderStatusManipulator(item.deliveryStatus)}
</MyText> </MyText>
</View> </View>
{item.isFlashDelivery && ( {item.isFlashDelivery && (

View file

@ -7,6 +7,7 @@ import { MaterialIcons, Ionicons } from '@expo/vector-icons';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { trpc } from '@/src/trpc-client'; import { trpc } from '@/src/trpc-client';
import { Image } from 'expo-image'; import { Image } from 'expo-image';
import { orderStatusManipulator } from '@/src/lib/string-manipulators';
interface OrderItem { interface OrderItem {
productName: string; productName: string;
@ -126,7 +127,7 @@ export default function NextOrderGlimpse() {
</MyText> </MyText>
</View> </View>
<View style={tw`bg-white px-2 py-1 rounded-lg border border-amber-200`}> <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>
</View> </View>

View file

@ -1,21 +1,10 @@
import { trpc } from '@/src/trpc-client'; import { trpc } from '@/src/trpc-client';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { useEffect } from 'react';
let isPrinted = false;
export function useProductSlotIdentifier() { export function useProductSlotIdentifier() {
// Fetch all slots with products // Fetch all slots with products
const { data: slotsData, isLoading: isProductsLoading } = trpc.user.slots.getSlotsWithProducts.useQuery(); const { data: slotsData, isLoading: isProductsLoading } = trpc.user.slots.getSlotsWithProducts.useQuery();
useEffect(() => {
if(!isProductsLoading && !isPrinted) {
isPrinted = true;
const slotInfo = slotsData?.slots.map(slot => ({...slot, products: slot.products.map(item => ({id: item.id, name: item.name}))}))
console.log(JSON.stringify(slotInfo))
}
},[slotsData])
const productSlotsMap = new Map<number, number[]>(); const productSlotsMap = new Map<number, number[]>();

View 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;
};

View file

@ -63,8 +63,8 @@ const isDevMode = Constants.executionEnvironment !== "standalone";
// const BASE_API_URL = API_URL; // const BASE_API_URL = API_URL;
// const BASE_API_URL = 'http://10.0.2.2:4000'; // 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.100.101:4000';
// const BASE_API_URL = 'http://192.168.1.3:4000'; const BASE_API_URL = 'http://192.168.1.3:4000';
let BASE_API_URL = "https://mf.freshyo.in"; // 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.100.104:4000';
// let BASE_API_URL = 'http://192.168.29.176:4000'; // let BASE_API_URL = 'http://192.168.29.176:4000';