Initial commit for new repo
All checks were successful
Scan for leaked secrets using Kingfisher / kingfisher-secrets-scan (push) Successful in 1m4s

This commit is contained in:
dhanabalan
2025-12-16 17:05:04 +05:30
commit 3f0d529640
862 changed files with 141157 additions and 0 deletions

View File

@@ -0,0 +1,344 @@
<?php
namespace App\Filament\Widgets;
use App\Models\Line;
use App\Models\ProductionQuantity;
use App\Models\QualityValidation;
use Filament\Widgets\ChartWidget;
use Filament\Support\RawJs;
use Illuminate\Support\HtmlString;
class CumulativeChart extends ChartWidget
{
protected static ?string $heading = 'Production Line Count';
//protected $listeners = ['cumulativeChart'];
protected static ?string $maxHeight = '400px';
protected int|string|array $columnSpan = 12;
// protected function getData(): array
// {
// $selectedPlant = session('selected_plant');
// $activeFilter = $this->filter;
// // Define date range based on filter
// switch ($activeFilter) {
// case 'yesterday':
// $startDate = now()->subDay()->startOfDay();
// $endDate = now()->subDay()->endOfDay();
// break;
// case 'this_week':
// $startDate = now()->startOfWeek();
// $endDate = now()->endOfWeek();
// break;
// case 'this_month':
// $startDate = now()->startOfMonth();
// $endDate = now()->endOfMonth();
// break;
// default: // today
// $startDate = now()->startOfDay();
// $endDate = now()->endOfDay();
// break;
// }
// // Get all lines for selected plant
// $lines = Line::where('plant_id', $selectedPlant)
// ->pluck('name', 'id')
// ->toArray();
// // Get total production per line in the date range
// $production = \DB::table('production_quantities')
// ->select('line_id', \DB::raw('COUNT(*) as total_quantity'))
// ->whereBetween('created_at', [$startDate, $endDate])
// ->whereIn('line_id', array_keys($lines))
// ->groupBy('line_id')
// ->pluck('total_quantity', 'line_id')
// ->toArray();
// // Match quantities with lines (fill 0 if missing)
// $labels = [];
// $data = [];
// foreach ($lines as $lineId => $lineName) {
// $labels[] = $lineName;
// $data[] = $production[$lineId] ?? 0;
// }
// return [
// 'labels' => $labels,
// 'datasets' => [
// [
// 'label' => match ($activeFilter) {
// 'yesterday' => "Production Quantity (Yesterday)",
// 'this_week' => "Daily Production This Week",
// 'this_month' => "Weekly Production This Month",
// default => "Today's Production",
// },
// 'data' => $data,
// ],
// ],
// ];
// }
protected function getData(): array
{
$selectedPlant = session('selected_plant');
// $selectedStatus = session('success_status');
$activeFilter = $this->filter;
// if (!$selectedPlant || !$selectedStatus) {
// return [
// 'labels' => [],
// 'datasets' => [],
// ];
// }
if (!$selectedPlant) {
return [
'labels' => [],
'datasets' => [],
];
}
switch ($activeFilter)
{
case 'yesterday':
$startDate = now()->subDay()->setTime(8, 0, 0);
$endDate = now()->setTime(8, 0, 0);
$groupBy = 'EXTRACT(HOUR FROM created_at)';
break;
case 'this_week':
$startDate = now()->startOfWeek()->setTime(8, 0, 0);
$endDate = now()->endOfWeek()->addDay()->setTime(8, 0, 0);
$groupBy = 'EXTRACT(DOW FROM created_at)';
break;
case 'this_month':
$startDate = now()->startOfMonth()->setTime(8, 0, 0);
$endDate = now()->endOfMonth()->addDay()->setTime(8, 0, 0);
$groupBy = "FLOOR((EXTRACT(DAY FROM created_at) - 1) / 7) + 1";
break;
default: // today
$startDate = now()->setTime(8, 0, 0);
$endDate = now()->copy()->addDay()->setTime(8, 0, 0);
$groupBy = 'EXTRACT(HOUR FROM created_at)';
break;
}
// Get lines with names and types
$lines = Line::where('plant_id', $selectedPlant)->get(['id', 'name', 'type']);
$lineNames = [];
$fgLineIds = [];
$nonFgLineIds = [];
foreach ($lines as $line)
{
$lineNames[$line->id] = $line->name;
if ($line->type == 'FG Line')
{
$fgLineIds[] = $line->id;
}
else
{
$nonFgLineIds[] = $line->id;
}
}
//FG production from quality_validations
$fgProduction = QualityValidation::select('line_id', \DB::raw('COUNT(*) as total_quantity'))
// ->whereBetween('created_at', [$startDate, $endDate])
->where('created_at', '>=', $startDate)
->where('created_at', '<', $endDate)
->whereIn('line_id', $fgLineIds)
->groupBy('line_id')
->pluck('total_quantity', 'line_id')
->toArray();
$nonFgQuery = ProductionQuantity::select('line_id', \DB::raw('COUNT(*) as total_quantity'))
->whereBetween('created_at', [$startDate, $endDate])
->whereIn('line_id', $nonFgLineIds);
// if ($selectedStatus) {
// $nonFgQuery->where('success_status', $selectedStatus);
// }
$nonFgProduction = $nonFgQuery->groupBy('line_id')
->pluck('total_quantity', 'line_id')
->toArray();
//Non-FG production from production_quantities
// $nonFgProduction = ProductionQuantity::select('line_id', \DB::raw('COUNT(*) as total_quantity'))
// ->whereBetween('created_at', [$startDate, $endDate])
// ->whereIn('line_id', $nonFgLineIds)
// ->groupBy('line_id')
// ->pluck('total_quantity', 'line_id')
// ->toArray();
//Separate FG and non-FG into different datasets
$labels = [];
$fgData = [];
$nonFgData = [];
foreach ($lineNames as $lineId => $lineName) {
$labels[] = $lineName;
if (in_array($lineId, $fgLineIds))
{
$fgData[] = $fgProduction[$lineId] ?? 0;
$nonFgData[] = null;
}
else
{
$nonFgData[] = $nonFgProduction[$lineId] ?? 0;
$fgData[] = null;
}
}
$nonFgData = array_map(function ($value) {
return ($value == 0 || is_null($value)) ? null : $value;
}, $nonFgData);
$fgData = array_map(function ($value) {
return ($value == 0 || is_null($value)) ? null : $value;
}, $fgData);
return [
'labels' => $labels,
'datasets' => array_filter([
[
'label' => match ($activeFilter) {
'yesterday' => "Production Quantity (Yesterday)",
'this_week' => "Daily Production This Week",
'this_month' => "Weekly Production This Month",
default => "Today's Production",
},
'data' => $nonFgData,
//'backgroundColor' => '#3b82f6', // Blue for non-FG
],
[
'label' => match ($activeFilter) {
'yesterday' => "FG Count (Yesterday)",
'this_week' => "FG Count This Week",
'this_month' => "FG Count This Month",
default => "Today's FG Count",
},
'data' => $fgData,
// 'backgroundColor' => '#ef4444', // Red for FG
],
]),
];
}
protected function getType(): string
{
return 'bar';
}
protected function getFilters(): ?array
{
return [
'today' => 'Today',
'yesterday' => 'Yesterday',
'this_week'=> 'This Week',
'this_month'=> 'This Month',
];
}
protected function getOptions(): array
{
return [
// 'plugins' => [
// 'datalabels' => false,
// ],
'plugins' => [
'datalabels' => [
'anchor' => 'start', // Positions the label relative to the top of the bar
'align' => 'start', // Aligns the label above the bar
'offset' => -15, // Adjust if needed (positive moves up, negative moves down)
'color' => '#000',
'font' => [
'weight' => 'bold',
],
'formatter' => RawJs::make('function(value) {
return value;
}'),
],
],
'scales' => [
'y' => [
'beginAtZero' => true, //Start Y-axis from 0
'ticks' => [
'stepSize' => 0.5,
],
],
],
];
}
public function getHeading(): HtmlString|string|null
{
$selectedPlant = session('selected_plant');
// Dynamic title and colors
$title = 'Total Production';
$titleColor = '#1e40af';
$iconColor = 'text-blue-500';
$textColor = '#b45309';
$iconSvg = '
<svg xmlns="http://www.w3.org/2000/svg"
class="w-8 h-8 ' . $iconColor . '" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M3 3v18h18M7 13h2v5H7zm4-8h2v13h-2zm4 5h2v8h-2z" />
</svg>
';
// Get chart data for the selected plant
$data = $this->getData();
$fgData = $data['datasets'][1]['data'] ?? [];
$nonFgData = $data['datasets'][0]['data'] ?? [];
// Sum all non-null values for cumulative total
$totalCount = array_sum(array_filter($fgData, fn($v) => !is_null($v)))
+ array_sum(array_filter($nonFgData, fn($v) => !is_null($v)));
$count = number_format($totalCount);
return new HtmlString('
<div class="flex items-center justify-between px-4 py-2 bg-white rounded-lg shadow-sm">
<div class="flex items-center space-x-3">
' . $iconSvg . '
<div style="margin-left: 12px; color: ' . $textColor . '; ">
<p style="color:' . $titleColor . '; font-weight: 600;">' . e($title) . '</p>
<h2 class="text-3xl font-extrabold text-gray-800">' . $count . '</h2>
</div>
</div>
</div>
');
}
// public static function canView(): bool
// {
// return request()->routeIs([
// 'filament.pages.hourly-production',
// 'filament.admin.resources.production-quantities.create',
// ]);
// }
}

View File

@@ -0,0 +1,167 @@
<?php
namespace App\Filament\Widgets;
use App\Models\GuardPatrolEntry;
use Carbon\Carbon;
use DB;
use Filament\Widgets\ChartWidget;
use Filament\Support\RawJs;
class GuardPatrolDayChart extends ChartWidget
{
protected static ?string $heading = 'GuardPatrolDayChart';
protected static ?string $maxHeight = '350px';
protected int|string|array $columnSpan = 12;
protected $listeners = ['patrolEntryChart'];
protected function getData(): array
{
$selectedPlant = session('selected_plant');
$activeFilter = $this->filter;
switch ($activeFilter)
{
case 'yesterday':
$startDate = now()->subDay()->startOfDay();
$endDate = now()->subDay()->endOfDay();
break;
case 'this_week':
$startDate = now()->startOfWeek();
$endDate = now()->endOfWeek();
break;
case 'this_month':
$startDate = now()->startOfMonth();
$endDate = now()->endOfMonth();
break;
default: // today
$startDate = now()->startOfDay();
$endDate = now()->endOfDay();
break;
}
$uniqueGuardNames = GuardPatrolEntry::join('guard_names', 'guard_patrol_entries.guard_name_id', '=', 'guard_names.id')
->where('guard_patrol_entries.plant_id', $selectedPlant)
->select('guard_names.id', 'guard_names.name')
->groupBy('guard_names.id', 'guard_names.name')
->orderBy('guard_names.name')
->pluck('name')
->toArray();
//dd($uniqueGuardNames);
$guardPatrols = GuardPatrolEntry::join('guard_names', 'guard_patrol_entries.guard_name_id', '=', 'guard_names.id')
->where('guard_patrol_entries.plant_id', $selectedPlant)
->whereBetween('guard_patrol_entries.patrol_time', [$startDate, $endDate])
->select('guard_names.id', 'guard_names.name', 'guard_patrol_entries.patrol_time')
->orderBy('guard_names.name')
->orderBy('guard_patrol_entries.patrol_time')
->get()
->groupBy('name');
$guardTimeSums = [];
foreach ($guardPatrols as $guardName => $patrols) {
$totalSeconds = 0;
$prevTime = null;
foreach ($patrols as $patrol)
{
$currentTime = Carbon::parse($patrol->patrol_time);
if ($prevTime)
{
$totalSeconds += abs($currentTime->diffInSeconds($prevTime));
}
$prevTime = $currentTime;
}
//$guardTimeSums[$guardName] = $totalSeconds / 60;
//$guardTimeSums[$guardName] = round($totalSeconds / 60);
$guardTimeSums[$guardName] = [
'minutes' => round($totalSeconds / 60),
'hours' => round($totalSeconds / 3600, 1),
];
}
$chartData = [];
foreach ($uniqueGuardNames as $guardName)
{
if ($activeFilter === 'today') {
$chartData[] = $guardTimeSums[$guardName]['minutes'] ?? 0;
}
elseif ($activeFilter === 'yesterday') {
$chartData[] = $guardTimeSums[$guardName]['minutes'] ?? 0;
}
else
{
$chartData[] = $guardTimeSums[$guardName]['hours'] ?? 0;
}
}
$chartData = array_map(function ($value) {
return ($value == 0 || is_null($value)) ? null : $value;
}, $chartData);
return [
'labels' => array_values($uniqueGuardNames),
'datasets' => [
[
'label' => match ($activeFilter)
{
'yesterday' => "Patrols by Guard (Yesterday) Minutes",
'this_week' => "Patrols by Guard (This Week) Hours",
'this_month' => "Patrols by Guard (This Month) Hours",
default => "Patrols by Guard (Today) Minutes",
},
'data' => $chartData,
],
],
];
}
protected function getType(): string
{
return 'bar';
}
protected function getFilters(): ?array
{
return [
'today' => 'Today',
'yesterday' => 'Yesterday',
'this_week'=> 'This Week',
'this_month'=> 'This Month',
];
}
protected function getOptions(): array
{
return [
'scales' => [
'y' => [
'beginAtZero' => true, //Start Y-axis from 0
'ticks' => [
'stepSize' => 5,
],
],
],
'plugins' => [
'datalabels' => [
'anchor' => 'start',
'align' => 'start',
'offset' => -15,
'color' => '#000',
'font' => [
'weight' => 'bold',
],
'formatter' => RawJs::make('function(value) {
return value;
}'),
],
],
];
}
}

View File

@@ -0,0 +1,202 @@
<?php
namespace App\Filament\Widgets;
use App\Models\CheckPointName;
use App\Models\CheckPointTime;
use App\Models\GuardPatrolEntry;
use Carbon\Carbon;
use DB;
use Filament\Widgets\ChartWidget;
use Illuminate\Support\Js;
class GuardPatrolHourlyChart extends ChartWidget
{
protected static ?string $heading = 'Chart';
protected static ?string $maxHeight = '330px';
protected int|string|array $columnSpan = 12;
protected function getData(): array
{
$selectedPlant = session('selected_plant');
$selectedDate = session('selected_date');
$selectedGuardName = session('selected_name');
$selectedTime = session('selected_time');
if (!$selectedPlant || !$selectedDate || !$selectedGuardName || empty($selectedTime)) {
return [];
}
$validSessions = session('valid_sessions', []);
$isValid = $validSessions[$selectedTime] ?? false;
if (!$isValid) {
return $this->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';
}
}

View File

@@ -0,0 +1,352 @@
<?php
namespace App\Filament\Widgets;
use App\Models\InvoiceValidation;
use App\Models\Line;
use App\Models\Plant;
use DB;
use Filament\Widgets\ChartWidget;
use Filament\Support\RawJs;
class InvoiceChart extends ChartWidget
{
protected static ?string $heading = 'Chart';
protected int|string|array $columnSpan = 12;
protected $listeners = ['invoiceChart'];
protected function getData(): array
{
$selectedPlant = session('selec_plant');
$selectedInvoice = session('select_invoice');
$activeFilter = $this->filter; // Assuming filter is passed and activeFilter can be 'yesterday', 'this_week', 'this_month'
if (!$selectedPlant || !$selectedInvoice) {
return [
'datasets' => [],
'labels' => [],
];
}
// Define the date range based on the active filter
if ($activeFilter == 'yesterday') {
$startDate = now()->subDay()->setTime(8, 0, 0);
$endDate = now()->setTime(8, 0, 0);
$groupBy = 'none'; // No grouping by hour
} elseif ($activeFilter == 'this_week') {
$startDate = now()->startOfWeek()->setTime(8, 0, 0);
$endDate = now()->endOfWeek()->addDay()->setTime(8, 0, 0);
$groupBy = 'day_of_week';
} elseif ($activeFilter == 'this_month') {
$startDate = now()->startOfMonth()->setTime(8, 0, 0);
$endDate = now()->endOfMonth()->setTime(8, 0, 0);
$groupBy = 'week_of_month';
} else {
$startDate = now()->setTime(8, 0, 0);
$endDate = now()->copy()->addDay()->setTime(8, 0, 0);
$groupBy = 'none'; // No grouping by hour
}
// Get the counts for Imported Invoices (unique invoice numbers) and Completed Invoices
if ($selectedInvoice == 'individual_material')
{
$importedInvoicesCount = InvoiceValidation::where('plant_id', $selectedPlant)
->where('quantity', 1)
->whereBetween('created_at', [$startDate, $endDate]) // Filter by date range
->distinct('invoice_number') // Only distinct invoice numbers
->count('invoice_number');
$completedInvoicesCount = InvoiceValidation::select('invoice_number')
->where('plant_id', $selectedPlant)
->where('quantity', 1)
->whereBetween('updated_at', [$startDate, $endDate])
->groupBy('invoice_number')
->havingRaw(
"COUNT(*) = SUM(CASE WHEN serial_number IS NOT NULL AND serial_number != '' THEN 1 ELSE 0 END)"
)
->count();
}
elseif ($selectedInvoice == 'serial_invoice')
{
$importedInvoicesCount = InvoiceValidation::where('plant_id', $selectedPlant)
->whereNull('quantity')
->whereBetween('created_at', [$startDate, $endDate])
->distinct('invoice_number')
->count('invoice_number');
$completedInvoicesCount = InvoiceValidation::select('invoice_number')
->where('plant_id', $selectedPlant)
->whereNull('quantity')
->whereBetween('updated_at', [$startDate, $endDate])
->groupBy('invoice_number')
->havingRaw(
"COUNT(*) = SUM(CASE WHEN scanned_status = 'Scanned' THEN 1 ELSE 0 END)"
)
->count();
}
elseif ($selectedInvoice == 'bundle_material')
{
$importedInvoicesCount = InvoiceValidation::where('plant_id', $selectedPlant)
->where('quantity', '>', 1)
->whereBetween('created_at', [$startDate, $endDate]) // Filter by date range
->distinct('invoice_number') // Only distinct invoice numbers
->count('invoice_number');
$completedInvoicesCount = InvoiceValidation::select('invoice_number')
->where('plant_id', $selectedPlant)
->where('quantity', '>', 1)
->whereBetween('updated_at', [$startDate, $endDate])
->groupBy('invoice_number')
->havingRaw(
"COUNT(*) = SUM(CASE WHEN serial_number IS NOT NULL AND serial_number != '' THEN 1 ELSE 0 END)"
)
->count();
}
// Prepare the chart data
$labels = []; // Labels for each bar
$datasets = []; // Datasets for the chart
if (in_array($activeFilter, ['yesterday'])) {
$labels = ['Imported Invoice', 'Completed Invoice'];
$datasets = [[
'label' => 'Invoices',
'data' => [$importedInvoicesCount, $completedInvoicesCount],
'backgroundColor' => ['rgba(75, 192, 192, 1)', 'rgba(23, 211, 80, 1)'],
'fill' => false,
]];
}
elseif ($activeFilter == 'this_week')
{
$daysOfWeek = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
$importedInvoicesPerDay = array_fill(0, 7, 0);
$completedInvoicesPerDay = array_fill(0, 7, 0);
for ($index = 0; $index < 7; $index++) {
$dayStart = now()->startOfWeek()->addDays($index)->setTime(8, 0, 0);
$dayEnd = $dayStart->copy()->addDay()->setTime(8, 0, 0);
$query = InvoiceValidation::where('plant_id', $selectedPlant);
// Build completedQuery with only 'invoice_number' in select
$completedQuery = InvoiceValidation::select('invoice_number')
->where('plant_id', $selectedPlant);
if ($selectedInvoice == 'individual_material')
{
$query->where('quantity', 1)->whereBetween('created_at', [$dayStart, $dayEnd]);
$completedQuery->where('quantity', 1)
->whereBetween('updated_at', [$dayStart, $dayEnd])
->groupBy('invoice_number')
->havingRaw(
"COUNT(*) = SUM(CASE WHEN serial_number IS NOT NULL AND serial_number != '' THEN 1 ELSE 0 END)"
);
}
elseif ($selectedInvoice == 'serial_invoice')
{
$query->whereNull('quantity')->whereBetween('created_at', [$dayStart, $dayEnd]);
$completedQuery->whereNull('quantity')
->whereBetween('updated_at', [$dayStart, $dayEnd])
->groupBy('invoice_number')
->havingRaw(
"COUNT(*) = SUM(CASE WHEN scanned_status = 'Scanned' THEN 1 ELSE 0 END)"
);
}
elseif ($selectedInvoice == 'bundle_material')
{
$query->where('quantity', '>', 1)->whereBetween('created_at', [$dayStart, $dayEnd]);
$completedQuery->where('quantity', '>', 1)
->whereBetween('updated_at', [$dayStart, $dayEnd])
->groupBy('invoice_number')
->havingRaw(
"COUNT(*) = SUM(CASE WHEN serial_number IS NOT NULL AND serial_number != '' THEN 1 ELSE 0 END)"
);
}
// Imported invoices count (distinct invoice_number)
$importedInvoicesPerDay[$index] = $query->distinct('invoice_number')->count('invoice_number');
$completedInvoicesPerDay[$index] = $completedQuery->count();
}
$labels = $daysOfWeek;
$datasets = [
[
'label' => 'Imported Invoices',
'data' => $importedInvoicesPerDay,
'backgroundColor' => 'rgba(75, 192, 192, 1)',
],
[
'label' => 'Completed Invoices',
'data' => $completedInvoicesPerDay,
'backgroundColor' => 'rgba(23, 211, 80, 1)',
],
];
}
elseif ($activeFilter == 'this_month') {
$startOfMonth = now()->startOfMonth()->setTime(8, 0, 0);
$endOfMonth = now()->endOfMonth()->addDay()->setTime(23, 59, 59); // include full last day
$monthName = $startOfMonth->format('M');
// Prepare weekly labels like "May(1-7)", "May(8-14)", etc.
$labels = [];
$weekStart = $startOfMonth->copy();
$importedInvoicesPerWeek = [];
$completedInvoicesPerWeek = [];
$weekIndex = 0;
while ($weekStart < $endOfMonth) {
$weekEnd = $weekStart->copy()->addDays(7);
$startDay = $weekStart->format('j');
$weekEndLimit = $weekEnd->copy()->subDay();
$actualEnd = $weekEndLimit->greaterThan($endOfMonth) ? $endOfMonth : $weekEndLimit;
$endDay = $actualEnd->format('j');
$labels[] = "{$monthName}({$startDay}-{$endDay})";
$queryImported = InvoiceValidation::where('plant_id', $selectedPlant)
->whereBetween('created_at', [$weekStart, $weekEnd]);
if ($selectedInvoice == 'individual_material')
{
$queryImported->where('quantity', 1);
}
elseif ($selectedInvoice == 'serial_invoice')
{
$queryImported->whereNull('quantity');
}
elseif ($selectedInvoice == 'bundle_material')
{
$queryImported->where('quantity', '>', 1);
}
$importedInvoicesPerWeek[$weekIndex] = $queryImported->distinct('invoice_number')->count('invoice_number');
// --- Completed ---
$queryCompleted = InvoiceValidation::select('invoice_number')
->where('plant_id', $selectedPlant)
->whereBetween('updated_at', [$weekStart, $weekEnd]);
if ($selectedInvoice == 'individual_material') {
$queryCompleted->where('quantity', 1)
->groupBy('invoice_number')
->havingRaw("COUNT(*) = SUM(CASE WHEN serial_number IS NOT NULL AND serial_number != '' THEN 1 ELSE 0 END)");
} elseif ($selectedInvoice == 'serial_invoice') {
$queryCompleted->whereNull('quantity')
->groupBy('invoice_number')
->havingRaw("COUNT(*) = SUM(CASE WHEN scanned_status = 'Scanned' THEN 1 ELSE 0 END)");
} elseif ($selectedInvoice == 'bundle_material') {
$queryCompleted->where('quantity', '>', 1)
->groupBy('invoice_number')
->havingRaw("COUNT(*) = SUM(CASE WHEN serial_number IS NOT NULL AND serial_number != '' THEN 1 ELSE 0 END)");
}
$completedInvoicesPerWeek[$weekIndex] = $queryCompleted->count();
// Move to next week
$weekStart = $weekEnd;
$weekIndex++;
}
$datasets = [
[
'label' => 'Imported Invoices',
'data' => $importedInvoicesPerWeek,
'backgroundColor' => 'rgba(75, 192, 192, 1)',
],
[
'label' => 'Completed Invoices',
'data' => $completedInvoicesPerWeek,
'backgroundColor' => 'rgba(23, 211, 80, 1)',
],
];
}
else
{
$labels = ['Imported Invoice', 'Completed Invoice'];
$datasets = [[
'label' => 'Invoices',
'data' => [$importedInvoicesCount, $completedInvoicesCount],
'backgroundColor' => ['rgba(75, 192, 192, 1)', 'rgba(23, 211, 80, 1)'],
'fill' => false,
]];
}
foreach ($datasets as &$dataset)
{
$dataset['data'] = array_map(function ($value) {
return ($value == 0 || is_null($value)) ? null : $value;
}, $dataset['data']);
}
return [
'datasets' => $datasets,
'labels' => $labels,
];
}
protected function getType(): string
{
return 'bar';
}
public static function getDefaultName(): string
{
return 'invoice-chart';
}
protected function getOptions(): array
{
return [
'plugins' => [
'datalabels' => [
'anchor' => 'start',
'align' => 'start',
'offset' => -15,
'color' => '#000',
'font' => [
'weight' => 'bold',
],
'formatter' => RawJs::make('function(value) {
return value;
}'),
],
],
'scales' => [
'y' => [
'beginAtZero' => true,
'ticks' => [
'stepSize' => 1,
],
],
],
];
}
protected function getFilters(): ?array
{
return [
'today' => 'Today',
'yesterday' => 'Yesterday',
'this_week'=> 'This Week',
'this_month'=> 'This Month',
];
}
public static function canView(): bool
{
// dd('Checking route:', request()->route()->getName());
// to avoid showing the widget in other pages
return request()->routeIs('filament.admin.pages.invoice-dashboard');
}
}

View File

@@ -0,0 +1,319 @@
<?php
namespace App\Filament\Widgets;
use App\Models\InvoiceDataValidation;
use App\Models\InvoiceOutValidation;
use App\Models\InvoiceValidation;
use Filament\Widgets\ChartWidget;
use Filament\Support\RawJs;
class InvoiceDataChart extends ChartWidget
{
protected static ?string $heading = 'Chart';
protected function getData(): array
{
$selectedPlant = session('selected_plant');
$selectedDistribution = session(key: 'dist_channel');
$activeFilter = $this->filter;
if (!$selectedPlant || !$selectedDistribution) {
return [
'datasets' => [],
'labels' => [],
];
}
if ($activeFilter == 'yesterday') {
$startDate = now()->subDay()->setTime(8, 0, 0);
$endDate = now()->setTime(8, 0, 0);
$groupBy = 'none'; // No grouping by hour
} elseif ($activeFilter == 'this_week') {
$startDate = now()->startOfWeek()->setTime(8, 0, 0);
$endDate = now()->endOfWeek()->addDay()->setTime(8, 0, 0);
$groupBy = 'day_of_week';
} elseif ($activeFilter == 'this_month') {
$startDate = now()->startOfMonth()->setTime(8, 0, 0);
$endDate = now()->endOfMonth()->setTime(8, 0, 0);
$groupBy = 'week_of_month';
} else {
$startDate = now()->setTime(8, 0, 0);
$endDate = now()->copy()->addDay()->setTime(8, 0, 0);
$groupBy = 'none';
}
if ($selectedDistribution == 'Direct Sale')
{
$totalInvoices = InvoiceDataValidation::where('plant_id', $selectedPlant)
->where('distribution_channel_desc', $selectedDistribution)
->whereBetween('document_date', [$startDate, $endDate])
->distinct('document_number')
->pluck('document_number')
->toArray();
if (empty($totalInvoices)) {
return [
'labels' => ['Total', 'Went Out', 'Pending'],
'datasets' => [
[
'label' => 'Invoice Count',
'data' => [0, 0, 0],
'backgroundColor' => ['#60A5FA', '#34D399', '#F87171'],
],
],
];
}
$wentOutInvoices = InvoiceOutValidation::where('plant_id', $selectedPlant)
->whereIn('qr_code', $totalInvoices)
->whereBetween('scanned_at', [$startDate, $endDate])
->distinct('qr_code')
->pluck('qr_code')
->toArray();
$totalCount = count($totalInvoices);
$wentOutCount = count($wentOutInvoices);
$pendingCount = $totalCount - $wentOutCount;
return [
'labels' => ['Total Invoices', 'Went Out', 'Pending'],
'datasets' => [
[
'label' => ucfirst(str_replace('_', ' ', $activeFilter)),
'data' => [$totalCount, $wentOutCount, $pendingCount],
'backgroundColor' => ['#60A5FA', '#34D399', '#F87171'], // blue, green, red
],
],
];
}
elseif ($selectedDistribution == 'Branch Sale')
{
$totalInvoices = InvoiceDataValidation::where('plant_id', $selectedPlant)
->where('distribution_channel_desc', $selectedDistribution)
->whereBetween('document_date', [$startDate, $endDate])
->distinct('document_number')
->pluck('document_number')
->toArray();
if (empty($totalInvoices)) {
return [
'labels' => ['Total', 'Went Out', 'Pending'],
'datasets' => [
[
'label' => 'Invoice Count',
'data' => [0, 0, 0],
'backgroundColor' => ['#60A5FA', '#34D399', '#F87171'],
],
],
];
}
$wentOutInvoices = InvoiceOutValidation::where('plant_id', $selectedPlant)
->whereIn('qr_code', $totalInvoices)
->whereBetween('scanned_at', [$startDate, $endDate])
->distinct('qr_code')
->pluck('qr_code')
->toArray();
$totalCount = count($totalInvoices);
$wentOutCount = count($wentOutInvoices);
$pendingCount = $totalCount - $wentOutCount;
return [
'labels' => ['Total Invoices', 'Went Out', 'Pending'],
'datasets' => [
[
'label' => ucfirst(str_replace('_', ' ', $activeFilter)),
'data' => [$totalCount, $wentOutCount, $pendingCount],
'backgroundColor' => ['#60A5FA', '#34D399', '#F87171'], // blue, green, red
],
],
];
}
elseif ($selectedDistribution == 'Internal Transfer')
{
$totalInvoices = InvoiceDataValidation::where('plant_id', $selectedPlant)
->where('distribution_channel_desc', $selectedDistribution)
->whereBetween('document_date', [$startDate, $endDate])
->distinct('document_number')
->pluck('document_number')
->toArray();
if (empty($totalInvoices)) {
return [
'labels' => ['Total', 'Went Out', 'Pending'],
'datasets' => [
[
'label' => 'Invoice Count',
'data' => [0, 0, 0],
'backgroundColor' => ['#60A5FA', '#34D399', '#F87171'],
],
],
];
}
$wentOutInvoices = InvoiceOutValidation::where('plant_id', $selectedPlant)
->whereIn('qr_code', $totalInvoices)
->whereBetween('scanned_at', [$startDate, $endDate])
->distinct('qr_code')
->pluck('qr_code')
->toArray();
$totalCount = count($totalInvoices);
$wentOutCount = count($wentOutInvoices);
$pendingCount = $totalCount - $wentOutCount;
return [
'labels' => ['Total Invoices', 'Went Out', 'Pending'],
'datasets' => [
[
'label' => ucfirst(str_replace('_', ' ', $activeFilter)),
'data' => [$totalCount, $wentOutCount, $pendingCount],
'backgroundColor' => ['#60A5FA', '#34D399', '#F87171'], // blue, green, red
],
],
];
}
elseif ($selectedDistribution == 'WOS')
{
$totalInvoices = InvoiceDataValidation::where('plant_id', $selectedPlant)
->where('distribution_channel_desc', $selectedDistribution)
->whereBetween('document_date', [$startDate, $endDate])
->distinct('document_number')
->pluck('document_number')
->toArray();
if (empty($totalInvoices)) {
return [
'labels' => ['Total', 'Went Out', 'Pending'],
'datasets' => [
[
'label' => 'Invoice Count',
'data' => [0, 0, 0],
'backgroundColor' => ['#60A5FA', '#34D399', '#F87171'],
],
],
];
}
$wentOutInvoices = InvoiceOutValidation::where('plant_id', $selectedPlant)
->whereIn('qr_code', $totalInvoices)
->whereBetween('scanned_at', [$startDate, $endDate])
->distinct('qr_code')
->pluck('qr_code')
->toArray();
$totalCount = count($totalInvoices);
$wentOutCount = count($wentOutInvoices);
$pendingCount = $totalCount - $wentOutCount;
return [
'labels' => ['Total Invoices', 'Went Out', 'Pending'],
'datasets' => [
[
'label' => ucfirst(str_replace('_', ' ', $activeFilter)),
'data' => [$totalCount, $wentOutCount, $pendingCount],
'backgroundColor' => ['#60A5FA', '#34D399', '#F87171'], // blue, green, red
],
],
];
}
// elseif ($selectedDistribution == 'Challan')
// {
// $totalInvoices = InvoiceDataValidation::where('plant_id', $selectedPlant)
// //->where('distribution_channel_desc', $selectedDistribution)
// ->where(function ($query) {
// $query->whereNull('distribution_channel_desc')
// ->orWhere('distribution_channel_desc', '');
// })
// ->whereBetween('document_date', [$startDate, $endDate])
// ->distinct('document_number')
// ->pluck('document_number')
// ->toArray();
// if (empty($totalInvoices)) {
// return [
// 'labels' => ['Total', 'Went Out', 'Pending'],
// 'datasets' => [
// [
// 'label' => 'Invoice Count',
// 'data' => [0, 0, 0],
// 'backgroundColor' => ['#60A5FA', '#34D399', '#F87171'],
// ],
// ],
// ];
// }
// $wentOutInvoices = InvoiceOutValidation::where('plant_id', $selectedPlant)
// ->whereIn('qr_code', $totalInvoices)
// ->whereBetween('scanned_at', [$startDate, $endDate])
// ->distinct('qr_code')
// ->pluck('qr_code')
// ->toArray();
// $totalCount = count($totalInvoices);
// $wentOutCount = count($wentOutInvoices);
// $pendingCount = $totalCount - $wentOutCount;
// return [
// 'labels' => ['Total Invoices', 'Went Out', 'Pending'],
// 'datasets' => [
// [
// 'label' => ucfirst(str_replace('_', ' ', $activeFilter)),
// 'data' => [$totalCount, $wentOutCount, $pendingCount],
// 'backgroundColor' => ['#60A5FA', '#34D399', '#F87171'], // blue, green, red
// ],
// ],
// ];
// }
}
protected function getFilters(): ?array
{
return [
'today' => 'Today',
'yesterday' => 'Yesterday',
'this_week'=> 'This Week',
'this_month'=> 'This Month',
];
}
protected function getType(): string
{
return 'bar';
}
protected function getOptions(): array
{
return [
// 'plugins' => [
// 'datalabels' => false,
// ],
'plugins' => [
'datalabels' => [
'anchor' => 'start', // Positions the label relative to the top of the bar
'align' => 'start', // Aligns the label above the bar
'offset' => -15, // Adjust if needed (positive moves up, negative moves down)
'color' => '#000',
'font' => [
'weight' => 'bold',
],
'formatter' => RawJs::make('function(value) {
return value;
}'),
],
],
'scales' => [
'y' => [
'beginAtZero' => true, //Start Y-axis from 0
'ticks' => [
'stepSize' => 0.5,
],
],
],
];
}
}

View File

@@ -0,0 +1,324 @@
<?php
namespace App\Filament\Widgets;
use App\Models\InvoiceValidation;
use Filament\Widgets\ChartWidget;
use Filament\Support\RawJs;
class InvoiceQuantity extends ChartWidget
{
protected static ?string $heading = 'Chart';
protected int|string|array $columnSpan = 12;
protected $listeners = ['invoiceChart'];
protected function getData(): array
{
$selectedPlant = session('selec_plant');
$selectedInvoice = session('select_invoice');
$activeFilter = $this->filter; // Assuming filter is passed and activeFilter can be 'yesterday', 'this_week', 'this_month'
if (!$selectedPlant || !$selectedInvoice) {
return [
'datasets' => [],
'labels' => [],
];
}
// Define the date range based on the active filter
if ($activeFilter == 'yesterday') {
$startDate = now()->subDay()->setTime(8, 0, 0);
$endDate = now()->setTime(8, 0, 0);
} elseif ($activeFilter == 'this_week') {
$startDate = now()->startOfWeek()->setTime(8, 0, 0);
$endDate = now()->endOfWeek()->addDay()->setTime(8, 0, 0);
} elseif ($activeFilter == 'this_month') {
$startDate = now()->startOfMonth()->setTime(8, 0, 0);
$endDate = now()->endOfMonth()->setTime(8, 0, 0);
} else {
$startDate = now()->setTime(8, 0, 0);
$endDate = now()->copy()->addDay()->setTime(8, 0, 0);
}
if ($selectedInvoice == 'individual_material')
{
$totalInvoicesCount = InvoiceValidation::where('plant_id', $selectedPlant)
->where('quantity', 1)
->whereBetween('created_at', [$startDate, $endDate])
->count();
$scannedInvoicesCount = InvoiceValidation::where('plant_id', $selectedPlant)
->where('quantity', 1)
->whereNotNull('serial_number')
->where('serial_number','!=', '')
->whereBetween('updated_at', [$startDate, $endDate])
->count();
}
elseif ($selectedInvoice == 'serial_invoice')
{
$totalInvoicesCount = InvoiceValidation::where('plant_id', $selectedPlant)
->where('quantity', null)
->whereBetween('created_at', [$startDate, $endDate])
->count();
$scannedInvoicesCount = InvoiceValidation::where('plant_id', $selectedPlant)
->where('scanned_status', 'Scanned')
->where(function($query) {
$query->whereNull('quantity')
->orWhere('quantity', 0);
})
->whereBetween('updated_at', [$startDate, $endDate])
->count();
}
elseif ($selectedInvoice == 'bundle_material')
{
$totalInvoicesCount = InvoiceValidation::where('plant_id', $selectedPlant)
->where('quantity', '>', 1)
->whereBetween('created_at', [$startDate, $endDate])
->count();
$scannedInvoicesCount = InvoiceValidation::where('plant_id', $selectedPlant)
->where('quantity', '>', 1)
->whereNotNull('serial_number')
->where('serial_number','!=', '')
->whereBetween('updated_at', [$startDate, $endDate])
->count();
}
// Prepare the chart data
$labels = []; // Labels for each bar
$datasets = []; // Datasets for the chart
if (in_array($activeFilter, ['yesterday'])) {
$labels = ['Total Invoices', 'Scanned Invoices'];
$datasets = [[
'label' => 'Invoices',
'data' => [$totalInvoicesCount, $scannedInvoicesCount],
'backgroundColor' => ['rgba(75, 192, 192, 1)', 'rgba(23, 211, 80, 1)'],
'fill' => false,
]];
}
elseif ($activeFilter == 'this_week')
{
$daysOfWeek = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
$TotalInvoicesPerDay = array_fill(0, 7, 0);
$scannedInvoicesPerDay = array_fill(0, 7, 0);
for ($index = 0; $index < 7; $index++) {
$dayStart = now()->startOfWeek()->addDays($index)->setTime(8, 0, 0);
$dayEnd = $dayStart->copy()->addDay()->setTime(8, 0, 0);
$query = InvoiceValidation::where('plant_id', $selectedPlant);
// Build completedQuery with only 'invoice_number' in select
$completedQuery = InvoiceValidation::select('invoice_number')
->where('plant_id', $selectedPlant);
if ($selectedInvoice == 'individual_material')
{
$query->where('quantity', 1)->whereBetween('created_at', [$dayStart, $dayEnd]);
$completedQuery->where('quantity', 1)
->whereBetween('updated_at', [$dayStart, $dayEnd])
->whereNotNull('serial_number')
->where('serial_number', '!=', '');
}
elseif ($selectedInvoice == 'serial_invoice')
{
$query->whereNull('quantity')->whereBetween('created_at', [$dayStart, $dayEnd]);
$completedQuery = InvoiceValidation::where('plant_id', $selectedPlant)
->where('scanned_status', 'Scanned')
->where(function($query) {
$query->whereNull('quantity')
->orWhere('quantity', 0);
})
->whereBetween('updated_at', [$dayStart, $dayEnd]);
}
elseif ($selectedInvoice == 'bundle_material')
{
$query->where('quantity', '>', 1)->whereBetween('created_at', [$dayStart, $dayEnd]);
$completedQuery->where('quantity', '>', 1)
->whereBetween('updated_at', [$dayStart, $dayEnd])
->whereNotNull('serial_number')
->where('serial_number', '!=', '');
}
// Imported invoices count (distinct invoice_number)
$TotalInvoicesPerDay[$index] = $query->count();
$scannedInvoicesPerDay[$index] = $completedQuery->count();
//dd($TotalInvoicesPerDay, $scannedInvoicesPerDay);
}
$labels = $daysOfWeek;
$datasets = [
[
'label' => 'Total Invoices',
'data' => $TotalInvoicesPerDay,
'backgroundColor' => 'rgba(75, 192, 192, 1)',
],
[
'label' => 'Scanned Invoices',
'data' => $scannedInvoicesPerDay,
'backgroundColor' => 'rgba(23, 211, 80, 1)',
],
];
}
elseif ($activeFilter == 'this_month')
{
$startOfMonth = now()->startOfMonth()->setTime(8, 0, 0);
$endOfMonth = now()->endOfMonth()->addDay()->setTime(8, 0, 0); // include full last day
$monthName = $startOfMonth->format('M');
// Prepare weekly labels like "May(1-7)", "May(8-14)", etc.
$labels = [];
$weekStart = $startOfMonth->copy();
$TotalInvoicesPerWeek = [];
$scannedInvoicesPerWeek = [];
$weekIndex = 0;
while ($weekStart < $endOfMonth) {
$weekEnd = $weekStart->copy()->addDays(7);
$startDay = $weekStart->format('j');
$weekEndLimit = $weekEnd->copy()->subDay();
$actualEnd = $weekEndLimit->greaterThan($endOfMonth) ? $endOfMonth : $weekEndLimit;
$endDay = $actualEnd->format('j');
$labels[] = "{$monthName}({$startDay}-{$endDay})";
$queryImported = InvoiceValidation::where('plant_id', $selectedPlant)
->whereBetween('created_at', [$weekStart, $weekEnd]);
if ($selectedInvoice == 'individual_material')
{
$queryImported->where('quantity', 1);
}
elseif ($selectedInvoice == 'serial_invoice')
{
$queryImported->whereNull('quantity');
}
elseif ($selectedInvoice == 'bundle_material')
{
$queryImported->where('quantity', '>', 1);
}
$TotalInvoicesPerWeek[$weekIndex] = $queryImported->count();
// --- Completed ---
$queryCompleted = InvoiceValidation::select('invoice_number')
->where('plant_id', $selectedPlant)
->whereBetween('updated_at', [$weekStart, $weekEnd]);
if ($selectedInvoice == 'individual_material') {
$queryCompleted->where('quantity', 1)
->whereNotNull('serial_number')
->where('serial_number', '!=', '');
} elseif ($selectedInvoice == 'serial_invoice') {
$queryCompleted->whereNull('quantity')
->where('scanned_status', 'Scanned')
->where(function($query) {
$query->whereNull('quantity')
->orWhere('quantity', 0);
});
} elseif ($selectedInvoice == 'bundle_material') {
$queryCompleted->where('quantity', '>', 1)
->whereNotNull('serial_number')
->where('serial_number', '!=', '');
}
$scannedInvoicesPerWeek[$weekIndex] = $queryCompleted->count();
// Move to next week
$weekStart = $weekEnd;
$weekIndex++;
}
$datasets = [
[
'label' => 'Total Invoices',
'data' => $TotalInvoicesPerWeek,
'backgroundColor' => 'rgba(75, 192, 192, 1)',
],
[
'label' => 'Scanned Invoices',
'data' => $scannedInvoicesPerWeek,
'backgroundColor' => 'rgba(23, 211, 80, 1)',
],
];
}
else
{
$labels = ['Total Invoices', 'Scanned Invoices'];
$datasets = [[
'label' => 'Invoices',
'data' => [$totalInvoicesCount, $scannedInvoicesCount],
'backgroundColor' => ['rgba(75, 192, 192, 1)', 'rgba(23, 211, 80, 1)'],
'fill' => false,
]];
}
foreach ($datasets as &$dataset)
{
$dataset['data'] = array_map(function ($value) {
return ($value == 0 || is_null($value)) ? null : $value;
}, $dataset['data']);
}
return [
'datasets' => $datasets,
'labels' => $labels,
];
}
protected function getType(): string
{
return 'bar';
}
protected function getOptions(): array
{
return [
'plugins' => [
'datalabels' => [
'anchor' => 'start',
'align' => 'start',
'offset' => -15,
'color' => '#000',
'font' => [
'weight' => 'bold',
],
'formatter' => RawJs::make('function(value) {
return value;
}'),
],
],
'scales' => [
'y' => [
'beginAtZero' => true,
'ticks' => [
'stepSize' => 1,
],
],
],
];
}
protected function getFilters(): ?array
{
return [
'today' => 'Today',
'yesterday' => 'Yesterday',
'this_week'=> 'This Week',
'this_month'=> 'This Month',
];
}
}

View File

@@ -0,0 +1,299 @@
<?php
namespace App\Filament\Widgets;
use Filament\Notifications\Notification;
use Filament\Support\Assets\Js;
use Filament\Support\RawJs;
use Filament\Widgets\ChartWidget;
use Illuminate\Contracts\View\View;
use Illuminate\Support\HtmlString;
class ItemOverview extends ChartWidget
{
protected static ?string $heading = 'Hourly Production';
public $totalCount = 0;
protected int|string|array $columnSpan = '12';
protected static ?string $maxHeight = '400px';
protected $listeners = ['filtersUpdated' => '$refresh'];
protected static string $color = 'info';
protected static ?string $icon = 'heroicon-o-chart-bar';
protected static ?string $iconColor = 'info';
protected static ?string $iconBackgroundColor = 'info';
protected static ?string $label = 'Total Production';
protected static ?string $badgeColor = 'success';
protected static ?string $badgeIcon = 'heroicon-o-check-circle';
protected static ?string $badgeIconPosition = 'after';
protected static ?string $badgeSize = 'xs';
public function getData(): array
{
$activeFilter = $this->filter;
$selectedPlant = session('selected_plant') ?? session('select_plant');
$selectedLine = session('selected_line') ?? session('select_line');
$selectedStatus = session('selected_status');
if (!$selectedPlant || !$selectedLine)
{
return [
'datasets' => [],
'labels' => [],
];
}
$this->dispatch('filtersUpdated', [
'plant' => $selectedPlant,
'line' => $selectedLine,
'filter' => $activeFilter,
]);
// Determine if line is FG Line
$line = \App\Models\Line::find($selectedLine);
$isFgLine = $line?->type == 'FG Line';
// Set date range and groupBy logic
if ($activeFilter == 'yesterday')
{
$startDate = now()->subDay()->setTime(8, 0, 0);
$endDate = now()->setTime(8, 0, 0);
$groupBy = 'EXTRACT(HOUR FROM created_at)';
}
elseif ($activeFilter == 'this_week')
{
$startDate = now()->startOfWeek()->setTime(8, 0, 0);
$endDate = now()->endOfWeek()->addDay()->setTime(8, 0, 0);
$groupBy = 'EXTRACT(DOW FROM created_at)';
}
elseif ($activeFilter == 'this_month')
{
$startDate = now()->startOfMonth();
$endDate = now()->endOfMonth();
$groupBy = "FLOOR((EXTRACT(DAY FROM created_at) - 1) / 7) + 1";
}
else
{
$startDate = now()->setTime(8, 0, 0);
$endDate = now()->copy()->addDay()->setTime(8, 0, 0);
$groupBy = 'EXTRACT(HOUR FROM created_at)';
}
$baseTable = $isFgLine ? 'quality_validations' : 'production_quantities';
// $query = \DB::table($baseTable)
// ->selectRaw("$groupBy AS time_unit, COUNT(*) AS total_quantity")
// ->where('created_at', '>=', $startDate)
// ->where('created_at', '<', $endDate)
// ->where('plant_id', $selectedPlant)
// ->where('line_id', $selectedLine)
// ->groupByRaw($groupBy)
// ->orderByRaw($groupBy)
// ->pluck('total_quantity', 'time_unit')
// ->toArray();
$query = \DB::table($baseTable)
->selectRaw("$groupBy AS time_unit, COUNT(*) AS total_quantity")
->where('created_at', '>=', $startDate)
->where('created_at', '<', $endDate)
->where('plant_id', $selectedPlant)
->where('line_id', $selectedLine);
if ($selectedStatus && !$isFgLine) {
// Only filter success_status for production_quantities (Non-FG)
$query->where('success_status', $selectedStatus);
}
$query = $query->groupByRaw($groupBy)
->orderByRaw($groupBy)
->pluck('total_quantity', 'time_unit')
->toArray();
if ($activeFilter == 'this_month')
{
$weeksCount = ceil($endDate->day / 7);
$allWeeks = array_fill(1, $weeksCount, 0);
$data = array_replace($allWeeks, $query);
$labels = [];
for ($i = 1; $i <= $weeksCount; $i++) {
$weekStart = $startDate->copy()->addDays(($i - 1) * 7)->format('d M');
$weekEnd = $startDate->copy()->addDays($i * 7 - 1)->min($endDate)->format('d M');
$labels[] = "Week $i ($weekStart - $weekEnd)";
}
$orderedData = array_values($data);
}
elseif ($activeFilter == 'this_week')
{
$labels = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
$data = array_fill(0, 7, 0);
foreach ($query as $dow => $count) {
$data[$dow] = $count;
}
$orderedData = [
$data[1] ?? 0, $data[2] ?? 0, $data[3] ?? 0,
$data[4] ?? 0, $data[5] ?? 0, $data[6] ?? 0,
$data[0] ?? 0,
];
}
// else
// {
// $allHours = array_fill(0, 24, 0);
// $data = array_replace($allHours, $query);
// $shiftedKeys = array_merge(range(8, 23), range(0, 7));
// $orderedData = array_map(fn($hour) => $data[$hour], $shiftedKeys);
// $labels = array_map(fn ($hour) => date("g A", strtotime("$hour:00")), $shiftedKeys);
// }
else
{
$allHours = array_fill(0, 24, 0);
$data = array_replace($allHours, $query);
$shiftedData = [];
foreach ($data as $hour => $count) {
$nextHour = ($hour + 1) % 24;
$shiftedData[$nextHour] = $count;
}
$shiftedKeys = array_merge(range(9, 23), range(0, 8));
$orderedData = array_map(fn($hour) => $shiftedData[$hour] ?? 0, $shiftedKeys);
$labels = array_map(function($hour) {
$prevHour = ($hour - 1 + 24) % 24;
return sprintf("%s",
date("g A", strtotime("$hour:00")),
date("g", strtotime("$prevHour:00")),
date("g A", strtotime("$hour:00"))
);
}, $shiftedKeys);
}
$this->totalCount = array_sum($orderedData);
return [
'datasets' => [
[
'label' => match ($activeFilter) {
'this_week' => $isFgLine ? 'Daily FG Count This Week' : 'Daily Production This Week',
'this_month' => $isFgLine ? 'Weekly FG Count This Month' : 'Weekly Production This Month',
'yesterday' => $isFgLine ? "Yesterday's FG Count" : "Yesterday's Hourly Production",
default => $isFgLine ? "Today's FG Count" : "Today's Hourly Production",
},
'data' => $orderedData,
// 'interaction' => [
// 'mode' => 'nearest',
// 'axis' => 'x',
// 'intersect' => false,
// ],
'borderColor' => 'rgba(75, 192, 192, 1)',
'backgroundColor' => 'rgba(75, 192, 192, 0.2)',
'fill' => false,
'tension' => 0.3,
],
],
'labels' => $labels,
];
}
protected function getType(): string
{
return 'line';
}
protected function getOptions(): array
{
return [
'plugins' => [
'datalabels' => [
'anchor' => 'end', // Change to 'end' to align to the right edge of the bar
'align' => 'right', // Align text to the right (optional, for label text alignment)
'offset' => 3, // Move label to the right by ~11px (≈3mm)
'color' => '#000',
'font' => [
'weight' => 'bold',
],
'formatter' => 'function(value) { return Number(value); }',
],
],
'scales' => [
'y' => [
'beginAtZero' => true,
'ticks' => [
'stepSize' => 0.5,
],
],
],
];
}
public function getHeading(): HtmlString|string|null
{
// Detect selected line & type
$selectedLine = session('selected_line') ?? session('select_line');
$line = \App\Models\Line::find($selectedLine);
$isFgLine = $line?->type == 'FG Line';
// Dynamic title and icon
$title = $isFgLine ? 'FG Line Production' : 'Total Production';
$titleColor = $isFgLine ? '#16a34a' : '#1e40af';
//$iconColor = $isFgLine ? 'text-green-500' : 'text-blue-500';
$iconColor = $isFgLine ? 'text-green-500' : 'text-blue-500';
//$textColor = $isFgLine ? '#16a34a' : '#3b82f6';
$textColor = $isFgLine ? '#0f766e' : '#b45309';
$iconSvg = '
<svg xmlns="http://www.w3.org/2000/svg"
class="w-8 h-8 ' . $iconColor . '" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M3 3v18h18M7 13h2v5H7zm4-8h2v13h-2zm4 5h2v8h-2z" />
</svg>
';
$count = number_format($this->totalCount ?? 0);
return new HtmlString('
<div class="flex items-center justify-between px-4 py-2 bg-white rounded-lg shadow-sm">
<div class="flex items-center space-x-3">
' . $iconSvg . '
<div style="margin-left: 12px; color: ' . $textColor . ';">
<p style="color:' . $titleColor . '; font-weight: 600;">' . e($title) . '</p>
<h2 class="text-3xl font-extrabold text-gray-800">' . $count . '</h2>
</div>
</div>
</div>
');
}
protected function getFilters(): ?array
{
return [
'today' => 'Today',
'yesterday' => 'Yesterday',
'this_week'=> 'This Week',
'this_month'=> 'This Month',
];
}
public static function canView(): bool
{
return request()->routeIs([
'filament.pages.hourly-production',
'filament.admin.resources.production-quantities.create',
]);
}
}

View File

@@ -0,0 +1,189 @@
<?php
namespace App\Filament\Widgets;
use Filament\Widgets\ChartWidget;
class ProductionLineStopChart extends ChartWidget
{
protected static ?string $heading = 'Production Line Stop Chart';
protected static ?string $maxHeight = '250px';
protected function getData(): array
{
$activeFilter = $this->filter;
$selectedPlant = session('selected_plant');
$selectedLine = session('selected_line');
if (!$selectedPlant || !$selectedLine)
{
return [
'datasets' => [],
'labels' => [],
];
}
// Set time range based on active filter
if ($activeFilter === 'yesterday') {
$startDate = now()->subDay()->setTime(8, 0, 0);
$endDate = now()->setTime(8, 0, 0);
} elseif ($activeFilter === 'this_week') {
$startDate = now()->startOfWeek()->setTime(8, 0, 0);
$endDate = now()->endOfWeek()->addDay()->setTime(8, 0, 0);
} elseif ($activeFilter === 'this_month') {
$startDate = now()->startOfMonth();
$endDate = now()->endOfMonth();
} else {
$startDate = now()->setTime(8, 0, 0);
$endDate = now()->copy()->addDay()->setTime(8, 0, 0);
}
// Fetch stop reason data
// treats minutes as a visual decimal (e.g., 15 mins = 0.15)
$query = \DB::table('production_line_stops')
->join('line_stops as ls', 'production_line_stops.linestop_id', '=', 'ls.id')
->join('lines', 'production_line_stops.line_id', '=', 'lines.id')
->join('plants', 'production_line_stops.plant_id', '=', 'plants.id')
->select(
'ls.code',
'ls.reason',
\DB::raw("
SUM(production_line_stops.stop_hour) as total_stop_hours,
SUM(production_line_stops.stop_min) as total_stop_minutes
")
)
->when($selectedPlant, fn($q) => $q->where('plants.id', $selectedPlant))
->when($selectedLine, fn($q) => $q->where('lines.id', $selectedLine))
->whereBetween('production_line_stops.created_at', [$startDate, $endDate])
->groupBy('ls.code', 'ls.reason')
//->orderByDesc('total_hours')
->get();
// Handle empty data gracefully
if ($query->isEmpty()) {
return [
'datasets' => [[
'label' => 'Line Stop Reasons',
'data' => [1],
'backgroundColor' => ['rgba(200, 200, 200, 0.4)'],
'borderColor' => ['rgba(200, 200, 200, 1)'],
'borderWidth' => 1,
]],
'labels' => ['No Data'],
];
}
$labels = [];
$data = [];
$backgroundColors = [];
$borderColors = [];
function generateColor(string $key, float $opacity): string {
// Use a stable hash of the key, no randomness
$hash = md5($key);
// Get the RGB components from the hash
$r = hexdec(substr($hash, 0, 2)); // Red component
$g = hexdec(substr($hash, 2, 2)); // Green component
$b = hexdec(substr($hash, 4, 2)); // Blue component
// Avoid deep green colors for the `code`
if ($g > 150 && $g < 255) {
// Adjust green component if it's in the deep green range
$g = 150; // Assign a fixed, safe value for green
}
// Return the color as rgba with the specified opacity
return "rgba($r, $g, $b, $opacity)";
}
$totalStopMinutes = 0;
foreach ($query as $row) {
$code = $row->code;
$reason = $row->reason;
$stopHours = $row->total_stop_hours;
$stopMinutes = $row->total_stop_minutes;
$visualTotal = $stopHours + ($stopMinutes / 100);
$codeLabel = "$code - $reason";
$labels[] = $codeLabel;
$data[] = $visualTotal;
$backgroundColors[] = generateColor($code, 0.7); // Unique color for each stop code
$borderColors[] = generateColor($code, 1.0);
// Accumulate total stop time (in minutes)
$totalStopMinutes += ($stopHours * 60) + $stopMinutes;
}
// Calculate remaining time (1440 minutes = 24 hours)
$remainingMinutes = 1440 - $totalStopMinutes;
$runtimeHours = floor($remainingMinutes / 60);
$runtimeMinutes = $remainingMinutes % 60;
$runtimeVisual = $runtimeHours + ($runtimeMinutes / 100);
// Add runtime slice with green color (either light or dark green)
$labels[] = 'Available Runtime';
$data[] = $runtimeVisual;
$backgroundColors[] = 'rgba(47, 218, 47, 0.94)'; // Green for runtime
$borderColors[] = 'rgba(75, 192, 75, 1)';
return [
'datasets' => [[
'label' => 'Line Stop Durations (hrs)',
'data' => $data,
'backgroundColor' => $backgroundColors,
'borderColor' => $borderColors,
'borderWidth' => 1,
]],
'labels' => $labels,
];
}
protected function getType(): string
{
return 'pie';
}
protected function getFilters(): ?array
{
return [
'today' => 'Today',
'yesterday' => 'Yesterday',
];
}
protected function getOptions(): array
{
return [
// 'responsive' => true,
// 'plugins' => [
// 'legend' => [
// 'position' => 'bottom',
// ],
// 'tooltip' => [
// 'enabled' => true, // Tooltips enabled on hover
// ],
// ],
'plugins' => [
'datalabels' => false,
],
'scales' => [
'x' => [
'display' => false, // Disable x-axis
],
'y' => [
'display' => false, // Disable y-axis
],
],
];
}
public static function canView(): bool
{
// Only show on HourlyProduction page
return request()->routeIs('filament.pages.production-line-stop-count');
}
}

View File

@@ -0,0 +1,263 @@
<?php
namespace App\Filament\Widgets;
use App\Models\Line;
use Filament\Widgets\ChartWidget;
use Illuminate\Support\Js;
use Filament\Support\RawJs;
use Illuminate\Support\Stringable;
class ProductionOrderChart extends ChartWidget
{
protected static ?string $heading = 'ProductionOrderCount';
protected int|string|array $columnSpan = 12;
protected static ?string $maxHeight = '330px';
protected $listeners = ['productionOrderChart'];
protected function getData(): array
{
$activeFilter = $this->filter;
$selectedPlant = session('selected_plant');
$selectedLine = session('selected_line');
$selectedStatus = session('selected_status');
$productionOrder = session('production_order');
if (!$selectedPlant || !$selectedLine || !$productionOrder || !$selectedStatus) {
return [
'datasets' => [],
'labels' => [],
];
}
// 1. Check line type
$lineType = Line::where('id', $selectedLine)->value('type');
// 2. Set base table and columns depending on line type
if ($lineType == 'FG Line')
{
$table = 'quality_validations';
$dateColumn = 'created_at';
$plantColumn = 'plant_id';
$lineColumn = 'line_id';
$orderColumn = 'production_order';
}
else
{
$table = 'production_quantities';
$dateColumn = 'created_at';
$plantColumn = 'plant_id';
$lineColumn = 'line_id';
$orderColumn = 'production_order';
}
// 3. Set filter logic as before
if ($activeFilter == 'yesterday')
{
$startDate = now()->subDay()->setTime(8, 0, 0);
$endDate = now()->setTime(8, 0, 0);
$groupBy = "EXTRACT(HOUR FROM $table.$dateColumn)";
}
else if ($activeFilter == 'this_week')
{
$startDate = now()->startOfWeek()->setTime(8, 0, 0);
$endDate = now()->endOfWeek()->addDay()->setTime(8, 0, 0);
$groupBy = "EXTRACT(DOW FROM $table.$dateColumn)";
}
else if ($activeFilter == 'this_month')
{
$startDate = now()->startOfMonth();
$endDate = now()->endOfMonth();
$groupBy = "FLOOR((EXTRACT(DAY FROM $table.$dateColumn) - 1) / 7) + 1";
}
else
{
$startDate = now()->setTime(8, 0, 0);
$endDate = now()->copy()->addDay()->setTime(8, 0, 0);
$groupBy = "EXTRACT(HOUR FROM $table.$dateColumn)";
}
// // 4. Build the query dynamically
// $query = \DB::table($table)
// ->join('plants', "$table.$plantColumn", '=', 'plants.id')
// ->join('lines', "$table.$lineColumn", '=', 'lines.id')
// ->selectRaw("$groupBy AS time_unit, count(*) AS total_quantity")
// ->whereBetween("$table.$dateColumn", [$startDate, $endDate])
// ->where('plants.id', $selectedPlant)
// ->where('lines.id', $selectedLine)
// ->when($productionOrder, fn($q) => $q->where("$table.$orderColumn", $productionOrder))
// ->groupByRaw($groupBy)
// ->orderByRaw($groupBy)
// ->pluck('total_quantity', 'time_unit')
// ->toArray();
$query = \DB::table($table)
->selectRaw("$groupBy AS time_unit, COUNT(*) AS total_quantity")
->whereBetween("$table.$dateColumn", [$startDate, $endDate])
->where("$table.$plantColumn", $selectedPlant)
->where("$table.$lineColumn", $selectedLine)
->where("$table.$orderColumn", $productionOrder);
// Apply status filter only for Non-FG lines
if ($selectedStatus && $lineType != 'FG Line') {
$query->where('success_status', $selectedStatus);
}
$query = $query
->groupByRaw($groupBy)
->orderByRaw($groupBy)
->pluck('total_quantity', 'time_unit')
->toArray();
if ($activeFilter == 'this_month')
{
$weeksCount = ceil($endDate->day / 7);
$allWeeks = array_fill(1, $weeksCount, 0);
$data = array_replace($allWeeks, $query);
$labels = [];
for ($i = 1; $i <= $weeksCount; $i++) {
$weekStart = $startDate->copy()->addDays(($i - 1) * 7)->format('d M');
$weekEnd = $startDate->copy()->addDays($i * 7 - 1)->min($endDate)->format('d M');
$labels[] = "Week $i ($weekStart - $weekEnd)";
}
$orderedData = array_values($data);
}
else if ($activeFilter == 'this_week')
{
$labels = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
$data = array_fill(0, 7, 0);
foreach ($query as $dow => $count) {
$data[$dow] = $count;
}
$orderedData = [
$data[1] ?? 0, // Monday
$data[2] ?? 0, // Tuesday
$data[3] ?? 0, // Wednesday
$data[4] ?? 0, // Thursday
$data[5] ?? 0, // Friday
$data[6] ?? 0, // Saturday
$data[0] ?? 0, // Sunday
];
}
// else
// {
// $allHours = array_fill(0, 24, 0);
// $data = array_replace($allHours, $query);
// $shiftedKeys = array_merge(range(9, 23), range(0, 8));
// $orderedData = array_map(fn($hour) => $data[$hour], $shiftedKeys);
// $labels = array_map(fn ($hour) => date("g A", strtotime("$hour:00")), $shiftedKeys);
// }
else
{
$allHours = array_fill(0, 24, 0);
$data = array_replace($allHours, $query);
// Shift counts - move each hour's count to the next hour
$shiftedData = [];
foreach ($data as $hour => $count) {
$nextHour = ($hour + 1) % 24;
$shiftedData[$nextHour] = $count;
}
// Order from 9AM to 8AM next day
$shiftedKeys = array_merge(range(9, 23), range(0, 8));
$orderedData = array_map(fn($hour) => $shiftedData[$hour] ?? 0, $shiftedKeys);
// Create labels
$labels = array_map(function($hour) {
return date("g A", strtotime("$hour:00"));
}, $shiftedKeys);
}
$orderedData = array_map(function ($value)
{
return ($value == 0 || is_null($value)) ? null : $value;
}, $orderedData);
return [
'datasets' => [
[
'label' => match ($activeFilter)
{
'this_week' => $lineType == 'FG Line'
? "Daily Fg Production Order Count This Week"
: "Daily Production Order Count This Week",
'this_month' => $lineType == 'FG Line'
? "Weekly Fg Production Order Count This Month"
: "Weekly Production Order Count This Month",
'yesterday' => $lineType == 'FG Line'
? "Yesterday's Hourly Fg Order Count Production"
: "Yesterday's Hourly Order Count Production",
default => $lineType == 'FG Line'
? "Today's Hourly Fg Production Order Count"
: "Today's Hourly Production Order Count",
},
'data' => $orderedData,
'borderColor' => 'rgba(75, 192, 192, 1)',
'backgroundColor' => 'rgba(75, 192, 192, 0.2)',
'fill' => false,
'tension' => 0.3,
],
],
'labels' => $labels,
];
}
protected function getType(): string
{
return 'bar';
}
protected function getOptions(): array
{
return [
'plugins' => [
'datalabels' => [
'anchor' => 'start',
'align' => 'start',
'offset' => -15,
'color' => '#000',
'font' => [
'weight' => 'bold',
],
'formatter' => Js::from("function(value) { return Number(value); }"),
],
],
'scales' => [
'y' => [
'beginAtZero' => true,
'ticks' => [
'stepSize' => 0.5,
],
],
],
];
}
protected function getFilters(): ?array
{
return [
'today' => 'Today',
'yesterday' => 'Yesterday',
'this_week'=> 'This Week',
'this_month'=> 'This Month',
];
}
public static function canView(): bool
{
// Only show on HourlyProduction page
return request()->routeIs('filament.pages.production-order-count');
}
}

View File

@@ -0,0 +1,92 @@
<?php
namespace App\Filament\Widgets;
use App\Models\Line;
use App\Models\ProductionQuantity;
use App\Models\QualityValidation;
use Filament\Widgets\StatsOverviewWidget as BaseWidget;
use Filament\Widgets\StatsOverviewWidget\Stat;
class ProductionQuantityStat extends BaseWidget
{
protected $listeners = ['filtersUpdated' => 'updateStats'];
public $selectedPlant;
public $selectedLine;
public $selectedFilter = 'Today';
public function updateStats($data=null)
{
if (!$data) {
return;
}
$this->selectedPlant = $data['plant'] ?? null;
$this->selectedLine = $data['line'] ?? null;
//$this->selectedFilter = $data['filter'] ?? 'today';
$this->selectedFilter = $data['filter'] ?? 'today';
$this->dispatch('$refresh');
}
protected function getStats(): array
{
if (!$this->selectedPlant || !$this->selectedLine) {
return [
Stat::make('Production Count', 0)
->description('Select Plant & Line')
->color('success'),
];
}
$line = Line::find($this->selectedLine);
if (!$line) {
return [
Stat::make('Production Count', 0)
->description('Line not found')
->color('success'),
];
}
$start = now()->setTime(8, 0, 0);
$end = now()->copy()->addDay()->setTime(8, 0, 0);
if ($this->selectedFilter == 'yesterday') {
$start = now()->subDay()->setTime(8, 0, 0);
$end = now()->setTime(8, 0, 0);
} elseif ($this->selectedFilter == 'this_week') {
$start = now()->startOfWeek()->setTime(8, 0, 0);
$end = now()->endOfWeek()->addDay()->setTime(8, 0, 0);
} elseif ($this->selectedFilter == 'this_month') {
$start = now()->startOfMonth()->setTime(8, 0, 0);
$end = now()->endOfMonth()->addDay()->setTime(8, 0, 0);
}
if ($line->type === 'FG Line') {
$count = QualityValidation::where('line_id', $line->id)
->whereBetween('created_at', [$start, $end])
->count();
return [
Stat::make('FG Production Count', $count)
->description("FG production for this line")
->color('success')
->icon('heroicon-s-check-circle'),
];
} else {
$count = ProductionQuantity::where('line_id', $line->id)
->whereBetween('created_at', [$start, $end])
->count();
return [
Stat::make('Total Production Count', $count)
->description("Total production for this line")
->color('primary')
->icon('heroicon-s-cube'),
];
}
}
}

View File

@@ -0,0 +1,553 @@
<?php
namespace App\Filament\Widgets;
use App\Models\MfmReading;
use Carbon\Carbon;
use Filament\Widgets\ChartWidget;
use Filament\Support\RawJs;
class TrendChartAnalysis extends ChartWidget
{
protected static ?string $heading = 'Trend Chart Analysis';
protected static ?string $maxHeight = '420px';
protected int|string|array $columnSpan = 12;
// Add a property to receive filters from parent or externally
protected function getData(): array
{
$fromDatetime = session('from_datetime');
$toDatetime = session('to_datetime');
$selectedPlant = session('selected_plant');
$meterId = session('selected_meter');
$parameter = session('parameter');
if (empty($fromDatetime) || empty($toDatetime) || empty($selectedPlant) || empty($meterId) || empty($parameter)) {
return [
'labels' => [],
'datasets' => [],
];
}
$fromDateTime = Carbon::parse($fromDatetime);
$toDateTime = Carbon::parse($toDatetime);
if ($fromDateTime->gt($toDateTime) || $fromDateTime->gt(now()) || $toDateTime->gt(now())) {
return [
'labels' => [],
'datasets' => [],
];
}
$durationHours = $fromDateTime->diffInHours($toDateTime);
//dd($durationHours);
if ($durationHours < 1 || $durationHours > 24) {
return [
'labels' => [],
'datasets' => [],
];
}
$intervalCount = $durationHours > 12 ? 12 : 10;
$intervalMinutes = $durationHours > 12 ? 120 : floor(($durationHours * 60) / $intervalCount);
$labels = [];
if ($parameter == 'Phase Voltage') {
// Helper function to get min, max, avg for a column with PostgreSQL casting
$dataAggregation = function (string $column) use ($fromDateTime, $toDateTime, $meterId) {
$min = MfmReading::whereBetween('created_at', [$fromDateTime, $toDateTime])
->where('mfm_meter_id', $meterId)
->min(\Illuminate\Support\Facades\DB::raw("{$column}::double precision"));
$max = MfmReading::whereBetween('created_at', [$fromDateTime, $toDateTime])
->where('mfm_meter_id', $meterId)
->max(\Illuminate\Support\Facades\DB::raw("{$column}::double precision"));
$avg = MfmReading::whereBetween('created_at', [$fromDateTime, $toDateTime])
->where('mfm_meter_id', $meterId)
->avg(\Illuminate\Support\Facades\DB::raw("{$column}::double precision"));
return [
'min' => round($min ?? 0, 2),
'max' => round($max ?? 0, 2),
'avg' => round($avg ?? 0, 2),
];
};
// Get aggregated data for all relevant columns
$voltageRYSummary = $dataAggregation('voltage_ry');
$voltageYBSummary = $dataAggregation('voltage_yb');
$voltageBRSummary = $dataAggregation('voltage_br');
$frequencySummary = $dataAggregation('frequency');
// Labels for each bar on X-axis (min, max, avg per column)
$labels = [
'Voltage RY Min', 'Voltage RY Max', 'Voltage RY Avg',
'Voltage YB Min', 'Voltage YB Max', 'Voltage YB Avg',
'Voltage BR Min', 'Voltage BR Max', 'Voltage BR Avg',
'Frequency Min', 'Frequency Max', 'Frequency Avg',
];
// Ordered data values matching labels above
$dataValues = [
$voltageRYSummary['min'], $voltageRYSummary['max'], $voltageRYSummary['avg'],
$voltageYBSummary['min'], $voltageYBSummary['max'], $voltageYBSummary['avg'],
$voltageBRSummary['min'], $voltageBRSummary['max'], $voltageBRSummary['avg'],
$frequencySummary['min'], $frequencySummary['max'], $frequencySummary['avg'],
];
// Colors by aggregation type: Min - yellow, Max - green, Avg - blue
$aggregationColors = [
'rgba(204, 163, 0, 0.8)', // Darker yellow (Min)
'rgba(30, 120, 120, 0.8)', // Darker teal/green (Max)
'rgba(30, 90, 140, 0.8)', // Darker blue (Avg)
];
// Each set of 3 bars per column repeats the colors: min, max, avg
$backgroundColorArray = [];
foreach (range(1, 4) as $group) { // 4 column groups
foreach ($aggregationColors as $color) {
$backgroundColorArray[] = $color;
}
}
// Construct chart data structure
return [
'labels' => $labels,
'datasets' => [
[
'label' => 'Phase Voltage & Frequency Stats',
// Bar values array: 12 bars total (4 groups × 3 stats each)
'data' => $dataValues,
'backgroundColor' => $backgroundColorArray,
'borderColor' => array_map(fn ($clr) => str_replace('0.6', '1', $clr), $backgroundColorArray),
'borderWidth' => 1,
],
],
];
}
else if ($parameter == 'Line Voltage') {
// Helper function to get min, max, avg for a column with PostgreSQL casting
$dataAggregation = function (string $column) use ($fromDateTime, $toDateTime, $meterId) {
$min = MfmReading::whereBetween('created_at', [$fromDateTime, $toDateTime])
->where('mfm_meter_id', $meterId)
->min(\Illuminate\Support\Facades\DB::raw("{$column}::double precision"));
$max = MfmReading::whereBetween('created_at', [$fromDateTime, $toDateTime])
->where('mfm_meter_id', $meterId)
->max(\Illuminate\Support\Facades\DB::raw("{$column}::double precision"));
$avg = MfmReading::whereBetween('created_at', [$fromDateTime, $toDateTime])
->where('mfm_meter_id', $meterId)
->avg(\Illuminate\Support\Facades\DB::raw("{$column}::double precision"));
return [
'min' => round($min ?? 0, 2),
'max' => round($max ?? 0, 2),
'avg' => round($avg ?? 0, 2),
];
};
// Get aggregated data for all relevant columns
$voltageRNSummary = $dataAggregation('voltage_r_n');
$voltageYNSummary = $dataAggregation('voltage_y_n');
$voltageBNSummary = $dataAggregation('voltage_b_n');
$frequencySummary = $dataAggregation('frequency');
// Labels for each bar on X-axis (min, max, avg per column)
$labels = [
'Voltage RN Min', 'Voltage RN Max', 'Voltage RN Avg',
'Voltage YN Min', 'Voltage YN Max', 'Voltage YN Avg',
'Voltage BN Min', 'Voltage BN Max', 'Voltage BN Avg',
'Frequency Min', 'Frequency Max', 'Frequency Avg',
];
// Ordered data values matching labels above
$dataValues = [
$voltageRNSummary['min'], $voltageRNSummary['max'], $voltageRNSummary['avg'],
$voltageYNSummary['min'], $voltageYNSummary['max'], $voltageYNSummary['avg'],
$voltageBNSummary['min'], $voltageBNSummary['max'], $voltageBNSummary['avg'],
$frequencySummary['min'], $frequencySummary['max'], $frequencySummary['avg'],
];
// Colors by aggregation type: Min - yellow, Max - green, Avg - blue
$aggregationColors = [
'rgba(204, 163, 0, 0.8)', // Darker yellow (Min)
'rgba(30, 120, 120, 0.8)', // Darker teal/green (Max)
'rgba(30, 90, 140, 0.8)', // Darker blue (Avg)
];
// Each set of 3 bars per column repeats the colors: min, max, avg
$backgroundColorArray = [];
foreach (range(1, 4) as $group) { // 4 column groups
foreach ($aggregationColors as $color) {
$backgroundColorArray[] = $color;
}
}
// Construct chart data structure
return [
'labels' => $labels,
'datasets' => [
[
'label' => 'Phase Voltage & Frequency Stats',
// Bar values array: 12 bars total (4 groups × 3 stats each)
'data' => $dataValues,
'backgroundColor' => $backgroundColorArray,
'borderColor' => array_map(fn ($clr) => str_replace('0.6', '1', $clr), $backgroundColorArray),
'borderWidth' => 1,
],
],
];
}
else if ($parameter == 'Current') {
// Helper function to get min, max, avg for a column with PostgreSQL casting
$dataAggregation = function (string $column) use ($fromDateTime, $toDateTime, $meterId) {
$min = MfmReading::whereBetween('created_at', [$fromDateTime, $toDateTime])
->where('mfm_meter_id', $meterId)
->min(\Illuminate\Support\Facades\DB::raw("{$column}::double precision"));
$max = MfmReading::whereBetween('created_at', [$fromDateTime, $toDateTime])
->where('mfm_meter_id', $meterId)
->max(\Illuminate\Support\Facades\DB::raw("{$column}::double precision"));
$avg = MfmReading::whereBetween('created_at', [$fromDateTime, $toDateTime])
->where('mfm_meter_id', $meterId)
->avg(\Illuminate\Support\Facades\DB::raw("{$column}::double precision"));
return [
'min' => round($min ?? 0, 2),
'max' => round($max ?? 0, 2),
'avg' => round($avg ?? 0, 2),
];
};
// Get aggregated data for all relevant columns
$curR = $dataAggregation('current_r');
$curY = $dataAggregation('current_y');
$curB = $dataAggregation('current_b');
$curN = $dataAggregation('current_n');
// Labels for each bar on X-axis (min, max, avg per column)
$labels = [
'Current R Min', 'Current R Max', 'Current R Avg',
'Current Y Min', 'Current Y Max', 'Current Y Avg',
'Current B Min', 'Current B Max', 'Current B Avg',
'Current N Min', 'Current N Max', 'Current N Avg',
];
// Ordered data values matching labels above
$dataValues = [
$curR['min'], $curR['max'], $curR['avg'],
$curY['min'], $curY['max'], $curY['avg'],
$curB['min'], $curB['max'], $curB['avg'],
$curN['min'], $curN['max'], $curN['avg'],
];
// Colors by aggregation type: Min - yellow, Max - green, Avg - blue
$aggregationColors = [
'rgba(204, 163, 0, 0.8)', // Darker yellow (Min)
'rgba(30, 120, 120, 0.8)', // Darker teal/green (Max)
'rgba(30, 90, 140, 0.8)', // Darker blue (Avg)
];
// Each set of 3 bars per column repeats the colors: min, max, avg
$backgroundColorArray = [];
foreach (range(1, 4) as $group) { // 4 column groups
foreach ($aggregationColors as $color) {
$backgroundColorArray[] = $color;
}
}
// Construct chart data structure
return [
'labels' => $labels,
'datasets' => [
[
'label' => 'Phase Voltage & Frequency Stats',
// Bar values array: 12 bars total (4 groups × 3 stats each)
'data' => $dataValues,
'backgroundColor' => $backgroundColorArray,
'borderColor' => array_map(fn ($clr) => str_replace('0.6', '1', $clr), $backgroundColorArray),
'borderWidth' => 1,
],
],
];
}
else if ($parameter == 'Active Power') {
// Helper function to get min, max, avg for a column with PostgreSQL casting
$dataAggregation = function (string $column) use ($fromDateTime, $toDateTime, $meterId) {
$min = MfmReading::whereBetween('created_at', [$fromDateTime, $toDateTime])
->where('mfm_meter_id', $meterId)
->min(\Illuminate\Support\Facades\DB::raw("{$column}::double precision"));
$max = MfmReading::whereBetween('created_at', [$fromDateTime, $toDateTime])
->where('mfm_meter_id', $meterId)
->max(\Illuminate\Support\Facades\DB::raw("{$column}::double precision"));
$avg = MfmReading::whereBetween('created_at', [$fromDateTime, $toDateTime])
->where('mfm_meter_id', $meterId)
->avg(\Illuminate\Support\Facades\DB::raw("{$column}::double precision"));
return [
'min' => round($min ?? 0, 2),
'max' => round($max ?? 0, 2),
'avg' => round($avg ?? 0, 2),
];
};
// Get aggregated data for all relevant columns
$activePowR = $dataAggregation('active_power_r');
$activePowY = $dataAggregation('active_power_y');
$activePowB = $dataAggregation('active_power_b');
$activePowTot = $dataAggregation('active_power_total');
// Labels for each bar on X-axis (min, max, avg per column)
$labels = [
'ActivePow R Min', 'ActivePow R Max', 'ActivePow R Avg',
'ActivePow Y Min', 'ActivePow Y Max', 'ActivePow Y Avg',
'ActivePow B Min', 'ActivePow B Max', 'ActivePow B Avg',
'ActivePow Tot Min', 'ActivePow Tot Max', 'ActivePow Tot Avg',
];
// Ordered data values matching labels above
$dataValues = [
$activePowR['min'], $activePowR['max'], $activePowR['avg'],
$activePowY['min'], $activePowY['max'], $activePowY['avg'],
$activePowB['min'], $activePowB['max'], $activePowB['avg'],
$activePowTot['min'], $activePowTot['max'], $activePowTot['avg'],
];
// Colors by aggregation type: Min - yellow, Max - green, Avg - blue
$aggregationColors = [
'rgba(204, 163, 0, 0.8)', // Darker yellow (Min)
'rgba(30, 120, 120, 0.8)', // Darker teal/green (Max)
'rgba(30, 90, 140, 0.8)', // Darker blue (Avg)
];
// Each set of 3 bars per column repeats the colors: min, max, avg
$backgroundColorArray = [];
foreach (range(1, 4) as $group) { // 4 column groups
foreach ($aggregationColors as $color) {
$backgroundColorArray[] = $color;
}
}
// Construct chart data structure
return [
'labels' => $labels,
'datasets' => [
[
'label' => 'Phase Voltage & Frequency Stats',
// Bar values array: 12 bars total (4 groups × 3 stats each)
'data' => $dataValues,
'backgroundColor' => $backgroundColorArray,
'borderColor' => array_map(fn ($clr) => str_replace('0.6', '1', $clr), $backgroundColorArray),
'borderWidth' => 1,
],
],
];
}
else if ($parameter == 'Power Factor') {
// Helper function to get min, max, avg for a column with PostgreSQL casting
$dataAggregation = function (string $column) use ($fromDateTime, $toDateTime, $meterId) {
$min = MfmReading::whereBetween('created_at', [$fromDateTime, $toDateTime])
->where('mfm_meter_id', $meterId)
->min(\Illuminate\Support\Facades\DB::raw("{$column}::double precision"));
$max = MfmReading::whereBetween('created_at', [$fromDateTime, $toDateTime])
->where('mfm_meter_id', $meterId)
->max(\Illuminate\Support\Facades\DB::raw("{$column}::double precision"));
$avg = MfmReading::whereBetween('created_at', [$fromDateTime, $toDateTime])
->where('mfm_meter_id', $meterId)
->avg(\Illuminate\Support\Facades\DB::raw("{$column}::double precision"));
return [
'min' => round($min ?? 0, 2),
'max' => round($max ?? 0, 2),
'avg' => round($avg ?? 0, 2),
];
};
// Get aggregated data for all relevant columns
$powFacR = $dataAggregation('power_factor_r');
$powFacY = $dataAggregation('power_factor_y');
$powFacB = $dataAggregation('power_factor_b');
$powFacTot = $dataAggregation('power_factor_total');
// Labels for each bar on X-axis (min, max, avg per column)
$labels = [
'PowerFac R Min', 'PowerFac R Max', 'PowerFac R Avg',
'PowerFac Y Min', 'PowerFac Y Max', 'PowerFac Y Avg',
'PowerFac B Min', 'PowerFac B Max', 'PowerFac B Avg',
'PowerFac Tot Min', 'PowerFac Tot Max', 'PowerFac Tot Avg',
];
// Ordered data values matching labels above
$dataValues = [
$powFacR['min'], $powFacR['max'], $powFacR['avg'],
$powFacY['min'], $powFacY['max'], $powFacY['avg'],
$powFacB['min'], $powFacB['max'], $powFacB['avg'],
$powFacTot['min'], $powFacTot['max'], $powFacTot['avg'],
];
// Colors by aggregation type: Min - yellow, Max - green, Avg - blue
$aggregationColors = [
'rgba(204, 163, 0, 0.8)', // Darker yellow (Min)
'rgba(30, 120, 120, 0.8)', // Darker teal/green (Max)
'rgba(30, 90, 140, 0.8)', // Darker blue (Avg)
];
// Each set of 3 bars per column repeats the colors: min, max, avg
$backgroundColorArray = [];
foreach (range(1, 4) as $group) { // 4 column groups
foreach ($aggregationColors as $color) {
$backgroundColorArray[] = $color;
}
}
// Construct chart data structure
return [
'labels' => $labels,
'datasets' => [
[
'label' => 'Phase Voltage & Frequency Stats',
// Bar values array: 12 bars total (4 groups × 3 stats each)
'data' => $dataValues,
'backgroundColor' => $backgroundColorArray,
'borderColor' => array_map(fn ($clr) => str_replace('0.6', '1', $clr), $backgroundColorArray),
'borderWidth' => 1,
],
],
];
}
else if ($parameter == 'Units') {
// Helper function to get min, max, avg for a column with PostgreSQL casting
$dataAggregation = function (string $column) use ($fromDateTime, $toDateTime, $meterId) {
$min = MfmReading::whereBetween('created_at', [$fromDateTime, $toDateTime])
->where('mfm_meter_id', $meterId)
->min(\Illuminate\Support\Facades\DB::raw("{$column}::double precision"));
$max = MfmReading::whereBetween('created_at', [$fromDateTime, $toDateTime])
->where('mfm_meter_id', $meterId)
->max(\Illuminate\Support\Facades\DB::raw("{$column}::double precision"));
$avg = MfmReading::whereBetween('created_at', [$fromDateTime, $toDateTime])
->where('mfm_meter_id', $meterId)
->avg(\Illuminate\Support\Facades\DB::raw("{$column}::double precision"));
return [
'min' => round($min ?? 0, 2),
'max' => round($max ?? 0, 2),
'avg' => round($avg ?? 0, 2),
];
};
// Get aggregated data for all relevant columns
$appEneRec = $dataAggregation('apparent_energy_received');
$reaEneRec = $dataAggregation('reactive_energy_received');
$actEneRec = $dataAggregation('active_energy_received');
// Labels for each bar on X-axis (min, max, avg per column)
$labels = [
'AppEneRec Min', 'AppEneRec Max', 'AppEneRec Avg',
'ReaEneRec Min', 'ReaEneRec Max', 'ReaEneRec Avg',
'ActEneRec Min', 'ActEneRec Max', 'ActEneRec Avg',
];
// Ordered data values matching labels above
$dataValues = [
$appEneRec['min'], $appEneRec['max'], $appEneRec['avg'],
$reaEneRec['min'], $reaEneRec['max'], $reaEneRec['avg'],
$actEneRec['min'], $actEneRec['max'], $actEneRec['avg'],
];
// Colors by aggregation type: Min - yellow, Max - green, Avg - blue
$aggregationColors = [
'rgba(204, 163, 0, 0.8)', // Darker yellow (Min)
'rgba(30, 120, 120, 0.8)', // Darker teal/green (Max)
'rgba(30, 90, 140, 0.8)', // Darker blue (Avg)
];
// Each set of 3 bars per column repeats the colors: min, max, avg
$backgroundColorArray = [];
foreach (range(1, 4) as $group) { // 4 column groups
foreach ($aggregationColors as $color) {
$backgroundColorArray[] = $color;
}
}
// Construct chart data structure
return [
'labels' => $labels,
'datasets' => [
[
'label' => 'Phase Voltage & Frequency Stats',
// Bar values array: 12 bars total (4 groups × 3 stats each)
'data' => $dataValues,
'backgroundColor' => $backgroundColorArray,
'borderColor' => array_map(fn ($clr) => str_replace('0.6', '1', $clr), $backgroundColorArray),
'borderWidth' => 1,
],
],
];
}
else
{
$data = [];
$labels = [];
$currentStart = $fromDateTime->copy();
return [
'labels' => $labels,
'datasets' => [
[
'label' => ucfirst(str_replace('_', ' ', $parameter)),
'data' => $data,
'backgroundColor' => 'rgba(75, 192, 192, 0.2)',
'borderColor' => 'rgba(75, 192, 192, 1)',
'fill' => false,
'tension' => 0.3,
'pointRadius' => 5,
'pointHoverRadius' => 7,
],
],
];
}
}
protected function getOptions(): array
{
return [
'plugins' => [
'datalabels' => [
'anchor' => 'start',
'align' => 'start',
'offset' => -15,
'color' => '#000',
'font' => [
'weight' => 'bold',
],
'formatter' => RawJs::make('function(value) {
return value;
}'),
],
],
'scales' => [
'y' => [
'beginAtZero' => true,
'ticks' => [
'stepSize' => 1,
],
],
],
];
}
protected function getType(): string
{
return 'bar';
}
}

View File

@@ -0,0 +1,206 @@
<?php
namespace App\Filament\Widgets;
use App\Models\MfmReading;
use Carbon\Carbon;
use Filament\Widgets\ChartWidget;
class TrendLineChart extends ChartWidget
{
protected static ?string $heading = 'Trend Line Analysis';
protected static ?string $maxHeight = '420px';
protected int|string|array $columnSpan = 12;
protected function getData(): array
{
$fromDatetime = session('from_datetime');
$toDatetime = session('to_datetime');
$selectedPlant = session('selected_plant');
$meterId = session('selected_meter');
$parameter = session('parameter');
if (empty($fromDatetime) || empty($toDatetime) || empty($selectedPlant) || empty($meterId) || empty($parameter)) {
return [
'labels' => [],
'datasets' => [],
];
}
$fromDateTime = Carbon::parse($fromDatetime);
$toDateTime = Carbon::parse($toDatetime);
if ($fromDateTime->gt($toDateTime) || $fromDateTime->gt(now()) || $toDateTime->gt(now())) {
return [
'labels' => [],
'datasets' => [],
];
}
$durationHours = $fromDateTime->diffInHours($toDateTime);
if ($durationHours < 1 || $durationHours > 24) {
return [
'labels' => [],
'datasets' => [],
];
}
$intervalCount = $durationHours > 12 ? 12 : 10;
$intervalMinutes = $durationHours > 12 ? 120 : floor(($durationHours * 60) / $intervalCount);
$labels = [];
$columnMap = [];
$datasetColors = [];
$dataSeries = [];
// Determine columns and labels based on selected parameter
switch ($parameter) {
case 'Phase Voltage':
$columnMap = [
'voltage_ry' => 'Voltage RY Max',
'voltage_yb' => 'Voltage YB Max',
'voltage_br' => 'Voltage BR Max',
'frequency' => 'Frequency Max',
];
$datasetColors = [
'voltage_ry' => 'rgba(255, 99, 132, 1)', // red
'voltage_yb' => 'rgba(54, 162, 235, 1)', // blue
'voltage_br' => 'rgba(255, 206, 86, 1)', // yellow
'frequency' => 'rgba(75, 192, 192, 1)', // teal
];
break;
case 'Line Voltage':
$columnMap = [
'voltage_r_n' => 'Voltage R-N Max',
'voltage_y_n' => 'Voltage Y-N Max',
'voltage_b_n' => 'Voltage B-N Max',
'frequency' => 'Frequency Max',
];
$datasetColors = [
'voltage_r_n' => 'rgba(153, 102, 255, 1)', // purple
'voltage_y_n' => 'rgba(255, 159, 64, 1)', // orange
'voltage_b_n' => 'rgba(0, 200, 83, 1)', // green
'frequency' => 'rgba(75, 192, 192, 1)', // teal
];
break;
case 'Current':
$columnMap = [
'current_r' => 'Current R Max',
'current_y' => 'Current Y Max',
'current_b' => 'Current B Max',
'current_n' => 'Current N Max ',
];
$datasetColors = [
'current_r' => 'rgba(153, 102, 255, 1)', // purple
'current_y' => 'rgba(255, 159, 64, 1)', // orange
'current_b' => 'rgba(0, 200, 83, 1)', // green
'current_n' => 'rgba(75, 192, 192, 1)', // teal
];
break;
case 'Active Power':
$columnMap = [
'active_power_r' => 'Active Pow R Max',
'active_power_y' => 'Active Pow Y Max',
'active_power_b' => 'Active Pow B Max',
'active_power_total' => 'Active Pow Tot Max ',
];
$datasetColors = [
'active_power_r' => 'rgba(153, 102, 255, 1)', // purple
'active_power_y' => 'rgba(255, 159, 64, 1)', // orange
'active_power_b' => 'rgba(0, 200, 83, 1)', // green
'active_power_total' => 'rgba(75, 192, 192, 1)', // teal
];
break;
case 'Power Factor':
$columnMap = [
'power_factor_r' => 'Power Fac R Max',
'power_factor_y' => 'Power Fac Y Max',
'power_factor_b' => 'Power Fac B Max',
'power_factor_total' => 'Power Fac Tot Max ',
];
$datasetColors = [
'power_factor_r' => 'rgba(153, 102, 255, 1)', // purple
'power_factor_y' => 'rgba(255, 159, 64, 1)', // orange
'power_factor_b' => 'rgba(0, 200, 83, 1)', // green
'power_factor_total' => 'rgba(75, 192, 192, 1)', // teal
];
break;
case 'Units':
$columnMap = [
'apparent_energy_received' => 'AppEneRec Max',
'reactive_energy_received' => 'ReacEneRec Max',
'active_energy_received' => 'ActiveEneRec Max',
];
$datasetColors = [
'apparent_energy_received' => 'rgba(153, 102, 255, 1)', // purple
'reactive_energy_received' => 'rgba(255, 159, 64, 1)', // orange
'active_energy_received' => 'rgba(0, 200, 83, 1)', // green
];
break;
default:
return [
'labels' => [],
'datasets' => [],
];
}
// Initialize empty arrays for each data series
foreach ($columnMap as $column => $label) {
$dataSeries[$column] = [];
}
$current = $fromDateTime->copy();
while ($current < $toDateTime) {
$next = $current->copy()->addMinutes($intervalMinutes);
$selectParts = [];
foreach ($columnMap as $column => $label) {
$selectParts[] = "MAX({$column}::double precision) as {$column}";
}
$readings = MfmReading::where('mfm_meter_id', $meterId)
->whereBetween('created_at', [$current, $next])
->selectRaw(implode(', ', $selectParts))
->first();
$labels[] = $current->format('H:i');
foreach ($columnMap as $column => $label) {
$dataSeries[$column][] = round($readings->{$column} ?? 0, 2);
}
$current = $next;
}
// Construct dataset array
$datasets = [];
foreach ($columnMap as $column => $label) {
$datasets[] = [
'label' => $label,
'data' => $dataSeries[$column],
'borderColor' => $datasetColors[$column],
'backgroundColor' => str_replace('1)', '0.2)', $datasetColors[$column]),
'fill' => false,
'tension' => 0.1,
];
}
return [
'labels' => $labels,
'datasets' => $datasets,
];
}
protected function getType(): string
{
return 'line';
}
}