Skip to content

18mukeshram/flowbit-aoi-assignment

Repository files navigation

AOI Creation – Flowbit Frontend Engineer Assignment

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 localStorage so they survive page reloads.
  • Includes end-to-end tests with Playwright.

1. Tech Stack

  • 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)

2. How to run the project

2.1. Prerequisites

  • Node.js (LTS version recommended)
  • npm (comes with Node)

2.2. Setup

# Install dependencies
npm install

3. Project Structure

. ├── 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

4. Map Library Choice

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.

5. Architecture Decisions

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.

6. Data Model & Schema (ER Diagram)

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 }

schema/ER view

AOI

  • id (UUID)
  • name
  • description
  • geometryType (e.g. "Point", "Polygon")
  • coordinates (array of [lat, lng] or GeoJSON)
  • createdAt
  • updatedAt

In a future multi-user system, AOIs would be linked to users:

User 1 --- * AOI

User

  • id (UUID)
  • email
  • name

7. API Design & Documentation (Hypothetical)

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" } ]

Example: POST /api/aois

Request body

{ "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

8. Map Functionality

  • 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.

9. Performance Considerations (Scalability to 1000s of AOIs)

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.

10. Testing Strategy

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).

11. Tradeoffs Made

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.

12. Production Readiness – What I Would Add

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.

13. Bonus Features Implemented

  • 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.

14. Time Spent (Approximate)

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).

15. Demo Video

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)

16. Notes

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.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors