Add a month filter to the custom dashboard and stop the OTP form from being submitted twice.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
957beb7ae0
commit
a462bb3100
|
|
@ -123,7 +123,7 @@ class ZenovaReportExport implements FromArray, WithEvents
|
||||||
if ($column['type'] === 'text') {
|
if ($column['type'] === 'text') {
|
||||||
$sheet->getStyle($dataRange)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT);
|
$sheet->getStyle($dataRange)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT);
|
||||||
} elseif ($column['type'] === 'percent') {
|
} elseif ($column['type'] === 'percent') {
|
||||||
$sheet->getStyle($dataRange)->getNumberFormat()->setFormatCode('0.00"%"');
|
$sheet->getStyle($dataRange)->getNumberFormat()->setFormatCode('0.0"%"');
|
||||||
$sheet->getStyle($dataRange)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT);
|
$sheet->getStyle($dataRange)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT);
|
||||||
} else {
|
} else {
|
||||||
$sheet->getStyle($dataRange)->getNumberFormat()->setFormatCode('#,##0');
|
$sheet->getStyle($dataRange)->getNumberFormat()->setFormatCode('#,##0');
|
||||||
|
|
@ -134,9 +134,9 @@ class ZenovaReportExport implements FromArray, WithEvents
|
||||||
$col += $span;
|
$col += $span;
|
||||||
}
|
}
|
||||||
|
|
||||||
$sheet->getRowDimension(1)->setRowHeight(22);
|
$sheet->getRowDimension(1)->setRowHeight(36);
|
||||||
$sheet->getRowDimension(2)->setRowHeight(36);
|
$sheet->getRowDimension(2)->setRowHeight(36);
|
||||||
$sheet->freezePane('A3');
|
$sheet->freezePane('C3');
|
||||||
|
|
||||||
if (!empty($this->totals)) {
|
if (!empty($this->totals)) {
|
||||||
$sheet->getStyle('A' . $rowCount . ':' . $lastCol . $rowCount)->applyFromArray([
|
$sheet->getStyle('A' . $rowCount . ':' . $lastCol . $rowCount)->applyFromArray([
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ class ZenovaReportController extends Controller
|
||||||
$this->_service = 'reports';
|
$this->_service = 'reports';
|
||||||
$this->_module = 'zenova_reports';
|
$this->_module = 'zenova_reports';
|
||||||
$this->_viewname = 'zenovareport';
|
$this->_viewname = 'zenovareport';
|
||||||
$this->_title = 'Glipiq & Sitazit Dashboard';
|
$this->_title = 'Custom Dashboard';
|
||||||
$this->_base_url = '/zenovareport';
|
$this->_base_url = '/zenovareport';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -35,11 +35,14 @@ class ZenovaReportController extends Controller
|
||||||
public function dashboard(Request $request)
|
public function dashboard(Request $request)
|
||||||
{
|
{
|
||||||
$level = $this->level($request);
|
$level = $this->level($request);
|
||||||
$built = $this->build($level);
|
$month = $this->month($request);
|
||||||
|
$built = $this->build($level, $month);
|
||||||
|
|
||||||
return view('zenovareport.dashboard', [
|
return view('zenovareport.dashboard', [
|
||||||
'title' => $this->_title,
|
'title' => $this->_title,
|
||||||
'level' => $level,
|
'level' => $level,
|
||||||
|
'month' => $month,
|
||||||
|
'months' => $this->months(),
|
||||||
'groups' => $built['groups'],
|
'groups' => $built['groups'],
|
||||||
'body' => $built['body'],
|
'body' => $built['body'],
|
||||||
'totals' => $built['totals'],
|
'totals' => $built['totals'],
|
||||||
|
|
@ -50,10 +53,11 @@ class ZenovaReportController extends Controller
|
||||||
public function export(Request $request)
|
public function export(Request $request)
|
||||||
{
|
{
|
||||||
$level = $this->level($request);
|
$level = $this->level($request);
|
||||||
$built = $this->build($level);
|
$month = $this->month($request);
|
||||||
|
$built = $this->build($level, $month);
|
||||||
|
|
||||||
if ($built['error']) {
|
if ($built['error']) {
|
||||||
return redirect('/zenovareport/dashboard?level=' . $level)->with('error', $built['error']);
|
return redirect('/zenovareport/dashboard?level=' . $level . '&month=' . urlencode($month))->with('error', $built['error']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Excel::download(
|
return Excel::download(
|
||||||
|
|
@ -69,24 +73,51 @@ class ZenovaReportController extends Controller
|
||||||
return ($level >= 1 && $level <= 5) ? $level : 3;
|
return ($level >= 1 && $level <= 5) ? $level : 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function build(int $level): array
|
private function months(): array
|
||||||
|
{
|
||||||
|
$now = now()->startOfMonth();
|
||||||
|
$startYear = $now->month >= 4 ? $now->year : $now->year - 1;
|
||||||
|
$cursor = now()->setDate($startYear, 4, 1)->startOfMonth();
|
||||||
|
$months = [];
|
||||||
|
while ($cursor->lte($now)) {
|
||||||
|
$months[$cursor->format('Y-m')] = $cursor->format('F Y');
|
||||||
|
$cursor->addMonth();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $months;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function month(Request $request): string
|
||||||
|
{
|
||||||
|
$months = $this->months();
|
||||||
|
$selected = (string) $request->input('month', '');
|
||||||
|
if (isset($months[$selected])) {
|
||||||
|
return $selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (string) array_key_last($months);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function build(int $level, string $month): array
|
||||||
{
|
{
|
||||||
$response = LambdaRequest::signedrequest(
|
$response = LambdaRequest::signedrequest(
|
||||||
'get',
|
'get',
|
||||||
'reports',
|
'reports',
|
||||||
'zenova_reports/glipiqsitazit_report',
|
'zenova_reports/glipiqsitazit_report',
|
||||||
['level' => (string) $level],
|
['level' => (string) $level, 'month' => $month],
|
||||||
120
|
120
|
||||||
);
|
);
|
||||||
|
|
||||||
$rows = [];
|
$rows = [];
|
||||||
$error = null;
|
$error = null;
|
||||||
if (!$response || ($response->status ?? '') !== 'success') {
|
if (!$response || ($response->status ?? '') !== 'success') {
|
||||||
$error = $response->message ?? 'Unable to load the Glipiq & Sitazit report.';
|
$error = $response->message ?? 'Unable to load the Custom Dashboard.';
|
||||||
} else {
|
} else {
|
||||||
$rows = is_array($response->data ?? null) ? $response->data : [];
|
$rows = is_array($response->data ?? null) ? $response->data : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$rows = $this->sortRows($rows);
|
||||||
|
|
||||||
$groups = $this->groups($rows);
|
$groups = $this->groups($rows);
|
||||||
$columns = [];
|
$columns = [];
|
||||||
foreach ($groups as $group) {
|
foreach ($groups as $group) {
|
||||||
|
|
@ -153,6 +184,70 @@ class ZenovaReportController extends Controller
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function sortRows(array $rows): array
|
||||||
|
{
|
||||||
|
usort($rows, function ($left, $right) {
|
||||||
|
$zoneLeft = $this->zoneName($left);
|
||||||
|
$zoneRight = $this->zoneName($right);
|
||||||
|
if ($zoneLeft === '' && $zoneRight !== '') {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if ($zoneRight === '' && $zoneLeft !== '') {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
$zone = strcasecmp($zoneLeft, $zoneRight);
|
||||||
|
if ($zone !== 0) {
|
||||||
|
return $zone;
|
||||||
|
}
|
||||||
|
$users = $this->fsoCount($right) <=> $this->fsoCount($left);
|
||||||
|
if ($users !== 0) {
|
||||||
|
return $users;
|
||||||
|
}
|
||||||
|
|
||||||
|
return strcasecmp($this->rowName($left), $this->rowName($right));
|
||||||
|
});
|
||||||
|
|
||||||
|
return $rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function zoneName($row): string
|
||||||
|
{
|
||||||
|
$parents = (array) ($row->parent_positions ?? []);
|
||||||
|
foreach ($parents as $key => $value) {
|
||||||
|
if (strcasecmp((string) $key, 'Zone') === 0 && $value !== null && $value !== '') {
|
||||||
|
return (string) $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$demo = (array) ($row->{'Demography & Details'} ?? []);
|
||||||
|
foreach ($demo as $key => $value) {
|
||||||
|
if (strcasecmp((string) $key, 'Name of Zone') === 0 && $value !== null && $value !== '') {
|
||||||
|
return (string) $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function fsoCount($row): float
|
||||||
|
{
|
||||||
|
$value = $row->{'Demography & Details'}->{'No. of FSOs'} ?? 0;
|
||||||
|
|
||||||
|
return is_numeric($value) ? (float) $value : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function rowName($row): string
|
||||||
|
{
|
||||||
|
$demo = (array) ($row->{'Demography & Details'} ?? []);
|
||||||
|
foreach ($demo as $key => $value) {
|
||||||
|
if (strpos((string) $key, 'Name of ') === 0) {
|
||||||
|
return (string) $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (string) ($row->position_name ?? '');
|
||||||
|
}
|
||||||
|
|
||||||
private function groups(array $rows): array
|
private function groups(array $rows): array
|
||||||
{
|
{
|
||||||
$nameKey = 'Name of Region';
|
$nameKey = 'Name of Region';
|
||||||
|
|
@ -197,7 +292,7 @@ class ZenovaReportController extends Controller
|
||||||
];
|
];
|
||||||
|
|
||||||
$groups = [
|
$groups = [
|
||||||
['label' => 'Demography & Details', 'tone' => 'demo', 'columns' => $demoColumns],
|
['label' => 'Demography & Details (Data as per Yesterday)', 'tone' => 'demo', 'columns' => $demoColumns],
|
||||||
];
|
];
|
||||||
|
|
||||||
$first = isset($rows[0]) ? $rows[0] : null;
|
$first = isset($rows[0]) ? $rows[0] : null;
|
||||||
|
|
@ -247,10 +342,13 @@ class ZenovaReportController extends Controller
|
||||||
if (strcasecmp($currentType, $type) === 0) {
|
if (strcasecmp($currentType, $type) === 0) {
|
||||||
$column = $this->column($type, 'Demography & Details', $nameKey, 'text');
|
$column = $this->column($type, 'Demography & Details', $nameKey, 'text');
|
||||||
$column['is_total'] = true;
|
$column['is_total'] = true;
|
||||||
$columns[] = $column;
|
} else {
|
||||||
continue;
|
$column = $this->column($type, 'parent_positions', $type, 'text');
|
||||||
}
|
}
|
||||||
$columns[] = $this->column($type, 'parent_positions', $type, 'text');
|
if (in_array($type, ['Zone', 'Region'], true)) {
|
||||||
|
$column['sticky'] = strtolower($type);
|
||||||
|
}
|
||||||
|
$columns[] = $column;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $columns;
|
return $columns;
|
||||||
|
|
@ -337,7 +435,7 @@ class ZenovaReportController extends Controller
|
||||||
}
|
}
|
||||||
$number = (float) $value;
|
$number = (float) $value;
|
||||||
if ($type === 'percent') {
|
if ($type === 'percent') {
|
||||||
return number_format($number, 2) . '%';
|
return number_format($number, 1) . '%';
|
||||||
}
|
}
|
||||||
if (floor($number) == $number) {
|
if (floor($number) == $number) {
|
||||||
return number_format($number, 0);
|
return number_format($number, 0);
|
||||||
|
|
|
||||||
|
|
@ -101,14 +101,17 @@
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-0">
|
<div class="mt-0">
|
||||||
<form method="POST" action="">
|
@if (session('message'))
|
||||||
|
<div class="alert alert-danger" role="alert">{{ session('message') }}</div>
|
||||||
|
@endif
|
||||||
|
<form method="POST" action="" id="otp-form">
|
||||||
@csrf
|
@csrf
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
<label for="otp" class="form-label">OTP</label>
|
<label for="otp" class="form-label">OTP</label>
|
||||||
<input class="form-control" type="text" id="otp" name="otp" required="" placeholder="Enter OTP">
|
<input class="form-control" type="text" id="otp" name="otp" required="" placeholder="Enter OTP">
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<button class="btn btn-primary btn-block waves-effect waves-light" type="submit"> Verify OTP </button>
|
<button class="btn btn-primary btn-block waves-effect waves-light" type="submit" id="verify-otp"> Verify OTP </button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
@ -136,7 +139,26 @@
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
@include('layouts.shared/footer-script')
|
@include('layouts.shared/footer-script')
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
var form = document.getElementById('otp-form');
|
||||||
|
var button = document.getElementById('verify-otp');
|
||||||
|
if (!form || !button) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var submitting = false;
|
||||||
|
form.addEventListener('submit', function (event) {
|
||||||
|
if (submitting) {
|
||||||
|
event.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
submitting = true;
|
||||||
|
setTimeout(function () {
|
||||||
|
button.disabled = true;
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -194,7 +194,7 @@
|
||||||
<a href="/zenovareport/dashboard">
|
<a href="/zenovareport/dashboard">
|
||||||
<i data-feather="file-text"></i>
|
<i data-feather="file-text"></i>
|
||||||
<span>
|
<span>
|
||||||
Glipiq & Sitazit </span>
|
Custom Dashboard </span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@endif
|
@endif
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,47 @@
|
||||||
}
|
}
|
||||||
.zenova-table thead tr:first-child th {
|
.zenova-table thead tr:first-child th {
|
||||||
top: 0;
|
top: 0;
|
||||||
|
height: 56px;
|
||||||
}
|
}
|
||||||
.zenova-table thead tr:nth-child(2) th {
|
.zenova-table thead tr:nth-child(2) th {
|
||||||
top: 34px;
|
top: 56px;
|
||||||
|
}
|
||||||
|
.zenova-table thead tr:nth-child(2) th.sticky-zone,
|
||||||
|
.zenova-table tbody td.sticky-zone {
|
||||||
|
position: sticky;
|
||||||
|
left: 0;
|
||||||
|
min-width: 160px;
|
||||||
|
width: 160px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.zenova-table thead tr:nth-child(2) th.sticky-region,
|
||||||
|
.zenova-table tbody td.sticky-region {
|
||||||
|
position: sticky;
|
||||||
|
left: 160px;
|
||||||
|
min-width: 160px;
|
||||||
|
width: 160px;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 2px 0 0 #e5e7eb;
|
||||||
|
}
|
||||||
|
.zenova-table tbody td.sticky-zone,
|
||||||
|
.zenova-table tbody td.sticky-region {
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.zenova-table tr.totals td.sticky-zone,
|
||||||
|
.zenova-table tr.totals td.sticky-region {
|
||||||
|
background: #f8f9fa;
|
||||||
|
}
|
||||||
|
.zenova-table thead tr:first-child th.sticky-group {
|
||||||
|
position: sticky;
|
||||||
|
left: 0;
|
||||||
|
z-index: 6;
|
||||||
|
min-width: 320px;
|
||||||
|
white-space: normal;
|
||||||
|
box-shadow: 2px 0 0 #e5e7eb;
|
||||||
|
}
|
||||||
|
.zenova-table thead th.sticky-zone,
|
||||||
|
.zenova-table thead th.sticky-region {
|
||||||
|
z-index: 5;
|
||||||
}
|
}
|
||||||
.zenova-table td {
|
.zenova-table td {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
|
@ -54,11 +92,15 @@
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 p-2">
|
<div class="col-12 p-2 d-flex flex-wrap justify-content-between align-items-center gap-2">
|
||||||
<h4 class="page-title">{{ $title }}</h4>
|
<h4 class="page-title mb-0">{{ $title }}</h4>
|
||||||
</div>
|
<form method="get" action="/zenovareport/dashboard" class="d-flex flex-wrap align-items-center gap-2">
|
||||||
<div class="col-md-6 p-2 d-flex justify-content-end align-items-center">
|
<label for="month" class="mb-0">Month</label>
|
||||||
<form method="get" action="/zenovareport/dashboard" class="d-flex align-items-center gap-2">
|
<select class="form-select" name="month" id="month" style="width: 160px;" onchange="this.form.submit()">
|
||||||
|
@foreach ($months as $value => $label)
|
||||||
|
<option value="{{ $value }}" {{ $month === $value ? 'selected' : '' }}>{{ $label }}</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
<label for="level" class="mb-0">Level</label>
|
<label for="level" class="mb-0">Level</label>
|
||||||
<select class="form-select" name="level" id="level" style="width: 110px;" onchange="this.form.submit()">
|
<select class="form-select" name="level" id="level" style="width: 110px;" onchange="this.form.submit()">
|
||||||
@for ($option = 1; $option <= 5; $option++)
|
@for ($option = 1; $option <= 5; $option++)
|
||||||
|
|
@ -67,9 +109,13 @@
|
||||||
</option>
|
</option>
|
||||||
@endfor
|
@endfor
|
||||||
</select>
|
</select>
|
||||||
<a href="/zenovareport/export?level={{ $level }}" class="btn btn-primary">Export</a>
|
<a href="/zenovareport/dashboard?level={{ $level }}&month={{ $month }}" class="btn btn-outline-secondary">Refresh</a>
|
||||||
|
<a href="/zenovareport/export?level={{ $level }}&month={{ $month }}" class="btn btn-primary">Export</a>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-12 px-2">
|
||||||
|
<p class="text-muted mb-2">Please refresh before downloading the latest report.</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (session()->has('error'))
|
@if (session()->has('error'))
|
||||||
|
|
@ -86,15 +132,31 @@
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@foreach ($groups as $group)
|
@foreach ($groups as $group)
|
||||||
<th class="tone-{{ $group['tone'] }}" colspan="{{ count($group['columns']) }}">
|
@php
|
||||||
{{ $group['label'] }}
|
$stickyCount = 0;
|
||||||
</th>
|
foreach ($group['columns'] as $column) {
|
||||||
|
if (!empty($column['sticky'])) {
|
||||||
|
$stickyCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$scrollCount = count($group['columns']) - $stickyCount;
|
||||||
|
@endphp
|
||||||
|
@if ($stickyCount > 0)
|
||||||
|
<th class="tone-{{ $group['tone'] }} sticky-group" colspan="{{ $stickyCount }}">
|
||||||
|
{{ $group['label'] }}
|
||||||
|
</th>
|
||||||
|
@endif
|
||||||
|
@if ($scrollCount > 0)
|
||||||
|
<th class="tone-{{ $group['tone'] }}" colspan="{{ $scrollCount }}">
|
||||||
|
{{ $stickyCount > 0 ? '' : $group['label'] }}
|
||||||
|
</th>
|
||||||
|
@endif
|
||||||
@endforeach
|
@endforeach
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@foreach ($groups as $group)
|
@foreach ($groups as $group)
|
||||||
@foreach ($group['columns'] as $column)
|
@foreach ($group['columns'] as $column)
|
||||||
<th class="tone-{{ $group['tone'] }} {{ $column['type'] === 'text' ? 'text-cell' : '' }}">
|
<th class="tone-{{ $group['tone'] }} {{ $column['type'] === 'text' ? 'text-cell' : '' }} {{ !empty($column['sticky']) ? 'sticky-'.$column['sticky'] : '' }}">
|
||||||
{{ $column['label'] }}
|
{{ $column['label'] }}
|
||||||
</th>
|
</th>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
|
@ -107,7 +169,7 @@
|
||||||
@php $cellIndex = 0; @endphp
|
@php $cellIndex = 0; @endphp
|
||||||
@foreach ($groups as $group)
|
@foreach ($groups as $group)
|
||||||
@foreach ($group['columns'] as $column)
|
@foreach ($group['columns'] as $column)
|
||||||
<td class="{{ $column['type'] === 'text' ? 'text-cell' : '' }}">
|
<td class="{{ $column['type'] === 'text' ? 'text-cell' : '' }} {{ !empty($column['sticky']) ? 'sticky-'.$column['sticky'] : '' }}">
|
||||||
{{ $line[$cellIndex] ?? '' }}
|
{{ $line[$cellIndex] ?? '' }}
|
||||||
</td>
|
</td>
|
||||||
@php $cellIndex++; @endphp
|
@php $cellIndex++; @endphp
|
||||||
|
|
@ -126,7 +188,7 @@
|
||||||
@php $cellIndex = 0; @endphp
|
@php $cellIndex = 0; @endphp
|
||||||
@foreach ($groups as $group)
|
@foreach ($groups as $group)
|
||||||
@foreach ($group['columns'] as $column)
|
@foreach ($group['columns'] as $column)
|
||||||
<td class="{{ $column['type'] === 'text' ? 'text-cell' : '' }}">
|
<td class="{{ $column['type'] === 'text' ? 'text-cell' : '' }} {{ !empty($column['sticky']) ? 'sticky-'.$column['sticky'] : '' }}">
|
||||||
{{ $totals[$cellIndex] ?? '' }}
|
{{ $totals[$cellIndex] ?? '' }}
|
||||||
</td>
|
</td>
|
||||||
@php $cellIndex++; @endphp
|
@php $cellIndex++; @endphp
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue