Update: 2026-08-07 02:58:47
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
// ============================================================
|
||||
// safety/list_incidents.php — قائمة بلاغات السلامة للوحة الإدارة
|
||||
//
|
||||
// قراءة خالصة. الترتيب: المفتوح أولاً ثم الأحدث — غرفة العمليات تحتاج
|
||||
// ما لم يُعالَج بعد، لا أحدث ما وقع.
|
||||
// ============================================================
|
||||
|
||||
// تحت Admin/ عمداً: بوّابة connect.php المسارية تفرض دور admin أو
|
||||
// super_admin على كل ما تحت هذا المجلد. لو بقي في safety/ لقرأه أي حامل
|
||||
// JWT صالح — بما فيه راكب — وفيه معرّفات ومواقع أطراف الحادثة.
|
||||
require_once __DIR__ . '/../../connect.php';
|
||||
|
||||
$status = filterRequest("status"); // open | acknowledged | resolved | false_alarm | (فارغ = الكل)
|
||||
$limit = (int) (filterRequest("limit", 'int') ?: 50);
|
||||
$limit = max(1, min(200, $limit));
|
||||
|
||||
$allowed = ['open', 'acknowledged', 'resolved', 'false_alarm'];
|
||||
|
||||
try {
|
||||
if ($status && in_array($status, $allowed, true)) {
|
||||
$stmt = $con->prepare("
|
||||
SELECT * FROM safety_incidents
|
||||
WHERE status = ?
|
||||
ORDER BY created_at DESC
|
||||
LIMIT $limit
|
||||
");
|
||||
$stmt->execute([$status]);
|
||||
} else {
|
||||
// FIELD() يضع 'open' أولاً مهما كان تاريخه — بلاغ مفتوح من أمس
|
||||
// أهم من بلاغ عولج قبل دقيقة.
|
||||
$stmt = $con->query("
|
||||
SELECT * FROM safety_incidents
|
||||
ORDER BY FIELD(status, 'open', 'acknowledged', 'resolved', 'false_alarm'),
|
||||
created_at DESC
|
||||
LIMIT $limit
|
||||
");
|
||||
}
|
||||
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// عدّاد المفتوح — الرقم الذي يظهر كشارة على اللوحة
|
||||
$openCount = (int) $con->query(
|
||||
"SELECT COUNT(*) FROM safety_incidents WHERE status = 'open'"
|
||||
)->fetchColumn();
|
||||
|
||||
jsonSuccess(['open_count' => $openCount, 'incidents' => $rows], "ok");
|
||||
|
||||
} catch (PDOException $e) {
|
||||
error_log("[safety/list] " . $e->getMessage());
|
||||
jsonError("DB Error", 500);
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
// ============================================================
|
||||
// Admin/safety/update_incident.php — معالجة بلاغ سلامة
|
||||
//
|
||||
// يحوّل البلاغ من "مفتوح" إلى حالة نهائية، ويرفع تجميد السائق عند
|
||||
// الإغلاق. بدون هذه النقطة يبقى السائق مجمّداً إلى الأبد — التجميد بلا
|
||||
// TTL عمداً، فرفعه قرار إنسان لا انقضاء وقت.
|
||||
//
|
||||
// تحت Admin/ فترث بوّابة الدور من connect.php.
|
||||
// ============================================================
|
||||
|
||||
require_once __DIR__ . '/../../connect.php';
|
||||
|
||||
$incidentId = filterRequest("incident_id", 'int');
|
||||
$newStatus = filterRequest("status");
|
||||
$note = filterRequest("note");
|
||||
$releaseHold = filterRequest("release_hold");
|
||||
|
||||
$allowed = ['acknowledged', 'resolved', 'false_alarm'];
|
||||
|
||||
if (!$incidentId || !in_array($newStatus, $allowed, true)) {
|
||||
jsonError("Missing incident_id or invalid status");
|
||||
}
|
||||
|
||||
try {
|
||||
$stmt = $con->prepare("SELECT * FROM safety_incidents WHERE id = ? LIMIT 1");
|
||||
$stmt->execute([$incidentId]);
|
||||
$incident = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$incident) {
|
||||
jsonError("Incident not found", 404);
|
||||
}
|
||||
|
||||
$con->prepare("
|
||||
UPDATE safety_incidents
|
||||
SET status = ?, acknowledged_by = ?, acknowledged_at = NOW(), resolution_note = ?
|
||||
WHERE id = ?
|
||||
")->execute([
|
||||
$newStatus,
|
||||
$user_id ?? 'admin',
|
||||
$note ? mb_substr($note, 0, 500) : null,
|
||||
$incidentId,
|
||||
]);
|
||||
|
||||
// ── رفع التجميد ─────────────────────────────────────────
|
||||
// افتراضياً يُرفع عند الإغلاق (resolved / false_alarm) ولا يُرفع عند
|
||||
// مجرد الاطلاع (acknowledged). ويمكن للمراجع أن يقرر صراحةً.
|
||||
$shouldRelease = $releaseHold !== null
|
||||
? in_array(strtolower((string) $releaseHold), ['1', 'true', 'yes'], true)
|
||||
: in_array($newStatus, ['resolved', 'false_alarm'], true);
|
||||
|
||||
$released = false;
|
||||
$driverId = $incident['driver_id'] ?? null;
|
||||
|
||||
if ($shouldRelease && $driverId) {
|
||||
try {
|
||||
// نرفع فقط إن كان التجميد من هذا البلاغ تحديداً: سائق عليه
|
||||
// بلاغ ثانٍ أحدث يجب ألا يُفرَج عنه بإغلاق الأقدم.
|
||||
$upd = $con->prepare("
|
||||
UPDATE driver SET safetyHoldIncidentId = NULL
|
||||
WHERE id = ? AND safetyHoldIncidentId = ?
|
||||
");
|
||||
$upd->execute([$driverId, $incidentId]);
|
||||
|
||||
if ($upd->rowCount() > 0) {
|
||||
if (isset($redisLocation) && $redisLocation) {
|
||||
$redisLocation->del("driver:$driverId:safety_hold");
|
||||
}
|
||||
$released = true;
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
error_log("[safety/update] تعذّر رفع تجميد السائق $driverId: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
error_log("[safety] بلاغ #$incidentId → $newStatus بواسطة "
|
||||
. ($user_id ?? 'admin') . ($released ? " (رُفع التجميد)" : ""));
|
||||
|
||||
jsonSuccess([
|
||||
'incident_id' => $incidentId,
|
||||
'status' => $newStatus,
|
||||
'hold_released' => $released,
|
||||
], "Incident updated");
|
||||
|
||||
} catch (PDOException $e) {
|
||||
error_log("[safety/update] " . $e->getMessage());
|
||||
jsonError("DB Error", 500);
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
-- 2026_08_07_safety_incidents.sql
|
||||
--
|
||||
-- غرفة عمليات السلامة — أثر يمكن مراجعته لواقعة حقيقية.
|
||||
--
|
||||
-- الوضع اليوم: زر SOS في تطبيق الراكب ينتهي عند رسالة واتساب لجهة اتصال
|
||||
-- شخصية خزّنها الراكب بنفسه (ui_interactions_controller.dart:145). لا نداء
|
||||
-- API، لا سجل، لا تنبيه لأحد داخل الشركة. لو وقعت حادثة فعلية، الشركة لا
|
||||
-- تعرف بها ولا تملك دليلاً عنها ولا تستطيع التدخل.
|
||||
--
|
||||
-- هذا الجدول هو الأثر المفقود. رسالة الواتساب تبقى كما هي — تُرسل بالتوازي
|
||||
-- لا بدلاً، لأنها الأسرع وصولاً لمن يهمّه الأمر فعلاً.
|
||||
--
|
||||
-- ⚠️ يُنفَّذ على قاعدة primary (jorSiroDB) وحدها.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `safety_incidents` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- من أبلغ ولماذا
|
||||
`reporter_type` VARCHAR(20) NOT NULL
|
||||
COMMENT 'passenger | driver — من ضغط الزر',
|
||||
`reporter_id` VARCHAR(100) NOT NULL,
|
||||
`trigger_type` VARCHAR(30) NOT NULL DEFAULT 'sos_button'
|
||||
COMMENT 'sos_button | shake | route_deviation | long_stop | overtime',
|
||||
|
||||
-- سياق الواقعة
|
||||
`ride_id` VARCHAR(20) NULL DEFAULT NULL,
|
||||
`driver_id` VARCHAR(100) NULL DEFAULT NULL,
|
||||
`passenger_id` VARCHAR(100) NULL DEFAULT NULL,
|
||||
`lat` DECIMAL(10,7) NULL DEFAULT NULL,
|
||||
`lng` DECIMAL(10,7) NULL DEFAULT NULL,
|
||||
`note` VARCHAR(500) NULL DEFAULT NULL,
|
||||
|
||||
-- لقطة الحالة وقت الواقعة، مجمّدة كما كانت.
|
||||
-- الرحلة تتغيّر بعد الحادثة (تُلغى، تُنهى، يُصفَّر السائق) فقراءتها لاحقاً
|
||||
-- لا تصف اللحظة. هذه اللقطة هي الدليل.
|
||||
`snapshot` JSON NULL DEFAULT NULL,
|
||||
|
||||
-- دورة المعالجة
|
||||
`status` VARCHAR(20) NOT NULL DEFAULT 'open'
|
||||
COMMENT 'open | acknowledged | resolved | false_alarm',
|
||||
`acknowledged_by` VARCHAR(100) NULL DEFAULT NULL,
|
||||
`acknowledged_at` DATETIME NULL DEFAULT NULL,
|
||||
`resolution_note` VARCHAR(500) NULL DEFAULT NULL,
|
||||
|
||||
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_status_created` (`status`, `created_at`),
|
||||
KEY `idx_ride` (`ride_id`),
|
||||
KEY `idx_driver` (`driver_id`, `created_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
|
||||
COMMENT='بلاغات السلامة — السجل الوحيد القابل للمراجعة لواقعة';
|
||||
|
||||
-- تجميد إسناد الرحلات للسائق حتى المراجعة.
|
||||
-- منفصل عن blockedUntil (عقوبة الإلغاءات المتكررة): ذاك مؤقت بساعات
|
||||
-- وينتهي وحده، وهذا يبقى حتى يرفعه إنسان.
|
||||
ALTER TABLE `driver`
|
||||
ADD COLUMN `safetyHoldIncidentId` INT NULL DEFAULT NULL
|
||||
COMMENT 'رقم بلاغ السلامة الذي جمّد هذا السائق. NULL = غير مجمّد';
|
||||
|
||||
-- للتراجع:
|
||||
-- DROP TABLE `safety_incidents`;
|
||||
-- ALTER TABLE `driver` DROP COLUMN `safetyHoldIncidentId`;
|
||||
@@ -64,6 +64,15 @@ try {
|
||||
printFailure("Your account is temporarily suspended until $blockedUntil");
|
||||
exit;
|
||||
}
|
||||
|
||||
// تجميد سلامة — بلا TTL: لا ينتهي بالوقت بل برفع إنسان له.
|
||||
// سائق عليه بلاغ سلامة مفتوح لا يُسند له راكب جديد.
|
||||
$safetyHold = $redisLocation->get("driver:$driverId:safety_hold");
|
||||
if ($safetyHold) {
|
||||
error_log("[accept_ride] DriverID=$driverId مجمّد ببلاغ سلامة #$safetyHold — رُفض القبول");
|
||||
printFailure("Your account is under review. Please contact support.");
|
||||
exit;
|
||||
}
|
||||
}
|
||||
} catch (Throwable $eBlock) {
|
||||
error_log("[accept_ride] تعذّر فحص الإيقاف: " . $eBlock->getMessage());
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
// ============================================================
|
||||
// safety/incident.php — تسجيل بلاغ سلامة
|
||||
//
|
||||
// كان زر SOS ينتهي عند رسالة واتساب لجهة اتصال شخصية. الشركة لا تعلم،
|
||||
// ولا تملك دليلاً، ولا تستطيع التدخل. هذه النقطة هي الأثر المفقود.
|
||||
//
|
||||
// رسالة الواتساب تبقى كما هي: التطبيق يرسلها **بالتوازي** لا بدلاً —
|
||||
// هي الأسرع وصولاً لمن يهمّه أمر الراكب فعلاً، وهذه النقطة للشركة.
|
||||
//
|
||||
// ثلاثة أشياء تحدث هنا، بهذا الترتيب عمداً:
|
||||
// ١. تسجيل البلاغ ولقطة الحالة ← الأهم، ولا يعتمد على شيء
|
||||
// ٢. تجميد إسناد الرحلات للسائق ← يمنع تكرار الواقعة فوراً
|
||||
// ٣. تنبيه الإدارة ← أفضل جهد، لا يُسقط ما قبله
|
||||
// ============================================================
|
||||
|
||||
require_once __DIR__ . '/../connect.php';
|
||||
|
||||
$reporterType = filterRequest("reporter_type") ?: 'passenger';
|
||||
$reporterId = filterRequest("reporter_id");
|
||||
$rideId = filterRequest("ride_id");
|
||||
$triggerType = filterRequest("trigger_type") ?: 'sos_button';
|
||||
$note = filterRequest("note");
|
||||
$lat = filterRequest("lat");
|
||||
$lng = filterRequest("lng");
|
||||
|
||||
if (!$reporterId) {
|
||||
jsonError("Missing reporter_id");
|
||||
}
|
||||
|
||||
// قائمة بيضاء: القيم تُخزَّن وتُعرض في لوحة الإدارة، ولا نريد نصاً حراً.
|
||||
$allowedReporters = ['passenger', 'driver'];
|
||||
$allowedTriggers = ['sos_button', 'shake', 'route_deviation', 'long_stop', 'overtime'];
|
||||
if (!in_array($reporterType, $allowedReporters, true)) $reporterType = 'passenger';
|
||||
if (!in_array($triggerType, $allowedTriggers, true)) $triggerType = 'sos_button';
|
||||
|
||||
$driverId = null;
|
||||
$passengerId = null;
|
||||
$snapshot = ['reported_at' => date('c'), 'trigger' => $triggerType];
|
||||
|
||||
try {
|
||||
// ── ١. لقطة الحالة ──────────────────────────────────────
|
||||
// تُلتقط الآن لأن الرحلة تتغيّر بعد الحادثة (تُلغى، يُصفَّر السائق)،
|
||||
// فقراءتها لاحقاً لا تصف اللحظة التي وقع فيها البلاغ.
|
||||
if ($rideId) {
|
||||
$stmt = $con->prepare("SELECT * FROM ride WHERE id = ? LIMIT 1");
|
||||
$stmt->execute([$rideId]);
|
||||
$ride = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($ride) {
|
||||
$driverId = ($ride['driver_id'] ?? '0') !== '0' ? $ride['driver_id'] : null;
|
||||
$passengerId = $ride['passenger_id'] ?? null;
|
||||
|
||||
$snapshot['ride'] = [
|
||||
'status' => $ride['status'] ?? null,
|
||||
'start_location' => $ride['start_location'] ?? null,
|
||||
'end_location' => $ride['end_location'] ?? null,
|
||||
'carType' => $ride['carType'] ?? $ride['car_type'] ?? null,
|
||||
'price' => $ride['price'] ?? null,
|
||||
'rideTimeStart' => $ride['rideTimeStart'] ?? null,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// آخر موقع معروف للسائق من Redis — أدق من أي شيء في MySQL، ويجيب
|
||||
// على السؤال الأول الذي تسأله غرفة العمليات: أين هم الآن؟
|
||||
if ($driverId && isset($redisLocation) && $redisLocation) {
|
||||
try {
|
||||
$loc = $redisLocation->hGetAll("driver:public:$driverId");
|
||||
if (!empty($loc)) {
|
||||
$snapshot['driver_location'] = [
|
||||
'lat' => $loc['lat'] ?? null,
|
||||
'lng' => $loc['lng'] ?? null,
|
||||
'heading' => $loc['heading'] ?? null,
|
||||
'speed' => $loc['speed'] ?? null,
|
||||
'updated_at' => $loc['updated_at'] ?? null,
|
||||
];
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
error_log("[safety] تعذّرت قراءة موقع السائق: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// موقع المبلِّغ كما أرسله التطبيق. نخزّنه كما هو ولا نبني عليه قراراً —
|
||||
// قيمة قادمة من العميل، لكنها في بلاغ سلامة أفضل من لا شيء.
|
||||
if (is_numeric($lat) && is_numeric($lng)) {
|
||||
$snapshot['reporter_location'] = ['lat' => (float) $lat, 'lng' => (float) $lng];
|
||||
}
|
||||
|
||||
// ── ٢. تسجيل البلاغ ─────────────────────────────────────
|
||||
$ins = $con->prepare("
|
||||
INSERT INTO safety_incidents
|
||||
(reporter_type, reporter_id, trigger_type, ride_id,
|
||||
driver_id, passenger_id, lat, lng, note, snapshot)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
");
|
||||
$ins->execute([
|
||||
$reporterType, $reporterId, $triggerType, $rideId ?: null,
|
||||
$driverId, $passengerId,
|
||||
is_numeric($lat) ? $lat : null,
|
||||
is_numeric($lng) ? $lng : null,
|
||||
$note ? mb_substr($note, 0, 500) : null,
|
||||
json_encode($snapshot, JSON_UNESCAPED_UNICODE),
|
||||
]);
|
||||
$incidentId = (int) $con->lastInsertId();
|
||||
|
||||
error_log("[safety] بلاغ #$incidentId — $triggerType من $reporterType"
|
||||
. " $reporterId (رحلة=" . ($rideId ?: '-') . " سائق=" . ($driverId ?: '-') . ")");
|
||||
|
||||
} catch (PDOException $e) {
|
||||
error_log("[safety] فشل تسجيل البلاغ: " . $e->getMessage());
|
||||
jsonError("Failed to record incident", 500);
|
||||
}
|
||||
|
||||
// ── ٣. تجميد إسناد الرحلات للسائق ───────────────────────────
|
||||
// يوقف تكرار الواقعة فوراً. منفصل عن blockedUntil (عقوبة الإلغاءات) الذي
|
||||
// ينتهي بعد ساعات وحده — هذا يبقى حتى يرفعه إنسان.
|
||||
// لا يُطبَّق على بلاغ السائق عن نفسه: قد يكون هو المعتدى عليه.
|
||||
if ($driverId && $reporterType === 'passenger') {
|
||||
try {
|
||||
$con->prepare("UPDATE driver SET safetyHoldIncidentId = ? WHERE id = ?")
|
||||
->execute([$incidentId, $driverId]);
|
||||
|
||||
// نسخة Redis هي ما يفحصه acceptRide.php — بلا TTL: التجميد لا ينتهي بالوقت.
|
||||
if (isset($redisLocation) && $redisLocation) {
|
||||
$redisLocation->set("driver:$driverId:safety_hold", (string) $incidentId);
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
error_log("[safety] تعذّر تجميد السائق $driverId: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// ── ٤. تنبيه الإدارة ────────────────────────────────────────
|
||||
// أفضل جهد: البلاغ مسجَّل بالفعل، وفشل التنبيه لا يُسقطه.
|
||||
try {
|
||||
if (isset($redis) && $redis) {
|
||||
// قائمة يقرأها بانل الإدارة — أسرع من استعلام على الجدول عند كل تحديث
|
||||
$redis->lPush('safety:open_incidents', (string) $incidentId);
|
||||
$redis->lTrim('safety:open_incidents', 0, 199);
|
||||
}
|
||||
|
||||
if (function_exists('sendFCM_Internal')) {
|
||||
$topic = getenv('SAFETY_ALERT_TOPIC') ?: 'siro_safety_ops';
|
||||
sendFCM_Internal(
|
||||
$topic,
|
||||
"🚨 بلاغ سلامة #$incidentId",
|
||||
"بلاغ $triggerType من $reporterType" . ($rideId ? " — رحلة $rideId" : ""),
|
||||
['category' => 'safety_incident', 'incident_id' => (string) $incidentId],
|
||||
"safety_incident",
|
||||
true // topic
|
||||
);
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
error_log("[safety] تعذّر تنبيه الإدارة للبلاغ #$incidentId: " . $e->getMessage());
|
||||
}
|
||||
|
||||
jsonSuccess([
|
||||
'incident_id' => $incidentId,
|
||||
'driver_frozen' => (bool) ($driverId && $reporterType === 'passenger'),
|
||||
], "Incident recorded");
|
||||
@@ -233,6 +233,11 @@ class AppLink {
|
||||
/// تستخدم نفس دالة الحساب التي يستخدمها الإلغاء الفعلي، فما يُعرض
|
||||
/// للراكب هو ما سيُخصم منه بالضبط.
|
||||
static String get previewCancelFee => "$server/ride/rides/preview_cancel_fee.php";
|
||||
|
||||
/// تسجيل بلاغ سلامة لدى الشركة. يُستدعى **بالتوازي** مع رسالة الواتساب
|
||||
/// لجهة الاتصال الشخصية، لا بدلاً منها: الواتساب أسرع وصولاً لمن يهمّه
|
||||
/// الأمر، وهذه النقطة هي الأثر الذي تملكه الشركة وتستطيع التدخل به.
|
||||
static String get safetyIncident => "$server/safety/incident.php";
|
||||
//-----------------ridessss------------------
|
||||
static String get addRides => "$rideServerSide/ride/rides/add.php";
|
||||
static String get getRides => "$rideServerSide/ride/rides/get.php";
|
||||
|
||||
@@ -128,6 +128,10 @@ class UiInteractionsController extends GetxController {
|
||||
kolor: AppColor.redColor,
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
// الشركة أولاً ثم الواتساب: تسجيل البلاغ لا ينتظر شيئاً ولا
|
||||
// يؤخّر الرسالة (بلا await)، لكنه يُطلق قبلها حتى لا يضيع
|
||||
// إن أغلق المستخدم التطبيق بعد فتح واتساب.
|
||||
_reportSafetyIncident();
|
||||
_shareTripDetailsSOS();
|
||||
},
|
||||
),
|
||||
@@ -142,6 +146,32 @@ class UiInteractionsController extends GetxController {
|
||||
});
|
||||
}
|
||||
|
||||
/// يسجّل البلاغ لدى الشركة: يُنشئ سجل حادثة، يُجمّد إسناد الرحلات
|
||||
/// للسائق حتى المراجعة، ويُنبّه غرفة العمليات.
|
||||
///
|
||||
/// لا ينتظر ولا يرمي: زر الطوارئ يجب ألا يتعطّل أبداً بسبب الشبكة.
|
||||
/// أي فشل هنا لا يمنع رسالة الواتساب التي تليه.
|
||||
void _reportSafetyIncident({String trigger = 'sos_button'}) {
|
||||
try {
|
||||
final rideLifecycle = Get.find<RideLifecycleController>();
|
||||
final locSearch = Get.find<LocationSearchController>();
|
||||
|
||||
CRUD().post(
|
||||
link: AppLink.safetyIncident,
|
||||
payload: {
|
||||
'reporter_type': 'passenger',
|
||||
'reporter_id': box.read(BoxName.passengerID).toString(),
|
||||
'ride_id': rideLifecycle.rideId?.toString() ?? '',
|
||||
'trigger_type': trigger,
|
||||
'lat': locSearch.passengerLocation.latitude.toString(),
|
||||
'lng': locSearch.passengerLocation.longitude.toString(),
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
Log.print("⚠️ تعذّر تسجيل بلاغ السلامة: $e");
|
||||
}
|
||||
}
|
||||
|
||||
void _shareTripDetailsSOS() {
|
||||
final rideLifecycle = Get.find<RideLifecycleController>();
|
||||
final locSearch = Get.find<LocationSearchController>();
|
||||
|
||||
Reference in New Issue
Block a user