import React, { useState, useEffect, useMemo } from "react"; import { View, TouchableOpacity, FlatList, Image, Alert, ActivityIndicator, TextInput, } from "react-native"; import { useRouter } from "expo-router"; import { AppContainer, MyText, tw, BottomDialog, BottomDropdown, Checkbox, } from "common-ui"; import { trpc } from "@/src/trpc-client"; import MaterialIcons from "@expo/vector-icons/MaterialIcons"; import { Entypo } from "@expo/vector-icons"; interface ProductItemProps { item: any; hasChanges: (productId: number) => boolean; pendingChanges: Record; setPendingChanges: React.Dispatch>>; openEditDialog: (product: any) => void; } const ProductItemComponent: React.FC = ({ item: product, hasChanges, pendingChanges, setPendingChanges, openEditDialog, }) => { const changed = hasChanges(product.id); const change = pendingChanges[product.id] || {}; const displayPrice = change.price !== undefined ? change.price : product.price; const displayMarketPrice = change.marketPrice !== undefined ? change.marketPrice : product.marketPrice; const displayFlashPrice = change.flashPrice !== undefined ? change.flashPrice : product.flashPrice; return ( {/* Change indicator */} {changed && } {/* First row: Image and Name */} {/* Product image */} {/* Product name and Flash Checkbox */} {product.name.length > 25 ? product.name.substring(0, 25) + '...' : product.name} { const currentValue = change.isFlashAvailable ?? product.isFlashAvailable ?? false; setPendingChanges(prev => ({ ...prev, [product.id]: { ...change, isFlashAvailable: !currentValue, }, })); }} style={tw`mr-1`} /> Flash {/* Second row: Prices */} {/* Our Price */} Our Price ₹{displayPrice} openEditDialog(product)} style={tw`ml-1`}> {/* Market Price */} Market Price {displayMarketPrice ? `₹${displayMarketPrice}` : "N/A"} openEditDialog(product)} style={tw`ml-1`}> {/* Flash Price */} Flash Price {displayFlashPrice ? `₹${displayFlashPrice}` : "N/A"} openEditDialog(product)} style={tw`ml-1`}> ); }; interface PendingChange { price?: number; marketPrice?: number | null; flashPrice?: number | null; isFlashAvailable?: boolean; } interface EditDialogState { open: boolean; product: any; tempPrice: string; tempMarketPrice: string; tempFlashPrice: string; } export default function PricesOverview() { const router = useRouter(); const [selectedStores, setSelectedStores] = useState([]); const [pendingChanges, setPendingChanges] = useState>({}); const [editDialog, setEditDialog] = useState({ open: false, product: null, tempPrice: "", tempMarketPrice: "", tempFlashPrice: "", }); const [showMenu, setShowMenu] = useState(false); const { data: productsData, isLoading: productsLoading, refetch: refetchProducts } = trpc.admin.product.getProducts.useQuery(); const { data: storesData, isLoading: storesLoading } = trpc.admin.store.getStores.useQuery(); const updatePricesMutation = trpc.admin.product.updateProductPrices.useMutation(); const stores = storesData?.stores || []; const allProducts = productsData?.products || []; // Sort stores alphabetically const sortedStores = useMemo(() => [...stores].sort((a, b) => a.name.localeCompare(b.name)), [stores] ); // Store options for dropdown const storeOptions = useMemo(() => sortedStores.map(store => ({ label: store.name, value: store.id.toString(), })), [sortedStores] ); // Initialize selectedStores to all if not set useEffect(() => { if (stores.length > 0 && selectedStores.length === 0) { setSelectedStores(stores.map(s => s.id.toString())); } }, [stores, selectedStores]); // Filter products by selected stores const filteredProducts = useMemo(() => { if (selectedStores.length === 0) return allProducts; return allProducts.filter(product => product.storeId && selectedStores.includes(product.storeId.toString()) ); }, [allProducts, selectedStores]); // Check if a product has changes const hasChanges = (productId: number) => !!pendingChanges[productId]; // Open edit dialog const openEditDialog = (product: any) => { const change = pendingChanges[product.id] || {}; setEditDialog({ open: true, product, tempPrice: (change.price ?? product.price)?.toString() || "", tempMarketPrice: (change.marketPrice ?? product.marketPrice)?.toString() || "", tempFlashPrice: (change.flashPrice ?? product.flashPrice)?.toString() || "", }); }; // Save edit dialog const saveEditDialog = () => { const price = parseFloat(editDialog.tempPrice); const marketPrice = editDialog.tempMarketPrice ? parseFloat(editDialog.tempMarketPrice) : null; const flashPrice = editDialog.tempFlashPrice ? parseFloat(editDialog.tempFlashPrice) : null; if (isNaN(price) || price <= 0) { Alert.alert("Error", "Please enter a valid price"); return; } if (editDialog.tempMarketPrice && (isNaN(marketPrice!) || marketPrice! <= 0)) { Alert.alert("Error", "Please enter a valid market price"); return; } if (editDialog.tempFlashPrice && (isNaN(flashPrice!) || flashPrice! <= 0)) { Alert.alert("Error", "Please enter a valid flash price"); return; } setPendingChanges(prev => ({ ...prev, [editDialog.product.id]: { price: price !== editDialog.product.price ? price : undefined, marketPrice: marketPrice !== editDialog.product.marketPrice ? marketPrice : undefined, flashPrice: flashPrice !== editDialog.product.flashPrice ? flashPrice : undefined, }, })); setEditDialog({ open: false, product: null, tempPrice: "", tempMarketPrice: "", tempFlashPrice: "" }); }; // Handle save all changes const handleSave = () => { const updates = Object.entries(pendingChanges).map(([productId, change]) => { const update: any = { productId: parseInt(productId) }; if (change.price !== undefined) update.price = change.price; if (change.marketPrice !== undefined) update.marketPrice = change.marketPrice; if (change.flashPrice !== undefined) update.flashPrice = change.flashPrice; if (change.isFlashAvailable !== undefined) update.isFlashAvailable = change.isFlashAvailable; return update; }); updatePricesMutation.mutate( { updates }, { onSuccess: () => { setPendingChanges({}); refetchProducts(); Alert.alert("Success", "Prices updated successfully"); }, onError: (error: any) => { Alert.alert("Error", `Failed to update prices: ${error.message || "Unknown error"}`); }, } ); }; const changeCount = Object.keys(pendingChanges).length; return ( {/* Stores filter, save button, and menu */} setSelectedStores(value as string[])} multiple={true} placeholder="Select stores" /> 0 && !updatePricesMutation.isPending ? tw`bg-blue-600` : tw`bg-gray-300`, ]} onPress={handleSave} disabled={changeCount === 0 || updatePricesMutation.isPending} > {updatePricesMutation.isPending ? ( 0 ? "white" : "#6b7280"} style={tw`mr-2`} /> ) : ( 0 ? "white" : "#6b7280"} style={tw`mr-2`} /> )} 0 ? tw`text-white` : tw`text-gray-500`, ]} > Save ({changeCount}) setShowMenu(true)} style={tw`p-2 -mr-2`} > {/* Content */} {productsLoading || storesLoading ? ( Loading... ) : ( ( )} keyExtractor={(item) => item.id.toString()} contentContainerStyle={tw`p-4`} showsVerticalScrollIndicator={false} /> )} {/* Edit Dialog */} setEditDialog({ ...editDialog, open: false, tempFlashPrice: "" })}> {editDialog.product?.name} Our Price setEditDialog({ ...editDialog, tempPrice: text })} keyboardType="numeric" placeholder="Enter price" /> Market Price (Optional) setEditDialog({ ...editDialog, tempMarketPrice: text })} keyboardType="numeric" placeholder="Enter market price" /> Flash Price (Optional) setEditDialog({ ...editDialog, tempFlashPrice: text })} keyboardType="numeric" placeholder="Enter flash price" /> Update Price {/* Menu Dialog */} setShowMenu(false)}> Options { router.push('/rebalance-orders' as any); setShowMenu(false); }} > Re-Balance Orders ); }