Freight CRM
Production freight-forwarding CRM for a Mumbai logistics firm — I built the React SPA framework the team rallied around, then shipped Buckets 6B–11: email drafts, enquiry board, sea/air enquiry forms, agent dispatch, and rates with L1 freeze. Built in a team of four (lead + three developers) over five weeks; live on a client-owned domain.
- Timeline
- May – Jun 2026 · ~5 weeks · paid freelance engagement
- Team
- Team lead + 3 developers · I shipped ~27 commits across Buckets 6B–11 + the SPA framework
- Stack
- React 19 · Vite 8 · Node.js 22 · Express 4.18 ·
better-sqlite312.10 ·express-session·bcryptjs - Email pipeline
imapflow·mailparser·nodemailer·pdf-parse·xlsx· Python IMAP sidecar- Scope
- My scope: ~50 of 124 endpoints · 13 of 31 tables · Buckets 6B–11 of 15 shipped by the team
- Infra
- GCP Compute Engine
e2-small· Ubuntu 22.04 · Nginx + Certbot TLS · PM2 under systemd · daily SQLite snapshots to GCS - Status
- Live deployment available on request
My contribution
- React SPA framework — converted the team's static HTML mockups into a React 19 + Vite app and set up the routing, sidebar, context, and the fetch wrapper that every later bucket landed inside.
- Buckets 6B–11 — Email Drafts pipeline (generator + UI + scheduler), Enquiry Board with Stage Tracker + Timeline, Sea-freight forms (FCL/LCL imp/exp), Air-freight forms + mode picker, Agent Dispatch with the 4-hour reminder loop and IMAP auto-reply threading, and the Rates table with L1 freeze + margin calculator.
- SPA integration for Buckets 2–5 — merged my teammates' AI scoring, field extraction, reply parser, and SMTP sender modules into the React app in a single merge sequence on 2026-05-25.
- Documentation — sole author of
REMAINING_WORK.mdandBUCKET_7_9_ASSUMPTIONS.md; co-author and primary maintainer of the developer guide.
What it does
End-to-end enquiry-to-quote pipeline. Gmail IMAP polls the client's ops inbox → an AI scorer ranks each new message → high-scoring messages auto-create enquiries, mid-scoring go to a review queue → field extraction lifts mode, ports, weight, and incoterm → the enquiry lands on a six-column Kanban → agents are dispatched over SMTP for rates → replies are parsed (PDF / Excel / text) into structured rate rows → quotes are built with a margin calculator that hard-locks below the threshold → L1 freeze snapshots the chosen rate and advances the stage with an immutable audit row.
Architecture
A Python IMAP sidecar (gmail_proxy.py) polls Gmail every 60 seconds and
POSTs to a secret-guarded ingest webhook (INGEST_SECRET) on the Express
backend, decoupling inbox polling from the request path. Express runs against a
single SQLite file in WAL mode (31 tables, indexed foreign keys); the React SPA is
served as static assets from the same Node process behind Nginx, with TLS via
Certbot and PM2 keeping the process alive under systemd. A nightly cron snapshots
the database to a GCS bucket.
AI scoring (Bucket 3, integrated)
Five weighted signals score each incoming message — sender domain (30), subject keywords like quote / RFQ / freight rate (25), body terms + quantity + incoterm (20), PDF/Excel/packing-list attachment (15), first-in-thread bonus (10). Thresholds: ≥75 auto-creates an enquiry, 50–74 routes to a review queue, <50 silently skips. Tunable through a sandbox UI on the Inbox screen — Shreshta built the scorer; I wired the sandbox into the React SPA.
Margin calculator + L1 freeze (Bucket 11)
The margin tab computes Buy(INR) = Buy(USD) × exchange_rate (default
83.50, mutable in settings). A slider sets the
margin 0–30%; under 12% the route hard-locks the quote with a
"needs approval" badge. L1 freeze is an idempotent endpoint — it snapshots the
INR conversion using the current settings.exchange_rate, advances the
enquiry stage, and writes one immutable row to enquiry_timeline.
Re-firing the route doesn't double-snapshot.
Team & work split
Built in a team of four — a lead who brought the team together and three developers. My buckets are listed above; crediting the rest by name and bucket below, partly because it's accurate, partly because portfolio-grade transparency on freelance team work is the framing reviewers actually want.
- Mohith — team lead; connected the team and ran the engagement end-to-end. Repo bootstrap, branch integration / release management, production deploy runbook (GCP / Nginx / Certbot / PM2), Bucket 6C (Gmail App Password UX). I owned the Node-side production build-serve config (Vite build pipeline + static-middleware ordering) the runbook deploys against; GCP / Nginx / TLS were lead-led.
- Shreshta — Buckets 3, 4, 5 and 6A (AI scoring, field extraction, reply parser, SMTP sender) — the ingest layer I integrated into the SPA.
- Lochan Sai — Buckets 12, 13, 14, 15 (quote versions, analytics, agent rating + Best-Fit ranking, Rate Recommendations, LLM-backed reply analysis, lost-deal patterns) + ops layer (jobs / customs / documents tables).
The team shipped 15 buckets in total; my scope was Buckets 6B–11 plus the SPA framework and the integration of Buckets 2–5 into it.