You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A modern, mobile-responsive e-commerce platform with pickup-only fulfillment and bank transfer payment. Customers browse products, place orders, upload payment receipts, and pick up at the store. Admin manages orders with full receipt review and rejection flow.
App Router: Pages in app/ with route groups for auth, customer, and admin sections.
API Routes: Route handlers in app/api/* β each file maps to an endpoint.
Middleware: Auth guard at /admin/* (requires ADMIN role) and /profile (requires login).
Guest Checkout: Works without an account β customer provides contact info at checkout.
File Uploads: Images uploaded to Cloudflare R2 via admin or customer receipt flow.
Order Lifecycle
PLACE ORDER CUSTOMER ADMIN
βββββββββββ ββββββββ βββββ
[Checkout] ββ> PENDING ββ> Upload Receipt ββ> CONFIRMED ββ> Mark Ready
(receiptImage) β
β READY_FOR_PICKUP
β β
[If fake receipt] Mark Picked Up
β β
REJECTED ββ> Customer PICKED_UP
(rejectionReason) re-uploads
β
βββ> CONFIRMED (re-upload clears reason)
Status Definitions
Status
Meaning
Who Sets It
PENDING
Order placed, awaiting receipt upload
System (checkout)
CONFIRMED
Receipt uploaded, awaiting admin review
Customer (receipt upload)
READY_FOR_PICKUP
Admin verified receipt, ready for pickup
Admin
PICKED_UP
Customer collected the order
Admin
REJECTED
Receipt invalid β customer must re-upload
Admin (with reason)
CANCELLED
Order cancelled
Admin
Payment & Receipt Flow
How it works
Checkout β Customer fills in contact info and places the order. Only Bank Transfer is available as payment method. Customer can optionally upload the receipt immediately during checkout.
Receipt Upload β On the order detail page, the customer uploads a clear image of their bank transfer receipt. This is sent to Cloudflare R2 and the URL is stored on the order.
Admin Review β Admin sees the receipt in the order management panel. They can:
Mark Ready β receipt looks valid, order becomes ready for pickup
Reject β opens a modal for entering a reason (e.g. "image unclear", "amount incorrect")
Rejection Flow β The customer sees the rejection reason prominently on their order page and can upload a new receipt. The old rejection reason is cleared.
Response: Returns updated order with paymentStatus: COMPLETED, status: CONFIRMED.
Product Image Gallery
The product detail page (/product/[id]) supports multiple images per product:
Main Image β Large display area showing the selected image
Thumbnail Strip β All product images shown as thumbnails below the main image
Desktop: 5-column grid layout
Mobile: Horizontal scrollable row with 64px thumbnails
Active thumbnail is highlighted with a primary color border and ring
Animated transitions β fade-in effect when switching images (0.3s)
Placeholder β SVG icon shown when images array is empty
Products store images as an array of URLs in the images: String[] Prisma field. Admin uploads images via the admin product form using the ImageUpload component (supports file upload to R2 and external URL input).
npm run db:generate # Generate Prisma client
npm run db:migrate # Create migration
npm run db:push # Push schema directly (dev only)
npm run db:seed # Seed database
npm run db:studio # Open Prisma Studio