import React, { useState, useCallback, useMemo } from 'react'; import { View, TouchableOpacity, Alert, RefreshControl, Dimensions } from 'react-native'; import { MaterialCommunityIcons } from '@expo/vector-icons'; import { LinearGradient } from 'expo-linear-gradient'; import { tw, MyButton, MyText, SearchBar, MyFlatList, useMarkDataFetchers, MyTouchableOpacity, BottomDropdown } from 'common-ui'; import useManualRefresh from 'common-ui/hooks/useManualRefresh'; import { trpc } from '@/src/trpc-client'; import { useRouter } from 'expo-router'; import { TabView, SceneMap, TabBar } from 'react-native-tab-view'; const CouponItem = ({ item, onDelete }: { item: any; onDelete: (id: number) => void }) => { const router = useRouter(); const getCouponStatus = (coupon: any) => { if (coupon.isInvalidated) return 'inactive'; if (coupon.validTill && new Date(coupon.validTill) <= new Date()) return 'expired'; return 'active'; }; const status = getCouponStatus(item); const getBorderColor = () => { if (status === 'active') return 'border-green-500'; if (status === 'expired') return 'border-yellow-500'; return 'border-red-500'; }; const getBgColor = () => { if (status === 'active') return 'bg-green-100'; if (status === 'expired') return 'bg-yellow-100'; return 'bg-red-100'; }; const getTextColor = () => { if (status === 'active') return 'text-green-600'; if (status === 'expired') return 'text-yellow-600'; return 'text-red-600'; }; const getIconColor = () => { if (status === 'active') return '#10b981'; if (status === 'expired') return '#f59e0b'; return '#ef4444'; }; return ( {item.couponCode} ID: {item.id} {status === 'active' ? 'Active' : status === 'expired' ? 'Expired' : 'Inactive'} Discount: {item.discountPercent ? `${item.discountPercent}% off` : item.flatDiscount ? `₹${item.flatDiscount} off` : 'N/A'} Min Order: {item.minOrder ? `₹${item.minOrder}` : 'None'} Max: {item.maxValue ? `₹${item.maxValue}` : 'None'} Valid Till: {item.validTill ? new Date(item.validTill).toLocaleDateString() : 'No expiry'} Target: {item.isApplyForAll ? 'All Users' : item.applicableUsers?.length > 0 ? `${item.applicableUsers.length} Users` : 'All Users'} {item.applicableProducts && item.applicableProducts.length > 0 && ( Products: {item.applicableProducts.length} selected )} router.push(`/(drawer)/edit-coupon/${item.id}`)} style={tw`bg-blue-500 p-3 rounded-lg shadow-md flex-1 flex-row items-center justify-center`} > Edit onDelete(item.id)} style={tw`bg-red-500 p-3 rounded-lg shadow-md flex-1 flex-row items-center justify-center`} > Delete ); }; const ReservedCouponItem = ({ item }: { item: any }) => { const getStatus = () => { if (item.isRedeemed) return 'redeemed'; if (item.validTill && new Date(item.validTill) <= new Date()) return 'expired'; return 'active'; }; const status = getStatus(); const getBorderColor = () => { if (status === 'active') return 'border-green-500'; if (status === 'expired') return 'border-yellow-500'; return 'border-blue-500'; }; const getBgColor = () => { if (status === 'active') return 'bg-green-100'; if (status === 'expired') return 'bg-yellow-100'; return 'bg-blue-100'; }; const getTextColor = () => { if (status === 'active') return 'text-green-600'; if (status === 'expired') return 'text-yellow-600'; return 'text-blue-600'; }; const getIconColor = () => { if (status === 'active') return '#10b981'; if (status === 'expired') return '#f59e0b'; return '#3b82f6'; }; return ( {item.secretCode} Coupon: {item.couponCode} {status === 'active' ? 'Active' : status === 'expired' ? 'Expired' : 'Redeemed'} Discount: {item.discountPercent ? `${item.discountPercent}% off` : item.flatDiscount ? `₹${item.flatDiscount} off` : 'N/A'} Min Order: {item.minOrder ? `₹${item.minOrder}` : 'None'} Max: {item.maxValue ? `₹${item.maxValue}` : 'None'} Valid Till: {item.validTill ? new Date(item.validTill).toLocaleDateString() : 'No expiry'} {item.isRedeemed && item.redeemedUser && ( Redeemed by: {item.redeemedUser.name || 'Unknown'} ({item.redeemedUser.mobile}) Redeemed on: {item.redeemedAt ? new Date(item.redeemedAt).toLocaleDateString() : 'N/A'} )} Created by: {item.creator?.name || 'Unknown'} ); }; const GeneralTab = () => { const router = useRouter(); const [refreshing, setRefreshing] = useState(false); const [searchQuery, setSearchQuery] = useState(''); const [statusFilters, setStatusFilters] = useState([]); const { data, fetchNextPage, hasNextPage, isLoading, isFetchingNextPage, error, refetch, } = trpc.admin.coupon.getAll.useInfiniteQuery( { limit: 20, search: searchQuery }, { getNextPageParam: (lastPage) => lastPage.nextCursor } ); const coupons = useMemo(() => data?.pages.flatMap(page => page.coupons) || [], [data]); const getCouponStatus = (coupon: any) => { if (coupon.isInvalidated) return 'inactive'; if (coupon.validTill && new Date(coupon.validTill) <= new Date()) return 'expired'; return 'active'; }; const filteredCoupons = useMemo(() => { let filtered = coupons; if (statusFilters.length > 0) { filtered = filtered.filter(coupon => statusFilters.includes(getCouponStatus(coupon))); } return filtered; }, [coupons, statusFilters]); const handleRefresh = useCallback(async () => { setRefreshing(true); await refetch(); setRefreshing(false); }, [refetch]); useManualRefresh(() => refetch()); useMarkDataFetchers(() => { refetch(); }); const deleteCoupon = trpc.admin.coupon.delete.useMutation(); const handleDeleteCoupon = (id: number) => { Alert.alert('Delete Coupon', 'Are you sure you want to delete this coupon?', [ { text: 'Cancel', style: 'cancel' }, { text: 'Delete', style: 'destructive', onPress: () => { deleteCoupon.mutate({ id }, { onSuccess: () => { Alert.alert('Success', 'Coupon deleted successfully'); refetch(); }, onError: (error: any) => { Alert.alert('Error', error.message || 'Failed to delete coupon'); }, }); }, }, ]); }; if (isLoading) { return ( Loading Coupons... ); } if (error) { return ( Failed to load coupons refetch()} style={tw`bg-red-500`}> Retry ); } return ( setStatusFilters(value as string[])} multiple={true} triggerComponent={({ onPress }) => ( )} /> item.id.toString()} renderItem={({ item }) => } refreshControl={ } contentContainerStyle={tw`px-4 pb-4`} onEndReached={() => { if (hasNextPage && !isFetchingNextPage) { fetchNextPage(); } }} onEndReachedThreshold={0.5} ListEmptyComponent={ No Coupons Found {searchQuery ? ( setSearchQuery('')} style={tw`bg-gray-500 mt-4`}> Clear Search ) : null} } /> ); }; const ReservedTab = () => { const router = useRouter(); const [refreshing, setRefreshing] = useState(false); const [searchQuery, setSearchQuery] = useState(''); const [statusFilters, setStatusFilters] = useState([]); const { data, isLoading, isFetchingNextPage, hasNextPage, fetchNextPage, error, refetch } = trpc.admin.coupon.getReservedCoupons.useInfiniteQuery( { limit: 20, search: searchQuery }, { getNextPageParam: (lastPage) => lastPage.nextCursor } ); const coupons = useMemo(() => data?.pages.flatMap((page) => page.coupons) || [], [data]); const getStatus = (coupon: any) => { if (coupon.isRedeemed) return 'redeemed'; if (coupon.validTill && new Date(coupon.validTill) <= new Date()) return 'expired'; return 'active'; }; const filteredCoupons = useMemo(() => { let filtered = coupons; if (statusFilters.length > 0) { filtered = filtered.filter(coupon => statusFilters.includes(getStatus(coupon))); } return filtered; }, [coupons, statusFilters]); const handleRefresh = useCallback(async () => { setRefreshing(true); await refetch(); setRefreshing(false); }, [refetch]); useManualRefresh(() => refetch()); useMarkDataFetchers(() => { refetch(); }); if (isLoading) { return ( Loading Reserved Coupons... ); } if (error) { return ( Failed to load reserved coupons refetch()} style={tw`bg-red-500`}> Retry ); } return ( setStatusFilters(value as string[])} multiple={true} triggerComponent={({ onPress }) => ( )} /> item.id.toString()} renderItem={({ item }) => } refreshControl={ } contentContainerStyle={tw`px-4 pb-4`} onEndReached={() => { if (hasNextPage && !isFetchingNextPage) { fetchNextPage(); } }} onEndReachedThreshold={0.5} ListEmptyComponent={ No Reserved Coupons Found {searchQuery ? ( setSearchQuery('')} style={tw`bg-gray-500 mt-4`}> Clear Search ) : null} } /> ); }; export default function Coupons() { const router = useRouter(); const [index, setIndex] = useState(0); const routes = [ { key: 'general', title: 'General' }, { key: 'reserved', title: 'Reserved' }, ]; return ( ( )} /> router.push('/(drawer)/create-coupon')} activeOpacity={0.95} style={{ position: 'absolute', bottom: 32, right: 24, zIndex: 100 }} > ); }