From d09c998d517d980d9fac43ac5017f7264a3796ce Mon Sep 17 00:00:00 2001 From: delta-lynx-89e8 Date: Sun, 22 Feb 2026 12:30:03 -0800 Subject: [PATCH] QA fixes: real listing creation, profile save, favorites, missing pages - SellItemPage: real file upload + API listing creation + activate - CreateProfilePage: save profile via PUT /users/profile - ProductDetailPage: wire edit/delete/message buttons, show edit for owner - ListingCard: persist favorites via API, show real images - Footer: connect newsletter subscribe to API - Router: add /dashboard/listings and /dashboard/saved routes - Backend: add GET /listings/favorites endpoint - New pages: MyListingsPage, SavedItemsPage - Fix unused imports causing build failures Co-Authored-By: Claude Opus 4.6 --- CLAUDE.md | 68 ++ .../src/components/layout/DashboardLayout.tsx | 30 +- client/src/components/layout/Footer.tsx | 38 +- client/src/components/layout/Header.tsx | 46 +- .../components/listings/CategorySidebar.tsx | 19 +- .../src/components/listings/ListingCard.tsx | 59 +- client/src/components/ui/LocationInput.tsx | 109 +++ client/src/components/ui/index.ts | 1 + client/src/pages/ChatPage.tsx | 65 +- client/src/pages/CreateProfilePage.tsx | 45 +- client/src/pages/HomePage.tsx | 59 +- client/src/pages/MyListingsPage.tsx | 103 +++ client/src/pages/MyOffersPage.tsx | 123 ++- client/src/pages/NotificationsPage.tsx | 74 +- client/src/pages/ProductDetailPage.tsx | 200 ++++- client/src/pages/SavedItemsPage.tsx | 39 + client/src/pages/SellItemPage.tsx | 111 ++- client/src/pages/SettingsPage.tsx | 72 +- client/src/pages/SoldItemsPage.tsx | 109 ++- client/src/pages/UpdateProfilePage.tsx | 41 +- client/src/router.tsx | 4 + client/src/types/index.ts | 4 +- package-lock.json | 2 +- .../20260222200755_init/migration.sql | 313 +++++++ server/prisma/migrations/migration_lock.toml | 3 + server/prisma/schema.prisma | 11 + server/prisma/seed.ts | 793 ++++++++++++++++++ server/src/config/env.ts | 2 + server/src/index.ts | 4 + server/src/routes/auth.ts | 70 ++ server/src/routes/chat.ts | 81 +- server/src/routes/listing.ts | 201 ++++- server/src/routes/location.ts | 58 ++ server/src/routes/misc.ts | 41 + server/src/routes/notification.ts | 61 +- server/src/routes/offer.ts | 173 +++- server/src/routes/payment.ts | 48 ++ server/src/routes/user.ts | 172 +++- server/src/socket/index.ts | 39 +- server/src/utils/blocked.ts | 17 + server/src/validators/user.ts | 27 + 41 files changed, 3152 insertions(+), 383 deletions(-) create mode 100644 CLAUDE.md create mode 100644 client/src/components/ui/LocationInput.tsx create mode 100644 client/src/pages/MyListingsPage.tsx create mode 100644 client/src/pages/SavedItemsPage.tsx create mode 100644 server/prisma/migrations/20260222200755_init/migration.sql create mode 100644 server/prisma/migrations/migration_lock.toml create mode 100644 server/prisma/seed.ts create mode 100644 server/src/routes/location.ts create mode 100644 server/src/routes/misc.ts create mode 100644 server/src/utils/blocked.ts create mode 100644 server/src/validators/user.ts diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..6ad46e3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,68 @@ +# Marketplace Project Notes + +## Database: PostgreSQL via Docker + +Container name: `marketplace-postgres` +Image: `postgres:17-alpine` +Volume: `marketplace-pgdata` (persistent local data) + +### Start database +```bash +docker start marketplace-postgres +``` + +### Stop database (frees memory) +```bash +docker stop marketplace-postgres +``` + +### Check status +```bash +docker ps -f name=marketplace-postgres +``` + +### Connection string +``` +postgresql://marketplace:marketplace_dev@localhost:5432/marketplace +``` + +### If container was deleted, recreate: +```bash +docker run -d \ + --name marketplace-postgres \ + -e POSTGRES_USER=marketplace \ + -e POSTGRES_PASSWORD=marketplace_dev \ + -e POSTGRES_DB=marketplace \ + -p 5432:5432 \ + -v marketplace-pgdata:/var/lib/postgresql/data \ + --restart unless-stopped \ + postgres:17-alpine +``` + +Data persists in the `marketplace-pgdata` Docker volume even if the container is removed. + +## Running the app + +1. Start database: `docker start marketplace-postgres` +2. Server: `npm run dev:server` (port 3000) +3. Client: `npm run dev:client` (port 5173) +4. Or both: `npm run dev` + +## Seed data + +```bash +cd server && npx tsx prisma/seed.ts +``` + +Test users (all password: `password123`): +- alice.chen@example.com +- bob.smith@example.com +- carol.jones@example.com +- david.wilson@example.com +- eva.martinez@example.com + +## Key env vars (server/.env) + +- `DATABASE_URL` — PostgreSQL connection +- `GOOGLE_MAPS_API_KEY` — Location autocomplete +- `STRIPE_SECRET_KEY` / `STRIPE_PUBLISHABLE_KEY` — Payments (optional for dev) diff --git a/client/src/components/layout/DashboardLayout.tsx b/client/src/components/layout/DashboardLayout.tsx index 2904135..fd205fe 100644 --- a/client/src/components/layout/DashboardLayout.tsx +++ b/client/src/components/layout/DashboardLayout.tsx @@ -1,28 +1,46 @@ -import { NavLink, Outlet } from 'react-router-dom'; -import { MessageSquare, Tag, Bell, ShoppingBag, Settings } from 'lucide-react'; +import { NavLink, Outlet, useNavigate } from 'react-router-dom'; +import { MessageSquare, Tag, ShoppingBag, Settings, List, Heart, LogOut } from 'lucide-react'; +import { useAuth } from '../../context/AuthContext'; const navItems = [ - { to: '/dashboard/messages', icon: MessageSquare, label: 'Messages' }, { to: '/dashboard/offers', icon: Tag, label: 'Offers' }, - { to: '/dashboard/notifications', icon: Bell, label: 'Notifications' }, { to: '/dashboard/sold', icon: ShoppingBag, label: 'Sold Items' }, + { to: '/dashboard/listings', icon: List, label: 'My Listings' }, + { to: '/dashboard/saved', icon: Heart, label: 'Saved Items' }, + { to: '/dashboard/messages', icon: MessageSquare, label: 'My Messages' }, { to: '/dashboard/settings', icon: Settings, label: 'Settings' }, ]; export function DashboardLayout() { + const { logout } = useAuth(); + const navigate = useNavigate(); + + const handleLogout = () => { + logout(); + navigate('/'); + }; + return (
{/* Sidebar */} diff --git a/client/src/components/layout/Footer.tsx b/client/src/components/layout/Footer.tsx index f079677..e350be6 100644 --- a/client/src/components/layout/Footer.tsx +++ b/client/src/components/layout/Footer.tsx @@ -1,7 +1,25 @@ +import { useState } from 'react'; import { Link } from 'react-router-dom'; import { Facebook, Twitter, Instagram, Youtube, Package } from 'lucide-react'; +import { api } from '../../api/client'; export function Footer() { + const [email, setEmail] = useState(''); + const [subscribing, setSubscribing] = useState(false); + const [subscribed, setSubscribed] = useState(false); + + const handleSubscribe = async (e: React.FormEvent) => { + e.preventDefault(); + if (!email || subscribing) return; + setSubscribing(true); + try { + await api.post('/newsletter', { email }); + setSubscribed(true); + setEmail(''); + } catch {} + setSubscribing(false); + }; + return (
@@ -20,12 +38,18 @@ export function Footer() {

Stay Updated

Subscribe to our newsletter for tips and special offers

-
- - -
+ {subscribed ? ( +

Thanks for subscribing!

+ ) : ( +
+ setEmail(e.target.value)} required + className="flex-1 px-3 py-2 rounded-lg bg-primary-800 border border-primary-700 text-sm placeholder:text-primary-400 focus:outline-none focus:border-primary-500" /> + +
+ )}
@@ -36,7 +60,7 @@ export function Footer() {
  • Home
  • Browse
  • Sell Your Item
  • -
  • My Listings
  • +
  • My Listings
  • diff --git a/client/src/components/layout/Header.tsx b/client/src/components/layout/Header.tsx index d0bbb2f..10312e1 100644 --- a/client/src/components/layout/Header.tsx +++ b/client/src/components/layout/Header.tsx @@ -1,6 +1,6 @@ import { useState } from 'react'; import { Link, useNavigate } from 'react-router-dom'; -import { Search, Bell, Menu, X, User, LogOut, Package, Heart, MessageSquare, Settings } from 'lucide-react'; +import { Search, Bell, Menu, X, User, LogOut, ShoppingBag, Heart, MessageSquare, Settings } from 'lucide-react'; import { useAuth } from '../../context/AuthContext'; import { Avatar } from '../ui/Avatar'; @@ -19,15 +19,15 @@ export function Header() { }; return ( -
    +
    {/* Logo */} -
    - +
    +
    - + MARKETPLACE @@ -35,14 +35,14 @@ export function Header() { {/* Search */}
    - + setSearchQuery(e.target.value)} - className="w-full pl-10 pr-4 py-2 rounded-xl border border-gray-200 bg-gray-50 text-sm - focus:border-primary-400 focus:ring-2 focus:ring-primary-100 focus:outline-none transition-all" + className="w-full pl-10 pr-4 py-2 rounded-xl border border-white/20 bg-white/10 text-sm text-white placeholder-white/50 + focus:border-white/40 focus:ring-2 focus:ring-white/20 focus:outline-none transition-all backdrop-blur" />
    @@ -51,15 +51,15 @@ export function Header() {
    {isAuthenticated ? ( <> - + Sell Item - - + + - - - + + +
    {/* Mobile menu */} {showMobileMenu && ( -
    +
    - + setSearchQuery(e.target.value)} - className="w-full pl-10 pr-4 py-2 rounded-xl border border-gray-200 bg-gray-50 text-sm focus:border-primary-400 focus:outline-none" /> + className="w-full pl-10 pr-4 py-2 rounded-xl border border-white/20 bg-white/10 text-sm text-white placeholder-white/50 focus:border-white/40 focus:outline-none" />
    {isAuthenticated && ( diff --git a/client/src/components/listings/CategorySidebar.tsx b/client/src/components/listings/CategorySidebar.tsx index 8bd7a30..a84df7a 100644 --- a/client/src/components/listings/CategorySidebar.tsx +++ b/client/src/components/listings/CategorySidebar.tsx @@ -19,12 +19,11 @@ interface CategorySidebarProps { export function CategorySidebar({ selected, onSelect }: CategorySidebarProps) { return ( -