
Honest street reviews for Lagos. From zero to live.
I designed and built Spotta, Nigeria's first street review platform, from concept to production in under two months. 1,590 streets. 1,083 reviews. 28 neighborhoods. Next.js, Supabase, and AI tools turning Figma files into live code.

Moving in Lagos is a gamble.
When someone in Lagos is looking for a place to live, they're asking real questions: Does this street have stable power? Does it flood? Is it safe at night? Is the water situation manageable? There's no reliable way to find out. You ask around, you visit, you hope. Google Maps tells you the street exists. Not whether the power supply will keep your fridge running.
I wanted to build the platform that answers those questions. A "Yelp for streets" where real residents review infrastructure (power, water, security, roads, flooding, internet, noise, and environment) so the next person moving in knows exactly what they're getting into.
The catch? I'm a product designer, not a full-stack engineer. I'd need to design the product, build the front-end, configure the database, handle authentication, manage street data for an entire city, optimize for search engines, and ship it live. So I used AI as my engineering partner.
Before designing screens, I needed streets.
A street review platform is only as good as its street data. I needed structured, accurate records for thousands of Lagos streets (name, area, local government area), organized in a way that prevents duplicates and supports search. Three approaches, each with tradeoffs:
Google Maps API – too expensive
The obvious choice. Google's Places API has excellent Lagos coverage, but the pricing at scale made it a non-starter for a bootstrapped civic project. Every search query, every place detail call. Costs add up fast when you're covering an entire city.
OpenStreetMap – the normalization trap
Free and open. I implemented OSM search so the homepage fetched from OSM, and streets would only be created in my database when a user left their first review, keeping the database lean. But I immediately hit a critical problem: boundary inconsistencies. The same street appeared under different area names depending on which OSM boundary polygon contained it. A street on the edge of two neighborhoods could show up twice with different parents. This defeated the entire purpose of a structured review platform.
Build my own system – with AI deduplication
I built an external tool using AI to scan the OSM-sourced data, find and flag duplicates across boundary zones, and present them for manual resolution. I'd resolve the conflicts, export clean data to XLSX, then bulk-import it through my admin dashboard. Way faster than adding streets individually. And the data was mine to control.
The review form I shipped first was wrong.
The first version of the review form let users select multiple categories (Parking Lot, Nightlife, Hospitals…) but only give one overall star rating. This felt simpler, but the data it produced was useless. A street with great security but terrible roads would get 3.5 stars. And that number tells you nothing about either.
After user feedback, I rebuilt the form so every selected category gets its own rating. Each user's individual category ratings combine into their overall score, and those aggregate into the street's per-category breakdown. Now when you look at a street page, you see Security ★4.5, Power ★3.3, Roads ★2.0, information you can actually act on.
One score for everything
Users pick categories but give one star rating. Simple form, but produces a meaningless average that masks what actually matters about a street.

Rate what you selected
Each category gets its own input. Stars for quality metrics (Power, Security). Frequency sliders for yes/no phenomena (Flood: Never→Always, Noise). Overall auto-calculated.

Not everything is a star rating.
When I started implementing per-category ratings, I hit a design problem: flooding isn't a quality you rate on a scale. A street either floods or it doesn't, with frequency in between. Same with noise. You don't give noise "4 stars". That doesn't mean anything.
The solution was contextual input types. Power, water, security, roads, and internet use star ratings, because quality is what matters. Flood and noise use frequency sliders (Never → Rarely → Sometimes → Often → Always). The slider maps to a 0–5 value: "Never" = 5 (best), "Always" = 0 (worst). This lets the system compute a meaningful aggregate while giving users an input that matches how they actually think about the data.
On the street page, these render differently too. Stars show as bar charts with numeric ratings. Flood and noise show as badges: "Never floods", "Mostly quiet", human-readable summaries derived from aggregate slider positions.
A dashboard for every street in Lagos.
Each street page is a living profile: reviews with threaded replies, category filter pills, photo uploads with lightbox, a shareable deep link per review, and the Street Analysis sidebar with ratings, sentiment analysis, cost of living, and property tabs.




The problems that made me a better builder.
Building a production web app from a design background meant solving problems I'd never encountered in Figma. Each one taught me something about how software actually works, and made me a sharper designer for it.
The authentication puzzle
Google OAuth redirects users away from the app and back. During that redirect, the system lost track of profile creation – accounts existed but profiles didn't. The fix: localStorage flags written before the redirect, checked on return. Now every Google sign-in creates a complete profile with auto-generated avatar and username derived from email. localStorage
The speed problem – 150+ calculations per page load
Avatar display was recalculating the image source for every review on every re-render. With 20 reviews on a page, that's 20 recalculations per state change. I learned about memoization – calculate once, remember the answer, only recalculate when the source data changes. Went from 150+ calculations to 2–3 per page load.
The SEO breakthrough – client-side to server-side rendering
With client-side rendering, Google saw empty pages – all content loaded after JavaScript executed. Search engines couldn't index the reviews, ratings, or street data. I migrated to server-side rendering so pages arrive fully formed with structured data (JSON-LD). Now searching "Allen Avenue Ikeja reviews" can surface Spotta with star ratings directly in Google results.
The bookmark ghost – state persisting after logout
Users would log out but bookmarked streets still appeared bookmarked. The system wasn't checking for an active session before rendering bookmark state. A small detail, but one that erodes trust. The fix was a session check that resets bookmark UI on logout. Designing the code taught me how much "trust" lives in tiny state management details.
The admin dashboard:
controlling the data.
Managing 1,590 streets and 1,083 reviews across 28 neighborhoods requires tooling. I designed and built a full admin dashboard (overview analytics, area-level drill-downs, street management with inline review previews, category configuration, and user management), all with the same AI-assisted development workflow as the public site.
Overview Dashboard
Total users, reviews, streets covered, average rating. User activity chart and recent activity feed.
Area Analytics
28 areas with street count, total reviews, average rating, and growth percentage.
Street Management
Full street list with area, state, LGA. Click any street for a detail panel with recent reviews.
Category Performance
See which categories users care about most, with trend indicators.
Bulk Import
Upload XLSX with hundreds of streets. System validates, generates URL slugs automatically.
Category Config
Create, edit, enable/disable review categories. Assign colors, set display order.

Building a community, not just a tool.
A review platform dies without contributors. I designed a user system that rewards participation: profile pages with review history and activity stats, a contributor level system (Neighborhood Scout → leveling up), and achievements.
First Steps, Regular Contributor, Area Explorer, Well-Rounded (review across 4+ categories), Monthly Enthusiast. Think Google Local Guides, built for Lagos streets.

Your review history, your impact
Every review you've written, organized by street. Sidebar shows activity stats (reviews, bookmarks, upvotes, replies), contributor level with progress bar, and key insights: most reviewed area, average rating, most active month.

Motivating local guides
Structured achievement tiers (Beginner, Explorer) with progress tracking. First Steps, Bookmarker, Conversationalist – completed. Regular Contributor (2/5), Collection Builder (1/5), Area Explorer (2/5) – in progress. Forward-thinking design to motivate sustained contribution.
Figma → AI → Production.
I'm a product designer who leverages AI to ship. The workflow: design in Figma, then use v0, Claude, ChatGPT, and Trae to convert designs into production code. v0 handled initial component scaffolding. Claude and ChatGPT helped me solve backend logic I'd never written before: Row Level Security policies, OAuth flows, database migrations. Trae helped with iteration speed.
This isn't "AI built my product." I made every product decision, designed every screen, and directed every implementation. AI was the engineering team I didn't have. And learning to collaborate with it made me understand code in ways that fundamentally changed how I design. I now think about state management when I design interactions, data models when I design information architecture, and rendering strategies when I think about SEO.
Live in production. Real users. Real data.
What building taught me about designing.
Spotta changed how I work. Before this project, I was a product designer who handed off specs. After Spotta, I'm a product designer who understands what happens after the handoff, and that understanding makes every design decision sharper.
State is a design material
The bookmark ghost bug taught me that UI state isn't just 'checked vs unchecked', it's a function of session, authentication, and persistence. Now when I design interactive states, I think about where the truth lives.
Data models shape UX
The review form evolution happened because the data model (one rating vs per-category ratings) determined what the product could even show. Designing the database schema is designing the user experience.
Performance is a feature
150+ unnecessary calculations per page load is invisible in Figma but devastating on a phone in Lagos. The memoization lesson taught me that what I don't render matters as much as what I do.
SEO is information architecture
Switching from client-side to server-side rendering wasn't a 'dev task'. It was a product decision about discoverability. If Google can't read your page, it doesn't exist. Structured data is IA for machines.
Designing an energy-aware calendar that respects your body's rhythms.