getInvalidSessionChartData(); } $query = GuardPatrolEntry::where('plant_id', $selectedPlant) ->where('guard_name_id', $selectedGuardName) ->whereDate('patrol_time', $selectedDate); if (!empty($selectedTime)) { [$startTime, $endTime] = explode(' - ', $selectedTime); $startDateTime = Carbon::parse($selectedDate . ' ' . $startTime); $endDateTime = Carbon::parse($selectedDate . ' ' . $endTime); $query->whereBetween('guard_patrol_entries.patrol_time', [$startDateTime, $endDateTime]); } $patrols = $query->orderBy('patrol_time')->get(); if ($patrols->count() < 2) { return []; } // Get all CheckPointTime records for the plant $checkPointTimes = CheckPointTime::where('plant_id', $selectedPlant) ->orderBy('sequence_number') ->get(['sequence_number', 'min_cushioning', 'max_cushioning', 'check_point1_id', 'check_point2_id']); // Build the expected sequence $expectedSequence = []; foreach ($checkPointTimes as $row) { $expectedSequence[] = $row->check_point1_id; } if ($checkPointTimes->isNotEmpty()) { $expectedSequence[] = $checkPointTimes->last()->check_point2_id; } // Prepare chart data $intervals = []; $labels = []; $colors = []; $cushioningData = $checkPointTimes->keyBy('sequence_number'); // Track the current position in the expected sequence $currentSeqIndex = 0; for ($i = 0; $i < $patrols->count() - 1; $i++) { $current = Carbon::parse($patrols[$i]->patrol_time); $next = Carbon::parse($patrols[$i+1]->patrol_time); // $interval = $next->diffInMinutes($current, true); $diffInSeconds = $next->floatDiffInRealSeconds($current, true); // get float seconds $interval = round($diffInSeconds / 60, 2); $intervals[] = $interval; $labels[] = "Seq " . ($i + 1); // Check if current patrol's checkpoint matches the next expected if ($currentSeqIndex < count($expectedSequence) && $patrols[$i]->check_point_name_id == $expectedSequence[$currentSeqIndex]) { $currentSeqIndex++; // Apply cushioning logic for valid sequence $sequenceNumber = ($currentSeqIndex - 1) % $checkPointTimes->count() + 1; $cushioning = $cushioningData[$sequenceNumber] ?? null; if (!$cushioning) { $colors[] = '#cccccc'; continue; } $min = $cushioning->min_cushioning; $max = $cushioning->max_cushioning; if ($interval < $min) { $colors[] = '#ffd700'; } elseif ($interval <= $max) { $colors[] = '#4caf50'; } else { $colors[] = '#ff4c4c'; } } else { // Out of order: mark as red $colors[] = '#ff4c4c'; } } return [ 'labels' => $labels, 'datasets' => [ [ 'label' => 'Interval (minutes)', 'data' => $intervals, 'backgroundColor' => $colors, 'borderColor' => '#333', 'borderWidth' => 1, ], ], ]; } protected function getInvalidSessionChartData(): array { return [ 'labels' => ['X'], 'datasets' => [ [ 'label' => 'Interval (minutes)', 'data' => [0], 'backgroundColor' => ['#ff4c4c'], 'borderColor' => '#333', 'borderWidth' => 1, ], ], ]; } protected function getOptions(): array { return [ 'scales' => [ 'y' => [ 'beginAtZero' => true, //Start Y-axis from 0 'ticks' => [ 'stepSize' => 5, ], ], ], 'plugins' => [ 'datalabels' => [ 'anchor' => 'start', 'align' => 'end', 'color' => '#000', 'font' => [ 'weight' => 'bold', ], //'formatter' => Js::raw('function(value) { return Number(value).toFixed(2) + " min"; }'), //'formatter' => 'function(value) { return Number(value).toFixed(2) + " min"; }', 'formatter' => Js::from("function(value) { return Number(value).toFixed(2) + ' min'; }"), //'formatter' => Js::from(new \Illuminate\Support\Stringable('function(value) { return Number(value).toFixed(2) + " min"; }')), ], ], ]; } // protected function getOptions(): array // { // return json_decode(json_encode([ // 'plugins' => [ // 'datalabels' => [ // 'anchor' => 'end', // 'align' => 'start', // 'offset' => 4, // 'color' => '#000', // 'font' => [ // 'weight' => 'bold', // ], // 'formatter' => null, // temporarily set null // ], // ], // 'scales' => [ // 'y' => [ // 'beginAtZero' => true, // ], // ], // ]), true); // } protected function getType(): string { return 'bar'; } }