diff --git a/scripts/seed-collections-with-cards.js b/scripts/seed-collections-with-cards.js new file mode 100644 index 0000000..be21196 --- /dev/null +++ b/scripts/seed-collections-with-cards.js @@ -0,0 +1,239 @@ +#!/usr/bin/env node + +import dotenv from 'dotenv'; +import { neon } from '@neondatabase/serverless'; +import { generateUniqueSlug } from '../lib/slug-utils.js'; + +dotenv.config({ path: '.env.local' }); + +async function seedCollectionsWithCards() { + const sql = neon(process.env.POSTGRES_URL); + + try { + console.log('๐ŸŽฎ Seeding collections with cards and thumbnails...'); + + // First, get Alice and Bob's user IDs + const users = await sql` + SELECT id, email FROM users + WHERE email IN ('alice@tcgvault.com', 'bob@tcgvault.com') + ORDER BY email + `; + + if (!users || users.length !== 2) { + console.error('โŒ Could not find Alice and Bob users'); + console.error('Expected 2 users, got:', users?.length || 0); + return; + } + + const alice = users.find(u => u.email === 'alice@tcgvault.com'); + const bob = users.find(u => u.email === 'bob@tcgvault.com'); + + console.log(`๐Ÿ‘ฉ Alice: ID ${alice.id}`); + console.log(`๐Ÿ‘จ Bob: ID ${bob.id}`); + + // Clear existing data + console.log('๐Ÿงน Clearing existing collections and cards...'); + await sql`DELETE FROM collection_cards`; + await sql`DELETE FROM collection_permissions`; + await sql`DELETE FROM collections`; + + // Add some sample cards if they don't exist + console.log('๐Ÿƒ Adding sample cards...'); + const sampleCards = [ + // MTG Cards + { name: 'Black Lotus', set_name: 'Alpha', rarity: 'mythic', game: 'MTG', market_price: 50000, image_url: 'https://cards.scryfall.io/normal/front/b/d/bd8fa327-dd41-4737-8f19-2cf5eb1f7cdd.jpg' }, + { name: 'Lightning Bolt', set_name: 'Alpha', rarity: 'common', game: 'MTG', market_price: 25, image_url: 'https://cards.scryfall.io/normal/front/c/e/ce711943-c1a1-43a0-8b89-8d169cfb8e06.jpg' }, + { name: 'Ancestral Recall', set_name: 'Alpha', rarity: 'mythic', game: 'MTG', market_price: 8000, image_url: 'https://cards.scryfall.io/normal/front/2/3/2398892d-28e9-4009-81ec-0d544af79d2b.jpg' }, + { name: 'Serra Angel', set_name: 'Alpha', rarity: 'uncommon', game: 'MTG', market_price: 15, image_url: 'https://cards.scryfall.io/normal/front/9/0/9067f035-3437-4c5c-bae9-d3c9001a3411.jpg' }, + { name: 'Shivan Dragon', set_name: 'Alpha', rarity: 'rare', game: 'MTG', market_price: 100, image_url: 'https://cards.scryfall.io/normal/front/2/2/227cf1b5-f85b-41fe-be98-66e383652039.jpg' }, + + // Pokemon Cards + { name: 'Charizard', set_name: 'Base Set', rarity: 'rare', game: 'Pokemon', market_price: 350, image_url: 'https://images.pokemontcg.io/base1/4_hires.png' }, + { name: 'Blastoise', set_name: 'Base Set', rarity: 'rare', game: 'Pokemon', market_price: 200, image_url: 'https://images.pokemontcg.io/base1/2_hires.png' }, + { name: 'Venusaur', set_name: 'Base Set', rarity: 'rare', game: 'Pokemon', market_price: 180, image_url: 'https://images.pokemontcg.io/base1/15_hires.png' }, + { name: 'Pikachu', set_name: 'Base Set', rarity: 'common', game: 'Pokemon', market_price: 50, image_url: 'https://images.pokemontcg.io/base1/58_hires.png' }, + { name: 'Machamp', set_name: 'Base Set', rarity: 'rare', game: 'Pokemon', market_price: 75, image_url: 'https://images.pokemontcg.io/base1/8_hires.png' }, + + // Lorcana Cards + { name: 'Elsa - Snow Queen', set_name: 'First Chapter', rarity: 'legendary', game: 'Lorcana', market_price: 25, image_url: 'https://example.com/elsa.jpg' }, + { name: 'Mickey Mouse - Brave Little Tailor', set_name: 'First Chapter', rarity: 'rare', game: 'Lorcana', market_price: 15, image_url: 'https://example.com/mickey.jpg' }, + { name: 'Simba - King of Pride Rock', set_name: 'First Chapter', rarity: 'rare', game: 'Lorcana', market_price: 20, image_url: 'https://example.com/simba.jpg' }, + ]; + + // Insert cards (or update if they exist) + const cardIds = []; + for (const card of sampleCards) { + // Check if card already exists + const existingCard = await sql` + SELECT id FROM cards WHERE name = ${card.name} AND set_name = ${card.set_name} + `; + + if (existingCard.length > 0) { + cardIds.push(existingCard[0].id); + } else { + const result = await sql` + INSERT INTO cards (name, set_name, rarity, game, market_price, image_url, created_at, updated_at) + VALUES (${card.name}, ${card.set_name}, ${card.rarity}, ${card.game}, ${card.market_price}, ${card.image_url}, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) + RETURNING id + `; + cardIds.push(result[0].id); + } + } + + console.log(`โœ… Added/updated ${cardIds.length} sample cards`); + + // Create collections with different states + const collectionsData = [ + // Alice's Collections + { + owner: alice, + name: "Alice's Power 9 Collection", + description: "The most powerful and expensive cards in Magic history. My crown jewels!", + tcg: "MTG", + isPublic: true, + tags: ["power-nine", "vintage", "expensive", "mythic"], + cardIds: [0, 2, 4], // Black Lotus, Ancestral Recall, Shivan Dragon + customThumbnail: null + }, + { + owner: alice, + name: "Pokemon Starter Collection", + description: "The classic starter Pokemon from the original Base Set. Nostalgia at its finest!", + tcg: "Pokemon", + isPublic: true, + tags: ["pokemon", "starters", "base-set", "classic"], + cardIds: [5, 6, 7, 8], // Charizard, Blastoise, Venusaur, Pikachu + customThumbnail: "https://images.unsplash.com/photo-1606890737304-57a1ca8a5b62?w=400&h=300&fit=crop" + }, + { + owner: alice, + name: "Empty Future Collection", + description: "This collection is waiting for the perfect cards to be added. Coming soon!", + tcg: "MTG", + isPublic: false, + tags: ["future", "planning", "wishlist"], + cardIds: [], // Empty collection + customThumbnail: null + }, + + // Bob's Collections + { + owner: bob, + name: "Bob's Budget MTG Deck", + description: "Affordable but effective cards for competitive play. Proof you don't need to break the bank!", + tcg: "MTG", + isPublic: true, + tags: ["budget", "competitive", "affordable", "deck"], + cardIds: [1, 3], // Lightning Bolt, Serra Angel + customThumbnail: null + }, + { + owner: bob, + name: "Disney Lorcana Heroes", + description: "My favorite Disney characters brought to life in card form. The magic of Disney!", + tcg: "Lorcana", + isPublic: true, + tags: ["disney", "lorcana", "heroes", "characters"], + cardIds: [10, 11, 12], // Elsa, Mickey, Simba + customThumbnail: "https://images.unsplash.com/photo-1578662996442-48f60103fc96?w=400&h=300&fit=crop" + }, + { + owner: bob, + name: "Secret Project Collection", + description: "A mysterious collection that's still under construction. What could it be?", + tcg: "Pokemon", + isPublic: false, + tags: ["secret", "mystery", "wip"], + cardIds: [9], // Just Machamp + customThumbnail: null + } + ]; + + // Get existing slugs + const existingSlugsData = await sql`SELECT slug FROM collections WHERE slug IS NOT NULL`; + const existingSlugs = (existingSlugsData || []).map(row => row.slug); + + console.log('๐Ÿ—๏ธ Creating collections with various states...'); + + for (const collectionData of collectionsData) { + // Generate unique slug + const uniqueSlug = await generateUniqueSlug(collectionData.name, existingSlugs); + existingSlugs.push(uniqueSlug); + + // Create collection + const collectionResult = await sql` + INSERT INTO collections (name, description, tcg, is_public, tags, user_id, slug, image, created_at, updated_at) + VALUES ( + ${collectionData.name}, + ${collectionData.description}, + ${collectionData.tcg}, + ${collectionData.isPublic}, + ${collectionData.tags.join(',')}, + ${collectionData.owner.id}, + ${uniqueSlug}, + ${collectionData.customThumbnail}, + CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP + ) + RETURNING id, name, slug + `; + + const collection = collectionResult[0]; + + // Create owner permission + await sql` + INSERT INTO collection_permissions (collection_id, user_id, role, status, created_at) + VALUES (${collection.id}, ${collectionData.owner.id}, 'owner', 'active', CURRENT_TIMESTAMP) + `; + + // Add cards to collection + if (collectionData.cardIds.length > 0) { + for (const cardIndex of collectionData.cardIds) { + const cardId = cardIds[cardIndex]; + const quantity = Math.floor(Math.random() * 3) + 1; // Random quantity 1-3 + + await sql` + INSERT INTO collection_cards (collection_id, card_id, quantity, created_at) + VALUES (${collection.id}, ${cardId}, ${quantity}, CURRENT_TIMESTAMP) + `; + } + } + + const statusEmoji = collectionData.cardIds.length === 0 ? '๐Ÿ˜ข' : + collectionData.customThumbnail ? '๐Ÿ–ผ๏ธ' : '๐Ÿƒ'; + const statusText = collectionData.cardIds.length === 0 ? 'Empty (crying emoji)' : + collectionData.customThumbnail ? 'Custom thumbnail' : + `${collectionData.cardIds.length} cards`; + + console.log(` ${statusEmoji} "${collectionData.name}" โ†’ "${uniqueSlug}" (${statusText})`); + } + + console.log('\n๐ŸŽ‰ Database seeded successfully!'); + + console.log('\n๐Ÿ“Š Collection States Created:'); + console.log(' ๐Ÿ˜ข Empty collections โ†’ Show crying emoji placeholder'); + console.log(' ๐Ÿƒ Collections with cards โ†’ Show white card boxes with images'); + console.log(' ๐Ÿ–ผ๏ธ Collections with custom thumbnails โ†’ Show uploaded images'); + + console.log('\n๐ŸŽฎ Test Scenarios:'); + console.log(' ๐Ÿ‘ฉ Alice has: Power 9 (cards), Pokemon (custom thumb), Empty (crying emoji)'); + console.log(' ๐Ÿ‘จ Bob has: Budget MTG (cards), Lorcana (custom thumb), Secret (1 card)'); + + console.log('\n๐Ÿ”— Ready to test the new thumbnail layouts!'); + console.log(' โ€ข Login as Alice or Bob'); + console.log(' โ€ข Visit /collections to see your collections'); + console.log(' โ€ข Visit /community/collections to see public ones'); + console.log(' โ€ข Each collection will show different thumbnail states'); + + } catch (error) { + console.error('โŒ Failed to seed collections with cards:', error.message); + console.error('Full error:', error); + process.exit(1); + } +} + +if (import.meta.url === `file://${process.argv[1]}`) { + seedCollectionsWithCards(); +} + +export { seedCollectionsWithCards }; \ No newline at end of file