Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

220 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Loop

πŸ“Ή Watch the demo walkthrough β€” the fastest way to see Loop working end to end.

Real-time geo-matching platform connecting cargo owners with vehicle drivers in Rwanda. Monorepo:

loop/
β”œβ”€β”€ mobile/   Flutter app (cargo owner + driver)    β†’ see mobile/README.md
β”œβ”€β”€ api/      NestJS REST API + PostgreSQL/PostGIS   β†’ see api/README.md
β”œβ”€β”€ admin/    Next.js admin (verification + metrics) β†’ see admin/README.md
└── docs/     engineering spec (BUILD_SPEC.md)

The API is the system of record (PostgreSQL/PostGIS). The mobile app and admin are clients of it.

Contents

Try it

Link
Admin (web) https://loop-admin-prod.up.railway.app β€” sign in with the demo admin admin@loop.rw / Admin@2026, a throwaway account seeded for evaluation since there is no public admin signup at the moment.
API https://loop-api-prod.up.railway.app Β· Swagger at /docs
Mobile app (Android APK) Download and install β€” see Install the Android app below. Or build/run the Flutter app against the deployed API.
Demo video https://drive.google.com/drive/folders/19V9SplFLdkcaqbnbVAdXDNMCyUUCHTqn?usp=drive_link

The mobile app runs against the deployed API with no local backend:

cd mobile && flutter pub get
flutter run --dart-define=API_BASE_URL=https://loop-api-prod.up.railway.app

An Android emulator (with Google Play services, for push) or a physical device works β€” see mobile/README.md.

Install the Android app (Android APK)

The release APK is already configured to talk to the hosted API β€” no local backend or setup is needed. It targets Android (Android 6.0 / API 23 and newer).

  1. Download the APK to your Android phone from the latest release: Loop release APK β†’ download the loop-*.apk asset from the latest release. Or scan this QR code with the phone's camera:

    QR code β€” Loop APK download

    (If you download it on a computer, transfer the file to the phone.)

  2. Allow install from this source. Open the downloaded file. Android will ask to permit installs from your browser or file manager: tap Settings β†’ allow from this source (the standard prompt for any app installed outside the Play Store), then go back and continue.

  3. Install. If a "App blocked" or "Unsafe app blocked" message appears (Play Protect scanning an app it doesn't recognise), do not tap OK or Got it β€” that cancels the install. Instead tap More details (or the small expander), then Install anyway. Open Loop.

  4. Grant permissions when asked β€” location (so nearby matching works) and notifications (for job/proposal push).

  5. Create an account in the app (pick Cargo owner or Driver), or sign in. Everything runs against the live hosted backend.

To try the full owner ↔ driver loop on one phone, register one account, sign out, and register the other role β€” or use two devices. The admin side (verification approval, metrics) is the web console linked in Try it.

⚠️ Gotcha β€” if the app times out on mobile data: on some Rwandan mobile networks (observed on MTN) the app can time out at login/registration while working fine on Wi-Fi. This is not an app bug β€” it is a routing problem between the mobile carrier and the host's (Railway's) edge IP, a documented Railway pattern that Railway staff attribute to the carrier's network, not the app. Workaround: use Wi-Fi or a different carrier. Durable fix: front the API with a Cloudflare-proxied custom domain β€” the plan and diagnosis are in DEPLOYMENT.md section 12. It was found on 10 July 2026, late in testing, because emulators, the dev network, and Wi-Fi all reach the API normally.

To build the APK yourself instead, see DEPLOYMENT.md section 6 (Mobile APK).

Quick start (local development)

Prerequisites: Docker, Node.js 20+, and the Flutter SDK.

# 1. Database β€” PostgreSQL 17 + PostGIS 3.5 (host port 5433)
docker compose up -d

# 2. API β€” NestJS on http://localhost:3000 (Swagger at /docs)
cd api
cp .env.example .env            # dev defaults: stub mail/storage/push
npm install
npm run migration:run           # creates the schema + PostGIS extension
npm run seed                     # seeds the admin account + pricing/size config
npm run start:dev

# 3. Admin β€” Next.js on http://localhost:3001
cd ../admin
cp .env.example .env.local       # NEXT_PUBLIC_API_BASE_URL=http://localhost:3000
npm install
PORT=3001 npm run dev            # http://localhost:3001  (login: admin@loop.rw)

# 4. Mobile β€” Flutter (iOS simulator / Android emulator / device)
cd ../mobile
flutter pub get
flutter run --dart-define=API_BASE_URL=http://localhost:3000
#   Android emulator: use http://10.0.2.2:3000 instead of localhost
#   Deployed API (no local backend): use https://loop-api-prod.up.railway.app

To run the mobile app against the deployed backend, just point API_BASE_URL at the hosted API β€” see mobile/README.md and, for the release APK, DEPLOYMENT.md section 6 (Mobile APK).

The seeded admin credentials come from ADMIN_EMAIL / ADMIN_PASSWORD in api/.env (defaults: admin@loop.rw / change-me-admin). The seed also provisions a fixed demo admin β€” admin@loop.rw / Admin@2026 β€” so the deployed admin console can be signed into for evaluation without a public admin signup (DEMO_ADMIN_EMAIL / DEMO_ADMIN_PASSWORD override it). It is a throwaway evaluation login and will be archived (rotated or removed) once the defence is complete. Backend details and the full endpoint list are in api/README.md; the admin app is documented in admin/README.md.

Deployment

Loop deploys to Railway as one project with three services (PostGIS DB, API, admin) plus external Firebase Storage/FCM and SendGrid. Full, reproducible steps β€” architecture, environment matrix, prerequisites, deploy commands, going-live, the mobile APK build, secrets handling, and a hosted-stack verification checklist β€” are in DEPLOYMENT.md.

Walkthrough video

πŸ“Ή Watch the walkthrough β€” the core loop end to end: driver verification, posting a job with a cost estimate, nearby matching, sending and accepting a proposal, in-app messaging, completing and rating, and the admin metrics dashboard.

Screenshots

The core flow, captured across both mobile apps and the admin console. More views (welcome, permissions) are in screenshots/.

Driver verification upload Admin verification queue
Driver verification upload Admin verification queue
Admin document review Reject with a reason
Admin document review Admin document review β€” reject with reason
Driver online Owner creates a job
Driver online Owner create job
Cost estimate Nearby drivers
Cost estimate Nearby drivers
Send proposal Driver job request
Send proposal Driver job request
In-app chat Driver sees a rejected document
In-app chat Driver sees a rejected document
Rate the driver Admin metrics dashboard
Rate the driver Admin metrics dashboard
Driver's ratings (reputation)
Driver My Ratings

Testing

Each package has an automated suite (API β€” Jest; admin β€” Vitest; mobile β€” Flutter), plus a manual end-to-end matrix and a post-deploy verification checklist. How to run each, the strategies, and the device/environment constraints are in TESTING.md.

cd api && npm test        # pricing formula, proposal state machine, auth
cd admin && npm test      # pagination + metrics formatters
cd mobile && flutter test # model / screen / widget tests

Status

All milestones M1–M7 are built (the milestone plan is section 6 of docs/BUILD_SPEC.md):

  • M1 β€” Foundation: monorepo, database schema for all core entities, NestJS-issued JWT auth (argon2, access + rotating refresh), driver verification + admin review.
  • M2 β€” Matching: availability + location capture, PostGIS nearby-driver query (approved and online, nearest first), flutter_map/OpenStreetMap map view + vehicle-type filter, vehicle CRUD.
  • M3 β€” Pricing + jobs: rule-based cost-estimate endpoint + editable config, pin-based job creation and posting (both the estimated cost and the owner-set price are persisted).
  • M3.5 β€” Location: OpenStreetMap place/landmark search + reverse-geocoding + "Open in Maps" navigation hand-off.
  • M4 β€” Transaction loop: proposals (accept/decline), in-app messaging (REST + WebSocket), tel: call button, FCM push (stub-safe).
  • M5 β€” Trust: two-way ratings + portable reputation.
  • M6 β€” Admin: Next.js verification queue + server-computed metrics dashboard + read-only drivers/users/jobs directory.
  • M7 β€” Routing + pricing v2 + navigation: OSRM road routing (with a great-circle fallback), a distance-and-time cost estimate (see Pricing below), and driver in-app turn-by-turn navigation (route line, follow-me camera, voice, off-route rerouting), with "Open in Maps" kept as a secondary option.

Pricing

The cost estimate is rule-based and transparent (not ML β€” there is no transaction history at cold start), computed server-side and rounded to whole RWF (a zero-decimal currency):

estimated_price = max( min_fare(vt),
                       base_fare(vt) + rate_per_km(vt) Γ— distance_km
                                     + rate_per_min(vt) Γ— duration_min
                                     + rate_per_kg(vt) Γ— weight_kg )
                  Γ— size_factor(size)

distance_km and duration_min come from OSRM road routing; if the router is unavailable the estimate falls back to the PostGIS great-circle distance and drops the time term (the weight term is dropped when weight is unknown). Every parameter is DB config (editable without a redeploy); the current v3 values (placeholders pending field research, whole RWF) are:

Vehicle type base_fare rate_per_km rate_per_min rate_per_kg min_fare
moto 800 280 25 6 1,000
pickup 2,500 550 50 8 3,000
van 3,500 700 60 6 4,500
small_truck 5,000 1,000 90 4 7,000
large_truck 8,000 1,600 150 3 12,000

size_factor is 1.0 (small), 1.3 (medium), 1.6 (large), and reflects bulk/awkwardness, not weight β€” weight is priced separately via rate_per_kg. The estimate is a reference β€” the owner sets the final price, and both are stored on the job. Recent changes beyond this README are logged in docs/CHANGES_LOG.md.

Technical report

The full write-up (how each proposal objective was met, a requirements-to-code traceability table, the analysis of results, discussion of the milestones, and future work notes) is in docs/TECHNICAL_REPORT.md.

Roadmap (Future Works)

Loop currently runs as a single Railway project (PostGIS DB + API + admin), chosen for pilot simplicity. The architecture is deliberately portable β€” Dockerised services, a standard DATABASE_URL, and an env-driven DB_SSL flag β€” so the planned production moves below are each a configuration change, not a rewrite:

  • Database β†’ managed Postgres + PostGIS (Supabase): automated backups + point-in-time recovery and a management dashboard, versus the pilot's self-managed container.
  • Admin β†’ Vercel: Next.js-native hosting with per-branch preview deployments.
  • API β†’ Fly.io (Johannesburg region): lower latency to users in Rwanda than EU-region hosting.

A product-side item is admin user management: today the single admin is seeded (no public admin signup, by design), and a later phase adds a super-admin who can create and manage other admin accounts from the admin console.

See DEPLOYMENT.md section 11 (Future / production migration) for the infrastructure detail. Other product/feature future work (payments, live driver tracking, traffic-aware routing and alternate routes) is tracked in docs/BUILD_SPEC.md.

Contributing

main is protected and always deployable, so work happens on short-lived branches and lands through a pull request.

  1. Branch from main: feat/<area>-<desc>, fix/<desc>, or chore/<desc> (e.g. feat/api-road-distance).
  2. Commit in the conventional style β€” feat(api): …, fix(mobile): …, docs: …. Keep messages clean, with no AI-attribution or co-author trailers.
  3. Test the package you touched before opening the PR:
    cd api && npm test          # NestJS (Jest)
    cd admin && npm test        # Next.js (Vitest)
    cd mobile && flutter test   # Flutter
    Per-package setup is in each package's README; conventions live in docs/BUILD_SPEC.md section 9.
  4. Open a pull request against main. A review is required before merge; keep the change scoped to the MVP feature set (check it against "What we are building" and "Out of scope" in the docs).

Secrets never go in git β€” commit .env.example, never a real .env or a service-account key.

License

Proprietary β€” Β© 2026 Habib Josue Ahadi, all rights reserved. The source is available in this repository for reference and academic evaluation only; it may not be copied, modified, redistributed, or used without prior written permission. See LICENSE. Third-party dependencies remain under their own licenses.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages