Inspiration

I'm a student in Bishkek. Last week I had an exam the next morning, walked to a café near Chuy Avenue that looked perfect on Google Maps, and found every seat taken, no outlets free, and Wi-Fi that kept dropping. I ended up studying in a corridor.

That's when I realized the real question isn't "where is a café?" — Google Maps already answers that. The question is "can I actually sit there for three hours right now and get work done?" Nobody answers that.

I checked the WhatsApp groups, the Instagram café tags, the 2GIS reviews — nothing tells you whether a place is quiet tonight, whether the Wi-Fi is working today, whether there are outlets free right now. A review from March says nothing about a Tuesday at 4 PM.

So I built the thing I actually needed.

What it does

FocuzSpot is a study-spot finder for Bishkek, in English and Russian.

You enter what you need — budget, quietness, Wi-Fi, outlets — and it ranks 120+ real study locations across the city by how well they match. Every result explains why it matched ("quiet environment", "free to use", "many outlets") and honestly flags what it doesn't know ("noise level unverified") instead of pretending.

Each location has a detail page with amenities, hours, price, "best for" / "not ideal for" tags, and — the important part — live conditions reported by other users. Occupancy, noise, free outlets, Wi-Fi status, and a short comment. Those reports expire after 2 hours. No stale data. If nobody's reported recently, it says "No recent update" instead of showing you yesterday's snapshot as if it were today's.

You can also save spots, run a study timer while you're there, earn points, and call a Yandex or Namba taxi straight from the location page.

How we built it

Solo build. 11 days.

Frontend: React 19 + Vite + TypeScript, custom CSS (no framework)

Backend & DB: Supabase (PostgreSQL + Auth + Row Level Security) — no custom server

Maps: MapLibre GL + react-map-gl, OpenFreeMap tiles, native clustering

i18n: i18next with a language toggle that persists across sessions

Deploy: Vercel

For the location data I wrote a Node script that hits the OpenStreetMap Overpass API for every café, library, and coworking space inside Bishkek's bounding box (42.80,74.50 → 42.95,74.70), normalizes the tags into my schema, and skips duplicates. That got me ~100 spots on top of the 20 I'd hand-researched. OSM has some garbage — places with no name, or tagged as a café but actually a kiosk — so the curated 20 stay marked as "verified" and the rest are honest about their unknowns.

The matching engine runs client-side. I skipped the Express layer entirely — Supabase handles auth, RLS, and queries, and the ranking is just arithmetic on an array. Zero network round trips for the results page. It's the right call for a demo this size and it removed a whole class of CORS and cold-start problems.

Challenges we ran into

The MapLibre worker broke the production build. Locally the map worked. On Vercel, Rollup failed to resolve import "maplibre-gl/dist/maplibre-gl-worker.mjs". Took two failed deploys to figure out the file is actually called maplibre-gl-csp-worker.js in v5, and that ?worker&url doesn't survive Rollup. Switched to ?url and it worked.

Supabase RLS is not automatic. I created the tables and my seed script immediately got permission denied for table locations. New Supabase projects don't grant service_role or anon any access to fresh tables — you have to GRANT explicitly. Half an hour lost. Worth knowing.

Translation key collision. I used explore.other both as a plural fallback ("X places shown") and as a filter chip label ("Other"). i18next silently resolved it to the chip label, so the map header said "Other" instead of "120 places shown" for a whole day. Lesson: never name a plural key the same as a category key.

Yandex Taxi deep links. My first URL format 404'd. Yandex uses an AppMetrica redirector (3.redirect.appmetrica.yandex.com) that opens the app on mobile and the web version on desktop. Not documented anywhere obvious. Found it in a Stack Overflow answer from 2022.

OSM data is messy. A big chunk of cafés have no name:en or name at all — just amenity=cafe on a random node. I filtered those out, but I still have entries where noise level and outlets are unknown. Instead of guessing, the app shows those as "unverified" and treats them as partial credit in the matching score. Being honest about missing data turned out to be a feature, not a bug.

Accomplishments that we're proud of

The live reports actually expire. It's not a mocked feature. Reports have an expires at column set by a Postgres default (NOW() + INTERVAL '2 hours'), and the query filters out anything past it. When a report ages out, the page quietly switches to "No recent update." That's the part that makes Focuzspot different from yet another map of cafés.

Bilingual from the start, not bolted on. I set up i18next on Day 1 and translated every string, including database-driven values like best for tags and report labels. Toggling EN → РУ changes the entire UI, the map filters, the modal, the profile, everything.

120+ real Bishkek spots loaded from OSM, plus 20 hand-curated ones with full data. That's not "sample data from a CSV." Sierra Coffee on Manas, ololoPlanet on Yunusaliev, the Republican Library on Atay Ogonbaev — real places real students can walk to.

The matching doesn't lie. If a location has no data on noise, it doesn't score 0 and get buried, and it doesn't score full marks and sneak to the top. It gets partial credit and shows a "⚠ unverified" warning. That's the honest version of a recommendation system.

It's actually deployed and works in incognito. Not "it works on my laptop." focuzspot-3.vercel.app.

What we learned

Show unknowns honestly. My first instinct was to fill missing fields with reasonable guesses. I did that for maybe an hour and then deleted them all. Saying "we don't know the noise level here" is more useful to a student than a confident wrong answer. And it turns out users trust an app more when it admits what it doesn't know.

RLS policies are not enabled by default, but they're also not enforced unless you write them. The default Supabase behaviour for a fresh table is "deny everyone." Which is right, but you find out by getting permission errors, not by reading docs.

Production builds are stricter than dev servers. Vite dev mode uses esbuild, which is forgiving about module paths and TypeScript strictness. Rollup, which runs in the production build, is not. The gap between "it works locally" and "it deploys" is almost entirely a bundler-differences problem.

Bishkek-specific integrations matter more than polish. Every taxi app I added — Yandex, Namba, 2GIS — is used daily in Kyrgyzstan. Adding them was 20 lines of code and made the app feel like it was built for this city rather than adapted from a template. Generic is easier; specific is better.

Set up i18n on day one. Retrofitting translations is painful. Doing it while strings are still short is trivial.

What's next for Focuzspot

Short term (next two weeks):

Photo uploads — one user photo per location, so you can see what a spot actually looks like before walking there

Push notifications — when a saved spot gets a fresh report ("Wi-Fi is back at Sierra")

Report confirmation —on each live report so the community self-corrects bad data

Public leaderboard — top 10 scouts of the month. The points system already exists; the leaderboard UI is a day's work

Medium term (one to two months):

AI natural-language search — "quiet spot with fast Wi-Fi for three hours, under 100 som" → structured preferences. This replaces the form with a single input.

Study groups — create or join a group by subject, chat, invite to a spot. Bishkek students already coordinate in WhatsApp groups; this replaces that with something searchable.

Personal analytics — weekly hours, favourite spots, best study times

Long term:

Expand beyond Bishkek — Almaty, Tashkent, Nur-Sultan. The matching engine and schema already handle multi-city; it's just data.

Native app — React Native build for the App Store and Play Store so students don't have to open a browser.

Verified contributor program — cafés and libraries can claim their page, update hours, and respond to reports

The core idea — "where can I actually study right now?" — has a clear answer in every city with a university. Bishkek is where it starts.

Built With

Share this project:

Updates

Submission history