deckhearth/scripts/create-sample-collections.js
Randall Stillwell 50a3156b92 🔗 Implement Collection Slug URLs
🎯 Vanity URLs for Collections:
- Added slug-based URLs like /collection/modern-masters-2021
- Backwards compatible with numeric IDs
- SEO-friendly and memorable URLs

🛠️ Slug System:
- Created lib/slug-utils.js with slug generation and validation
- generateSlug() converts names to URL-friendly format
- generateUniqueSlug() handles duplicates with numeric suffixes
- isValidSlug() validates format (lowercase, hyphens, no special chars)

📊 Database Schema:
- Added slug column to collections table with unique constraint
- Migration script adds slugs to existing collections
- Database constraints ensure slug format and uniqueness
- Performance index on slug column

🔌 API Updates:
- Updated collections API to generate slugs for new collections
- New [identifier].js endpoint handles both slugs and IDs
- Thumbnails API supports both slug and ID lookups
- Smart identifier detection (slug vs numeric ID)

🎨 Frontend Integration:
- Collections page uses slugs for navigation
- Fallback to ID if slug not available (backwards compatibility)
- Updated all collection links to use slugs
- Sample collections created with proper slugs

 URL Examples:
- /collection/modern-masters-2021 (new slug format)
- /collection/123 (old ID format still works)
- Automatic redirect potential for future

The collection URLs are now beautiful and shareable! 🚀
2025-07-26 22:06:52 -05:00

115 lines
No EOL
3.7 KiB
JavaScript

#!/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 createSampleCollections() {
const sql = neon(process.env.POSTGRES_URL);
try {
console.log('🎨 Creating sample collections with slugs...');
// First, check if we have a user to assign collections to
const users = await sql`SELECT id FROM users LIMIT 1`;
if (!users || users.length === 0) {
console.log('❌ No users found. Please create a user first.');
return;
}
const userId = users[0].id;
const sampleCollections = [
{
name: "Modern Masters 2021",
description: "Complete set of Modern Masters 2021 with all the best reprints",
tcg: "MTG",
isPublic: true,
tags: ["modern", "masters", "complete", "reprint"]
},
{
name: "Pokemon Base Set",
description: "Original Pokemon base set collection from 1998",
tcg: "Pokemon",
isPublic: false,
tags: ["base", "original", "holographic", "vintage"]
},
{
name: "Commander Staples",
description: "Essential cards for Commander format gameplay",
tcg: "MTG",
isPublic: true,
tags: ["commander", "staples", "multiplayer", "edh"]
},
{
name: "Disney Lorcana First Chapter",
description: "Disney Lorcana First Chapter collection with rare enchanted cards",
tcg: "Lorcana",
isPublic: true,
tags: ["disney", "first-chapter", "enchanted", "new"]
},
{
name: "Vintage Pokemon Cards",
description: "Rare vintage Pokemon cards from the early sets",
tcg: "Pokemon",
isPublic: false,
tags: ["vintage", "rare", "holographic", "investment"]
}
];
// 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...');
for (const collection of sampleCollections) {
// Generate unique slug
const uniqueSlug = await generateUniqueSlug(collection.name, existingSlugs);
existingSlugs.push(uniqueSlug);
// Insert collection
const result = await sql`
INSERT INTO collections (name, description, tcg, is_public, tags, user_id, slug)
VALUES (
${collection.name},
${collection.description},
${collection.tcg},
${collection.isPublic},
${collection.tags.join(',')},
${userId},
${uniqueSlug}
)
RETURNING id, name, slug
`;
// Create owner permission
await sql`
INSERT INTO collection_permissions (collection_id, user_id, role, status)
VALUES (${result[0].id}, ${userId}, 'owner', 'active')
`;
console.log(`✅ Created: "${collection.name}" → "${uniqueSlug}"`);
}
console.log('\n🎉 Sample collections created successfully!');
console.log('\n🔗 You can now access collections via:');
console.log(' • /collection/modern-masters-2021');
console.log(' • /collection/pokemon-base-set');
console.log(' • /collection/commander-staples');
console.log(' • /collection/disney-lorcana-first-chapter');
console.log(' • /collection/vintage-pokemon-cards');
} catch (error) {
console.error('❌ Failed to create sample collections:', error.message);
console.error('Full error:', error);
process.exit(1);
}
}
if (import.meta.url === `file://${process.argv[1]}`) {
createSampleCollections();
}
export { createSampleCollections };