92 lines
3.4 KiB
JavaScript
92 lines
3.4 KiB
JavaScript
// ==============================|| 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.'
|
|
}
|
|
}
|
|
];
|
|
}
|
|
};
|