146 lines
5.7 KiB
TypeScript
146 lines
5.7 KiB
TypeScript
import React from 'react'
|
|
import { View, TouchableOpacity, Alert, Image, FlatList } from 'react-native'
|
|
import { MaterialCommunityIcons } from '@expo/vector-icons'
|
|
import { MyText, tw, MyTouchableOpacity, useManualRefresh, useMarkDataFetchers } from 'common-ui'
|
|
import { trpc } from '@/src/trpc-client'
|
|
import { useRouter } from 'expo-router'
|
|
import dayjs from 'dayjs'
|
|
import { LinearGradient } from 'expo-linear-gradient'
|
|
|
|
export default function OffersList() {
|
|
const router = useRouter()
|
|
const { data, isLoading, refetch } = trpc.admin.offers.getAll.useQuery()
|
|
const toggleSuspend = trpc.admin.offers.toggleSuspend.useMutation({
|
|
onSuccess: () => refetch(),
|
|
})
|
|
const deleteOffer = trpc.admin.offers.delete.useMutation({
|
|
onSuccess: () => refetch(),
|
|
})
|
|
|
|
const offers = data?.offers || []
|
|
|
|
useManualRefresh(() => refetch())
|
|
useMarkDataFetchers(() => refetch())
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<View style={tw`flex-1 justify-center items-center bg-white`}>
|
|
<MyText>Loading offers...</MyText>
|
|
</View>
|
|
)
|
|
}
|
|
|
|
const handleToggleSuspend = (id: number) => {
|
|
toggleSuspend.mutate({ id })
|
|
}
|
|
|
|
const handleDelete = (id: number) => {
|
|
Alert.alert('Delete Offer', 'Are you sure you want to delete this offer?', [
|
|
{ text: 'Cancel', style: 'cancel' },
|
|
{ text: 'Delete', style: 'destructive', onPress: () => deleteOffer.mutate({ id }) },
|
|
])
|
|
}
|
|
|
|
return (
|
|
<View style={tw`flex-1 bg-white relative`}>
|
|
<FlatList
|
|
data={offers}
|
|
keyExtractor={(item: any) => item.id.toString()}
|
|
contentContainerStyle={tw`p-4`}
|
|
ListEmptyComponent={
|
|
<View style={tw`flex-1 justify-center items-center py-20`}>
|
|
<MaterialCommunityIcons name="sale" size={64} color="#E5E7EB" />
|
|
<MyText style={tw`text-gray-500 mt-4 text-lg`}>No offers yet</MyText>
|
|
<MyText style={tw`text-gray-400 text-sm mt-1`}>Create your first offer</MyText>
|
|
</View>
|
|
}
|
|
renderItem={({ item: offer }: { item: any }) => (
|
|
<TouchableOpacity
|
|
onPress={() => router.push(`/offers/edit/${offer.id}` as any)}
|
|
style={tw`bg-white border border-gray-200 rounded-2xl mb-4 overflow-hidden shadow-sm`}
|
|
>
|
|
<View style={tw`h-40 bg-gray-100`}>
|
|
{offer.images?.[0] ? (
|
|
<Image source={{ uri: offer.images[0] }} style={tw`w-full h-full`} resizeMode="cover" />
|
|
) : (
|
|
<View style={tw`flex-1 justify-center items-center`}>
|
|
<MaterialCommunityIcons name="image-off" size={48} color="#D1D5DB" />
|
|
</View>
|
|
)}
|
|
{offer.isSuspended && (
|
|
<View style={tw`absolute top-3 left-3 bg-red-500 px-3 py-1 rounded-full`}>
|
|
<MyText style={tw`text-white text-xs font-bold`}>Suspended</MyText>
|
|
</View>
|
|
)}
|
|
</View>
|
|
|
|
<View style={tw`p-4`}>
|
|
<MyText style={tw`text-lg font-bold text-gray-900`} numberOfLines={1}>
|
|
{offer.name}
|
|
</MyText>
|
|
|
|
{offer.shortDescription && (
|
|
<MyText style={tw`text-gray-500 text-sm mt-1`} numberOfLines={2}>
|
|
{offer.shortDescription}
|
|
</MyText>
|
|
)}
|
|
|
|
<View style={tw`flex-row items-center mt-3`}>
|
|
<MyText style={tw`text-xl font-bold text-gray-900`}>₹{offer.price}</MyText>
|
|
{offer.marketPrice && Number(offer.marketPrice) > Number(offer.price) && (
|
|
<MyText style={tw`text-sm text-gray-400 line-through ml-2`}>
|
|
₹{offer.marketPrice}
|
|
</MyText>
|
|
)}
|
|
</View>
|
|
|
|
<View style={tw`flex-row items-center mt-2`}>
|
|
<MaterialCommunityIcons name="package-variant" size={16} color="#9CA3AF" />
|
|
<MyText style={tw`text-gray-500 text-sm ml-1`}>
|
|
{offer.items?.length || 0} product{(offer.items?.length || 0) !== 1 ? 's' : ''}
|
|
</MyText>
|
|
</View>
|
|
|
|
<MyText style={tw`text-gray-400 text-xs mt-2`}>
|
|
Added {dayjs(offer.addedOn).format('DD MMM, YYYY')}
|
|
</MyText>
|
|
</View>
|
|
|
|
<View style={tw`flex-row border-t border-gray-100`}>
|
|
<TouchableOpacity
|
|
onPress={() => handleToggleSuspend(offer.id)}
|
|
style={tw`flex-1 py-3 items-center ${offer.isSuspended ? 'bg-green-50' : 'bg-red-50'}`}
|
|
>
|
|
<MyText style={tw`text-sm font-bold ${offer.isSuspended ? 'text-green-600' : 'text-red-600'}`}>
|
|
{offer.isSuspended ? 'Activate' : 'Suspend'}
|
|
</MyText>
|
|
</TouchableOpacity>
|
|
<TouchableOpacity
|
|
onPress={() => handleDelete(offer.id)}
|
|
style={tw`flex-1 py-3 items-center bg-red-50 border-l border-gray-100`}
|
|
>
|
|
<MyText style={tw`text-sm font-bold text-red-600`}>Delete</MyText>
|
|
</TouchableOpacity>
|
|
</View>
|
|
</TouchableOpacity>
|
|
)}
|
|
/>
|
|
|
|
<MyTouchableOpacity
|
|
testID="add-offer-fab"
|
|
accessibilityLabel="add-offer-fab"
|
|
onPress={() => router.push('/offers/add' as any)}
|
|
style={{ position: 'absolute', bottom: 32, right: 24, zIndex: 100 }}
|
|
>
|
|
<LinearGradient
|
|
colors={['#F83758', '#E91E63']}
|
|
start={{ x: 0, y: 0 }}
|
|
end={{ x: 1, y: 1 }}
|
|
style={tw`w-16 h-16 rounded-[24px] items-center justify-center shadow-lg`}
|
|
>
|
|
<MaterialCommunityIcons name="plus" size={32} color="white" />
|
|
</LinearGradient>
|
|
</MyTouchableOpacity>
|
|
</View>
|
|
)
|
|
}
|