onmangekoi

Duration
4 months
Date
April 2026
Homepage of onmangekoi
Homepage of onmangekoi
Homepage of onmangekoi in dark mode
Homepage of onmangekoi in dark mode
First visit, a pseudonym is enough
First visit, a pseudonym is enough
Social card of onmangekoi
Social card of onmangekoi

Preface

"Where do we eat?" followed by "I don't know, whatever you want" is a ten-minute conversation that happens every day in every office. onmangekoi turns it into a two-minute vote.

The host picks a few restaurants, shares a link, a QR code or a six-character code you can say out loud, and each participant votes on each restaurant: meh, fine by me, favourite, or veto. When everyone has voted, the session closes itself and the ranking is shown. No account is needed, a pseudonym is enough.

You can try it here: www.onmangekoi.fr. The code is open source: onmangekoi on GitHub.

Stacks involved

  • Next.js 16 (App Router, Cache Components) and React 19
  • TypeScript, Tailwind CSS v4, shadcn/ui, Base UI
  • Supabase: PostgreSQL 17, Row Level Security, Realtime, Auth, pg_cron
  • Google Places API (New)
  • PostHog (EU), consent-gated
  • Vitest, Testing Library, Playwright, GitHub Actions, Bun, Vercel

Key capabilities

  • Restaurant voting sessions with four vote values and one joker each for favourite and veto
  • Sharing by spoken 6-character code, link or QR code, with a camera scanner to join
  • Saved restaurant lists, manual entries and import from Google Places
  • Realtime session room: participants and progress update live
  • Optional email account to recover lists across devices
  • GDPR self-service: export all my data, delete my account

Details

The rules of the game live in the database, not in the UI. Every write goes through a transactional PostgreSQL function: create a session, join it, launch it, submit a vote, close it. Row Level Security is on for every table, individual votes are never readable, only aggregates are. The front-end cannot cheat even if it wanted to.

Sessions and lists are addressed by short Crockford base32 codes, six characters for a session and ten for a list, so no UUID ever appears in a URL and a code can be read over the phone. Input is forgiving: lowercase, spaces, dashes, a confused "O" for "0", or a whole pasted link all resolve.

The app uses Next.js 16 Cache Components: every route pre-renders a static shell and personal data streams into Suspense boundaries. Only the public restaurant catalogue is cached, through a cookie-less client, so nothing personal ever enters a shared cache.

Role

Solo project, from the Supabase schema and migrations to the visual identity, the front-end, the CI and the deployment. The MVP was bootstrapped in a day, then rebuilt on a hardened database core with a new design system.

What I built

The voting engine

  • Transactional, security-definer RPCs for every state change
  • Automatic close when the last participant finishes, or forced close by the host
  • Aggregated results only; votes stay private

Restaurants

  • Seeded catalogue, favourites lists that can be shared and copied
  • Manual restaurant creation
  • Google Places import with two field masks (cheap for search, expensive only on import), a 24-hour cache and idempotent upserts by place id
  • Opening-hours engine that handles overnight periods and time zones
  • Key-less winner mini-map built from OpenStreetMap tiles

Realtime and sharing

  • Supabase Realtime on sessions and participants, with a light polling safety net and resync on foreground
  • QR code generation for the host, native BarcodeDetector scanning with a lazy jsQR fallback for guests
  • Per-invitation dynamic Open Graph image for shared links

Operations and privacy

  • Nightly pg_cron maintenance purging inactive anonymous accounts and stale sessions
  • JSON export and one-transaction account deletion, with rankings surviving as "deleted participant"
  • PostHog double-gated (no key, no module; no consent, no SDK), URLs masked because the code in the URL is the access secret
  • Typed event catalogue so an unplanned analytics property fails compilation

Quality

  • 31 unit and component test files, an end-to-end Playwright spec run against a real local Supabase stack in CI
  • SQL scenario tests replaying the GDPR flows
  • A database workflow that can be driven from GitHub on a phone: check, types, pull, plan, push

Technical achievements

Business rules enforced by PostgreSQL

Sessions, joins, votes and closing are all database functions with their own invariants. The application layer is thin and the guarantees hold whatever the client does.

Zero friction, real accounts underneath

A visitor picks a pseudonym and is already an anonymous Supabase user. Adding an email later upgrades the same account, so nothing is lost and no session is interrupted.

Shared cache without leaks

Cache Components made it possible to serve a static shell on every route while keeping a strict invariant: nothing personal in a shared cache.

In short

onmangekoi is a small product with a serious core. It solves an everyday problem in two minutes, without an account, and the interesting engineering is under the surface: database-enforced rules, realtime sessions, privacy-first analytics and GDPR by design. It is live at www.onmangekoi.fr.

Similar projects

Homepage of tripbrain.fr with the live demo embedded in a phone mockup
The TripBrain roadbook on mobile
TripBrain
TripBrain
Duration
6 months
Date
March 2026
Homepage of song choicer
Homepage of song choicer mobile
Song choicer logo
Song Choicer
Duration
3 week
Date
July 2024
Documentation homepage of bucketcode
Documentation homepage of bucketcode on mobile
bucketcode
bucketcode
Duration
2 weeks
Date
August 2026
Logo

© All rights reserved by Abderrahmane Mouzoune