mwn

Merwan Laouini

I build software end to end, from the database schema to the day it goes live.

A system, in section.

Each product exploded into its real layers, from the interface down to the infrastructure. The routes and models below are read from the projects' code, not drawn for the occasion.

NODE_01

Reputap

Google review collection via NFC, multi-location, with abuse controls.

In production

Hover the interface to follow a piece of data down to the database.

Read the case study
L0 · Interface
Reputap merchant dashboard: total scans, conversion rate, loyal customers, prizes handed out and real-time activity for a demo bakery.
L1 · Routes and actions
  • GET/s/[publicToken]resolves the tag, creates the scan session
  • GET/r/[token]short redirect printed on the plate
  • POST/api/session/[sessionToken]/review-linkrecords the review intent, opens Google
  • POST/api/session/[sessionToken]/resultserver draw, ticket issued
  • GET/win/[campaignId]prize wheel, plays the result
  • GET/api/merchant/dashboardestablishment figures
  • GET/admin/campaigns/[id]anti-abuse rules, wheel and prizes per campaign
  • GET/admin/onboardingtenant and first establishment setup
  • GET/api/admin/billing/overviewsubscriptions, invoices
  • GET/api/admin/system/healthdatabase latency, disk, logs
L2 · Data model
  • Tenantidnameslugstatus
  • EstablishmenttenantIdnamecitytimezonegoogleReviewUrl
  • NfcTagestablishmentIdcampaignIdpublicTokenlastRotatedAt
  • CampaignestablishmentIdtypestatusconfigJsonstartsAt
  • ScanSessionnfcTagIdsessionTokendeviceIdipHashstatusexpiresAt
  • ReviewIntentscanSessionIdprovidertargetUrl
  • PrizewheelIdnameweightdailyCaptotalCap
  • SpinscanSessionIdwheelIdprizeIdresultType
  • ClaimspinIdclaimCodestatusexpiresAtredeemedByMembershipIdrejectionReason
L3 · Infrastructure
  • Vercelhosting, server functions
  • PostgreSQL · Supabasedatabase, tenant isolation
  • Prismamodel and migrations
  • Auth.jssessions, super_admin / owner / user roles
  • Resendtransactional email
  • Stripebilling
NODE_02

KastMe

An interactive portfolio at one address, for artists, models and photographers.

Building

Hover the interface to follow a piece of data down to the database.

Read the case study
L0 · Interface
KastMe demo public page: a fictional wildlife photographer's photos float in a sphere around the profile card and a booking button.
L1 · Routes and actions
  • GET/[slug]public page, SSR + ISR, generated OG image
  • GET/p/[id]old URL, 301 to the slug
  • GET/registerFirebase Auth sign-up
  • GET/api/slug/checkavailability and reserved names
  • GET/editor/[[...id]]sphere and media editor
  • PATCH/api/portfolios/[id]save, validated with Zod
  • POST/api/trackdaily visit statistics
  • GET/api/account/exportaccount data export
L2 · Data model
  • users/{uid}planrolecreatedAt
  • PortfoliouserIdslugpreviousSlugspublishedprofilegallerytheme
  • Profilenameprofessionscity
  • MediatypeurlthumbUrlposterUrlwidthheightalt
  • Linkidlabelurlfeatured
  • ThemepresetmonochromedensityrotationSpeedbackgroundColor
  • slugs/{slug}userId
  • stats/{day}viewsclicks
L3 · Infrastructure
  • Vercelhosting, ISR, OG images
  • Firebase Authcreator accounts
  • Firestoredocuments, tested security rules
  • Cloud Storage10 MB images, 50 MB videos
  • Firebase Adminserver-side writes (plan, role)
NODE_03

Capsule Manufacturing

From quote to production tracking, for a small-series textile workshop.

Building

Hover the interface to follow a piece of data down to the database.

Read the case study
L0 · Interface
Project page on the workshop side: manufacturing details, delivery address and nine-step production tracker with the current step highlighted.
L1 · Routes and actions
  • GET/marketing site and six-step quote configurator
  • POST/api/quoteprivate tech-pack upload, quote persisted
  • GET/auth/signinbrand via magic link, workshop via credentials
  • GET/admin/quotes/[id]pricing: unit price, setup fees, lead time
  • PATCH/api/admin/quotes/[id]PRICED creates the client account, PAID creates the project
  • POST/api/client/quotes/[id]/pay(simulated) payment then project and shipment
  • PATCH/api/admin/projects/[id]move to the next step
  • GET/client/projects/[id]same timeline on the brand side
  • PATCH/api/client/projects/[id]/addressaddress editable until shipped
  • GET/admin/projects/[id]/delivery-slipA4 delivery slip, browser print
  • GET/admin/materialsfabric receptions, screen present, entry to come
L2 · Data model
  • UseremailbrandNameroleemailVerified
  • QuoteproductmaterialTypegrammagebrandingbrandingLocationsquantityunitPricesetupFeesleadTimeschemaFilestatus
  • ProjectreferencequoteIduserIdproductquantitystatus
  • MaterialprojectIdcarriertrackingNumberquantityMetersreceivedAt
  • ShipmentprojectIdtypecarrieraddressshippedAtdeliveredAt
  • SessionsessionTokenuserIdexpires
L3 · Infrastructure
  • Next.js 16App Router, React Compiler, proxy.ts
  • MySQL · Prisma 6relational model, eight tables
  • NextAuth 4JWT, magic link and credentials, ADMIN / CLIENT roles
  • Stockage privétech packs outside the public folder
  • Impression A4delivery slip without a PDF library