Synaplan
Open-source AI platform for companies: self-hosted AI agents, GDPR-compliant chatbot widget, RAG knowledge base, WhatsApp and Outlook AI, MCP, multi-model routing. Docker, Kubernetes or hosted in Germ
Upload your shipment bookings. Every carton, code and kilo is checked against the rulebook, then the right carrier is picked.
BusinessTypeScript · HTMLGitHub @OMSOHANI07OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Pre-Flight Shipment Checker — catch shipment booking mistakes before they ship (Ops Hackathon Case 2)
Translation pending
Taken from the repository README
Loading the full README.
Read the full README ↗Catch it before it ships. Upload a workbook of UK → EU shipment bookings, or just describe a shipment by chat or voice. Every booking is checked against the 10-rule rulebook (plus UK export-invoice tips). Each mistake is explained with its numbers and a fix, a carrier is picked for the seller's priority, and you get a plain-English confirmation.
Ship Eazy is a shipment checker built for Ops Hackathon – Case 2: Conversational Shipment Booking & "Pre-Flight" Error Checker.
/samples/stress-test.xlsx, then in Checker click Choose file (or drop it on the card). It has 16 bookings: one booking breaks each rule R-01…R-10, plus combinations, a missing field, an overweight-for-every-carrier case, a 5-digit numeric HS code, an orphan item and a "Bluetooth speaker".
Or drop your own .xlsx / .xls in the template format. Excel template download in the hero gives a blank workbook: just the Bookings and Items sheets with their column headings./results/<id>#BK-xxx for anyone.The 76-booking edge-case workbook is at /samples/edge-cases.xlsx: 21 can't ship yet, 15 details missing, 7 check first, 33 ready to ship, plus the orphan item EC-99 (V-11).
/api/voice/token; the ElevenLabs key never leaves the server.update_booking, get_booking_summary, confirm_booking, undo_last_change, start_over, download_purchase_order, download_corrected_excel). So the rules, guardrails, maths, summary panel and booking card are the same.Uploading the original template gives BK-001 GREEN, Carrier A Road economy, £540.00. That file contains only the grey example row, so it is checked as a demo. In any file that also has real rows, the grey example row is skipped.
Browser Server (Vercel, Next.js route handlers)
─────── ───────────────────────────────────────
Excel ─► lib/parseExcel.ts (SheetJS, raw cells)
│ Booking[] + Item[]
▼
lib/calculations.ts runFile() ◄── the ONLY source of pass/fail and numbers
│ "possible" restricted items ─────────────► POST /api/r06 Gemini yes/no (8 s, Zod) → else A-09 tip
▼ runFile() again with the verdicts
Results UI (cards, highlights, purchase order, corrected Excel)
│ BookingResult facts ─────────────────────► POST /api/explain Gemini wording (8 s, Zod, numbers-grounded check)
│ └ fallback: deterministic text from lib/explain.ts
├─ save ────────────────────────────────────► POST /api/uploads → Neon (uploads, bookings, items)
└─ confirm ─────────────────────────────────► POST /api/confirm → re-checks status rules → Neon (confirmations)
/results/[uploadId] ◄── server-rendered from Neon (public share link)
Chat / Voice ─► POST /api/chat { message, state, mode }
lib/conversation/engine.ts runTurn():
1 Gemini extracts raw values (JSON schema, Zod) └ fallback: keyword/pending-field parser
2 code normalises + applies them (provenance: stated / calculated / default), cascades totals
3 lib/calculations.ts runs the same checks as File Upload
4 code picks the next question (fixed priority order — never Gemini)
5 Gemini words the reply from FACTS; every £, date, carrier and rule ID must be in FACTS
└ else a fixed template reply
state saved → Neon conversations (version-checked; 409 on a stale tab)
lib/calculations.ts. Gemini only (a) gives a restricted yes/no on items keyword detection marks as "possible", and (b) rephrases facts the engine already produced. Gemini output is schema-validated, and any £ amount or date not found in the input facts is rejected.CARRIERS / RULES constants.| Rule | Check | Severity |
|---|---|---|
| R-01 | Every item has an HS code of at least 6 digits | Red |
| R-02 | Declared value = Σ(quantity × unit price), within 1% | Red |
| R-03 | Units per carton × cartons = quantity (per item) | Red |
| R-04 | Booking cartons = Σ item cartons | Red |
| R-05 | Gross weight ÷ pallets ≤ chosen carrier's max per pallet | Red |
| R-06 | No lithium batteries, aerosols or flammable liquids | Red |
| R-07 | Gross weight ≥ Σ(unit weight × quantity) | Amber |
| R-08 | Warehouse deliveries have an appointment booked | Amber |
| R-09 | Every item has a country of origin | Amber |
| R-10 | Ready-to-ship date is not in the past | Amber |
Plus input checks V-01…V-11, which make a booking Incomplete, and tips A-01…A-09 from the UK government export-invoice guide. Tips never change a booking's status. The full spec is in docs/RULEBOOK.md, docs/RECOMMENDATION_RULES.md and docs/CALCULATIONS_FORMULAS.pdf.
| Run | What | Result |
|---|---|---|
| Unit, scripted & parity | npm test — 302 Vitest cases: every rule and boundary, V-01…V-11, tips, carriers, parser warnings, the 76-booking edge-case workbook against its Expected sheet; conversation state, next-question order, normalisers, read-backs, reply validator; scripted chats (§7.1 vague seller, §7.2 add a pallet, contradictions, restricted goods, voice, prompt injection, Gemini down); chat ↔ Excel parity | 302 / 302 pass |
| Local build | npm run build && npm run e2e — 73 Playwright cases. The local server runs with GEMINI_MOCK=1, so chat runs are repeatable. Covers V1 plus chat booking to purchase order and corrected Excel, add-a-pallet toast and highlight, Undo, refresh starting fresh, voice token / mic-denied / not-configured / no-mic handling, guardrails on the rules page, HTML-as-text and axe | 71 pass, 2 skipped by design |
| Production | E2E_BASE_URL=https://preflight-shipment-checker-omega.vercel.app npm run e2e (the mocked-chat tests skip; the full chat was also run by hand with real Gemini, about 2–4 s per turn) | 63 pass, 7 skipped |
| No database, no AI (V1 run) | same suite with E2E_NO_DB=1 E2E_NO_AI=1 against a server with Neon unreachable and no Gemini key | 48 pass, 3 skipped by design |
npx tsx scripts/collect-metrics.ts measures speed and AI success on the live site; npx tsx scripts/build-report-data.ts turns all of it into reports/data/*.json, the datasets behind the test report dashboard (reports/dashboard/index.html).
npm install
cp .env.example .env.local # then fill in the values
npm run db:migrate && npm run db:seed # optional: Neon tables + carriers/rules
npm run dev
| Name | Required | Purpose |
|---|---|---|
GEMINI_API_KEY | No (fallback text without it) | Google AI Studio key. Used server-side only. |
GEMINI_CHAT_MODEL | No | Model for chat extraction and replies (default gemini-3.5-flash, minimal thinking, for about 2–5 s per turn) |
ELEVENLABS_API_KEY | For voice | ElevenLabs API key. Server only — used by /api/voice/token to mint a conversation token |
ELEVENLABS_AGENT_ID | For voice | The ElevenLabs agent to talk to |
GEMINI_MOCK | No | 1 = no Gemini calls in chat; the demo messages map to fixed extractions (used by Playwright) |
CHAT_RATE_LIMIT | No | Chat messages per minute per IP (default 30) |
GEMINI_MODEL | No | Overrides the model (production uses gemini-3.8-flash, the newest stable Flash on the key, with thinking set to low so calls fit the 8 s time-box). If empty, the newest stable gemini-X.Y-flash from the key's model list is used. |
DATABASE_URL | No (no share links without it) | Neon Postgres connection string |
| Script | What it does |
|---|---|
npm run dev / build / start | Next.js |
npm test | Vitest: engine, Excel parser, edge cases and the conversation (302 tests) |
npm run e2e | Playwright, V1 and V2 (run npm run build first). Add E2E_BASE_URL=https://… to test a deployment. |
npx tsx scripts/elevenlabs-setup.ts | Registers (or updates) the 7 client tools on the ElevenLabs agent; --check only reports. Reads the key from .env.local |
npm run make:stress | Regenerates public/samples/stress-test.xlsx from the real template |
npm run db:generate / db:migrate / db:seed | Drizzle migrations and the idempotent seed |
npm run lint / typecheck | ESLint / tsc |
lib/calculations.ts rule engine (spec, used as-is; see "Changes to the engine")
lib/parseExcel.ts header detection, grey example row, raw HS codes, Excel date serials
lib/checkFile.ts browser pipeline: parse → check → AI R-06 → re-check
lib/explain.ts confirmation, next steps, invoice checklist, fallback explanations
lib/gemini.ts server-only Gemini client (8 s time-box, JSON mode, model discovery)
lib/pdf.ts purchase order PDF per booking (jsPDF + autotable, embedded Unicode font)
lib/workbook.ts blank Excel template and the corrected workbook (exceljs)
lib/conversation/guard.ts guardrails that run before the AI
lib/conversation/ V2: state, normalisers, next-question picker, read-backs, FACTS + validator, turn engine
lib/gemini/ extraction, reply writer, R-06, mock
app/api/* r06, explain, uploads, confirm, chat, conversation
components/conversation/ Chat, Voice, summary panel, diff toast, review card
db/ Drizzle schema, migrations, seed
components/ page sections, booking card, carrier card, data tables
scripts/ stress-workbook generator and its expectations
tests/, e2e/ Vitest and Playwright
The page sits on a full-screen photo backdrop: a ship at sea, a container port and a lorry and plane at dusk. It cross-fades between them as you scroll from section to section, drifts slowly, and a plane crosses the sky now and then. The photos are small, so the backdrop uses blurred copies, and the hero shows them sharp in a tall window (full-screen on phones). Sections are translucent so the backdrop shows through. All the motion stops when the device asks for reduced motion.
The page borrows the feel of a product page: a big, tight hero headline; calm grey subheads; alternating light and dark full-width sections; a translucent sticky nav with a product sub-nav; pill buttons; large rounded cards; and scroll-reveal motion. It uses no third-party brand assets. Colour is used only for status (Red #ff3b30, Amber #ff9f0a, Green #34c759, Incomplete #8e8e93, Tips blue), with darker text variants for WCAG AA contrast.
It works from 360 px phones up, in light and dark mode, follows prefers-reduced-motion, and supports the keyboard (visible focus rings, tooltips that open on focus). The hero illustration is an original SVG.
lib/calculations.ts is the provided spec, with one fix. runFile() passed one geminiR06Verdicts map, keyed by item index, to every booking. A file-level AI verdict for item #0 of one booking would therefore also mark item #0 of every other booking as restricted. The new geminiR06VerdictsByBooking option scopes verdicts to their booking. A test covers it in tests/calculations.test.ts.
Edge-case fixes since then, each with a test: line values rounded to the pence; dates with a trailing time and decimal commas read correctly; Booking IDs matched ignoring case and extra spaces; "-free" and "no lithium" wording no longer flags R-06; unreadable cells explained instead of treated as blank; scientific-notation HS codes explained; parser warnings for merged cells and formulas with no saved value; CSV exports guarded against formula injection.
Estimates only — costs and delivery dates come from the fixed hackathon rate card.