Merge branch 'main' of https://git.so-manager-dev.com/so-manager/krgm.so-manager-dev.com
All checks were successful
Deploy main / deploy (push) Successful in 23s
All checks were successful
Deploy main / deploy (push) Successful in 23s
This commit is contained in:
commit
8b85451145
16
.env
16
.env
@ -46,14 +46,16 @@ REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_MAILER=log
|
||||
MAIL_SCHEME=null
|
||||
MAIL_HOST=127.0.0.1
|
||||
MAIL_PORT=2525
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_FROM_ADDRESS="hello@so-manager-dev.com"
|
||||
MAIL_MAILER=smtp
|
||||
#MAIL_SCHEME=null
|
||||
MAIL_HOST=tomatofox9.sakura.ne.jp
|
||||
MAIL_PORT=587
|
||||
MAIL_USERNAME=demo@so-rin.jp
|
||||
MAIL_PASSWORD=rokuchou4665
|
||||
MAIL_ENCRYPTION=tls
|
||||
MAIL_FROM_ADDRESS=demo@so-rin.jp
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
MAIL_ADMIN=demo@so-rin.jp
|
||||
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
|
||||
@ -13,6 +13,7 @@ class ContractorController extends Controller
|
||||
*/
|
||||
public function list(Request $request)
|
||||
{
|
||||
// ベースクエリを構築
|
||||
$q = DB::table('regular_contract as rc')
|
||||
->leftJoin('user as u','rc.user_id','=','u.user_id')
|
||||
->select([
|
||||
@ -33,10 +34,12 @@ class ContractorController extends Controller
|
||||
'rc.contract_permission',
|
||||
'rc.contract_manual',
|
||||
'rc.contract_notice',
|
||||
'rc.update_flag',
|
||||
'p.park_name',
|
||||
'u.user_name',
|
||||
'u.user_phonetic',
|
||||
'u.user_mobile',
|
||||
'u.user_seq',
|
||||
'u.user_homephone',
|
||||
'u.user_primemail',
|
||||
'u.user_gender',
|
||||
@ -64,7 +67,7 @@ class ContractorController extends Controller
|
||||
WHEN 12 THEN '年'
|
||||
ELSE CONCAT(rc.enable_months, 'ヶ月') END as ticket_type"),
|
||||
DB::raw('ps.psection_subject as vehicle_type'),
|
||||
// 利用者分類名(usertype テーブル)
|
||||
// 利用者分類のラベル(usertype テーブルの subject を取得)
|
||||
DB::raw('ut.usertype_subject1 as user_category1'),
|
||||
DB::raw('ut.usertype_subject2 as user_category2'),
|
||||
DB::raw('ut.usertype_subject3 as user_category3'),
|
||||
@ -74,63 +77,73 @@ class ContractorController extends Controller
|
||||
->leftJoin('usertype as ut', 'u.user_categoryid', '=', 'ut.user_categoryid');
|
||||
|
||||
// ===== 絞り込み条件 =====
|
||||
// 駐輪場
|
||||
|
||||
// 駐輪場で絞る(完全一致)
|
||||
if ($request->filled('park_id')) {
|
||||
$q->where('rc.park_id', $request->park_id);
|
||||
}
|
||||
// 利用者ID(完全一致)
|
||||
|
||||
// 利用者IDで絞る(完全一致)
|
||||
if ($request->filled('user_id')) {
|
||||
$q->where('rc.user_id', $request->user_id);
|
||||
}
|
||||
|
||||
// 分類名1(完全一致): ut.usertype_subject1 = 入力値
|
||||
// 利用者分類で絞る(※ select の value を user_categoryid にしているため、user テーブルのカラムで比較)
|
||||
if ($request->filled('user_category1')) {
|
||||
$q->where('ut.usertype_subject1', $request->user_category1);
|
||||
$q->where('u.user_categoryid', $request->user_category1);
|
||||
}
|
||||
// タグシリアル64進(部分一致)
|
||||
|
||||
// タグシリアル64進で部分一致検索
|
||||
if ($request->filled('user_tag_serial_64')) {
|
||||
$val = $request->user_tag_serial_64;
|
||||
$q->where('u.user_tag_serial_64','like','%'.$val.'%');
|
||||
}
|
||||
// 有効期限(契約有効期間 終了日:指定日以前を抽出)
|
||||
|
||||
// 有効期限で絞る(指定日以前を抽出する= <= を使用)
|
||||
if ($request->filled('contract_periode')) {
|
||||
$raw = trim($request->contract_periode);
|
||||
$norm = str_replace('/', '-', $raw); // スラッシュ入力許容
|
||||
$norm = str_replace('/', '-', $raw); // スラッシュ入力を許容
|
||||
try {
|
||||
$target = \Carbon\Carbon::parse($norm)->format('Y-m-d');
|
||||
// 指定日 “以前” を含む (以前のみなら '<' に変更)
|
||||
$q->whereDate('rc.contract_periode', '>=', $target);
|
||||
// 指定日「以前」を含める
|
||||
$q->whereDate('rc.contract_periode', '<=', $target);
|
||||
} catch (\Exception $e) {
|
||||
// 不正日付は無視
|
||||
// 無効な日付は無視する
|
||||
}
|
||||
}
|
||||
// フリガナ(部分一致)
|
||||
|
||||
// フリガナで部分一致
|
||||
if ($request->filled('user_phonetic')) {
|
||||
$q->where('u.user_phonetic', 'like', '%' . $request->user_phonetic . '%');
|
||||
}
|
||||
// 携帯電話(部分一致)
|
||||
|
||||
// 携帯電話で部分一致
|
||||
if ($request->filled('user_mobile')) {
|
||||
$q->where('u.user_mobile', 'like', '%' . $request->user_mobile . '%');
|
||||
}
|
||||
// メール(部分一致)
|
||||
|
||||
// メールアドレスで部分一致
|
||||
if ($request->filled('user_primemail')) {
|
||||
$q->where('u.user_primemail', 'like', '%' . $request->user_primemail . '%');
|
||||
}
|
||||
// 勤務先(部分一致)
|
||||
|
||||
// 勤務先で部分一致
|
||||
if ($request->filled('user_workplace')) {
|
||||
$q->where('u.user_workplace', 'like', '%' . $request->user_workplace . '%');
|
||||
}
|
||||
// 学校(部分一致)
|
||||
|
||||
// 学校で部分一致
|
||||
if ($request->filled('user_school')) {
|
||||
$q->where('u.user_school', 'like', '%' . $request->user_school . '%');
|
||||
}
|
||||
// タグ・QR(完全一致)
|
||||
|
||||
// タグ・QR フラグで絞る(空文字は無視)
|
||||
if ($request->filled('tag_qr_flag') && $request->tag_qr_flag !== '') {
|
||||
$q->where('rc.tag_qr_flag', $request->tag_qr_flag);
|
||||
}
|
||||
|
||||
// ソート処理
|
||||
// パラメータが来た時だけ適用。未指定なら主キー昇順
|
||||
// ===== ソート処理 =====
|
||||
// 指定があればその列でソート、なければデフォルトで契約IDの昇順
|
||||
$sort = $request->input('sort'); // null 許容
|
||||
$sortType = $request->input('sort_type','asc');
|
||||
|
||||
@ -145,18 +158,23 @@ class ContractorController extends Controller
|
||||
$sortType = $sortType === 'desc' ? 'desc' : 'asc';
|
||||
$q->orderBy($sort, $sortType);
|
||||
} else {
|
||||
// 初期表示は契約ID昇順 (物理順に近い)
|
||||
// デフォルトソート
|
||||
$sort = null;
|
||||
$sortType = null;
|
||||
$q->orderBy('rc.contract_id','asc');
|
||||
}
|
||||
|
||||
// ページネーション(クエリ文字列を引き継ぐ)
|
||||
$rows = $q->paginate(20)->appends($request->query());
|
||||
|
||||
// 駐輪場セレクト用
|
||||
// 駐輪場セレクト用データ取得
|
||||
$parks = DB::table('park')->select('park_id', 'park_name')->orderBy('park_name')->get();
|
||||
|
||||
return view('admin.contractor.list', compact('rows', 'sort', 'sortType', 'parks'));
|
||||
// 利用者分類セレクト用:実際に使用されている分類のみを取得する
|
||||
$categories = $this->buildCategoryOptions(true);
|
||||
|
||||
// ビューに渡す
|
||||
return view('admin.contractor.list', compact('rows', 'sort', 'sortType', 'parks', 'categories'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -164,6 +182,7 @@ class ContractorController extends Controller
|
||||
*/
|
||||
public function info($id)
|
||||
{
|
||||
// 指定契約IDの詳細を取得
|
||||
$contract = DB::table('regular_contract as rc')
|
||||
->select([
|
||||
'rc.*',
|
||||
@ -186,4 +205,55 @@ class ContractorController extends Controller
|
||||
|
||||
return view('admin.contractor.info', compact('contract'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 利用者分類選択肢を取得
|
||||
*
|
||||
* @param bool $onlyUsed true の場合は regular_contract に出現する分類のみ返す
|
||||
* @return array [user_categoryid => label, ...]
|
||||
*/
|
||||
private function buildCategoryOptions(bool $onlyUsed = false): array
|
||||
{
|
||||
if (! $onlyUsed) {
|
||||
// 全件取得(既存の挙動)
|
||||
return DB::table('usertype')
|
||||
->orderBy('user_categoryid', 'asc')
|
||||
->get()
|
||||
->mapWithKeys(function ($row) {
|
||||
$label = collect([
|
||||
$row->usertype_subject1 ?? '',
|
||||
$row->usertype_subject2 ?? '',
|
||||
$row->usertype_subject3 ?? '',
|
||||
])->filter(fn ($v) => $v !== '')->implode('/');
|
||||
|
||||
return [$row->user_categoryid => $label !== '' ? $label : (string) $row->user_categoryid];
|
||||
})
|
||||
->toArray();
|
||||
}
|
||||
|
||||
// 実際に使用されている分類のみ取得する(内部結合で user と regular_contract と紐付くもの)
|
||||
$rows = DB::table('usertype as ut')
|
||||
->join('user as u', 'u.user_categoryid', '=', 'ut.user_categoryid')
|
||||
->join('regular_contract as rc', 'rc.user_id', '=', 'u.user_id')
|
||||
->select(
|
||||
'ut.user_categoryid',
|
||||
'ut.usertype_subject1',
|
||||
'ut.usertype_subject2',
|
||||
'ut.usertype_subject3'
|
||||
)
|
||||
->groupBy('ut.user_categoryid', 'ut.usertype_subject1', 'ut.usertype_subject2', 'ut.usertype_subject3')
|
||||
->orderBy('ut.user_categoryid', 'asc')
|
||||
->get();
|
||||
|
||||
// ラベルを組み立てて配列で返す
|
||||
return $rows->mapWithKeys(function ($row) {
|
||||
$label = collect([
|
||||
$row->usertype_subject1 ?? '',
|
||||
$row->usertype_subject2 ?? '',
|
||||
$row->usertype_subject3 ?? '',
|
||||
])->filter(fn ($v) => $v !== '')->implode('/');
|
||||
|
||||
return [$row->user_categoryid => $label !== '' ? $label : (string) $row->user_categoryid];
|
||||
})->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,10 +9,11 @@ use Illuminate\Support\Facades\DB;
|
||||
class ContractorListController extends Controller
|
||||
{
|
||||
/**
|
||||
* 未更新者一覧 (contract_renewal IS NULL)
|
||||
* 一覧表示(GET/POST)
|
||||
*/
|
||||
public function list(Request $request)
|
||||
{
|
||||
// ベースクエリを構築
|
||||
$q = DB::table('regular_contract as rc')
|
||||
->leftJoin('user as u','rc.user_id','=','u.user_id')
|
||||
->select([
|
||||
@ -33,10 +34,14 @@ class ContractorListController extends Controller
|
||||
'rc.contract_permission',
|
||||
'rc.contract_manual',
|
||||
'rc.contract_notice',
|
||||
'rc.update_flag',
|
||||
'rc.user_securitynum',
|
||||
'rc.contract_seal_issue',
|
||||
'p.park_name',
|
||||
'u.user_name',
|
||||
'u.user_phonetic',
|
||||
'u.user_mobile',
|
||||
'u.user_seq',
|
||||
'u.user_homephone',
|
||||
'u.user_primemail',
|
||||
'u.user_gender',
|
||||
@ -64,7 +69,7 @@ class ContractorListController extends Controller
|
||||
WHEN 12 THEN '年'
|
||||
ELSE CONCAT(rc.enable_months, 'ヶ月') END as ticket_type"),
|
||||
DB::raw('ps.psection_subject as vehicle_type'),
|
||||
// 利用者分類名(usertype テーブル)
|
||||
// 利用者分類のラベル(usertype テーブルの subject を取得)
|
||||
DB::raw('ut.usertype_subject1 as user_category1'),
|
||||
DB::raw('ut.usertype_subject2 as user_category2'),
|
||||
DB::raw('ut.usertype_subject3 as user_category3'),
|
||||
@ -74,89 +79,183 @@ class ContractorListController extends Controller
|
||||
->leftJoin('usertype as ut', 'u.user_categoryid', '=', 'ut.user_categoryid');
|
||||
|
||||
// ===== 絞り込み条件 =====
|
||||
// 駐輪場
|
||||
|
||||
// 駐輪場で絞る(完全一致)
|
||||
if ($request->filled('park_id')) {
|
||||
$q->where('rc.park_id', $request->park_id);
|
||||
}
|
||||
// 利用者ID(完全一致)
|
||||
|
||||
// 利用者IDで絞る(完全一致)
|
||||
if ($request->filled('user_id')) {
|
||||
$q->where('rc.user_id', $request->user_id);
|
||||
}
|
||||
// 分類名1(完全一致)
|
||||
|
||||
// 利用者分類で絞る(※ select の value を user_categoryid にしているため、user テーブルのカラムで比較)
|
||||
if ($request->filled('user_category1')) {
|
||||
$q->where('ut.usertype_subject1', $request->user_category1);
|
||||
$q->where('u.user_categoryid', $request->user_category1);
|
||||
}
|
||||
// タグシリアル64進(部分一致)
|
||||
|
||||
// タグシリアル64進で部分一致検索
|
||||
if ($request->filled('user_tag_serial_64')) {
|
||||
$val = $request->user_tag_serial_64;
|
||||
$q->where('u.user_tag_serial_64','like','%'.$val.'%');
|
||||
}
|
||||
// 有効期限(契約有効期間 終了日:指定日以前を抽出)
|
||||
|
||||
// 有効期限で絞る(指定日以前を抽出する= <= を使用)
|
||||
if ($request->filled('contract_periode')) {
|
||||
$raw = trim($request->contract_periode);
|
||||
$norm = str_replace('/', '-', $raw); // スラッシュ入力許容
|
||||
$norm = str_replace('/', '-', $raw); // スラッシュ入力を許容
|
||||
try {
|
||||
$target = \Carbon\Carbon::parse($norm)->format('Y-m-d');
|
||||
// 指定日 “以前” を含む (以前のみなら '<' に変更)
|
||||
$q->whereDate('rc.contract_periode', '>=', $target);
|
||||
// 指定日「以前」を含める
|
||||
$q->whereDate('rc.contract_periode', '<=', $target);
|
||||
} catch (\Exception $e) {
|
||||
// 不正日付は無視
|
||||
// 無効な日付は無視する
|
||||
}
|
||||
}
|
||||
// フリガナ(部分一致)
|
||||
|
||||
// フリガナで部分一致
|
||||
if ($request->filled('user_phonetic')) {
|
||||
$q->where('u.user_phonetic', 'like', '%' . $request->user_phonetic . '%');
|
||||
}
|
||||
// 携帯電話(部分一致)
|
||||
|
||||
// 携帯電話で部分一致
|
||||
if ($request->filled('user_mobile')) {
|
||||
$q->where('u.user_mobile', 'like', '%' . $request->user_mobile . '%');
|
||||
}
|
||||
// メール(部分一致)
|
||||
|
||||
// メールアドレスで部分一致
|
||||
if ($request->filled('user_primemail')) {
|
||||
$q->where('u.user_primemail', 'like', '%' . $request->user_primemail . '%');
|
||||
}
|
||||
// 勤務先(部分一致)
|
||||
|
||||
// 勤務先で部分一致
|
||||
if ($request->filled('user_workplace')) {
|
||||
$q->where('u.user_workplace', 'like', '%' . $request->user_workplace . '%');
|
||||
}
|
||||
// 学校(部分一致)
|
||||
|
||||
// 学校で部分一致
|
||||
if ($request->filled('user_school')) {
|
||||
$q->where('u.user_school', 'like', '%' . $request->user_school . '%');
|
||||
}
|
||||
// タグ・QR(完全一致)
|
||||
|
||||
// タグ・QR フラグで絞る(空文字は無視)
|
||||
if ($request->filled('tag_qr_flag') && $request->tag_qr_flag !== '') {
|
||||
$q->where('rc.tag_qr_flag', $request->tag_qr_flag);
|
||||
}
|
||||
|
||||
// ---- ソート ----
|
||||
$sort = $request->input('sort',''); // 初期は未指定
|
||||
// ===== ソート処理 =====
|
||||
// 指定があればその列でソート、なければデフォルトで契約IDの昇順
|
||||
$sort = $request->input('sort'); // null 許容
|
||||
$sortType = $request->input('sort_type','asc');
|
||||
|
||||
$allowSorts = [
|
||||
'user_id' => 'rc.user_id',
|
||||
'user_name' => 'u.user_name',
|
||||
'contract_id' => 'rc.contract_id',
|
||||
'tag_qr_flag' => 'rc.tag_qr_flag',
|
||||
'park_name' => 'p.park_name',
|
||||
'rc.contract_id',
|
||||
'rc.user_id',
|
||||
'u.user_name',
|
||||
'rc.tag_qr_flag',
|
||||
'p.park_name',
|
||||
];
|
||||
if($sort !== '' && isset($allowSorts[$sort])){
|
||||
if ($sort && in_array($sort, $allowSorts)) {
|
||||
$sortType = $sortType === 'desc' ? 'desc' : 'asc';
|
||||
$q->orderBy($allowSorts[$sort], $sortType)
|
||||
->orderBy('rc.contract_id','asc'); // セカンダリ安定
|
||||
$q->orderBy($sort, $sortType);
|
||||
} else {
|
||||
// 初期表示: DB登録順の近似(PK昇順)
|
||||
$sort = ''; // Blade 側で未ソート状態
|
||||
// デフォルトソート
|
||||
$sort = null;
|
||||
$sortType = null;
|
||||
$q->orderBy('rc.contract_id','asc');
|
||||
}
|
||||
|
||||
// 駐輪場選択
|
||||
$parks = DB::table('park')
|
||||
->select('park_id','park_name')
|
||||
->orderBy('park_name')
|
||||
->orderBy('park_id')
|
||||
->get();
|
||||
|
||||
// ページネーション(クエリ文字列を引き継ぐ)
|
||||
$rows = $q->paginate(20)->appends($request->query());
|
||||
|
||||
return view('admin.contractor_list.list', compact('rows','sort','sortType','parks'));
|
||||
// 駐輪場セレクト用データ取得
|
||||
$parks = DB::table('park')->select('park_id', 'park_name')->orderBy('park_name')->get();
|
||||
|
||||
// 利用者分類セレクト用:実際に使用されている分類のみを取得する
|
||||
$categories = $this->buildCategoryOptions(true);
|
||||
|
||||
// ビューに渡す
|
||||
return view('admin.contractor_List.list', compact('rows', 'sort', 'sortType', 'parks', 'categories'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 詳細表示
|
||||
*/
|
||||
public function info($id)
|
||||
{
|
||||
// 指定契約IDの詳細を取得
|
||||
$contract = DB::table('regular_contract as rc')
|
||||
->select([
|
||||
'rc.*',
|
||||
'p.park_name',
|
||||
'u.user_name',
|
||||
'u.user_phonetic',
|
||||
'u.user_mobile',
|
||||
'u.user_homephone',
|
||||
'u.user_primemail',
|
||||
'u.user_gender',
|
||||
'u.user_birthdate',
|
||||
'u.user_regident_city',
|
||||
])
|
||||
->leftJoin('park as p', 'rc.park_id', '=', 'p.park_id')
|
||||
->leftJoin('user as u', 'rc.user_id', '=', 'u.user_id')
|
||||
->where('rc.contract_id', $id)
|
||||
->first();
|
||||
|
||||
if (!$contract) { abort(404); }
|
||||
|
||||
return view('admin.contractor_List.info', compact('contract'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 利用者分類選択肢を取得
|
||||
*
|
||||
* @param bool $onlyUsed true の場合は regular_contract に出現する分類のみ返す
|
||||
* @return array [user_categoryid => label, ...]
|
||||
*/
|
||||
private function buildCategoryOptions(bool $onlyUsed = false): array
|
||||
{
|
||||
if (! $onlyUsed) {
|
||||
// 全件取得(既存の挙動)
|
||||
return DB::table('usertype')
|
||||
->orderBy('user_categoryid', 'asc')
|
||||
->get()
|
||||
->mapWithKeys(function ($row) {
|
||||
$label = collect([
|
||||
$row->usertype_subject1 ?? '',
|
||||
$row->usertype_subject2 ?? '',
|
||||
$row->usertype_subject3 ?? '',
|
||||
])->filter(fn ($v) => $v !== '')->implode('/');
|
||||
|
||||
return [$row->user_categoryid => $label !== '' ? $label : (string) $row->user_categoryid];
|
||||
})
|
||||
->toArray();
|
||||
}
|
||||
|
||||
// 実際に使用されている分類のみ取得する(内部結合で user と regular_contract と紐付くもの)
|
||||
$rows = DB::table('usertype as ut')
|
||||
->join('user as u', 'u.user_categoryid', '=', 'ut.user_categoryid')
|
||||
->join('regular_contract as rc', 'rc.user_id', '=', 'u.user_id')
|
||||
->select(
|
||||
'ut.user_categoryid',
|
||||
'ut.usertype_subject1',
|
||||
'ut.usertype_subject2',
|
||||
'ut.usertype_subject3'
|
||||
)
|
||||
->groupBy('ut.user_categoryid', 'ut.usertype_subject1', 'ut.usertype_subject2', 'ut.usertype_subject3')
|
||||
->orderBy('ut.user_categoryid', 'asc')
|
||||
->get();
|
||||
|
||||
// ラベルを組み立てて配列で返す
|
||||
return $rows->mapWithKeys(function ($row) {
|
||||
$label = collect([
|
||||
$row->usertype_subject1 ?? '',
|
||||
$row->usertype_subject2 ?? '',
|
||||
$row->usertype_subject3 ?? '',
|
||||
])->filter(fn ($v) => $v !== '')->implode('/');
|
||||
|
||||
return [$row->user_categoryid => $label !== '' ? $label : (string) $row->user_categoryid];
|
||||
})->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,15 +9,45 @@ use Illuminate\Support\Facades\DB;
|
||||
class ReservationController extends Controller
|
||||
{
|
||||
/**
|
||||
* 予約者一覧表示
|
||||
* 一覧表示(GET/POST)
|
||||
*/
|
||||
public function list(Request $request)
|
||||
{
|
||||
$q = DB::table('reserve as r')
|
||||
// ベースクエリを構築
|
||||
$q = DB::table('regular_contract as rc')
|
||||
->leftJoin('user as u','rc.user_id','=','u.user_id')
|
||||
->select([
|
||||
'r.user_id',
|
||||
'rc.contract_id',
|
||||
'rc.contract_qr_id',
|
||||
'rc.user_id',
|
||||
'rc.user_categoryid',
|
||||
'rc.park_id',
|
||||
'rc.contract_created_at',
|
||||
'rc.contract_periods',
|
||||
'rc.contract_periode',
|
||||
'rc.tag_qr_flag',
|
||||
'rc.contract_flag',
|
||||
'rc.contract_cancel_flag',
|
||||
'rc.contract_payment_day',
|
||||
'rc.contract_money',
|
||||
'rc.billing_amount',
|
||||
'rc.contract_permission',
|
||||
'rc.contract_manual',
|
||||
'rc.contract_notice',
|
||||
'rc.update_flag',
|
||||
'rc.800m_flag',
|
||||
'rc.price_parkplaceid',
|
||||
'rc.psection_id',
|
||||
'rc.reserve_date',
|
||||
'p.park_name',
|
||||
'u.user_name',
|
||||
'u.user_phonetic',
|
||||
'u.user_mobile',
|
||||
'u.user_seq',
|
||||
'u.user_homephone',
|
||||
'u.user_primemail',
|
||||
'u.user_gender',
|
||||
'u.user_birthdate',
|
||||
'u.user_regident_zip',
|
||||
'u.user_regident_pre',
|
||||
'u.user_regident_city',
|
||||
@ -26,96 +56,195 @@ class ReservationController extends Controller
|
||||
'u.user_relate_pre',
|
||||
'u.user_relate_city',
|
||||
'u.user_relate_add',
|
||||
'u.user_birthdate',
|
||||
'u.user_gender',
|
||||
'u.user_mobile',
|
||||
'u.user_homephone',
|
||||
'u.user_school',
|
||||
'u.user_graduate',
|
||||
'u.user_workplace',
|
||||
'u.user_school',
|
||||
'u.user_remarks',
|
||||
'r.reserve_id',
|
||||
'r.park_id',
|
||||
'p.park_name',
|
||||
'r.price_parkplaceid',
|
||||
'r.psection_id',
|
||||
'u.user_tag_serial_64',
|
||||
'u.user_reduction',
|
||||
DB::raw('rc.user_securitynum as crime_prevention'),
|
||||
DB::raw('rc.contract_seal_issue as seal_issue_count'),
|
||||
DB::raw("CASE rc.enable_months
|
||||
WHEN 1 THEN '月極(1ヶ月)'
|
||||
WHEN 3 THEN '3ヶ月'
|
||||
WHEN 6 THEN '6ヶ月'
|
||||
WHEN 12 THEN '年'
|
||||
ELSE CONCAT(rc.enable_months, 'ヶ月') END as ticket_type"),
|
||||
DB::raw('ps.psection_subject as vehicle_type'),
|
||||
// 利用者分類のラベル(usertype テーブルの subject を取得)
|
||||
DB::raw('ut.usertype_subject1 as user_category1'),
|
||||
DB::raw('ut.usertype_subject2 as user_category2'),
|
||||
DB::raw('ut.usertype_subject3 as user_category3'),
|
||||
'r.reserve_date',
|
||||
'r.reserve_reduction as reduction',
|
||||
DB::raw('r.`800m_flag` as within_800m_flag'),
|
||||
])
|
||||
->leftJoin('user as u', 'r.user_id', '=', 'u.user_id')
|
||||
->leftJoin('park as p', 'r.park_id', '=', 'p.park_id')
|
||||
->leftJoin('psection as ps','r.psection_id','=','ps.psection_id')
|
||||
->leftJoin('park as p', 'rc.park_id', '=', 'p.park_id')
|
||||
->leftJoin('psection as ps', 'rc.psection_id', '=', 'ps.psection_id')
|
||||
->leftJoin('usertype as ut', 'u.user_categoryid', '=', 'ut.user_categoryid');
|
||||
|
||||
// フィルター条件
|
||||
// ===== 絞り込み条件 =====
|
||||
|
||||
// 駐輪場で絞る(完全一致)
|
||||
if ($request->filled('park_id')) {
|
||||
$q->where('r.park_id', $request->input('park_id'));
|
||||
$q->where('rc.park_id', $request->park_id);
|
||||
}
|
||||
|
||||
// 利用者IDで絞る(完全一致)
|
||||
if ($request->filled('user_id')) {
|
||||
$q->where('r.user_id', $request->input('user_id'));
|
||||
$q->where('rc.user_id', $request->user_id);
|
||||
}
|
||||
// 利用者分類(契約者一覧に合わせ、分類名1の完全一致を優先)
|
||||
|
||||
// 利用者分類で絞る(※ select の value を user_categoryid にしているため、user テーブルのカラムで比較)
|
||||
if ($request->filled('user_category1')) {
|
||||
$val = trim(mb_convert_kana($request->input('user_category1'), 'asKV'));
|
||||
$q->where('ut.usertype_subject1', $val);
|
||||
} elseif ($request->filled('user_categoryid')) {
|
||||
// 既存互換:ID指定も残す
|
||||
$q->where('u.user_categoryid', $request->input('user_categoryid'));
|
||||
$q->where('u.user_categoryid', $request->user_category1);
|
||||
}
|
||||
|
||||
// タグシリアルで部分一致検索
|
||||
if ($request->filled('user_tag_serial')) {
|
||||
$q->where('u.user_tag_serial', 'like', '%' . $request->input('user_tag_serial') . '%');
|
||||
}
|
||||
|
||||
// タグシリアル64進で部分一致検索
|
||||
if ($request->filled('user_tag_serial_64')) {
|
||||
$q->where('u.user_tag_serial_64', 'like', '%' . $request->input('user_tag_serial_64') . '%');
|
||||
$val = $request->user_tag_serial_64;
|
||||
$q->where('u.user_tag_serial_64','like','%'.$val.'%');
|
||||
}
|
||||
|
||||
// フリガナで部分一致
|
||||
if ($request->filled('user_phonetic')) {
|
||||
$q->where('u.user_phonetic', 'like', '%' . $request->input('user_phonetic') . '%');
|
||||
$q->where('u.user_phonetic', 'like', '%' . $request->user_phonetic . '%');
|
||||
}
|
||||
|
||||
// 携帯電話で部分一致
|
||||
if ($request->filled('user_mobile')) {
|
||||
$q->where(function($sub) use ($request) {
|
||||
$sub->where('u.user_mobile', 'like', '%' . $request->input('user_mobile') . '%')
|
||||
->orWhere('u.user_homephone', 'like', '%' . $request->input('user_mobile') . '%');
|
||||
});
|
||||
$q->where('u.user_mobile', 'like', '%' . $request->user_mobile . '%');
|
||||
}
|
||||
|
||||
// メールアドレスで部分一致
|
||||
if ($request->filled('user_primemail')) {
|
||||
$like = '%' . $request->input('user_primemail') . '%';
|
||||
$q->where(function($w) use ($like){
|
||||
$w->where('u.user_primemail','like',$like)
|
||||
->orWhere('u.user_submail','like',$like);
|
||||
});
|
||||
$q->where('u.user_primemail', 'like', '%' . $request->user_primemail . '%');
|
||||
}
|
||||
|
||||
// 勤務先で部分一致
|
||||
if ($request->filled('user_workplace')) {
|
||||
$q->where('u.user_workplace', 'like', '%' . $request->input('user_workplace') . '%');
|
||||
$q->where('u.user_workplace', 'like', '%' . $request->user_workplace . '%');
|
||||
}
|
||||
|
||||
// 学校で部分一致
|
||||
if ($request->filled('user_school')) {
|
||||
$q->where('u.user_school', 'like', '%' . $request->input('user_school') . '%');
|
||||
$q->where('u.user_school', 'like', '%' . $request->user_school . '%');
|
||||
}
|
||||
|
||||
// ソート(契約者一覧に合わせて許可する列を拡張)
|
||||
$sort = $request->input('sort', 'reserve_id');
|
||||
// ===== ソート処理 =====
|
||||
// 指定があればその列でソート、なければデフォルトで契約IDの昇順
|
||||
$sort = $request->input('sort'); // null 許容
|
||||
$sortType = $request->input('sort_type','asc');
|
||||
$allow = [
|
||||
'reserve_id' => 'r.reserve_id',
|
||||
'user_id' => 'r.user_id',
|
||||
'user_name' => 'u.user_name',
|
||||
'park_id' => 'r.park_id',
|
||||
'price_parkplaceid' => 'r.price_parkplaceid',
|
||||
'psection_id' => 'r.psection_id',
|
||||
'reserve_date' => 'r.reserve_date',
|
||||
|
||||
$allowSorts = [
|
||||
'rc.contract_id',
|
||||
'rc.user_id',
|
||||
'u.user_name',
|
||||
'rc.tag_qr_flag',
|
||||
'p.park_name',
|
||||
];
|
||||
if (!isset($allow[$sort])) $sort = 'reserve_id';
|
||||
if ($sort && in_array($sort, $allowSorts)) {
|
||||
$sortType = $sortType === 'desc' ? 'desc' : 'asc';
|
||||
$q->orderBy($sort, $sortType);
|
||||
} else {
|
||||
// デフォルトソート
|
||||
$sort = null;
|
||||
$sortType = null;
|
||||
$q->orderBy('rc.contract_id','asc');
|
||||
}
|
||||
|
||||
$rows = $q->orderBy($allow[$sort], $sortType)
|
||||
->paginate(20)->withQueryString();
|
||||
// ページネーション(クエリ文字列を引き継ぐ)
|
||||
$rows = $q->paginate(20)->appends($request->query());
|
||||
|
||||
// 駐輪場リスト取得(必要なら)
|
||||
// 駐輪場セレクト用データ取得
|
||||
$parks = DB::table('park')->select('park_id', 'park_name')->orderBy('park_name')->get();
|
||||
|
||||
return view('admin.reservation.list', compact('rows', 'sort', 'sortType', 'parks'));
|
||||
// 利用者分類セレクト用:実際に使用されている分類のみを取得する
|
||||
$categories = $this->buildCategoryOptions(true);
|
||||
|
||||
// ビューに渡す
|
||||
return view('admin.reservation.list', compact('rows', 'sort', 'sortType', 'parks', 'categories'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 詳細表示
|
||||
*/
|
||||
public function info($id)
|
||||
{
|
||||
// 指定契約IDの詳細を取得
|
||||
$contract = DB::table('regular_contract as rc')
|
||||
->select([
|
||||
'rc.*',
|
||||
'p.park_name',
|
||||
'u.user_name',
|
||||
'u.user_phonetic',
|
||||
'u.user_mobile',
|
||||
'u.user_homephone',
|
||||
'u.user_primemail',
|
||||
'u.user_gender',
|
||||
'u.user_birthdate',
|
||||
'u.user_regident_city',
|
||||
])
|
||||
->leftJoin('park as p', 'rc.park_id', '=', 'p.park_id')
|
||||
->leftJoin('user as u', 'rc.user_id', '=', 'u.user_id')
|
||||
->where('rc.contract_id', $id)
|
||||
->first();
|
||||
|
||||
if (!$contract) { abort(404); }
|
||||
|
||||
return view('admin.reservation.info', compact('contract'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 利用者分類選択肢を取得
|
||||
*
|
||||
* @param bool $onlyUsed true の場合は regular_contract に出現する分類のみ返す
|
||||
* @return array [user_categoryid => label, ...]
|
||||
*/
|
||||
private function buildCategoryOptions(bool $onlyUsed = false): array
|
||||
{
|
||||
if (! $onlyUsed) {
|
||||
// 全件取得(既存の挙動)
|
||||
return DB::table('usertype')
|
||||
->orderBy('user_categoryid', 'asc')
|
||||
->get()
|
||||
->mapWithKeys(function ($row) {
|
||||
$label = collect([
|
||||
$row->usertype_subject1 ?? '',
|
||||
$row->usertype_subject2 ?? '',
|
||||
$row->usertype_subject3 ?? '',
|
||||
])->filter(fn ($v) => $v !== '')->implode('/');
|
||||
|
||||
return [$row->user_categoryid => $label !== '' ? $label : (string) $row->user_categoryid];
|
||||
})
|
||||
->toArray();
|
||||
}
|
||||
|
||||
// 実際に使用されている分類のみ取得する(内部結合で user と regular_contract と紐付くもの)
|
||||
$rows = DB::table('usertype as ut')
|
||||
->join('user as u', 'u.user_categoryid', '=', 'ut.user_categoryid')
|
||||
->join('regular_contract as rc', 'rc.user_id', '=', 'u.user_id')
|
||||
->select(
|
||||
'ut.user_categoryid',
|
||||
'ut.usertype_subject1',
|
||||
'ut.usertype_subject2',
|
||||
'ut.usertype_subject3'
|
||||
)
|
||||
->groupBy('ut.user_categoryid', 'ut.usertype_subject1', 'ut.usertype_subject2', 'ut.usertype_subject3')
|
||||
->orderBy('ut.user_categoryid', 'asc')
|
||||
->get();
|
||||
|
||||
// ラベルを組み立てて配列で返す
|
||||
return $rows->mapWithKeys(function ($row) {
|
||||
$label = collect([
|
||||
$row->usertype_subject1 ?? '',
|
||||
$row->usertype_subject2 ?? '',
|
||||
$row->usertype_subject3 ?? '',
|
||||
])->filter(fn ($v) => $v !== '')->implode('/');
|
||||
|
||||
return [$row->user_categoryid => $label !== '' ? $label : (string) $row->user_categoryid];
|
||||
})->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,6 +54,10 @@ class TagissueController extends Controller
|
||||
'user.user_regident_pre',
|
||||
'user.user_regident_city',
|
||||
'user.user_regident_add',
|
||||
'user.user_relate_zip',
|
||||
'user.user_relate_pre',
|
||||
'user.user_relate_city',
|
||||
'user.user_relate_add',
|
||||
'operator_que.que_id',
|
||||
'operator_que.que_class',
|
||||
'operator_que.que_status'
|
||||
@ -125,9 +129,7 @@ class TagissueController extends Controller
|
||||
{
|
||||
$ids = $request->input('ids', []); // チェックされたuser_id配列
|
||||
$action = $request->input('action'); // 'to_issued' or 'to_unissued'
|
||||
if (empty($ids) || !is_array($ids)) {
|
||||
return back()->with('error', 'チェックボックスを選択してください。');
|
||||
}
|
||||
|
||||
$operatorId = auth()->id();
|
||||
$now = now();
|
||||
// 対象ユーザーのoperator_queを取得
|
||||
|
||||
@ -20,14 +20,21 @@
|
||||
</div>
|
||||
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
|
||||
{{-- 絞り込みフィルター --}}
|
||||
<form method="GET" action="{{ route('contractor') }}" class="mb-3" id="list-form">
|
||||
<input type="hidden" name="sort" value="{{ $sort ?? request('sort') }}">
|
||||
<input type="hidden" name="sort_type" value="{{ $sortType ?? request('sort_type') }}">
|
||||
<div class="card p-3">
|
||||
<h6 class="mb-3">絞り込みフィルター</h6>
|
||||
|
||||
{{-- ▼ カード全体 --}}
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
{{-- ▼ 絞り込みフィルター(card-header) --}}
|
||||
<div class="card-header" style="font-weight:bold;">
|
||||
絞り込みフィルター
|
||||
</div>
|
||||
|
||||
{{-- ▼ フィルター条件エリア(card-body) --}}
|
||||
<div class="card-body bg-white">
|
||||
<div class="row">
|
||||
{{-- 左カラム --}}
|
||||
<div class="col-md-6">
|
||||
@ -44,7 +51,8 @@
|
||||
@endforeach
|
||||
</select>
|
||||
@else
|
||||
<input type="text" name="park_name" value="{{ request('park_name') }}" class="form-control">
|
||||
<input type="text" name="park_name" value="{{ request('park_name') }}" class="form-control"
|
||||
placeholder="123456">
|
||||
@endisset
|
||||
</div>
|
||||
</div>
|
||||
@ -52,14 +60,29 @@
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">利用者ID</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="user_id" value="{{ request('user_id') }}" class="form-control">
|
||||
<input type="text" name="user_id" value="{{ request('user_id') }}" class="form-control"
|
||||
placeholder="123456">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ===== 利用者分類:input を select に変更 ===== --}}
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">利用者分類</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="user_category1" value="{{ request('user_category1') }}" class="form-control" placeholder="分類名1">
|
||||
@isset($categories)
|
||||
<select name="user_category1" class="form-control">
|
||||
<option value="">全て</option>
|
||||
@foreach($categories as $key => $label)
|
||||
<option value="{{ $key }}"
|
||||
{{ (string) request('user_category1') === (string) $key ? 'selected' : '' }}>
|
||||
{{ $label }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@else
|
||||
<input type="text" name="user_category1" value="{{ request('user_category1') }}" class="form-control"
|
||||
placeholder="123456">
|
||||
@endisset
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -67,7 +90,7 @@
|
||||
<label class="col-sm-3 col-form-label">タグシリアル64進</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="user_tag_serial_64" value="{{ request('user_tag_serial_64') }}"
|
||||
class="form-control">
|
||||
class="form-control" placeholder="キーワード...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -85,35 +108,40 @@
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">フリガナ</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="user_phonetic" value="{{ request('user_phonetic') }}" class="form-control">
|
||||
<input type="text" name="user_phonetic" value="{{ request('user_phonetic') }}" class="form-control"
|
||||
placeholder="123456">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">電話番号</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="user_mobile" value="{{ request('user_mobile') }}" class="form-control">
|
||||
<input type="text" name="user_mobile" value="{{ request('user_mobile') }}" class="form-control"
|
||||
placeholder="08011112222">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">メールアドレス</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="user_primemail" value="{{ request('user_primemail') }}" class="form-control">
|
||||
<input type="text" name="user_primemail" value="{{ request('user_primemail') }}" class="form-control"
|
||||
placeholder="キーワード...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">勤務先</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="user_workplace" value="{{ request('user_workplace') }}" class="form-control">
|
||||
<input type="text" name="user_workplace" value="{{ request('user_workplace') }}" class="form-control"
|
||||
placeholder="キーワード...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">学校</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="user_school" value="{{ request('user_school') }}" class="form-control">
|
||||
<input type="text" name="user_school" value="{{ request('user_school') }}" class="form-control"
|
||||
placeholder="キーワード...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -130,55 +158,97 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-2">
|
||||
<button type="submit" class="btn btn-default">絞り込み</button>
|
||||
<a href="{{ route('contractor') }}" class="btn btn-default">解除</a>
|
||||
{{-- ▼ 絞り込みボタン --}}
|
||||
<div class="mt-2 mb-3 text-start">
|
||||
<button type="submit" class="btn btn-default px-4">絞り込み</button>
|
||||
<a href="{{ route('contractor') }}" class="btn btn-default px-4">解除</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{-- ページネーションのラッパーを追加 --}}
|
||||
<div class="container-fluid mb20 pagination-area">
|
||||
{{-- ページネーションのビジュアルラッパー --}}
|
||||
<div class="pagination-wrapper mt-2">
|
||||
<nav aria-label="ページネーション">
|
||||
{{-- Bootstrap のリンク出力(元の呼び出しをそのまま利用) --}}
|
||||
{{ $rows->appends(request()->except('page'))->links('pagination::bootstrap-4') }}
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{{-- 一覧テーブル --}}
|
||||
{{-- ▼ 一覧テーブル部:スクロール対応 --}}
|
||||
<div class="col-lg-12 row sample03-wrapper no_padding_right mb20">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-hover table-sm rv-table text-nowrap">
|
||||
<thead>
|
||||
<table class="table table-bordered table-hover table-sm rv-table text-nowrap mb-0">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
@php($activeSort = request('sort'))
|
||||
@php($activeType = request('sort_type'))
|
||||
<th
|
||||
class="sorting {{ $activeSort === 'rc.user_id' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
|
||||
sort="rc.user_id"
|
||||
aria-sort="{{ $activeSort === 'rc.user_id' ? ($activeType === 'asc' ? 'ascending' : 'descending') : 'none' }}">
|
||||
<span class="th-inner"><span class="th-label">利用者ID</span><span class="th-arrows"><span
|
||||
class="up">↑</span><span class="down">↓</span></span></span>
|
||||
>
|
||||
<span class="sort-label">
|
||||
利用者ID
|
||||
<span class="sort-arrows" aria-hidden="true">
|
||||
<span class="up">↑</span>
|
||||
<span class="down">↓</span>
|
||||
</span>
|
||||
</span>
|
||||
</th>
|
||||
|
||||
<th
|
||||
class="sorting {{ $activeSort === 'u.user_name' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
|
||||
sort="u.user_name"
|
||||
aria-sort="{{ $activeSort === 'u.user_name' ? ($activeType === 'asc' ? 'ascending' : 'descending') : 'none' }}">
|
||||
<span class="th-inner"><span class="th-label">氏名</span><span class="th-arrows"><span
|
||||
class="up">↑</span><span class="down">↓</span></span></span>
|
||||
>
|
||||
<span class="sort-label">
|
||||
氏名
|
||||
<span class="sort-arrows" aria-hidden="true">
|
||||
<span class="up">↑</span>
|
||||
<span class="down">↓</span>
|
||||
</span>
|
||||
</span>
|
||||
</th>
|
||||
|
||||
<th>フリガナ</th>
|
||||
|
||||
<th
|
||||
class="sorting {{ $activeSort === 'rc.contract_id' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
|
||||
sort="rc.contract_id"
|
||||
aria-sort="{{ $activeSort === 'rc.contract_id' ? ($activeType === 'asc' ? 'ascending' : 'descending') : 'none' }}">
|
||||
<span class="th-inner"><span class="th-label">定期契約ID</span><span class="th-arrows"><span
|
||||
class="up">↑</span><span class="down">↓</span></span></span>
|
||||
>
|
||||
<span class="sort-label">
|
||||
定期契約ID
|
||||
<span class="sort-arrows" aria-hidden="true">
|
||||
<span class="up">↑</span>
|
||||
<span class="down">↓</span>
|
||||
</span>
|
||||
</span>
|
||||
</th>
|
||||
|
||||
<th
|
||||
class="sorting {{ $activeSort === 'rc.tag_qr_flag' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
|
||||
sort="rc.tag_qr_flag"
|
||||
aria-sort="{{ $activeSort === 'rc.tag_qr_flag' ? ($activeType === 'asc' ? 'ascending' : 'descending') : 'none' }}">
|
||||
<span class="th-inner"><span class="th-label">タグ・QR</span><span class="th-arrows"><span
|
||||
class="up">↑</span><span class="down">↓</span></span></span>
|
||||
>
|
||||
<span class="sort-label">
|
||||
タグ・QR
|
||||
<span class="sort-arrows" aria-hidden="true">
|
||||
<span class="up">↑</span>
|
||||
<span class="down">↓</span>
|
||||
</span>
|
||||
</span>
|
||||
</th>
|
||||
|
||||
<th
|
||||
class="sorting {{ $activeSort === 'p.park_name' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
|
||||
sort="p.park_name"
|
||||
aria-sort="{{ $activeSort === 'p.park_name' ? ($activeType === 'asc' ? 'ascending' : 'descending') : 'none' }}">
|
||||
<span class="th-inner"><span class="th-label">駐輪場</span><span class="th-arrows"><span
|
||||
class="up">↑</span><span class="down">↓</span></span></span>
|
||||
>
|
||||
<span class="sort-label">
|
||||
駐輪場
|
||||
<span class="sort-arrows" aria-hidden="true">
|
||||
<span class="up">↑</span>
|
||||
<span class="down">↓</span>
|
||||
</span>
|
||||
</span>
|
||||
</th>
|
||||
<th>車種区分</th>
|
||||
<th>減免措置</th>
|
||||
@ -193,29 +263,32 @@
|
||||
<th>居住所:都道府県</th>
|
||||
<th>居住所:市区群</th>
|
||||
<th>居住所:住所</th>
|
||||
<th>関連住所:郵便番号</th>
|
||||
<th>関連住所:都道府県</th>
|
||||
<th>関連住所:市区群</th>
|
||||
<th>関連住所:住所</th>
|
||||
<th>契約日</th>
|
||||
<th>利用期間</th>
|
||||
<th>有効期間</th>
|
||||
<th>定期券区分</th>
|
||||
<th>勤務先名</th>
|
||||
<th>学校</th>
|
||||
<th>卒業予定</th>
|
||||
<th>シール発行回数</th>
|
||||
<th>防犯登録</th>
|
||||
<th>備考</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse ($rows as $row)
|
||||
<tr>
|
||||
<td>{{ $row->user_id }}</td>
|
||||
<td>
|
||||
<a href="{{ route('users_edit', ['seq' => $row->user_seq ?? $row->user_id]) }}">
|
||||
{{ $row->user_id }}
|
||||
</a>
|
||||
</td>
|
||||
|
||||
<td>{{ $row->user_name }}</td>
|
||||
<td>{{ $row->user_phonetic }}</td>
|
||||
<td>{{ $row->contract_id }}</td>
|
||||
|
||||
<td>
|
||||
<a href="{{ route('regularcontracts_edit', ['contract_id' => $row->contract_id]) }}">
|
||||
{{ $row->contract_id }}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ $row->tag_qr_flag ? 'QR' : 'タグ' }}</td>
|
||||
<td>{{ $row->park_name }}</td>
|
||||
<td>{{ $row->vehicle_type ?? '' }}</td>
|
||||
@ -231,83 +304,78 @@
|
||||
<td>{{ $row->user_regident_pre }}</td>
|
||||
<td>{{ $row->user_regident_city }}</td>
|
||||
<td>{{ $row->user_regident_add }}</td>
|
||||
<td>{{ $row->user_relate_zip }}</td>
|
||||
<td>{{ $row->user_relate_pre }}</td>
|
||||
<td>{{ $row->user_relate_city }}</td>
|
||||
<td>{{ $row->user_relate_add }}</td>
|
||||
<td>{{ $row->contract_created_at }}</td>
|
||||
<td>{{ $row->contract_periods }}</td>
|
||||
<td>{{ $row->contract_periode }}</td>
|
||||
<td>{{ $row->ticket_type ?? '' }}</td>
|
||||
<td>
|
||||
{{-- 契約日を「yyyymmdd」形式で表示する --}}
|
||||
{{ $row->contract_created_at
|
||||
? \Illuminate\Support\Carbon::parse($row->contract_created_at)->format('Ymd')
|
||||
: '' }}
|
||||
</td>
|
||||
<td>
|
||||
<?php
|
||||
// 直近ラベルを計算(本日と有効期間終了日の月差で判定)
|
||||
$label = '';
|
||||
try {
|
||||
if (!empty($row->contract_periode)) {
|
||||
$today = \Illuminate\Support\Carbon::now()->startOfDay();
|
||||
$end = \Illuminate\Support\Carbon::parse($row->contract_periode)->startOfDay();
|
||||
|
||||
if ($end->lte($today)) {
|
||||
$months = $end->diffInMonths($today);
|
||||
if ($months <= 1) {
|
||||
$label = '直近1ヶ月';
|
||||
} elseif ($months <= 3) {
|
||||
$label = '直近3ヶ月';
|
||||
} elseif ($months <= 6) {
|
||||
$label = '直近6ヶ月';
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$label = '';
|
||||
}
|
||||
?>
|
||||
{{ $label }}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
{{ \Carbon\Carbon::parse($row->contract_periods)->format('Y-m-d') }}
|
||||
~
|
||||
{{ \Carbon\Carbon::parse($row->contract_periode)->format('Y-m-d') }}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@if($row->update_flag == 1)
|
||||
継続
|
||||
@elseif($row->update_flag == 2)
|
||||
それ
|
||||
@else
|
||||
{{-- それ以外は空白 --}}
|
||||
@endif
|
||||
</td>
|
||||
<td>{{ $row->user_workplace }}</td>
|
||||
<td>{{ $row->user_school }}</td>
|
||||
<td>{{ $row->user_graduate }}</td>
|
||||
<td>{{ $row->seal_issue_count ?? '' }}</td>
|
||||
<td>{{ $row->crime_prevention ?? '' }}</td>
|
||||
<td>{{ $row->user_remarks }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="33" class="text-center">データがありません。</td>
|
||||
<td colspan="25" class="text-center">データがありません。</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="mt-3">
|
||||
{{ $rows->appends(request()->except('page'))->links('pagination::bootstrap-4') }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{-- 画面用スタイル(表頭をグレー、データ部分を白) --}}
|
||||
<style>
|
||||
.rv-table thead th {
|
||||
background: #eeeeee;
|
||||
/* settlement_transactions も薄グレー系 */
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
padding: 8px 10px;
|
||||
/* settlement_transactions のデフォルトに近い余白 */
|
||||
font-size: 0.875rem;
|
||||
/* 視認性と行高を近づける */
|
||||
}
|
||||
|
||||
.rv-table tbody tr {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
/* 矢印色制御:デフォルトはグレー、アクティブ方向のみ黒 */
|
||||
th.sorting .th-arrows .up,
|
||||
th.sorting .th-arrows .down {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
th.sorting_asc .th-arrows .up {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
th.sorting_desc .th-arrows .down {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/* アクセシビリティ向上:フォーカス時の視認性 */
|
||||
th.sorting:focus,
|
||||
th.sorting_asc:focus,
|
||||
th.sorting_desc:focus {
|
||||
outline: 2px solid #999;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
th[aria-sort] {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@push('scripts')
|
||||
<script>
|
||||
// 契約者一覧:決済トランザクション方式のクリックソート
|
||||
document.querySelectorAll('th.sorting, th.sorting_asc, th.sorting_desc').forEach(th => {
|
||||
// ▼ ソート機能(日本語コメント)
|
||||
// th 要素をクリックすると sort と sort_type を設定してフォーム送信します
|
||||
document.querySelectorAll('th[sort]').forEach(th => {
|
||||
th.classList.add('sorting'); // ベースクラスを確実に付ける
|
||||
th.addEventListener('click', function () {
|
||||
const col = this.getAttribute('sort');
|
||||
if (!col) return;
|
||||
@ -320,11 +388,155 @@
|
||||
if (current === col) {
|
||||
nextType = (currentType === 'asc') ? 'desc' : 'asc';
|
||||
}
|
||||
sortInput.value = col;
|
||||
typeInput.value = nextType;
|
||||
if (sortInput) sortInput.value = col;
|
||||
if (typeInput) typeInput.value = nextType;
|
||||
|
||||
// 視覚的にソートクラスを切り替え(矢印の色に反映)
|
||||
document.querySelectorAll('th[sort]').forEach(h => {
|
||||
h.classList.remove('sorting_asc', 'sorting_desc');
|
||||
if (!h.classList.contains('sorting')) h.classList.add('sorting');
|
||||
});
|
||||
if (nextType === 'asc') {
|
||||
this.classList.add('sorting_asc');
|
||||
} else {
|
||||
this.classList.add('sorting_desc');
|
||||
}
|
||||
|
||||
form.submit();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* ----------------------------------------
|
||||
ページネーションの横幅を確保して右寄せ(スクリーンショットは右寄せ)
|
||||
---------------------------------------- */
|
||||
.pagination-area .pagination-wrapper {
|
||||
display: flex;
|
||||
justify-content: flex-end; /* 右寄せ:必要なら center に変更 */
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
ソート矢印の色ルール(日本語コメント)
|
||||
状態:
|
||||
未ソート -> 左(上)灰色, 右(下)灰色
|
||||
昇順(asc) -> 左(上)黒色, 右(下)灰色
|
||||
降順(desc) -> 左(上)灰色, 右(下)黒色
|
||||
======================================== */
|
||||
|
||||
/* 既存の擬似要素で出る矢印を無効化(保険) */
|
||||
th.sorting::after,
|
||||
th.sorting_asc::after,
|
||||
th.sorting_desc::after {
|
||||
display: none !important;
|
||||
content: none !important;
|
||||
background-image: none !important;
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
/* 矢印を水平に並べる(左: up, 右: down) */
|
||||
th[sort] .sort-arrows,
|
||||
th.sorting .sort-arrows,
|
||||
th.sorting_asc .sort-arrows,
|
||||
th.sorting_desc .sort-arrows {
|
||||
display: inline-flex !important;
|
||||
flex-direction: row !important;
|
||||
gap: 6px !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
line-height: 1 !important;
|
||||
font-size: 12px !important;
|
||||
user-select: none !important;
|
||||
width: auto !important;
|
||||
box-sizing: border-box;
|
||||
vertical-align: middle;
|
||||
color: #bfc9d6 !important; /* デフォルト灰 */
|
||||
}
|
||||
|
||||
th[sort] .sort-arrows .up,
|
||||
th[sort] .sort-arrows .down,
|
||||
th.sorting .sort-arrows .up,
|
||||
th.sorting .sort-arrows .down {
|
||||
display: inline-block !important;
|
||||
min-width: 14px;
|
||||
text-align: center;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* 未ソート: 両方灰 */
|
||||
th.sorting:not(.sorting_asc):not(.sorting_desc) .sort-arrows .up,
|
||||
th.sorting:not(.sorting_asc):not(.sorting_desc) .sort-arrows .down {
|
||||
color: #bfc9d6 !important;
|
||||
}
|
||||
|
||||
/* 昇順: 左(上) 黒、右(下) 灰 */
|
||||
th.sorting_asc .sort-arrows .up,
|
||||
th[sort].sorting_asc .sort-arrows .up {
|
||||
color: #000000 !important;
|
||||
}
|
||||
th.sorting_asc .sort-arrows .down,
|
||||
th[sort].sorting_asc .sort-arrows .down {
|
||||
color: #bfc9d6 !important;
|
||||
}
|
||||
|
||||
/* 降順: 左(上) 灰、右(下) 黒 */
|
||||
th.sorting_desc .sort-arrows .up,
|
||||
th[sort].sorting_desc .sort-arrows .up {
|
||||
color: #bfc9d6 !important;
|
||||
}
|
||||
th.sorting_desc .sort-arrows .down,
|
||||
th[sort].sorting_desc .sort-arrows .down {
|
||||
color: #000000 !important;
|
||||
}
|
||||
|
||||
/* 矢印間を詰めたい場合(例: 見た目で letter-spacing 相当) */
|
||||
th[sort] .sort-arrows { gap: 0 !important; }
|
||||
th[sort] .sort-arrows .down { margin-left: -5px !important; }
|
||||
|
||||
/* 矢印の下線を消す(リンク内にある場合の保険) */
|
||||
th .sort-arrows,
|
||||
th .sort-label,
|
||||
th[sort] .sort-arrows,
|
||||
th.sorting .sort-arrows {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
th .sort-label a,
|
||||
th .sort-arrows a {
|
||||
text-decoration: none !important;
|
||||
border-bottom: none !important;
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
/* SVG 対応(矢印が svg の場合) */
|
||||
th[sort] .sort-arrows svg path,
|
||||
th.sorting .sort-arrows svg path {
|
||||
fill: #bfc9d6 !important;
|
||||
}
|
||||
th.sorting_asc .sort-arrows svg path,
|
||||
th[sort].sorting_asc .sort-arrows svg path {
|
||||
fill: #000 !important;
|
||||
}
|
||||
th.sorting_desc .sort-arrows svg path,
|
||||
th[sort].sorting_desc .sort-arrows svg path {
|
||||
fill: #000 !important;
|
||||
}
|
||||
|
||||
/* レスポンシブ:小画面で間隔を狭める */
|
||||
@media (max-width: 575.98px) {
|
||||
th[sort] .sort-arrows { gap: 4px !important; font-size: 11px !important; }
|
||||
}
|
||||
|
||||
/* フォーカス時の視認性(アクセシビリティ) */
|
||||
th.sorting:focus,
|
||||
th.sorting_asc:focus,
|
||||
th.sorting_desc:focus {
|
||||
outline: 2px solid rgba(0,0,0,0.08);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@endpush
|
||||
@endsection
|
||||
@ -20,14 +20,21 @@
|
||||
</div>
|
||||
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
|
||||
{{-- 絞り込みフィルター --}}
|
||||
<form method="GET" action="{{ route('contractor_List') }}" class="mb-3" id="list-form">
|
||||
<input type="hidden" name="sort" value="{{ $sort ?? request('sort','rc.contract_id') }}">
|
||||
<input type="hidden" name="sort_type" value="{{ $sortType ?? request('sort_type','desc') }}">
|
||||
<div class="card p-3">
|
||||
<h6 class="mb-3">絞り込みフィルター</h6>
|
||||
<input type="hidden" name="sort" value="{{ $sort ?? request('sort') }}">
|
||||
<input type="hidden" name="sort_type" value="{{ $sortType ?? request('sort_type') }}">
|
||||
|
||||
{{-- ▼ カード全体 --}}
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
{{-- ▼ 絞り込みフィルター(card-header) --}}
|
||||
<div class="card-header" style="font-weight:bold;">
|
||||
絞り込みフィルター
|
||||
</div>
|
||||
|
||||
{{-- ▼ フィルター条件エリア(card-body) --}}
|
||||
<div class="card-body bg-white">
|
||||
<div class="row">
|
||||
{{-- 左カラム --}}
|
||||
<div class="col-md-6">
|
||||
@ -44,7 +51,8 @@
|
||||
@endforeach
|
||||
</select>
|
||||
@else
|
||||
<input type="text" name="park_name" value="{{ request('park_name') }}" class="form-control">
|
||||
<input type="text" name="park_name" value="{{ request('park_name') }}" class="form-control"
|
||||
placeholder="123456">
|
||||
@endisset
|
||||
</div>
|
||||
</div>
|
||||
@ -52,16 +60,29 @@
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">利用者ID</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="user_id" value="{{ request('user_id') }}" class="form-control">
|
||||
<input type="text" name="user_id" value="{{ request('user_id') }}" class="form-control"
|
||||
placeholder="123456">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ===== 利用者分類:input を select に変更 ===== --}}
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">利用者分類</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="user_category1"
|
||||
value="{{ request('user_category1') }}"
|
||||
class="form-control" placeholder="分類名1">
|
||||
@isset($categories)
|
||||
<select name="user_category1" class="form-control">
|
||||
<option value="">全て</option>
|
||||
@foreach($categories as $key => $label)
|
||||
<option value="{{ $key }}"
|
||||
{{ (string) request('user_category1') === (string) $key ? 'selected' : '' }}>
|
||||
{{ $label }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@else
|
||||
<input type="text" name="user_category1" value="{{ request('user_category1') }}" class="form-control"
|
||||
placeholder="123456">
|
||||
@endisset
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -69,7 +90,7 @@
|
||||
<label class="col-sm-3 col-form-label">タグシリアル64進</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="user_tag_serial_64" value="{{ request('user_tag_serial_64') }}"
|
||||
class="form-control">
|
||||
class="form-control" placeholder="キーワード...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -87,35 +108,40 @@
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">フリガナ</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="user_phonetic" value="{{ request('user_phonetic') }}" class="form-control">
|
||||
<input type="text" name="user_phonetic" value="{{ request('user_phonetic') }}" class="form-control"
|
||||
placeholder="123456">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">電話番号</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="user_mobile" value="{{ request('user_mobile') }}" class="form-control">
|
||||
<input type="text" name="user_mobile" value="{{ request('user_mobile') }}" class="form-control"
|
||||
placeholder="08011112222">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">メールアドレス</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="user_primemail" value="{{ request('user_primemail') }}" class="form-control">
|
||||
<input type="text" name="user_primemail" value="{{ request('user_primemail') }}" class="form-control"
|
||||
placeholder="キーワード...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">勤務先</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="user_workplace" value="{{ request('user_workplace') }}" class="form-control">
|
||||
<input type="text" name="user_workplace" value="{{ request('user_workplace') }}" class="form-control"
|
||||
placeholder="キーワード...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">学校</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="user_school" value="{{ request('user_school') }}" class="form-control">
|
||||
<input type="text" name="user_school" value="{{ request('user_school') }}" class="form-control"
|
||||
placeholder="キーワード...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -132,57 +158,100 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-2">
|
||||
<button type="submit" class="btn btn-default">絞り込み</button>
|
||||
<a href="{{ route('contractor_List') }}" class="btn btn-default">解除</a>
|
||||
{{-- ▼ 絞り込みボタン --}}
|
||||
<div class="mt-2 mb-3 text-start">
|
||||
<button type="submit" class="btn btn-default px-4">絞り込み</button>
|
||||
<a href="{{ route('contractor_List') }}" class="btn btn-default px-4">解除</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{-- ページネーションのラッパーを追加 --}}
|
||||
<div class="container-fluid mb20 pagination-area">
|
||||
{{-- ページネーションのビジュアルラッパー --}}
|
||||
<div class="pagination-wrapper mt-2">
|
||||
<nav aria-label="ページネーション">
|
||||
{{-- Bootstrap のリンク出力(元の呼び出しをそのまま利用) --}}
|
||||
{{ $rows->appends(request()->except('page'))->links('pagination::bootstrap-4') }}
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{{-- 一覧テーブル --}}
|
||||
{{-- ▼ 一覧テーブル部:スクロール対応 --}}
|
||||
<div class="col-lg-12 row sample03-wrapper no_padding_right mb20">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-hover table-sm rv-table text-nowrap">
|
||||
<thead>
|
||||
<table class="table table-bordered table-hover table-sm rv-table text-nowrap mb-0">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
@php($activeSort = request('sort'))
|
||||
@php($activeType = request('sort_type'))
|
||||
<th
|
||||
class="sorting {{ $activeSort === 'rc.user_id' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
|
||||
sort="rc.user_id"
|
||||
aria-sort="{{ $activeSort === 'rc.user_id' ? ($activeType === 'asc' ? 'ascending' : 'descending') : 'none' }}">
|
||||
<span class="th-inner"><span class="th-label">利用者ID</span><span class="th-arrows"><span
|
||||
class="up">↑</span><span class="down">↓</span></span></span>
|
||||
>
|
||||
<span class="sort-label">
|
||||
利用者ID
|
||||
<span class="sort-arrows" aria-hidden="true">
|
||||
<span class="up">↑</span>
|
||||
<span class="down">↓</span>
|
||||
</span>
|
||||
</span>
|
||||
</th>
|
||||
|
||||
<th
|
||||
class="sorting {{ $activeSort === 'u.user_name' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
|
||||
sort="u.user_name"
|
||||
aria-sort="{{ $activeSort === 'u.user_name' ? ($activeType === 'asc' ? 'ascending' : 'descending') : 'none' }}">
|
||||
<span class="th-inner"><span class="th-label">氏名</span><span class="th-arrows"><span
|
||||
class="up">↑</span><span class="down">↓</span></span></span>
|
||||
>
|
||||
<span class="sort-label">
|
||||
氏名
|
||||
<span class="sort-arrows" aria-hidden="true">
|
||||
<span class="up">↑</span>
|
||||
<span class="down">↓</span>
|
||||
</span>
|
||||
</span>
|
||||
</th>
|
||||
|
||||
<th>フリガナ</th>
|
||||
|
||||
<th
|
||||
class="sorting {{ $activeSort === 'rc.contract_id' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
|
||||
sort="rc.contract_id"
|
||||
aria-sort="{{ $activeSort === 'rc.contract_id' ? ($activeType === 'asc' ? 'ascending' : 'descending') : 'none' }}">
|
||||
<span class="th-inner"><span class="th-label">定期契約ID</span><span class="th-arrows"><span
|
||||
class="up">↑</span><span class="down">↓</span></span></span>
|
||||
>
|
||||
<span class="sort-label">
|
||||
定期契約ID
|
||||
<span class="sort-arrows" aria-hidden="true">
|
||||
<span class="up">↑</span>
|
||||
<span class="down">↓</span>
|
||||
</span>
|
||||
</span>
|
||||
</th>
|
||||
|
||||
<th
|
||||
class="sorting {{ $activeSort === 'rc.tag_qr_flag' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
|
||||
sort="rc.tag_qr_flag"
|
||||
aria-sort="{{ $activeSort === 'rc.tag_qr_flag' ? ($activeType === 'asc' ? 'ascending' : 'descending') : 'none' }}">
|
||||
<span class="th-inner"><span class="th-label">タグ・QR</span><span class="th-arrows"><span
|
||||
class="up">↑</span><span class="down">↓</span></span></span>
|
||||
>
|
||||
<span class="sort-label">
|
||||
タグ・QR
|
||||
<span class="sort-arrows" aria-hidden="true">
|
||||
<span class="up">↑</span>
|
||||
<span class="down">↓</span>
|
||||
</span>
|
||||
</span>
|
||||
</th>
|
||||
|
||||
<th
|
||||
class="sorting {{ $activeSort === 'p.park_name' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
|
||||
sort="p.park_name"
|
||||
aria-sort="{{ $activeSort === 'p.park_name' ? ($activeType === 'asc' ? 'ascending' : 'descending') : 'none' }}">
|
||||
<span class="th-inner"><span class="th-label">駐輪場</span><span class="th-arrows"><span
|
||||
class="up">↑</span><span class="down">↓</span></span></span>
|
||||
>
|
||||
<span class="sort-label">
|
||||
駐輪場
|
||||
<span class="sort-arrows" aria-hidden="true">
|
||||
<span class="up">↑</span>
|
||||
<span class="down">↓</span>
|
||||
</span>
|
||||
</span>
|
||||
</th>
|
||||
<th>車種区分</th>
|
||||
<th>有効期間</th>
|
||||
<th>減免措置</th>
|
||||
<th>利用者分類1</th>
|
||||
<th>利用者分類2</th>
|
||||
@ -195,15 +264,13 @@
|
||||
<th>居住所:都道府県</th>
|
||||
<th>居住所:市区群</th>
|
||||
<th>居住所:住所</th>
|
||||
<th>関連住所:郵便番号</th>
|
||||
<th>関連住所:都道府県</th>
|
||||
<th>関連住所:市区群</th>
|
||||
<th>関連住所:住所</th>
|
||||
<th>関連住所:郵便番号</th>
|
||||
<th>関連住所:都道府県</th>
|
||||
<th>関連住所:市区群</th>
|
||||
<th>関連住所:住所</th>
|
||||
<th>契約日</th>
|
||||
<th>利用期間</th>
|
||||
<th>有効期間</th>
|
||||
<th>定期券区分</th>
|
||||
<th>勤務先名</th>
|
||||
<th>学校</th>
|
||||
<th>卒業予定</th>
|
||||
<th>シール発行回数</th>
|
||||
@ -214,13 +281,28 @@
|
||||
<tbody>
|
||||
@forelse ($rows as $row)
|
||||
<tr>
|
||||
<td>{{ $row->user_id }}</td>
|
||||
<td>
|
||||
<a href="{{ route('users_edit', ['seq' => $row->user_seq ?? $row->user_id]) }}">
|
||||
{{ $row->user_id }}
|
||||
</a>
|
||||
</td>
|
||||
|
||||
<td>{{ $row->user_name }}</td>
|
||||
<td>{{ $row->user_phonetic }}</td>
|
||||
<td>{{ $row->contract_id }}</td>
|
||||
|
||||
<td>
|
||||
<a href="{{ route('regularcontracts_edit', ['contract_id' => $row->contract_id]) }}">
|
||||
{{ $row->contract_id }}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ $row->tag_qr_flag ? 'QR' : 'タグ' }}</td>
|
||||
<td>{{ $row->park_name }}</td>
|
||||
<td>{{ $row->vehicle_type ?? '' }}</td>
|
||||
<td>
|
||||
{{ \Carbon\Carbon::parse($row->contract_periods)->format('Y-m-d') }}
|
||||
~
|
||||
{{ \Carbon\Carbon::parse($row->contract_periode)->format('Y-m-d') }}
|
||||
</td>
|
||||
<td>{{ $row->user_reduction ?? '' }}</td>
|
||||
<td>{{ $row->user_category1 ?? '' }}</td>
|
||||
<td>{{ $row->user_category2 ?? '' }}</td>
|
||||
@ -237,79 +319,75 @@
|
||||
<td>{{ $row->user_relate_pre }}</td>
|
||||
<td>{{ $row->user_relate_city }}</td>
|
||||
<td>{{ $row->user_relate_add }}</td>
|
||||
<td>{{ $row->contract_created_at }}</td>
|
||||
<td>{{ $row->contract_periods }}</td>
|
||||
<td>{{ $row->contract_periode }}</td>
|
||||
<td>{{ $row->ticket_type ?? '' }}</td>
|
||||
<td>{{ $row->user_workplace }}</td>
|
||||
<td>
|
||||
{{-- 契約日を「yyyymmdd」形式で表示する --}}
|
||||
{{ $row->contract_created_at
|
||||
? \Illuminate\Support\Carbon::parse($row->contract_created_at)->format('Ymd')
|
||||
: '' }}
|
||||
</td>
|
||||
<td>
|
||||
<?php
|
||||
// 直近ラベルを計算(本日と有効期間終了日の月差で判定)
|
||||
$label = '';
|
||||
try {
|
||||
if (!empty($row->contract_periode)) {
|
||||
$today = \Illuminate\Support\Carbon::now()->startOfDay();
|
||||
$end = \Illuminate\Support\Carbon::parse($row->contract_periode)->startOfDay();
|
||||
|
||||
if ($end->lte($today)) {
|
||||
$months = $end->diffInMonths($today);
|
||||
if ($months <= 1) {
|
||||
$label = '直近1ヶ月';
|
||||
} elseif ($months <= 3) {
|
||||
$label = '直近3ヶ月';
|
||||
} elseif ($months <= 6) {
|
||||
$label = '直近6ヶ月';
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$label = '';
|
||||
}
|
||||
?>
|
||||
{{ $label }}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@if($row->update_flag == 1)
|
||||
継続
|
||||
@elseif($row->update_flag == 2)
|
||||
それ
|
||||
@else
|
||||
{{-- それ以外は空白 --}}
|
||||
@endif
|
||||
</td>
|
||||
|
||||
<td>{{ $row->user_school }}</td>
|
||||
<td>{{ $row->user_graduate }}</td>
|
||||
<td>{{ $row->seal_issue_count ?? '' }}</td>
|
||||
<td>{{ $row->crime_prevention ?? '' }}</td>
|
||||
<td>{{ $row->contract_seal_issue }}</td>
|
||||
<td>{{ $row->user_securitynum }}</td>
|
||||
<td>{{ $row->user_remarks }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="33" class="text-center">データがありません。</td>
|
||||
<td colspan="25" class="text-center">データがありません。</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="mt-3">
|
||||
{{ $rows->appends(request()->except('page'))->links('pagination::bootstrap-4') }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{-- 画面用スタイル(表頭をグレー、データ部分を白) --}}
|
||||
<style>
|
||||
.rv-table thead th {
|
||||
background: #eeeeee;
|
||||
/* settlement_transactions も薄グレー系 */
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
padding: 8px 10px;
|
||||
/* settlement_transactions のデフォルトに近い余白 */
|
||||
font-size: 0.875rem;
|
||||
/* 視認性と行高を近づける */
|
||||
}
|
||||
|
||||
.rv-table tbody tr {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
/* 矢印色制御:デフォルトはグレー、アクティブ方向のみ黒 */
|
||||
th.sorting .th-arrows .up,
|
||||
th.sorting .th-arrows .down {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
th.sorting_asc .th-arrows .up {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
th.sorting_desc .th-arrows .down {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/* アクセシビリティ向上:フォーカス時の視認性 */
|
||||
th.sorting:focus,
|
||||
th.sorting_asc:focus,
|
||||
th.sorting_desc:focus {
|
||||
outline: 2px solid #999;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
th[aria-sort] {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@push('scripts')
|
||||
<script>
|
||||
// 未更新者一覧:決済トランザクション方式のクリックソート
|
||||
document.querySelectorAll('th.sorting, th.sorting_asc, th.sorting_desc').forEach(th => {
|
||||
// ▼ ソート機能(日本語コメント)
|
||||
// th 要素をクリックすると sort と sort_type を設定してフォーム送信します
|
||||
document.querySelectorAll('th[sort]').forEach(th => {
|
||||
th.classList.add('sorting'); // ベースクラスを確実に付ける
|
||||
th.addEventListener('click', function () {
|
||||
const col = this.getAttribute('sort');
|
||||
if (!col) return;
|
||||
@ -322,11 +400,155 @@
|
||||
if (current === col) {
|
||||
nextType = (currentType === 'asc') ? 'desc' : 'asc';
|
||||
}
|
||||
sortInput.value = col;
|
||||
typeInput.value = nextType;
|
||||
if (sortInput) sortInput.value = col;
|
||||
if (typeInput) typeInput.value = nextType;
|
||||
|
||||
// 視覚的にソートクラスを切り替え(矢印の色に反映)
|
||||
document.querySelectorAll('th[sort]').forEach(h => {
|
||||
h.classList.remove('sorting_asc', 'sorting_desc');
|
||||
if (!h.classList.contains('sorting')) h.classList.add('sorting');
|
||||
});
|
||||
if (nextType === 'asc') {
|
||||
this.classList.add('sorting_asc');
|
||||
} else {
|
||||
this.classList.add('sorting_desc');
|
||||
}
|
||||
|
||||
form.submit();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* ----------------------------------------
|
||||
ページネーションの横幅を確保して右寄せ(スクリーンショットは右寄せ)
|
||||
---------------------------------------- */
|
||||
.pagination-area .pagination-wrapper {
|
||||
display: flex;
|
||||
justify-content: flex-end; /* 右寄せ:必要なら center に変更 */
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
ソート矢印の色ルール(日本語コメント)
|
||||
状態:
|
||||
未ソート -> 左(上)灰色, 右(下)灰色
|
||||
昇順(asc) -> 左(上)黒色, 右(下)灰色
|
||||
降順(desc) -> 左(上)灰色, 右(下)黒色
|
||||
======================================== */
|
||||
|
||||
/* 既存の擬似要素で出る矢印を無効化(保険) */
|
||||
th.sorting::after,
|
||||
th.sorting_asc::after,
|
||||
th.sorting_desc::after {
|
||||
display: none !important;
|
||||
content: none !important;
|
||||
background-image: none !important;
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
/* 矢印を水平に並べる(左: up, 右: down) */
|
||||
th[sort] .sort-arrows,
|
||||
th.sorting .sort-arrows,
|
||||
th.sorting_asc .sort-arrows,
|
||||
th.sorting_desc .sort-arrows {
|
||||
display: inline-flex !important;
|
||||
flex-direction: row !important;
|
||||
gap: 6px !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
line-height: 1 !important;
|
||||
font-size: 12px !important;
|
||||
user-select: none !important;
|
||||
width: auto !important;
|
||||
box-sizing: border-box;
|
||||
vertical-align: middle;
|
||||
color: #bfc9d6 !important; /* デフォルト灰 */
|
||||
}
|
||||
|
||||
th[sort] .sort-arrows .up,
|
||||
th[sort] .sort-arrows .down,
|
||||
th.sorting .sort-arrows .up,
|
||||
th.sorting .sort-arrows .down {
|
||||
display: inline-block !important;
|
||||
min-width: 14px;
|
||||
text-align: center;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* 未ソート: 両方灰 */
|
||||
th.sorting:not(.sorting_asc):not(.sorting_desc) .sort-arrows .up,
|
||||
th.sorting:not(.sorting_asc):not(.sorting_desc) .sort-arrows .down {
|
||||
color: #bfc9d6 !important;
|
||||
}
|
||||
|
||||
/* 昇順: 左(上) 黒、右(下) 灰 */
|
||||
th.sorting_asc .sort-arrows .up,
|
||||
th[sort].sorting_asc .sort-arrows .up {
|
||||
color: #000000 !important;
|
||||
}
|
||||
th.sorting_asc .sort-arrows .down,
|
||||
th[sort].sorting_asc .sort-arrows .down {
|
||||
color: #bfc9d6 !important;
|
||||
}
|
||||
|
||||
/* 降順: 左(上) 灰、右(下) 黒 */
|
||||
th.sorting_desc .sort-arrows .up,
|
||||
th[sort].sorting_desc .sort-arrows .up {
|
||||
color: #bfc9d6 !important;
|
||||
}
|
||||
th.sorting_desc .sort-arrows .down,
|
||||
th[sort].sorting_desc .sort-arrows .down {
|
||||
color: #000000 !important;
|
||||
}
|
||||
|
||||
/* 矢印間を詰めたい場合(例: 見た目で letter-spacing 相当) */
|
||||
th[sort] .sort-arrows { gap: 0 !important; }
|
||||
th[sort] .sort-arrows .down { margin-left: -5px !important; }
|
||||
|
||||
/* 矢印の下線を消す(リンク内にある場合の保険) */
|
||||
th .sort-arrows,
|
||||
th .sort-label,
|
||||
th[sort] .sort-arrows,
|
||||
th.sorting .sort-arrows {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
th .sort-label a,
|
||||
th .sort-arrows a {
|
||||
text-decoration: none !important;
|
||||
border-bottom: none !important;
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
/* SVG 対応(矢印が svg の場合) */
|
||||
th[sort] .sort-arrows svg path,
|
||||
th.sorting .sort-arrows svg path {
|
||||
fill: #bfc9d6 !important;
|
||||
}
|
||||
th.sorting_asc .sort-arrows svg path,
|
||||
th[sort].sorting_asc .sort-arrows svg path {
|
||||
fill: #000 !important;
|
||||
}
|
||||
th.sorting_desc .sort-arrows svg path,
|
||||
th[sort].sorting_desc .sort-arrows svg path {
|
||||
fill: #000 !important;
|
||||
}
|
||||
|
||||
/* レスポンシブ:小画面で間隔を狭める */
|
||||
@media (max-width: 575.98px) {
|
||||
th[sort] .sort-arrows { gap: 4px !important; font-size: 11px !important; }
|
||||
}
|
||||
|
||||
/* フォーカス時の視認性(アクセシビリティ) */
|
||||
th.sorting:focus,
|
||||
th.sorting_asc:focus,
|
||||
th.sorting_desc:focus {
|
||||
outline: 2px solid rgba(0,0,0,0.08);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@endpush
|
||||
@endsection
|
||||
@ -379,9 +379,7 @@
|
||||
@endphp
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-right {{ $sortClass('contract_id') }}" sort="contract_id" style="white-space:nowrap;">
|
||||
契約ID {!! $renderSortIcon('contract_id') !!}
|
||||
</th>
|
||||
<th style="width:50px; text-align:right;"></th>
|
||||
{{-- 操作列(チェック+編集。背景色を付与) --}}
|
||||
<th style="width:120px">
|
||||
<div class="d-flex align-items-center">
|
||||
@ -389,7 +387,9 @@
|
||||
<span></span>
|
||||
</div>
|
||||
</th>
|
||||
|
||||
<th class="text-right {{ $sortClass('contract_id') }}" sort="contract_id" style="white-space:nowrap;">
|
||||
契約ID {!! $renderSortIcon('contract_id') !!}
|
||||
</th>
|
||||
{{-- 表頭(要件に合わせて網羅) --}}
|
||||
<th class="text-right {{ $sortClass('contract_qr_id') }}" sort="contract_qr_id">
|
||||
定期契約ID {!! $renderSortIcon('contract_qr_id') !!}
|
||||
@ -523,8 +523,8 @@
|
||||
@endphp
|
||||
@foreach($list as $item)
|
||||
<tr>
|
||||
{{-- 先頭の並び替え列(行の contract_id を表示) --}}
|
||||
<td class="text-right">{{ $item->contract_id }}</td>
|
||||
|
||||
<td class="text-right">{{ $loop->iteration }}</td>
|
||||
|
||||
{{-- 操作列:チェック + 編集(背景色つき) --}}
|
||||
<td class="op-cell">
|
||||
@ -535,6 +535,8 @@
|
||||
</div>
|
||||
</td>
|
||||
|
||||
{{-- 先頭の並び替え列(行の contract_id を表示) --}}
|
||||
<td class="text-right">{{ $item->contract_id }}</td>
|
||||
{{-- データ列 --}}
|
||||
<td class="text-right">{{ $item->contract_qr_id }}</td>
|
||||
<td>{{ $item->old_contract_id ?? '' }}</td>
|
||||
|
||||
@ -6,10 +6,10 @@
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<div class="col-lg-6">
|
||||
<h1 class="m-0 text-dark">予約者一覧</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="col-lg-6">
|
||||
<ol class="breadcrumb float-sm-right text-sm">
|
||||
<li class="breadcrumb-item"><a href="{{ url('/home') }}">ホーム</a></li>
|
||||
<li class="breadcrumb-item active">予約者一覧</li>
|
||||
@ -20,14 +20,23 @@
|
||||
</div>
|
||||
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
|
||||
{{-- 絞り込みフィルター(順序を画像に完全一致) --}}
|
||||
<form method="GET" action="{{ route('reservation') }}" class="mb-3" id="filter-form">
|
||||
<div class="card p-3">
|
||||
<h6 class="mb-3">絞り込みフィルター</h6>
|
||||
<form method="GET" action="{{ route('reservation') }}" class="mb-3" id="list-form">
|
||||
<input type="hidden" name="sort" value="{{ $sort ?? request('sort') }}">
|
||||
<input type="hidden" name="sort_type" value="{{ $sortType ?? request('sort_type') }}">
|
||||
|
||||
{{-- ▼ カード全体 --}}
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
{{-- ▼ 絞り込みフィルター(card-header) --}}
|
||||
<div class="card-header" style="font-weight:bold;">
|
||||
絞り込みフィルター
|
||||
</div>
|
||||
|
||||
{{-- ▼ フィルター条件エリア(card-body) --}}
|
||||
<div class="card-body bg-white">
|
||||
<div class="row">
|
||||
{{-- 左カラム:駐輪場 → 利用者ID → 利用者分類 → タグシリアル → タグシリアル64進 --}}
|
||||
{{-- 左カラム --}}
|
||||
<div class="col-md-6">
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">駐輪場</label>
|
||||
@ -42,7 +51,8 @@
|
||||
@endforeach
|
||||
</select>
|
||||
@else
|
||||
<input type="text" name="park_name" value="{{ request('park_name') }}" class="form-control" >
|
||||
<input type="text" name="park_name" value="{{ request('park_name') }}" class="form-control"
|
||||
placeholder="123456">
|
||||
@endisset
|
||||
</div>
|
||||
</div>
|
||||
@ -50,112 +60,143 @@
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">利用者ID</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="user_id" value="{{ request('user_id') }}" class="form-control" >
|
||||
<input type="text" name="user_id" value="{{ request('user_id') }}" class="form-control"
|
||||
placeholder="123456">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 利用者分類 --}}
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">利用者分類</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="user_category1" value="{{ request('user_category1') }}" class="form-control" placeholder="分類名1">
|
||||
{{-- 既存互換: IDパラメータが残る可能性があるため hidden で保持 --}}
|
||||
@if(request()->filled('user_categoryid'))
|
||||
<input type="hidden" name="user_categoryid" value="{{ request('user_categoryid') }}">
|
||||
@endif
|
||||
@isset($categories)
|
||||
<select name="user_category1" class="form-control">
|
||||
<option value="">全て</option>
|
||||
@foreach($categories as $key => $label)
|
||||
<option value="{{ $key }}" {{ (string) request('user_category1') === (string) $key ? 'selected' : '' }}>
|
||||
{{ $label }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@else
|
||||
<input type="text" name="user_category1" value="{{ request('user_category1') }}" class="form-control"
|
||||
placeholder="123456">
|
||||
@endisset
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">タグシリアル</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="user_tag_serial" value="{{ request('user_tag_serial') }}" class="form-control" >
|
||||
<input type="text" name="user_tag_serial" value="{{ request('user_tag_serial') }}"
|
||||
class="form-control" placeholder="キーワード...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">タグシリアル64進</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="user_tag_serial_64" value="{{ request('user_tag_serial_64') }}" class="form-control" >
|
||||
<input type="text" name="user_tag_serial_64" value="{{ request('user_tag_serial_64') }}"
|
||||
class="form-control" placeholder="キーワード...">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 右カラム:フリガナ → 電話番号 → メールアドレス → 勤務先 → 学校 --}}
|
||||
{{-- 右カラム --}}
|
||||
<div class="col-md-6">
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">フリガナ</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="user_phonetic" value="{{ request('user_phonetic') }}" class="form-control" >
|
||||
<input type="text" name="user_phonetic" value="{{ request('user_phonetic') }}" class="form-control"
|
||||
placeholder="123456">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">電話番号</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="user_mobile" value="{{ request('user_mobile') }}" class="form-control" >
|
||||
<input type="text" name="user_mobile" value="{{ request('user_mobile') }}" class="form-control"
|
||||
placeholder="08011112222">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">メールアドレス</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="user_primemail" value="{{ request('user_primemail') }}" class="form-control" >
|
||||
<input type="text" name="user_primemail" value="{{ request('user_primemail') }}" class="form-control"
|
||||
placeholder="キーワード...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">勤務先</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="user_workplace" value="{{ request('user_workplace') }}" class="form-control" >
|
||||
<input type="text" name="user_workplace" value="{{ request('user_workplace') }}" class="form-control"
|
||||
placeholder="キーワード...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">学校</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" name="user_school" value="{{ request('user_school') }}" class="form-control" >
|
||||
<input type="text" name="user_school" value="{{ request('user_school') }}" class="form-control"
|
||||
placeholder="キーワード...">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-2">
|
||||
<button type="submit" class="btn btn-default">絞り込み</button>
|
||||
<a href="{{ route('reservation') }}" class="btn btn-default">解除</a>
|
||||
{{-- ▼ 絞り込みボタン --}}
|
||||
<div class="card-body pt-2">
|
||||
<button type="submit" class="btn btn-default mr10">絞り込み</button>
|
||||
<a href="{{ route('reservation') }}" class="btn btn-default mr10">解除</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{-- ページネーションのラッパーを追加 --}}
|
||||
<div class="container-fluid mb20 pagination-area">
|
||||
{{-- ページネーションのビジュアルラッパー --}}
|
||||
<div class="pagination-wrapper mt-2">
|
||||
<nav aria-label="ページネーション">
|
||||
{{-- Bootstrap のリンク出力(元の呼び出しをそのまま利用) --}}
|
||||
{{ $rows->appends(request()->except('page'))->links('pagination::bootstrap-4') }}
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{{-- 一覧テーブル(ソート機能付きヘッダー) --}}
|
||||
{{-- ▼ 一覧テーブル部:スクロール対応 --}}
|
||||
<div class="col-lg-12 row sample03-wrapper no_padding_right mb20">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-hover table-sm rv-table text-nowrap">
|
||||
<thead>
|
||||
<table class="table table-bordered table-hover table-sm rv-table text-nowrap mb-0">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
{{-- ソート可能な項目にはリンクを追加 --}}
|
||||
<th>
|
||||
<a href="{{ request()->fullUrlWithQuery(['sort' => 'user_id', 'sort_type' => request('sort') === 'user_id' && request('sort_type') === 'desc' ? 'asc' : 'desc']) }}" class="text-dark text-decoration-none">
|
||||
@php($activeSort = request('sort'))
|
||||
@php($activeType = request('sort_type'))
|
||||
<th
|
||||
class="sorting {{ $activeSort === 'rc.user_id' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
|
||||
sort="rc.user_id">
|
||||
<span class="sort-label">
|
||||
利用者ID
|
||||
@if(request('sort') === 'user_id' && request('sort_type') === 'asc')
|
||||
↑<span class="text-muted">↓</span>
|
||||
@elseif(request('sort') === 'user_id' && request('sort_type') === 'desc')
|
||||
<span class="text-muted">↑</span>↓
|
||||
@else
|
||||
<span class="text-muted">↑↓</span>
|
||||
@endif
|
||||
</a>
|
||||
<span class="sort-arrows" aria-hidden="true">
|
||||
<span class="up">↑</span>
|
||||
<span class="down">↓</span>
|
||||
</span>
|
||||
</span>
|
||||
</th>
|
||||
<th>
|
||||
<a href="{{ request()->fullUrlWithQuery(['sort' => 'user_name', 'sort_type' => request('sort') === 'user_name' && request('sort_type') === 'desc' ? 'asc' : 'desc']) }}" class="text-dark text-decoration-none">
|
||||
|
||||
<th
|
||||
class="sorting {{ $activeSort === 'u.user_name' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
|
||||
sort="u.user_name">
|
||||
<span class="sort-label">
|
||||
氏名
|
||||
@if(request('sort') === 'user_name' && request('sort_type') === 'asc')
|
||||
↑<span class="text-muted">↓</span>
|
||||
@elseif(request('sort') === 'user_name' && request('sort_type') === 'desc')
|
||||
<span class="text-muted">↑</span>↓
|
||||
@else
|
||||
<span class="text-muted">↑↓</span>
|
||||
@endif
|
||||
</a>
|
||||
<span class="sort-arrows" aria-hidden="true">
|
||||
<span class="up">↑</span>
|
||||
<span class="down">↓</span>
|
||||
</span>
|
||||
</span>
|
||||
</th>
|
||||
|
||||
<th>フリガナ</th>
|
||||
<th>居住所:郵便番号</th>
|
||||
<th>居住所:都道府県</th>
|
||||
@ -172,55 +213,46 @@
|
||||
<th>学校</th>
|
||||
<th>卒業予定</th>
|
||||
<th>備考</th>
|
||||
<th>定期予約ID</th>
|
||||
<th
|
||||
class="sorting {{ $activeSort === 'rc.reserve_id' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
|
||||
sort="rc.reserve_id">
|
||||
<span class="sort-label">
|
||||
定期予約ID
|
||||
<span class="sort-arrows" aria-hidden="true">
|
||||
<span class="up">↑</span>
|
||||
<span class="down">↓</span>
|
||||
</span>
|
||||
</span>
|
||||
</th>
|
||||
<th>利用者分類1</th>
|
||||
<th>利用者分類2</th>
|
||||
<th>利用者分類3</th>
|
||||
<th>
|
||||
<a href="{{ request()->fullUrlWithQuery(['sort' => 'park_id', 'sort_type' => request('sort') === 'park_id' && request('sort_type') === 'desc' ? 'asc' : 'desc']) }}" class="text-dark text-decoration-none">
|
||||
駐輪場ID
|
||||
@if(request('sort') === 'park_id' && request('sort_type') === 'asc')
|
||||
↑<span class="text-muted">↓</span>
|
||||
@elseif(request('sort') === 'park_id' && request('sort_type') === 'desc')
|
||||
<span class="text-muted">↑</span>↓
|
||||
@else
|
||||
<span class="text-muted">↑↓</span>
|
||||
@endif
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a href="{{ request()->fullUrlWithQuery(['sort' => 'price_parkplaceid', 'sort_type' => request('sort') === 'price_parkplaceid' && request('sort_type') === 'desc' ? 'asc' : 'desc']) }}" class="text-dark text-decoration-none">
|
||||
駐輪場所ID
|
||||
@if(request('sort') === 'price_parkplaceid' && request('sort_type') === 'asc')
|
||||
↑<span class="text-muted">↓</span>
|
||||
@elseif(request('sort') === 'price_parkplaceid' && request('sort_type') === 'desc')
|
||||
<span class="text-muted">↑</span>↓
|
||||
@else
|
||||
<span class="text-muted">↑↓</span>
|
||||
@endif
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a href="{{ request()->fullUrlWithQuery(['sort' => 'psection_id', 'sort_type' => request('sort') === 'psection_id' && request('sort_type') === 'desc' ? 'asc' : 'desc']) }}" class="text-dark text-decoration-none">
|
||||
車種区分ID
|
||||
@if(request('sort') === 'psection_id' && request('sort_type') === 'asc')
|
||||
↑<span class="text-muted">↓</span>
|
||||
@elseif(request('sort') === 'psection_id' && request('sort_type') === 'desc')
|
||||
<span class="text-muted">↑</span>↓
|
||||
@else
|
||||
<span class="text-muted">↑↓</span>
|
||||
@endif
|
||||
</a>
|
||||
<th
|
||||
class="sorting {{ $activeSort === 'p.park_name' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
|
||||
sort="p.park_name">
|
||||
<span class="sort-label">
|
||||
駐輪場
|
||||
<span class="sort-arrows" aria-hidden="true">
|
||||
<span class="up">↑</span>
|
||||
<span class="down">↓</span>
|
||||
</span>
|
||||
</span>
|
||||
</th>
|
||||
<th>駐輪場所ID</th>
|
||||
<th>車種区分ID</th>
|
||||
<th>予約日時</th>
|
||||
<th>減免措置</th>
|
||||
<th>800M以内フラグ</th>
|
||||
<th>800M以内フラグ</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse ($rows as $row)
|
||||
<tr>
|
||||
<td>{{ $row->user_id }}</td>
|
||||
<td>
|
||||
<a href="{{ route('users_edit', ['seq' => $row->user_seq ?? $row->user_id]) }}">
|
||||
{{ $row->user_id }}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ $row->user_name }}</td>
|
||||
<td>{{ $row->user_phonetic }}</td>
|
||||
<td>{{ $row->user_regident_zip }}</td>
|
||||
@ -238,35 +270,217 @@
|
||||
<td>{{ $row->user_school }}</td>
|
||||
<td>{{ $row->user_graduate }}</td>
|
||||
<td>{{ $row->user_remarks }}</td>
|
||||
<td>{{ $row->reserve_id }}</td>
|
||||
<td>
|
||||
<a href="{{ route('regularcontracts_edit', ['contract_id' => $row->contract_id]) }}">
|
||||
{{ $row->contract_id }}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ $row->user_category1 ?? '' }}</td>
|
||||
<td>{{ $row->user_category2 ?? '' }}</td>
|
||||
<td>{{ $row->user_category3 ?? '' }}</td>
|
||||
<td>{{ $row->park_id }}</td>
|
||||
<td>{{ $row->price_parkplaceid }}</td>
|
||||
<td>{{ $row->psection_id }}</td>
|
||||
<td>{{ [1 => '自転車', 2 => '原付', 3 => 'その他'][$row->psection_id] ?? '' }}</td>
|
||||
<td>{{ $row->reserve_date }}</td>
|
||||
<td>{{ $row->reduction ?? '' }}</td>
|
||||
<td>
|
||||
@php $f800 = data_get($row,'within_800m_flag', data_get($row,'flag_800m', null)); @endphp
|
||||
{{ (string)$f800 === '1' ? '800M以内' : '-' }}
|
||||
</td>
|
||||
<td>{{ $row->user_reduction ?? '' }}</td>
|
||||
<td>{{ $row->{'800m_flag'} ?? '' }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="28" class="text-center">データがありません。</td></tr>
|
||||
<tr>
|
||||
<td colspan="25" class="text-center">データがありません。</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="mt-3">
|
||||
{{ $rows->appends(request()->except('page'))->links('pagination::bootstrap-4') }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
// ▼ ソート機能(日本語コメント)
|
||||
// th 要素をクリックすると sort と sort_type を設定してフォーム送信します
|
||||
document.querySelectorAll('th[sort]').forEach(th => {
|
||||
th.classList.add('sorting'); // ベースクラスを確実に付ける
|
||||
th.addEventListener('click', function () {
|
||||
const col = this.getAttribute('sort');
|
||||
if (!col) return;
|
||||
const form = document.getElementById('list-form');
|
||||
const sortInput = form.querySelector('input[name="sort"]');
|
||||
const typeInput = form.querySelector('input[name="sort_type"]');
|
||||
const current = sortInput.value;
|
||||
const currentType = typeInput.value;
|
||||
let nextType = 'asc';
|
||||
if (current === col) {
|
||||
nextType = (currentType === 'asc') ? 'desc' : 'asc';
|
||||
}
|
||||
if (sortInput) sortInput.value = col;
|
||||
if (typeInput) typeInput.value = nextType;
|
||||
|
||||
// 視覚的にソートクラスを切り替え(矢印の色に反映)
|
||||
document.querySelectorAll('th[sort]').forEach(h => {
|
||||
h.classList.remove('sorting_asc', 'sorting_desc');
|
||||
if (!h.classList.contains('sorting')) h.classList.add('sorting');
|
||||
});
|
||||
if (nextType === 'asc') {
|
||||
this.classList.add('sorting_asc');
|
||||
} else {
|
||||
this.classList.add('sorting_desc');
|
||||
}
|
||||
|
||||
form.submit();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.rv-table thead th{ background:#eeeeee; white-space:nowrap; vertical-align:middle; }
|
||||
.rv-table tbody tr{ background:#fff; }
|
||||
/* ----------------------------------------
|
||||
ページネーションの横幅を確保して右寄せ(スクリーンショットは右寄せ)
|
||||
---------------------------------------- */
|
||||
.pagination-area .pagination-wrapper {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
/* 右寄せ:必要なら center に変更 */
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
ソート矢印の色ルール(日本語コメント)
|
||||
状態:
|
||||
未ソート -> 左(上)灰色, 右(下)灰色
|
||||
昇順(asc) -> 左(上)黒色, 右(下)灰色
|
||||
降順(desc) -> 左(上)灰色, 右(下)黒色
|
||||
======================================== */
|
||||
|
||||
/* 既存の擬似要素で出る矢印を無効化(保険) */
|
||||
th.sorting::after,
|
||||
th.sorting_asc::after,
|
||||
th.sorting_desc::after {
|
||||
display: none !important;
|
||||
content: none !important;
|
||||
background-image: none !important;
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
/* 矢印を水平に並べる(左: up, 右: down) */
|
||||
th[sort] .sort-arrows,
|
||||
th.sorting .sort-arrows,
|
||||
th.sorting_asc .sort-arrows,
|
||||
th.sorting_desc .sort-arrows {
|
||||
display: inline-flex !important;
|
||||
flex-direction: row !important;
|
||||
gap: 6px !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
line-height: 1 !important;
|
||||
font-size: 12px !important;
|
||||
user-select: none !important;
|
||||
width: auto !important;
|
||||
box-sizing: border-box;
|
||||
vertical-align: middle;
|
||||
color: #bfc9d6 !important;
|
||||
/* デフォルト灰 */
|
||||
}
|
||||
|
||||
th[sort] .sort-arrows .up,
|
||||
th[sort] .sort-arrows .down,
|
||||
th.sorting .sort-arrows .up,
|
||||
th.sorting .sort-arrows .down {
|
||||
display: inline-block !important;
|
||||
min-width: 14px;
|
||||
text-align: center;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* 未ソート: 両方灰 */
|
||||
th.sorting:not(.sorting_asc):not(.sorting_desc) .sort-arrows .up,
|
||||
th.sorting:not(.sorting_asc):not(.sorting_desc) .sort-arrows .down {
|
||||
color: #bfc9d6 !important;
|
||||
}
|
||||
|
||||
/* 昇順: 左(上) 黒、右(下) 灰 */
|
||||
th.sorting_asc .sort-arrows .up,
|
||||
th[sort].sorting_asc .sort-arrows .up {
|
||||
color: #000000 !important;
|
||||
}
|
||||
|
||||
th.sorting_asc .sort-arrows .down,
|
||||
th[sort].sorting_asc .sort-arrows .down {
|
||||
color: #bfc9d6 !important;
|
||||
}
|
||||
|
||||
/* 降順: 左(上) 灰、右(下) 黒 */
|
||||
th.sorting_desc .sort-arrows .up,
|
||||
th[sort].sorting_desc .sort-arrows .up {
|
||||
color: #bfc9d6 !important;
|
||||
}
|
||||
|
||||
th.sorting_desc .sort-arrows .down,
|
||||
th[sort].sorting_desc .sort-arrows .down {
|
||||
color: #000000 !important;
|
||||
}
|
||||
|
||||
/* 矢印間を詰めたい場合(例: 見た目で letter-spacing 相当) */
|
||||
th[sort] .sort-arrows {
|
||||
gap: 0 !important;
|
||||
}
|
||||
|
||||
th[sort] .sort-arrows .down {
|
||||
margin-left: -5px !important;
|
||||
}
|
||||
|
||||
/* 矢印の下線を消す(リンク内にある場合の保険) */
|
||||
th .sort-arrows,
|
||||
th .sort-label,
|
||||
th[sort] .sort-arrows,
|
||||
th.sorting .sort-arrows {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
th .sort-label a,
|
||||
th .sort-arrows a {
|
||||
text-decoration: none !important;
|
||||
border-bottom: none !important;
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
/* SVG 対応(矢印が svg の場合) */
|
||||
th[sort] .sort-arrows svg path,
|
||||
th.sorting .sort-arrows svg path {
|
||||
fill: #bfc9d6 !important;
|
||||
}
|
||||
|
||||
th.sorting_asc .sort-arrows svg path,
|
||||
th[sort].sorting_asc .sort-arrows svg path {
|
||||
fill: #000 !important;
|
||||
}
|
||||
|
||||
th.sorting_desc .sort-arrows svg path,
|
||||
th[sort].sorting_desc .sort-arrows svg path {
|
||||
fill: #000 !important;
|
||||
}
|
||||
|
||||
/* レスポンシブ:小画面で間隔を狭める */
|
||||
@media (max-width: 575.98px) {
|
||||
th[sort] .sort-arrows {
|
||||
gap: 4px !important;
|
||||
font-size: 11px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* フォーカス時の視認性(アクセシビリティ) */
|
||||
th.sorting:focus,
|
||||
th.sorting_asc:focus,
|
||||
th.sorting_desc:focus {
|
||||
outline: 2px solid rgba(0, 0, 0, 0.08);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@endpush
|
||||
@endsection
|
||||
@ -23,10 +23,11 @@
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- 絞り込み -->
|
||||
<!-- 絞り込みヘッダー -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<!-- 1行目:印刷処理 -->
|
||||
<div class="card-body py-2">
|
||||
|
||||
{{-- 印刷処理 --}}
|
||||
<div class="row mb-2">
|
||||
<div class="col-md-12 font-weight-bold mb-1">印刷処理</div>
|
||||
<div class="col-md-12">
|
||||
@ -34,73 +35,45 @@
|
||||
<div class="col-md-2 mb-1">
|
||||
<button type="button" class="btn btn-outline-secondary btn-xs w-100 py-1 px-2"
|
||||
style="font-size:0.85rem;" onclick="handlePrintLabels()">タグ発送宛名印刷</button>
|
||||
<script>
|
||||
function handlePrintLabels() {
|
||||
var checkboxes = document.querySelectorAll('input[name="ids[]"]:checked');
|
||||
if (checkboxes.length === 0) {
|
||||
alert('1件以上選択してください。');
|
||||
return;
|
||||
}
|
||||
if (confirm('タグ発送用宛名を印刷してよろしいですか?')) {
|
||||
// 送信用form生成
|
||||
var form = document.createElement('form');
|
||||
form.method = 'POST';
|
||||
form.action = '/tagissue/print-unissued-labels';
|
||||
form.target = '_blank';
|
||||
// CSRF
|
||||
var csrf = document.createElement('input');
|
||||
csrf.type = 'hidden';
|
||||
csrf.name = '_token';
|
||||
csrf.value = '{{ csrf_token() }}';
|
||||
form.appendChild(csrf);
|
||||
// ids
|
||||
checkboxes.forEach(function(cb) {
|
||||
var input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = 'ids[]';
|
||||
input.value = cb.value;
|
||||
form.appendChild(input);
|
||||
});
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
document.body.removeChild(form);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
<div class="col-md-10 mb-1"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 2行目:絞り込みフィルター -->
|
||||
</div>
|
||||
|
||||
{{-- 種別フィルター --}}
|
||||
<div class="row mb-2">
|
||||
<div class="col-md-12 font-weight-bold mb-1">絞り込みフィルター</div>
|
||||
<div class="col-md-12 font-weight-bold mb-1">種別フィルター</div>
|
||||
<div class="col-md-12">
|
||||
<div class="row g-1">
|
||||
<div class="col-md-2 mb-1">
|
||||
<form method="GET" style="display:inline;">
|
||||
<form method="GET">
|
||||
@if(request('filter_type') == 'unissued_toggle')
|
||||
<button type="submit" class="btn btn-warning btn-xs w-100 py-1 px-2" style="font-size:0.85rem;">タグ未発送解除</button>
|
||||
<button type="submit" class="btn btn-warning btn-xs w-100 py-1 px-2"
|
||||
style="font-size:0.85rem;">タグ未発送解除</button>
|
||||
@else
|
||||
<input type="hidden" name="filter_type" value="unissued_toggle">
|
||||
<button type="submit" class="btn btn-outline-secondary btn-xs w-100 py-1 px-2" style="font-size:0.85rem;">タグ発送未発送</button>
|
||||
<button type="submit" class="btn btn-outline-secondary btn-xs w-100 py-1 px-2"
|
||||
style="font-size:0.85rem;">タグ発送未発送</button>
|
||||
@endif
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-2 mb-1">
|
||||
<form method="GET" style="display:inline;">
|
||||
<form method="GET">
|
||||
@if(request('filter_type') == 'issued_toggle')
|
||||
<button type="submit" class="btn btn-warning btn-xs w-100 py-1 px-2" style="font-size:0.85rem;">タグ発送済み解除</button>
|
||||
<button type="submit" class="btn btn-warning btn-xs w-100 py-1 px-2"
|
||||
style="font-size:0.85rem;">タグ発送済み解除</button>
|
||||
@else
|
||||
<input type="hidden" name="filter_type" value="issued_toggle">
|
||||
<button type="submit" class="btn btn-outline-secondary btn-xs w-100 py-1 px-2" style="font-size:0.85rem;">タグ発送済み</button>
|
||||
<button type="submit" class="btn btn-outline-secondary btn-xs w-100 py-1 px-2"
|
||||
style="font-size:0.85rem;">タグ発送済み</button>
|
||||
@endif
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 3行目:タグシリアルフィルター -->
|
||||
|
||||
{{-- タグシリアルフィルター --}}
|
||||
<div class="row mb-2">
|
||||
<div class="col-md-12 font-weight-bold mb-1">タグシリアルフィルター</div>
|
||||
<div class="col-md-12">
|
||||
@ -108,28 +81,39 @@
|
||||
<div class="row align-items-center g-1">
|
||||
<div class="col-auto font-weight-bold" style="padding-right:4px;">タグシリアル</div>
|
||||
<div class="col-auto" style="min-width:140px;">
|
||||
<input type="text" name="tag_serial" value="{{ request('tag_serial') }}" class="form-control form-control-sm" style="font-size:0.85rem; padding:2px 6px;" placeholder="キーワード…">
|
||||
<input type="text" name="tag_serial" value="{{ request('tag_serial') }}"
|
||||
class="form-control form-control-sm" style="font-size:0.85rem; padding:2px 6px;"
|
||||
placeholder="キーワード…">
|
||||
</div>
|
||||
<div class="col-auto font-weight-bold" style="padding-right:4px; padding-left:8px;">タグシリアル64進</div>
|
||||
<div class="col-auto font-weight-bold" style="padding-right:4px; padding-left:8px;">
|
||||
タグシリアル64進</div>
|
||||
<div class="col-auto" style="min-width:140px;">
|
||||
<input type="text" name="tag_serial_64" value="{{ request('tag_serial_64') }}" class="form-control form-control-sm" style="font-size:0.85rem; padding:2px 6px;" placeholder="キーワード…">
|
||||
<input type="text" name="tag_serial_64" value="{{ request('tag_serial_64') }}"
|
||||
class="form-control form-control-sm" style="font-size:0.85rem; padding:2px 6px;"
|
||||
placeholder="キーワード…">
|
||||
</div>
|
||||
<div class="col-auto text-end" style="padding-left:8px;">
|
||||
<button type="submit" class="btn btn-default btn-xs py-1 px-2 me-1" style="font-size:0.85rem; min-width:60px;">絞り込み</button>
|
||||
<a href="{{ route('tagissue') }}" class="btn btn-default btn-xs py-1 px-2" style="font-size:0.85rem; min-width:60px;">全表示</a>
|
||||
<button type="submit" class="btn btn-default btn-xs py-1 px-2 me-1"
|
||||
style="font-size:0.85rem; min-width:60px;">絞り込み</button>
|
||||
<a href="{{ route('tagissue') }}" class="btn btn-default btn-xs py-1 px-2"
|
||||
style="font-size:0.85rem; min-width:60px;">全表示</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 4行目:タグ発送ステータス変更 -->
|
||||
|
||||
{{-- タグ発送ステータス変更 --}}
|
||||
<div class="row mb-2">
|
||||
<div class="col-md-12 font-weight-bold mb-1">タグ発送ステータス変更</div>
|
||||
<div class="col-md-12">
|
||||
{{-- form 不包含表格 --}}
|
||||
<form id="statusForm" method="POST" action="{{ route('tagissue.status') }}">
|
||||
@csrf
|
||||
<input type="hidden" name="action" id="statusAction" value="">
|
||||
<div class="row g-1">
|
||||
</form>
|
||||
|
||||
<div class="row g-1 mb-2">
|
||||
<div class="col-md-2 mb-1">
|
||||
<button type="button" class="btn btn-outline-secondary btn-xs w-100 py-1 px-2"
|
||||
style="font-size:0.85rem;" onclick="handleStatusChange('to_issued')">タグ発送済み</button>
|
||||
@ -138,25 +122,44 @@
|
||||
<button type="button" class="btn btn-outline-secondary btn-xs w-100 py-1 px-2"
|
||||
style="font-size:0.85rem;" onclick="handleStatusChange('to_unissued')">タグ未発送</button>
|
||||
</div>
|
||||
<div class="col-md-8 mb-1"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 表格区域 --}}
|
||||
<div class="mt-2">
|
||||
<div class="d-flex justify-content-end mb-2">
|
||||
<div class="ms-auto">
|
||||
{{ $users->appends(request()->except('page'))->links('pagination') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="overflow-x: auto;">
|
||||
<table class="table table-bordered dataTable text-nowrap">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th style="width:40px;background-color:"><input type="checkbox" onclick="document.querySelectorAll('input[name=\'ids[]\']').forEach(cb => cb.checked = this.checked);"></th>
|
||||
<th class="sorting {{ ($sort=='que_id') ? ($sortType=='asc'?'sorting_asc':'sorting_desc') : '' }}" sort="que_id" style="color:#212529;cursor:pointer;"><span>キューID</span></th>
|
||||
<th class="sorting {{ ($sort=='user_tag_serial') ? ($sortType=='asc'?'sorting_asc':'sorting_desc') : '' }}" sort="user_tag_serial" style="color:#212529;cursor:pointer;"><span>タグシリアル</span></th>
|
||||
<th class="sorting {{ ($sort=='user_tag_serial_64') ? ($sortType=='asc'?'sorting_asc':'sorting_desc') : '' }}" sort="user_tag_serial_64" style="color:#212529;cursor:pointer;"><span>タグシリアル64進</span></th>
|
||||
<th class="sorting {{ ($sort=='user_tag_issue') ? ($sortType=='asc'?'sorting_asc':'sorting_desc') : '' }}" sort="user_tag_issue" style="color:#212529;cursor:pointer;"><span>タグ発送ステータス</span></th>
|
||||
<th class="sorting {{ ($sort=='user_name') ? ($sortType=='asc'?'sorting_asc':'sorting_desc') : '' }}" sort="user_name" style="color:#212529;cursor:pointer;"><span>利用者名</span></th>
|
||||
<th><span>携帯電話番号</span></th>
|
||||
<th><span>自宅電話番号</span></th>
|
||||
<th><span>居住所:郵便番号</span></th>
|
||||
<th><span>居住所:都道府県</span></th>
|
||||
<th><span>居住所:市区郡</span></th>
|
||||
<th><span>居住所:住所</span></th>
|
||||
<th style="width:40px;">
|
||||
<input type="checkbox"
|
||||
onclick="document.querySelectorAll('input[name=\'ids[]\']').forEach(cb => cb.checked = this.checked);">
|
||||
</th>
|
||||
<th sort="que_id">キューID</th>
|
||||
<th sort="user_tag_serial">タグシリアル</th>
|
||||
<th sort="user_tag_serial_64">タグシリアル64進</th>
|
||||
<th sort="user_tag_issue">タグ発送ステータス</th>
|
||||
<th sort="user_name">利用者名</th>
|
||||
<th>携帯電話番号</th>
|
||||
<th>自宅電話番号</th>
|
||||
<th>居住所:郵便番号</th>
|
||||
<th>居住所:都道府県</th>
|
||||
<th>居住所:市区群</th>
|
||||
<th>居住所:住所</th>
|
||||
<th>関連住所:郵便番号</th>
|
||||
<th>関連住所:都道府県</th>
|
||||
<th>関連住所:市区群</th>
|
||||
<th>関連住所:住所</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -175,10 +178,10 @@
|
||||
<td>
|
||||
@if(!empty($user->user_seq))
|
||||
<a href="{{ route('users_edit', ['seq' => $user->user_seq]) }}" target="_blank">
|
||||
{{ $user->user_name }}
|
||||
{{ $user->user_id }}: {{ $user->user_name }}
|
||||
</a>
|
||||
@else
|
||||
{{ $user->user_name }}
|
||||
{{ $user->user_id }}: {{ $user->user_name }}
|
||||
@endif
|
||||
</td>
|
||||
<td>{{ $user->user_mobile }}</td>
|
||||
@ -187,29 +190,77 @@
|
||||
<td>{{ $user->user_regident_pre }}</td>
|
||||
<td>{{ $user->user_regident_city }}</td>
|
||||
<td>{{ $user->user_regident_add }}</td>
|
||||
<td>{{ $user->user_relate_zip }}</td>
|
||||
<td>{{ $user->user_relate_pre }}</td>
|
||||
<td>{{ $user->user_relate_city }}</td>
|
||||
<td>{{ $user->user_relate_add }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<div class="ms-auto">
|
||||
{{ $users->appends(request()->except('page'))->links('pagination') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{{-- 印刷処理用 JS --}}
|
||||
<script>
|
||||
function handleStatusChange(actionType) {
|
||||
var form = document.getElementById('statusForm');
|
||||
document.getElementById('statusAction').value = actionType;
|
||||
// タグ発送宛名印刷
|
||||
function handlePrintLabels() {
|
||||
var checkboxes = document.querySelectorAll('input[name="ids[]"]:checked');
|
||||
if (checkboxes.length === 0) {
|
||||
alert('1件以上選択してください。');
|
||||
$.alert({
|
||||
title: '選択エラー',
|
||||
content: '1件以上選択してください。'
|
||||
});
|
||||
return;
|
||||
}
|
||||
$.confirm({
|
||||
title: '確認',
|
||||
content: 'タグ発送用宛名を印刷してよろしいですか?',
|
||||
buttons: {
|
||||
はい: {
|
||||
btnClass: 'btn-primary',
|
||||
action: function () {
|
||||
var form = document.createElement('form');
|
||||
form.method = 'POST';
|
||||
form.action = '/tagissue/print-unissued-labels';
|
||||
form.target = '_blank';
|
||||
var csrf = document.createElement('input');
|
||||
csrf.type = 'hidden';
|
||||
csrf.name = '_token';
|
||||
csrf.value = '{{ csrf_token() }}';
|
||||
form.appendChild(csrf);
|
||||
checkboxes.forEach(function (cb) {
|
||||
var input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = 'ids[]';
|
||||
input.value = cb.value;
|
||||
form.appendChild(input);
|
||||
});
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
document.body.removeChild(form);
|
||||
}
|
||||
},
|
||||
いいえ: function () {}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ステータス変更処理
|
||||
function handleStatusChange(actionType) {
|
||||
var checkboxes = document.querySelectorAll('input[name="ids[]"]:checked');
|
||||
if (checkboxes.length === 0) {
|
||||
$.alert({
|
||||
title: '選択エラー',
|
||||
content: '1件以上選択してください。'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var ids = Array.from(checkboxes).map(cb => cb.value);
|
||||
var ajaxType = (actionType === 'to_issued') ? 'issued' : 'unissued';
|
||||
|
||||
fetch('/tagissue/check-status', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@ -221,41 +272,69 @@
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (ajaxType === 'issued' && data.alreadyIssued.length > 0) {
|
||||
alert('以下のユーザーはすでにタグ発送済みです:\n' + data.alreadyIssued.join('\n'));
|
||||
$.alert({
|
||||
title: '確認エラー',
|
||||
content: '以下のユーザーはすでにタグ発送済みです:<br>' + data.alreadyIssued.join('<br>')
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (ajaxType === 'unissued' && data.alreadyUnissued && data.alreadyUnissued.length > 0) {
|
||||
alert('以下のユーザーはすでにタグ未発送です:\n' + data.alreadyUnissued.join('\n'));
|
||||
$.alert({
|
||||
title: '確認エラー',
|
||||
content: '以下のユーザーはすでにタグ未発送です:<br>' + data.alreadyUnissued.join('<br>')
|
||||
});
|
||||
return;
|
||||
}
|
||||
var confirmMsg = (ajaxType === 'issued') ? 'ステータスをタグ発送済に変更してよろしいですか?' : 'ステータスをタグ未発送に変更してよろしいですか?';
|
||||
if (confirm(confirmMsg)) {
|
||||
|
||||
var confirmMsg = (ajaxType === 'issued')
|
||||
? 'ステータスをタグ発送済に変更してよろしいですか?'
|
||||
: 'ステータスをタグ未発送に変更してよろしいですか?';
|
||||
|
||||
$.confirm({
|
||||
title: '確認ダイアログ',
|
||||
content: confirmMsg,
|
||||
buttons: {
|
||||
はい: {
|
||||
text: 'はい',
|
||||
btnClass: 'btn-primary',
|
||||
keys: ['enter'],
|
||||
action: function () {
|
||||
var form = document.getElementById('statusForm');
|
||||
// 古い input 清除
|
||||
form.querySelectorAll('input[name="ids[]"]').forEach(e => e.remove());
|
||||
// 动态附加选中 ID
|
||||
ids.forEach(id => {
|
||||
var input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = 'ids[]';
|
||||
input.value = id;
|
||||
form.appendChild(input);
|
||||
});
|
||||
document.getElementById('statusAction').value = actionType;
|
||||
form.submit();
|
||||
}
|
||||
},
|
||||
いいえ: function () { }
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
alert('ステータス確認に失敗しました。');
|
||||
$.alert({
|
||||
title: '通信エラー',
|
||||
content: 'ステータス確認に失敗しました。'
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
|
||||
// ソート挙動
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
document.querySelectorAll('th.sorting').forEach(function(th) {
|
||||
document.querySelectorAll('th[sort]').forEach(function (th) {
|
||||
th.addEventListener('click', function () {
|
||||
var sort = th.getAttribute('sort');
|
||||
var currentSort = new URLSearchParams(window.location.search).get('sort');
|
||||
var currentType = new URLSearchParams(window.location.search).get('sort_type');
|
||||
var nextType = 'asc';
|
||||
if (currentSort === sort) {
|
||||
nextType = (currentType === 'asc') ? 'desc' : 'asc';
|
||||
}
|
||||
var params = new URLSearchParams(window.location.search);
|
||||
var currentSort = params.get('sort');
|
||||
var currentType = params.get('sort_type');
|
||||
var nextType = (currentSort === sort && currentType === 'asc') ? 'desc' : 'asc';
|
||||
params.set('sort', sort);
|
||||
params.set('sort_type', nextType);
|
||||
window.location.search = params.toString();
|
||||
|
||||
103
resources/views/admin/tag_issue/pdf_labels.blade.php
Normal file
103
resources/views/admin/tag_issue/pdf_labels.blade.php
Normal file
@ -0,0 +1,103 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>タグ発送宛名PDF</title>
|
||||
<!-- Google Fonts: Noto Sans JP -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Noto Sans JP', 'Meiryo', 'MS PGothic', sans-serif;
|
||||
font-size: 15px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.label-block {
|
||||
width: 100%;
|
||||
max-width: 700px;
|
||||
margin: 0 auto;
|
||||
padding: 60px 60px 40px 60px;
|
||||
min-height: 900px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.header {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
.user-name {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
margin: 18px 0 0 0;
|
||||
}
|
||||
.address {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.content {
|
||||
margin-top: 32px;
|
||||
}
|
||||
.content p {
|
||||
margin-bottom: 18px;
|
||||
line-height: 2.1;
|
||||
}
|
||||
.content ol {
|
||||
margin-left: 0;
|
||||
padding-left: 1.2em;
|
||||
font-size: 15px;
|
||||
}
|
||||
.content li {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.note {
|
||||
margin-top: 40px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.footer {
|
||||
margin-top: 50px;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
.red {
|
||||
color: #d00;
|
||||
font-weight: bold;
|
||||
}
|
||||
ul {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 40px 30px 40px 30px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
</body>
|
||||
@foreach($users as $user)
|
||||
<div class="label-block" style="width:100%;max-width:800px;margin:0 auto;padding:60px 60px 40px 60px;">
|
||||
<div class="header">
|
||||
<div style="font-size:15px;letter-spacing:1px;">〒{{ $user->user_regident_zip }}</div>
|
||||
<div style="font-size:15px;letter-spacing:1px;">{{ $user->user_regident_pre }}</div>
|
||||
<div class="user-name">{{ $user->user_name }}様</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>この度は So-Manager(ソーマネージャー)のお申込み、ありがとうございます。<br>
|
||||
定期利用シール発行にご利用いただく「ICタグ」を同封いたしましたので、ご確認いただき、<br>
|
||||
以下のようにお手続きを進めてください。</p>
|
||||
<ol>
|
||||
<li><span class="red">9/27 日頃 SMBC ファイナンスサービス(株)より別便でお手元に届く「払込取扱票」を<BR>コンビニエンスストア等にお持ちになり、利用料金をお支払いください。</span></li>
|
||||
<li>お支払い完了後(3~5分程度)「ICタグ」が有効になりますので、<BR>駐輪場に設置している定期利用シール発行機で定期利用シールを発行してください。<BR>(シール発行機の読み取り機にICタグをかざすと、シールが発行されます。)</li>
|
||||
<li>印字された定期利用シールを自転車に貼ってご利用ください。<BR>(定期利用シールは泥除けなどの見やすい位置に貼ってください)</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div class="note">
|
||||
<div style="font-weight:bold;text-align:center;margin-bottom:10px;">【注意事項】</div>
|
||||
<ul>
|
||||
<li>※定期利用シールを貼っていないと、一時利用自転車と区別がつかず<BR>無断駐輪自転車と判断されますのでご注意ください。</li>
|
||||
<li>※その他ご不明な点等ございましたら「So-Managerサポートセンター」にお問合せください。</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer" style="margin-top:50px;font-size:14px;text-align:center;">
|
||||
So-manager サポートセンター:TEL:03-5856-4720<br>
|
||||
受付時間 毎日12:00~22:00
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</html>
|
||||
@ -705,7 +705,7 @@
|
||||
@stack('scripts')
|
||||
|
||||
<!-- Main Footer -->
|
||||
<footer class="main-footer">
|
||||
<footer class="main-footer" style="margin-left:270px;">
|
||||
<strong>Copyright © 2018 <a href="./index2.html">So-Manager for back office by so-rin
|
||||
Co.,Ltd.</a></strong> All rights reserved.
|
||||
</footer>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user