Candidate: Bellamkonda Sai Mukesh Ram Email: mukeshrambellamkonda@gmail.com
This repo contains my solution for the Frontend Engineer Internship assignment at Flowbit Private Limited.
The app is a small single-page application that:
- Displays a map with a WMS orthophoto layer (NRW).
- Lets the user click on the map to define an AOI (Area of Interest) as a point.
- Stores AOIs in a sidebar list and on the map.
- Persists AOIs in
localStorageso they survive page reloads. - Includes end-to-end tests with Playwright.
- React (with TypeScript)
- Vite (bundler / dev server)
- Tailwind CSS (styling)
- React-Leaflet + Leaflet (map rendering, WMS)
- Playwright (end-to-end tests)
- localStorage (client-side persistence, no backend)
- Node.js (LTS version recommended)
- npm (comes with Node)
# Install dependencies
npm install. ├── src │ ├── components │ │ ├── Header.tsx # Top bar (title, context) │ │ ├── Sidebar.tsx # AOI creation + list + layer toggle │ │ └── MapView.tsx # Map, WMS layer, AOI markers │ │ │ ├── hooks │ │ └── useAoiStore.ts # AOI state management + localStorage │ │ │ ├── types │ │ └── aoi.ts # AOI / Geometry TypeScript types │ │ │ ├── App.tsx # Main layout and wiring of components │ └── main.tsx # React entry point │ ├── playwright │ └── tests │ ├── basic.spec.ts # App loads + core UI available │ └── aoi.spec.ts # AOI creation flow e2e test │ ├── playwright.config.ts # Playwright configuration ├── vite.config.ts # Vite + Tailwind setup ├── tsconfig.json # TypeScript configuration └── README.md
Chosen: React-Leaflet + Leaflet
I chose Leaflet with React-Leaflet bindings for this assignment.
Reasons:
WMS support is first-class Leaflet has built-in support for WMS layers (L.tileLayer.wms), and React-Leaflet exposes this as WMSTileLayer. That made it straightforward to add the NRW orthophoto WMS layer.
Simple mental model with React React-Leaflet maps map elements (markers, popups, layers) to React components, which fits naturally with React’s component structure and props/state.
Mature ecosystem Leaflet is very mature and has many plugins (drawing, clustering, heatmaps) that could be used in the future for more advanced AOI tools and performance optimizations.
Alternatives considered
MapLibre GL JS
Pros: WebGL-based, very good for vector tiles, great performance with many features.
Cons: WMS integration is not as direct; would require more custom work or intermediate services.
OpenLayers
Pros: Very powerful and feature-rich, good support for different projections and WMS/WFS.
Cons: Higher learning curve and a slightly heavier API; might be overkill for a small assignment.
react-map-gl (Mapbox / MapLibre)
Pros: Great for highly interactive GL maps.
Cons: Oriented more toward vector tiles; WMS is not a primary use case.
Given the time constraints and the WMS requirement, React-Leaflet was the most pragmatic choice.
Component structure
App.tsx
Top-level layout (Header, Sidebar, MapView).
Connects AOI store with map and sidebar.
Manages WMS visibility and the “draft point” selected on the map.
Header.tsx
Simple static header matching the Figma style (title, subtitle, context text).
Sidebar.tsx
Layer management: toggle visibility of the WMS orthophoto layer.
AOI creation panel:
Shows when a point has been selected on the map.
Form for name + optional description.
AOI list:
Displays saved AOIs with coordinates.
Selecting an AOI focuses it on the map.
Delete button to remove AOIs.
MapView.tsx
Configures the Leaflet map and base OSM tiles.
Adds the NRW WMS orthophoto layer via WMSTileLayer.
Renders AOI markers + popups for each AOI.
Handles map click to create a “draft” AOI position.
Centers/flyTo the selected AOI when chosen from the sidebar.
State management
Custom hook: useAoiStore.ts
Holds the AOI array and the selected AOI.
Exposes addAoi, updateAoi, removeAoi, setSelectedId.
Persists AOIs in localStorage under a fixed key.
This acts as a tiny client-side “store” instead of bringing in Redux or Zustand.
This keeps data logic centralized in one hook while keeping UI components mostly presentational.
For this assignment, the main entity is an AOI (Area of Interest).
AOI TypeScript model type GeometryType = 'point' | 'polygon'
interface AoiGeometry { type: GeometryType coordinates: [number, number][] // [lat, lng] }
interface Aoi { id: string name: string description?: string geometry: AoiGeometry createdAt: string updatedAt: string }
AOI
- id (UUID)
- name
- description
- geometryType (e.g. "Point", "Polygon")
- coordinates (array of [lat, lng] or GeoJSON)
- createdAt
- updatedAt
User 1 --- * AOI
User
- id (UUID)
- name
For this assignment there is no real backend; data is stored in localStorage. However, the frontend is designed as if it were talking to a REST API.
Planned endpoints
GET /api/aois – list all AOIs for the current user
GET /api/aois/:id – get details for a single AOI
POST /api/aois – create a new AOI
PATCH /api/aois/:id – update an AOI
DELETE /api/aois/:id – delete an AOI
Example: GET /api/aois
Response 200 OK
[ { "id": "aoi_1", "name": "Field 1 – North", "description": "Test AOI near Dortmund", "geometry": { "type": "point", "coordinates": [[51.518, 7.465]] }, "createdAt": "2025-11-29T11:32:00.000Z", "updatedAt": "2025-11-29T11:32:00.000Z" } ]
{ "name": "Field 1 – North", "description": "Test AOI near Dortmund", "geometry": { "type": "point", "coordinates": [[51.518, 7.465]] } } Response 201 Created { "id": "aoi_1", "name": "Field 1 – North", "description": "Test AOI near Dortmund", "geometry": { "type": "point", "coordinates": [[51.518, 7.465]] }, "createdAt": "2025-11-29T11:32:00.000Z", "updatedAt": "2025-11-29T11:32:00.000Z" } Example: DELETE /api/aois/:id
Response 204 No Content
Empty body.
Mapping to the current implementation
In this project, these endpoints are simulated by useAoiStore:
addAoi ≈ POST /api/aois
updateAoi ≈ PATCH /api/aois/:id
removeAoi ≈ DELETE /api/aois/:id
Initial load from localStorage ≈ GET /api/aois
- Native Leaflet interactions are enabled including:
- Scroll wheel zoom
- Double-click zoom
- Touch pinch zoom
- Drag-to-pan navigation
- Default + / – zoom controls
Base map: OpenStreetMap tiles.
WMS overlay: NRW orthophoto layer from https://www.wms.nrw.de/geobasis/wms_nw_dop
Interactive features:
Pan and zoom using mouse/trackpad.
Click on the map to pick an AOI point.
AOI markers are shown for each saved AOI.
Clicking a marker or choosing an AOI in the sidebar focuses the map on that AOI.
The current implementation is small, but I designed it with growth in mind.
If the app needs to handle thousands of points/polygons, I would:
Reduce React re-renders
Keep large geometry collections in Leaflet layers (L.GeoJSON) instead of individual React components per feature.
Use useMemo / React.memo for map subcomponents that don’t need to re-render often.
Use clustering / WebGL
Use a marker clustering plugin or a WebGL-based renderer when there are many point AOIs.
If requirements become very heavy, consider switching the rendering layer to a WebGL library like MapLibre GL for performance.
Virtualize long lists
If the AOI list in the sidebar becomes very long, use a virtualized list (e.g. react-window) to only render visible items.
Debounce expensive operations
Debounce map move/zoom events before running expensive computations.
Debounce any text search / geocoding to reduce API calls.
Backend-side filtering (future)
For a real backend service, introduce server-side bounding-box filters so only AOIs in the current viewport are loaded.
What is tested now
I used Playwright for end-to-end testing:
basic.spec.ts
Visits the app.
Asserts that:
The AOI Creation UI (header and sidebar) is visible.
The map container is rendered.
aoi.spec.ts
Clicks on the Leaflet map to select a point.
Fills in the AOI name.
Clicks “Save AOI”.
Verifies that:
The AOI count in the sidebar increments.
The new AOI name appears in the list.
These tests focus on the critical user flows:
“App loads correctly”
“User can create an AOI from the map”
What I would test with more time
Unit tests (Vitest)
useAoiStore:
Adding, updating, and removing AOIs.
Persistence to and from localStorage.
Helper utilities (e.g. geocoding if added).
Map behavior
Ensuring the selected AOI is focused (map center changes).
Ensuring AOIs reappear correctly after a reload.
Visual/Regression tests
Playwright screenshot tests to detect layout regressions (matching the Figma more strictly).
Only point AOIs implemented
The UI and data model support the idea of polygon, but creation is limited to clicking a single point to keep the implementation small, clear, and stable within the given time.
Custom state hook instead of Redux/Zustand
A simple useAoiStore hook is enough for this assignment.
Avoiding additional dependencies keeps the project easier to understand for reviewers.
Static WMS layer configuration
The WMS URL and layer name are hardcoded in MapView.tsx.
With more time I would parse the WMS GetCapabilities document and build a dynamic layer picker.
Minimal styling system
Styling is done directly with Tailwind utility classes for speed and “pixel-perfect” control.
No additional design system or component library is used to keep the bundle small.
If this were to go to production, I would:
Add authentication and real backend
Multi-user support with AOIs linked to authenticated users.
Persist AOIs in a database (e.g., Postgres with PostGIS).
Improve error handling
Global error boundary for React.
User-friendly error messages for failed WMS requests or lost network.
Configuration via environment variables
WMS URL, default layer, and any geocoding endpoints as env variables rather than hardcoded.
Stronger validation
Use a form library (e.g., React Hook Form) + schema validation (Zod) for AOI forms.
Accessibility (A11Y)
Ensure keyboard navigation for all controls.
Add ARIA labels for buttons and landmark regions.
Make the app usable with screen readers as far as possible.
CI/CD pipeline
Run ESLint, Prettier, TypeScript checks, unit tests, and Playwright tests on every PR.
Deploy to a static hosting platform (e.g., Vercel, Netlify).
Analytics & logging
Basic analytics to see how users interact with AOI creation.
Log map errors and WMS/API failures.
- Layer Management: Users can toggle visibility of the WMS orthophoto layer from the sidebar.
- Persistent AOIs: AOIs are stored in
localStorage, making them persist between page reloads. - Accessibility Improvements: All form inputs have associated labels and ARIA attributes to support screen readers and automated testing.
- Testing Coverage: End-to-end testing added using Playwright to validate core user workflows.
You can adjust these numbers to your real times. Example breakdown:
Project setup (Vite, TypeScript, Tailwind, React-Leaflet, Playwright): ~2–3 hours
Map integration (OSM + WMS layer) & AOI click handling: ~1-2 hours
Layout & UI (header, sidebar, styling tweaks): ~4-5 hours
State management (useAoiStore, localStorage persistence): ~3-4 hours
Playwright tests & configuration: ~1 hour
Documentation (README, schema, API design) & video recording: ~1–2 hours
Total: ~13–18 hours (spread across 3 days).
Short 3–5 minute demo video (screen recording) showing:
App startup (npm run dev).
Layout overview (header, sidebar, map).
WMS layer toggle on/off.
Creating an AOI by clicking on the map + saving it.
Verifying persistence by reloading the page.
Running Playwright tests and showing them passing.
Demo video link: (https://drive.google.com/file/d/1B6gqt8IK2BgvFgojr-MIT68qFxvCb-bM/view?usp=drive_link)
This project focuses on clarity, maintainability, and explainability over maximum feature set.
Many potential extensions are intentionally left as future work so that the core user flow (view map → click → create AOI → see it appear) is reliable and easy to understand.