- Frontend: Vite + React 19 + TypeScript + Tailwind CSS v4
- Map: mapcn (MapLibre GL via shadcn/ui) — free CARTO tiles, no API key
- Routing: OSRM (router.project-osrm.org) — free, no key
- Theme: Dark/light via CSS variables + ThemeProvider context
- i18n: react-i18next + i18next-browser-languagedetector (pt default, en fallback)
- UI: shadcn/ui (Button, Map, MapMarker, MapRoute, etc.)
- Backend: Supabase (Postgres + Auth + Storage)
- Data Fetching: TanStack Query (@tanstack/react-query) — caching, mutations, auto-refetch
- Validation: Zod (zod) — schema-based form validation
- Icons: Tabler Icons (@tabler/icons-react), lucide-react (shadcn dependency)
- Font: DotGothic16 (Google Fonts)
User requested Vite instead of Next.js. React Router v7 handles SPA routing.
mapcn shadcn/ui component wrapping Maplibre GL JS:
- Free CARTO basemaps (dark-matter / positron, theme-synced)
- Built-in controls (zoom, compass, locate, fullscreen)
<MapMarker>with<MarkerContent>and<MarkerTooltip><MapRoute>for OSRM route overlays<MapClusterLayer>for clustered markers- No API key required
Public OSRM API at router.project-osrm.org for driving directions:
- Route geometry rendered as
<MapRoute>on the map - Distance/duration displayed in route info panel
- Browser Geolocation API for user origin
- Dark theme default via
class="dark"on<html> .lightCSS class overrides shadcn variables + DevSpot custom colorsThemeProvidercontext with localStorage persistence- Map basemap syncs automatically (CARTO dark-matter ↔ positron)
- Portuguese (Angolan) default language, English fallback
- Language detection via localStorage → navigator
- All user-facing strings in
src/lib/i18n.ts LanguageSwitcherin header toggles pt ↔ en- All hardcoded English strings replaced with t() calls
- TanStack Query for all Supabase data operations
useQueryfor reads (places, reviews, profile)useMutationfor writes (createPlace, createReview)- Automatic cache invalidation on mutations
- staleTime: 15-30s for responsive UI
- Zod schemas in
src/lib/schemas.ts(placeSchema, reviewSchema) - Real-time field validation on submit
- Error messages use i18n t() keys
- Address field with Nominatim autocomplete geocoding
- Text input with Nominatim OpenStreetMap API autocomplete
- 500ms debounce, 1 req/sec rate limit respected
- Country filter: Angola (ao)
- Lat/lng populated from selected address result
- Falls back to geolocation toggle for location
TanStack Query for server state. React context for theme + auth. Local component state for forms and UI.
Tailwind v4 for utility classes + custom .ds-* component classes. Shadcn CSS variables for system-level theming.
Ctrl/Cmd+K opens overlay with 18 functional actions:
- Submit place, toggle language, toggle theme
- Switch view (split/map/list)
- Toggle filters (vibe, use case, type)
- Arrow key navigation, Enter to execute
- All labels use t() for i18n
- Project scaffold (Vite + React + TS + Tailwind)
- Design tokens in Tailwind theme
- Component CSS classes (ds-pill, ds-tag, ds-card, etc.)
- TypeScript types (Place, PlaceWithRating, Review, User, Filters, etc.)
- shadcn/ui + mapcn initialized
- Supabase project created and linked (brdesbsngyoqixfdnfgd)
- Database schema deployed (places, reviews, profiles, saves + RLS + storage)
- Auth context (signIn/signUp/signOut/Google OAuth)
- Auth pages (SignInPage, SignUpPage)
- Supabase data service (fetchPlaces, createPlace, uploadPhoto, etc.)
- Photo upload to Supabase Storage (place-photos bucket)
- Auth guard: submit requires login (redirects to /signin)
- Map component (DevSpotMap with mapcn/MapLibre GL)
- OSRM routing integration (route from user location to selected place)
- Mapbox removed (no references, no types package)
- Theme-synced map basemap (dark ↔ light CARTO)
- i18n setup (react-i18next, pt/en locales)
- LanguageSwitcher in header
- All components use useTranslation() — no hardcoded strings
- Default language set to Portuguese
- Complete PT ↔ EN translation coverage
- Dark theme default (class="dark" on html)
- Light theme via .light CSS class
- ThemeProvider with localStorage persistence
- Map theme sync
- Theme toggle button in header (sun/moon icon)
- Ctrl/Cmd+K overlay with keyboard navigation
- i18n support for all labels and groups
- Functional: submit, lang, theme toggle
- Functional: view switch (split/map/list)
- Functional: filter toggle (vibe, use, type)
- @tanstack/react-query installed
- QueryClientProvider wrapping app
- useQuery for places, reviews, profile data
- useMutation for createPlace, createReview, deletePlace, deleteReview
- Cache invalidation on all mutations for auto-refresh
- Zod schemas (placeSchema, reviewSchema)
- Validation errors displayed below fields
- i18n error messages via t()
- Submit disabled during mutation (loading state)
- Address text field with Nominatim autocomplete
- Geolocation toggle (ON/OFF) as alternative
- Lat/lng populated from selected address
- Validation: address required when location OFF
- Home (TanStack Query, real ratings, cmd palette, auth guard)
- PlaceDetailPage (TanStack Query, real reviews, photos, reviews CRUD, place delete, route)
- SignInPage (i18n, email/password + Google OAuth)
- SignUpPage (i18n, username field, auto-profile creation)
- ProfilePage (TanStack Query, real places with ratings, inline bio editing, sign-out)
- WiFi/noise/power badges on review cards
- Edit/delete own reviews (pre-filled form, confirm dialog)
- Delete own places (confirm dialog, cached invalidation)
- Profile auto-creation (DB trigger + client-side upsert)
- Mobile hamburger menu (theme, K, lang, view, profile, sign-out, submit)
- Profile icon in navbar (links to /profile/{username})
- Logout overlay (centered message, fade-out animation)
- Security audit completed (no service_role, no user_metadata in RLS, no SECURITY DEFINER)
- Security patches applied (RLS for all tables + storage, CSP headers, file validation, npm audit zero vulns)
- Seed data (sample places/reviews for Luanda)
- Code splitting for maplibre-gl (large chunk warning)
- Real-time updates via Supabase subscriptions
- Keep component CSS classes matching the HTML prototype exactly
- Use Tabler Icons for all iconography (lucide-react only for shadcn internals)
- Follow the dark/light monospace, terminal-like aesthetic
- Portuguese default for Portuguese-speaking market (Luanda)
- Write clean, minimal code without comments
- Commit each file with a comprehensive message
- Build must pass before committing
- Use TanStack Query for all server data
- Don't add comments to code
- Don't use emojis in code — use Tabler Icons
- Don't over-engineer state management
- Don't add features not in the PRD scope
- Don't hardcode strings — always use t()
Production-ready MVP with TanStack Query, Zod validation, real Supabase data, and responsive UI. mapcn/MapLibre GL map with theme-synced basemaps. OSRM routing. Full i18n (Portuguese default, English toggle) with no hardcoded strings. Command palette (Ctrl+K) with 18 actions and i18n labels. Dark/light theme with header toggle button and persistence. Auth with login/signup/Google OAuth. Photo uploads to Supabase Storage. Address input with Nominatim geocoding. Place ratings calculated from real reviews. WiFi/noise/power badges on reviews. Edit/delete own content. Mobile-responsive hamburger menu. Logout animation. Security audited and patched.
- Chunk size warning for maplibre-gl (needs code splitting)
- No seed data for Luanda places in the database
- Supabase Storage buckets need manual creation (migration provided)