import { PrismaClient } from '@prisma/client'; import bcryptjs from 'bcryptjs'; const { hashSync } = bcryptjs; const prisma = new PrismaClient(); async function main() { console.log('Seeding database...'); const passwordHash = hashSync('password123', 10); // ── Clear existing data (reverse dependency order) ────────────────── await prisma.$transaction([ prisma.message.deleteMany(), prisma.conversation.deleteMany(), prisma.notification.deleteMany(), prisma.payment.deleteMany(), prisma.favorite.deleteMany(), prisma.offer.deleteMany(), prisma.listingImage.deleteMany(), prisma.listing.deleteMany(), prisma.blockedUser.deleteMany(), prisma.session.deleteMany(), prisma.user.deleteMany(), ]); console.log('Cleared existing data.'); // ── Users ─────────────────────────────────────────────────────────── const users = await prisma.$transaction([ prisma.user.create({ data: { id: 'user-alice', email: 'alice.chen@example.com', passwordHash, fullName: 'Alice Chen', nickname: 'alice', avatar: '/uploads/avatars/alice.jpg', phone: '(415) 555-0101', location: 'San Francisco, CA', bio: 'Tech enthusiast and avid reader. Always looking for great deals on gadgets and books.', rating: 4.8, ratingCount: 24, showEmail: false, showPhone: true, showLocation: true, showOnline: true, showRating: true, isActive: true, marketingEmail: true, }, }), prisma.user.create({ data: { id: 'user-bob', email: 'bob.martinez@example.com', passwordHash, fullName: 'Bob Martinez', nickname: 'bobby_m', avatar: '/uploads/avatars/bob.jpg', phone: '(213) 555-0202', location: 'Los Angeles, CA', bio: 'Sports memorabilia collector and weekend warrior. Selling gear I no longer use.', rating: 4.5, ratingCount: 18, showEmail: true, showPhone: true, showLocation: true, showOnline: true, showRating: true, isActive: true, }, }), prisma.user.create({ data: { id: 'user-carol', email: 'carol.nguyen@example.com', passwordHash, fullName: 'Carol Nguyen', nickname: 'carol_n', avatar: '/uploads/avatars/carol.jpg', phone: '(503) 555-0303', location: 'Portland, OR', bio: 'Interior designer with an eye for vintage furniture. Downsizing my collection.', rating: 4.9, ratingCount: 32, showEmail: false, showPhone: false, showLocation: true, showOnline: false, showRating: true, isActive: true, twoFactorEnabled: true, }, }), prisma.user.create({ data: { id: 'user-david', email: 'david.kim@example.com', passwordHash, fullName: 'David Kim', nickname: 'dave_k', avatar: '/uploads/avatars/david.jpg', phone: '(312) 555-0404', location: 'Chicago, IL', bio: 'Gamer and music lover. Clearing out my shelves to make room for new hobbies.', rating: 4.2, ratingCount: 11, showEmail: false, showPhone: true, showLocation: true, showOnline: true, showRating: true, isActive: true, }, }), prisma.user.create({ data: { id: 'user-eva', email: 'eva.johnson@example.com', passwordHash, fullName: 'Eva Johnson', nickname: 'eva_j', avatar: '/uploads/avatars/eva.jpg', phone: '(206) 555-0505', location: 'Seattle, WA', bio: 'Outdoor enthusiast and plant mom. Selling items that need a new home.', rating: 4.7, ratingCount: 15, showEmail: true, showPhone: true, showLocation: true, showOnline: true, showRating: true, isActive: true, notifFavorites: false, }, }), ]); console.log(`Created ${users.length} users.`); // ── Listings ──────────────────────────────────────────────────────── const listingsData = [ // ACTIVE listings (10) { id: 'listing-01', title: 'MacBook Pro 14" M3 Pro — 18GB RAM', description: 'Barely used MacBook Pro 14-inch with M3 Pro chip, 18GB unified memory, 512GB SSD. Includes original charger and box. AppleCare+ until March 2027. No scratches or dents, always used with a case.', price: 1650.00, obo: true, category: 'ELECTRONICS' as const, condition: 'LIKE_NEW' as const, status: 'ACTIVE' as const, location: 'San Francisco, CA', viewCount: 142, sellerId: 'user-alice', }, { id: 'listing-02', title: 'Mid-Century Modern Walnut Coffee Table', description: 'Beautiful solid walnut coffee table with tapered legs. Dimensions: 48"L x 24"W x 16"H. Minor surface wear consistent with age. A real statement piece for any living room.', price: 320.00, obo: true, category: 'FURNITURE' as const, condition: 'GENTLY_USED' as const, status: 'ACTIVE' as const, location: 'Portland, OR', viewCount: 87, sellerId: 'user-carol', }, { id: 'listing-03', title: 'Patagonia Better Sweater Jacket — Men\'s Large', description: 'Classic Patagonia fleece jacket in "New Navy" colorway, size Large. Worn a handful of times, no pilling or stains. Great for layering or casual wear.', price: 75.00, obo: false, category: 'CLOTHING' as const, condition: 'LIKE_NEW' as const, status: 'ACTIVE' as const, location: 'Seattle, WA', viewCount: 63, sellerId: 'user-eva', }, { id: 'listing-04', title: 'Weber Spirit II E-310 Gas Grill', description: 'Three-burner propane grill with side tables. Used for two summers. GS4 grilling system, porcelain-enameled cast-iron grates. Includes cover and propane tank. Pickup only.', price: 280.00, obo: true, category: 'HOME_GARDEN' as const, condition: 'USED' as const, status: 'ACTIVE' as const, location: 'Chicago, IL', viewCount: 54, sellerId: 'user-david', }, { id: 'listing-05', title: 'Trek Marlin 7 Mountain Bike — Size M', description: '2024 Trek Marlin 7 in Matte Nautical Navy. Size Medium (fits 5\'5"–5\'9"). Shimano Deore 1x10 drivetrain, RockShox Judy fork. About 300 miles on it. Ready to ride.', price: 620.00, obo: true, category: 'SPORTS' as const, condition: 'GENTLY_USED' as const, status: 'ACTIVE' as const, location: 'Portland, OR', viewCount: 109, sellerId: 'user-carol', }, { id: 'listing-06', title: 'Complete Dune Series (6 Books, Hardcover)', description: 'The original six Dune novels by Frank Herbert in hardcover. Ace/Putnam editions with dust jackets. All in very good condition — no writing, highlighting, or torn pages.', price: 95.00, obo: false, category: 'BOOKS' as const, condition: 'GENTLY_USED' as const, status: 'ACTIVE' as const, location: 'San Francisco, CA', viewCount: 76, sellerId: 'user-alice', }, { id: 'listing-07', title: 'PlayStation 5 Disc Edition + 4 Games', description: 'PS5 Disc Edition console with two DualSense controllers, charging dock, and four games: Spider-Man 2, FF7 Rebirth, Elden Ring, and Baldur\'s Gate 3. Everything works perfectly.', price: 420.00, obo: true, category: 'GAMES' as const, condition: 'GENTLY_USED' as const, status: 'ACTIVE' as const, location: 'Chicago, IL', viewCount: 203, sellerId: 'user-david', }, { id: 'listing-08', title: 'Sony WH-1000XM5 Wireless Headphones', description: 'Industry-leading noise cancelling headphones in black. Includes carrying case, USB-C cable, and audio cable. Battery still holds 30+ hours. Ear pads in excellent shape.', price: 199.00, obo: false, category: 'ELECTRONICS' as const, condition: 'LIKE_NEW' as const, status: 'ACTIVE' as const, location: 'Los Angeles, CA', viewCount: 91, sellerId: 'user-bob', }, { id: 'listing-09', title: 'Dyson V12 Detect Slim Cordless Vacuum', description: 'Powerful cordless stick vacuum with laser dust detection. Comes with all original attachments and wall-mount dock. About 1 year old, works flawlessly.', price: 340.00, obo: true, category: 'HOME_GARDEN' as const, condition: 'GENTLY_USED' as const, status: 'ACTIVE' as const, location: 'Seattle, WA', viewCount: 68, sellerId: 'user-eva', }, { id: 'listing-10', title: 'Vintage Schwinn Varsity Road Bike', description: '1975 Schwinn Varsity 10-speed in original Kool Lemon color. All-original components, rides well. Some cosmetic patina adds character. A classic commuter or collector piece.', price: 250.00, obo: true, category: 'VEHICLES' as const, condition: 'FAIR' as const, status: 'ACTIVE' as const, location: 'Los Angeles, CA', viewCount: 45, sellerId: 'user-bob', }, // SOLD listings (3) { id: 'listing-11', title: 'Nintendo Switch OLED + Pro Controller', description: 'White Nintendo Switch OLED model with Pro Controller, dock, and carrying case. Screen is pristine. Includes 3 physical game cartridges.', price: 300.00, obo: false, category: 'GAMES' as const, condition: 'GENTLY_USED' as const, status: 'SOLD' as const, location: 'San Francisco, CA', viewCount: 178, sellerId: 'user-alice', }, { id: 'listing-12', title: 'Herman Miller Aeron Chair — Size B', description: 'Fully loaded Aeron chair in graphite. Size B (medium). PostureFit SL, adjustable arms, tilt limiter. Purchased from authorized dealer in 2022. 12-year warranty still active.', price: 750.00, obo: true, category: 'FURNITURE' as const, condition: 'LIKE_NEW' as const, status: 'SOLD' as const, location: 'Portland, OR', viewCount: 256, sellerId: 'user-carol', }, { id: 'listing-13', title: 'Canon EOS R6 Mark II Body Only', description: 'Professional mirrorless camera body. 24.2MP full-frame sensor, 4K 60fps video, IBIS. Shutter count under 5,000. Includes battery, charger, and strap. No box.', price: 1800.00, obo: true, category: 'ELECTRONICS' as const, condition: 'LIKE_NEW' as const, status: 'SOLD' as const, location: 'Chicago, IL', viewCount: 312, sellerId: 'user-david', }, // DRAFT listings (2) { id: 'listing-14', title: 'Garmin Fenix 7X Solar Smartwatch', description: 'Multi-sport GPS watch with solar charging. Titanium bezel, sapphire lens. Includes QuickFit bands. Still drafting — need to take photos.', price: 450.00, obo: true, category: 'ELECTRONICS' as const, condition: 'GENTLY_USED' as const, status: 'DRAFT' as const, location: 'Seattle, WA', viewCount: 0, sellerId: 'user-eva', }, { id: 'listing-15', title: 'Restoration Hardware Cloud Sofa — 2 Seat', description: 'RH Cloud sofa in white perennials fabric. Extremely comfortable. Moving and cannot bring it. Will update with measurements and photos shortly.', price: 2200.00, obo: true, category: 'FURNITURE' as const, condition: 'USED' as const, status: 'DRAFT' as const, location: 'Los Angeles, CA', viewCount: 0, sellerId: 'user-bob', }, ]; const listings = await prisma.$transaction( listingsData.map((l) => prisma.listing.create({ data: l })) ); console.log(`Created ${listings.length} listings.`); // ── Listing Images ────────────────────────────────────────────────── const imageRecords = [ { listingId: 'listing-01', uploadedBy: 'user-alice', url: '/uploads/placeholder-1.jpg', order: 0 }, { listingId: 'listing-01', uploadedBy: 'user-alice', url: '/uploads/placeholder-2.jpg', order: 1 }, { listingId: 'listing-02', uploadedBy: 'user-carol', url: '/uploads/placeholder-3.jpg', order: 0 }, { listingId: 'listing-02', uploadedBy: 'user-carol', url: '/uploads/placeholder-4.jpg', order: 1 }, { listingId: 'listing-03', uploadedBy: 'user-eva', url: '/uploads/placeholder-5.jpg', order: 0 }, { listingId: 'listing-04', uploadedBy: 'user-david', url: '/uploads/placeholder-6.jpg', order: 0 }, { listingId: 'listing-04', uploadedBy: 'user-david', url: '/uploads/placeholder-7.jpg', order: 1 }, { listingId: 'listing-05', uploadedBy: 'user-carol', url: '/uploads/placeholder-8.jpg', order: 0 }, { listingId: 'listing-05', uploadedBy: 'user-carol', url: '/uploads/placeholder-9.jpg', order: 1 }, { listingId: 'listing-06', uploadedBy: 'user-alice', url: '/uploads/placeholder-10.jpg', order: 0 }, { listingId: 'listing-07', uploadedBy: 'user-david', url: '/uploads/placeholder-11.jpg', order: 0 }, { listingId: 'listing-07', uploadedBy: 'user-david', url: '/uploads/placeholder-12.jpg', order: 1 }, { listingId: 'listing-08', uploadedBy: 'user-bob', url: '/uploads/placeholder-13.jpg', order: 0 }, { listingId: 'listing-09', uploadedBy: 'user-eva', url: '/uploads/placeholder-14.jpg', order: 0 }, { listingId: 'listing-10', uploadedBy: 'user-bob', url: '/uploads/placeholder-15.jpg', order: 0 }, { listingId: 'listing-10', uploadedBy: 'user-bob', url: '/uploads/placeholder-16.jpg', order: 1 }, { listingId: 'listing-11', uploadedBy: 'user-alice', url: '/uploads/placeholder-17.jpg', order: 0 }, { listingId: 'listing-12', uploadedBy: 'user-carol', url: '/uploads/placeholder-18.jpg', order: 0 }, { listingId: 'listing-12', uploadedBy: 'user-carol', url: '/uploads/placeholder-19.jpg', order: 1 }, { listingId: 'listing-13', uploadedBy: 'user-david', url: '/uploads/placeholder-20.jpg', order: 0 }, { listingId: 'listing-13', uploadedBy: 'user-david', url: '/uploads/placeholder-21.jpg', order: 1 }, ]; const images = await prisma.$transaction( imageRecords.map((img) => prisma.listingImage.create({ data: img })) ); console.log(`Created ${images.length} listing images.`); // ── Offers ────────────────────────────────────────────────────────── const now = new Date(); const inOneWeek = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000); const daysAgo = (d: number) => new Date(now.getTime() - d * 24 * 60 * 60 * 1000); const offersData = [ // ACCEPTED offers on SOLD listings { id: 'offer-01', amount: 275.00, message: 'Would you take $275? I can pick it up today.', status: 'ACCEPTED' as const, buyerId: 'user-bob', sellerId: 'user-alice', listingId: 'listing-11', expiresAt: daysAgo(-2), createdAt: daysAgo(10), }, { id: 'offer-02', amount: 680.00, message: 'Beautiful chair! I have been looking for one of these. Would $680 work?', status: 'ACCEPTED' as const, counterAmount: 720.00, buyerId: 'user-eva', sellerId: 'user-carol', listingId: 'listing-12', expiresAt: daysAgo(-1), createdAt: daysAgo(14), }, { id: 'offer-03', amount: 1700.00, message: 'Interested in the R6 II. Offering $1700 — can meet anywhere in Chicago.', status: 'ACCEPTED' as const, buyerId: 'user-alice', sellerId: 'user-david', listingId: 'listing-13', expiresAt: daysAgo(-3), createdAt: daysAgo(7), }, // PENDING offers { id: 'offer-04', amount: 1500.00, message: 'Is there any flexibility on price? I see similar configs going for around $1500.', status: 'PENDING' as const, buyerId: 'user-david', sellerId: 'user-alice', listingId: 'listing-01', expiresAt: inOneWeek, createdAt: daysAgo(1), }, { id: 'offer-05', amount: 380.00, message: 'Great deal on the PS5 bundle. Would you accept $380?', status: 'PENDING' as const, buyerId: 'user-carol', sellerId: 'user-david', listingId: 'listing-07', expiresAt: inOneWeek, createdAt: daysAgo(2), }, { id: 'offer-06', amount: 550.00, message: 'Love this bike! Would you consider $550?', status: 'PENDING' as const, buyerId: 'user-alice', sellerId: 'user-carol', listingId: 'listing-05', expiresAt: inOneWeek, createdAt: daysAgo(1), }, // DECLINED offers { id: 'offer-07', amount: 200.00, message: 'I can offer $200 for the coffee table.', status: 'DECLINED' as const, buyerId: 'user-david', sellerId: 'user-carol', listingId: 'listing-02', expiresAt: daysAgo(1), createdAt: daysAgo(5), }, { id: 'offer-08', amount: 150.00, message: 'Budget is tight but really want those headphones. $150?', status: 'DECLINED' as const, buyerId: 'user-eva', sellerId: 'user-bob', listingId: 'listing-08', expiresAt: daysAgo(2), createdAt: daysAgo(8), }, // COUNTERED offers { id: 'offer-09', amount: 250.00, message: 'Interested in the grill. How about $250?', status: 'COUNTERED' as const, counterAmount: 270.00, buyerId: 'user-alice', sellerId: 'user-david', listingId: 'listing-04', expiresAt: inOneWeek, createdAt: daysAgo(3), }, { id: 'offer-10', amount: 290.00, message: 'Would $290 work for the vacuum? I am in the Ballard area and can pick up.', status: 'COUNTERED' as const, counterAmount: 310.00, buyerId: 'user-bob', sellerId: 'user-eva', listingId: 'listing-09', expiresAt: inOneWeek, createdAt: daysAgo(2), }, ]; const offers = await prisma.$transaction( offersData.map((o) => prisma.offer.create({ data: o })) ); console.log(`Created ${offers.length} offers.`); // ── Conversations & Messages ──────────────────────────────────────── const conv1 = await prisma.conversation.create({ data: { id: 'conv-01', user1Id: 'user-bob', user2Id: 'user-alice', listingId: 'listing-11', }, }); const conv2 = await prisma.conversation.create({ data: { id: 'conv-02', user1Id: 'user-eva', user2Id: 'user-carol', listingId: 'listing-12', }, }); const conv3 = await prisma.conversation.create({ data: { id: 'conv-03', user1Id: 'user-david', user2Id: 'user-alice', listingId: 'listing-01', }, }); const conv4 = await prisma.conversation.create({ data: { id: 'conv-04', user1Id: 'user-alice', user2Id: 'user-david', listingId: 'listing-04', }, }); console.log('Created 4 conversations.'); const messagesData = [ // Conv 1: Bob buying Switch from Alice (completed sale) { content: 'Hi! Is the Switch still available?', senderId: 'user-bob', conversationId: 'conv-01', isRead: true, createdAt: daysAgo(11) }, { content: 'Yes it is! Everything works great.', senderId: 'user-alice', conversationId: 'conv-01', isRead: true, createdAt: daysAgo(11) }, { content: 'Awesome. Would you take $275 for it?', senderId: 'user-bob', conversationId: 'conv-01', isRead: true, offerAmount: 275.00, createdAt: daysAgo(10) }, { content: 'That works for me. When can you pick up?', senderId: 'user-alice', conversationId: 'conv-01', isRead: true, createdAt: daysAgo(10) }, { content: 'How about Saturday around noon? I can come to the Mission district.', senderId: 'user-bob', conversationId: 'conv-01', isRead: true, createdAt: daysAgo(9) }, { content: 'Perfect. I will DM you the exact address on Saturday morning.', senderId: 'user-alice', conversationId: 'conv-01', isRead: true, createdAt: daysAgo(9) }, { content: 'Picked it up — everything looks great. Thanks Alice!', senderId: 'user-bob', conversationId: 'conv-01', isRead: true, createdAt: daysAgo(7) }, // Conv 2: Eva buying Aeron from Carol (completed sale) { content: 'Hi Carol, love the Aeron listing! Is the warranty transferable?', senderId: 'user-eva', conversationId: 'conv-02', isRead: true, createdAt: daysAgo(15) }, { content: 'Hi Eva! Yes, Herman Miller warranties follow the chair, not the owner.', senderId: 'user-carol', conversationId: 'conv-02', isRead: true, createdAt: daysAgo(15) }, { content: 'Great to know. Would $680 be fair? I see refurbs going for about that.', senderId: 'user-eva', conversationId: 'conv-02', isRead: true, offerAmount: 680.00, createdAt: daysAgo(14) }, { content: 'This is from an authorized dealer though. How about we meet at $720?', senderId: 'user-carol', conversationId: 'conv-02', isRead: true, offerAmount: 720.00, createdAt: daysAgo(14) }, { content: '$720 is fair. Deal! Can you ship to Seattle or is it pickup only?', senderId: 'user-eva', conversationId: 'conv-02', isRead: true, createdAt: daysAgo(13) }, { content: 'I can ship via UPS freight. I will factor that into the price — still $720 total.', senderId: 'user-carol', conversationId: 'conv-02', isRead: true, createdAt: daysAgo(13) }, { content: 'That is incredibly generous. Sending payment now.', senderId: 'user-eva', conversationId: 'conv-02', isRead: true, createdAt: daysAgo(12) }, { content: 'Payment received! Shipping Monday. Will send tracking.', senderId: 'user-carol', conversationId: 'conv-02', isRead: true, createdAt: daysAgo(12) }, // Conv 3: David interested in MacBook from Alice (ongoing) { content: 'Hey, quick question about the MacBook — what is the battery cycle count?', senderId: 'user-david', conversationId: 'conv-03', isRead: true, createdAt: daysAgo(3) }, { content: 'Let me check... 47 cycles. Basically brand new battery.', senderId: 'user-alice', conversationId: 'conv-03', isRead: true, createdAt: daysAgo(3) }, { content: 'Nice. Any issues with the keyboard or trackpad?', senderId: 'user-david', conversationId: 'conv-03', isRead: true, createdAt: daysAgo(2) }, { content: 'Nope, everything is perfect. I mostly used it with an external display and keyboard.', senderId: 'user-alice', conversationId: 'conv-03', isRead: true, createdAt: daysAgo(2) }, { content: 'I submitted an offer for $1500 — let me know what you think.', senderId: 'user-david', conversationId: 'conv-03', isRead: true, offerAmount: 1500.00, createdAt: daysAgo(1) }, { content: 'I will take a look. That is a bit lower than I was hoping for but let me think about it.', senderId: 'user-alice', conversationId: 'conv-03', isRead: false, createdAt: daysAgo(0) }, // Conv 4: Alice interested in grill from David (ongoing) { content: 'Hi David! Does the grill come with the cover shown in the photo?', senderId: 'user-alice', conversationId: 'conv-04', isRead: true, createdAt: daysAgo(4) }, { content: 'Yes, the cover and propane tank are both included.', senderId: 'user-david', conversationId: 'conv-04', isRead: true, createdAt: daysAgo(4) }, { content: 'I put in an offer for $250. Let me know if that works.', senderId: 'user-alice', conversationId: 'conv-04', isRead: true, offerAmount: 250.00, createdAt: daysAgo(3) }, { content: 'Appreciate the offer! Could you do $270? I paid $500 new last year.', senderId: 'user-david', conversationId: 'conv-04', isRead: true, offerAmount: 270.00, createdAt: daysAgo(3) }, { content: 'Let me think on it and get back to you.', senderId: 'user-alice', conversationId: 'conv-04', isRead: false, createdAt: daysAgo(2) }, ]; const messages = await prisma.$transaction( messagesData.map((m) => prisma.message.create({ data: m })) ); console.log(`Created ${messages.length} messages.`); // ── Favorites ─────────────────────────────────────────────────────── const favoritesData = [ { userId: 'user-bob', listingId: 'listing-01' }, { userId: 'user-bob', listingId: 'listing-06' }, { userId: 'user-carol', listingId: 'listing-07' }, { userId: 'user-carol', listingId: 'listing-08' }, { userId: 'user-david', listingId: 'listing-02' }, { userId: 'user-david', listingId: 'listing-05' }, { userId: 'user-eva', listingId: 'listing-01' }, { userId: 'user-eva', listingId: 'listing-07' }, { userId: 'user-alice', listingId: 'listing-04' }, { userId: 'user-alice', listingId: 'listing-05' }, { userId: 'user-alice', listingId: 'listing-08' }, ]; const favorites = await prisma.$transaction( favoritesData.map((f) => prisma.favorite.create({ data: f })) ); console.log(`Created ${favorites.length} favorites.`); // ── Notifications ─────────────────────────────────────────────────── const notificationsData = [ // NEW_OFFER { userId: 'user-alice', type: 'NEW_OFFER' as const, title: 'New Offer Received', body: 'David Kim offered $1,500 for your MacBook Pro 14" M3 Pro.', data: { offerId: 'offer-04', listingId: 'listing-01' }, isRead: false, createdAt: daysAgo(1), }, { userId: 'user-david', type: 'NEW_OFFER' as const, title: 'New Offer Received', body: 'Carol Nguyen offered $380 for your PlayStation 5 Disc Edition.', data: { offerId: 'offer-05', listingId: 'listing-07' }, isRead: true, createdAt: daysAgo(2), }, // OFFER_ACCEPTED { userId: 'user-bob', type: 'OFFER_ACCEPTED' as const, title: 'Offer Accepted!', body: 'Alice Chen accepted your offer of $275 for the Nintendo Switch OLED.', data: { offerId: 'offer-01', listingId: 'listing-11' }, isRead: true, createdAt: daysAgo(10), }, { userId: 'user-alice', type: 'OFFER_ACCEPTED' as const, title: 'Offer Accepted!', body: 'David Kim accepted your offer of $1,700 for the Canon EOS R6 Mark II.', data: { offerId: 'offer-03', listingId: 'listing-13' }, isRead: true, createdAt: daysAgo(7), }, // OFFER_DECLINED { userId: 'user-david', type: 'OFFER_DECLINED' as const, title: 'Offer Declined', body: 'Carol Nguyen declined your offer of $200 for the Mid-Century Coffee Table.', data: { offerId: 'offer-07', listingId: 'listing-02' }, isRead: true, createdAt: daysAgo(5), }, { userId: 'user-eva', type: 'OFFER_DECLINED' as const, title: 'Offer Declined', body: 'Bob Martinez declined your offer of $150 for the Sony WH-1000XM5.', data: { offerId: 'offer-08', listingId: 'listing-08' }, isRead: false, createdAt: daysAgo(8), }, // ITEM_SOLD { userId: 'user-alice', type: 'ITEM_SOLD' as const, title: 'Item Sold!', body: 'Your Nintendo Switch OLED has been sold for $275.', data: { listingId: 'listing-11', amount: 275 }, isRead: true, createdAt: daysAgo(9), }, { userId: 'user-carol', type: 'ITEM_SOLD' as const, title: 'Item Sold!', body: 'Your Herman Miller Aeron Chair has been sold for $720.', data: { listingId: 'listing-12', amount: 720 }, isRead: true, createdAt: daysAgo(12), }, // NEW_MESSAGE { userId: 'user-alice', type: 'NEW_MESSAGE' as const, title: 'New Message', body: 'David Kim sent you a message about MacBook Pro 14" M3 Pro.', data: { conversationId: 'conv-03', listingId: 'listing-01' }, isRead: true, createdAt: daysAgo(3), }, { userId: 'user-david', type: 'NEW_MESSAGE' as const, title: 'New Message', body: 'Alice Chen sent you a message about Weber Spirit II Gas Grill.', data: { conversationId: 'conv-04', listingId: 'listing-04' }, isRead: false, createdAt: daysAgo(2), }, // ITEM_FAVORITED { userId: 'user-alice', type: 'ITEM_FAVORITED' as const, title: 'Item Favorited', body: 'Someone favorited your MacBook Pro 14" M3 Pro listing.', data: { listingId: 'listing-01' }, isRead: false, createdAt: daysAgo(1), }, { userId: 'user-carol', type: 'ITEM_FAVORITED' as const, title: 'Item Favorited', body: 'Someone favorited your Trek Marlin 7 Mountain Bike listing.', data: { listingId: 'listing-05' }, isRead: true, createdAt: daysAgo(4), }, ]; const notifications = await prisma.$transaction( notificationsData.map((n) => prisma.notification.create({ data: n })) ); console.log(`Created ${notifications.length} notifications.`); // ── Payments (3 completed for sold items) ─────────────────────────── const paymentsData = [ { userId: 'user-bob', listingId: 'listing-11', stripePaymentId: 'pi_3PxSwitch001', amount: 275.00, status: 'COMPLETED' as const, createdAt: daysAgo(9), }, { userId: 'user-eva', listingId: 'listing-12', stripePaymentId: 'pi_3PxAeron002', amount: 720.00, status: 'COMPLETED' as const, createdAt: daysAgo(12), }, { userId: 'user-alice', listingId: 'listing-13', stripePaymentId: 'pi_3PxCanon003', amount: 1700.00, status: 'COMPLETED' as const, createdAt: daysAgo(6), }, ]; const payments = await prisma.$transaction( paymentsData.map((p) => prisma.payment.create({ data: p })) ); console.log(`Created ${payments.length} payments.`); // ── Blocked Users ─────────────────────────────────────────────────── await prisma.blockedUser.create({ data: { blockerId: 'user-carol', blockedId: 'user-david', }, }); console.log('Created 1 blocked user entry.'); console.log('\nSeed completed successfully!'); } main() .catch((e) => { console.error('Seed failed:', e); process.exit(1); }) .finally(async () => { await prisma.$disconnect(); });