add total collection value calculation and loading state in Collection and Community views
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import { Search, Loader2, Trash2, CheckCircle, XCircle, RefreshCw, Plus, Minus, X } from 'lucide-react';
|
||||
import { Card } from '../types';
|
||||
import { getUserCollectionPaginated, getCardsByIds, addCardToCollection } from '../services/api';
|
||||
import { getUserCollectionPaginated, getCardsByIds, addCardToCollection, getCollectionTotalValue } from '../services/api';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { supabase } from '../lib/supabase';
|
||||
import ConfirmModal from './ConfirmModal';
|
||||
@@ -18,6 +18,8 @@ export default function Collection() {
|
||||
const [hasMore, setHasMore] = useState(false);
|
||||
const [offset, setOffset] = useState(0);
|
||||
const [totalCount, setTotalCount] = useState(0);
|
||||
const [totalCollectionValue, setTotalCollectionValue] = useState<number>(0);
|
||||
const [isLoadingTotalValue, setIsLoadingTotalValue] = useState(true);
|
||||
const [hoveredCard, setHoveredCard] = useState<Card | null>(null);
|
||||
const [selectedCard, setSelectedCard] = useState<{ card: Card; quantity: number } | null>(null);
|
||||
const [cardFaceIndex, setCardFaceIndex] = useState<Map<string, number>>(new Map());
|
||||
@@ -69,6 +71,30 @@ export default function Collection() {
|
||||
});
|
||||
};
|
||||
|
||||
// Calculate total collection value (lightweight query from database)
|
||||
useEffect(() => {
|
||||
const calculateTotalValue = async () => {
|
||||
if (!user) {
|
||||
setIsLoadingTotalValue(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsLoadingTotalValue(true);
|
||||
// Get total value directly from database (no need to fetch all cards!)
|
||||
const totalValue = await getCollectionTotalValue(user.id);
|
||||
setTotalCollectionValue(totalValue);
|
||||
} catch (error) {
|
||||
console.error('Error calculating total collection value:', error);
|
||||
setTotalCollectionValue(0);
|
||||
} finally {
|
||||
setIsLoadingTotalValue(false);
|
||||
}
|
||||
};
|
||||
|
||||
calculateTotalValue();
|
||||
}, [user]);
|
||||
|
||||
// Load user's collection from Supabase on mount
|
||||
useEffect(() => {
|
||||
const loadCollection = async () => {
|
||||
@@ -287,12 +313,22 @@ export default function Collection() {
|
||||
</h2>
|
||||
{/* Collection Value Summary */}
|
||||
<div className="bg-gray-800 border border-gray-700 rounded-lg px-4 py-2">
|
||||
<div className="text-xs text-gray-400 mb-0.5">Total Collection Value</div>
|
||||
<div className="text-xs text-gray-400 mb-0.5">
|
||||
{searchQuery ? 'Filtered Value' : 'Total Collection Value'}
|
||||
</div>
|
||||
<div className="text-lg font-bold text-green-400">
|
||||
${(searchQuery ? filteredCollection : collection).reduce((total, { card, quantity }) => {
|
||||
{isLoadingTotalValue ? (
|
||||
<Loader2 className="animate-spin" size={20} />
|
||||
) : searchQuery ? (
|
||||
// For search results, calculate from filtered collection
|
||||
`$${filteredCollection.reduce((total, { card, quantity }) => {
|
||||
const price = card.prices?.usd ? parseFloat(card.prices.usd) : 0;
|
||||
return total + (price * quantity);
|
||||
}, 0).toFixed(2)}
|
||||
}, 0).toFixed(2)}`
|
||||
) : (
|
||||
// For full collection, use pre-calculated total
|
||||
`$${totalCollectionValue.toFixed(2)}`
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
Trade,
|
||||
TradeItem,
|
||||
} from '../services/tradesService';
|
||||
import { getUserCollection, getUserCollectionPaginated, getCardsByIds } from '../services/api';
|
||||
import { getUserCollectionPaginated, getCardsByIds, getCollectionTotalValue } from '../services/api';
|
||||
import { Card } from '../types';
|
||||
import TradeCreator from './TradeCreator';
|
||||
import TradeDetail from './TradeDetail';
|
||||
@@ -68,6 +68,8 @@ export default function Community() {
|
||||
const [hasMoreUserCards, setHasMoreUserCards] = useState(false);
|
||||
const [userCollectionOffset, setUserCollectionOffset] = useState(0);
|
||||
const [userCollectionTotalCount, setUserCollectionTotalCount] = useState(0);
|
||||
const [userCollectionTotalValue, setUserCollectionTotalValue] = useState<number>(0);
|
||||
const [isLoadingUserTotalValue, setIsLoadingUserTotalValue] = useState(true);
|
||||
const [showTradeCreator, setShowTradeCreator] = useState(false);
|
||||
const [userCollectionSearch, setUserCollectionSearch] = useState('');
|
||||
const [hoveredUserCard, setHoveredUserCard] = useState<Card | null>(null);
|
||||
@@ -305,15 +307,20 @@ export default function Community() {
|
||||
|
||||
const loadUserCollection = async (userId: string) => {
|
||||
setLoadingCollection(true);
|
||||
setIsLoadingUserTotalValue(true);
|
||||
setSelectedUserCollection([]);
|
||||
setUserCollectionOffset(0);
|
||||
|
||||
try {
|
||||
// Load paginated collection for display
|
||||
const result = await getUserCollectionPaginated(userId, PAGE_SIZE, 0);
|
||||
setUserCollectionTotalCount(result.totalCount);
|
||||
setHasMoreUserCards(result.hasMore);
|
||||
|
||||
if (result.items.size === 0) {
|
||||
setSelectedUserCollection([]);
|
||||
setUserCollectionTotalValue(0);
|
||||
setIsLoadingUserTotalValue(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -324,11 +331,17 @@ export default function Community() {
|
||||
quantity: result.items.get(card.id) || 0,
|
||||
})));
|
||||
setUserCollectionOffset(PAGE_SIZE);
|
||||
|
||||
// Calculate total value (lightweight query from database)
|
||||
const totalValue = await getCollectionTotalValue(userId);
|
||||
setUserCollectionTotalValue(totalValue);
|
||||
} catch (error) {
|
||||
console.error('Error loading collection:', error);
|
||||
setSelectedUserCollection([]);
|
||||
setUserCollectionTotalValue(0);
|
||||
} finally {
|
||||
setLoadingCollection(false);
|
||||
setIsLoadingUserTotalValue(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -700,12 +713,22 @@ export default function Community() {
|
||||
</h2>
|
||||
{/* Collection Value Summary */}
|
||||
<div className="bg-gray-800 border border-gray-700 rounded-lg px-4 py-2">
|
||||
<div className="text-xs text-gray-400 mb-0.5">Total Collection Value</div>
|
||||
<div className="text-xs text-gray-400 mb-0.5">
|
||||
{userCollectionSearch ? 'Filtered Value' : 'Total Collection Value'}
|
||||
</div>
|
||||
<div className="text-lg font-bold text-green-400">
|
||||
${(userCollectionSearch ? filteredUserCollection : selectedUserCollection).reduce((total, { card, quantity }) => {
|
||||
{isLoadingUserTotalValue ? (
|
||||
<Loader2 className="animate-spin" size={20} />
|
||||
) : userCollectionSearch ? (
|
||||
// For search results, calculate from filtered collection
|
||||
`$${filteredUserCollection.reduce((total, { card, quantity }) => {
|
||||
const price = card.prices?.usd ? parseFloat(card.prices.usd) : 0;
|
||||
return total + (price * quantity);
|
||||
}, 0).toFixed(2)}
|
||||
}, 0).toFixed(2)}`
|
||||
) : (
|
||||
// For full collection, use pre-calculated total
|
||||
`$${userCollectionTotalValue.toFixed(2)}`
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user