364 lines
17 KiB
PHP
364 lines
17 KiB
PHP
@extends('layouts.app')
|
||
|
||
{{--
|
||
Laravel 12移行対応:区画別利用率状況ページ
|
||
旧Laravel 5.7から新Laravel 12への移行版
|
||
機能:駐輪場の利用率統計表示とフィルタリング
|
||
--}}
|
||
|
||
@section('title', '区画別利用率状況')
|
||
|
||
@section('content')
|
||
<div class="content-header">
|
||
<div class="container-fluid">
|
||
<div class="row mb-2">
|
||
<div class="col-lg-6">
|
||
<h1 class="m-0 text-dark">区画別利用率状況</h1>
|
||
</div><!-- /.col -->
|
||
<div class="col-lg-6">
|
||
<ol class="breadcrumb float-sm-right text-sm">
|
||
<li class="breadcrumb-item"><a href="{{ route('home') }}">ホーム</a></li>
|
||
<li class="breadcrumb-item active">区画別利用率状況</li>
|
||
</ol>
|
||
</div><!-- /.col -->
|
||
</div><!-- /.row -->
|
||
</div><!-- /.container-fluid -->
|
||
</div>
|
||
<!-- /.content-header -->
|
||
|
||
<!-- Main content -->
|
||
<section class="content">
|
||
<div class="container-fluid">
|
||
|
||
{{--
|
||
Laravel 12変更点:セッションメッセージの表示
|
||
エラーハンドリングの強化
|
||
--}}
|
||
@if(session('success'))
|
||
<div class="alert alert-success alert-dismissible">
|
||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||
<h4><i class="icon fa fa-check"></i> {{ __('成功') }}</h4>
|
||
{{ session('success') }}
|
||
</div>
|
||
@endif
|
||
|
||
@if(session('error'))
|
||
<div class="alert alert-danger alert-dismissible">
|
||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||
<h4><i class="icon fa fa-ban"></i> {{ __('エラー') }}</h4>
|
||
{{ session('error') }}
|
||
</div>
|
||
@endif
|
||
|
||
@if(session('warning'))
|
||
<div class="alert alert-warning alert-dismissible">
|
||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||
<h4><i class="icon fa fa-warning"></i> {{ __('警告') }}</h4>
|
||
{{ session('warning') }}
|
||
</div>
|
||
@endif
|
||
|
||
{{-- 絞り込みフォーム --}}
|
||
<div class="row">
|
||
<div class="col-lg-12">
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h3 class="card-title">絞り込み</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
{{--
|
||
Laravel 12変更点:CSRFトークンの自動処理
|
||
route() ヘルパーの使用
|
||
--}}
|
||
<form action="{{ route('using_status') }}" method="get" id="list-form">
|
||
@csrf
|
||
|
||
<!-- 行内垂直居中 -->
|
||
<div class="row align-items-center">
|
||
<!-- タグ列 -->
|
||
<div class="form-group col-2 d-flex align-items-center justify-content-start">
|
||
<label for="search_park" class="col-form-label text-dark mb-0">駐輪場</label>
|
||
</div><!-- /.form group -->
|
||
|
||
<!-- プルダウン -->
|
||
<div class="form-group col-4">
|
||
<div class="input-group">
|
||
<select name="park_id" class="form-control" id="search_park" onchange="this.form.submit()">
|
||
<option value="">全て</option>
|
||
{{--
|
||
Laravel 12変更点:Bladeディレクティブの最適化
|
||
データバインディングの改善
|
||
--}}
|
||
@if($parkList && $parkList->isNotEmpty())
|
||
@foreach($parkList as $park)
|
||
<option value="{{ $park->park_id }}"
|
||
{{ $selectedParkId == $park->park_id ? 'selected' : '' }}>
|
||
{{ $park->park_name }}
|
||
</option>
|
||
@endforeach
|
||
@endif
|
||
</select>
|
||
</div>
|
||
</div><!-- /.form group -->
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{{-- データ表示テーブル --}}
|
||
<div class="form col-lg-12 {{ empty($selectedParkId) ? 'd-none' : '' }}">
|
||
<div class="col-lg-12 row sample03-wrapper no_padding_right mb20">
|
||
<div class="col-lg-12 col-xl-12 col-md-12 col-sm-12 col-xs-12 table_right no_padding_right">
|
||
<div class="scroll">
|
||
<table class="table dataTable">
|
||
<thead>
|
||
<tr>
|
||
<th class="text-center align-middle">駐輪場</th>
|
||
<th class="text-center align-middle">車種</th>
|
||
<th class="text-center align-middle">限界収容台数</th>
|
||
<th class="text-center align-middle">現在収容台数</th>
|
||
<th class="text-center align-middle">空き</th>
|
||
<th class="text-center align-middle">利用率</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{{--
|
||
Laravel 12変更点:データ表示ロジックの改善
|
||
エラーハンドリングの追加
|
||
--}}
|
||
@if($hasData && $utilizationStats->isNotEmpty())
|
||
@php
|
||
$currentParkId = null;
|
||
$parkRowCount = 0;
|
||
@endphp
|
||
|
||
{{-- 駐輪場ごとの行数を事前計算 --}}
|
||
@foreach($utilizationStats->groupBy('park_id') as $parkId => $parkStats)
|
||
@php
|
||
$parkRowCounts[$parkId] = $parkStats->count();
|
||
@endphp
|
||
@endforeach
|
||
|
||
@foreach($utilizationStats as $index => $stat)
|
||
<tr>
|
||
{{-- 駐輪場名(rowspan処理) --}}
|
||
@if($currentParkId !== $stat->park_id)
|
||
@php
|
||
$currentParkId = $stat->park_id;
|
||
$rowspan = $parkRowCounts[$stat->park_id] ?? 1;
|
||
@endphp
|
||
<td rowspan="{{ $rowspan }}" class="text-center align-middle">
|
||
{{ $stat->park_name }}
|
||
</td>
|
||
@endif
|
||
|
||
{{-- 車種 --}}
|
||
<td class="text-center align-middle">
|
||
{{ $stat->psection_subject }}
|
||
</td>
|
||
|
||
{{-- 限界収容台数 --}}
|
||
<td class="text-center align-middle">
|
||
{{ number_format($stat->park_limit ?? 0) }}
|
||
</td>
|
||
|
||
{{-- 現在収容台数 --}}
|
||
<td class="text-center align-middle">
|
||
{{ number_format($stat->current_count ?? 0) }}
|
||
</td>
|
||
|
||
{{-- 空き台数 --}}
|
||
<td class="text-center align-middle">
|
||
{{ number_format($stat->available ?? 0) }}
|
||
</td>
|
||
|
||
{{-- 利用率 --}}
|
||
<td class="text-center align-middle">
|
||
{{ number_format($stat->usage_rate ?? 0, 1) }}%
|
||
</td>
|
||
</tr>
|
||
@endforeach
|
||
|
||
{{-- 合計行 --}}
|
||
<tr>
|
||
<td class="text-center align-middle"><strong>合計</strong></td>
|
||
<td class="text-center align-middle"></td>
|
||
<td class="text-center align-middle">
|
||
<strong>{{ number_format($totals['total_limit'] ?? 0) }}</strong>
|
||
</td>
|
||
<td class="text-center align-middle">
|
||
<strong>{{ number_format($totals['total_current'] ?? 0) }}</strong>
|
||
</td>
|
||
<td class="text-center align-middle">
|
||
<strong>{{ number_format($totals['total_available'] ?? 0) }}</strong>
|
||
</td>
|
||
<td class="text-center align-middle">
|
||
<strong>{{ number_format($totals['total_usage_rate'] ?? 0, 1) }}%</strong>
|
||
</td>
|
||
</tr>
|
||
@else
|
||
{{-- データなしの表示 --}}
|
||
<tr>
|
||
<td colspan="6" class="text-center align-middle" style="padding: 2rem;">
|
||
@if($isSearchRequest)
|
||
<div class="alert alert-info mb-0">
|
||
<i class="fa fa-info-circle"></i>
|
||
選択された条件に該当するデータが見つかりませんでした。<br>
|
||
別の駐輪場を選択するか、「全て」を選択してください。
|
||
</div>
|
||
@else
|
||
<div class="alert alert-warning mb-0">
|
||
<i class="fa fa-exclamation-triangle"></i>
|
||
利用率データが見つかりませんでした。<br>
|
||
データベースに駐輪場と車種分類の設定を確認してください。
|
||
</div>
|
||
@endif
|
||
</td>
|
||
</tr>
|
||
@endif
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- /.row -->
|
||
</div><!-- /.container-fluid -->
|
||
</section>
|
||
<!-- /.content -->
|
||
@endsection
|
||
|
||
{{--
|
||
Laravel 12変更点:スクリプトセクションの分離
|
||
JavaScript処理の最適化
|
||
--}}
|
||
@section('scripts')
|
||
<script>
|
||
$(function () {
|
||
$('#search_park').select2({
|
||
placeholder: '駐輪場を選択してください',
|
||
allowClear: true,
|
||
language: 'ja'
|
||
});
|
||
|
||
// 変更時は GET リダイレクトで遷移し、park_id を確実にサーバへ渡す
|
||
$('#search_park').on('change', function() {
|
||
var v = $(this).val();
|
||
var base = '{{ route("using_status") }}';
|
||
if (v && v !== '') {
|
||
// 「第一駐輪場」など具体値 → クエリ付きで遷移
|
||
window.location.href = base + '?park_id=' + encodeURIComponent(v);
|
||
} else {
|
||
// 「全て」→ ベースURLへ(表は表示しない)
|
||
window.location.href = base;
|
||
}
|
||
});
|
||
|
||
// フォーム送信時のローディング表示
|
||
$('#list-form').on('submit', function() {
|
||
// 送信ボタンを無効化
|
||
$('input[type="submit"]').prop('disabled', true);
|
||
|
||
// ローディング表示
|
||
if (typeof toastr !== 'undefined') {
|
||
toastr.info('データを取得中です...', '処理中');
|
||
}
|
||
});
|
||
|
||
// Laravel 12対応:DataTableの初期化(レスポンシブ対応)
|
||
if ($('.dataTable tbody tr').length > 1) {
|
||
$('.dataTable').DataTable({
|
||
paging: false, // ページングを無効化
|
||
searching: false, // 検索を無効化
|
||
ordering: false, // ソートを無効化
|
||
info: false, // 情報表示を無効化
|
||
responsive: true, // レスポンシブ対応
|
||
language: {
|
||
"emptyTable": "データがありません"
|
||
}
|
||
});
|
||
}
|
||
|
||
// Laravel 12対応:ツールチップの初期化
|
||
$('[data-toggle="tooltip"]').tooltip();
|
||
|
||
// 利用率に応じた色分け表示(オプション機能)
|
||
$('.dataTable tbody tr').each(function() {
|
||
var usageCell = $(this).find('td:last-child');
|
||
var usageText = usageCell.text();
|
||
var usageRate = parseFloat(usageText.replace('%', ''));
|
||
|
||
if (!isNaN(usageRate)) {
|
||
if (usageRate >= 90) {
|
||
usageCell.addClass('text-danger font-weight-bold');
|
||
} else if (usageRate >= 70) {
|
||
usageCell.addClass('text-warning font-weight-bold');
|
||
} else if (usageRate >= 50) {
|
||
usageCell.addClass('text-success');
|
||
}
|
||
}
|
||
});
|
||
|
||
// エラー・成功メッセージの自動非表示
|
||
setTimeout(function() {
|
||
$('.alert-dismissible').fadeOut('slow');
|
||
}, 5000);
|
||
});
|
||
|
||
// Laravel 12対応:CSVエクスポート機能(将来拡張用)
|
||
function exportToCsv() {
|
||
var selectedParkId = $('#search_park').val();
|
||
var exportUrl = '{{ route("using_status") }}/export';
|
||
|
||
if (selectedParkId) {
|
||
exportUrl += '?park_id=' + selectedParkId;
|
||
}
|
||
|
||
window.location.href = exportUrl;
|
||
}
|
||
|
||
// Laravel 12対応:印刷機能
|
||
function printTable() {
|
||
window.print();
|
||
}
|
||
</script>
|
||
|
||
{{-- 印刷用CSS --}}
|
||
<style>
|
||
@media print {
|
||
.card-header, .breadcrumb, .alert, #search_ark, .btn {
|
||
display: none !important;
|
||
}
|
||
|
||
.content-wrapper {
|
||
margin: 0 !important;
|
||
padding: 0 !important;
|
||
}
|
||
|
||
.table {
|
||
font-size: 12px;
|
||
}
|
||
|
||
.table th, .table td {
|
||
border: 1px solid #000 !important;
|
||
padding: 4px !important;
|
||
}
|
||
}
|
||
|
||
/* Laravel 12対応:利用率表示の色分け */
|
||
.text-danger { color: #dc3545 !important; }
|
||
.text-warning { color: #ffc107 !important; }
|
||
.text-success { color: #28a745 !important; }
|
||
|
||
/* レスポンシブテーブル対応 */
|
||
@media (max-width: 768px) {
|
||
.table-responsive { border: none; }
|
||
.table td, .table th {
|
||
font-size: 0.875rem;
|
||
padding: 0.5rem 0.25rem;
|
||
}
|
||
}
|
||
</style>
|
||
@endsection |