deckhearth/src/components/admin/CardLoader.tsx

185 lines
6.5 KiB
TypeScript
Raw Normal View History

import React, { useState, useEffect } from 'react';
import { useAuth } from '../../contexts/AuthContext';
interface CardCounts {
MTG?: number;
POKEMON?: number;
LORCANA?: number;
total?: number;
}
interface LoadingResults {
mtg?: number;
pokemon?: number;
lorcana?: number;
}
const CardLoader: React.FC = () => {
const { user, token } = useAuth();
const [cardCounts, setCardCounts] = useState<CardCounts>({});
const [loading, setLoading] = useState(false);
const [loadingResults, setLoadingResults] = useState<LoadingResults>({});
const [selectedGame, setSelectedGame] = useState<string>('ALL');
const [message, setMessage] = useState<string>('');
// Fetch current card counts
const fetchCardCounts = async () => {
try {
const response = await fetch('https://tcg-vault.vercel.app/api/admin/load-cards', {
headers: {
'Authorization': `Bearer ${token}`
}
});
if (response.ok) {
const data = await response.json();
setCardCounts(data.counts || {});
}
} catch (error) {
console.error('Error fetching card counts:', error);
}
};
useEffect(() => {
fetchCardCounts();
}, []);
// Load cards from external APIs
const loadCards = async (game: string) => {
setLoading(true);
setMessage(`Loading ${game} cards...`);
setLoadingResults({});
try {
const response = await fetch(`https://tcg-vault.vercel.app/api/admin/load-cards?game=${game}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
if (response.ok) {
const data = await response.json();
setLoadingResults(data.results || {});
setMessage(`Successfully loaded cards! ${data.message}`);
// Refresh card counts
setTimeout(() => {
fetchCardCounts();
}, 1000);
} else {
const errorData = await response.json();
setMessage(`Error loading cards: ${errorData.error}`);
}
} catch (error) {
console.error('Error loading cards:', error);
setMessage('Error loading cards. Please try again.');
} finally {
setLoading(false);
}
};
const handleLoadCards = () => {
loadCards(selectedGame);
};
return (
<div className="bg-white rounded-lg shadow-md p-6">
<h2 className="text-2xl font-bold text-gray-800 mb-6">Card Database Loader</h2>
{/* Current Card Counts */}
<div className="mb-6">
<h3 className="text-lg font-semibold text-gray-700 mb-3">Current Database Status</h3>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<div className="bg-blue-50 p-4 rounded-lg">
<div className="text-2xl font-bold text-blue-600">{cardCounts.MTG || 0}</div>
<div className="text-sm text-blue-500">MTG Cards</div>
</div>
<div className="bg-yellow-50 p-4 rounded-lg">
<div className="text-2xl font-bold text-yellow-600">{cardCounts.POKEMON || 0}</div>
<div className="text-sm text-yellow-500">Pokémon Cards</div>
</div>
<div className="bg-purple-50 p-4 rounded-lg">
<div className="text-2xl font-bold text-purple-600">{cardCounts.LORCANA || 0}</div>
<div className="text-sm text-purple-500">Lorcana Cards</div>
</div>
<div className="bg-green-50 p-4 rounded-lg">
<div className="text-2xl font-bold text-green-600">{cardCounts.total || 0}</div>
<div className="text-sm text-green-500">Total Cards</div>
</div>
</div>
</div>
{/* Load Cards Section */}
<div className="mb-6">
<h3 className="text-lg font-semibold text-gray-700 mb-3">Load Cards from External APIs</h3>
<div className="flex flex-col sm:flex-row gap-4 mb-4">
<select
value={selectedGame}
onChange={(e) => setSelectedGame(e.target.value)}
className="px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
>
<option value="ALL">All Games (MTG, Pokémon, Lorcana)</option>
<option value="MTG">Magic: The Gathering</option>
<option value="POKEMON">Pokémon</option>
<option value="LORCANA">Disney Lorcana</option>
</select>
<button
onClick={handleLoadCards}
disabled={loading}
className="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors"
>
{loading ? 'Loading...' : 'Load Cards'}
</button>
</div>
{/* Loading Results */}
{Object.keys(loadingResults).length > 0 && (
<div className="bg-gray-50 p-4 rounded-lg">
<h4 className="font-semibold text-gray-700 mb-2">Loading Results:</h4>
<div className="space-y-2">
{loadingResults.mtg !== undefined && (
<div className="text-blue-600">MTG: {loadingResults.mtg} cards loaded</div>
)}
{loadingResults.pokemon !== undefined && (
<div className="text-yellow-600">Pokémon: {loadingResults.pokemon} cards loaded</div>
)}
{loadingResults.lorcana !== undefined && (
<div className="text-purple-600">Lorcana: {loadingResults.lorcana} cards loaded</div>
)}
</div>
</div>
)}
{/* Message */}
{message && (
<div className={`mt-4 p-3 rounded-lg ${
message.includes('Error')
? 'bg-red-50 text-red-700 border border-red-200'
: 'bg-green-50 text-green-700 border border-green-200'
}`}>
{message}
</div>
)}
</div>
{/* Instructions */}
<div className="bg-gray-50 p-4 rounded-lg">
<h3 className="text-lg font-semibold text-gray-700 mb-2">Instructions</h3>
<ul className="text-sm text-gray-600 space-y-1">
<li> This will load cards from external APIs into your database</li>
<li> MTG cards come from Scryfall API</li>
<li> Pokémon cards come from Pokémon TCG API</li>
<li> Lorcana cards come from Lorcana API and Lorcast API</li>
<li> Loading may take several minutes for large datasets</li>
<li> Cards are deduplicated automatically</li>
</ul>
</div>
</div>
);
};
export default CardLoader;