Files

993 lines
56 KiB
PHP
Executable File

@extends('m3::liberty-theme.layout.main')
@section('title')
Exec Summary - Line Chart
@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 - Line Chart</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' || $(this).val() == 'monthly';
// $('#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';
}
});
//// Trigger Period change
$('input[name="reportPeriod"]').trigger("change");
//// 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");
}
// Get selected period
const checkedPeriod = $('input[name="reportPeriod"]:checked').val();
// console.log(checkedPeriod);
// Check From and To dates
const fromDate = $("#REPORTDATE").val();
const toDate = $("#TOREPORTDATE").val();
if ((checkedPeriod == 'monthly' || checkedPeriod == 'custom') && !fromDate) {
isValid = false;
errorMessages.push("Please select date");
}
else 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;
//// Run Ajax
$.ajax({
url: "{{ route('m3.execsummaryline') }}",
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;
dataResultLY = response.resultLY; // Last year data
reportPeriod = response.reportPeriod;
// Build Data for Current Year
const data = dataResult.map(item => ({
year: item.YEARS,
dollars: parseInt(item.DOLLARS),
weight: parseInt(item.WEIGHT),
lines: parseInt(item.LINES),
whs: parseInt(item.WHS)
}));
// Build Data for Last Year
const dataLY = dataResultLY ? dataResultLY.map(item => ({
year: item.YEARS,
dollars: parseInt(item.DOLLARS),
weight: parseInt(item.WEIGHT),
lines: parseInt(item.LINES),
whs: parseInt(item.WHS)
})) : [];
// Determine data format (year, quarterly, monthly, or daily)
let dataFormat;
if (data.length > 0) {
const sampleYear = data[0].year;
if (sampleYear.includes("Q")) {
dataFormat = "quarterly";
} else if (sampleYear.match(/^\d{4}-\d{2}$/)) {
dataFormat = "monthly";
} else if (/^\d{2}$/.test(sampleYear)) {
dataFormat = "daily";
} else {
dataFormat = "yearly";
}
}
// Function to generate distinct colors
function generateDistinctColors(count) {
let colors = [];
for (let i = 0; i < count; i++) {
let hue = (i * 137.5) % 360; // Golden angle for uniqueness
let saturation = 70 + (i % 3) * 10;
let lightness = 50 + (i % 2) * 10;
colors.push(`hsl(${hue}, ${saturation}%, ${lightness}%)`);
}
return colors;
}
// Function to create a lighter version of a color for LY data
function createLighterColor(color) {
// Extract HSL values
const match = color.match(/hsl\((\d+\.?\d*),\s*(\d+\.?\d*)%,\s*(\d+\.?\d*)%\)/);
if (match) {
const h = parseFloat(match[1]);
const s = parseFloat(match[2]);
const l = parseFloat(match[3]);
// Create lighter version by increasing lightness and reducing saturation
return `hsl(${h}, ${Math.max(s - 20, 30)}%, ${Math.min(l + 15, 85)}%)`;
}
return color; // Return original if parsing fails
}
// Group data according to format
let labels = [];
let datasets = [];
const whsSet = new Set();
// Initialize warehouse set from data
data.forEach(entry => whsSet.add(entry.whs));
if (dataLY) dataLY.forEach(entry => whsSet.add(entry.whs));
const whsArray = Array.from(whsSet).sort((a, b) => a - b);
// Monthly data format handling
if (dataFormat === "monthly") {
// Extract unique year-month combinations
const periodData = new Map();
const monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
// Process current year data
data.forEach(entry => {
const [year, month] = entry.year.split("-");
const monthIndex = parseInt(month) - 1;
const monthName = monthNames[monthIndex];
const yearSuffix = year.substring(2); // Last 2 digits
const periodKey = `${monthName}`; // '${yearSuffix}
const periodKey2 = `${monthName} '${yearSuffix}`;
if (!periodData.has(periodKey)) {
periodData.set(periodKey, { current: {}, ly: {} ,label: periodKey2 });
}
const whsData = periodData.get(periodKey).current;
if (!whsData[entry.whs]) {
whsData[entry.whs] = { dollars: 0, weight: 0, lines: 0 };
}
whsData[entry.whs].dollars += entry.dollars;
whsData[entry.whs].weight += entry.weight;
whsData[entry.whs].lines += entry.lines;
});
// Process last year data if available
if (dataLY && dataLY.length > 0) {
dataLY.forEach(entry => {
const [year, month] = entry.year.split("-");
const monthIndex = parseInt(month) - 1;
const monthName = monthNames[monthIndex];
const yearSuffix = year.substring(2); // Last 2 digits
const periodKey = `${monthName}`; // '${yearSuffix}
const periodKey2 = `${monthName} '${yearSuffix}`;
if (!periodData.has(periodKey)) {
periodData.set(periodKey, { current: {}, ly: {},label: periodKey2 });
}
const whsData = periodData.get(periodKey).ly;
if (!whsData[entry.whs]) {
whsData[entry.whs] = { dollars: 0, weight: 0, lines: 0 };
}
whsData[entry.whs].dollars += entry.dollars;
whsData[entry.whs].weight += entry.weight;
whsData[entry.whs].lines += entry.lines;
});
}
// Sort labels chronologically
labels = Array.from(periodData.keys()).sort((a, b) => {
// Parse month and year
const [monthA, yearA] = a.split(" '");
const [monthB, yearB] = b.split(" '");
const yearDiff = parseInt(yearA) - parseInt(yearB);
if (yearDiff !== 0) return yearDiff;
// Compare months if same year
const monthOrder = { "Jan": 1, "Feb": 2, "Mar": 3, "Apr": 4, "May": 5, "Jun": 6,"Jul": 7, "Aug": 8, "Sep": 9, "Oct": 10, "Nov": 11, "Dec": 12 };
return monthOrder[monthA] - monthOrder[monthB];
});
// Generate colors for each WHS
const whsColors = {};
whsArray.forEach((whs, index) => {
whsColors[whs] = generateDistinctColors(whsArray.length)[index];
});
// Create datasets for each metric and warehouse
whsArray.forEach(whs => {
if (checkSales === 'on') {
// Sales dataset for current year
datasets.push({
type: 'line',
label: `Sales TY (WHS ${whs})`,
data: labels.map(period => ({
x: period,
y: periodData.get(period)?.current[whs]?.dollars || 0,
label: periodData.get(period)?.label || period
})),
borderColor: whsColors[whs],
backgroundColor: whsColors[whs],
yAxisID: 'yLeft',
tension: 0.3,
fill: false
});
// Sales dataset for last year
if (dataLY && dataLY.length > 0) {
datasets.push({
type: 'line',
label: `Sales LY (WHS ${whs})`,
data: labels.map(period => ({
x: period,
y: periodData.get(period)?.ly[whs]?.dollars || 0,
label: periodData.get(period)?.label || period
})),
borderColor: createLighterColor(whsColors[whs]),
backgroundColor: createLighterColor(whsColors[whs]),
borderDash: [10, 5],
yAxisID: 'yLeft',
tension: 0.3,
fill: false,
order: 1
});
}
}
// Weight
if (checkWeight === 'on') {
// Weight dataset for current year
datasets.push({
type: 'line',
label: `Weight TY (WHS ${whs})`,
data: labels.map(period =>
periodData.get(period)?.current[whs]?.weight || 0
),
borderColor: whsColors[whs],
backgroundColor: whsColors[whs],
// borderDash: [5, 5],
yAxisID: 'yLeft',
tension: 0.3,
fill: false
});
// Weight dataset for last year
if (dataLY && dataLY.length > 0) {
datasets.push({
type: 'line',
label: `Weight LY (WHS ${whs})`,
data: labels.map(period =>
periodData.get(period)?.ly[whs]?.weight || 0
),
borderColor: createLighterColor(whsColors[whs]),
backgroundColor: createLighterColor(whsColors[whs]),
borderDash: [10, 10],
yAxisID: 'yLeft',
tension: 0.3,
fill: false,
order: 1
});
}
}
// Lines
if (checkLines === 'on') {
// Lines dataset for current year
datasets.push({
type: 'line',
label: `Lines TY (WHS ${whs})`,
data: labels.map(period =>
periodData.get(period)?.current[whs]?.lines || 0
),
borderColor: whsColors[whs],
backgroundColor: whsColors[whs],
// borderDash: [2, 2],
yAxisID: 'yRight',
tension: 0.3,
fill: false
});
// Lines dataset for last year
if (dataLY && dataLY.length > 0) {
datasets.push({
type: 'line',
label: `Lines LY (WHS ${whs})`,
data: labels.map(period =>
periodData.get(period)?.ly[whs]?.lines || 0
),
borderColor: createLighterColor(whsColors[whs]),
backgroundColor: createLighterColor(whsColors[whs]),
borderDash: [10, 2],
yAxisID: 'yRight',
tension: 0.3,
fill: false,
order: 1
});
}
}
});
}
// Quarterly data format handling
else if (dataFormat === "quarterly") {
// Extract unique years and quarters
const yearsSet = new Set();
const periodsSet = new Set();
data.forEach(entry => {
const [quarter, year] = entry.year.split("-");
yearsSet.add(year);
periodsSet.add(quarter);
});
const years = Array.from(yearsSet).sort();
const periods = Array.from(periodsSet).sort((a, b) =>
parseInt(a.substring(1)) - parseInt(b.substring(1))
);
// Create groupedData structure for quarters
const groupedData = new Map();
const groupedDataLY = new Map();
// Initialize data structures
years.forEach(year => {
groupedData.set(year, new Map());
periods.forEach(quarter => {
groupedData.get(year).set(quarter, new Map());
});
});
// Process current year data
data.forEach(entry => {
const [quarter, year] = entry.year.split("-");
if (!groupedData.has(year)) {
groupedData.set(year, new Map());
}
if (!groupedData.get(year).has(quarter)) {
groupedData.get(year).set(quarter, new Map());
}
if (!groupedData.get(year).get(quarter).has(entry.whs)) {
groupedData.get(year).get(quarter).set(entry.whs, {
dollars: 0, weight: 0, lines: 0
});
}
const whsData = groupedData.get(year).get(quarter).get(entry.whs);
whsData.dollars += entry.dollars;
whsData.weight += entry.weight;
whsData.lines += entry.lines;
});
// Process last year data if available
if (dataLY && dataLY.length > 0) {
// Initialize LY structure
years.forEach(year => {
groupedDataLY.set(year, new Map());
periods.forEach(quarter => {
groupedDataLY.get(year).set(quarter, new Map());
});
});
dataLY.forEach(entry => {
const [quarter, year] = entry.year.split("-");
if (!groupedDataLY.has(year)) {
groupedDataLY.set(year, new Map());
}
if (!groupedDataLY.get(year).has(quarter)) {
groupedDataLY.get(year).set(quarter, new Map());
}
if (!groupedDataLY.get(year).get(quarter).has(entry.whs)) {
groupedDataLY.get(year).get(quarter).set(entry.whs, {
dollars: 0, weight: 0, lines: 0
});
}
const whsData = groupedDataLY.get(year).get(quarter).get(entry.whs);
whsData.dollars += entry.dollars;
whsData.weight += entry.weight;
whsData.lines += entry.lines;
});
}
// Set labels to quarters
labels = periods;
// Create color map for years
const yearColors = {};
const generatedColors = generateDistinctColors(years.length);
years.forEach((year, index) => {
yearColors[year] = generatedColors[index];
});
// Create datasets
whsArray.forEach(whs => {
years.forEach(year => {
if (checkSales === 'on') {
// Current year sales
datasets.push({
type: 'line',
label: `Sales (WHS ${whs} - ${year})`,
data: periods.map(period =>
groupedData.get(year)?.get(period)?.get(whs)?.dollars || 0
),
borderColor: yearColors[year],
backgroundColor: yearColors[year],
yAxisID: 'yLeft',
tension: 0.3,
fill: false
});
// Last year sales
if (dataLY && dataLY.length > 0) {
datasets.push({
type: 'line',
label: `Sales LY (WHS ${whs} - ${year})`,
data: periods.map(period =>
groupedDataLY.get(year)?.get(period)?.get(whs)?.dollars || 0
),
borderColor: yearColors[year],
backgroundColor: yearColors[year],
borderDash: [10, 5],
yAxisID: 'yLeft',
tension: 0.3,
fill: false,
order: 1
});
}
}
if (checkWeight === 'on') {
// Current year weight
datasets.push({
type: 'line',
label: `Weight (WHS ${whs} - ${year})`,
data: periods.map(period =>
groupedData.get(year)?.get(period)?.get(whs)?.weight || 0
),
borderColor: yearColors[year],
backgroundColor: yearColors[year],
borderDash: [5, 5],
yAxisID: 'yLeft',
tension: 0.3,
fill: false
});
// Last year weight
if (dataLY && dataLY.length > 0) {
datasets.push({
type: 'line',
label: `Weight LY (WHS ${whs} - ${year})`,
data: periods.map(period =>
groupedDataLY.get(year)?.get(period)?.get(whs)?.weight || 0
),
borderColor: yearColors[year],
backgroundColor: yearColors[year],
borderDash: [10, 10],
yAxisID: 'yLeft',
tension: 0.3,
fill: false,
order: 1
});
}
}
if (checkLines === 'on') {
// Current year lines
datasets.push({
type: 'line',
label: `Lines (WHS ${whs} - ${year})`,
data: periods.map(period =>
groupedData.get(year)?.get(period)?.get(whs)?.lines || 0
),
borderColor: yearColors[year],
backgroundColor: yearColors[year],
borderDash: [2, 2],
yAxisID: 'yRight',
tension: 0.3,
fill: false
});
// Last year lines
if (dataLY && dataLY.length > 0) {
datasets.push({
type: 'line',
label: `Lines LY (WHS ${whs} - ${year})`,
data: periods.map(period =>
groupedDataLY.get(year)?.get(period)?.get(whs)?.lines || 0
),
borderColor: yearColors[year],
backgroundColor: yearColors[year],
borderDash: [10, 2],
yAxisID: 'yRight',
tension: 0.3,
fill: false,
order: 1
});
}
}
});
});
}
// Yearly or daily format handling
else {
// Group data by year/day and warehouse
const groupedData = new Map();
const groupedDataLY = new Map();
// Process current year data
data.forEach(entry => {
if (!groupedData.has(entry.year)) {
groupedData.set(entry.year, new Map());
}
if (!groupedData.get(entry.year).has(entry.whs)) {
groupedData.get(entry.year).set(entry.whs, {
dollars: 0, weight: 0, lines: 0
});
}
const whsData = groupedData.get(entry.year).get(entry.whs);
whsData.dollars += entry.dollars;
whsData.weight += entry.weight;
whsData.lines += entry.lines;
});
// Process last year data if available
if (dataLY && dataLY.length > 0) {
dataLY.forEach(entry => {
if (!groupedDataLY.has(entry.year)) {
groupedDataLY.set(entry.year, new Map());
}
if (!groupedDataLY.get(entry.year).has(entry.whs)) {
groupedDataLY.get(entry.year).set(entry.whs, {
dollars: 0, weight: 0, lines: 0
});
}
const whsData = groupedDataLY.get(entry.year).get(entry.whs);
whsData.dollars += entry.dollars;
whsData.weight += entry.weight;
whsData.lines += entry.lines;
});
}
// Set labels to sorted years or days
labels = Array.from(groupedData.keys()).sort();
// Helper function to get time key
function getTimeKey(yearLabel) {
if (yearLabel.includes("Q")) {
return yearLabel.split("-")[0] + "-";
} else if (yearLabel.match(/^\d{4}-\d{2}$/)) {
return yearLabel.split("-")[1]; // Get month part
} else if (/^\d{2}$/.test(yearLabel)) {
return yearLabel;
}
return yearLabel;
}
// Create color map
let uniqueKeys = new Set();
data.forEach(item => {
const timeKey = getTimeKey(item.year);
whsArray.forEach(whs => {
if (/^\d{2}$/.test(item.year)) {
// Daily case
uniqueKeys.add(`${whs}_Sales`);
uniqueKeys.add(`${whs}_Weight`);
uniqueKeys.add(`${whs}_Lines`);
} else {
// Yearly case
uniqueKeys.add(`${timeKey}_${whs}`);
}
});
});
uniqueKeys = [...uniqueKeys];
let colorMap = {};
let generatedColors = generateDistinctColors(uniqueKeys.length);
uniqueKeys.forEach((key, index) => {
colorMap[key] = generatedColors[index];
});
// Function to get color
function getDatasetColor(year, whs, metric, isLastYear = false) {
const timeKey = getTimeKey(year);
const key = /^\d{2}$/.test(year) ?
`${whs}_${metric}${isLastYear ? '_LY' : ''}` :
`${timeKey}_${whs}`;
return colorMap[key] || "rgba(200, 200, 200, 0.6)";
}
// Create datasets
whsArray.forEach(whs => {
// Function to create dataset with unique colors
const createDataset = (label, dataKey, stack, yAxisID, metric, isLastYear = false, dataGroup) => ({
type: 'line',
label: `${label}${isLastYear ? ' LY' : ''} (WHS ${whs} - ${metric})`,
data: labels.map(year => dataGroup.get(year)?.get(whs)?.[dataKey] || 0),
borderColor: labels.map(year => getDatasetColor(year, whs, metric, isLastYear)),
backgroundColor: labels.map(year => getDatasetColor(year, whs, metric, isLastYear)),
borderDash: isLastYear ? [10, 5] : undefined,
stack,
yAxisID,
tension: 0.3,
fill: false,
order: isLastYear ? 1 : 0
});
if (checkSales === 'on') {
// Current year data
datasets.push(createDataset("Sales", "dollars", "Sales", 'yLeft', "Sales", false, groupedData));
// Last year data for daily format
if ((dataFormat === "daily" || dataFormat === "yearly") && dataLY && dataLY.length > 0) {
datasets.push(createDataset("Sales", "dollars", "Sales_LY", 'yLeft', "Sales", true, groupedDataLY));
}
}
if (checkWeight === 'on') {
// Current year data
datasets.push(createDataset("Weight", "weight", "Weight", 'yLeft', "Weight", false, groupedData));
// Last year data for daily/yearly format
if ((dataFormat === "daily" || dataFormat === "yearly") && dataLY && dataLY.length > 0) {
datasets.push(createDataset("Weight", "weight", "Weight_LY", 'yLeft', "Weight", true, groupedDataLY));
}
}
if (checkLines === 'on') {
// Current year data
datasets.push(createDataset("Lines", "lines", "Lines", 'yRight', "Lines", false, groupedData));
// Last year data for daily/yearly format
if ((dataFormat === "daily" || dataFormat === "yearly") && dataLY && dataLY.length > 0) {
datasets.push(createDataset("Lines", "lines", "Lines_LY", 'yRight', "Lines", true, groupedDataLY));
}
}
});
}
// Generate Chart
window.execSummaryChart = new Chart(
document.getElementById('acquisitions'), {
type: 'line',
data: {
labels,
datasets
},
options: {
interaction: {
intersect: false,
mode: 'index'
},
responsive: true,
elements: {
point: {
radius: 5,
hoverRadius: 8
}
},
scales: {
yLeft: {
type: 'linear',
position: 'left',
title: {
display: true,
text: 'Sales & Weight'
},
beginAtZero: true
},
yRight: {
type: 'linear',
position: 'right',
title: {
display: true,
text: 'Lines'
},
grid: {
drawOnChartArea: false
},
beginAtZero: true
},
x: {
title: {
display: true,
text: dataFormat === "quarterly" ? 'Quarter' :
dataFormat === "monthly" ? 'Month' :
dataFormat === "daily" ? 'Day' : 'Year'
}
}
},
plugins: {
tooltip: {
callbacks: {
title: function(tooltipItems) {
return tooltipItems[0].label;
// return tooltipItems[0].raw.label;
}
}
},
legend: {
position: 'top',
labels: {
usePointStyle: true
}
}
}
}
}
);
}
// End Success
},
error: function(xhr, status, error) {
console.error("Error:", error);
// Display error message to user
Swal.fire({
icon: 'error',
title: 'Ajax Error',
text: 'Failed to load chart data. Please try again.',
confirmButtonText: 'OK'
});
}
});
// End Ajax
});
//// Trigger Click on init
$("#updateCharts").trigger("click");
});
</script>
@endsection