✅ Database & API Fixes: - Fixed collection detail API to use correct column names (card_type, market_price, image_url) - Removed all mock data and fallbacks - Updated field mappings throughout collection detail page - Fixed hero section to use real collection data with proper image support �� Test Users Created: - admin@tcgvault.com / admin123 (Admin) - alice@tcgvault.com / alice123 (User) - bob@tcgvault.com / bob123 (User) 🃏 Sample Cards Added: - Lightning Bolt (MTG) - $2.50 - Black Lotus (MTG) - $25,000 - Pikachu (Pokemon) - $8.50 - Charizard (Pokemon) - $350 - Mickey Mouse (Lorcana) - $45 - Elsa (Lorcana) - $15.75 🔧 Collaboration Features: - Added CollaborationManager to collection detail page - Integrated real user permissions (isOwner check) - Updated hero section with real stats and creator info 🔍 Card Management: - Created cards search API (/api/cards/search) - Implemented quick add functionality in empty state - Real-time card search with dropdown results - Add cards directly to collection with quantity �� Ready for Testing: 1. Login as any user to see only their collections 2. Create collections with real data 3. Add cards using search functionality 4. Invite collaborators via email system 5. Switch users to test collaboration workflow Complete end-to-end testing environment ready! 🚀
132 lines
No EOL
3.8 KiB
JavaScript
132 lines
No EOL
3.8 KiB
JavaScript
import { sql } from '@vercel/postgres';
|
|
import { withCollectionPermission, getUserFromRequest, logCollectionActivity } from '../../../lib/permission-middleware';
|
|
|
|
async function handler(req, res) {
|
|
// Set CORS headers
|
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
|
|
// Handle preflight requests
|
|
if (req.method === 'OPTIONS') {
|
|
res.status(200).end();
|
|
return;
|
|
}
|
|
|
|
const { id } = req.query;
|
|
|
|
if (req.method === 'GET') {
|
|
// GET requests use the permission from middleware
|
|
const collection = req.permission.collection;
|
|
const userRole = req.permission.role;
|
|
|
|
try {
|
|
// Get detailed collection info with creator
|
|
const collectionResult = await sql`
|
|
SELECT
|
|
c.*,
|
|
u.email as creator_email
|
|
FROM collections c
|
|
LEFT JOIN users u ON c.user_id = u.id
|
|
WHERE c.id = ${id}
|
|
`;
|
|
|
|
const collectionDetails = collectionResult.rows[0];
|
|
|
|
// Get cards in the collection
|
|
const cardsResult = await sql`
|
|
SELECT
|
|
cc.*,
|
|
cards.name,
|
|
cards.set_name,
|
|
cards.rarity,
|
|
cards.card_type,
|
|
cards.image_url,
|
|
cards.market_price
|
|
FROM collection_cards cc
|
|
JOIN cards ON cc.card_id = cards.id
|
|
WHERE cc.collection_id = ${id}
|
|
ORDER BY cc.created_at ASC
|
|
`;
|
|
|
|
const cards = cardsResult.rows;
|
|
|
|
// Calculate collection stats
|
|
const totalCards = cards.reduce((sum, card) => sum + card.quantity, 0);
|
|
const totalValue = cards.reduce((sum, card) => sum + (card.market_price * card.quantity), 0);
|
|
|
|
res.status(200).json({
|
|
collection: {
|
|
...collectionDetails,
|
|
totalCards,
|
|
totalValue,
|
|
userRole
|
|
},
|
|
cards
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Error fetching collection:', error);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
} else if (req.method === 'PUT') {
|
|
// Update collection - requires editor permissions
|
|
try {
|
|
const { name, description, isPublic } = req.body;
|
|
|
|
const result = await sql`
|
|
UPDATE collections
|
|
SET
|
|
name = ${name},
|
|
description = ${description},
|
|
is_public = ${isPublic},
|
|
updated_at = NOW()
|
|
WHERE id = ${id}
|
|
RETURNING *
|
|
`;
|
|
|
|
// Log activity
|
|
await logCollectionActivity(id, req.user.userId, 'collection_updated', {
|
|
name,
|
|
description,
|
|
isPublic
|
|
});
|
|
|
|
res.status(200).json(result.rows[0]);
|
|
|
|
} catch (error) {
|
|
console.error('Error updating collection:', error);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
} else if (req.method === 'DELETE') {
|
|
// Delete collection - requires owner permissions
|
|
try {
|
|
// Log activity before deletion
|
|
await logCollectionActivity(id, req.user.userId, 'collection_deleted', {});
|
|
|
|
// Delete collection (cascade will handle related records)
|
|
const result = await sql`DELETE FROM collections WHERE id = ${id} RETURNING *`;
|
|
|
|
res.status(200).json({ message: 'Collection deleted successfully' });
|
|
|
|
} catch (error) {
|
|
console.error('Error deleting collection:', error);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
} else {
|
|
res.status(405).json({ error: 'Method not allowed' });
|
|
}
|
|
}
|
|
|
|
// Apply permission middleware based on method
|
|
export default async function(req, res) {
|
|
let requiredPermission = 'viewer'; // Default for GET
|
|
|
|
if (req.method === 'PUT') {
|
|
requiredPermission = 'editor';
|
|
} else if (req.method === 'DELETE') {
|
|
requiredPermission = 'owner';
|
|
}
|
|
|
|
return withCollectionPermission(requiredPermission)(handler)(req, res);
|
|
};
|