Rapport-Laravel/resources/views/adherencepackratioreport/report.blade.php

397 lines
18 KiB
PHP

@extends('layouts.vertical-individual', ['page_title' => $title ?? 'Adherence Pack Ratio Report'])
@section('css')
<style>
body .account-pages > .container {
max-width: 100%;
padding-left: 0;
padding-right: 0;
}
.adherence-pack-ratio-report-page .row {
--bs-gutter-x: 0;
margin-left: 0;
margin-right: 0;
}
.adherence-pack-ratio-report-page .card {
border-left: 0;
border-right: 0;
border-radius: 0;
margin-bottom: 0;
}
.adherence-pack-ratio-report-page .card-body {
padding-left: 0;
padding-right: 0;
}
.adherence-pack-ratio-report-page .page-header {
padding-left: 12px;
padding-right: 12px;
}
.adherence-pack-ratio-report-table th,
.adherence-pack-ratio-report-table td {
white-space: nowrap;
vertical-align: middle;
border: 1px solid #dee2e6;
}
.adherence-pack-ratio-report-table thead th {
text-align: center;
background: #f8f9fa;
}
.adherence-pack-ratio-report-table td.ratio-up {
background-color: #d4edda;
color: #155724;
font-weight: 600;
}
.adherence-pack-ratio-report-table td.ratio-down {
background-color: #f8d7da;
color: #721c24;
font-weight: 600;
}
.adherence-pack-ratio-report-scroll {
overflow-x: auto;
container-type: inline-size;
}
.adherence-pack-ratio-report-table .tm-col {
position: sticky;
left: 0;
z-index: 2;
background: #fff;
min-width: 120px;
max-width: min(50cqw, 50vw);
overflow: hidden;
text-overflow: ellipsis;
}
.adherence-pack-ratio-report-table .meta-col {
min-width: 120px;
}
.adherence-pack-ratio-report-table thead .tm-col {
z-index: 3;
background: #f8f9fa;
}
.adherence-pack-ratio-report-pagination {
padding: 12px 16px;
border-top: 1px solid #dee2e6;
background: #fff;
position: sticky;
bottom: 0;
z-index: 5;
}
.adherence-pack-ratio-report-pagination-top {
border-top: 0;
border-bottom: 1px solid #dee2e6;
}
.adherence-pack-ratio-report-pagination .pagination {
margin-bottom: 0;
}
#adherence-pack-ratio-report-loader {
align-items: center;
background: rgba(255, 255, 255, 0.85);
display: none;
flex-direction: column;
gap: 12px;
height: 100%;
justify-content: center;
left: 0;
position: fixed;
top: 0;
width: 100%;
z-index: 9999;
}
#adherence-pack-ratio-report-loader.is-visible {
display: flex;
}
#adherence-pack-ratio-report-loader img {
height: 64px;
width: 64px;
}
</style>
@endsection
@section('content')
@php
$pagination = $pagination ?? (object) ['page' => 1, 'per_page' => 50, 'total' => 0, 'total_pages' => 0];
$currentPage = (int) ($pagination->page ?? 1);
$totalPages = (int) ($pagination->total_pages ?? 0);
$perPage = (int) ($perPage ?? $pagination->per_page ?? 50);
$totalTms = (int) ($pagination->total ?? 0);
$search = trim((string) ($search ?? ''));
$type = $type ?? 'dpi';
$quartersOnly = !empty($quartersOnly);
$quarters = $quarters ?? [];
$quarter_data = $quarter_data ?? (object) [];
$months = $months ?? [];
$metrics = $metrics ?? [];
$quartersAfterMonth = [];
if (!$quartersOnly) {
foreach ($quarters as $quarter) {
$afterMonth = $quarter->after_month ?? null;
if ($afterMonth) {
$quartersAfterMonth[$afterMonth][] = $quarter;
}
}
}
$pageQuery = http_build_query(array_filter([
'token' => $token,
'type' => $type,
'per_page' => $perPage,
'search' => $search,
]));
$pageBase = $base_url . '/report?' . $pageQuery;
$metaColumnCount = 1;
@endphp
<div id="adherence-pack-ratio-report-loader" class="is-visible" aria-live="polite" aria-busy="true">
<img src="/images/loader-large.gif" alt="">
<span class="text-muted">Loading report...</span>
</div>
<div class="adherence-pack-ratio-report-page">
<div class="row mb-3 page-header">
<div class="col-md-6">
<h4 class="page-title">{{ $title }}</h4>
<p class="text-muted mb-0">
Foracort Campaign adherence pack ratio from April 2026 through the current month.
@if ($quartersOnly)
Showing quarterly ratio columns only (Q1 %, Q2 %, …).
Green = equal/increasing vs previous quarter; Red = decreasing.
@else
Showing {{ $typeLabel ?? strtoupper($type) }} metrics
({{ collect($metrics)->pluck('label')->implode(', ') }}), plus quarterly ratio columns.
@endif
</p>
@if ($totalTms > 0)
<p class="text-muted mb-0 small">
Showing {{ count($tms) }} of {{ number_format($totalTms) }} TMs
(page {{ $currentPage }} of {{ max($totalPages, 1) }})
@if ($search !== '')
matching "{{ $search }}"
@endif
</p>
@elseif ($search !== '')
<p class="text-muted mb-0 small">No TMs found matching "{{ $search }}".</p>
@endif
</div>
<div class="col-md-6 d-flex justify-content-end align-items-center gap-2 flex-wrap">
<form method="get" action="{{ $base_url }}/report" id="adherence-search-form" class="d-flex align-items-center gap-2 adherence-pack-ratio-report-nav-form">
<input type="hidden" name="token" value="{{ $token }}">
<input type="hidden" name="type" value="{{ $type }}">
<input type="hidden" name="page" value="1">
<input type="hidden" name="per_page" value="{{ $perPage }}">
<label for="adherence-report-search" class="form-label mb-0 small text-muted">Search</label>
<input type="search" name="search" id="adherence-report-search" class="form-control form-control-sm" style="width: 220px;" value="{{ $search }}" placeholder="TM name, employee code, zone...">
<button type="submit" class="btn btn-outline-secondary btn-sm">Apply</button>
@if ($search !== '')
<a href="{{ $base_url }}/report?{{ http_build_query(['token' => $token, 'type' => $type, 'per_page' => $perPage]) }}" class="btn btn-link btn-sm text-decoration-none">Clear</a>
@endif
</form>
<form method="get" action="{{ $base_url }}/report" id="adherence-per-page-form" class="d-flex align-items-center gap-2 adherence-pack-ratio-report-nav-form">
<input type="hidden" name="token" value="{{ $token }}">
<input type="hidden" name="type" value="{{ $type }}">
<input type="hidden" name="page" value="1">
@if ($search !== '')
<input type="hidden" name="search" value="{{ $search }}">
@endif
<label for="per_page" class="form-label mb-0 small text-muted">Per page</label>
<select name="per_page" id="per_page" class="form-select form-select-sm" style="width: auto;" onchange="this.form.submit()">
@foreach ([25, 50, 100, 200] as $size)
<option value="{{ $size }}"@if($perPage === $size) selected @endif>{{ $size }}</option>
@endforeach
</select>
</form>
<a href="{{ $base_url }}/export?{{ http_build_query(array_filter(['token' => $token, 'type' => $type, 'search' => $search])) }}" class="btn btn-dark btn-sm">
Export All
</a>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="card">
@include('adherencepackratioreport._pagination', ['paginationPosition' => 'adherence-pack-ratio-report-pagination-top'])
<div class="card-body adherence-pack-ratio-report-scroll">
<table class="table table-sm table-bordered adherence-pack-ratio-report-table mb-0">
<thead>
@if ($quartersOnly)
<tr>
<th class="tm-col">TM Name</th>
@foreach ($quarters as $quarter)
<th>Q{{ $quarter->q ?? preg_replace('/\D+/', '', $quarter->label ?? $quarter->key ?? '') }} %</th>
@endforeach
</tr>
@else
<tr>
<th class="tm-col" rowspan="2">TM Name</th>
@foreach ($months as $month)
<th colspan="{{ count($metrics) }}">{{ $month->label ?? $month->key }}</th>
@foreach ($quartersAfterMonth[$month->key ?? ''] ?? [] as $quarter)
<th rowspan="2">{{ preg_match('/%\s*$/', (string) ($quarter->label ?? '')) ? ($quarter->label ?? $quarter->key) : trim(($quarter->label ?? $quarter->key) . ' %') }}</th>
@endforeach
@endforeach
</tr>
<tr>
@foreach ($months as $month)
@foreach ($metrics as $metric)
<th>{{ $metric->label ?? $metric->id }}</th>
@endforeach
@endforeach
</tr>
@endif
</thead>
<tbody>
@forelse ($tms as $tm)
@php
$tmQuarterData = is_object($quarter_data) ? ($quarter_data->{$tm->id} ?? null) : null;
$parseRatio = function ($value) {
// Missing/blank values count as 0 for highlight comparisons only.
if ($value === null || $value === '' || $value === '-') {
return 0.0;
}
if (is_numeric($value)) {
return (float) $value;
}
$normalized = preg_replace('/[^0-9.\-]/', '', (string) $value);
return $normalized === '' || !is_numeric($normalized) ? 0.0 : (float) $normalized;
};
$prevQuarterRatio = null;
$hasPrevQuarter = false;
@endphp
<tr>
<td class="tm-col">{{ $tm->name ?? $tm->id }}</td>
@if ($quartersOnly)
@foreach ($quarters as $quarter)
@php
$quarterKey = $quarter->key ?? '';
$qValue = is_object($tmQuarterData) ? ($tmQuarterData->{$quarterKey} ?? null) : null;
$currentRatio = $parseRatio($qValue);
$ratioClass = '';
if ($hasPrevQuarter) {
$ratioClass = $currentRatio >= $prevQuarterRatio ? 'ratio-up' : 'ratio-down';
}
$prevQuarterRatio = $currentRatio;
$hasPrevQuarter = true;
@endphp
<td class="text-center {{ $ratioClass }}">{{ $qValue !== null && $qValue !== '' ? $qValue : '-' }}</td>
@endforeach
@else
@foreach ($months as $month)
@php
$monthKey = $month->key ?? '';
$tmData = $data->{$tm->id} ?? null;
$monthData = is_object($tmData) ? ($tmData->{$monthKey} ?? null) : null;
@endphp
@foreach ($metrics as $metric)
@php
$metricId = $metric->id ?? '';
$value = is_object($monthData) ? ($monthData->{$metricId} ?? null) : null;
@endphp
<td class="text-center">{{ $value !== null && $value !== '' ? $value : '-' }}</td>
@endforeach
@foreach ($quartersAfterMonth[$monthKey] ?? [] as $quarter)
@php
$quarterKey = $quarter->key ?? '';
$qValue = is_object($tmQuarterData) ? ($tmQuarterData->{$quarterKey} ?? null) : null;
$currentRatio = $parseRatio($qValue);
$ratioClass = '';
if ($hasPrevQuarter) {
$ratioClass = $currentRatio >= $prevQuarterRatio ? 'ratio-up' : 'ratio-down';
}
$prevQuarterRatio = $currentRatio;
$hasPrevQuarter = true;
@endphp
<td class="text-center {{ $ratioClass }}">{{ $qValue !== null && $qValue !== '' ? $qValue : '-' }}</td>
@endforeach
@endforeach
@endif
</tr>
@empty
<tr>
<td colspan="{{ $metaColumnCount + ($quartersOnly ? count($quarters) : (count($months) * max(count($metrics), 1) + count($quarters))) }}" class="text-center text-muted">
@if ($search !== '')
No TMs found matching "{{ $search }}".
@else
No TM data found.
@endif
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@include('adherencepackratioreport._pagination')
</div>
</div>
</div>
</div>
@endsection
@section('script-bottom')
<script>
(function () {
var loader = document.getElementById('adherence-pack-ratio-report-loader');
if (!loader) {
return;
}
function hideLoader() {
loader.classList.remove('is-visible');
loader.setAttribute('aria-busy', 'false');
}
function showLoader() {
loader.classList.add('is-visible');
loader.setAttribute('aria-busy', 'true');
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', hideLoader, { once: true });
} else {
hideLoader();
}
window.addEventListener('pageshow', function (event) {
if (event.persisted) {
hideLoader();
}
});
document.addEventListener('click', function (event) {
var link = event.target.closest('.adherence-pack-ratio-report-pagination a.page-link[href]:not([href="#"])');
if (link) {
showLoader();
}
});
var searchForm = document.getElementById('adherence-search-form');
if (searchForm) {
searchForm.addEventListener('submit', function () {
showLoader();
});
}
var perPageForm = document.getElementById('adherence-per-page-form');
if (perPageForm) {
perPageForm.addEventListener('change', function (event) {
if (event.target && event.target.name === 'per_page') {
showLoader();
}
});
}
})();
</script>
@endsection