@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.
- package/package.json +46 -0
- package/src/components/CalcuttaAuction.tsx +453 -0
- package/src/components/CalcuttaAuctionItem.tsx +292 -0
- package/src/components/CalcuttaBidInput.tsx +214 -0
- package/src/components/CalcuttaCard.tsx +131 -0
- package/src/components/CalcuttaDetail.tsx +377 -0
- package/src/components/CalcuttaEscrow.tsx +464 -0
- package/src/components/CalcuttaItemResults.tsx +207 -0
- package/src/components/CalcuttaLeaderboard.tsx +179 -0
- package/src/components/CalcuttaPayoutPreview.tsx +194 -0
- package/src/components/CalcuttaRoundResults.tsx +250 -0
- package/src/components/CalcuttaTemplateSelector.tsx +124 -0
- package/src/components/sealed/AuctionResultsModal.tsx +165 -0
- package/src/components/sealed/EscrowBottomSheet.tsx +185 -0
- package/src/components/sealed/SealedBidAuction.tsx +541 -0
- package/src/components/sealed/SealedBidHeader.tsx +116 -0
- package/src/components/sealed/SealedBidInfoTab.tsx +247 -0
- package/src/components/sealed/SealedBidItemCard.tsx +385 -0
- package/src/components/sealed/SealedBidItemsTab.tsx +235 -0
- package/src/components/sealed/SealedBidMyBidsTab.tsx +512 -0
- package/src/components/sealed/SealedBidPlayersTab.tsx +220 -0
- package/src/components/sealed/SealedBidStatusBar.tsx +415 -0
- package/src/components/sealed/SealedBidTabBar.tsx +172 -0
- package/src/helpers/formatting.ts +56 -0
- package/src/helpers/lifecycleState.ts +71 -0
- package/src/helpers/payout.ts +39 -0
- package/src/helpers/validation.ts +64 -0
- package/src/hooks/useCalcuttaAuction.ts +164 -0
- package/src/hooks/useCalcuttaBid.ts +43 -0
- package/src/hooks/useCalcuttaCompetition.ts +63 -0
- package/src/hooks/useCalcuttaEscrow.ts +52 -0
- package/src/hooks/useCalcuttaItemImages.ts +79 -0
- package/src/hooks/useCalcuttaPlayers.ts +46 -0
- package/src/hooks/useCalcuttaResults.ts +58 -0
- package/src/hooks/useCalcuttaSocket.ts +131 -0
- package/src/hooks/useCalcuttaTemplates.ts +36 -0
- package/src/index.ts +74 -0
- package/src/types.ts +31 -0
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
import React, { useMemo, useState } from 'react';
|
|
2
|
+
import { StyleSheet, TouchableOpacity, FlatList, Image } from 'react-native';
|
|
3
|
+
import { View, Text, useTheme } from '@bettoredge/styles';
|
|
4
|
+
import { Ionicons } from '@expo/vector-icons';
|
|
5
|
+
import type { CalcuttaAuctionItemProps, CalcuttaBidProps, CalcuttaEscrowProps, CalcuttaItemResultProps, CalcuttaRoundProps } from '@bettoredge/types';
|
|
6
|
+
import type { ItemImageMap } from '../../hooks/useCalcuttaItemImages';
|
|
7
|
+
import type { CalcuttaLifecycleState } from '../../helpers/lifecycleState';
|
|
8
|
+
import { formatCurrency, getBidStatusColor, getStatusLabel } from '../../helpers/formatting';
|
|
9
|
+
|
|
10
|
+
interface SealedBidMyBidsTabProps {
|
|
11
|
+
items: CalcuttaAuctionItemProps[];
|
|
12
|
+
my_bids: CalcuttaBidProps[];
|
|
13
|
+
escrow?: CalcuttaEscrowProps;
|
|
14
|
+
market_type: string;
|
|
15
|
+
player_id?: string;
|
|
16
|
+
lifecycleState: CalcuttaLifecycleState;
|
|
17
|
+
itemImages?: ItemImageMap;
|
|
18
|
+
item_results?: CalcuttaItemResultProps[];
|
|
19
|
+
rounds?: CalcuttaRoundProps[];
|
|
20
|
+
onCancelBid: (calcutta_bid_id: string) => void;
|
|
21
|
+
onUpdateBid: (calcutta_auction_item_id: string) => void;
|
|
22
|
+
cancelLoading: boolean;
|
|
23
|
+
auctionExpired?: boolean;
|
|
24
|
+
listHeader?: React.ReactNode;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface BidRow {
|
|
28
|
+
bid: CalcuttaBidProps;
|
|
29
|
+
item?: CalcuttaAuctionItemProps;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const SealedBidMyBidsTab: React.FC<SealedBidMyBidsTabProps> = ({
|
|
33
|
+
items,
|
|
34
|
+
my_bids,
|
|
35
|
+
escrow,
|
|
36
|
+
market_type,
|
|
37
|
+
player_id,
|
|
38
|
+
lifecycleState,
|
|
39
|
+
itemImages,
|
|
40
|
+
item_results = [],
|
|
41
|
+
rounds = [],
|
|
42
|
+
onCancelBid,
|
|
43
|
+
onUpdateBid,
|
|
44
|
+
cancelLoading,
|
|
45
|
+
auctionExpired,
|
|
46
|
+
listHeader,
|
|
47
|
+
}) => {
|
|
48
|
+
const { theme } = useTheme();
|
|
49
|
+
const [expandedItemId, setExpandedItemId] = useState<string | null>(null);
|
|
50
|
+
|
|
51
|
+
const isPostAuction = lifecycleState === 'tournament' || lifecycleState === 'completed';
|
|
52
|
+
const isPreAuction = lifecycleState === 'pending' || lifecycleState === 'scheduled';
|
|
53
|
+
|
|
54
|
+
// All items I won at auction — includes sold (active) AND eliminated
|
|
55
|
+
const myItems = useMemo(
|
|
56
|
+
() => items.filter(i => i.winning_player_id === player_id),
|
|
57
|
+
[items, player_id],
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
// For backward compat / badge counts — only active (non-eliminated) items
|
|
61
|
+
const wonItems = useMemo(
|
|
62
|
+
() => myItems.filter(i => i.status === 'sold'),
|
|
63
|
+
[myItems],
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const totalSpent = useMemo(
|
|
67
|
+
() => myItems.reduce((s, i) => s + Number(i.winning_bid), 0),
|
|
68
|
+
[myItems],
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const activeBids = useMemo(
|
|
72
|
+
() => my_bids.filter(b => b.bid_status === 'active'),
|
|
73
|
+
[my_bids],
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const rows: BidRow[] = useMemo(
|
|
77
|
+
() => activeBids.map(bid => ({
|
|
78
|
+
bid,
|
|
79
|
+
item: items.find(i => i.calcutta_auction_item_id === bid.calcutta_auction_item_id),
|
|
80
|
+
})),
|
|
81
|
+
[activeBids, items],
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const totalCommitted = useMemo(
|
|
85
|
+
() => activeBids.reduce((sum, b) => sum + Number(b.bid_amount), 0),
|
|
86
|
+
[activeBids],
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const budgetRemaining = escrow ? Number(escrow.escrow_balance) : 0;
|
|
90
|
+
|
|
91
|
+
// Build per-item result map for tournament/completed
|
|
92
|
+
const itemResultMap = useMemo(() => {
|
|
93
|
+
const map: Record<string, CalcuttaItemResultProps[]> = {};
|
|
94
|
+
for (const r of item_results) {
|
|
95
|
+
const key = r.calcutta_auction_item_id;
|
|
96
|
+
if (!map[key]) map[key] = [];
|
|
97
|
+
map[key].push(r);
|
|
98
|
+
}
|
|
99
|
+
return map;
|
|
100
|
+
}, [item_results]);
|
|
101
|
+
|
|
102
|
+
// Round name lookup
|
|
103
|
+
const roundNameMap = useMemo(() => {
|
|
104
|
+
const map: Record<number, string> = {};
|
|
105
|
+
for (const r of rounds) map[r.round_number] = r.round_name;
|
|
106
|
+
return map;
|
|
107
|
+
}, [rounds]);
|
|
108
|
+
|
|
109
|
+
// ============================================
|
|
110
|
+
// PENDING / SCHEDULED — Empty state
|
|
111
|
+
// ============================================
|
|
112
|
+
if (isPreAuction) {
|
|
113
|
+
return (
|
|
114
|
+
<View variant="transparent" style={styles.container}>
|
|
115
|
+
<>{listHeader}</>
|
|
116
|
+
<View variant="transparent" style={styles.emptyContainer}>
|
|
117
|
+
<Ionicons name="time-outline" size={40} color={theme.colors.text.tertiary} />
|
|
118
|
+
<Text variant="body" color="tertiary" style={{ marginTop: 12 }}>Bidding hasn't started yet</Text>
|
|
119
|
+
<Text variant="caption" color="tertiary" style={{ marginTop: 4, textAlign: 'center' }}>
|
|
120
|
+
You'll be able to place bids once the auction begins
|
|
121
|
+
</Text>
|
|
122
|
+
</View>
|
|
123
|
+
</View>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ============================================
|
|
128
|
+
// TOURNAMENT / COMPLETED — "My Items" ownership view with round results
|
|
129
|
+
// ============================================
|
|
130
|
+
if (isPostAuction) {
|
|
131
|
+
if (myItems.length === 0) {
|
|
132
|
+
return (
|
|
133
|
+
<View variant="transparent" style={styles.container}>
|
|
134
|
+
<>{listHeader}</>
|
|
135
|
+
<View variant="transparent" style={styles.emptyContainer}>
|
|
136
|
+
<Ionicons name="ribbon-outline" size={40} color={theme.colors.text.tertiary} />
|
|
137
|
+
<Text variant="body" color="tertiary" style={{ marginTop: 12 }}>No items won</Text>
|
|
138
|
+
<Text variant="caption" color="tertiary" style={{ marginTop: 4, textAlign: 'center' }}>
|
|
139
|
+
You didn't win any items in this auction
|
|
140
|
+
</Text>
|
|
141
|
+
</View>
|
|
142
|
+
</View>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const totalEarned = myItems.reduce((s, i) => {
|
|
147
|
+
const results = itemResultMap[i.calcutta_auction_item_id] || [];
|
|
148
|
+
return s + results.reduce((rs, r) => rs + Number(r.payout_earned || 0), 0);
|
|
149
|
+
}, 0);
|
|
150
|
+
const totalROI = totalEarned - totalSpent;
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<View variant="transparent" style={styles.container}>
|
|
154
|
+
<FlatList
|
|
155
|
+
data={myItems}
|
|
156
|
+
keyExtractor={i => i.calcutta_auction_item_id}
|
|
157
|
+
contentContainerStyle={styles.list}
|
|
158
|
+
ListHeaderComponent={
|
|
159
|
+
<View variant="transparent">
|
|
160
|
+
<>{listHeader}</>
|
|
161
|
+
<View variant="transparent" style={[styles.summary, {
|
|
162
|
+
backgroundColor: totalROI >= 0 ? theme.colors.status.successSubtle : theme.colors.status.error + '08',
|
|
163
|
+
borderColor: totalROI >= 0 ? theme.colors.status.success + '40' : theme.colors.status.error + '30',
|
|
164
|
+
}]}>
|
|
165
|
+
<View variant="transparent" style={styles.summaryItem}>
|
|
166
|
+
<Ionicons name="trophy" size={20} color="#F59E0B" />
|
|
167
|
+
<Text variant="caption" color="secondary" style={{ marginTop: 4 }}>Items</Text>
|
|
168
|
+
<Text variant="h3" bold>{myItems.length}</Text>
|
|
169
|
+
</View>
|
|
170
|
+
<View variant="transparent" style={[styles.summaryItem, { borderLeftWidth: 1, borderColor: theme.colors.border.subtle }]}>
|
|
171
|
+
<Ionicons name="arrow-down-circle-outline" size={20} color={theme.colors.text.tertiary} />
|
|
172
|
+
<Text variant="caption" color="secondary" style={{ marginTop: 4 }}>Spent</Text>
|
|
173
|
+
<Text variant="h3" bold>{formatCurrency(totalSpent, market_type)}</Text>
|
|
174
|
+
</View>
|
|
175
|
+
<View variant="transparent" style={[styles.summaryItem, { borderLeftWidth: 1, borderColor: theme.colors.border.subtle }]}>
|
|
176
|
+
<Ionicons name="arrow-up-circle-outline" size={20} color={theme.colors.status.success} />
|
|
177
|
+
<Text variant="caption" color="secondary" style={{ marginTop: 4 }}>Earned</Text>
|
|
178
|
+
<Text variant="h3" bold style={{ color: theme.colors.status.success }}>{formatCurrency(totalEarned, market_type)}</Text>
|
|
179
|
+
</View>
|
|
180
|
+
</View>
|
|
181
|
+
{/* ROI banner */}
|
|
182
|
+
<View variant="transparent" style={[styles.roiBanner, {
|
|
183
|
+
backgroundColor: totalROI >= 0 ? theme.colors.status.successSubtle : theme.colors.status.error + '10',
|
|
184
|
+
}]}>
|
|
185
|
+
<Text variant="caption" bold style={{ color: totalROI >= 0 ? theme.colors.status.success : theme.colors.status.error }}>
|
|
186
|
+
ROI: {totalROI >= 0 ? '+' : ''}{formatCurrency(totalROI, market_type)}
|
|
187
|
+
</Text>
|
|
188
|
+
</View>
|
|
189
|
+
<Text variant="caption" color="tertiary" style={styles.sectionLabel}>Your Items</Text>
|
|
190
|
+
</View>
|
|
191
|
+
}
|
|
192
|
+
renderItem={({ item }) => {
|
|
193
|
+
const results = itemResultMap[item.calcutta_auction_item_id] || [];
|
|
194
|
+
const totalPayout = results.reduce((s, r) => s + Number(r.payout_earned || 0), 0);
|
|
195
|
+
const itemROI = totalPayout - Number(item.winning_bid);
|
|
196
|
+
const isEliminated = item.status === 'eliminated';
|
|
197
|
+
const isExpanded = expandedItemId === item.calcutta_auction_item_id;
|
|
198
|
+
|
|
199
|
+
return (
|
|
200
|
+
<TouchableOpacity
|
|
201
|
+
activeOpacity={0.7}
|
|
202
|
+
onPress={() => setExpandedItemId(isExpanded ? null : item.calcutta_auction_item_id)}
|
|
203
|
+
style={[styles.ownedCard, {
|
|
204
|
+
backgroundColor: theme.colors.surface.elevated,
|
|
205
|
+
borderColor: isEliminated
|
|
206
|
+
? theme.colors.status.error + '30'
|
|
207
|
+
: itemROI > 0
|
|
208
|
+
? theme.colors.status.success + '40'
|
|
209
|
+
: theme.colors.border.subtle,
|
|
210
|
+
opacity: isEliminated ? 0.8 : 1,
|
|
211
|
+
}]}
|
|
212
|
+
>
|
|
213
|
+
<View variant="transparent" style={styles.ownedRow}>
|
|
214
|
+
<View variant="transparent" style={[styles.ownedIcon, {
|
|
215
|
+
backgroundColor: isEliminated ? theme.colors.status.error + '10' : theme.colors.status.successSubtle,
|
|
216
|
+
overflow: 'hidden',
|
|
217
|
+
}]}>
|
|
218
|
+
{(itemImages?.[item.item_id]?.url || item.item_image?.url) ? (
|
|
219
|
+
<Image source={{ uri: itemImages?.[item.item_id]?.url || item.item_image?.url }} style={styles.ownedItemImage} resizeMode="contain" />
|
|
220
|
+
) : isEliminated ? (
|
|
221
|
+
<Ionicons name="close-circle" size={18} color={theme.colors.status.error} />
|
|
222
|
+
) : (
|
|
223
|
+
<Ionicons name="trophy" size={18} color="#F59E0B" />
|
|
224
|
+
)}
|
|
225
|
+
</View>
|
|
226
|
+
<View variant="transparent" style={{ flex: 1 }}>
|
|
227
|
+
<Text variant="body" bold numberOfLines={1}>{item.item_name}</Text>
|
|
228
|
+
<View variant="transparent" style={{ flexDirection: 'row', alignItems: 'center', marginTop: 2 }}>
|
|
229
|
+
{isEliminated && (
|
|
230
|
+
<Text variant="caption" bold style={{ color: theme.colors.status.error, fontSize: 10, marginRight: 6 }}>ELIMINATED</Text>
|
|
231
|
+
)}
|
|
232
|
+
{item.seed != null && (
|
|
233
|
+
<Text variant="caption" color="tertiary">Seed #{item.seed}</Text>
|
|
234
|
+
)}
|
|
235
|
+
</View>
|
|
236
|
+
</View>
|
|
237
|
+
<View variant="transparent" style={{ alignItems: 'flex-end' }}>
|
|
238
|
+
<Text variant="caption" color="tertiary" style={{ fontSize: 10 }}>ROI</Text>
|
|
239
|
+
<Text variant="body" bold style={{
|
|
240
|
+
color: itemROI > 0
|
|
241
|
+
? theme.colors.status.success
|
|
242
|
+
: itemROI < 0
|
|
243
|
+
? theme.colors.status.error
|
|
244
|
+
: theme.colors.text.primary,
|
|
245
|
+
}}>
|
|
246
|
+
{itemROI >= 0 ? '+' : ''}{formatCurrency(itemROI, market_type)}
|
|
247
|
+
</Text>
|
|
248
|
+
</View>
|
|
249
|
+
<Ionicons
|
|
250
|
+
name={isExpanded ? 'chevron-up' : 'chevron-down'}
|
|
251
|
+
size={14}
|
|
252
|
+
color={theme.colors.text.tertiary}
|
|
253
|
+
style={{ marginLeft: 6 }}
|
|
254
|
+
/>
|
|
255
|
+
</View>
|
|
256
|
+
|
|
257
|
+
{/* Expanded: cost + earnings + round progression */}
|
|
258
|
+
{isExpanded && (
|
|
259
|
+
<View variant="transparent" style={[styles.resultsExpanded, { borderColor: theme.colors.border.subtle }]}>
|
|
260
|
+
<View variant="transparent" style={styles.resultRow}>
|
|
261
|
+
<Text variant="caption" color="secondary" style={{ flex: 1 }}>Paid at auction</Text>
|
|
262
|
+
<Text variant="caption" bold>{formatCurrency(item.winning_bid, market_type)}</Text>
|
|
263
|
+
</View>
|
|
264
|
+
{results
|
|
265
|
+
.sort((a, b) => (a.round_number ?? 0) - (b.round_number ?? 0))
|
|
266
|
+
.map(r => {
|
|
267
|
+
const isAdvanced = r.result === 'advanced' || r.result === 'won';
|
|
268
|
+
const rn = r.round_number ?? 0;
|
|
269
|
+
const roundName = roundNameMap[rn] || `Round ${rn}`;
|
|
270
|
+
return (
|
|
271
|
+
<View key={r.calcutta_item_result_id} variant="transparent" style={styles.resultRow}>
|
|
272
|
+
<Ionicons
|
|
273
|
+
name={isAdvanced ? 'checkmark-circle' : 'close-circle'}
|
|
274
|
+
size={14}
|
|
275
|
+
color={isAdvanced ? theme.colors.status.success : theme.colors.status.error}
|
|
276
|
+
/>
|
|
277
|
+
<Text variant="caption" style={{ flex: 1, marginLeft: 6 }}>{roundName}</Text>
|
|
278
|
+
<Text variant="caption" color="secondary">{getStatusLabel(r.result)}</Text>
|
|
279
|
+
{Number(r.payout_earned) > 0 && (
|
|
280
|
+
<Text variant="caption" bold style={{ color: theme.colors.status.success, marginLeft: 8 }}>
|
|
281
|
+
+{formatCurrency(r.payout_earned, market_type)}
|
|
282
|
+
</Text>
|
|
283
|
+
)}
|
|
284
|
+
</View>
|
|
285
|
+
);
|
|
286
|
+
})}
|
|
287
|
+
{results.length === 0 && (
|
|
288
|
+
<Text variant="caption" color="tertiary" style={{ textAlign: 'center', paddingVertical: 4 }}>
|
|
289
|
+
No round results yet
|
|
290
|
+
</Text>
|
|
291
|
+
)}
|
|
292
|
+
</View>
|
|
293
|
+
)}
|
|
294
|
+
</TouchableOpacity>
|
|
295
|
+
);
|
|
296
|
+
}}
|
|
297
|
+
/>
|
|
298
|
+
</View>
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// ============================================
|
|
303
|
+
// AUCTIONING — "My Bids" bidding view
|
|
304
|
+
// ============================================
|
|
305
|
+
if (rows.length === 0) {
|
|
306
|
+
return (
|
|
307
|
+
<View variant="transparent" style={styles.container}>
|
|
308
|
+
<>{listHeader}</>
|
|
309
|
+
<View variant="transparent" style={styles.emptyContainer}>
|
|
310
|
+
<Ionicons name="pricetag-outline" size={40} color={theme.colors.text.tertiary} />
|
|
311
|
+
<Text variant="body" color="tertiary" style={{ marginTop: 12 }}>No bids yet</Text>
|
|
312
|
+
<Text variant="caption" color="tertiary" style={{ marginTop: 4, textAlign: 'center' }}>
|
|
313
|
+
Browse the Items tab to start bidding
|
|
314
|
+
</Text>
|
|
315
|
+
</View>
|
|
316
|
+
</View>
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return (
|
|
321
|
+
<View variant="transparent" style={styles.container}>
|
|
322
|
+
<FlatList
|
|
323
|
+
data={rows}
|
|
324
|
+
keyExtractor={r => r.bid.calcutta_bid_id}
|
|
325
|
+
contentContainerStyle={styles.list}
|
|
326
|
+
ListHeaderComponent={
|
|
327
|
+
<View variant="transparent">
|
|
328
|
+
<>{listHeader}</>
|
|
329
|
+
<View variant="transparent" style={[styles.summary, { backgroundColor: theme.colors.surface.elevated, borderColor: theme.colors.border.subtle }]}>
|
|
330
|
+
<View variant="transparent" style={styles.summaryItem}>
|
|
331
|
+
<Text variant="caption" color="tertiary">Total Bids</Text>
|
|
332
|
+
<Text variant="body" bold>{activeBids.length}</Text>
|
|
333
|
+
</View>
|
|
334
|
+
<View variant="transparent" style={[styles.summaryItem, { borderLeftWidth: 1, borderColor: theme.colors.border.subtle }]}>
|
|
335
|
+
<Text variant="caption" color="tertiary">Committed</Text>
|
|
336
|
+
<Text variant="body" bold>{formatCurrency(totalCommitted, market_type)}</Text>
|
|
337
|
+
</View>
|
|
338
|
+
<View variant="transparent" style={[styles.summaryItem, { borderLeftWidth: 1, borderColor: theme.colors.border.subtle }]}>
|
|
339
|
+
<Text variant="caption" color="tertiary">Remaining</Text>
|
|
340
|
+
<Text variant="body" bold>{formatCurrency(budgetRemaining, market_type)}</Text>
|
|
341
|
+
</View>
|
|
342
|
+
</View>
|
|
343
|
+
</View>
|
|
344
|
+
}
|
|
345
|
+
renderItem={({ item: row }) => (
|
|
346
|
+
<View
|
|
347
|
+
variant="transparent"
|
|
348
|
+
style={[styles.bidCard, { backgroundColor: theme.colors.surface.elevated, borderColor: theme.colors.border.subtle }]}
|
|
349
|
+
>
|
|
350
|
+
<View variant="transparent" style={styles.bidRow}>
|
|
351
|
+
<View variant="transparent" style={{ flex: 1 }}>
|
|
352
|
+
<Text variant="body" bold numberOfLines={1}>
|
|
353
|
+
{row.item?.item_name ?? 'Unknown Item'}
|
|
354
|
+
</Text>
|
|
355
|
+
<View variant="transparent" style={{ flexDirection: 'row', alignItems: 'center', marginTop: 2 }}>
|
|
356
|
+
<Ionicons
|
|
357
|
+
name="ellipse"
|
|
358
|
+
size={8}
|
|
359
|
+
color={getBidStatusColor(row.bid.bid_status)}
|
|
360
|
+
/>
|
|
361
|
+
<Text variant="caption" style={{ color: getBidStatusColor(row.bid.bid_status), marginLeft: 4, fontSize: 11 }}>
|
|
362
|
+
{getStatusLabel(row.bid.bid_status)}
|
|
363
|
+
</Text>
|
|
364
|
+
</View>
|
|
365
|
+
</View>
|
|
366
|
+
<Text variant="h3" bold style={{ color: theme.colors.status.success }}>
|
|
367
|
+
{formatCurrency(row.bid.bid_amount, market_type)}
|
|
368
|
+
</Text>
|
|
369
|
+
</View>
|
|
370
|
+
|
|
371
|
+
{/* Actions — only when auctioning AND timer hasn't expired */}
|
|
372
|
+
{!auctionExpired ? (
|
|
373
|
+
<View variant="transparent" style={styles.actions}>
|
|
374
|
+
<TouchableOpacity
|
|
375
|
+
style={[styles.actionBtn, { borderColor: theme.colors.border.subtle }]}
|
|
376
|
+
onPress={() => onUpdateBid(row.bid.calcutta_auction_item_id)}
|
|
377
|
+
activeOpacity={0.7}
|
|
378
|
+
>
|
|
379
|
+
<Ionicons name="create-outline" size={14} color={theme.colors.primary.default} />
|
|
380
|
+
<Text variant="caption" bold style={{ color: theme.colors.primary.default, marginLeft: 4 }}>
|
|
381
|
+
Update
|
|
382
|
+
</Text>
|
|
383
|
+
</TouchableOpacity>
|
|
384
|
+
<TouchableOpacity
|
|
385
|
+
style={[styles.actionBtn, { borderColor: theme.colors.status.error + '40', marginLeft: 8 }]}
|
|
386
|
+
onPress={() => onCancelBid(row.bid.calcutta_bid_id)}
|
|
387
|
+
disabled={cancelLoading}
|
|
388
|
+
activeOpacity={0.7}
|
|
389
|
+
>
|
|
390
|
+
<Ionicons name="close-outline" size={14} color={theme.colors.status.error} />
|
|
391
|
+
<Text variant="caption" bold style={{ color: theme.colors.status.error, marginLeft: 4 }}>
|
|
392
|
+
Cancel
|
|
393
|
+
</Text>
|
|
394
|
+
</TouchableOpacity>
|
|
395
|
+
</View>
|
|
396
|
+
) : (
|
|
397
|
+
<View variant="transparent" style={[styles.expiredNotice, { backgroundColor: theme.colors.surface.base }]}>
|
|
398
|
+
<Ionicons name="time-outline" size={14} color={theme.colors.text.tertiary} />
|
|
399
|
+
<Text variant="caption" color="tertiary" style={{ marginLeft: 6 }}>
|
|
400
|
+
Auction ended — resolving results...
|
|
401
|
+
</Text>
|
|
402
|
+
</View>
|
|
403
|
+
)}
|
|
404
|
+
</View>
|
|
405
|
+
)}
|
|
406
|
+
/>
|
|
407
|
+
</View>
|
|
408
|
+
);
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
const styles = StyleSheet.create({
|
|
412
|
+
container: {
|
|
413
|
+
flex: 1,
|
|
414
|
+
},
|
|
415
|
+
emptyContainer: {
|
|
416
|
+
flex: 1,
|
|
417
|
+
alignItems: 'center',
|
|
418
|
+
justifyContent: 'center',
|
|
419
|
+
padding: 40,
|
|
420
|
+
},
|
|
421
|
+
summary: {
|
|
422
|
+
flexDirection: 'row',
|
|
423
|
+
margin: 12,
|
|
424
|
+
borderRadius: 10,
|
|
425
|
+
borderWidth: 1,
|
|
426
|
+
overflow: 'hidden',
|
|
427
|
+
},
|
|
428
|
+
summaryItem: {
|
|
429
|
+
flex: 1,
|
|
430
|
+
alignItems: 'center',
|
|
431
|
+
paddingVertical: 12,
|
|
432
|
+
},
|
|
433
|
+
roiBanner: {
|
|
434
|
+
marginHorizontal: 12,
|
|
435
|
+
marginTop: 8,
|
|
436
|
+
paddingVertical: 8,
|
|
437
|
+
borderRadius: 8,
|
|
438
|
+
alignItems: 'center',
|
|
439
|
+
},
|
|
440
|
+
sectionLabel: {
|
|
441
|
+
paddingHorizontal: 16,
|
|
442
|
+
paddingBottom: 8,
|
|
443
|
+
},
|
|
444
|
+
list: {
|
|
445
|
+
paddingHorizontal: 12,
|
|
446
|
+
paddingBottom: 40,
|
|
447
|
+
},
|
|
448
|
+
ownedCard: {
|
|
449
|
+
borderWidth: 1,
|
|
450
|
+
borderRadius: 10,
|
|
451
|
+
marginBottom: 8,
|
|
452
|
+
overflow: 'hidden',
|
|
453
|
+
},
|
|
454
|
+
ownedRow: {
|
|
455
|
+
flexDirection: 'row',
|
|
456
|
+
alignItems: 'center',
|
|
457
|
+
padding: 12,
|
|
458
|
+
},
|
|
459
|
+
ownedIcon: {
|
|
460
|
+
width: 40,
|
|
461
|
+
height: 40,
|
|
462
|
+
borderRadius: 20,
|
|
463
|
+
alignItems: 'center',
|
|
464
|
+
justifyContent: 'center',
|
|
465
|
+
marginRight: 12,
|
|
466
|
+
},
|
|
467
|
+
ownedItemImage: {
|
|
468
|
+
width: 40,
|
|
469
|
+
height: 40,
|
|
470
|
+
borderRadius: 20,
|
|
471
|
+
},
|
|
472
|
+
resultsExpanded: {
|
|
473
|
+
borderTopWidth: 1,
|
|
474
|
+
paddingHorizontal: 12,
|
|
475
|
+
paddingVertical: 6,
|
|
476
|
+
},
|
|
477
|
+
resultRow: {
|
|
478
|
+
flexDirection: 'row',
|
|
479
|
+
alignItems: 'center',
|
|
480
|
+
paddingVertical: 4,
|
|
481
|
+
},
|
|
482
|
+
bidCard: {
|
|
483
|
+
borderWidth: 1,
|
|
484
|
+
borderRadius: 10,
|
|
485
|
+
padding: 12,
|
|
486
|
+
marginBottom: 8,
|
|
487
|
+
},
|
|
488
|
+
bidRow: {
|
|
489
|
+
flexDirection: 'row',
|
|
490
|
+
alignItems: 'center',
|
|
491
|
+
},
|
|
492
|
+
actions: {
|
|
493
|
+
flexDirection: 'row',
|
|
494
|
+
marginTop: 10,
|
|
495
|
+
},
|
|
496
|
+
actionBtn: {
|
|
497
|
+
flexDirection: 'row',
|
|
498
|
+
alignItems: 'center',
|
|
499
|
+
paddingHorizontal: 12,
|
|
500
|
+
paddingVertical: 6,
|
|
501
|
+
borderRadius: 6,
|
|
502
|
+
borderWidth: 1,
|
|
503
|
+
},
|
|
504
|
+
expiredNotice: {
|
|
505
|
+
flexDirection: 'row',
|
|
506
|
+
alignItems: 'center',
|
|
507
|
+
marginTop: 10,
|
|
508
|
+
paddingHorizontal: 10,
|
|
509
|
+
paddingVertical: 8,
|
|
510
|
+
borderRadius: 6,
|
|
511
|
+
},
|
|
512
|
+
});
|