Initial marketplace implementation

Full-stack marketplace for buying/selling second-hand items.
React 19 + TypeScript + Tailwind CSS v4 frontend with 17 screens,
Express + Prisma + Socket.io backend, Stripe payments, JWT auth.

Deployed at https://marketplace.173.212.212.157.sslip.io/

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
delta-lynx-89e8
2026-02-22 07:00:44 -08:00
commit b37b734c82
95 changed files with 10921 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
export const CATEGORIES: { value: string; label: string; icon: string }[] = [
{ value: 'ELECTRONICS', label: 'Electronics', icon: 'Smartphone' },
{ value: 'FURNITURE', label: 'Furniture', icon: 'Armchair' },
{ value: 'CLOTHING', label: 'Clothing & Shoes', icon: 'Shirt' },
{ value: 'HOME_GARDEN', label: 'Home & Garden', icon: 'Home' },
{ value: 'SPORTS', label: 'Sports & Outdoors', icon: 'Dumbbell' },
{ value: 'BOOKS', label: 'Books', icon: 'BookOpen' },
{ value: 'GAMES', label: 'Games', icon: 'Gamepad2' },
{ value: 'VEHICLES', label: 'Vehicles', icon: 'Car' },
{ value: 'OTHER', label: 'Other', icon: 'Package' },
];
export const CONDITIONS: { value: string; label: string }[] = [
{ value: 'NEW', label: 'New' },
{ value: 'LIKE_NEW', label: 'Like New' },
{ value: 'GENTLY_USED', label: 'Gently Used' },
{ value: 'USED', label: 'Used' },
{ value: 'FAIR', label: 'Fair' },
];
export const LISTING_FEE = 5;

View File

@@ -0,0 +1,31 @@
export function formatCurrency(amount: number): string {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 0,
maximumFractionDigits: 2,
}).format(amount);
}
export function formatDate(date: string): string {
const d = new Date(date);
const now = new Date();
const diff = now.getTime() - d.getTime();
const minutes = Math.floor(diff / 60000);
const hours = Math.floor(diff / 3600000);
const days = Math.floor(diff / 86400000);
if (minutes < 1) return 'Just now';
if (minutes < 60) return `${minutes}m ago`;
if (hours < 24) return `${hours}h ago`;
if (days < 7) return `${days}d ago`;
return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
}
export function formatDateFull(date: string): string {
return new Date(date).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
});
}

View File

@@ -0,0 +1,368 @@
import type { User, Listing, Offer, Conversation, Message, Notification } from '../types';
export const mockUsers: User[] = [
{
id: '1',
email: 'john@email.com',
fullName: 'John Smith',
nickname: 'JohnS',
avatar: undefined,
phone: '(555) 123-4567',
location: 'Houston, TX',
bio: 'Avid collector and seller of quality pre-owned items.',
rating: 4.8,
createdAt: '2024-01-15T10:00:00Z',
showEmail: false,
showPhone: true,
showLocation: true,
},
{
id: '2',
email: 'mike@email.com',
fullName: 'Mike Brown',
nickname: 'MikeB',
avatar: undefined,
phone: '(555) 987-6543',
location: 'Austin, TX',
bio: 'Looking for great deals on furniture and electronics.',
rating: 4.5,
createdAt: '2024-02-20T10:00:00Z',
showEmail: true,
showPhone: true,
showLocation: true,
},
{
id: '3',
email: 'sarah@email.com',
fullName: 'Sarah Williams',
nickname: 'SarahW',
avatar: undefined,
phone: '(555) 456-7890',
location: 'Dallas, TX',
bio: 'Fashion enthusiast selling vintage finds.',
rating: 4.9,
createdAt: '2024-03-10T10:00:00Z',
showEmail: false,
showPhone: false,
showLocation: true,
},
];
export const mockCurrentUser = mockUsers[0]!;
export const mockListings: Listing[] = [
{
id: '1',
title: 'Leather Armchair',
description: 'Comfortable leather armchair in great condition! Has minimal wear. Perfect for any living room. Rich brown color with brass studs. Selling because I\'m redecorating.',
price: 120,
obo: true,
category: 'FURNITURE',
condition: 'GENTLY_USED',
status: 'ACTIVE',
location: 'Houston, TX',
viewCount: 245,
images: [
{ id: '1', url: '/placeholder-armchair.jpg', order: 0 },
{ id: '2', url: '/placeholder-armchair-2.jpg', order: 1 },
],
seller: mockUsers[0]!,
sellerId: '1',
isFavorited: false,
createdAt: '2024-06-01T10:00:00Z',
updatedAt: '2024-06-01T10:00:00Z',
},
{
id: '2',
title: 'Wireless Headphones',
description: 'Premium wireless headphones with noise cancellation. Battery lasts 30+ hours. Comes with original box and charging cable.',
price: 65,
obo: true,
category: 'ELECTRONICS',
condition: 'LIKE_NEW',
status: 'ACTIVE',
location: 'Austin, TX',
viewCount: 189,
images: [{ id: '3', url: '/placeholder-headphones.jpg', order: 0 }],
seller: mockUsers[1]!,
sellerId: '2',
isFavorited: true,
createdAt: '2024-06-05T10:00:00Z',
updatedAt: '2024-06-05T10:00:00Z',
},
{
id: '3',
title: 'Denim Jacket',
description: 'Classic denim jacket, size M. Barely worn, like new condition. Great for layering in spring and fall.',
price: 32,
obo: false,
category: 'CLOTHING',
condition: 'LIKE_NEW',
status: 'ACTIVE',
location: 'Dallas, TX',
viewCount: 78,
images: [{ id: '4', url: '/placeholder-jacket.jpg', order: 0 }],
seller: mockUsers[2]!,
sellerId: '3',
isFavorited: false,
createdAt: '2024-06-10T10:00:00Z',
updatedAt: '2024-06-10T10:00:00Z',
},
{
id: '4',
title: 'Coffee Maker',
description: 'Programmable 12-cup coffee maker. Works perfectly. Includes reusable filter and carafe.',
price: 45,
obo: true,
category: 'HOME_GARDEN',
condition: 'USED',
status: 'ACTIVE',
location: 'Houston, TX',
viewCount: 134,
images: [{ id: '5', url: '/placeholder-coffee.jpg', order: 0 }],
seller: mockUsers[0]!,
sellerId: '1',
isFavorited: false,
createdAt: '2024-06-15T10:00:00Z',
updatedAt: '2024-06-15T10:00:00Z',
},
{
id: '5',
title: 'Smartwatch',
description: 'Feature-rich smartwatch with heart rate monitor, GPS, and sleep tracking. Compatible with iOS and Android.',
price: 85,
obo: true,
category: 'ELECTRONICS',
condition: 'GENTLY_USED',
status: 'ACTIVE',
location: 'Austin, TX',
viewCount: 312,
images: [{ id: '6', url: '/placeholder-watch.jpg', order: 0 }],
seller: mockUsers[1]!,
sellerId: '2',
isFavorited: true,
createdAt: '2024-06-18T10:00:00Z',
updatedAt: '2024-06-18T10:00:00Z',
},
{
id: '6',
title: 'Designer Handbag',
description: 'Authentic designer handbag in excellent condition. Comes with dust bag and authenticity card.',
price: 220,
obo: true,
category: 'CLOTHING',
condition: 'GENTLY_USED',
status: 'ACTIVE',
location: 'Dallas, TX',
viewCount: 456,
images: [{ id: '7', url: '/placeholder-handbag.jpg', order: 0 }],
seller: mockUsers[2]!,
sellerId: '3',
isFavorited: false,
createdAt: '2024-06-20T10:00:00Z',
updatedAt: '2024-06-20T10:00:00Z',
},
{
id: '7',
title: 'Gaming Laptop',
description: 'High-performance gaming laptop with RTX 3070, 16GB RAM, 512GB SSD. Runs all modern games at high settings.',
price: 1200,
obo: true,
category: 'ELECTRONICS',
condition: 'GENTLY_USED',
status: 'ACTIVE',
location: 'Houston, TX',
viewCount: 567,
images: [{ id: '8', url: '/placeholder-laptop.jpg', order: 0 }],
seller: mockUsers[0]!,
sellerId: '1',
isFavorited: false,
createdAt: '2024-06-22T10:00:00Z',
updatedAt: '2024-06-22T10:00:00Z',
},
{
id: '8',
title: 'Mountain Bike',
description: '21-speed mountain bike, aluminum frame. Recently serviced with new tires and brakes.',
price: 300,
obo: true,
category: 'SPORTS',
condition: 'USED',
status: 'ACTIVE',
location: 'Austin, TX',
viewCount: 223,
images: [{ id: '9', url: '/placeholder-bike.jpg', order: 0 }],
seller: mockUsers[1]!,
sellerId: '2',
isFavorited: false,
createdAt: '2024-06-25T10:00:00Z',
updatedAt: '2024-06-25T10:00:00Z',
},
];
export const mockOffers: Offer[] = [
{
id: '1',
amount: 98,
message: 'Would you accept $98 for the armchair?',
status: 'PENDING',
buyer: mockUsers[1]!,
buyerId: '2',
seller: mockUsers[0]!,
sellerId: '1',
listing: mockListings[0]!,
listingId: '1',
createdAt: '2024-06-20T14:00:00Z',
updatedAt: '2024-06-20T14:00:00Z',
},
{
id: '2',
amount: 50,
message: 'Interested in the headphones. Can you do $50?',
status: 'PENDING',
buyer: mockUsers[0]!,
buyerId: '1',
seller: mockUsers[1]!,
sellerId: '2',
listing: mockListings[1]!,
listingId: '2',
createdAt: '2024-06-21T10:00:00Z',
updatedAt: '2024-06-21T10:00:00Z',
},
{
id: '3',
amount: 180,
message: 'I can pick up today if you accept $180.',
status: 'ACCEPTED',
buyer: mockUsers[1]!,
buyerId: '2',
seller: mockUsers[0]!,
sellerId: '1',
listing: mockListings[6]!,
listingId: '7',
createdAt: '2024-06-22T09:00:00Z',
updatedAt: '2024-06-22T12:00:00Z',
},
{
id: '4',
amount: 35,
message: 'Is the coffee maker still available?',
status: 'DECLINED',
buyer: mockUsers[2]!,
buyerId: '3',
seller: mockUsers[0]!,
sellerId: '1',
listing: mockListings[3]!,
listingId: '4',
createdAt: '2024-06-23T16:00:00Z',
updatedAt: '2024-06-23T18:00:00Z',
},
];
export const mockConversations: Conversation[] = [
{
id: '1',
user1: mockUsers[0]!,
user2: mockUsers[1]!,
listing: mockListings[0],
lastMessage: {
id: 'm1',
content: 'Sounds good! I can do $110. 😊',
senderId: '1',
conversationId: '1',
isRead: true,
createdAt: '2024-06-20T15:30:00Z',
},
unreadCount: 0,
updatedAt: '2024-06-20T15:30:00Z',
},
{
id: '2',
user1: mockUsers[0]!,
user2: mockUsers[2]!,
listing: mockListings[3],
lastMessage: {
id: 'm2',
content: 'Is the coffee maker still available?',
senderId: '3',
conversationId: '2',
isRead: false,
createdAt: '2024-06-21T09:00:00Z',
},
unreadCount: 1,
updatedAt: '2024-06-21T09:00:00Z',
},
];
export const mockMessages: Message[] = [
{
id: 'm1',
content: 'Hi! Would you take $110 for the Leather Armchair? I\'m interested.',
senderId: '2',
conversationId: '1',
isRead: true,
createdAt: '2024-06-20T14:00:00Z',
},
{
id: 'm2',
content: 'Sounds good! I can do $110. 😊',
senderId: '1',
conversationId: '1',
isRead: true,
createdAt: '2024-06-20T15:30:00Z',
},
{
id: 'm3',
content: 'Is the coffee maker still available?',
senderId: '3',
conversationId: '2',
isRead: false,
createdAt: '2024-06-21T09:00:00Z',
},
];
export const mockNotifications: Notification[] = [
{
id: '1',
type: 'NEW_OFFER',
title: 'New Offer',
body: 'JakoTV123 just made an offer for your Nintendo Switch',
data: { listingId: '1', offerId: '1', amount: 180 },
isRead: false,
createdAt: new Date(Date.now() - 3600000).toISOString(),
},
{
id: '2',
type: 'ITEM_SOLD',
title: 'Item Sold',
body: 'You sold your Coffee Maker for $45',
data: { listingId: '4' },
isRead: false,
createdAt: new Date(Date.now() - 7200000).toISOString(),
},
{
id: '3',
type: 'ITEM_FAVORITED',
title: 'Item Favorited',
body: 'Ethan123 loved your Reebok Sneakers',
data: { listingId: '3' },
isRead: true,
createdAt: new Date(Date.now() - 10800000).toISOString(),
},
{
id: '4',
type: 'NEW_OFFER',
title: 'New Offer',
body: 'AlessaJd1 just made an offer for your Mountain Bike',
data: { listingId: '8', offerId: '5', amount: 270 },
isRead: true,
createdAt: new Date(Date.now() - 14400000).toISOString(),
},
];
export const mockSoldItems = [
{ listing: mockListings[0]!, salePrice: 180, buyer: mockUsers[1]!, date: '2024-06-20T12:00:00Z' },
{ listing: mockListings[1]!, salePrice: 70, buyer: mockUsers[2]!, date: '2024-04-02T10:00:00Z' },
{ listing: mockListings[2]!, salePrice: 30, buyer: mockUsers[1]!, date: '2024-03-27T10:00:00Z' },
{ listing: mockListings[3]!, salePrice: 40, buyer: mockUsers[1]!, date: '2024-03-27T10:00:00Z' },
];