// ==============================|| Skill: Rider Battery & Device Safety ||============================== // const OPEN_STATUSES = new Set(['pending', 'accepted', 'arrived', 'picked', 'active', 'skipped']); const isOpen = (row) => OPEN_STATUSES.has(String(row?.orderstatus || '').toLowerCase()); const ref = (row) => row?.orderid || row?.bookingno || (row?.bookingid ? `#${row.bookingid}` : '—'); export const RiderBatterySafetySkill = { id: 'skill_rider_battery_safety', name: 'Rider Device & SOS Safety', description: 'Monitors rider device telemetry to prevent unreachability due to low battery during active deliveries.', category: 'rider_safety', icon: 'BatteryWarning', // OFF by default (2026-09-29): the rows this rule reads carry no battery level: it lives on the rider profile (milerprofiles.batterypercentage), not on booking rows. // Enabled, it would read undefined on every row and report an all-clear board — // the silent failure normalise.js warns about. Turn it on only once the data exists. dataGap: 'no battery level: it lives on the rider profile (milerprofiles.batterypercentage), not on booking rows', defaultEnabled: false, thresholds: { criticalBatteryPercent: { label: 'Critical Battery Level (%)', description: 'Threshold below which rider device is considered at risk of shutdown.', value: 15, min: 5, max: 30, step: 5, unit: '%' } }, instructions: ` - When a rider has critical battery while carrying in-transit orders, advise charging or offloading remaining pending pickups. `, tools: [ { name: 'alert_low_battery_rider', description: 'Notifies rider to connect portable charger or report to nearest hub.', parameters: { type: 'object', properties: { milerId: { type: 'number', description: 'Target miler user ID' } }, required: ['milerId'] }, handler: async (args) => ({ success: true, proposal: { action: 'battery_alert', milerId: args.milerId, blastRadius: `Sends device power warning notification to Rider #${args.milerId}.` } }) } ], evaluate: (rows = [], now, currentThresholds = {}) => { const minBattery = currentThresholds.criticalBatteryPercent ?? 15; const safeRows = Array.isArray(rows) ? rows : []; const atRisk = safeRows.filter((r) => { if (!isOpen(r) || !r.userid) return false; const battery = Number(r.battery_percentage || r.battery_level || r.battery || 100); return battery > 0 && battery <= minBattery; }); if (!atRisk.length) return []; return [ { id: 'rider_critical_battery', skillId: 'skill_rider_battery_safety', severity: 'warning', title: `${atRisk.length} active delivery under low rider device battery (≤ ${minBattery}%)`, why: `Rider phone is nearing shutdown, risking lost GPS telemetry and customer communication failure.`, count: atRisk.length, rows: atRisk, detail: atRisk.slice(0, 8).map((r) => { const bat = r.battery_percentage || r.battery_level || r.battery || 'Low'; return `${ref(r)} — ${r.ridername || 'Rider'} (${bat}% battery)`; }), proposal: { label: 'Send power alert', tool: 'alert_low_battery_rider', scope: atRisk, blastRadius: 'Sends charging prompt to rider.' } } ]; } };