A fulfillment network optimization platform for logistics consultants. Upload client order history and carrier rate data, configure constraints, and get zip-code-level fulfillment center placement recommendations scored on cost, speed, and sustainability.
Live: https://nodevantage.vercel.app
Fulfillment network design is one of the highest-leverage decisions in e-commerce logistics — and one of the least data-driven in practice. Most consultants are doing this work in Excel: manually mapping order volumes, eyeballing zone distributions, and building cost models that break every time a carrier updates their rate card.
NodeVantage is a first attempt at productizing that process. It's built on a problem space I know well from logistics consulting work, and designed around the actual inputs consultants have access to: order history CSVs and carrier rate cards.
Zone lookup is isolated by design.
Zone depth — the primary speed proxy in the scoring model — is approximated using straight-line distance between zip code centroids. That logic lives in a single isolated module (zone_lookup.py) that owns all zone math and is intentionally decoupled from the optimizer. The MVP uses distance bands mapped to carrier zones (0–50 miles → Zone 2, 1200+ miles → Zone 8). Swapping this for a live carrier API requires changing one module — nothing else.
The scoring pipeline is deterministic.
Every FC recommendation traces back to a formula. Order assignment uses a weighted score across three components — cost (inverse of DIM-adjusted billable rate), speed (inverse of zone depth), and sustainability (zone depth proxy at MVP). Weights are user-configurable. There are no black box outputs: every number in the results has a traceable source.
Rate cards are carrier-agnostic.
The system doesn't hard-code carrier logic. Each rate card is uploaded as a CSV with a defined schema — carrier, service level, origin zip prefix, destination zone, weight bracket, rate, transit days — and a DIM divisor captured at upload time. UPS Ground, FedEx 2-Day, a regional carrier, a fictional carrier: the optimizer doesn't care. Multiple rate cards participate in scoring simultaneously, so cross-carrier cost comparisons happen automatically.
- Upload order history CSV and one or more carrier rate cards
- Configure optimization: KPI weights, FC count, locked and flexible existing FCs
- The backend parses inputs and runs K-means clustering on destination zips
- The scoring engine assigns each order to the best FC by weighted score — locked FCs honored as hard constraints, flexible FCs always included without consuming recommendation slots
- Results are persisted to Supabase with a job ID written to the URL
- The frontend renders FC cards with per-carrier cost breakdowns and a Leaflet map — results survive page refresh and can be shared or revisited by URL
The live instance at nodevantage.vercel.app is the intended entry point.
Sample data is included in the repo under sample-data/ if you want to run a real analysis without building your own files:
sample_orders.csv— 500 synthetic orders with destination zips, dimensions, and weightsample_rates_ups_ground.csv,sample_rates_ups_2day.csv,sample_rates_fedex_ground.csv— rate cards for three carrier/service level combinations
Upload the order history, upload one or more rate cards with carrier metadata, configure your constraints, and run the analysis.
Each recommended FC includes:
- Zip code and city/state
- Orders assigned and coverage percentage
- Average zone depth across assigned orders
- Per-carrier, per-service-level cost breakdown with billable weight and cost per order
- FC type badge: Recommended, Locked FC, or Flexible FC
Results are plotted on an interactive map with distinct markers per FC type.
| Layer | Choice |
|---|---|
| Frontend | React + Vite + Tailwind + Leaflet.js |
| Backend | Python + FastAPI |
| Database | Supabase (PostgreSQL) |
| Frontend hosting | Vercel |
| Backend hosting | Render |
| Keep-alive | UptimeRobot pinging /ping every 5 minutes |
nodevantage/
├── backend/
│ ├── app/
│ │ ├── data/
│ │ │ └── uszips.csv
│ │ ├── db/
│ │ │ └── supabase.py
│ │ ├── models/
│ │ │ └── schemas.py
│ │ ├── routers/
│ │ │ ├── analysis.py
│ │ │ ├── jobs.py
│ │ │ └── upload.py
│ │ ├── services/
│ │ │ ├── csv_parser.py
│ │ │ ├── dim_weight.py # isolated DIM weight logic
│ │ │ ├── optimizer.py
│ │ │ ├── scorer.py
│ │ │ └── zone_lookup.py # isolated zone approximation
│ │ └── main.py
│ ├── tests/
│ │ ├── test_db.py
│ │ ├── test_optimizer.py
│ │ └── test_services.py
│ ├── .env.example
│ ├── render.yaml
│ └── requirements.txt
├── frontend/
│ ├── public/
│ │ ├── nodevantage_carrier_rates_template.csv
│ │ └── nodevantage_order_history_template.csv
│ ├── src/
│ │ ├── components/
│ │ │ ├── ConfigPanel.jsx
│ │ │ ├── FCCard.jsx
│ │ │ ├── ResultsMap.jsx
│ │ │ ├── ResultsTable.jsx
│ │ │ └── UploadPanel.jsx
│ │ ├── lib/
│ │ │ ├── api.js
│ │ │ └── supabaseClient.js
│ │ ├── pages/
│ │ │ ├── Analysis.jsx
│ │ │ └── Home.jsx
│ │ ├── App.jsx
│ │ ├── index.css
│ │ └── main.jsx
│ ├── index.html
│ ├── package.json
│ ├── tailwind.config.js
│ └── vite.config.js
├── sample-data/
│ ├── sample_orders.csv
│ ├── sample_rates_fedex_ground.csv
│ ├── sample_rates_ups_2day.csv
│ └── sample_rates_ups_ground.csv
├── supabase_schema.sql
└── README.md
A portfolio project anchored in a problem space I know well from logistics consulting. My first end-to-end solo build — planned using a structured five-phase product and engineering framework before any code was written, then built and deployed using Claude Code.
Built by Victor Caceres · LinkedIn