Skip to content

Commit c19ce61

Browse files
hh
1 parent cfc166d commit c19ce61

11 files changed

Lines changed: 265 additions & 117 deletions

File tree

components/Yugioh/GridView.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,8 @@ const GridView = ( { aggregatedData, onDeleteCard, onUpdateCard, sortConfig, han
209209

210210
const cardImage = getCardImage( card.productName );
211211
const cardInfo = cardData.find( ( item ) => item.name === card.productName );
212-
const imageSrc = cardImage || card.remoteImageUrl || FALLBACK_IMAGE;
212+
const cardIdImage = card.cardId ? getFullImagePath( card.cardId ) : null;
213+
const imageSrc = cardImage || cardIdImage || card.remoteImageUrl || FALLBACK_IMAGE;
213214
const isFlipped = Boolean( flippedCards[ card._id ] );
214215
const quantity = Number( card.quantity ) || 0;
215216
const removeAmount = editValues[ card._id ]?.deleteAmount || 1;
@@ -330,16 +331,16 @@ const GridView = ( { aggregatedData, onDeleteCard, onUpdateCard, sortConfig, han
330331
<div className="col-start-1 row-start-1 flex w-full flex-col justify-between gap-3 rounded border border-white/10 bg-black p-4 text-white shadow-lg dark:border-white/20 dark:bg-gray-900/80 [backface-visibility:hidden] [transform:rotateY(180deg)]">
331332
<div className="space-y-3">
332333
<h3 className="text-lg font-semibold text-center text-pretty break-words">{ card.productName }</h3>
333-
<div className="space-y-1 text-xs text-white/70 sm:text-sm">
334-
{ detailEntries.map( ( entry ) => (
334+
<div className="space-y-1 text-xs text-white/70 sm:text-sm">
335+
{ detailEntries.map( ( entry ) => (
335336
<div key={ entry.label } className="flex min-w-0 items-start justify-between gap-3 border-b border-white/5 pb-1 last:border-b-0">
336337
<span className="text-[0.65rem] font-semibold uppercase tracking-wide text-white/60 sm:text-xs">
337338
{ entry.label }
338339
</span>
339340
<span className="min-w-0 text-right break-words text-white">{ entry.value }</span>
340341
</div>
341-
) ) }
342-
</div>
342+
) ) }
343+
</div>
343344
</div>
344345
<div className="mx-auto mt-2 flex flex-wrap w-full gap-2 text-pretty sm:gap-3">
345346
<Link

package-lock.json

Lines changed: 20 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pages/api/Yugioh/card/[...cardId].js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@
2626
race: card?.race,
2727
archetype: card?.archetype,
2828
ygoprodeck_url: card?.ygoprodeck_url,
29+
card_images: card?.card_images?.map( ( img ) => ( {
30+
id: img?.id,
31+
image_url: img?.image_url,
32+
image_url_small: img?.image_url_small,
33+
} ) ) || [],
2934
card_sets: card?.card_sets?.map( ( set ) => ( {
3035
set_name: set.set_name,
3136
set_code: set.set_code,

pages/api/Yugioh/card/lookup.js

Lines changed: 104 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,86 @@
1+
const normalizeNameKey = ( value ) =>
2+
( value ?? "" ).toString().toLowerCase().replace( /[^a-z0-9]+/g, "" ).trim();
3+
4+
const buildNameCandidates = ( name ) => {
5+
const trimmed = ( name ?? "" ).toString().trim();
6+
if ( !trimmed ) return [];
7+
8+
const candidates = new Set( [ trimmed ] );
9+
const withoutParens = trimmed.replace( /\s*\([^)]*\)\s*$/, "" ).trim();
10+
const withoutBrackets = trimmed.replace( /\s*\[[^\]]*\]\s*$/, "" ).trim();
11+
const withoutDashSuffix = trimmed.replace( /\s*[-]\s*[^-]+$/, "" ).trim();
12+
const withoutRarityToken = trimmed.replace(
13+
/\s*\b(UTR|UR|SR|CR|GR|QCR|PCR|PGR|SP|SE|PR|HR|R|C|SCR|SNR|GCR|GUR|GSR)\b\s*$/i,
14+
""
15+
).trim();
16+
17+
[ withoutParens, withoutBrackets, withoutDashSuffix, withoutRarityToken ]
18+
.filter( Boolean )
19+
.forEach( ( candidate ) => candidates.add( candidate ) );
20+
21+
return Array.from( candidates );
22+
};
23+
24+
const fetchExactName = async ( name ) => {
25+
const url = `https://db.ygoprodeck.com/api/v7/cardinfo.php?name=${ encodeURIComponent( name ) }&tcgplayer_data=true`;
26+
const response = await fetch( url );
27+
const data = await response.json();
28+
if ( !Array.isArray( data?.data ) || data.data.length === 0 ) {
29+
return null;
30+
}
31+
return data.data[ 0 ];
32+
};
33+
34+
const fetchFuzzyName = async ( name ) => {
35+
const url = `https://db.ygoprodeck.com/api/v7/cardinfo.php?fname=${ encodeURIComponent( name ) }&tcgplayer_data=true`;
36+
const response = await fetch( url );
37+
const data = await response.json();
38+
return Array.isArray( data?.data ) ? data.data : [];
39+
};
40+
41+
const pickBestMatch = ( matches, candidates ) => {
42+
if ( !Array.isArray( matches ) || matches.length === 0 ) {
43+
return null;
44+
}
45+
46+
const candidateKeys = new Set( candidates.map( normalizeNameKey ).filter( Boolean ) );
47+
if ( candidateKeys.size === 0 ) {
48+
return matches[ 0 ];
49+
}
50+
51+
const exact = matches.find( ( card ) => candidateKeys.has( normalizeNameKey( card?.name ) ) );
52+
return exact || matches[ 0 ];
53+
};
54+
55+
const formatCard = ( card ) => ( {
56+
id: card?.id,
57+
name: card?.name,
58+
type: card?.type,
59+
desc: card?.desc,
60+
frameType: card?.frameType,
61+
race: card?.race,
62+
archetype: card?.archetype,
63+
ygoprodeck_url: card?.ygoprodeck_url,
64+
card_images: card?.card_images?.map( ( img ) => ( {
65+
id: img?.id,
66+
image_url: img?.image_url,
67+
image_url_small: img?.image_url_small,
68+
} ) ) || [],
69+
card_sets: card?.card_sets?.map( ( set ) => ( {
70+
set_name: set.set_name,
71+
set_code: set.set_code,
72+
set_rarity: set.set_rarity,
73+
rarity_code: set.set_rarity_code,
74+
set_edition: set.set_edition || "Unknown Edition",
75+
set_price: set.set_price || "0.00",
76+
} ) ) || [],
77+
card_prices: card?.card_prices?.map( ( price ) => ( {
78+
tcgplayer_price: price.tcgplayer_price || "0.00",
79+
ebay_price: price.ebay_price || "0.00",
80+
amazon_price: price.amazon_price || "0.00",
81+
} ) ) || [],
82+
} );
83+
184
export default async function handler( req, res ) {
285
const { name } = req.query;
386
const normalizedName = Array.isArray( name ) ? name[ 0 ] : name;
@@ -6,42 +89,32 @@ export default async function handler( req, res ) {
689
return res.status( 400 ).json( { error: "Missing card name" } );
790
}
891

92+
const candidates = buildNameCandidates( normalizedName );
93+
if ( candidates.length === 0 ) {
94+
return res.status( 400 ).json( { error: "Missing card name" } );
95+
}
96+
997
try {
10-
const url = `https://db.ygoprodeck.com/api/v7/cardinfo.php?name=${ encodeURIComponent( normalizedName ) }&tcgplayer_data=true`;
11-
const response = await fetch( url );
12-
const data = await response.json();
98+
let card = null;
99+
100+
for ( const candidate of candidates ) {
101+
card = await fetchExactName( candidate );
102+
if ( card ) {
103+
break;
104+
}
105+
}
106+
107+
if ( !card ) {
108+
const fallbackName = candidates[ candidates.length - 1 ];
109+
const fuzzyMatches = await fetchFuzzyName( fallbackName );
110+
card = pickBestMatch( fuzzyMatches, candidates );
111+
}
13112

14-
if ( !Array.isArray( data?.data ) || data.data.length === 0 ) {
113+
if ( !card ) {
15114
return res.status( 404 ).json( { error: "Card not found" } );
16115
}
17116

18-
const card = data.data?.[ 0 ];
19-
20-
const formattedCard = {
21-
id: card?.id,
22-
name: card?.name,
23-
type: card?.type,
24-
desc: card?.desc,
25-
frameType: card?.frameType,
26-
race: card?.race,
27-
archetype: card?.archetype,
28-
ygoprodeck_url: card?.ygoprodeck_url,
29-
card_sets: card?.card_sets?.map( ( set ) => ( {
30-
set_name: set.set_name,
31-
set_code: set.set_code,
32-
set_rarity: set.set_rarity,
33-
rarity_code: set.set_rarity_code,
34-
set_edition: set.set_edition || "Unknown Edition",
35-
set_price: set.set_price || "0.00",
36-
} ) ) || [],
37-
card_prices: card?.card_prices?.map( ( price ) => ( {
38-
tcgplayer_price: price.tcgplayer_price || "0.00",
39-
ebay_price: price.ebay_price || "0.00",
40-
amazon_price: price.amazon_price || "0.00",
41-
} ) ) || [],
42-
};
43-
44-
res.status( 200 ).json( formattedCard );
117+
res.status( 200 ).json( formatCard( card ) );
45118
} catch ( error ) {
46119
console.error( "Card lookup failed:", error );
47120
res.status( 500 ).json( { error: "Internal Server Error" } );

pages/api/Yugioh/cards.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,13 @@ export default async function handler( req, res ) {
6464
card.cardId === null || card.cardId === undefined
6565
? null
6666
: coerceStringField( card.cardId, { maxLength: 128 } ),
67+
remoteImageUrl: coerceStringField( card.remoteImageUrl ?? "", { maxLength: 1024, allowEmpty: true } ),
6768
};
6869

70+
if ( !sanitizedCard.remoteImageUrl ) {
71+
sanitizedCard.remoteImageUrl = null;
72+
}
73+
6974
const key = buildCardKey( sanitizedCard );
7075
const existing = dedupedCards.get( key );
7176

@@ -74,6 +79,7 @@ export default async function handler( req, res ) {
7479
existing.marketPrice = sanitizedCard.marketPrice || existing.marketPrice;
7580
existing.lowPrice = sanitizedCard.lowPrice || existing.lowPrice;
7681
existing.cardId = sanitizedCard.cardId ?? existing.cardId;
82+
existing.remoteImageUrl = sanitizedCard.remoteImageUrl || existing.remoteImageUrl || null;
7783
} else {
7884
dedupedCards.set( key, sanitizedCard );
7985
}
@@ -90,6 +96,16 @@ export default async function handler( req, res ) {
9096

9197
const bulkOps = sanitizedCards.map( ( card ) => {
9298
const now = new Date();
99+
const setFields = {
100+
oldPrice: null,
101+
cardId: card.cardId || null,
102+
updatedAt: now,
103+
};
104+
105+
if ( card.remoteImageUrl ) {
106+
setFields.remoteImageUrl = card.remoteImageUrl;
107+
}
108+
93109
return {
94110
updateOne: {
95111
filter: {
@@ -104,9 +120,7 @@ export default async function handler( req, res ) {
104120
update: {
105121
$inc: { quantity: card.quantity },
106122
$set: {
107-
oldPrice: null,
108-
cardId: card.cardId || null,
109-
updatedAt: now
123+
...setFields
110124
},
111125
$setOnInsert: {
112126
marketPrice: card.marketPrice || 0,
@@ -127,4 +141,3 @@ export default async function handler( req, res ) {
127141
return res.status( 500 ).json( { message: "Server error" } );
128142
}
129143
}
130-

pages/api/Yugioh/my-collection.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export default async function handler( req, res ) {
3131
oldPrice: 1,
3232
marketPrice: 1,
3333
lowPrice: 1,
34+
remoteImageUrl: 1,
3435
quantity: 1,
3536
},
3637
},

pages/api/Yugioh/updateCards.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export default async function handler( req, res ) {
6666
{ $set: { [ field ]: sanitizedValue } }
6767
);
6868

69-
if ( result.modifiedCount > 0 ) {
69+
if ( result.modifiedCount >= 0 ) {
7070
return res.status( 200 ).json( { message: "Card updated successfully" } );
7171
}
7272

pages/yugioh/deck-builder.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ export default function DeckBuilder() {
212212
<div className="grid grid-cols-8 gap-2 mt-2">
213213
{ extraDeck.map( ( card, index ) => (
214214
<div
215-
key={ card.id ?? `${ card.name }-${ index }` }
215+
key={ `${ card.id ?? card.name }-${ index }` }
216216
className="relative w-20 h-28 bg-gray-200 flex flex-col items-center justify-center border border-gray-400 rounded"
217217
>
218218
<Image
@@ -303,7 +303,7 @@ export default function DeckBuilder() {
303303
{ filteredArchetypeSuggestions && filteredArchetypeSuggestions.length > 0 ? (
304304
<div className="inline-grid grid-cols-1 sm:grid-cols-2 gap-4 mt-4 overflow-y-auto max-h-[400px]">
305305
{ filteredArchetypeSuggestions.map( ( card, index ) => (
306-
<div key={ card.id ?? `${ card.name }-${ index }` } className="w-full rounded bg-white p-3 shadow">
306+
<div key={ `${ card.id ?? card.name }-${ index }` } className="w-full rounded bg-white p-3 shadow">
307307
<div className="flex flex-col gap-3 sm:flex-row">
308308
<Image
309309
src={ card.card_images?.[ 0 ]?.image_url || "/images/backgrounds/yugioh/background.svg" }

0 commit comments

Comments
 (0)