Genoa Tours
Travel agencies in Algeria and Morocco file tourist manifests for entry into Egypt. Before this, that ran on email and spreadsheets.
At a glance
- Client
- Genoa Tours
- Year
- 2026
- Scope
- Two front-ends, one API
- Role
- Proposed → built → delivered → hosted
Worth knowing
- 01
115 partner companies, each walled off from every other
- 02
English and Arabic with full RTL — the layout mirrors, it isn't translated
- 03
Formal Arabic guarantee letters generated server-side, across multiple flights
01The problem
Genoa Tours sits between North-African travel agencies and Egyptian entry approvals. An agency sends a manifest — flight details plus passport data for every tourist on it. Genoa's staff review it, send it back for corrections, approve it, and issue a formal Arabic guarantee letter that the tourists travel on.
The whole loop ran over email. No status an agency could check without asking, no structured record of what had been submitted, every letter assembled by hand. With 115 partner companies that does not scale, and the failure mode is a tourist at an airport holding a wrong document.
02What I built
Three pieces, all mine end to end. The partner portal is what agencies see: submit a manifest, track it through review → revision → approval, download the guarantee letter when it clears. The point was to remove the phone call.
The admin console is the internal surface — review and approve, manage partner companies, set per-tourist pricing, track running balances and payments, edit the document template, generate letters.
Both run on one ASP.NET Core 10 API over PostgreSQL with EF Core. Auth is JWT with silent refresh across four roles, so a partner can never see another partner's manifests.
03The parts that were actually hard
Bilingual, properly. Both front-ends are EN↔AR with full RTL — a layout that mirrors, not a dictionary bolted on. Arabic is the language the letters are issued in, so this was load-bearing.
Server-side Arabic PDF generation. Getting Arabic to shape and render correctly in a headless Linux container — right fonts present, right direction, no tofu — took longer than the feature it supports. And a single approval often spans several flights, so the letter has to fold them into one document rather than emitting one per flight.
04Running it
I host it myself on a Linux VPS: Caddy terminating TLS, systemd supervising, PostgreSQL on the same box, a self-contained .NET publish so the server needs no runtime installed. Schema changes ship as idempotent EF migration scripts.
Uploads and generated PDFs live on a persistent path outside the deploy directory, so a redeploy never takes client documents with it. Nobody thanks you for that until the one time it matters.
05Stack
- Next.js 16
- React 19
- TypeScript
- Tailwind v4
- ASP.NET Core 10
- PostgreSQL
- EF Core
- JWT auth
- Caddy
- systemd