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 = ({ item, drag, isActive, }) => { return ( {/* Drag Handle */} {/* Product Image */} {item.images?.[0] ? ( ) : ( )} {/* Product Info */} {item.name.length > 30 ? item.name.substring(0, 30) + '...' : item.name} {item.isOutOfStock && ( )} ); }; export default function AllItemsOrder() { const router = useRouter(); const queryClient = useQueryClient(); const [products, setProducts] = useState([]); 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 ( ); }, []); 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 ( router.back()} style={tw`p-2 -ml-4`} > All Items Order {isLoadingConstants ? 'Loading order...' : 'Loading products...'} ); } // Show error state if queries failed if (constantsError || productsError) { return ( router.back()} style={tw`p-2 -ml-4`} > All Items Order Error {constantsError ? 'Failed to load order' : 'Failed to load products'} router.back()} style={tw`mt-6 bg-blue-600 px-6 py-3 rounded-full`} > Go Back ); } return ( {/* Header */} router.back()} style={tw`p-2 -ml-4`} > All Items Order {updateConstants.isPending ? 'Saving...' : 'Save'} {/* Content */} {products.length === 0 ? ( No products available ) : ( Long press and drag to reorder • {products.length} items item.id.toString()} onDragEnd={handleDragEnd} showsVerticalScrollIndicator={true} contentContainerStyle={{ paddingBottom: 20 }} containerStyle={tw`flex-1`} keyboardShouldPersistTaps="handled" // Enable auto-scroll during drag activationDistance={10} /> )} ); } 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, }, });