Skip to content

MOAI-GIGA/frontend

Repository files navigation

GIGA Project Connect — Mozambique

Frontend for school connectivity visualization and optimal tower network planning in Mozambique. Client: GIGA (UNICEF–ITU) · Developer: MOAI Analytics · License: MIT


Quick Start

git clone <repository-url>
cd giga-maps
npm install
npm run dev          # → http://localhost:5173

Default login: admin / giga2026


Table of Contents


Overview

MOAI | GIGA - Mozambique enables decision-makers to visualize and prioritize school connectivity investments by rendering multi-dimensional geospatial indices — climate risk, social vulnerability, infrastructure access — on an interactive WebGL map. The platform calculates optimal tower placement using configurable priority weights and K-values (50 / 200 / 400 total towers to install).


Features

Category Detail
Interactive Map WebGL rendering via MapLibre GL with dark/light/streets base styles
Priority Index System Configurable weights across Climate, Social, and Access axes (levels Low / Medium / High)
Tower Network Optimization K=50, K=200, and K=400 total towers to install with Haversine distance calculations
Multi-filter Panel Filter schools by connectivity status, electricity availability, and education level
Index Selector Threshold slider (0–100) with color gradient visualization
Layer Management Toggle individual layers or groups with parent-child hierarchy
Rich Tooltips School detail cards with all index values and color-coded indicators
Responsive Design Optimized breakpoints for desktop (1920px), laptop (1366px), tablet (1024px), and mobile (768px–360px)
Authentication Simple login gate with localStorage persistence

Tech Stack

Layer Technology Version
Framework React 19.2.0
Language TypeScript 5.9.3 (strict)
Build Tool Vite 7.x
Map Engine MapLibre GL JS 5.17.0
Styling Tailwind CSS 4.1.18
Icons react-icons 5.5.0
Testing Vitest + React Testing Library 4.x / 16.x
Linting ESLint (flat config) 9.x
Formatting Prettier 3.x

Node.js ≥ 18 and npm ≥ 9 required.


Project Structure

giga-maps/
├── public/
│   ├── data/
│   │   ├── schools_climate_index.geojson     # Climate risk index per school
│   │   ├── schools_social_index.geojson      # Social vulnerability index per school
│   │   ├── schools_access_index.geojson      # Infrastructure access index per school
│   │   ├── towers_existing.geojson           # Existing tower locations
│   │   ├── priority_school/                  # 8 school priority GeoJSON variants
│   │   └── priority_tower/                   # 27 tower placement GeoJSON variants (K50 + K200 + K400)
│   ├── icons/                                # SVG marker icons (house.svg)
│   └── img/                                  # Logos: GIGA, UNICEF, ITU, MOAI
├── src/
│   ├── components/
│   │   ├── Map/
│   │   │   └── MapContainer.tsx              # Core map: layers, icons, interactions  
│   │   └── UI/
│   │       ├── Sidebar.tsx                   # Layer toggle panel with priority/K-value selectors
│   │       ├── ConnectivityFilter.tsx        # Multi-section filter (connectivity, electricity, education)
│   │       ├── FilterPanel.tsx               # Connectivity status toggle buttons (Yes/No/Unknown)
│   │       ├── Slider.tsx                    # Index threshold slider (0–100)
│   │       ├── MapControls.tsx               # Zoom, fit-bounds, style toggle
│   │       ├── LoginModal.tsx                # Authentication modal
│   │       ├── LogoutConfirmModal.tsx        # Two-step logout confirmation
│   │       ├── LoaderModal.tsx               # Loading overlay with spinner
│   │       ├── InfoModal.tsx                 # Help and usage documentation modal
│   │       ├── PrioritySelector.tsx          # Priority level selector (Low / Medium / High)
│   │       ├── KValueSelector.tsx            # Tower K-value selector (50/200/400)
│   │       └── ConnectedSchoolsBanner.tsx    # Real-time connected school count banner
│   ├── hooks/
│   │   └── useGeoJSON.ts                     # Async GeoJSON loader with validation
│   ├── types/
│   │   └── geojson.ts                        # SchoolProperties, LayerConfig, FilterState, etc.
│   ├── test/                                 # Vitest test suite (27 tests)
│   ├── App.tsx                               # Root component with auth gating
│   ├── main.tsx                              # React 19 entry point
│   └── index.css                             # Global styles + responsive breakpoints
├── Dockerfile                                # Multi-stage production image
├── vite.config.ts                            # Build + test config
├── tsconfig.json                             # TypeScript project references
├── eslint.config.js                          # ESLint flat config
└── .prettierrc                               # Prettier config

Architecture

src/
├── components/
│   ├── Map/
│   │   └── MapContainer.tsx         # Core map component — layers, icons, interactions
│   └── UI/
│       ├── Sidebar.tsx              # Layer toggle panel with priority/K-value selectors
│       ├── ConnectivityFilter.tsx   # Multi-section filter (connectivity, electricity, education)
│       ├── FilterPanel.tsx          # Connectivity status toggle buttons
│       ├── Slider.tsx               # Heat index threshold slider
│       ├── Tooltip.tsx              # School detail popup
│       ├── MapControls.tsx          # Zoom, fit-bounds, style toggle
│       ├── LoginModal.tsx           # Authentication modal
│       ├── LoaderModal.tsx          # Loading overlay
│       ├── PrioritySelector.tsx     # Priority level selector (Low / Medium / High)
│       └── KValueSelector.tsx       # Tower K-value selector (50/200/400)
├── hooks/
│   └── useGeoJSON.ts               # Async GeoJSON loader with validation
├── types/
│   └── geojson.ts                   # SchoolProperties, LayerConfig, FilterState
├── App.tsx                          # Root component with auth gating
├── main.tsx                         # Entry point
└── index.css                        # Global styles + responsive breakpoints

Development Workflow

Prerequisites

node --version   # ≥ 18
npm --version    # ≥ 9

Setup

npm install
npm run dev

Before committing

npm run typecheck       # TypeScript — must pass with 0 errors
npm run lint            # ESLint — 0 errors (35 known warnings, threshold 40)
npm run format:check    # Prettier — all files must be formatted
npm test                # Vitest — 27/27 must pass

Or run all at once:

npm run typecheck && npm run lint && npm run format:check && npm test

Scripts Reference

Command Description
npm run dev Start Vite dev server (HMR)
npm run build TypeScript check + production build
npm run preview Serve production build locally
npm run typecheck Run tsc --noEmit (no output, just type errors)
npm run lint ESLint with --max-warnings 40 (35 known warnings within threshold)
npm run lint:fix Auto-fix ESLint issues
npm run format Prettier write over src/**/*.{ts,tsx,css,json}
npm run format:check Check formatting without modifying files
npm test Vitest single run
npm run test:watch Vitest interactive watch mode
npm run test:coverage Coverage report (v8, lcov output)

Testing

Tests live in src/test/. Run with Vitest in jsdom environment.

giga-maps/
├── public/
│   ├── data/
│   │   ├── priority_school/         # School priority GeoJSON variants
│   │   ├── priority_tower/          # Tower placement GeoJSON variants (K50 / K200 / K400)
│   │   ├── schools_climate_index.geojson
│   │   ├── schools_social_index.geojson
│   │   ├── schools_access_index.geojson
│   │   └── towers_existing.geojson
│   ├── icons/                       # SVG icons (house, tower)
│   └── img/                         # Logos and favicon
├── src/                             # Application source (see Architecture)
├── eslint.config.js                 # ESLint flat config
├── .prettierrc                      # Prettier configuration
├── vite.config.ts                   # Vite build config with production optimizations
├── tsconfig.json                    # TypeScript project references
├── Dockerfile                       # Multi-stage production build
├── .dockerignore                    # Docker build exclusions
└── package.json

Data Layer

All geospatial data is served as static GeoJSON files from public/data/.

Base Layers

File Description
schools_climate_index.geojson Climate risk index per school
schools_social_index.geojson Social vulnerability index per school
schools_access_index.geojson Infrastructure access index per school
towers_existing.geojson Existing tower locations

Priority Layers

Generated from combinations of parent axes — climate (cli), social (soc), access (acc) — and their sub-indices, each weighted at Low (1), Medium (2), or High (3). Sibling axes use _ as separator; parent-to-child groups use -:

  • School Priority: priority_school/schools_priority_index-cli{w}_soc{w}_acc{w}[-children...].geojson
  • Tower Optimal Placement: priority_tower/optimal_towers_K{50|200|400}-cli{w}_soc{w}_acc{w}[-children...].geojson

Sub-indices appear only when their parent reaches High (3): ext, geo, sho, hyd for Climate; pov, nut for Social; hea, pow, roa for Access.

Constraint rules:

  • Exactly one High is required among parents at all times; the only exception is the all-Medium state (2, 2, 2).
  • Parents: Setting Low is blocked (with a warning banner) unless another parent is already at High.
  • Sub-indices: Setting Low always succeeds; if no sibling is High, the system auto-promotes the first available sibling to High.
  • Removing the last High (→ Medium) auto-resets any Lows to Medium (both parents and sub-indices).

School Properties

Each school feature includes: School Name, School Giga ID, connectivity, electricity, education_level, num_students, PRIORITY_IDX (0–100 composite score), CLIM_IDX, SOC_IDX, ACC_IDX, and individual sub-index fields.


Environment & Deployment

Netlify (Current)

The application is deployed on Netlify with automatic builds from the main branch.

Live URL: https://moai-giga.netlify.app/

Production Build

npm run build
# Output: dist/

Build applies:

  • Terser minification with console.log and debugger removal
  • Vendor chunk splitting: vendor (React/ReactDOM), maplibre (MapLibre GL)
  • Source maps disabled
  • noindex, nofollow meta tag (internal tool — not for public indexing)

Netlify

Deployed on Netlify from the master branch. Configuration in netlify.toml:

  • Build command: npm run build, publish directory: dist/
  • SPA redirect rule (/* → /index.html)
  • GeoJSON served as application/json → triggers automatic gzip/Brotli compression on the CDN
  • Security headers: X-Frame-Options, X-Content-Type-Options, Referrer-Policy

Live URL: https://moai-giga.netlify.app/


Docker

# Build
docker build -t giga-maps .

# Run
docker run -p 8080:80 giga-maps
# → http://localhost:8080

Multi-stage build: node:20-alpine (build) → nginx:alpine (serve). SPA fallback routing configured via try_files.


Code Conventions

  • TypeScript strict mode — no any, no unused variables (enforced by ESLint)
  • Prettier — single quotes, semicolons, trailing commas, print width 100, LF line endings
  • Component files — one default export per file, named same as file
  • No global state — all state local to MapContainer.tsx passed down as props
  • GeoJSON files excluded from Prettier (large files, formatter excluded via .prettierignore)
  • Comments in English only
  • No console.log in production code (Terser drops them, but keep source clean)

License

MIT — Copyright © 2025–2026 MOAI Analytics · GIGA UNICEF–ITU

About

Frontend for school connectivity visualization and optimal tower network planning in Mozambique

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages