Skip to content

Work

2025

LensMap — a marketplace for photographers

A PWA marketplace: find a photographer on a map, public profiles, booking requests with a deposit, an admin panel, RU/EN.

Role

  • Architecture
  • Development
  • DevOps
  • SEO

Stack

  • Next.js 15
  • Supabase
  • PostgreSQL
  • Vercel
  • Stripe
  • OpenAI

The map as the primary search interface

A photographer is chosen by where the shoot happens, so the map here is not a widget above a list — it is the search tool itself. The list and the markers read from the same filtered set; two separate sources would drift apart the first time someone changed the genre or price filter. In a dense city centre dozens of pins land in one block and the map turns into a smear, so markers are clustered rather than thinned out: dropping a photographer from the results because they happened to stand near another one is not an acceptable failure.

Public profiles are the product's search surface

In a marketplace, organic traffic does not arrive at the home page — it arrives at one specific provider's page. So a profile, with its portfolio, reviews, pricing and availability calendar, is rendered on the server and indexed like an ordinary page instead of being assembled on the client inside a card application. That decision also set a boundary: the map is allowed to be heavy and interactive, but a profile has to open and be complete without a JavaScript application wrapped around it.

Enquiry and booking are kept apart

An enquiry and a payment are two different commitments, and merging them into one step loses everyone who is still only checking a date. The request and the conversation with the photographer come first; the Stripe deposit only after the booking is confirmed. The deposit exists precisely as the cost of cancelling: a booking that costs nothing to make costs nothing to drop, which makes the other side's calendar worthless.

An admin panel built for the photographer, not the administrator

The photographer manages their own profile, portfolio, pricing and schedule without contacting support — otherwise every price change becomes a ticket, and on a marketplace this size support is one person. The panel shows each provider the statistics for their own views and requests, because without that feedback they cannot tell why an empty calendar is empty. This is in-product analytics for the profile owner, not a marketing promise about results.

Authorisation lives in the database, not in the interface

Row Level Security in Supabase means access rules are described in the database rather than in application code: a missed check in the UI cannot show one photographer another's booking requests, because the rows simply do not come back from the query. The price is that debugging a policy is harder than debugging an ordinary conditional, and a mistake in a policy breaks the whole product rather than a single page. On a marketplace where someone else's personal data and payment amounts sit in the same table as yours, that is the right trade. Sign-in is OAuth 2.0, with email and phone verification.

RU/EN as two versions of the content, not two dictionaries

Not just the interface is localised but the user-generated content too, so a language is a separate version of the page with its own URL and hreflang rather than a label switcher over identical data. The language is detected automatically on a first visit, and that is a deliberate compromise: for a marketplace with an international audience, guessing right helps more often than it hurts. The studio's own site makes the opposite call — there, language only changes on an explicit action.

  • PWA
  • Marketplace
  • AI
  • Maps