Extract admin editor state into useCardEditor and UI into CardEditorView; keep dynamic(ssr: false) on the page export. Page is ~40 lines. Co-authored-by: Cursor <cursoragent@cursor.com>
185 lines
4.8 KiB
JavaScript
185 lines
4.8 KiB
JavaScript
import { useState, useEffect } from 'react';
|
|
|
|
const EMPTY_FORM = {
|
|
name: '',
|
|
set_name: '',
|
|
set_code: '',
|
|
card_number: '',
|
|
rarity: '',
|
|
game: '',
|
|
mana_cost: '',
|
|
cmc: '',
|
|
card_type: '',
|
|
colors: [],
|
|
oracle_text: '',
|
|
power: '',
|
|
toughness: '',
|
|
image_url: '',
|
|
stock_image_url: '',
|
|
current_price: '',
|
|
market_price: '',
|
|
};
|
|
|
|
/**
|
|
* Admin card editor state and handlers (god-component split).
|
|
*/
|
|
export function useCardEditor(router) {
|
|
const { id } = router.query;
|
|
|
|
const [card, setCard] = useState(null);
|
|
const [fetchLoading, setFetchLoading] = useState(false);
|
|
const loading = id ? fetchLoading : false;
|
|
const [saving, setSaving] = useState(false);
|
|
const [message, setMessage] = useState('');
|
|
const [searchQuery, setSearchQuery] = useState('');
|
|
const [searchResults, setSearchResults] = useState([]);
|
|
const [searchLoading, setSearchLoading] = useState(false);
|
|
const [formData, setFormData] = useState(EMPTY_FORM);
|
|
|
|
const fetchCard = async (cardId) => {
|
|
setFetchLoading(true);
|
|
try {
|
|
const response = await fetch(`/api/cards/${cardId}`);
|
|
if (response.ok) {
|
|
const cardData = await response.json();
|
|
setCard(cardData);
|
|
setFormData({
|
|
name: cardData.name || '',
|
|
set_name: cardData.set_name || '',
|
|
set_code: cardData.set_code || '',
|
|
card_number: cardData.card_number || '',
|
|
rarity: cardData.rarity || '',
|
|
game: cardData.game || '',
|
|
mana_cost: cardData.mana_cost || '',
|
|
cmc: cardData.cmc || '',
|
|
card_type: cardData.card_type || '',
|
|
colors: Array.isArray(cardData.colors) ? cardData.colors : [],
|
|
oracle_text: cardData.oracle_text || '',
|
|
power: cardData.power || '',
|
|
toughness: cardData.toughness || '',
|
|
image_url: cardData.image_url || '',
|
|
stock_image_url: cardData.stock_image_url || '',
|
|
current_price: cardData.current_price || '',
|
|
market_price: cardData.market_price || '',
|
|
});
|
|
} else {
|
|
setMessage('Card not found');
|
|
}
|
|
} catch (error) {
|
|
setMessage('Error loading card: ' + error.message);
|
|
} finally {
|
|
setFetchLoading(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (id) {
|
|
// eslint-disable-next-line react-hooks/set-state-in-effect -- load card when route id is set
|
|
fetchCard(id);
|
|
}
|
|
}, [id]);
|
|
|
|
const searchCards = async (query) => {
|
|
if (!query.trim()) {
|
|
setSearchResults([]);
|
|
return;
|
|
}
|
|
|
|
setSearchLoading(true);
|
|
try {
|
|
const response = await fetch(
|
|
`/api/cards/search?query=${encodeURIComponent(query)}&limit=20`
|
|
);
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
setSearchResults(data.cards || []);
|
|
}
|
|
} catch (error) {
|
|
console.error('Search error:', error);
|
|
} finally {
|
|
setSearchLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleInputChange = (field, value) => {
|
|
setFormData((prev) => ({
|
|
...prev,
|
|
[field]: value,
|
|
}));
|
|
};
|
|
|
|
const handleColorChange = (color, checked) => {
|
|
setFormData((prev) => ({
|
|
...prev,
|
|
colors: checked
|
|
? [...prev.colors, color]
|
|
: prev.colors.filter((c) => c !== color),
|
|
}));
|
|
};
|
|
|
|
const handleSave = async () => {
|
|
if (!card?.id) {
|
|
setMessage('No card selected to edit');
|
|
return;
|
|
}
|
|
|
|
setSaving(true);
|
|
setMessage('');
|
|
|
|
try {
|
|
const response = await fetch(`/api/cards/${card.id}`, {
|
|
method: 'PUT',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
...formData,
|
|
cmc: formData.cmc ? parseInt(formData.cmc, 10) : null,
|
|
current_price: formData.current_price ? parseFloat(formData.current_price) : null,
|
|
market_price: formData.market_price ? parseFloat(formData.market_price) : null,
|
|
}),
|
|
});
|
|
|
|
if (response.ok) {
|
|
const updatedCard = await response.json();
|
|
setCard(updatedCard);
|
|
setMessage('Card updated successfully!');
|
|
setTimeout(() => setMessage(''), 3000);
|
|
} else {
|
|
const errorData = await response.json();
|
|
setMessage('Error updating card: ' + (errorData.error || 'Unknown error'));
|
|
}
|
|
} catch (error) {
|
|
setMessage('Error updating card: ' + error.message);
|
|
} finally {
|
|
setSaving(false);
|
|
}
|
|
};
|
|
|
|
const selectCard = (selectedCard) => {
|
|
router.push(`/admin/card-editor?id=${selectedCard.id}`);
|
|
};
|
|
|
|
const onSearchQueryChange = (value) => {
|
|
setSearchQuery(value);
|
|
searchCards(value);
|
|
};
|
|
|
|
return {
|
|
card,
|
|
formData,
|
|
handleColorChange,
|
|
handleInputChange,
|
|
handleSave,
|
|
loading,
|
|
message,
|
|
onSearchQueryChange,
|
|
router,
|
|
saving,
|
|
searchLoading,
|
|
searchQuery,
|
|
searchResults,
|
|
selectCard,
|
|
showEmptyState: !card && !loading,
|
|
};
|
|
}
|