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