From cbfe7a56bb8327ca77b8f1af707bf205dbd4ce46 Mon Sep 17 00:00:00 2001 From: dhanabalan Date: Wed, 25 Jun 2025 14:23:52 +0530 Subject: [PATCH] Added guard hourly patrol chart --- app/Filament/Pages/GuardPatrolHourlyCount.php | 378 ++++++++++++++++++ .../Widgets/GuardPatrolHourlyChart.php | 249 ++++++++++++ database/seeders/PermissionSeeder.php | 2 + .../pages/guard-patrol-hourly-count.blade.php | 10 + 4 files changed, 639 insertions(+) create mode 100644 app/Filament/Pages/GuardPatrolHourlyCount.php create mode 100644 app/Filament/Widgets/GuardPatrolHourlyChart.php create mode 100644 resources/views/filament/pages/guard-patrol-hourly-count.blade.php diff --git a/app/Filament/Pages/GuardPatrolHourlyCount.php b/app/Filament/Pages/GuardPatrolHourlyCount.php new file mode 100644 index 0000000..288d110 --- /dev/null +++ b/app/Filament/Pages/GuardPatrolHourlyCount.php @@ -0,0 +1,378 @@ +forget(['selected_plant']); + session()->forget(['selected_date']); + session()->forget(['selected_name']); + session()->forget(['selected_time']); + session()->forget(['valid_sessions']); + $this->filtersForm->fill([ + 'plant' => null, + 'date' => null, + 'time_range' => null, + 'guard_name' => null + ]); + } + + public function filtersForm(Form $form): Form + { + return $form + ->statePath('filters') + ->schema([ + Select::make('plant') + ->options(Plant::pluck('name', 'id')) + ->label('Select Plant') + ->reactive() + ->required() + ->afterStateUpdated(function ($state) { + session(['selected_plant' => $state]); + // $this->dispatch('patrolEntryChart'); + }), + DatePicker::make('date') + ->label('Select Date') + ->placeholder('Select Date') + ->reactive() + ->required() + ->beforeOrEqual(now()) + ->default(now()->format('Y-m-d')) + ->afterStateUpdated(function ($state) { + session(['selected_date' => $state]); + // $this->dispatch('patrolEntryChart'); + }), + Select::make('guard_name') + ->label('Guard Name') + ->options(function (callable $get) { + $plantId = $get('plant'); + if (!$plantId) { + return []; + } + return GuardPatrolEntry::where('guard_patrol_entries.plant_id', $plantId) + ->join('guard_names', 'guard_patrol_entries.guard_name_id', '=', 'guard_names.id') + ->select('guard_names.id', 'guard_names.name') + ->groupBy('guard_names.id', 'guard_names.name') + ->pluck('guard_names.name', 'guard_names.id') + ->toArray(); + + }) + ->reactive() + ->required() + ->afterStateUpdated(function ($state,callable $set) + { + if (is_null($state)) + { + session()->forget('selected_name'); + } + else + { + session(['selected_name' => $state]); + } + + // Clear the time range field and its session + $set('time_range', null); + session()->forget('selected_time'); + }), + + // Select::make('time_range') + // ->label('Patrol Time Range') + // ->options(function (callable $get) { + // $plantId = $get('plant'); + // $guardName = trim($get('guard_name')); + // $date = $get('date'); + + // if (!$plantId || !$guardName || !$date) { + // return []; + // } + + // $patrols = GuardPatrolEntry::where('guard_patrol_entries.plant_id', $plantId) + // ->join('guard_names', 'guard_patrol_entries.guard_name_id', '=', 'guard_names.id') + // ->where('guard_patrol_entries.guard_name_id', $guardName) + // ->whereDate('guard_patrol_entries.patrol_time', $date) + // ->orderBy('guard_patrol_entries.patrol_time') + // ->get(['guard_patrol_entries.patrol_time', 'guard_patrol_entries.check_point_name_id']); + + // if ($patrols->isEmpty()) { + // return []; + // } + + // // $startCheckpointId = 1; // "STP BACKSIDE" + // // $endCheckpointId = 10; // "D 72 END" + // $firstCheckpoint = CheckPointName::where('plant_id', $plantId) + // ->orderBy('id') + // ->first(['id', 'name']); + + // $lastCheckpoint = CheckPointName::where('plant_id', $plantId) + // ->orderByDesc('id') + // ->first(['id', 'name']); + + // $startCheckpointId = $firstCheckpoint->id; + // $endCheckpointId = $lastCheckpoint->id; + + // $sessions = []; + // $currentSession = []; + + // foreach ($patrols as $patrol) + // { + // $currentSession[] = $patrol; + // if ($patrol->check_point_name_id == $endCheckpointId) + // { + // if (!empty($currentSession) && $currentSession[0]->check_point_name_id == $startCheckpointId) { + // $sessions[] = $currentSession; + // } + // $currentSession = []; + // } + // } + + // $options = []; + // $validSessions = []; + + // $startCheckpointId = $firstCheckpoint->id; + // $endCheckpointId = $lastCheckpoint->id; + + // foreach ($sessions as $session) { + // $start = Carbon::parse($session[0]->patrol_time)->format('H:i:s'); + // $end = Carbon::parse(end($session)->patrol_time)->format('H:i:s'); + // $timeRange = "$start - $end"; + // $options[$timeRange] = $timeRange; + + // // Check if session is valid + // $isValid = ($session[0]->check_point_name_id == $startCheckpointId) && + // (end($session)->check_point_name_id == $endCheckpointId); + // $validSessions[$timeRange] = $isValid; + // } + + // // Store validSessions in session or somewhere accessible + // session(['valid_sessions' => $validSessions]); + + // return $options; + + // // $options = []; + // // foreach ($sessions as $session) + // // { + // // $start = Carbon::parse($session[0]->patrol_time)->format('H:i:s'); + // // $end = Carbon::parse(end($session)->patrol_time)->format('H:i:s'); + // // $timeRange = "$start - $end"; + // // $options[$timeRange] = $timeRange; + // // } + // // return $options; + // }) + // ->reactive() + // ->required() + // ->afterStateUpdated(function ($state) { + // session(['selected_time' => $state]); + // }), + + // Select::make('time_range') + // ->label('Patrol Time Range') + // ->options(function (callable $get) { + // $plantId = $get('plant'); + // $guardName = trim($get('guard_name')); + // $date = $get('date'); + + // if (!$plantId || !$guardName || !$date) { + // return []; + // } + + // // Get all patrols for the guard, plant, and date + // $patrols = GuardPatrolEntry::where('guard_patrol_entries.plant_id', $plantId) + // ->join('guard_names', 'guard_patrol_entries.guard_name_id', '=', 'guard_names.id') + // ->where('guard_patrol_entries.guard_name_id', $guardName) + // ->whereDate('guard_patrol_entries.patrol_time', $date) + // ->orderBy('guard_patrol_entries.patrol_time') + // ->get(['guard_patrol_entries.patrol_time', 'guard_patrol_entries.check_point_name_id']); + + // if ($patrols->isEmpty()) { + // session(['valid_sessions' => []]); + // return []; + // } + + // // Get the name of the first checkpoint in the patrol + // $firstPatrolCheckpointId = $patrols[0]->check_point_name_id; + // $firstPatrolCheckpointName = CheckPointName::where('id', $firstPatrolCheckpointId) + // ->value('name'); + + // // Get the name of the first checkpoint in the system (for this plant) + // $firstCheckpointName = CheckPointName::where('plant_id', $plantId) + // ->orderBy('id') + // ->value('name'); + + // // If the first patrol checkpoint is not the system's first checkpoint, invalid + // if ($firstPatrolCheckpointName != $firstCheckpointName) { + // session(['valid_sessions' => []]); + // return []; + // } + + // // Get the last checkpoint in the system (for this plant) + // $lastCheckpointName = CheckPointName::where('plant_id', $plantId) + // ->orderByDesc('id') + // ->value('name'); + // $lastCheckpointId = CheckPointName::where('plant_id', $plantId) + // ->orderByDesc('id') + // ->value('id'); + + // $sessions = []; + // $currentSession = []; + + // foreach ($patrols as $patrol) { + // $currentSession[] = $patrol; + // if ($patrol->check_point_name_id == $lastCheckpointId) { + // if (!empty($currentSession) && $currentSession[0]->check_point_name_id == $firstPatrolCheckpointId) { + // $sessions[] = $currentSession; + // } + // $currentSession = []; + // } + // } + + // $options = []; + // $validSessions = []; + + // foreach ($sessions as $session) { + // $start = Carbon::parse($session[0]->patrol_time)->format('H:i:s'); + // $end = Carbon::parse(end($session)->patrol_time)->format('H:i:s'); + // $timeRange = "$start - $end"; + // $options[$timeRange] = $timeRange; + + // // Check if session is valid (should always be true if the above checks pass) + // $isValid = ($session[0]->check_point_name_id == $firstPatrolCheckpointId) && + // (end($session)->check_point_name_id == $lastCheckpointId); + // $validSessions[$timeRange] = $isValid; + // } + + // session(['valid_sessions' => $validSessions]); + // return $options; + // }) + // ->reactive() + // ->required() + // ->afterStateUpdated(function ($state) { + // session(['selected_time' => $state]); + // }), + + Select::make('time_range') + ->label('Patrol Time Range') + ->options(function (callable $get) { + $plantId = $get('plant'); + $guardName = trim($get('guard_name')); + $date = $get('date'); + + if (!$plantId || !$guardName || !$date) { + session(['valid_sessions' => []]); + return []; + } + + // Get all patrols for the guard, plant, and date + $patrols = GuardPatrolEntry::where('guard_patrol_entries.plant_id', $plantId) + ->join('guard_names', 'guard_patrol_entries.guard_name_id', '=', 'guard_names.id') + ->where('guard_patrol_entries.guard_name_id', $guardName) + ->whereDate('guard_patrol_entries.patrol_time', $date) + ->orderBy('guard_patrol_entries.patrol_time') + ->get(['guard_patrol_entries.patrol_time', 'guard_patrol_entries.check_point_name_id']); + + if ($patrols->isEmpty()) { + session(['valid_sessions' => []]); + return []; + } + + // Get the first and last checkpoint for this plant + $firstCheckpoint = CheckPointName::where('plant_id', $plantId) + ->orderBy('id') + ->first(['id', 'name']); + $lastCheckpoint = CheckPointName::where('plant_id', $plantId) + ->orderByDesc('id') + ->first(['id', 'name']); + + $startCheckpointId = $firstCheckpoint->id; + $endCheckpointId = $lastCheckpoint->id; + + // Optionally: Check if the first patrol is the system's first checkpoint + // (If you want to mark all sessions as invalid if the first patrol is not the first checkpoint) + $firstPatrolCheckpointId = $patrols[0]->check_point_name_id; + $firstPatrolCheckpointName = CheckPointName::where('id', $firstPatrolCheckpointId)->value('name'); + $firstCheckpointName = $firstCheckpoint->name; + + $allSessionsInvalid = ($firstPatrolCheckpointName != $firstCheckpointName); + + // Build sessions: group patrols between start and end checkpoints + // (Or just group all patrols into one session, or as you prefer) + // For this example, let's assume you want to group between start and end checkpoints + $sessions = []; + $currentSession = []; + + foreach ($patrols as $patrol) { + $currentSession[] = $patrol; + if ($patrol->check_point_name_id == $endCheckpointId) { + $sessions[] = $currentSession; + $currentSession = []; + } + } + // Add any remaining patrols as a session (optional) + if (!empty($currentSession)) { + $sessions[] = $currentSession; + } + + $options = []; + $validSessions = []; + + foreach ($sessions as $session) { + $start = Carbon::parse($session[0]->patrol_time)->format('H:i:s'); + $end = Carbon::parse(end($session)->patrol_time)->format('H:i:s'); + $timeRange = "$start - $end"; + $options[$timeRange] = $timeRange; + + // Check if session is valid + $isValid = !$allSessionsInvalid && // All sessions are invalid if first patrol is not first checkpoint + ($session[0]->check_point_name_id == $startCheckpointId) && + (end($session)->check_point_name_id == $endCheckpointId); + $validSessions[$timeRange] = $isValid; + } + + // Store validSessions in session + session(['valid_sessions' => $validSessions]); + + return $options; + }) + ->reactive() + ->required() + ->afterStateUpdated(function ($state) { + session(['selected_time' => $state]); + }), + ]) + ->columns(4); + } + + public function getWidgets(): array + { + return [ + GuardPatrolHourlyChart::class, + ]; + } + + public static function canAccess(): bool + { + return Auth::check() && Auth::user()->can('view guard patrol hourly count dashboard'); + } +} diff --git a/app/Filament/Widgets/GuardPatrolHourlyChart.php b/app/Filament/Widgets/GuardPatrolHourlyChart.php new file mode 100644 index 0000000..2c5f7e6 --- /dev/null +++ b/app/Filament/Widgets/GuardPatrolHourlyChart.php @@ -0,0 +1,249 @@ +where('guard_patrol_entries.guard_name_id', $selectedGuardName) + ->whereDate('guard_patrol_entries.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('guard_patrol_entries.patrol_time')->get(); + + if ($patrols->count() < 2) + { + return []; + } + $checkpointNames = CheckPointName::where('plant_id', $selectedPlant) + ->whereIn('id', $patrols->pluck('check_point_name_id')->unique()) + ->orderBy('id') // or another column + ->pluck('name', 'id'); + + $nameToSequence = []; + $sequence = 1; + foreach ($checkpointNames as $id => $name) + { + $nameToSequence[$name] = $sequence++; + } + + $sequenceNumbers = array_values($nameToSequence); + + $cushioningData = CheckPointTime::where('plant_id', $selectedPlant) + ->whereIn('sequence_number', $sequenceNumbers) + ->get(['sequence_number', 'min_cushioning', 'max_cushioning']) + ->keyBy('sequence_number'); + + $numSequences = count($cushioningData); + + + $intervals = []; + $labels = []; + $colors = []; + + for ($i = 0; $i < $patrols->count() - 1; $i++) + { + // Calculate interval + $current = Carbon::parse($patrols[$i]->patrol_time); + $next = Carbon::parse($patrols[$i+1]->patrol_time); + $interval = $next->diffInMinutes($current, true); + $intervals[] = $interval; + + $labels[] = "Seq " . ($i + 1); + $sequenceNumber = ($i % $numSequences) + 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'; + } + } + + + + return [ + 'labels' => $labels, + 'datasets' => [ + [ + 'label' => 'Interval (minutes)', + 'data' => $intervals, + 'backgroundColor' => $colors, + 'borderColor' => '#333', + 'borderWidth' => 1, + ], + ], + ]; + + + // $colors = []; + // $intervals = []; + + // 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); + // $intervals[] = $interval; + // } + + // $numSequences = count($sequenceNumbers); + + // for ($i = 0; $i < $patrols->count() - 1; $i++) + // { + // $sequenceNumber = $sequenceNumbers[$i % $numSequences]; + // $cushioning = $cushioningData[$sequenceNumber] ?? null; + + // if (!$cushioning) { + // $colors[] = '#cccccc'; + // continue; + // } + + // $min = $cushioning->min_cushioning; + // $max = $cushioning->max_cushioning; + // $interval = $intervals[$i]; + + // if ($interval < $min) { + // $colors[] = '#ffd700'; + // } elseif ($interval <= $max) { + // $colors[] = '#4caf50'; + // } else { + // $colors[] = '#ff4c4c'; + // } + // } + + // $labels = []; + // for ($i = 0; $i < count($intervals); $i++) { + // $labels[] = "Seq " . ($i + 1); + // } + + // return [ + // 'labels' => $labels, + // 'datasets' => [ + // [ + // 'label' => 'Interval (minutes)', + // 'data' => $intervals, + // 'backgroundColor' => $colors, + // 'borderColor' => '#333', + // 'borderWidth' => 1, + // ], + // ], + // ]; + + + // $colors = []; + + // $intervals = []; + // 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); + // $intervals[] = $interval; + // } + + // for ($i = 0; $i < $patrols->count() - 1; $i++) + // { + // $checkpointId = $patrols[$i+1]->check_point_name_id; + // $name = $checkpointNames[$checkpointId] ?? null; + // $sequenceNumber = $nameToSequence[$name] ?? null; + + // if (!$sequenceNumber) + // { + // $colors[] = '#cccccc'; + // continue; + // } + + // $cushioning = $cushioningData[$sequenceNumber] ?? null; + + // if (!$cushioning) + // { + // $colors[] = '#cccccc'; + // continue; + // } + + // $min = $cushioning->min_cushioning; + // $max = $cushioning->max_cushioning; + // $interval = $intervals[$i]; + + // if ($interval < $min) + // { + // $colors[] = '#ffd700'; + // } elseif ($interval <= $max) + // { + // $colors[] = '#4caf50'; + // } + // else + // { + // $colors[] = '#ff4c4c'; + // } + // } + + // $labels = []; + // for ($i = 0; $i < count($intervals); $i++) + // { + // $labels[] = "Seq " . ($i + 1); + // } + + // return [ + // 'labels' => $labels, + // 'datasets' => [ + // [ + // 'label' => 'Interval (minutes)', + // 'data' => $intervals, + // 'backgroundColor' => $colors, + // 'borderColor' => '#333', + // 'borderWidth' => 1, + // ], + // ], + // ]; + } + + protected function getType(): string + { + return 'bar'; + } +} diff --git a/database/seeders/PermissionSeeder.php b/database/seeders/PermissionSeeder.php index 46d178c..e0d5ce2 100644 --- a/database/seeders/PermissionSeeder.php +++ b/database/seeders/PermissionSeeder.php @@ -131,6 +131,8 @@ class PermissionSeeder extends Seeder Permission::updateOrCreate(['name' => 'view production line stop count dashboard']); Permission::updateOrCreate(['name' => 'view guard patrol entry dashboard']); Permission::updateOrCreate(['name' => 'view guard patrol day count dashboard']); + Permission::updateOrCreate(['name' => 'view guard patrol hourly count dashboard']); + } } diff --git a/resources/views/filament/pages/guard-patrol-hourly-count.blade.php b/resources/views/filament/pages/guard-patrol-hourly-count.blade.php new file mode 100644 index 0000000..250a3ce --- /dev/null +++ b/resources/views/filament/pages/guard-patrol-hourly-count.blade.php @@ -0,0 +1,10 @@ + +
+ {{-- Filters form --}} + {{ $this->filtersForm($this->form) }} + + {{-- Chart widget --}} + + +
+