localgreenchain/lib/marketplace/store.ts
Claude b3c2af51bf
Implement marketplace foundation (Agent 9)
Add comprehensive plant trading marketplace with:
- Prisma schema with marketplace models (Listing, Offer, SellerProfile, WishlistItem)
- Service layer for listings, offers, search, and matching
- API endpoints for CRUD operations, search, and recommendations
- Marketplace pages: home, listing detail, create, my-listings, my-offers
- Reusable UI components: ListingCard, ListingGrid, OfferForm, SearchFilters, etc.

Features:
- Browse and search listings by category, price, tags
- Create and manage listings (draft, active, sold, cancelled)
- Make and manage offers on listings
- Seller and buyer views with statistics
- Featured and recommended listings
- In-memory store (ready for database migration via Agent 2)
2025-11-23 03:58:08 +00:00

273 lines
8.7 KiB
TypeScript

// In-Memory Store for Marketplace
// This will be replaced with Prisma database calls once Agent 2 completes database setup
import {
Listing,
Offer,
SellerProfile,
WishlistItem,
ListingCategory,
ListingStatus,
OfferStatus,
} from './types';
// In-memory storage
const listings: Map<string, Listing> = new Map();
const offers: Map<string, Offer> = new Map();
const sellerProfiles: Map<string, SellerProfile> = new Map();
const wishlistItems: Map<string, WishlistItem> = new Map();
// Helper to generate IDs
export const generateId = (): string => {
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
};
// Seed with sample data for development
const seedSampleData = () => {
const sampleListings: Listing[] = [
{
id: 'listing-1',
sellerId: 'user-1',
sellerName: 'Green Thumb Gardens',
title: 'Heirloom Tomato Seedlings - Cherokee Purple',
description: 'Beautiful heirloom Cherokee Purple tomato seedlings, organically grown. These produce large, deep purple-red fruits with rich, complex flavor. Perfect for home gardens.',
price: 4.99,
currency: 'USD',
quantity: 24,
category: ListingCategory.SEEDLINGS,
status: ListingStatus.ACTIVE,
location: { lat: 40.7128, lng: -74.006, city: 'New York', region: 'NY' },
tags: ['organic', 'heirloom', 'tomato', 'vegetable'],
images: [
{ id: 'img-1', listingId: 'listing-1', url: '/images/tomato-seedling.jpg', alt: 'Cherokee Purple Tomato Seedling', isPrimary: true, createdAt: new Date() }
],
viewCount: 142,
createdAt: new Date('2024-03-01'),
updatedAt: new Date('2024-03-15'),
},
{
id: 'listing-2',
sellerId: 'user-2',
sellerName: 'Urban Herb Farm',
title: 'Fresh Basil Plants - Genovese',
description: 'Ready-to-harvest Genovese basil plants grown in our vertical farm. Perfect for pesto, salads, and Italian cuisine. Each plant is 6-8 inches tall.',
price: 6.50,
currency: 'USD',
quantity: 50,
category: ListingCategory.MATURE_PLANTS,
status: ListingStatus.ACTIVE,
location: { lat: 34.0522, lng: -118.2437, city: 'Los Angeles', region: 'CA' },
tags: ['herbs', 'basil', 'culinary', 'fresh'],
images: [],
viewCount: 89,
createdAt: new Date('2024-03-10'),
updatedAt: new Date('2024-03-10'),
},
{
id: 'listing-3',
sellerId: 'user-1',
sellerName: 'Green Thumb Gardens',
title: 'Organic Lettuce Mix Seeds',
description: 'Premium mix of organic lettuce seeds including romaine, butterhead, and red leaf varieties. Perfect for succession planting.',
price: 3.99,
currency: 'USD',
quantity: 100,
category: ListingCategory.SEEDS,
status: ListingStatus.ACTIVE,
location: { lat: 40.7128, lng: -74.006, city: 'New York', region: 'NY' },
tags: ['organic', 'seeds', 'lettuce', 'salad'],
images: [],
viewCount: 256,
createdAt: new Date('2024-02-15'),
updatedAt: new Date('2024-03-01'),
},
{
id: 'listing-4',
sellerId: 'user-3',
sellerName: 'Succulent Paradise',
title: 'Assorted Succulent Cuttings - 10 Pack',
description: 'Beautiful assortment of succulent cuttings ready for propagation. Includes echeveria, sedum, and crassula varieties.',
price: 15.00,
currency: 'USD',
quantity: 30,
category: ListingCategory.CUTTINGS,
status: ListingStatus.ACTIVE,
location: { lat: 33.4484, lng: -112.074, city: 'Phoenix', region: 'AZ' },
tags: ['succulents', 'cuttings', 'propagation', 'drought-tolerant'],
images: [],
viewCount: 178,
createdAt: new Date('2024-03-05'),
updatedAt: new Date('2024-03-12'),
},
{
id: 'listing-5',
sellerId: 'user-2',
sellerName: 'Urban Herb Farm',
title: 'Fresh Microgreens - Chef\'s Mix',
description: 'Freshly harvested microgreens mix including sunflower, radish, and pea shoots. Harvested same day as shipping for maximum freshness.',
price: 8.99,
currency: 'USD',
quantity: 40,
category: ListingCategory.PRODUCE,
status: ListingStatus.ACTIVE,
location: { lat: 34.0522, lng: -118.2437, city: 'Los Angeles', region: 'CA' },
tags: ['microgreens', 'fresh', 'produce', 'chef'],
images: [],
viewCount: 312,
createdAt: new Date('2024-03-18'),
updatedAt: new Date('2024-03-18'),
},
];
const sampleProfiles: SellerProfile[] = [
{
userId: 'user-1',
displayName: 'Green Thumb Gardens',
bio: 'Family-owned nursery specializing in heirloom vegetables and native plants.',
location: { city: 'New York', region: 'NY' },
rating: 4.8,
reviewCount: 127,
totalSales: 523,
memberSince: new Date('2023-01-15'),
verified: true,
},
{
userId: 'user-2',
displayName: 'Urban Herb Farm',
bio: 'Vertical farm growing fresh herbs and microgreens in the heart of LA.',
location: { city: 'Los Angeles', region: 'CA' },
rating: 4.9,
reviewCount: 89,
totalSales: 412,
memberSince: new Date('2023-03-20'),
verified: true,
},
{
userId: 'user-3',
displayName: 'Succulent Paradise',
bio: 'Desert plant enthusiast sharing the beauty of succulents.',
location: { city: 'Phoenix', region: 'AZ' },
rating: 4.7,
reviewCount: 56,
totalSales: 198,
memberSince: new Date('2023-06-01'),
verified: false,
},
];
// Seed listings
sampleListings.forEach(listing => {
listings.set(listing.id, listing);
});
// Seed profiles
sampleProfiles.forEach(profile => {
sellerProfiles.set(profile.userId, profile);
});
};
// Initialize with sample data
seedSampleData();
// Export store operations
export const listingStore = {
getAll: (): Listing[] => Array.from(listings.values()),
getById: (id: string): Listing | undefined => listings.get(id),
getBySellerId: (sellerId: string): Listing[] =>
Array.from(listings.values()).filter(l => l.sellerId === sellerId),
create: (listing: Listing): Listing => {
listings.set(listing.id, listing);
return listing;
},
update: (id: string, updates: Partial<Listing>): Listing | undefined => {
const existing = listings.get(id);
if (!existing) return undefined;
const updated = { ...existing, ...updates, updatedAt: new Date() };
listings.set(id, updated);
return updated;
},
delete: (id: string): boolean => listings.delete(id),
incrementViewCount: (id: string): void => {
const listing = listings.get(id);
if (listing) {
listing.viewCount++;
listings.set(id, listing);
}
},
};
export const offerStore = {
getAll: (): Offer[] => Array.from(offers.values()),
getById: (id: string): Offer | undefined => offers.get(id),
getByListingId: (listingId: string): Offer[] =>
Array.from(offers.values()).filter(o => o.listingId === listingId),
getByBuyerId: (buyerId: string): Offer[] =>
Array.from(offers.values()).filter(o => o.buyerId === buyerId),
create: (offer: Offer): Offer => {
offers.set(offer.id, offer);
return offer;
},
update: (id: string, updates: Partial<Offer>): Offer | undefined => {
const existing = offers.get(id);
if (!existing) return undefined;
const updated = { ...existing, ...updates, updatedAt: new Date() };
offers.set(id, updated);
return updated;
},
delete: (id: string): boolean => offers.delete(id),
};
export const sellerProfileStore = {
getByUserId: (userId: string): SellerProfile | undefined =>
sellerProfiles.get(userId),
create: (profile: SellerProfile): SellerProfile => {
sellerProfiles.set(profile.userId, profile);
return profile;
},
update: (userId: string, updates: Partial<SellerProfile>): SellerProfile | undefined => {
const existing = sellerProfiles.get(userId);
if (!existing) return undefined;
const updated = { ...existing, ...updates };
sellerProfiles.set(userId, updated);
return updated;
},
};
export const wishlistStore = {
getByUserId: (userId: string): WishlistItem[] =>
Array.from(wishlistItems.values()).filter(w => w.userId === userId),
add: (item: WishlistItem): WishlistItem => {
wishlistItems.set(item.id, item);
return item;
},
remove: (userId: string, listingId: string): boolean => {
const item = Array.from(wishlistItems.values()).find(
w => w.userId === userId && w.listingId === listingId
);
if (item) {
return wishlistItems.delete(item.id);
}
return false;
},
exists: (userId: string, listingId: string): boolean =>
Array.from(wishlistItems.values()).some(
w => w.userId === userId && w.listingId === listingId
),
};