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_editpermission) - 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
- Group view: collapsible groups from
- 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 toolbar | Hidden | Visible |
| Geofence dragging | Disabled | Enabled (vertices, edges) |
| Detail form | Read-only | Editable |
| Delete button | Hidden | Visible |
| Visual indicator | None | Top banner or highlighted toggle |
| Permission | can_view | can_edit required |
Interaction Flows
Drawing a New Geofence (Edit Mode)
- Select shape tool (polygon / circle / rectangle)
- Draw on map via Google Maps Drawing Manager
- Side panel opens edit form — pre-filled coordinates, user enters name, color, group (optional)
- Save → convert to polygon points JSON, calculate bounding box, insert into
data_geofences - Cancel → remove temporary shape from map
Editing a Geofence (Edit Mode)
- Click geofence on map or in side panel → selected + highlighted
- Editable fields: name, color, description, is_enabled, display_on_map
- 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
- Save → update
data_geofences
Deleting a Geofence (Edit Mode)
- Select geofence → click delete button
- Confirmation dialog
- 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 calculationService 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:
geofencesin module permissions system - Route:
requiredModule: 'geofences' can_view: view geofences and heatmap overlaycan_edit: create, edit, delete geofences + mode toggle visiblePermissionGatewraps 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.