@bettoredge/calcutta 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/package.json +46 -0
  2. package/src/components/CalcuttaAuction.tsx +453 -0
  3. package/src/components/CalcuttaAuctionItem.tsx +292 -0
  4. package/src/components/CalcuttaBidInput.tsx +214 -0
  5. package/src/components/CalcuttaCard.tsx +131 -0
  6. package/src/components/CalcuttaDetail.tsx +377 -0
  7. package/src/components/CalcuttaEscrow.tsx +464 -0
  8. package/src/components/CalcuttaItemResults.tsx +207 -0
  9. package/src/components/CalcuttaLeaderboard.tsx +179 -0
  10. package/src/components/CalcuttaPayoutPreview.tsx +194 -0
  11. package/src/components/CalcuttaRoundResults.tsx +250 -0
  12. package/src/components/CalcuttaTemplateSelector.tsx +124 -0
  13. package/src/components/sealed/AuctionResultsModal.tsx +165 -0
  14. package/src/components/sealed/EscrowBottomSheet.tsx +185 -0
  15. package/src/components/sealed/SealedBidAuction.tsx +541 -0
  16. package/src/components/sealed/SealedBidHeader.tsx +116 -0
  17. package/src/components/sealed/SealedBidInfoTab.tsx +247 -0
  18. package/src/components/sealed/SealedBidItemCard.tsx +385 -0
  19. package/src/components/sealed/SealedBidItemsTab.tsx +235 -0
  20. package/src/components/sealed/SealedBidMyBidsTab.tsx +512 -0
  21. package/src/components/sealed/SealedBidPlayersTab.tsx +220 -0
  22. package/src/components/sealed/SealedBidStatusBar.tsx +415 -0
  23. package/src/components/sealed/SealedBidTabBar.tsx +172 -0
  24. package/src/helpers/formatting.ts +56 -0
  25. package/src/helpers/lifecycleState.ts +71 -0
  26. package/src/helpers/payout.ts +39 -0
  27. package/src/helpers/validation.ts +64 -0
  28. package/src/hooks/useCalcuttaAuction.ts +164 -0
  29. package/src/hooks/useCalcuttaBid.ts +43 -0
  30. package/src/hooks/useCalcuttaCompetition.ts +63 -0
  31. package/src/hooks/useCalcuttaEscrow.ts +52 -0
  32. package/src/hooks/useCalcuttaItemImages.ts +79 -0
  33. package/src/hooks/useCalcuttaPlayers.ts +46 -0
  34. package/src/hooks/useCalcuttaResults.ts +58 -0
  35. package/src/hooks/useCalcuttaSocket.ts +131 -0
  36. package/src/hooks/useCalcuttaTemplates.ts +36 -0
  37. package/src/index.ts +74 -0
  38. package/src/types.ts +31 -0
@@ -0,0 +1,235 @@
1
+ import React, { useState, useMemo, useCallback } from 'react';
2
+ import { StyleSheet, FlatList, TouchableOpacity, RefreshControl, TextInput } from 'react-native';
3
+ import { View, Text, useTheme } from '@bettoredge/styles';
4
+ import { Ionicons } from '@expo/vector-icons';
5
+ import type { CalcuttaAuctionItemProps, CalcuttaBidProps } from '@bettoredge/types';
6
+ import type { ItemImageMap } from '../../hooks/useCalcuttaItemImages';
7
+ import type { CalcuttaLifecycleState } from '../../helpers/lifecycleState';
8
+ import { SealedBidItemCard } from './SealedBidItemCard';
9
+
10
+ type ActiveFilter = 'all' | 'not_bid' | 'my_bids';
11
+ type BrowseFilter = 'all';
12
+
13
+ interface SealedBidItemsTabProps {
14
+ items: CalcuttaAuctionItemProps[];
15
+ my_bids: CalcuttaBidProps[];
16
+ min_bid: number;
17
+ bid_increment: number;
18
+ escrow_balance: number;
19
+ market_type: string;
20
+ player_id?: string;
21
+ lifecycleState: CalcuttaLifecycleState;
22
+ itemImages?: ItemImageMap;
23
+ onPlaceBid: (calcutta_auction_item_id: string, amount: number) => void;
24
+ bidLoading: boolean;
25
+ onRefresh: () => void;
26
+ refreshing: boolean;
27
+ disabled?: boolean;
28
+ listHeader?: React.ReactNode;
29
+ }
30
+
31
+ const ACTIVE_FILTERS: { key: ActiveFilter; label: string }[] = [
32
+ { key: 'all', label: 'All' },
33
+ { key: 'not_bid', label: 'Not Bid' },
34
+ { key: 'my_bids', label: 'My Bids' },
35
+ ];
36
+
37
+ const BROWSE_FILTERS: { key: BrowseFilter; label: string }[] = [
38
+ { key: 'all', label: 'All' },
39
+ ];
40
+
41
+ export const SealedBidItemsTab: React.FC<SealedBidItemsTabProps> = ({
42
+ items,
43
+ my_bids,
44
+ min_bid,
45
+ bid_increment,
46
+ escrow_balance,
47
+ market_type,
48
+ player_id,
49
+ lifecycleState,
50
+ itemImages,
51
+ onPlaceBid,
52
+ bidLoading,
53
+ onRefresh,
54
+ refreshing,
55
+ disabled,
56
+ listHeader,
57
+ }) => {
58
+ const { theme } = useTheme();
59
+ const [activeFilter, setActiveFilter] = useState<ActiveFilter>('all');
60
+ const [searchQuery, setSearchQuery] = useState('');
61
+
62
+ const isBrowseMode = lifecycleState === 'pending' || lifecycleState === 'scheduled';
63
+
64
+ const myBidItemIds = useMemo(
65
+ () => new Set(my_bids.filter(b => b.bid_status === 'active' || b.bid_status === 'won').map(b => b.calcutta_auction_item_id)),
66
+ [my_bids],
67
+ );
68
+
69
+ const filteredItems = useMemo(() => {
70
+ let result = items;
71
+ if (!isBrowseMode) {
72
+ switch (activeFilter) {
73
+ case 'not_bid':
74
+ result = result.filter(i => !myBidItemIds.has(i.calcutta_auction_item_id));
75
+ break;
76
+ case 'my_bids':
77
+ result = result.filter(i => myBidItemIds.has(i.calcutta_auction_item_id));
78
+ break;
79
+ }
80
+ }
81
+ if (searchQuery.trim()) {
82
+ const q = searchQuery.trim().toLowerCase();
83
+ result = result.filter(i => i.item_name.toLowerCase().includes(q));
84
+ }
85
+ return result;
86
+ }, [items, activeFilter, isBrowseMode, myBidItemIds, searchQuery]);
87
+
88
+ const renderItem = useCallback(({ item }: { item: CalcuttaAuctionItemProps }) => {
89
+ const myBid = my_bids.find(
90
+ b => b.calcutta_auction_item_id === item.calcutta_auction_item_id
91
+ && (b.bid_status === 'active' || b.bid_status === 'won')
92
+ );
93
+ return (
94
+ <SealedBidItemCard
95
+ item={item}
96
+ my_bid={myBid}
97
+ min_bid={min_bid}
98
+ bid_increment={bid_increment}
99
+ escrow_balance={escrow_balance}
100
+ market_type={market_type}
101
+ player_id={player_id}
102
+ lifecycleState={lifecycleState}
103
+ itemImage={itemImages?.[item.item_id]}
104
+ onPlaceBid={(amount) => onPlaceBid(item.calcutta_auction_item_id, amount)}
105
+ bidLoading={bidLoading}
106
+ disabled={disabled}
107
+ />
108
+ );
109
+ }, [my_bids, min_bid, bid_increment, escrow_balance, market_type, player_id, lifecycleState, itemImages, onPlaceBid, bidLoading, disabled]);
110
+
111
+ const filters = isBrowseMode ? BROWSE_FILTERS : ACTIVE_FILTERS;
112
+ const currentFilter = isBrowseMode ? 'all' : activeFilter;
113
+ const setFilter = (f: string) => setActiveFilter(f as ActiveFilter);
114
+
115
+ const headerComponent = (
116
+ <View variant="transparent">
117
+ <>{listHeader}</>
118
+
119
+ {/* Search */}
120
+ <View variant="transparent" style={[styles.searchRow, { borderColor: theme.colors.border.subtle }]}>
121
+ <Ionicons name="search-outline" size={16} color={theme.colors.text.tertiary} />
122
+ <TextInput
123
+ style={[styles.searchInput, { color: theme.colors.text.primary }]}
124
+ value={searchQuery}
125
+ onChangeText={setSearchQuery}
126
+ placeholder="Search items..."
127
+ placeholderTextColor={theme.colors.text.tertiary}
128
+ autoCorrect={false}
129
+ />
130
+ {searchQuery.length > 0 && (
131
+ <TouchableOpacity onPress={() => setSearchQuery('')} hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}>
132
+ <Ionicons name="close-circle" size={16} color={theme.colors.text.tertiary} />
133
+ </TouchableOpacity>
134
+ )}
135
+ </View>
136
+
137
+ {/* Filter chips — hide single-filter row in browse mode */}
138
+ {!isBrowseMode && (
139
+ <View variant="transparent" style={styles.filterRow}>
140
+ {filters.map(f => {
141
+ const active = currentFilter === f.key;
142
+ return (
143
+ <TouchableOpacity
144
+ key={f.key}
145
+ style={[
146
+ styles.chip,
147
+ {
148
+ backgroundColor: active ? theme.colors.primary.default : theme.colors.surface.elevated,
149
+ borderColor: active ? theme.colors.primary.default : theme.colors.border.subtle,
150
+ },
151
+ ]}
152
+ onPress={() => setFilter(f.key)}
153
+ activeOpacity={0.7}
154
+ >
155
+ <Text
156
+ variant="caption"
157
+ bold={active}
158
+ style={{ color: active ? '#FFFFFF' : theme.colors.text.secondary, fontSize: 11 }}
159
+ >
160
+ {f.label}
161
+ </Text>
162
+ </TouchableOpacity>
163
+ );
164
+ })}
165
+ </View>
166
+ )}
167
+ </View>
168
+ );
169
+
170
+ return (
171
+ <View variant="transparent" style={styles.container}>
172
+ <FlatList
173
+ data={filteredItems}
174
+ keyExtractor={item => item.calcutta_auction_item_id}
175
+ renderItem={renderItem}
176
+ ListHeaderComponent={headerComponent}
177
+ contentContainerStyle={styles.list}
178
+ stickyHeaderIndices={[]}
179
+ refreshControl={
180
+ <RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
181
+ }
182
+ ListEmptyComponent={
183
+ <Text variant="caption" color="tertiary" style={styles.empty}>
184
+ {isBrowseMode
185
+ ? 'No items available'
186
+ : activeFilter === 'my_bids' ? 'No bids placed yet' : 'No items available'}
187
+ </Text>
188
+ }
189
+ />
190
+ </View>
191
+ );
192
+ };
193
+
194
+ const styles = StyleSheet.create({
195
+ container: {
196
+ flex: 1,
197
+ },
198
+ searchRow: {
199
+ flexDirection: 'row',
200
+ alignItems: 'center',
201
+ marginHorizontal: 12,
202
+ marginTop: 10,
203
+ paddingHorizontal: 12,
204
+ height: 44,
205
+ borderRadius: 10,
206
+ borderWidth: 1,
207
+ },
208
+ searchInput: {
209
+ flex: 1,
210
+ marginLeft: 8,
211
+ fontSize: 15,
212
+ height: 44,
213
+ padding: 0,
214
+ },
215
+ filterRow: {
216
+ flexDirection: 'row',
217
+ paddingHorizontal: 12,
218
+ paddingVertical: 10,
219
+ gap: 8,
220
+ },
221
+ chip: {
222
+ paddingHorizontal: 12,
223
+ paddingVertical: 6,
224
+ borderRadius: 14,
225
+ borderWidth: 1,
226
+ },
227
+ list: {
228
+ paddingBottom: 40,
229
+ paddingTop: 8,
230
+ },
231
+ empty: {
232
+ textAlign: 'center',
233
+ padding: 40,
234
+ },
235
+ });