Filter by client:
Open Work Orders
Compliance Status
🚨 Top 5 At-Risk Assets
📏 Overdue Meter Readings
| Title | Building | Priority | Status | Assigned To | Opened | Actions |
|---|
| Building | Category | Certificate # | Expiry Date | Status | Findings | Actions |
|---|
| Title | Building | Severity | OSHA | Status | Date | Actions |
|---|
| Title | Building | Category | Scheduled | Status | Score | Actions |
|---|
| Name | Building | Category | Last Service | Next Service | Status | Actions |
|---|
| Company | Contact | Speciality | Rating | Jobs | Status | Actions |
|---|
| Title | Category | Building | Uploaded By | Date | Actions |
|---|
⚙️ Maintenance KPIs
📋 Compliance KPIs
👷 Contractor Performance
🔍 Inspection KPIs
🤖 Draft a Report
📄 Report Draft
Your AI-drafted report will appear here.
Review carefully before sending.
| Name | Role | Status | Last Login | Actions |
|---|
🎨 Platform Theme
Super Admin only · Changes apply immediately for all users
Select a colour theme for the ScanOps platform. The theme can be set per client to match their brand identity.
Citya Brand
Burgundy · Silver accent — currently active
🏢 Organisation
⚙️ System
💰 Cost Approval Thresholds
Configured per building at creation
| Building | Approval Threshold (MUR) | Approver (M+1) | Actions |
|---|---|---|---|
| Résidence Les Flamboyants | 10,000 | Guy Régis Fachette (Chief Technical) | |
| Garden Court Ebène | 15,000 | Guy Régis Fachette (Chief Technical) |
🔍 Recent Audit Log
| Timestamp | User | Action | Entity | Detail |
|---|
🏢 Building Registry
| Ref # | Building Name | Address | Units | Approval Limit | Open WOs | Compliance | Actions |
|---|
| Unit # | Floor | Building | Owner | Owner Email | Resident | Actions |
|---|
📬 Submit a Request
This portal can be shared as a public link — no login required for residents and co-owners.
📷
Tap to take a photo or choose from library
JPG, PNG — max 5 photos, 5MB each
JPG, PNG — max 5 photos, 5MB each
📋 Recent Requests
🏛️ Client Portfolio
📏 Scheduled Readings
| Asset | Meter Type | Last Value | Due Date | Status | Action |
|---|
📋 Recent Readings
| Asset | Type | Value | Range | Recorded |
|---|
📈 Electricity Trend (kWh) · Résidence Les Flamboyants
🏗️ Asset Health Scorecard
Composite of WO frequency · PM recency · Inspection pass/fail · Incidents · Parts
| Asset | Building | Health Score | Open WOs | Last PM | Insp. Pass | Incidents | Action |
|---|
🚨 Top 5 At-Risk Assets
✅ Top 5 Best Performing
📅 Work Schedules
👤 Technician Shifts
🗓️ Public Holidays — Mauritius 2026
🔁 Preventive Maintenance Plans
Recurring templates. The nightly cron job auto-generates a work order whenever a plan is due (minus its lead-time).
| Ref | Name | Building | Category | Every | Next Due | Active | Actions |
|---|
🛠️ Skill Library
👷 Coverage Matrix
🟢 Expert 🔵 Competent 🟡 Trainee ⬜ None
⚠️ Expiring Certifications — Next 60 Days
| Technician | Skill | Level | Expires | Days Left |
|---|
⚠️ Rules are enforced at submission. Users cannot close Work Orders, submit Inspections or complete PM executions without satisfying all required attachments.
📎 Attachment Rules
📊 Compliance Snapshot
🔍 Preview — WO Close (blocked)
🔒 Cannot close until all required attachments are uploaded.
ℹ️ Admin & Manager roles have org-wide access. Technician & Viewer roles are scoped to their assigned sites only — they cannot see data from other sites.
🔐 User Site Access Matrix
| User | Role | Résidence Les Flamboyants | Garden Court Ebène | Effective Scope |
|---|
📋 Recent Access Changes (Audit)
| Timestamp | User | Site | Action | Granted By |
|---|
Predictive Intelligence
AI-surfaced risks from your operational data — no IoT required
GEAR AI
Avg MTTR
—
Mean Time To Repair (hours)
Avg MTBF
—
Mean Time Between Failures (days)
Overdue Assets
—
Past next service date
Compliance Expiring
—
Within 30 days
⚠️ Active Risk Alerts
Auto-refreshed from operational data
🏗️ Asset Health Scorecard
| Asset | Building | Last Service | Next Service | Days Overdue | Health | Action |
|---|
Supplier & Contractor Accountability
SLA adherence, performance scores, and contract review evidence
GEAR AI
📊 SLA Performance Dashboard
📋 Contractor Detail — SLA Log
| Contractor | Speciality | Jobs | Avg Rating | SLA Adherence | On-Time % | Status |
|---|
Executive Automated Report
Board-ready KPI summary — auto-generated, no manual assembly
⚡ Auto-generated
GEAR AI
🤖 AI Narrative Summary
🔧 Work Orders Breakdown
📋 Compliance Status
AI Hub — Modular Intelligence Control
Activate or deactivate AI features per client or site
GEAR AI
🔮 Predictive Intelligence
MTTR / MTBF Tracking
Track repair and failure intervals per asset automatically
Asset Risk Alerts
Flag assets approaching or past service dates
Compliance Expiry Warnings
Auto-alert 30, 14, and 7 days before certificate expiry
Failure Pattern Detection
Surface assets with repeated failures within 90 days
⚡ Admin Automation
Auto Preventive Scheduling
Create WOs automatically when assets hit service date
WhatsApp Follow-up Reminders
Auto-ping technician if WO open > 48 hours with no update
Auto Report Generation
Send weekly KPI digest to manager every Monday 8am
Compliance Auto-Logging
Log every compliance event to audit trail without manual entry
🤝 Supplier Intelligence
SLA Adherence Tracking
Score each contractor on on-time delivery and quality
Underperformance Alerts
Flag contractors with rating below 3.5 or on-time < 70%
Contract Review Evidence Pack
Auto-compile SLA data CSV for contract renegotiation
📊 Executive Reporting
Weekly KPI Digest
Auto-email leadership summary every Monday
AI Narrative Generation
Generate plain-language summary of KPI changes
Board PDF Auto-Export
Generate formatted PDF report on 1st of each month
📈 Automation Savings Estimate
📋 Capacitor Integration Guide (10 Steps)
VITE + REACT 18
1
Install Capacitor Core + Android + CLI
npm install @capacitor/core @capacitor/android
npm install -D @capacitor/cli
2
Build the Vite PWA first (required before cap init)
npm run build # produces dist/
3
Initialise Capacitor with correct appId
npx cap init ScanOps digital.celard.scanops --web-dir dist
4
Set Vite base to './' for file:// compatibility
// vite.config.ts
export default defineConfig({
base: './', // ← REQUIRED for Capacitor
plugins: [react()],
})
5
Configure capacitor.config.ts (production-ready)
// capacitor.config.ts
import type { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'digital.celard.scanops',
appName: 'ScanOps',
webDir: 'dist',
server: {
hostname: 'app.scanops.mu',
androidScheme: 'https',
allowNavigation: ['*.scanops.mu','api.scanops.mu'],
},
plugins: {
SplashScreen: {
launchShowDuration: 2500,
backgroundColor: '#0F2B2B',
androidSplashResourceName: 'splash',
launchAutoHide: false,
},
StatusBar: {
style: 'DARK',
backgroundColor: '#0F2B2B',
},
PushNotifications: {
presentationOptions: ['badge','sound','alert'],
},
Keyboard: { resize: 'body' },
},
};
export default config;
6
Add Android platform
npx cap add android # generates android/ directory
7
Add permissions to AndroidManifest.xml
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
8
Sync web assets to native (run after every build)
npm run build && npx cap sync android
9
Open Android Studio
npx cap open android
10
Generate signed AAB for Play Store
cd android
./gradlew bundleRelease
# Output: android/app/build/outputs/bundle/release/app-release.aab
📶 Service Worker — Offline Cache Strategy
Workbox
📂 Static Assets (App Shell) → CacheFirst · 30 days
JS/CSS/fonts/images — served from cache instantly. Network only used on first load or after force refresh.
📋 Job LIST /api/work-orders → StaleWhileRevalidate · 24h TTL · Max 5 entries
Shows cached list immediately; revalidates in background. Keeps technician job queue visible offline.
🔧 Job DETAIL /api/work-orders/:id → NetworkFirst · 5s timeout · 48h TTL · Max 50
Tries live data first; falls back to cache if offline or slow. Technician can still view full job details on-site.
💰 Service Rates → CacheFirst · 7 days
Rates change rarely; serve from cache to speed up invoice line creation offline.
🔄 Background Sync Queue: scanops-job-sync-queue (3-day retention)
PATCH job-complete + POST check-in requests queued when offline. Auto-sync fires when connectivity returns. Zero data loss for technicians in weak-signal zones.
🔌 Native Plugin Integrations
3 PLUGINS
Camera — Photo Evidence
@capacitor/camera
npm install @capacitor/camera
npx cap sync android
// src/hooks/useJobCamera.ts
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
export function useJobCamera() {
const [photos, setPhotos] = useState<string[]>([]);
const capturePhoto = async () => {
const perm = await Camera.requestPermissions({ permissions: ['camera'] });
if (perm.camera !== 'granted') throw new Error('Camera permission denied');
const image = await Camera.getPhoto({
quality: 85,
allowEditing: false,
resultType: CameraResultType.Base64,
source: CameraSource.Camera,
saveToGallery: false,
});
if (image.base64String) {
setPhotos(prev => [...prev, image.base64String!]);
}
return image.base64String;
};
const uploadPhotos = async (workOrderId: string) => {
const uploads = photos.map(b64 =>
fetch('/api/work-orders/' + workOrderId + '/photos', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ data: b64, mimeType: 'image/jpeg' }),
})
);
await Promise.all(uploads);
setPhotos([]);
};
return { photos, capturePhoto, uploadPhotos };
}
Geolocation — Check-In Verification
@capacitor/geolocation
npm install @capacitor/geolocation
npx cap sync android
// src/hooks/useCheckinGeo.ts
import { Geolocation } from '@capacitor/geolocation';
const GEOFENCE_RADIUS_M = 150;
function haversineDistance(lat1:number,lon1:number,lat2:number,lon2:number){
const R=6371000;const dLat=(lat2-lat1)*Math.PI/180;
const dLon=(lon2-lon1)*Math.PI/180;
const a=Math.sin(dLat/2)**2+Math.cos(lat1*Math.PI/180)
*Math.cos(lat2*Math.PI/180)*Math.sin(dLon/2)**2;
return R*2*Math.atan2(Math.sqrt(a),Math.sqrt(1-a));
}
export function useCheckinGeo() {
const checkIn = async (siteLat: number, siteLng: number) => {
const perm = await Geolocation.requestPermissions();
if (perm.location !== 'granted') throw new Error('Location denied');
const pos = await Geolocation.getCurrentPosition({
enableHighAccuracy: true, timeout: 10000,
});
const { latitude, longitude } = pos.coords;
const dist = haversineDistance(latitude,longitude,siteLat,siteLng);
return {
latitude, longitude,
distanceMetres: Math.round(dist),
withinGeofence: dist <= GEOFENCE_RADIUS_M,
timestamp: new Date().toISOString(),
};
};
return { checkIn };
}
Push Notifications — Job Assignments
@capacitor/push-notifications
npm install @capacitor/push-notifications
npx cap sync android
// src/lib/pushNotifications.ts
import { PushNotifications } from '@capacitor/push-notifications';
export async function initialisePush(userId: string) {
const perm = await PushNotifications.requestPermissions();
if (perm.receive !== 'granted') return;
await PushNotifications.register();
PushNotifications.addListener('registration', async ({ value: fcmToken }) => {
await fetch('/api/users/push-token', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId, fcmToken }),
});
});
PushNotifications.addListener('pushNotificationReceived', notification => {
const { type, workOrderId, title } = notification.data;
if (type === 'job_assigned')
window.dispatchEvent(new CustomEvent('scanops:navigate',
{ detail: { page: 'workorders', woId: workOrderId } }));
});
PushNotifications.addListener('pushNotificationActionPerformed', action => {
const { workOrderId } = action.notification.data;
if (workOrderId)
window.dispatchEvent(new CustomEvent('scanops:navigate',
{ detail: { page: 'workorders', woId: workOrderId } }));
});
}
🏪 Google Play Store Checklist
DPA 2017
- ✅App Icons: 48×48 (mdpi), 72×72 (hdpi), 96×96 (xhdpi), 144×144 (xxhdpi), 192×192 (xxxhdpi), 512×512 (Play Store), 108dp adaptive
- ✅Feature Graphic: 1024×500px JPG/PNG — landscape promotional banner
- ✅AAB: targetSdk 34, minSdk 26 (Android 8.0+)
- ✅Content Rating: IARC — Everyone (no age restriction needed)
- ✅Privacy Policy: https://scanops.mu/privacy — DPA 2017 compliant, DPO: Marcio Isnard (marcio.isnard@celard.digital)
- ✅Data Safety Form: Declare geolocation (precise, on-device only), camera (photos encrypted, purpose: job evidence)
- ⏳google-services.json: FCM config file for push notifications — generate in Firebase Console
- ⏳Signed Keystore: Generate production keystore, store credentials securely (GitHub Secrets recommended)
📝 Store Listing — EN Short Description (80 chars):
"Location-intelligent field ops for syndic property managers in Mauritius."
📝 Store Listing — FR Short Description (80 chars):
"Gestion des opérations terrain intelligente pour syndics à l'Île Maurice."
📝 Full EN Description (excerpt):
ScanOps is the field operations platform built for syndic property managers and maintenance teams across Mauritius. Powered by location intelligence, ScanOps connects your on-site technicians, syndic managers, and chief technical officers in a single real-time workflow — from job assignment to close-out. Key features: GPS-verified technician check-ins, photo evidence capture, offline job access, 3-level digital approval gate, auto-generated MUR invoices with VAT, compliance certificate tracking, and predictive asset maintenance alerts. Built by CelArd Ltée, Mauritius. DPO: Marcio Isnard.
🧾 Invoice Register
| Invoice # | Property | Work Order | Date | Amount (MUR) | Status | Actions |
|---|
📄 Invoice Preview
Select an invoice to preview
💰 Service Rate Card — MUR
Code
Description
Unit Price (MUR)
VAT?
Active
Actions
3-level gate required before invoice generation
Select a work order to view its approval gate