Skip to main content
< Sai Komal/>
Back to Portfolio

Independent · Product Design & Web Development

GITAM Marketplace

Independently designed, built and deployed a university marketplace using React, Next.js, TypeScript and Tailwind CSS, integrating backend services for product workflows.

GITAM Marketplace home page — join with a university email, list in under three minutes, chat then meet on campus
Objective

The product objective

A student-only, peer-to-peer marketplace where verified GITAM students buy, sell and pass on items on campus. Listing is self-service and publishes immediately; safety is enforced by design rather than by an approval queue; payment always happens in person, never through the site.

Role
Independent project — product, design, front-end and integration
Timeline
Independent Project
Deployment
Deployed on Vercel; Supabase for data, auth and storage.
Stack

Technology stack

  • Next.js 16 (App Router)
  • React 19
  • TypeScript (strict)
  • Tailwind CSS v4
  • shadcn/ui · Radix
  • Zod · React Hook Form
  • Supabase — Postgres, Auth, Storage, Realtime
  • Vitest · PGlite · Playwright
Ownership

Designed, implemented, integrated

What I did on each side of the project — kept separate, because they are different kinds of work. AI tools accelerated implementation; understanding the code, reviewing the output, integrating it and validating it stayed with me.

What I designed

  • Product requirements written up as a PRD, an implementation plan and a requirement-by-requirement traceability matrix.
  • The core journeys: university-email sign-up, a short publish-first listing form, search and filters, chat-then-meet exchange.
  • Safety UX — reports with case numbers, blocks, appeals, and safe-exchange guidance on the sell form, the listing and the chat.
  • Community threads with nested replies, voting, Hot/New/Top sorting and a moderation queue.

What I implemented

  • Feature-module architecture: auth, listings, search, messaging, moderation, admin, account and uploads, each owning its server actions, queries and components.
  • Shared UI primitives on shadcn/Radix, plus product components — cards, badges, empty, loading and error states.
  • A listing lifecycle state machine (draft, active, on hold, sold, expired, removed) that drives the seller's available actions.
  • A deterministic, versioned moderation rules engine in TypeScript.
  • Zod validation at every boundary.

What I integrated

  • Supabase Auth with the university email domain allowlist enforced in the database, and verification by link or 6-digit code.
  • Postgres with row-level security on every table and explicit grants; state changes go through database functions.
  • An image pipeline: upload route handler → file-type sniffing → re-encode to WebP (stripping EXIF/GPS) → thumbnail → QR-code detection; private storage served through short-lived signed URLs.
  • Realtime messaging, an admin console and audit trail.

Where AI assisted

  • Implementation was accelerated with AI coding assistants, directed by the PRD, implementation plan and traceability matrix kept in the repository.
  • Generated code was reviewed, adapted and verified against the unit, database and end-to-end test suites and the production build.
Problem solving

Challenges and decisions

  1. 01

    Challenge

    Listings must go live immediately — no human approval queue — without letting clearly prohibited items through.

    Approach

    Deterministic automated checks run on save: clear violations are blocked with a specific reason and a review path; ambiguous signals are hidden pending review.

  2. 02

    Challenge

    The interface and the database could disagree about which status changes a seller is allowed to make.

    Approach

    The database function is the authority. The TypeScript mirror that drives the UI is checked against it in a parity test.

  3. 03

    Challenge

    Event-ticket photos can carry barcodes, QR codes and booking codes.

    Approach

    Ticket listings with photos publish quarantined until a moderator checks them; text-only ticket listings publish immediately.

Validation

How it is verified

  • Unit tests for the moderation engine, schemas, state machine, search parameters and image pipeline (Vitest).
  • Database tests that apply the real migrations in PGlite and verify row-level security, triggers and RPCs — no Docker needed.
  • Playwright smoke tests and end-to-end MVP journeys.
Code

From the repository

The seller-side lifecycle. The database is the authority; this mirror drives the UI and is parity-tested against it.

src/features/listings/status.tsView on GitHub
/**
 * Seller-initiated lifecycle transitions (PRD §8.3). The database function
 * public.listing_transition_allowed() is the authority; this mirror drives the UI and is checked
 * against the database in tests/db/status-parity.test.ts.
 */
export const SELLER_TRANSITIONS: Record<ListingStatus, readonly ListingStatus[]> = {
  draft: ["removed"],
  active: ["on_hold", "sold", "removed"],
  on_hold: ["active", "sold", "removed"],
  sold: ["active", "removed"],
  expired: ["active", "removed"],
  removed: [],
};

export function canTransition(from: ListingStatus, to: ListingStatus): boolean {
  return SELLER_TRANSITIONS[from].includes(to);
}

Want to work together?

Let's build the next one together.