348 lines
17 KiB
PHP
348 lines
17 KiB
PHP
@extends('layouts.app')
|
||
@section('title', '定期予約マスタ')
|
||
|
||
@section('content')
|
||
<style>
|
||
/* 画面全体のフォント/サイズを統一(やや小さめ) */
|
||
.rv-page,
|
||
.rv-page .card,
|
||
.rv-page .form-control,
|
||
.rv-page .btn,
|
||
.rv-page table,
|
||
.rv-page .breadcrumb {
|
||
font-family: "Noto Sans JP","Hiragino Kaku Gothic ProN","Meiryo",system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
|
||
font-size: 13px;
|
||
line-height: 1.45;
|
||
}
|
||
|
||
/* フィルター:1行=左ラベル/右入力、を縦に積む */
|
||
.rv-filter .field{display:flex;align-items:center;margin-bottom:.6rem;}
|
||
.rv-filter .label{flex:0 0 170px;margin:0;color:#333;font-weight:600;white-space:nowrap;}
|
||
.rv-filter .input{flex:1 1 auto;min-width:220px;}
|
||
.rv-filter .form-control{height:calc(2.0rem + 2px);padding:.25rem .5rem;}
|
||
.rv-filter .inline-range{display:flex;gap:.5rem;align-items:center;}
|
||
.rv-filter .tilde{color:#666;}
|
||
|
||
/* 一覧テーブル:斑馬(ゼブラ)無し、やや詰め気味 */
|
||
.rv-table th,.rv-table td{padding:.35rem .5rem;font-size:12px;}
|
||
.rv-table thead th{white-space:nowrap;background:#eeeeee;}
|
||
.rv-table thead th.sortable{cursor:pointer;text-decoration:none;}
|
||
.rv-table thead th,
|
||
.rv-table thead th .sort-icon,
|
||
.rv-table thead th .sort-icon .up,
|
||
.rv-table thead th .sort-icon .down{
|
||
text-decoration:none !important;
|
||
}
|
||
.rv-table thead th.sorting_asc .sort-icon .up,
|
||
.rv-table thead th.sorting_desc .sort-icon .down{color:#000;}
|
||
.rv-table thead th .sort-icon{
|
||
display:inline-flex;
|
||
align-items:center;
|
||
flex-direction:row;
|
||
gap:2px;
|
||
font-size:11px;
|
||
color:#b5b5b5;
|
||
letter-spacing: -5px; /* ↑↓を詰める */
|
||
text-decoration: none; /* _削除 */
|
||
}
|
||
|
||
/* ツールバー:左にボタン群、右にページャ */
|
||
.rv-toolbar{display:flex;align-items:center;justify-content:space-between;gap:.75rem;flex-wrap:wrap;}
|
||
.rv-toolbar .btn+.btn{margin-left:.4rem;}
|
||
|
||
/* 操作セルの背景色(定期契約マスタと同じ雰囲気) */
|
||
.op-cell{background:#faebd7;}
|
||
|
||
</style>
|
||
|
||
<div class="rv-page">
|
||
{{-- パンくず・ヘッダ --}}
|
||
<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>
|
||
<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>
|
||
</div>
|
||
@if(session('success'))
|
||
<div class="alert alert-success py-2 px-3 my-2">{{ session('success') }}</div>
|
||
@endif
|
||
@if($errors->any())
|
||
<div class="alert alert-danger py-2 px-3 my-2">
|
||
<ul class="mb-0">
|
||
@foreach($errors->all() as $e)<li>{{ $e }}</li>@endforeach
|
||
</ul>
|
||
</div>
|
||
@endif
|
||
<p class="text-muted mb-0">※この画面では予約情報の検索・一括削除が行えます。</p>
|
||
</div>
|
||
</div>
|
||
|
||
<section class="content">
|
||
<div class="container-fluid">
|
||
|
||
{{-- 絞り込みフィルター(ここは前回どおり。必要に応じて調整可) --}}
|
||
<div class="card rv-filter">
|
||
<div class="card-header"><h3 class="card-title">絞り込みフィルター</h3></div>
|
||
<div class="card-body">
|
||
<form action="{{ route('reserves') }}" method="post" id="filter-form">
|
||
@csrf
|
||
<input type="hidden" name="sort" id="sort" value="{{ $sort }}">
|
||
<input type="hidden" name="sort_type" id="sort_type" value="{{ $sort_type }}">
|
||
|
||
<div class="row">
|
||
<div class="col-lg-6">
|
||
<div class="field">
|
||
<label class="label">利用者ID</label>
|
||
<div class="input">
|
||
<input type="text" class="form-control" name="user_id"
|
||
value="{{ $user_id ?? '' }}" placeholder="123456">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="field">
|
||
<label class="label">有効フラグ</label>
|
||
<div class="input">
|
||
<select name="valid_flag" class="form-control">
|
||
<option value="">全て</option>
|
||
<option value="1" {{ (isset($valid_flag) && (string)$valid_flag==='1')?'selected':'' }}>有効</option>
|
||
<option value="0" {{ (isset($valid_flag) && (string)$valid_flag==='0')?'selected':'' }}>無効</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="field">
|
||
<label class="label">メール送信日時</label>
|
||
<div class="input inline-range">
|
||
<input type="datetime-local" class="form-control" name="mail_sent_from"
|
||
value="{{ $mail_sent_from ?? '' }}">
|
||
<span class="tilde">~</span>
|
||
<input type="datetime-local" class="form-control" name="mail_sent_to"
|
||
value="{{ $mail_sent_to ?? '' }}">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-lg-6">
|
||
<div class="field">
|
||
<label class="label">駐輪場</label>
|
||
<div class="input">
|
||
<select name="park_id" class="form-control">
|
||
<option value="">全て</option>
|
||
@if(!empty($parkOptions) && is_iterable($parkOptions))
|
||
@foreach($parkOptions as $pid => $pname)
|
||
<option value="{{ $pid }}" {{ (isset($park_id) && (string)$park_id===(string)$pid)?'selected':'' }}>
|
||
{{ $pname }}
|
||
</option>
|
||
@endforeach
|
||
@endif
|
||
</select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mt-2">
|
||
<button type="submit" class="btn btn-default">絞り込み</button>
|
||
<a href="{{ route('reserves') }}" class="btn btn-default">解除</a>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
{{-- ツールバー:左=ボタン群、右=ページャ(CSV出力→削除) --}}
|
||
<div class="rv-toolbar mb-2">
|
||
<div class="left">
|
||
<a href="{{ route('reserves_add') }}" class="btn btn-sm btn-default">新規</a>
|
||
<button type="button" class="btn btn-sm btn-default" id="btnBulkDel">削除</button>
|
||
</div>
|
||
<div class="right">
|
||
{{ $list->appends(['sort'=>$sort,'sort_type'=>$sort_type])->links('pagination') }}
|
||
</div>
|
||
</div>
|
||
|
||
{{-- 一覧テーブル(先頭列の並び替えを削除/操作列にチェック+編集) --}}
|
||
<div class="card">
|
||
<form id="bulkDeleteForm" method="post" action="{{ route('reserves_delete') }}">
|
||
@csrf
|
||
<input type="hidden" name="confirmed" value="1">
|
||
<div class="table-responsive">
|
||
<table class="table table-bordered table-hover rv-table mb-0 text-nowrap">
|
||
<thead>
|
||
@php
|
||
$currentSort = $sort ?? 'reserve_id';
|
||
$currentDir = strtolower($sort_type ?? 'asc');
|
||
if (!in_array($currentDir, ['asc','desc'], true)) { $currentDir = 'asc'; }
|
||
$sortClass = function(string $key) use ($currentSort, $currentDir) {
|
||
if ($currentSort === $key) {
|
||
return 'sortable sorting_' . $currentDir;
|
||
}
|
||
return 'sortable sorting';
|
||
};
|
||
$renderSortIcon = function() {
|
||
return '<span class="sort-icon"><span class="up">↑</span><span class="down">↓</span></span>';
|
||
};
|
||
@endphp
|
||
<tr>
|
||
<th style="width:140px;">
|
||
<input type="checkbox" id="chkAll">
|
||
<span class="ml-1"></span>
|
||
</th>
|
||
<th class="{{ $sortClass('reserve_id') }}" data-sort="reserve_id">定期予約ID {!! $renderSortIcon() !!}</th>
|
||
<th class="{{ $sortClass('contract_id') }}" data-sort="contract_id">定期契約ID {!! $renderSortIcon() !!}</th>
|
||
<th class="{{ $sortClass('contract_created_at') }}" data-sort="contract_created_at">定期契約日時 {!! $renderSortIcon() !!}</th>
|
||
<th class="{{ $sortClass('user_categoryid') }}" data-sort="user_categoryid">利用者分類ID {!! $renderSortIcon() !!}</th>
|
||
<th class="{{ $sortClass('user_id') }}" data-sort="user_id">利用者ID {!! $renderSortIcon() !!}</th>
|
||
<th class="{{ $sortClass('reserve_date') }}" data-sort="reserve_date">予約日時 {!! $renderSortIcon() !!}</th>
|
||
<th class="{{ $sortClass('park_price_name') }}" data-sort="park_price_name">駐輪場ID {!! $renderSortIcon() !!}</th>
|
||
<th class="{{ $sortClass('price_parkplaceid') }}" data-sort="price_parkplaceid">駐輪場所ID {!! $renderSortIcon() !!}</th>
|
||
<th class="{{ $sortClass('psection_subject') }}" data-sort="psection_subject">車種区分ID {!! $renderSortIcon() !!}</th>
|
||
<th class="{{ $sortClass('ptype_subject') }}" data-sort="ptype_subject">駐輪分類ID {!! $renderSortIcon() !!}</th>
|
||
<th class="{{ $sortClass('reserve_reduction') }}" data-sort="reserve_reduction">減免措置 {!! $renderSortIcon() !!}</th>
|
||
<th class="{{ $sortClass('reserve_auto_remind') }}" data-sort="reserve_auto_remind">自動リマインド日 {!! $renderSortIcon() !!}</th>
|
||
<th class="{{ $sortClass('reserve_manual_remind') }}" data-sort="reserve_manual_remind">手動リマインド日 {!! $renderSortIcon() !!}</th>
|
||
<th class="{{ $sortClass('flag_800m') }}" data-sort="flag_800m">800M以内フラグ {!! $renderSortIcon() !!}</th>
|
||
<th class="{{ $sortClass('reserve_cancelday') }}" data-sort="reserve_cancelday">解約日 {!! $renderSortIcon() !!}</th>
|
||
<th class="{{ $sortClass('valid_flag') }}" data-sort="valid_flag">有効フラグ {!! $renderSortIcon() !!}</th>
|
||
<th class="{{ $sortClass('sent_date') }}" data-sort="sent_date">メール送信日時 {!! $renderSortIcon() !!}</th>
|
||
<th class="{{ $sortClass('reserve_manual') }}" data-sort="reserve_manual">手動通知 {!! $renderSortIcon() !!}</th>
|
||
<th class="{{ $sortClass('reserve_notice') }}" data-sort="reserve_notice">手動通知方法 {!! $renderSortIcon() !!}</th>
|
||
<th class="{{ $sortClass('reserve_order') }}" data-sort="reserve_order">空き待ち順 {!! $renderSortIcon() !!}</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
@foreach($list as $row)
|
||
<tr>
|
||
{{-- 操作セル--}}
|
||
<td class="op-cell">
|
||
<div class="d-flex align-items-center">
|
||
<input type="checkbox" class="mr-2 chkRow" name="ids[]" value="{{ $row->reserve_id }}">
|
||
<a href="{{ route('reserves_edit', ['reserve_id' => $row->reserve_id]) }}"
|
||
class="btn btn-sm btn-default">編集</a>
|
||
</div>
|
||
</td>
|
||
|
||
{{-- データ列(存在しないカラムは空文字で安全に表示) --}}
|
||
<td class="text-right">{{ $row->reserve_id }}</td> {{-- 定期予約ID --}}
|
||
<td class="text-right">{{ $row->contract_id ?? '' }}</td> {{-- 定期契約ID --}}
|
||
<td>{{ $row->contract_created_at ?? '' }}</td> {{-- 定期契約日時 --}}
|
||
<td>
|
||
@php
|
||
$subjects = array_values(array_filter([
|
||
$row->usertype_subject1 ?? '',
|
||
$row->usertype_subject2 ?? '',
|
||
$row->usertype_subject3 ?? '',
|
||
], fn($value) => $value !== ''));
|
||
@endphp
|
||
{{ $subjects ? implode('/', $subjects) : '' }}
|
||
</td> {{-- 利用者分類ID(分類名1/2/3 表示) --}}
|
||
<td>{{ trim(($row->user_id ?? '') . ' ' . ($row->user_name ?? '')) }}</td> {{-- 利用者ID(ID 名字) --}}
|
||
<td>{{ $row->reserve_date ?? '' }}</td> {{-- 予約日時 --}}
|
||
<td>{{ trim($row->park_name ?? '') }}</td> {{-- 駐輪場ID --}}
|
||
<td>{{ $row->display_prine_name ?? '' }}</td> {{-- 駐輪場所ID --}}
|
||
<td>{{ $row->psection_subject ?? '' }}</td> {{-- 車種区分ID:区分名を表示 --}}
|
||
<td>{{ $row->ptype_subject ?? '' }}</td> {{-- 駐輪分類ID:ptype_subject 表示 --}}
|
||
<td>{{ $row->reserve_reduction ?? '' }}</td> {{-- 減免措置 --}}
|
||
<td>{{ $row->reserve_auto_remind ?? '' }}</td> {{-- 自動リマインド日 --}}
|
||
<td>{{ $row->reserve_manual_remind ?? '' }}</td> {{-- 手動リマインド日 --}}
|
||
<td>{{ $row->flag_800m ?? '' }}</td> {{-- 800M以内フラグ --}}
|
||
<td>{{ $row->reserve_cancelday ?? '' }}</td> {{-- 解約日 --}}
|
||
<td>
|
||
@if((string)$row->valid_flag === '1')
|
||
有効
|
||
@elseif((string)$row->valid_flag === '0')
|
||
無効
|
||
@else
|
||
{{ $row->valid_flag ?? '' }}
|
||
@endif
|
||
</td> {{-- 有効フラグ --}}
|
||
<td>{{ $row->sent_date ?? '' }}</td> {{-- メール送信日時 --}}
|
||
<td>{{ $row->reserve_manual ?? '' }}</td> {{-- 手動通知 --}}
|
||
<td>{{ $row->reserve_notice ?? '' }}</td> {{-- 手動通知方法 --}}
|
||
<td class="text-right">{{ $row->reserve_order ?? '' }}</td> {{-- 空き待ち順 --}}
|
||
</tr>
|
||
@endforeach
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
{{-- 下側ページャ(右寄せ) --}}
|
||
<div class="d-flex justify-content-end mt-2">
|
||
{{ $list->appends(['sort'=>$sort,'sort_type'=>$sort_type])->links('pagination') }}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
|
||
{{-- 一括削除のフロント処理(jQuery不要) --}}
|
||
<script>
|
||
(function(){
|
||
var chkAll = document.getElementById('chkAll');
|
||
var chks = document.getElementsByClassName('chkRow');
|
||
var filterForm = document.getElementById('filter-form');
|
||
var sortInput = document.getElementById('sort');
|
||
var sortTypeInput = document.getElementById('sort_type');
|
||
|
||
// 全選択チェック
|
||
if (chkAll) {
|
||
chkAll.addEventListener('change', function(){
|
||
Array.prototype.forEach.call(chks, function(c){ c.checked = chkAll.checked; });
|
||
});
|
||
}
|
||
|
||
// 一括削除ボタン
|
||
var btn = document.getElementById('btnBulkDel');
|
||
var form = document.getElementById('bulkDeleteForm');
|
||
if (btn && form) {
|
||
btn.addEventListener('click', function(){
|
||
var any = false;
|
||
Array.prototype.forEach.call(chks, function(c){ if (c.checked) any = true; });
|
||
if (!any) { alert('削除対象の行を選択してください。'); return; }
|
||
|
||
var submitDelete = function () { form.submit(); };
|
||
|
||
if (window.jQuery && typeof window.jQuery.confirm === 'function') {
|
||
window.jQuery.confirm({
|
||
title: '確認ダイアログ。',
|
||
content: '削除してよろしいですか? はい/いいえ',
|
||
buttons: {
|
||
ok: {
|
||
text: 'はい',
|
||
btnClass: 'btn-primary',
|
||
keys: ['enter'],
|
||
action: submitDelete
|
||
},
|
||
いいえ: function () {}
|
||
}
|
||
});
|
||
} else if (confirm('削除してよろしいですか? はい/いいえ')) {
|
||
submitDelete();
|
||
}
|
||
});
|
||
}
|
||
var sortHeaders = document.querySelectorAll('th[data-sort]');
|
||
if (sortHeaders.length && filterForm && sortInput && sortTypeInput) {
|
||
Array.prototype.forEach.call(sortHeaders, function(th){
|
||
th.addEventListener('click', function(){
|
||
var key = th.getAttribute('data-sort');
|
||
if (!key) { return; }
|
||
var nextDir = 'asc';
|
||
if (sortInput.value === key) {
|
||
nextDir = sortTypeInput.value === 'asc' ? 'desc' : 'asc';
|
||
}
|
||
sortInput.value = key;
|
||
sortTypeInput.value = nextDir;
|
||
filterForm.submit();
|
||
});
|
||
});
|
||
}
|
||
})();
|
||
</script>
|
||
@endsection
|