{{ theme.skipToContentLabel || 'Skip to content' }}

Geofences Page Design

Overview

New geofence management page for viewing, creating, editing, and deleting geofences on a Google Map. Supports displaying API-scraped geofences from SingleSourceIOT alongside user-created custom geofences. Includes optional heatmap overlay for dust distribution context.

Layout

Full-screen Google Map + collapsible right side panel. Route configured with showDatePicker: true and showSiteSelector: true (global filters).

Map Area

  • Renders all geofences (polygon / circle / rectangle) for the selected mine site
  • Drawing toolbar (visible in edit mode only): polygon, circle, rectangle tools
  • Heatmap overlay toggle — loads heatmap data by mine site + date range
  • Click geofence → highlight + scroll to item in side panel

Side Panel

  • View mode / Edit mode toggle button (requires can_edit permission)
  • Heatmap overlay switch + opacity slider
  • Geofence list with two view modes (toggle between):
    • Group view: collapsible groups from data_geofence_groups, manual geofences under a "Custom" group
    • Flat view: all geofences in a flat list, filterable by group
  • Click list item → map flies to geofence and highlights it
  • Geofence detail/edit form (in edit mode)

View / Edit Mode

View Mode (default)Edit Mode
Drawing toolbarHiddenVisible
Geofence draggingDisabledEnabled (vertices, edges)
Detail formRead-onlyEditable
Delete buttonHiddenVisible
Visual indicatorNoneTop banner or highlighted toggle
Permissioncan_viewcan_edit required

Interaction Flows

Drawing a New Geofence (Edit Mode)

  1. Select shape tool (polygon / circle / rectangle)
  2. Draw on map via Google Maps Drawing Manager
  3. Side panel opens edit form — pre-filled coordinates, user enters name, color, group (optional)
  4. Save → convert to polygon points JSON, calculate bounding box, insert into data_geofences
  5. Cancel → remove temporary shape from map

Editing a Geofence (Edit Mode)

  1. Click geofence on map or in side panel → selected + highlighted
  2. Editable fields: name, color, description, is_enabled, display_on_map
  3. Shape editing via Google Maps native setEditable(true):
    • Polygon: drag vertices to move, drag edge midpoints to insert new vertex, right-click vertex to delete (min 3 vertices)
    • Circle: drag edge to resize radius, drag center to move
    • Rectangle: drag corners/edges to resize, drag center to move
  4. Save → update data_geofences

Deleting a Geofence (Edit Mode)

  1. Select geofence → click delete button
  2. Confirmation dialog
  3. Soft delete (deleted = true)

Data Model

Uses existing tables in the main Supabase instance:

data_geofence_groups

  • Group metadata from SingleSourceIOT API
  • Fields: id, mine_site_id, name, description, colour_code, geofence_count, geofence_group_code, sss_client_id

data_geofences

  • Individual geofences (both API-scraped and manually created)
  • Fields: id, mine_site_id, name, points (JSON), min/max_latitude/longitude, map_fill_colour, colour_code, display_on_map, is_enabled, deleted, geofence_group_code, geofence_id, site_name
  • No source distinction — API and manual geofences coexist in the same table
  • Circle and rectangle shapes stored as polygon points for consistency

TypeScript Types

typescript
interface GeofenceGroup {
  id: string;
  mine_site_id: string;
  name: string;
  description: string | null;
  colour_code: string | null;
  geofence_count: number;
  geofence_group_code: string | null;
}

interface Geofence {
  id: string;
  mine_site_id: string;
  geofence_group_code: string | null;
  name: string;
  points: string; // JSON string of coordinate array
  min_latitude: number | null;
  max_latitude: number | null;
  min_longitude: number | null;
  max_longitude: number | null;
  map_fill_colour: string | null;
  colour_code: string | null;
  display_on_map: boolean;
  is_enabled: boolean;
  deleted: boolean;
  site_name: string | null;
  geofence_id: number | null; // external API ID, null for manual
}

type GeofenceShapeType = 'polygon' | 'circle' | 'rectangle';

Feature Structure

src/features/geofences/
├── components/
│   ├── GeofencesPage.tsx          # Main page component
│   ├── GeofenceMap.tsx            # Google Maps + drawing tools
│   ├── GeofenceSidePanel.tsx      # Right side panel container
│   ├── GeofenceList.tsx           # Flat list view
│   ├── GeofenceGroupList.tsx      # Grouped list view
│   ├── GeofenceListItem.tsx       # Single geofence list item
│   ├── GeofenceDetailForm.tsx     # Detail / edit form
│   └── HeatmapOverlayToggle.tsx   # Heatmap toggle + opacity control
├── services/
│   └── geofenceService.ts         # Static class, CRUD for data_geofences + data_geofence_groups
├── hooks/
│   ├── useGeofences.ts            # Geofence data fetching + state
│   └── useGeofenceHeatmap.ts      # Heatmap overlay data (reuses heatmap databaseService)
├── types.ts
├── constants.ts
└── utils.ts                       # Coordinate conversion, bounding box calculation

Service Methods

typescript
class GeofenceService {
  static async fetchGeofences(mineSiteId: string): Promise<Geofence[]>
  static async fetchGeofenceGroups(mineSiteId: string): Promise<GeofenceGroup[]>
  static async createGeofence(data: CreateGeofenceInput): Promise<Geofence>
  static async updateGeofence(id: string, data: UpdateGeofenceInput): Promise<Geofence>
  static async deleteGeofence(id: string): Promise<void> // soft delete
}

Heatmap Overlay

  • Default: off
  • Toggle on → fetch heatmap data from data_heatmap table using current mine site + date range
  • Render with Google Maps native HeatmapLayer (simplified, no canvas mode)
  • Opacity slider control
  • Date picker changes refresh heatmap data only (geofence data is not date-dependent)

Permissions

  • New module: geofences in module permissions system
  • Route: requiredModule: 'geofences'
  • can_view: view geofences and heatmap overlay
  • can_edit: create, edit, delete geofences + mode toggle visible
  • PermissionGate wraps edit mode toggle and drawing tools

Routing

typescript
// In Routes.tsx layoutsRoutes
{
  path: "/geofences",
  name: "Geofences",
  element: <Geofences />,
  requiredModule: "geofences",
  showDatePicker: true,
  showSiteSelector: true,
}

Sidebar menu: under "Analysis & Monitoring" section, with a map/fence icon.