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 SkuItemProps { sku: any; productName: string; hasChanges: (skuId: number) => boolean; pendingChanges: Record; setPendingChanges: React.Dispatch>>; openEditDialog: (sku: any, productName: string) => void; } const SkuItemComponent: React.FC = ({ sku, productName, hasChanges, pendingChanges, setPendingChanges, openEditDialog, }) => { const changed = hasChanges(sku.id); const change = pendingChanges[sku.id] || {}; const displayPrice = change.price !== undefined ? change.price : sku.price; const displayMarketPrice = change.marketPrice !== undefined ? change.marketPrice : sku.marketPrice; const displayFlashPrice = change.flashPrice !== undefined ? change.flashPrice : sku.flashPrice; const displayProductQuantity = change.productQuantity !== undefined ? change.productQuantity : sku.productQuantity; return ( {changed && } {productName.length > 20 ? productName.substring(0, 20) + '...' : productName} {sku.displayName || sku.name || ''} { const currentValue = change.isFlashAvailable ?? sku.isFlashAvailable ?? false; setPendingChanges(prev => ({ ...prev, [sku.id]: { ...prev[sku.id], isFlashAvailable: !currentValue, }, })); }} style={tw`mr-1`} /> Flash Our Price ₹{displayPrice} openEditDialog(sku, productName)} style={tw`ml-1`}> Market Price {displayMarketPrice ? `₹${displayMarketPrice}` : "N/A"} openEditDialog(sku, productName)} style={tw`ml-1`}> Flash Price {displayFlashPrice ? `₹${displayFlashPrice}` : "N/A"} openEditDialog(sku, productName)} style={tw`ml-1`}> Size {displayProductQuantity ? `${displayProductQuantity}${sku.unit?.shortNotation || ''}` : "N/A"} openEditDialog(sku, productName)} style={tw`ml-1`}> ); }; interface PendingChange { price?: number; marketPrice?: number | null; flashPrice?: number | null; productQuantity?: number | null; isFlashAvailable?: boolean; } interface EditDialogState { open: boolean; sku: any; productName: string; tempPrice: string; tempMarketPrice: string; tempFlashPrice: string; tempProductQuantity: string; } export default function PricesOverview() { const router = useRouter(); const [selectedStores, setSelectedStores] = useState([]); const [pendingChanges, setPendingChanges] = useState>({}); const [editDialog, setEditDialog] = useState({ open: false, sku: null, productName: "", tempPrice: "", tempMarketPrice: "", tempFlashPrice: "", tempProductQuantity: "", }); 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 || []; const sortedStores = useMemo(() => [...stores].sort((a, b) => a.name.localeCompare(b.name)), [stores] ); const storeOptions = useMemo(() => sortedStores.map(store => ({ label: store.name, value: store.id.toString(), })), [sortedStores] ); useEffect(() => { if (stores.length > 0 && selectedStores.length === 0) { setSelectedStores(stores.map(s => s.id.toString())); } }, [stores, selectedStores]); const allSkus = useMemo(() => { const skus: any[] = []; for (const product of allProducts) { if (product.skus && product.skus.length > 0) { for (const sku of product.skus) { skus.push({ ...sku, _productName: product.name, _storeId: product.storeId }); } } } return skus; }, [allProducts]); const filteredSkus = useMemo(() => { if (selectedStores.length === 0) return allSkus; return allSkus.filter(sku => sku._storeId && selectedStores.includes(sku._storeId.toString()) ); }, [allSkus, selectedStores]); const hasChanges = (skuId: number) => !!pendingChanges[skuId]; const openEditDialog = (sku: any, productName: string) => { const change = pendingChanges[sku.id] || {}; setEditDialog({ open: true, sku, productName, tempPrice: (change.price ?? sku.price)?.toString() || "", tempMarketPrice: (change.marketPrice ?? sku.marketPrice)?.toString() || "", tempFlashPrice: (change.flashPrice ?? sku.flashPrice)?.toString() || "", tempProductQuantity: (change.productQuantity ?? sku.productQuantity)?.toString() || "", }); }; const saveEditDialog = () => { const price = parseFloat(editDialog.tempPrice); const marketPrice = editDialog.tempMarketPrice ? parseFloat(editDialog.tempMarketPrice) : null; const flashPrice = editDialog.tempFlashPrice ? parseFloat(editDialog.tempFlashPrice) : null; const productQuantity = editDialog.tempProductQuantity ? parseFloat(editDialog.tempProductQuantity) : 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; } if (editDialog.tempProductQuantity && (isNaN(productQuantity!) || productQuantity! <= 0)) { Alert.alert("Error", "Please enter a valid size"); return; } setPendingChanges(prev => ({ ...prev, [editDialog.sku.id]: { price: price !== parseFloat(editDialog.sku.price) ? price : undefined, marketPrice: marketPrice !== parseFloat(editDialog.sku.marketPrice || '0') ? marketPrice : undefined, flashPrice: flashPrice !== parseFloat(editDialog.sku.flashPrice || '0') ? flashPrice : undefined, productQuantity: productQuantity !== (editDialog.sku.productQuantity || 1) ? productQuantity : undefined, }, })); setEditDialog({ open: false, sku: null, productName: "", tempPrice: "", tempMarketPrice: "", tempFlashPrice: "", tempProductQuantity: "" }); }; const handleSave = () => { const updates = Object.entries(pendingChanges).map(([skuId, change]) => { const sku = allSkus.find(s => s.id === parseInt(skuId)); const update: any = { productId: sku?.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.productQuantity !== undefined) update.productQuantity = change.productQuantity; 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 ( 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`} > {productsLoading || storesLoading ? ( Loading... ) : ( ( )} keyExtractor={(item) => item.id.toString()} contentContainerStyle={tw`p-4`} showsVerticalScrollIndicator={false} /> )} setEditDialog({ ...editDialog, open: false, tempFlashPrice: "", tempMarketPrice: "", tempPrice: "", tempProductQuantity: "" })}> {editDialog.productName} {editDialog.sku?.displayName || editDialog.sku?.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" /> Size setEditDialog({ ...editDialog, tempProductQuantity: text })} keyboardType="decimal-pad" placeholder="Enter size" /> Update Price setShowMenu(false)}> Options { router.push('/rebalance-orders' as any); setShowMenu(false); }} > Re-Balance Orders ); }