519 lines
26 KiB
PHP
Executable File
519 lines
26 KiB
PHP
Executable File
@extends('m3::liberty-theme.layout.main')
|
|
@section('title')
|
|
Exec Summary
|
|
@endsection
|
|
|
|
{{-- Flat Picker CSS --}}
|
|
<link rel="stylesheet" href="{{ route('m3.assets', ['path' => 'liberty-theme/vendor/flatpickr-4.6.13.min.css']) }}">
|
|
<link rel="stylesheet" href="{{ route('m3.assets', ['path' => 'liberty-theme/vendor/flatpickr-monthSelect-4.6.13.css']) }}">
|
|
|
|
@section('content')
|
|
<div class="container-fluid px-4">
|
|
<h1 class="mt-4">Exec Summary</h1>
|
|
<div class="tab-content" id="pills-tabContent">
|
|
<div class="tab-pane fade show active" id="pills-home" role="tabpanel" aria-labelledby="pills-home-tab">
|
|
<div class="card mb-4">
|
|
{{-- <div class="card-header"></div> --}}
|
|
<div class="card-body">
|
|
<div class="row">
|
|
{{-- Selectors --}}
|
|
<div class="col-2 border-end">
|
|
<form id="chartSelectorsForm">
|
|
<h6>Sales / Weight / Lines</h6>
|
|
|
|
{{-- Warehouses --}}
|
|
<div class="mb-3">
|
|
<div class="form-check-container">
|
|
@foreach ($IC670M as $key => $IC670MR)
|
|
@php $currentWHDDA = trim($IC670MR->WDCO).trim($IC670MR->WDPL).trim($IC670MR->WDWHS); @endphp
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="WHDDA[]"
|
|
id="WHDDA_{{ $currentWHDDA }}" value="{{ $currentWHDDA }}"
|
|
data-comp="{{ trim($IC670MR->WDCO) }}"
|
|
data-pl="{{ trim($IC670MR->WDPL) }}"
|
|
data-wh="{{ trim($IC670MR->WDWHS) }}"
|
|
{{ $WHDDA != '' && $WHDDA == $currentWHDDA ? 'checked' : '' }}>
|
|
<label class="form-check-label" for="WHDDA_{{ $currentWHDDA }}">
|
|
{{ trim(Str::upper($IC670MR->WDWNAM)) }}
|
|
</label>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
|
|
<hr>
|
|
|
|
{{-- Report Period --}}
|
|
<div class="mb-3">
|
|
<div class="btn-group" aria-label="Report Period">
|
|
<input type="radio" class="btn-check" name="reportPeriod" id="periodMonthly"
|
|
value="monthly" checked>
|
|
<label class="btn btn-outline-primary btn-sm"
|
|
for="periodMonthly">Monthly</label>
|
|
|
|
<input type="radio" class="btn-check" name="reportPeriod" id="periodQuarter"
|
|
value="quarter">
|
|
<label class="btn btn-outline-primary btn-sm"
|
|
for="periodQuarter">Quarter</label>
|
|
|
|
<input type="radio" class="btn-check" name="reportPeriod" id="periodYear"
|
|
value="year">
|
|
<label class="btn btn-outline-primary btn-sm" for="periodYear">Year</label>
|
|
|
|
<input type="radio" class="btn-check" name="reportPeriod" id="preiodCustom"
|
|
value="custom">
|
|
<label class="btn btn-outline-primary btn-sm" for="preiodCustom">Daily</label>
|
|
</div>
|
|
</div>
|
|
|
|
<hr>
|
|
|
|
{{-- From To Dates --}}
|
|
<div class="mb-3">
|
|
@php
|
|
$todayDate = today()->format('Ym');
|
|
$reportDate = \DateTime::createFromFormat('Ym', $todayDate)
|
|
->modify('first day of January last year')
|
|
->format('Y-m');
|
|
$toreportDate = \DateTime::createFromFormat('Ym', $todayDate)->format(
|
|
'Y-m',
|
|
);
|
|
@endphp
|
|
<div class="row">
|
|
{{-- <fieldset id="fromToDates"> --}}
|
|
<div class="row align-items-center mb-2">
|
|
<div class="col-md-4">
|
|
<label class="col-form-label" for="REPORTDATE">From</label>
|
|
</div>
|
|
<div class="col-md-8">
|
|
<input type="text" class="form-control" id="REPORTDATE"
|
|
name="REPORTDATE" value="{{ $reportDate }}">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row align-items-center" id="TOREPORTDATEDIV">
|
|
<div class="col-md-4">
|
|
<label class="col-form-label" for="TOREPORTDATE">To</label>
|
|
</div>
|
|
<div class="col-md-8">
|
|
<input type="text" class="form-control" id="TOREPORTDATE"
|
|
name="TOREPORTDATE" value="{{ $toreportDate }}">
|
|
</div>
|
|
</div>
|
|
{{-- </fieldset> --}}
|
|
</div>
|
|
</div>
|
|
|
|
<hr>
|
|
|
|
{{-- Sales Weight Lines --}}
|
|
<div class="mb-3">
|
|
<div class="form-check form-check-inline">
|
|
<input class="form-check-input" type="checkbox" id="checkSales"
|
|
name="checkSales" checked>
|
|
<label class="form-check-label" for="checkSales">Sales</label>
|
|
</div>
|
|
<div class="form-check form-check-inline">
|
|
<input class="form-check-input" type="checkbox" id="checkWeight"
|
|
name="checkWeight">
|
|
<label class="form-check-label" for="checkWeight">Weight</label>
|
|
</div>
|
|
<div class="form-check form-check-inline">
|
|
<input class="form-check-input" type="checkbox" id="checkLines"
|
|
name="checkLines">
|
|
<label class="form-check-label" for="checkLines">Lines</label>
|
|
</div>
|
|
</div>
|
|
|
|
<hr>
|
|
|
|
<input type="hidden" name="_token" value="{{ csrf_token() }}">
|
|
|
|
<button type="button" class="btn btn-primary mt-4" id="updateCharts">Submit</button>
|
|
</form>
|
|
</div>
|
|
{{-- Charts --}}
|
|
<div class="col-10">
|
|
<div class="container-fluid">
|
|
<canvas id="acquisitions"></canvas>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
@endsection
|
|
|
|
@section('scripts')
|
|
{{-- Chart JS --}}
|
|
<script src="{{ route('m3.assets', ['path' => 'liberty-theme/vendor/chart-4.4.8.umd.min.js']) }}"></script>
|
|
|
|
<!-- Flatpickr JS -->
|
|
<script src="{{ route('m3.assets', ['path' => 'liberty-theme/vendor/flatpickr-4.6.13.min.js']) }}"></script>
|
|
<!-- Month Select Plugin -->
|
|
<script src="{{ route('m3.assets', ['path' => 'liberty-theme/vendor/flatpickr-monthSelect-4.6.13.js']) }}"></script>
|
|
|
|
<script>
|
|
$(document).ready(function() {
|
|
|
|
//// Init Dates
|
|
let fromMonthPicker = flatpickr("#REPORTDATE", {
|
|
dateFormat: "Y-m", // Format as Year-Month
|
|
plugins: [
|
|
new monthSelectPlugin({
|
|
shorthand: false, // Show short month names (e.g., Jan, Feb)
|
|
readOnly: false,
|
|
dateFormat: "Y-m", // Format output
|
|
altFormat: "F Y", // Display full month name (e.g., January 2025)
|
|
theme: "material_blue" // Options: "light", "dark", "material_blue", etc.
|
|
})
|
|
],
|
|
onChange: function(selectedDates, dateStr) {
|
|
toMonthPicker.set("minDate",
|
|
dateStr); // Prevent selecting a "To" month before "From"
|
|
},
|
|
// Add these explicit styling options
|
|
onReady: function(dateObj, dateStr, instance) {
|
|
instance.input.style.backgroundColor = 'white';
|
|
instance.input.style.cursor = 'pointer';
|
|
}
|
|
});
|
|
|
|
let toMonthPicker = flatpickr("#TOREPORTDATE", {
|
|
dateFormat: "Y-m",
|
|
plugins: [
|
|
new monthSelectPlugin({
|
|
shorthand: true,
|
|
dateFormat: "Y-m",
|
|
altFormat: "F Y",
|
|
theme: "material_blue"
|
|
})
|
|
],
|
|
onChange: function(selectedDates, dateStr) {
|
|
fromMonthPicker.set("maxDate",
|
|
dateStr); // Prevent selecting a "From" month after "To"
|
|
},
|
|
// Add these explicit styling options
|
|
onReady: function(dateObj, dateStr, instance) {
|
|
instance.input.style.backgroundColor = 'white';
|
|
instance.input.style.cursor = 'pointer';
|
|
}
|
|
});
|
|
//// End Dates
|
|
|
|
//// Add change event listener to radio buttons
|
|
$('input[name="reportPeriod"]').change(function() {
|
|
const isDisabled = $(this).val() == 'custom';
|
|
// $('#TOREPORTDATE').prop('disabled', isDisabled);
|
|
|
|
if (isDisabled) {
|
|
$('#TOREPORTDATEDIV').hide();
|
|
// toMonthPicker.input.style.backgroundColor = '#e9ecef';
|
|
// toMonthPicker.input.style.cursor = 'not-allowed';
|
|
|
|
// Set fromMonthPicker to today
|
|
const today = new Date();
|
|
fromMonthPicker.setDate(today);
|
|
}
|
|
// Show Todate
|
|
else {
|
|
$('#TOREPORTDATEDIV').show();
|
|
// toMonthPicker.input.style.backgroundColor = 'white';
|
|
// toMonthPicker.input.style.cursor = 'pointer';
|
|
}
|
|
});
|
|
|
|
//// Update Charts
|
|
$("#updateCharts").click(function() {
|
|
//// Validate Fields before calling Ajax
|
|
// Validation checks
|
|
let isValid = true;
|
|
let errorMessages = [];
|
|
|
|
// Check if at least one WHDDA checkbox is checked
|
|
const checkedWhdda = $("input[name='WHDDA[]']:checked").length;
|
|
if (checkedWhdda === 0) {
|
|
isValid = false;
|
|
errorMessages.push("Please select at least one Warehouse/Plant");
|
|
}
|
|
|
|
// Check From and To dates
|
|
const fromDate = $("#REPORTDATE").val();
|
|
const toDate = $("#TOREPORTDATE").val();
|
|
if (!fromDate || !toDate) {
|
|
isValid = false;
|
|
errorMessages.push("Please select both From and To dates");
|
|
}
|
|
|
|
// Check if at least one of Sales/Weight/Lines is checked
|
|
const checkedMetrics =
|
|
$("#checkSales:checked").length +
|
|
$("#checkWeight:checked").length +
|
|
$("#checkLines:checked").length;
|
|
|
|
if (checkedMetrics === 0) {
|
|
isValid = false;
|
|
errorMessages.push("Please select at least one metric (Sales, Weight, or Lines)");
|
|
}
|
|
|
|
// If validation fails, show SweetAlert
|
|
if (!isValid) {
|
|
Swal.fire({
|
|
icon: 'warning',
|
|
title: 'Validation Error',
|
|
html: errorMessages.map(msg => `<p>${msg}</p>`).join(''),
|
|
confirmButtonText: 'OK'
|
|
});
|
|
return;
|
|
}
|
|
|
|
let formData = $('#chartSelectorsForm').serialize(); // Serialize form data
|
|
// console.log(formData); return false;
|
|
|
|
//// Ran Ajax
|
|
$.ajax({
|
|
url: "{{ route('m3.execsummary') }}",
|
|
type: "POST",
|
|
data: formData,
|
|
success: function(response) {
|
|
//// Update charts
|
|
if (response.success == true) {
|
|
|
|
//// First: Destroy Chart if it exists
|
|
if (window.execSummaryChart !== undefined) {
|
|
window.execSummaryChart.destroy();
|
|
}
|
|
|
|
checkSales = response.checkSales;
|
|
checkWeight = response.checkWeight;
|
|
checkLines = response.checkLines;
|
|
|
|
dataResult = response.result;
|
|
reportPeriod = response.reportPeriod;
|
|
|
|
// Build Data
|
|
const data = dataResult.map(item => ({
|
|
year: item.YEARS,
|
|
dollars: parseInt(item.DOLLARS),
|
|
weight: parseInt(item.WEIGHT),
|
|
lines: parseInt(item.LINES),
|
|
whs: parseInt(item.WHS)
|
|
}));
|
|
|
|
// Group data by year and WHS
|
|
const groupedData = new Map();
|
|
const whsSet = new Set();
|
|
const yearsSet = new Set();
|
|
|
|
data.forEach(entry => {
|
|
const { year, whs,dollars,weight,lines} = entry;
|
|
|
|
if (!groupedData.has(year)) {
|
|
groupedData.set(year, new Map());
|
|
yearsSet.add(year);
|
|
}
|
|
|
|
if (!groupedData.get(year).has(whs)) {groupedData.get(year).set(whs, {dollars: 0,weight: 0,lines: 0});}
|
|
|
|
const whsData = groupedData.get(year).get(whs);
|
|
whsData.dollars += dollars;
|
|
whsData.weight += weight;
|
|
whsData.lines += lines;
|
|
whsSet.add(whs);
|
|
});
|
|
|
|
// Convert Sets to Arrays
|
|
const labels = [...groupedData.keys()].sort();
|
|
const whsArray = Array.from(whsSet);
|
|
const yearsArray = Array.from(yearsSet);
|
|
|
|
//##### MAK
|
|
|
|
// Function to extract the time period (month or quarter) regardless of year
|
|
function getTimePeriod(yearLabel) {
|
|
if (yearLabel.includes("Q")) {
|
|
// Quarterly format: "Q1-24" -> Return "Q1"
|
|
return yearLabel.split("-")[0];
|
|
} else if (yearLabel.match(/^\d{2}-\d{4}$/)) {
|
|
// Monthly format: "01-2024" -> Check if odd or even
|
|
const month = parseInt(yearLabel.split("-")[0]);
|
|
return month % 2 === 0 ? "even" : "odd";
|
|
} else if (/^\d{2}$/.test(yearLabel)) {
|
|
// Daily format: "01" -> Return "day"
|
|
return "day";
|
|
}
|
|
return yearLabel; // Default case
|
|
}
|
|
|
|
// Function to check if a label is a daily format
|
|
function isDailyFormat(yearLabel) {
|
|
return /^\d{2}$/.test(yearLabel);
|
|
}
|
|
|
|
// Define base hues for metrics
|
|
const HUE_BLUE = 195; // Sales → Blue
|
|
const HUE_GREEN = 140; // Weight → Green
|
|
const HUE_GREY = 220; // Lines → Grey (desaturated)
|
|
|
|
// Create color maps - first level for period type, second level for WHS
|
|
let salesColorMap = {};
|
|
let weightColorMap = {};
|
|
let linesColorMap = {};
|
|
|
|
// Define period types
|
|
const periodTypes = ["Q1", "Q2", "Q3", "Q4", "odd", "even", "day"];
|
|
|
|
// Generate colors for each period type
|
|
periodTypes.forEach((period, periodIndex) => {
|
|
// Calculate hue variations based on period type
|
|
let salesHue = HUE_BLUE;
|
|
let weightHue = HUE_GREEN;
|
|
|
|
// Adjust hue for quarters only
|
|
if (period.startsWith("Q")) {
|
|
const quarterNum = parseInt(period.substring(1));
|
|
salesHue = HUE_BLUE + (quarterNum - 1) * 15;
|
|
weightHue = HUE_GREEN + (quarterNum - 1) * 15;
|
|
}
|
|
|
|
// For monthly periods (odd/even), use slight variations
|
|
if (period === "odd") {
|
|
salesHue = HUE_BLUE + 5;
|
|
weightHue = HUE_GREEN + 5;
|
|
} else if (period === "even") {
|
|
salesHue = HUE_BLUE + 20;
|
|
weightHue = HUE_GREEN + 20;
|
|
}
|
|
|
|
// Create color maps for each WHS within this period
|
|
salesColorMap[period] = {};
|
|
weightColorMap[period] = {};
|
|
linesColorMap[period] = {};
|
|
|
|
// Generate different shades for each WHS - darker to lighter
|
|
whsArray.forEach((whs, whsIndex) => {
|
|
// Vary lightness by WHS - start darker (25%) and get lighter
|
|
// Reverse the WHS index calculation to go from dark to light
|
|
const reversedIndex = whsArray.length - 1 - whsIndex;
|
|
const lightness = 45 + (reversedIndex % 8) * 6;
|
|
|
|
// Assign colors for each metric
|
|
salesColorMap[period][whs] = `hsl(${salesHue}, 85%, ${lightness}%)`;
|
|
weightColorMap[period][whs] = `hsl(${weightHue}, 85%, ${lightness}%)`;
|
|
linesColorMap[period][whs] = `hsl(${HUE_GREY}, 10%, ${lightness}%)`; // Pure grey for Lines
|
|
});
|
|
});
|
|
|
|
// Function to get the appropriate color based on time period, metric, and WHS
|
|
function getDatasetColor(yearLabel, metric, whs) {
|
|
const period = getTimePeriod(yearLabel);
|
|
|
|
if (metric === "Sales") {
|
|
return salesColorMap[period]?.[whs] || `hsl(${HUE_BLUE}, 85%, 50%)`;
|
|
} else if (metric === "Weight") {
|
|
return weightColorMap[period]?.[whs] || `hsl(${HUE_GREEN}, 85%, 50%)`;
|
|
} else if (metric === "Lines") {
|
|
return linesColorMap[period]?.[whs] || `hsl(${HUE_GREY}, 0%, 50%)`;
|
|
}
|
|
|
|
return "rgba(200, 200, 200, 0.6)"; // Default fallback
|
|
}
|
|
|
|
// Extract datasets
|
|
let datasets = [];
|
|
|
|
whsArray.forEach(whs => {
|
|
// Function to create dataset with colors based on time period, metric, and WHS
|
|
const createDataset = (label, dataKey, stack, yAxisID, metric) => ({
|
|
label: `${label} (WHS ${whs} - ${metric})`,
|
|
data: labels.map(year => groupedData.get(year)?.get(whs)?.[dataKey] || 0),
|
|
backgroundColor: labels.map(year => getDatasetColor(year, metric, whs)),
|
|
stack,
|
|
yAxisID,
|
|
borderWidth: 2
|
|
});
|
|
|
|
if (checkSales === 'on') {
|
|
datasets.push(createDataset("Sales", "dollars", "Sales", 'yLeft', "Sales"));
|
|
}
|
|
if (checkWeight === 'on') {
|
|
datasets.push(createDataset("Weight", "weight", "Weight", 'yLeft', "Weight"));
|
|
}
|
|
if (checkLines === 'on') {
|
|
datasets.push(createDataset("Lines", "lines", "Lines", 'yRight', "Lines"));
|
|
}
|
|
});
|
|
|
|
|
|
//###### END MAK
|
|
|
|
// Generate Chart
|
|
window.execSummaryChart = new Chart(
|
|
document.getElementById('acquisitions'), {
|
|
type: 'bar',
|
|
data: {
|
|
labels,
|
|
datasets
|
|
},
|
|
options: {
|
|
interaction: {
|
|
intersect: false
|
|
},
|
|
responsive: true,
|
|
scales: {
|
|
yLeft: {
|
|
type: 'linear',
|
|
position: 'left',
|
|
title: {
|
|
display: true,
|
|
text: 'Sales & Weight'
|
|
},
|
|
stacked: true
|
|
},
|
|
yRight: {
|
|
type: 'linear',
|
|
position: 'right',
|
|
title: {
|
|
display: true,
|
|
text: 'Lines'
|
|
},
|
|
grid: {
|
|
drawOnChartArea: false
|
|
},
|
|
stacked: true
|
|
},
|
|
x: {
|
|
stacked: true
|
|
},
|
|
}
|
|
}
|
|
}
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
error: function(xhr, status, error) {
|
|
console.error("Error:", error);
|
|
// Handle errors
|
|
}
|
|
});
|
|
// End Ajax
|
|
|
|
});
|
|
|
|
//// Trigger Click on init
|
|
$("#updateCharts").trigger("click");
|
|
|
|
});
|
|
</script>
|
|
@endsection
|