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

This commit is contained in:
kin.rinzen 2025-10-14 09:59:05 +09:00
commit 8b85451145
12 changed files with 2244 additions and 1110 deletions

16
.env
View File

@ -46,14 +46,16 @@ REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null REDIS_PASSWORD=null
REDIS_PORT=6379 REDIS_PORT=6379
MAIL_MAILER=log MAIL_MAILER=smtp
MAIL_SCHEME=null #MAIL_SCHEME=null
MAIL_HOST=127.0.0.1 MAIL_HOST=tomatofox9.sakura.ne.jp
MAIL_PORT=2525 MAIL_PORT=587
MAIL_USERNAME=null MAIL_USERNAME=demo@so-rin.jp
MAIL_PASSWORD=null MAIL_PASSWORD=rokuchou4665
MAIL_FROM_ADDRESS="hello@so-manager-dev.com" MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=demo@so-rin.jp
MAIL_FROM_NAME="${APP_NAME}" MAIL_FROM_NAME="${APP_NAME}"
MAIL_ADMIN=demo@so-rin.jp
AWS_ACCESS_KEY_ID= AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY= AWS_SECRET_ACCESS_KEY=

View File

@ -13,6 +13,7 @@ class ContractorController extends Controller
*/ */
public function list(Request $request) public function list(Request $request)
{ {
// ベースクエリを構築
$q = DB::table('regular_contract as rc') $q = DB::table('regular_contract as rc')
->leftJoin('user as u','rc.user_id','=','u.user_id') ->leftJoin('user as u','rc.user_id','=','u.user_id')
->select([ ->select([
@ -33,10 +34,12 @@ class ContractorController extends Controller
'rc.contract_permission', 'rc.contract_permission',
'rc.contract_manual', 'rc.contract_manual',
'rc.contract_notice', 'rc.contract_notice',
'rc.update_flag',
'p.park_name', 'p.park_name',
'u.user_name', 'u.user_name',
'u.user_phonetic', 'u.user_phonetic',
'u.user_mobile', 'u.user_mobile',
'u.user_seq',
'u.user_homephone', 'u.user_homephone',
'u.user_primemail', 'u.user_primemail',
'u.user_gender', 'u.user_gender',
@ -64,7 +67,7 @@ class ContractorController extends Controller
WHEN 12 THEN '年' WHEN 12 THEN '年'
ELSE CONCAT(rc.enable_months, 'ヶ月') END as ticket_type"), ELSE CONCAT(rc.enable_months, 'ヶ月') END as ticket_type"),
DB::raw('ps.psection_subject as vehicle_type'), DB::raw('ps.psection_subject as vehicle_type'),
// 利用者分類usertype テーブル // 利用者分類のラベルusertype テーブルの subject を取得
DB::raw('ut.usertype_subject1 as user_category1'), DB::raw('ut.usertype_subject1 as user_category1'),
DB::raw('ut.usertype_subject2 as user_category2'), DB::raw('ut.usertype_subject2 as user_category2'),
DB::raw('ut.usertype_subject3 as user_category3'), 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'); ->leftJoin('usertype as ut', 'u.user_categoryid', '=', 'ut.user_categoryid');
// ===== 絞り込み条件 ===== // ===== 絞り込み条件 =====
// 駐輪場
// 駐輪場で絞る(完全一致)
if ($request->filled('park_id')) { if ($request->filled('park_id')) {
$q->where('rc.park_id', $request->park_id); $q->where('rc.park_id', $request->park_id);
} }
// 利用者ID完全一致
// 利用者IDで絞る完全一致
if ($request->filled('user_id')) { if ($request->filled('user_id')) {
$q->where('rc.user_id', $request->user_id); $q->where('rc.user_id', $request->user_id);
} }
// 分類名1完全一致: ut.usertype_subject1 = 入力値 // 利用者分類で絞る(※ select の value を user_categoryid にしているため、user テーブルのカラムで比較)
if ($request->filled('user_category1')) { 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')) { if ($request->filled('user_tag_serial_64')) {
$val = $request->user_tag_serial_64; $val = $request->user_tag_serial_64;
$q->where('u.user_tag_serial_64','like','%'.$val.'%'); $q->where('u.user_tag_serial_64','like','%'.$val.'%');
} }
// 有効期限(契約有効期間 終了日:指定日以前を抽出)
// 有効期限で絞る(指定日以前を抽出する= <= を使用)
if ($request->filled('contract_periode')) { if ($request->filled('contract_periode')) {
$raw = trim($request->contract_periode); $raw = trim($request->contract_periode);
$norm = str_replace('/', '-', $raw); // スラッシュ入力許容 $norm = str_replace('/', '-', $raw); // スラッシュ入力許容
try { try {
$target = \Carbon\Carbon::parse($norm)->format('Y-m-d'); $target = \Carbon\Carbon::parse($norm)->format('Y-m-d');
// 指定日 “以前” を含む (以前のみなら '<' に変更) // 指定日「以前」を含める
$q->whereDate('rc.contract_periode', '>=', $target); $q->whereDate('rc.contract_periode', '<=', $target);
} catch (\Exception $e) { } catch (\Exception $e) {
// 不正日付は無視 // 無効な日付は無視する
} }
} }
// フリガナ(部分一致)
// フリガナで部分一致
if ($request->filled('user_phonetic')) { if ($request->filled('user_phonetic')) {
$q->where('u.user_phonetic', 'like', '%' . $request->user_phonetic . '%'); $q->where('u.user_phonetic', 'like', '%' . $request->user_phonetic . '%');
} }
// 携帯電話(部分一致)
// 携帯電話で部分一致
if ($request->filled('user_mobile')) { if ($request->filled('user_mobile')) {
$q->where('u.user_mobile', 'like', '%' . $request->user_mobile . '%'); $q->where('u.user_mobile', 'like', '%' . $request->user_mobile . '%');
} }
// メール(部分一致)
// メールアドレスで部分一致
if ($request->filled('user_primemail')) { if ($request->filled('user_primemail')) {
$q->where('u.user_primemail', 'like', '%' . $request->user_primemail . '%'); $q->where('u.user_primemail', 'like', '%' . $request->user_primemail . '%');
} }
// 勤務先(部分一致)
// 勤務先で部分一致
if ($request->filled('user_workplace')) { if ($request->filled('user_workplace')) {
$q->where('u.user_workplace', 'like', '%' . $request->user_workplace . '%'); $q->where('u.user_workplace', 'like', '%' . $request->user_workplace . '%');
} }
// 学校(部分一致)
// 学校で部分一致
if ($request->filled('user_school')) { if ($request->filled('user_school')) {
$q->where('u.user_school', 'like', '%' . $request->user_school . '%'); $q->where('u.user_school', 'like', '%' . $request->user_school . '%');
} }
// タグ・QR完全一致
// タグ・QR フラグで絞る(空文字は無視)
if ($request->filled('tag_qr_flag') && $request->tag_qr_flag !== '') { if ($request->filled('tag_qr_flag') && $request->tag_qr_flag !== '') {
$q->where('rc.tag_qr_flag', $request->tag_qr_flag); $q->where('rc.tag_qr_flag', $request->tag_qr_flag);
} }
// ソート処理 // ===== ソート処理 =====
// パラメータが来た時だけ適用。未指定なら主キー昇順 // 指定があればその列でソート、なければデフォルトで契約IDの昇順
$sort = $request->input('sort'); // null 許容 $sort = $request->input('sort'); // null 許容
$sortType = $request->input('sort_type','asc'); $sortType = $request->input('sort_type','asc');
@ -145,18 +158,23 @@ class ContractorController extends Controller
$sortType = $sortType === 'desc' ? 'desc' : 'asc'; $sortType = $sortType === 'desc' ? 'desc' : 'asc';
$q->orderBy($sort, $sortType); $q->orderBy($sort, $sortType);
} else { } else {
// 初期表示は契約ID昇順 (物理順に近い) // デフォルトソート
$sort = null; $sort = null;
$sortType = null; $sortType = null;
$q->orderBy('rc.contract_id','asc'); $q->orderBy('rc.contract_id','asc');
} }
// ページネーション(クエリ文字列を引き継ぐ)
$rows = $q->paginate(20)->appends($request->query()); $rows = $q->paginate(20)->appends($request->query());
// 駐輪場セレクト用 // 駐輪場セレクト用データ取得
$parks = DB::table('park')->select('park_id', 'park_name')->orderBy('park_name')->get(); $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) public function info($id)
{ {
// 指定契約IDの詳細を取得
$contract = DB::table('regular_contract as rc') $contract = DB::table('regular_contract as rc')
->select([ ->select([
'rc.*', 'rc.*',
@ -186,4 +205,55 @@ class ContractorController extends Controller
return view('admin.contractor.info', compact('contract')); 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();
}
} }

View File

@ -9,10 +9,11 @@ use Illuminate\Support\Facades\DB;
class ContractorListController extends Controller class ContractorListController extends Controller
{ {
/** /**
* 未更新者一覧 (contract_renewal IS NULL) * 一覧表示GET/POST
*/ */
public function list(Request $request) public function list(Request $request)
{ {
// ベースクエリを構築
$q = DB::table('regular_contract as rc') $q = DB::table('regular_contract as rc')
->leftJoin('user as u','rc.user_id','=','u.user_id') ->leftJoin('user as u','rc.user_id','=','u.user_id')
->select([ ->select([
@ -33,10 +34,14 @@ class ContractorListController extends Controller
'rc.contract_permission', 'rc.contract_permission',
'rc.contract_manual', 'rc.contract_manual',
'rc.contract_notice', 'rc.contract_notice',
'rc.update_flag',
'rc.user_securitynum',
'rc.contract_seal_issue',
'p.park_name', 'p.park_name',
'u.user_name', 'u.user_name',
'u.user_phonetic', 'u.user_phonetic',
'u.user_mobile', 'u.user_mobile',
'u.user_seq',
'u.user_homephone', 'u.user_homephone',
'u.user_primemail', 'u.user_primemail',
'u.user_gender', 'u.user_gender',
@ -64,7 +69,7 @@ class ContractorListController extends Controller
WHEN 12 THEN '年' WHEN 12 THEN '年'
ELSE CONCAT(rc.enable_months, 'ヶ月') END as ticket_type"), ELSE CONCAT(rc.enable_months, 'ヶ月') END as ticket_type"),
DB::raw('ps.psection_subject as vehicle_type'), DB::raw('ps.psection_subject as vehicle_type'),
// 利用者分類usertype テーブル // 利用者分類のラベルusertype テーブルの subject を取得
DB::raw('ut.usertype_subject1 as user_category1'), DB::raw('ut.usertype_subject1 as user_category1'),
DB::raw('ut.usertype_subject2 as user_category2'), DB::raw('ut.usertype_subject2 as user_category2'),
DB::raw('ut.usertype_subject3 as user_category3'), 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'); ->leftJoin('usertype as ut', 'u.user_categoryid', '=', 'ut.user_categoryid');
// ===== 絞り込み条件 ===== // ===== 絞り込み条件 =====
// 駐輪場
// 駐輪場で絞る(完全一致)
if ($request->filled('park_id')) { if ($request->filled('park_id')) {
$q->where('rc.park_id', $request->park_id); $q->where('rc.park_id', $request->park_id);
} }
// 利用者ID完全一致
// 利用者IDで絞る完全一致
if ($request->filled('user_id')) { if ($request->filled('user_id')) {
$q->where('rc.user_id', $request->user_id); $q->where('rc.user_id', $request->user_id);
} }
// 分類名1完全一致
// 利用者分類で絞る(※ select の value を user_categoryid にしているため、user テーブルのカラムで比較)
if ($request->filled('user_category1')) { 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')) { if ($request->filled('user_tag_serial_64')) {
$val = $request->user_tag_serial_64; $val = $request->user_tag_serial_64;
$q->where('u.user_tag_serial_64','like','%'.$val.'%'); $q->where('u.user_tag_serial_64','like','%'.$val.'%');
} }
// 有効期限(契約有効期間 終了日:指定日以前を抽出)
// 有効期限で絞る(指定日以前を抽出する= <= を使用)
if ($request->filled('contract_periode')) { if ($request->filled('contract_periode')) {
$raw = trim($request->contract_periode); $raw = trim($request->contract_periode);
$norm = str_replace('/', '-', $raw); // スラッシュ入力許容 $norm = str_replace('/', '-', $raw); // スラッシュ入力許容
try { try {
$target = \Carbon\Carbon::parse($norm)->format('Y-m-d'); $target = \Carbon\Carbon::parse($norm)->format('Y-m-d');
// 指定日 “以前” を含む (以前のみなら '<' に変更) // 指定日「以前」を含める
$q->whereDate('rc.contract_periode', '>=', $target); $q->whereDate('rc.contract_periode', '<=', $target);
} catch (\Exception $e) { } catch (\Exception $e) {
// 不正日付は無視 // 無効な日付は無視する
} }
} }
// フリガナ(部分一致)
// フリガナで部分一致
if ($request->filled('user_phonetic')) { if ($request->filled('user_phonetic')) {
$q->where('u.user_phonetic', 'like', '%' . $request->user_phonetic . '%'); $q->where('u.user_phonetic', 'like', '%' . $request->user_phonetic . '%');
} }
// 携帯電話(部分一致)
// 携帯電話で部分一致
if ($request->filled('user_mobile')) { if ($request->filled('user_mobile')) {
$q->where('u.user_mobile', 'like', '%' . $request->user_mobile . '%'); $q->where('u.user_mobile', 'like', '%' . $request->user_mobile . '%');
} }
// メール(部分一致)
// メールアドレスで部分一致
if ($request->filled('user_primemail')) { if ($request->filled('user_primemail')) {
$q->where('u.user_primemail', 'like', '%' . $request->user_primemail . '%'); $q->where('u.user_primemail', 'like', '%' . $request->user_primemail . '%');
} }
// 勤務先(部分一致)
// 勤務先で部分一致
if ($request->filled('user_workplace')) { if ($request->filled('user_workplace')) {
$q->where('u.user_workplace', 'like', '%' . $request->user_workplace . '%'); $q->where('u.user_workplace', 'like', '%' . $request->user_workplace . '%');
} }
// 学校(部分一致)
// 学校で部分一致
if ($request->filled('user_school')) { if ($request->filled('user_school')) {
$q->where('u.user_school', 'like', '%' . $request->user_school . '%'); $q->where('u.user_school', 'like', '%' . $request->user_school . '%');
} }
// タグ・QR完全一致
// タグ・QR フラグで絞る(空文字は無視)
if ($request->filled('tag_qr_flag') && $request->tag_qr_flag !== '') { if ($request->filled('tag_qr_flag') && $request->tag_qr_flag !== '') {
$q->where('rc.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'); $sortType = $request->input('sort_type','asc');
$allowSorts = [ $allowSorts = [
'user_id' => 'rc.user_id', 'rc.contract_id',
'user_name' => 'u.user_name', 'rc.user_id',
'contract_id' => 'rc.contract_id', 'u.user_name',
'tag_qr_flag' => 'rc.tag_qr_flag', 'rc.tag_qr_flag',
'park_name' => 'p.park_name', 'p.park_name',
]; ];
if($sort !== '' && isset($allowSorts[$sort])){ if ($sort && in_array($sort, $allowSorts)) {
$sortType = $sortType === 'desc' ? 'desc' : 'asc'; $sortType = $sortType === 'desc' ? 'desc' : 'asc';
$q->orderBy($allowSorts[$sort], $sortType) $q->orderBy($sort, $sortType);
->orderBy('rc.contract_id','asc'); // セカンダリ安定
} else { } else {
// 初期表示: DB登録順の近似PK昇順 // デフォルトソート
$sort = ''; // Blade 側で未ソート状態 $sort = null;
$sortType = null;
$q->orderBy('rc.contract_id','asc'); $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()); $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();
} }
} }

View File

@ -9,15 +9,45 @@ use Illuminate\Support\Facades\DB;
class ReservationController extends Controller class ReservationController extends Controller
{ {
/** /**
* 予約者一覧表示 * 一覧表示GET/POST
*/ */
public function list(Request $request) 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([ ->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_name',
'u.user_phonetic', '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_zip',
'u.user_regident_pre', 'u.user_regident_pre',
'u.user_regident_city', 'u.user_regident_city',
@ -26,96 +56,195 @@ class ReservationController extends Controller
'u.user_relate_pre', 'u.user_relate_pre',
'u.user_relate_city', 'u.user_relate_city',
'u.user_relate_add', 'u.user_relate_add',
'u.user_birthdate',
'u.user_gender',
'u.user_mobile',
'u.user_homephone',
'u.user_school',
'u.user_graduate', 'u.user_graduate',
'u.user_workplace',
'u.user_school',
'u.user_remarks', 'u.user_remarks',
'r.reserve_id', 'u.user_tag_serial_64',
'r.park_id', 'u.user_reduction',
'p.park_name', DB::raw('rc.user_securitynum as crime_prevention'),
'r.price_parkplaceid', DB::raw('rc.contract_seal_issue as seal_issue_count'),
'r.psection_id', 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'), DB::raw('ps.psection_subject as vehicle_type'),
// 利用者分類のラベルusertype テーブルの subject を取得)
DB::raw('ut.usertype_subject1 as user_category1'), DB::raw('ut.usertype_subject1 as user_category1'),
DB::raw('ut.usertype_subject2 as user_category2'), DB::raw('ut.usertype_subject2 as user_category2'),
DB::raw('ut.usertype_subject3 as user_category3'), 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', 'rc.park_id', '=', 'p.park_id')
->leftJoin('park as p', 'r.park_id', '=', 'p.park_id') ->leftJoin('psection as ps', 'rc.psection_id', '=', 'ps.psection_id')
->leftJoin('psection as ps','r.psection_id','=','ps.psection_id')
->leftJoin('usertype as ut', 'u.user_categoryid', '=', 'ut.user_categoryid'); ->leftJoin('usertype as ut', 'u.user_categoryid', '=', 'ut.user_categoryid');
// フィルター条件 // ===== 絞り込み条件 =====
// 駐輪場で絞る(完全一致)
if ($request->filled('park_id')) { 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')) { 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')) { if ($request->filled('user_category1')) {
$val = trim(mb_convert_kana($request->input('user_category1'), 'asKV')); $q->where('u.user_categoryid', $request->user_category1);
$q->where('ut.usertype_subject1', $val);
} elseif ($request->filled('user_categoryid')) {
// 既存互換ID指定も残す
$q->where('u.user_categoryid', $request->input('user_categoryid'));
} }
// タグシリアルで部分一致検索
if ($request->filled('user_tag_serial')) { if ($request->filled('user_tag_serial')) {
$q->where('u.user_tag_serial', 'like', '%' . $request->input('user_tag_serial') . '%'); $q->where('u.user_tag_serial', 'like', '%' . $request->input('user_tag_serial') . '%');
} }
// タグシリアル64進で部分一致検索
if ($request->filled('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')) { 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')) { if ($request->filled('user_mobile')) {
$q->where(function($sub) use ($request) { $q->where('u.user_mobile', 'like', '%' . $request->user_mobile . '%');
$sub->where('u.user_mobile', 'like', '%' . $request->input('user_mobile') . '%')
->orWhere('u.user_homephone', 'like', '%' . $request->input('user_mobile') . '%');
});
} }
// メールアドレスで部分一致
if ($request->filled('user_primemail')) { if ($request->filled('user_primemail')) {
$like = '%' . $request->input('user_primemail') . '%'; $q->where('u.user_primemail', 'like', '%' . $request->user_primemail . '%');
$q->where(function($w) use ($like){
$w->where('u.user_primemail','like',$like)
->orWhere('u.user_submail','like',$like);
});
} }
// 勤務先で部分一致
if ($request->filled('user_workplace')) { 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')) { 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'); $sortType = $request->input('sort_type','asc');
$allow = [
'reserve_id' => 'r.reserve_id', $allowSorts = [
'user_id' => 'r.user_id', 'rc.contract_id',
'user_name' => 'u.user_name', 'rc.user_id',
'park_id' => 'r.park_id', 'u.user_name',
'price_parkplaceid' => 'r.price_parkplaceid', 'rc.tag_qr_flag',
'psection_id' => 'r.psection_id', 'p.park_name',
'reserve_date' => 'r.reserve_date',
]; ];
if (!isset($allow[$sort])) $sort = 'reserve_id'; if ($sort && in_array($sort, $allowSorts)) {
$sortType = $sortType === 'desc' ? 'desc' : 'asc'; $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(); $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();
} }
} }

View File

@ -54,6 +54,10 @@ class TagissueController extends Controller
'user.user_regident_pre', 'user.user_regident_pre',
'user.user_regident_city', 'user.user_regident_city',
'user.user_regident_add', '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_id',
'operator_que.que_class', 'operator_que.que_class',
'operator_que.que_status' 'operator_que.que_status'
@ -125,9 +129,7 @@ class TagissueController extends Controller
{ {
$ids = $request->input('ids', []); // チェックされたuser_id配列 $ids = $request->input('ids', []); // チェックされたuser_id配列
$action = $request->input('action'); // 'to_issued' or 'to_unissued' $action = $request->input('action'); // 'to_issued' or 'to_unissued'
if (empty($ids) || !is_array($ids)) {
return back()->with('error', 'チェックボックスを選択してください。');
}
$operatorId = auth()->id(); $operatorId = auth()->id();
$now = now(); $now = now();
// 対象ユーザーのoperator_queを取得 // 対象ユーザーのoperator_queを取得

View File

@ -20,14 +20,21 @@
</div> </div>
<section class="content"> <section class="content">
<div class="container-fluid">
{{-- 絞り込みフィルター --}}
<form method="GET" action="{{ route('contractor') }}" class="mb-3" id="list-form"> <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" value="{{ $sort ?? request('sort') }}">
<input type="hidden" name="sort_type" value="{{ $sortType ?? request('sort_type') }}"> <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="row">
{{-- 左カラム --}} {{-- 左カラム --}}
<div class="col-md-6"> <div class="col-md-6">
@ -44,7 +51,8 @@
@endforeach @endforeach
</select> </select>
@else @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 @endisset
</div> </div>
</div> </div>
@ -52,14 +60,29 @@
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">利用者ID</label> <label class="col-sm-3 col-form-label">利用者ID</label>
<div class="col-sm-9"> <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> </div>
{{-- ===== 利用者分類input select に変更 ===== --}}
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">利用者分類</label> <label class="col-sm-3 col-form-label">利用者分類</label>
<div class="col-sm-9"> <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>
</div> </div>
@ -67,7 +90,7 @@
<label class="col-sm-3 col-form-label">タグシリアル64進</label> <label class="col-sm-3 col-form-label">タグシリアル64進</label>
<div class="col-sm-9"> <div class="col-sm-9">
<input type="text" name="user_tag_serial_64" value="{{ request('user_tag_serial_64') }}" <input type="text" name="user_tag_serial_64" value="{{ request('user_tag_serial_64') }}"
class="form-control"> class="form-control" placeholder="キーワード...">
</div> </div>
</div> </div>
@ -85,35 +108,40 @@
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">フリガナ</label> <label class="col-sm-3 col-form-label">フリガナ</label>
<div class="col-sm-9"> <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> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">電話番号</label> <label class="col-sm-3 col-form-label">電話番号</label>
<div class="col-sm-9"> <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> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">メールアドレス</label> <label class="col-sm-3 col-form-label">メールアドレス</label>
<div class="col-sm-9"> <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> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">勤務先</label> <label class="col-sm-3 col-form-label">勤務先</label>
<div class="col-sm-9"> <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> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">学校</label> <label class="col-sm-3 col-form-label">学校</label>
<div class="col-sm-9"> <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>
@ -130,55 +158,97 @@
</div> </div>
</div> </div>
<div class="mt-2"> {{-- 絞り込みボタン --}}
<button type="submit" class="btn btn-default">絞り込み</button> <div class="mt-2 mb-3 text-start">
<a href="{{ route('contractor') }}" class="btn btn-default">解除</a> <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>
</div> </div>
</form>
{{-- 一覧テーブル --}} {{-- 一覧テーブル部:スクロール対応 --}}
<div class="col-lg-12 row sample03-wrapper no_padding_right mb20">
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-bordered table-hover table-sm rv-table text-nowrap"> <table class="table table-bordered table-hover table-sm rv-table text-nowrap mb-0">
<thead> <thead class="thead-light">
<tr> <tr>
@php($activeSort = request('sort')) @php($activeSort = request('sort'))
@php($activeType = request('sort_type')) @php($activeType = request('sort_type'))
<th <th
class="sorting {{ $activeSort === 'rc.user_id' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}" class="sorting {{ $activeSort === 'rc.user_id' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
sort="rc.user_id" 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 <span class="sort-label">
class="up"></span><span class="down"></span></span></span> 利用者ID
<span class="sort-arrows" aria-hidden="true">
<span class="up"></span>
<span class="down"></span>
</span>
</span>
</th> </th>
<th <th
class="sorting {{ $activeSort === 'u.user_name' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}" class="sorting {{ $activeSort === 'u.user_name' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
sort="u.user_name" 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 <span class="sort-label">
class="up"></span><span class="down"></span></span></span> 氏名
<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
class="sorting {{ $activeSort === 'rc.contract_id' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}" class="sorting {{ $activeSort === 'rc.contract_id' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
sort="rc.contract_id" 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 <span class="sort-label">
class="up"></span><span class="down"></span></span></span> 定期契約ID
<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.tag_qr_flag' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}" class="sorting {{ $activeSort === 'rc.tag_qr_flag' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
sort="rc.tag_qr_flag" 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 <span class="sort-label">
class="up"></span><span class="down"></span></span></span> タグ・QR
<span class="sort-arrows" aria-hidden="true">
<span class="up"></span>
<span class="down"></span>
</span>
</span>
</th> </th>
<th <th
class="sorting {{ $activeSort === 'p.park_name' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}" class="sorting {{ $activeSort === 'p.park_name' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
sort="p.park_name" 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 <span class="sort-label">
class="up"></span><span class="down"></span></span></span> 駐輪場
<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> <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> <th>定期券区分</th>
<th>勤務先名</th> <th>勤務先名</th>
<th>学校</th> <th>学校</th>
<th>卒業予定</th>
<th>シール発行回数</th>
<th>防犯登録</th>
<th>備考</th> <th>備考</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@forelse ($rows as $row) @forelse ($rows as $row)
<tr> <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_name }}</td>
<td>{{ $row->user_phonetic }}</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->tag_qr_flag ? 'QR' : 'タグ' }}</td>
<td>{{ $row->park_name }}</td> <td>{{ $row->park_name }}</td>
<td>{{ $row->vehicle_type ?? '' }}</td> <td>{{ $row->vehicle_type ?? '' }}</td>
@ -231,83 +304,78 @@
<td>{{ $row->user_regident_pre }}</td> <td>{{ $row->user_regident_pre }}</td>
<td>{{ $row->user_regident_city }}</td> <td>{{ $row->user_regident_city }}</td>
<td>{{ $row->user_regident_add }}</td> <td>{{ $row->user_regident_add }}</td>
<td>{{ $row->user_relate_zip }}</td> <td>
<td>{{ $row->user_relate_pre }}</td> {{-- 契約日を「yyyymmdd」形式で表示する --}}
<td>{{ $row->user_relate_city }}</td> {{ $row->contract_created_at
<td>{{ $row->user_relate_add }}</td> ? \Illuminate\Support\Carbon::parse($row->contract_created_at)->format('Ymd')
<td>{{ $row->contract_created_at }}</td> : '' }}
<td>{{ $row->contract_periods }}</td> </td>
<td>{{ $row->contract_periode }}</td> <td>
<td>{{ $row->ticket_type ?? '' }}</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_workplace }}</td>
<td>{{ $row->user_school }}</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> <td>{{ $row->user_remarks }}</td>
</tr> </tr>
@empty @empty
<tr> <tr>
<td colspan="33" class="text-center">データがありません。</td> <td colspan="25" class="text-center">データがありません。</td>
</tr> </tr>
@endforelse @endforelse
</tbody> </tbody>
</table> </table>
</div> </div>
</div>
</div>
</form>
<div class="mt-3">
{{ $rows->appends(request()->except('page'))->links('pagination::bootstrap-4') }}
</div>
</div>
</section> </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') @push('scripts')
<script> <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 () { th.addEventListener('click', function () {
const col = this.getAttribute('sort'); const col = this.getAttribute('sort');
if (!col) return; if (!col) return;
@ -320,11 +388,155 @@
if (current === col) { if (current === col) {
nextType = (currentType === 'asc') ? 'desc' : 'asc'; nextType = (currentType === 'asc') ? 'desc' : 'asc';
} }
sortInput.value = col; if (sortInput) sortInput.value = col;
typeInput.value = nextType; 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(); form.submit();
}); });
}); });
</script> </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 @endpush
@endsection @endsection

View File

@ -20,14 +20,21 @@
</div> </div>
<section class="content"> <section class="content">
<div class="container-fluid">
{{-- 絞り込みフィルター --}}
<form method="GET" action="{{ route('contractor_List') }}" class="mb-3" id="list-form"> <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" value="{{ $sort ?? request('sort') }}">
<input type="hidden" name="sort_type" value="{{ $sortType ?? request('sort_type','desc') }}"> <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="row">
{{-- 左カラム --}} {{-- 左カラム --}}
<div class="col-md-6"> <div class="col-md-6">
@ -44,7 +51,8 @@
@endforeach @endforeach
</select> </select>
@else @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 @endisset
</div> </div>
</div> </div>
@ -52,16 +60,29 @@
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">利用者ID</label> <label class="col-sm-3 col-form-label">利用者ID</label>
<div class="col-sm-9"> <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> </div>
{{-- ===== 利用者分類input select に変更 ===== --}}
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">利用者分類</label> <label class="col-sm-3 col-form-label">利用者分類</label>
<div class="col-sm-9"> <div class="col-sm-9">
<input type="text" name="user_category1" @isset($categories)
value="{{ request('user_category1') }}" <select name="user_category1" class="form-control">
class="form-control" placeholder="分類名1"> <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> </div>
@ -69,7 +90,7 @@
<label class="col-sm-3 col-form-label">タグシリアル64進</label> <label class="col-sm-3 col-form-label">タグシリアル64進</label>
<div class="col-sm-9"> <div class="col-sm-9">
<input type="text" name="user_tag_serial_64" value="{{ request('user_tag_serial_64') }}" <input type="text" name="user_tag_serial_64" value="{{ request('user_tag_serial_64') }}"
class="form-control"> class="form-control" placeholder="キーワード...">
</div> </div>
</div> </div>
@ -87,35 +108,40 @@
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">フリガナ</label> <label class="col-sm-3 col-form-label">フリガナ</label>
<div class="col-sm-9"> <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> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">電話番号</label> <label class="col-sm-3 col-form-label">電話番号</label>
<div class="col-sm-9"> <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> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">メールアドレス</label> <label class="col-sm-3 col-form-label">メールアドレス</label>
<div class="col-sm-9"> <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> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">勤務先</label> <label class="col-sm-3 col-form-label">勤務先</label>
<div class="col-sm-9"> <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> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">学校</label> <label class="col-sm-3 col-form-label">学校</label>
<div class="col-sm-9"> <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>
@ -132,57 +158,100 @@
</div> </div>
</div> </div>
<div class="mt-2"> {{-- 絞り込みボタン --}}
<button type="submit" class="btn btn-default">絞り込み</button> <div class="mt-2 mb-3 text-start">
<a href="{{ route('contractor_List') }}" class="btn btn-default">解除</a> <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>
</div> </div>
</form>
{{-- 一覧テーブル --}} {{-- 一覧テーブル部:スクロール対応 --}}
<div class="col-lg-12 row sample03-wrapper no_padding_right mb20">
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-bordered table-hover table-sm rv-table text-nowrap"> <table class="table table-bordered table-hover table-sm rv-table text-nowrap mb-0">
<thead> <thead class="thead-light">
<tr> <tr>
@php($activeSort = request('sort')) @php($activeSort = request('sort'))
@php($activeType = request('sort_type')) @php($activeType = request('sort_type'))
<th <th
class="sorting {{ $activeSort === 'rc.user_id' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}" class="sorting {{ $activeSort === 'rc.user_id' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
sort="rc.user_id" 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 <span class="sort-label">
class="up"></span><span class="down"></span></span></span> 利用者ID
<span class="sort-arrows" aria-hidden="true">
<span class="up"></span>
<span class="down"></span>
</span>
</span>
</th> </th>
<th <th
class="sorting {{ $activeSort === 'u.user_name' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}" class="sorting {{ $activeSort === 'u.user_name' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
sort="u.user_name" 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 <span class="sort-label">
class="up"></span><span class="down"></span></span></span> 氏名
<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
class="sorting {{ $activeSort === 'rc.contract_id' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}" class="sorting {{ $activeSort === 'rc.contract_id' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
sort="rc.contract_id" 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 <span class="sort-label">
class="up"></span><span class="down"></span></span></span> 定期契約ID
<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.tag_qr_flag' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}" class="sorting {{ $activeSort === 'rc.tag_qr_flag' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
sort="rc.tag_qr_flag" 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 <span class="sort-label">
class="up"></span><span class="down"></span></span></span> タグ・QR
<span class="sort-arrows" aria-hidden="true">
<span class="up"></span>
<span class="down"></span>
</span>
</span>
</th> </th>
<th <th
class="sorting {{ $activeSort === 'p.park_name' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}" class="sorting {{ $activeSort === 'p.park_name' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
sort="p.park_name" 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 <span class="sort-label">
class="up"></span><span class="down"></span></span></span> 駐輪場
<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>
<th>減免措置</th> <th>減免措置</th>
<th>利用者分類1</th> <th>利用者分類1</th>
<th>利用者分類2</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>
<th>定期券区分</th> <th>定期券区分</th>
<th>勤務先名</th>
<th>学校</th> <th>学校</th>
<th>卒業予定</th> <th>卒業予定</th>
<th>シール発行回数</th> <th>シール発行回数</th>
@ -214,13 +281,28 @@
<tbody> <tbody>
@forelse ($rows as $row) @forelse ($rows as $row)
<tr> <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_name }}</td>
<td>{{ $row->user_phonetic }}</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->tag_qr_flag ? 'QR' : 'タグ' }}</td>
<td>{{ $row->park_name }}</td> <td>{{ $row->park_name }}</td>
<td>{{ $row->vehicle_type ?? '' }}</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_reduction ?? '' }}</td>
<td>{{ $row->user_category1 ?? '' }}</td> <td>{{ $row->user_category1 ?? '' }}</td>
<td>{{ $row->user_category2 ?? '' }}</td> <td>{{ $row->user_category2 ?? '' }}</td>
@ -237,79 +319,75 @@
<td>{{ $row->user_relate_pre }}</td> <td>{{ $row->user_relate_pre }}</td>
<td>{{ $row->user_relate_city }}</td> <td>{{ $row->user_relate_city }}</td>
<td>{{ $row->user_relate_add }}</td> <td>{{ $row->user_relate_add }}</td>
<td>{{ $row->contract_created_at }}</td> <td>
<td>{{ $row->contract_periods }}</td> {{-- 契約日を「yyyymmdd」形式で表示する --}}
<td>{{ $row->contract_periode }}</td> {{ $row->contract_created_at
<td>{{ $row->ticket_type ?? '' }}</td> ? \Illuminate\Support\Carbon::parse($row->contract_created_at)->format('Ymd')
<td>{{ $row->user_workplace }}</td> : '' }}
</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_school }}</td>
<td>{{ $row->user_graduate }}</td> <td>{{ $row->user_graduate }}</td>
<td>{{ $row->seal_issue_count ?? '' }}</td> <td>{{ $row->contract_seal_issue }}</td>
<td>{{ $row->crime_prevention ?? '' }}</td> <td>{{ $row->user_securitynum }}</td>
<td>{{ $row->user_remarks }}</td> <td>{{ $row->user_remarks }}</td>
</tr> </tr>
@empty @empty
<tr> <tr>
<td colspan="33" class="text-center">データがありません。</td> <td colspan="25" class="text-center">データがありません。</td>
</tr> </tr>
@endforelse @endforelse
</tbody> </tbody>
</table> </table>
</div> </div>
</div>
</div>
</form>
<div class="mt-3">
{{ $rows->appends(request()->except('page'))->links('pagination::bootstrap-4') }}
</div>
</div>
</section> </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') @push('scripts')
<script> <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 () { th.addEventListener('click', function () {
const col = this.getAttribute('sort'); const col = this.getAttribute('sort');
if (!col) return; if (!col) return;
@ -322,11 +400,155 @@
if (current === col) { if (current === col) {
nextType = (currentType === 'asc') ? 'desc' : 'asc'; nextType = (currentType === 'asc') ? 'desc' : 'asc';
} }
sortInput.value = col; if (sortInput) sortInput.value = col;
typeInput.value = nextType; 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(); form.submit();
}); });
}); });
</script> </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 @endpush
@endsection @endsection

View File

@ -379,9 +379,7 @@
@endphp @endphp
<thead> <thead>
<tr> <tr>
<th class="text-right {{ $sortClass('contract_id') }}" sort="contract_id" style="white-space:nowrap;"> <th style="width:50px; text-align:right;"></th>
契約ID {!! $renderSortIcon('contract_id') !!}
</th>
{{-- 操作列(チェック+編集。背景色を付与) --}} {{-- 操作列(チェック+編集。背景色を付与) --}}
<th style="width:120px"> <th style="width:120px">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
@ -389,7 +387,9 @@
<span></span> <span></span>
</div> </div>
</th> </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"> <th class="text-right {{ $sortClass('contract_qr_id') }}" sort="contract_qr_id">
定期契約ID {!! $renderSortIcon('contract_qr_id') !!} 定期契約ID {!! $renderSortIcon('contract_qr_id') !!}
@ -523,8 +523,8 @@
@endphp @endphp
@foreach($list as $item) @foreach($list as $item)
<tr> <tr>
{{-- 先頭の並び替え列(行の contract_id を表示) --}}
<td class="text-right">{{ $item->contract_id }}</td> <td class="text-right">{{ $loop->iteration }}</td>
{{-- 操作列:チェック + 編集(背景色つき) --}} {{-- 操作列:チェック + 編集(背景色つき) --}}
<td class="op-cell"> <td class="op-cell">
@ -535,6 +535,8 @@
</div> </div>
</td> </td>
{{-- 先頭の並び替え列(行の contract_id を表示) --}}
<td class="text-right">{{ $item->contract_id }}</td>
{{-- データ列 --}} {{-- データ列 --}}
<td class="text-right">{{ $item->contract_qr_id }}</td> <td class="text-right">{{ $item->contract_qr_id }}</td>
<td>{{ $item->old_contract_id ?? '' }}</td> <td>{{ $item->old_contract_id ?? '' }}</td>

View File

@ -6,10 +6,10 @@
<div class="content-header"> <div class="content-header">
<div class="container-fluid"> <div class="container-fluid">
<div class="row mb-2"> <div class="row mb-2">
<div class="col-sm-6"> <div class="col-lg-6">
<h1 class="m-0 text-dark">予約者一覧</h1> <h1 class="m-0 text-dark">予約者一覧</h1>
</div> </div>
<div class="col-sm-6"> <div class="col-lg-6">
<ol class="breadcrumb float-sm-right text-sm"> <ol class="breadcrumb float-sm-right text-sm">
<li class="breadcrumb-item"><a href="{{ url('/home') }}">ホーム</a></li> <li class="breadcrumb-item"><a href="{{ url('/home') }}">ホーム</a></li>
<li class="breadcrumb-item active">予約者一覧</li> <li class="breadcrumb-item active">予約者一覧</li>
@ -20,14 +20,23 @@
</div> </div>
<section class="content"> <section class="content">
<div class="container-fluid">
{{-- 絞り込みフィルター(順序を画像に完全一致) --}} <form method="GET" action="{{ route('reservation') }}" class="mb-3" id="list-form">
<form method="GET" action="{{ route('reservation') }}" class="mb-3" id="filter-form"> <input type="hidden" name="sort" value="{{ $sort ?? request('sort') }}">
<div class="card p-3"> <input type="hidden" name="sort_type" value="{{ $sortType ?? request('sort_type') }}">
<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="row">
{{-- 左カラム:駐輪場 利用者ID 利用者分類 タグシリアル タグシリアル64進 --}} {{-- 左カラム --}}
<div class="col-md-6"> <div class="col-md-6">
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">駐輪場</label> <label class="col-sm-3 col-form-label">駐輪場</label>
@ -42,7 +51,8 @@
@endforeach @endforeach
</select> </select>
@else @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 @endisset
</div> </div>
</div> </div>
@ -50,112 +60,143 @@
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">利用者ID</label> <label class="col-sm-3 col-form-label">利用者ID</label>
<div class="col-sm-9"> <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> </div>
{{-- 利用者分類 --}}
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">利用者分類</label> <label class="col-sm-3 col-form-label">利用者分類</label>
<div class="col-sm-9"> <div class="col-sm-9">
<input type="text" name="user_category1" value="{{ request('user_category1') }}" class="form-control" placeholder="分類名1"> @isset($categories)
{{-- 既存互換: IDパラメータが残る可能性があるため hidden で保持 --}} <select name="user_category1" class="form-control">
@if(request()->filled('user_categoryid')) <option value="">全て</option>
<input type="hidden" name="user_categoryid" value="{{ request('user_categoryid') }}"> @foreach($categories as $key => $label)
@endif <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> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">タグシリアル</label> <label class="col-sm-3 col-form-label">タグシリアル</label>
<div class="col-sm-9"> <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> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">タグシリアル64進</label> <label class="col-sm-3 col-form-label">タグシリアル64進</label>
<div class="col-sm-9"> <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>
</div> </div>
{{-- 右カラム:フリガナ 電話番号 メールアドレス 勤務先 学校 --}} {{-- 右カラム --}}
<div class="col-md-6"> <div class="col-md-6">
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">フリガナ</label> <label class="col-sm-3 col-form-label">フリガナ</label>
<div class="col-sm-9"> <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> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">電話番号</label> <label class="col-sm-3 col-form-label">電話番号</label>
<div class="col-sm-9"> <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> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">メールアドレス</label> <label class="col-sm-3 col-form-label">メールアドレス</label>
<div class="col-sm-9"> <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> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">勤務先</label> <label class="col-sm-3 col-form-label">勤務先</label>
<div class="col-sm-9"> <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> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">学校</label> <label class="col-sm-3 col-form-label">学校</label>
<div class="col-sm-9"> <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> </div>
</div> </div>
<div class="mt-2"> {{-- 絞り込みボタン --}}
<button type="submit" class="btn btn-default">絞り込み</button> <div class="card-body pt-2">
<a href="{{ route('reservation') }}" class="btn btn-default">解除</a> <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>
</div> </div>
</form>
{{-- 一覧テーブル(ソート機能付きヘッダー) --}} {{-- 一覧テーブル部:スクロール対応 --}}
<div class="col-lg-12 row sample03-wrapper no_padding_right mb20">
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-bordered table-hover table-sm rv-table text-nowrap"> <table class="table table-bordered table-hover table-sm rv-table text-nowrap mb-0">
<thead> <thead class="thead-light">
<tr> <tr>
{{-- ソート可能な項目にはリンクを追加 --}} @php($activeSort = request('sort'))
<th> @php($activeType = request('sort_type'))
<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"> <th
class="sorting {{ $activeSort === 'rc.user_id' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
sort="rc.user_id">
<span class="sort-label">
利用者ID 利用者ID
@if(request('sort') === 'user_id' && request('sort_type') === 'asc') <span class="sort-arrows" aria-hidden="true">
<span class="text-muted"></span> <span class="up"></span>
@elseif(request('sort') === 'user_id' && request('sort_type') === 'desc') <span class="down"></span>
<span class="text-muted"></span> </span>
@else </span>
<span class="text-muted">↑↓</span>
@endif
</a>
</th> </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="sort-arrows" aria-hidden="true">
<span class="text-muted"></span> <span class="up"></span>
@elseif(request('sort') === 'user_name' && request('sort_type') === 'desc') <span class="down"></span>
<span class="text-muted"></span> </span>
@else </span>
<span class="text-muted">↑↓</span>
@endif
</a>
</th> </th>
<th>フリガナ</th> <th>フリガナ</th>
<th>居住所:郵便番号</th> <th>居住所:郵便番号</th>
<th>居住所:都道府県</th> <th>居住所:都道府県</th>
@ -172,55 +213,46 @@
<th>学校</th> <th>学校</th>
<th>卒業予定</th> <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>利用者分類1</th>
<th>利用者分類2</th> <th>利用者分類2</th>
<th>利用者分類3</th> <th>利用者分類3</th>
<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"> class="sorting {{ $activeSort === 'p.park_name' ? ($activeType === 'asc' ? 'sorting_asc' : 'sorting_desc') : '' }}"
駐輪場ID sort="p.park_name">
@if(request('sort') === 'park_id' && request('sort_type') === 'asc') <span class="sort-label">
<span class="text-muted"></span> 駐輪場
@elseif(request('sort') === 'park_id' && request('sort_type') === 'desc') <span class="sort-arrows" aria-hidden="true">
<span class="text-muted"></span> <span class="up"></span>
@else <span class="down"></span>
<span class="text-muted">↑↓</span> </span>
@endif </span>
</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> </th>
<th>駐輪場所ID</th>
<th>車種区分ID</th>
<th>予約日時</th> <th>予約日時</th>
<th>減免措置</th> <th>減免措置</th>
<th>M以内フラグ</th> <th>800M以内フラグ</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@forelse ($rows as $row) @forelse ($rows as $row)
<tr> <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_name }}</td>
<td>{{ $row->user_phonetic }}</td> <td>{{ $row->user_phonetic }}</td>
<td>{{ $row->user_regident_zip }}</td> <td>{{ $row->user_regident_zip }}</td>
@ -238,35 +270,217 @@
<td>{{ $row->user_school }}</td> <td>{{ $row->user_school }}</td>
<td>{{ $row->user_graduate }}</td> <td>{{ $row->user_graduate }}</td>
<td>{{ $row->user_remarks }}</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_category1 ?? '' }}</td>
<td>{{ $row->user_category2 ?? '' }}</td> <td>{{ $row->user_category2 ?? '' }}</td>
<td>{{ $row->user_category3 ?? '' }}</td> <td>{{ $row->user_category3 ?? '' }}</td>
<td>{{ $row->park_id }}</td> <td>{{ $row->park_id }}</td>
<td>{{ $row->price_parkplaceid }}</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->reserve_date }}</td>
<td>{{ $row->reduction ?? '' }}</td> <td>{{ $row->user_reduction ?? '' }}</td>
<td> <td>{{ $row->{'800m_flag'} ?? '' }}</td>
@php $f800 = data_get($row,'within_800m_flag', data_get($row,'flag_800m', null)); @endphp
{{ (string)$f800 === '1' ? 'M以内' : '' }}
</td>
</tr> </tr>
@empty @empty
<tr><td colspan="28" class="text-center">データがありません。</td></tr> <tr>
<td colspan="25" class="text-center">データがありません。</td>
</tr>
@endforelse @endforelse
</tbody> </tbody>
</table> </table>
</div> </div>
</div>
</div>
</form>
<div class="mt-3">
{{ $rows->appends(request()->except('page'))->links('pagination::bootstrap-4') }}
</div>
</div>
</section> </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> <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> </style>
@endpush
@endsection @endsection

View File

@ -23,10 +23,11 @@
</nav> </nav>
</div> </div>
<!-- 絞り込み --> <!-- 絞り込みヘッダー -->
<div class="card mb-3"> <div class="card mb-3">
<div class="card-body"> <div class="card-body py-2">
<!-- 1行目:印刷処理 -->
{{-- 印刷処理 --}}
<div class="row mb-2"> <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="col-md-12">
@ -34,73 +35,45 @@
<div class="col-md-2 mb-1"> <div class="col-md-2 mb-1">
<button type="button" class="btn btn-outline-secondary btn-xs w-100 py-1 px-2" <button type="button" class="btn btn-outline-secondary btn-xs w-100 py-1 px-2"
style="font-size:0.85rem;" onclick="handlePrintLabels()">タグ発送宛名印刷</button> 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> </div>
</div> </div>
<!-- 2行目:絞り込みフィルター --> </div>
{{-- 種別フィルター --}}
<div class="row mb-2"> <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="col-md-12">
<div class="row g-1"> <div class="row g-1">
<div class="col-md-2 mb-1"> <div class="col-md-2 mb-1">
<form method="GET" style="display:inline;"> <form method="GET">
@if(request('filter_type') == 'unissued_toggle') @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 @else
<input type="hidden" name="filter_type" value="unissued_toggle"> <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 @endif
</form> </form>
</div> </div>
<div class="col-md-2 mb-1"> <div class="col-md-2 mb-1">
<form method="GET" style="display:inline;"> <form method="GET">
@if(request('filter_type') == 'issued_toggle') @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 @else
<input type="hidden" name="filter_type" value="issued_toggle"> <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 @endif
</form> </form>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<!-- 3行目:タグシリアルフィルター -->
{{-- タグシリアルフィルター --}}
<div class="row mb-2"> <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="col-md-12">
@ -108,28 +81,39 @@
<div class="row align-items-center g-1"> <div class="row align-items-center g-1">
<div class="col-auto font-weight-bold" style="padding-right:4px;">タグシリアル</div> <div class="col-auto font-weight-bold" style="padding-right:4px;">タグシリアル</div>
<div class="col-auto" style="min-width:140px;"> <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>
<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;"> <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>
<div class="col-auto text-end" style="padding-left:8px;"> <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> <button type="submit" class="btn btn-default btn-xs py-1 px-2 me-1"
<a href="{{ route('tagissue') }}" class="btn btn-default btn-xs py-1 px-2" style="font-size:0.85rem; min-width:60px;">全表示</a> 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>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
<!-- 4行目:タグ発送ステータス変更 -->
{{-- タグ発送ステータス変更 --}}
<div class="row mb-2"> <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="col-md-12">
{{-- form 不包含表格 --}}
<form id="statusForm" method="POST" action="{{ route('tagissue.status') }}"> <form id="statusForm" method="POST" action="{{ route('tagissue.status') }}">
@csrf @csrf
<input type="hidden" name="action" id="statusAction" value=""> <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"> <div class="col-md-2 mb-1">
<button type="button" class="btn btn-outline-secondary btn-xs w-100 py-1 px-2" <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> 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" <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> style="font-size:0.85rem;" onclick="handleStatusChange('to_unissued')">タグ未発送</button>
</div> </div>
<div class="col-md-8 mb-1"></div>
</div> </div>
</div>
</div>
</div>
</div>
{{-- 表格区域 --}}
<div class="mt-2"> <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;"> <div style="overflow-x: auto;">
<table class="table table-bordered dataTable text-nowrap"> <table class="table table-bordered dataTable text-nowrap">
<thead class="thead-light"> <thead class="thead-light">
<tr> <tr>
<th style="width:40px;background-color:"><input type="checkbox" onclick="document.querySelectorAll('input[name=\'ids[]\']').forEach(cb => cb.checked = this.checked);"></th> <th style="width:40px;">
<th class="sorting {{ ($sort=='que_id') ? ($sortType=='asc'?'sorting_asc':'sorting_desc') : '' }}" sort="que_id" style="color:#212529;cursor:pointer;"><span>キューID</span></th> <input type="checkbox"
<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> onclick="document.querySelectorAll('input[name=\'ids[]\']').forEach(cb => cb.checked = this.checked);">
<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>
<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 sort="que_id">キューID</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 sort="user_tag_serial">タグシリアル</th>
<th><span>携帯電話番号</span></th> <th sort="user_tag_serial_64">タグシリアル64進</th>
<th><span>自宅電話番号</span></th> <th sort="user_tag_issue">タグ発送ステータス</th>
<th><span>居住所:郵便番号</span></th> <th sort="user_name">利用者名</th>
<th><span>居住所:都道府県</span></th> <th>携帯電話番号</th>
<th><span>居住所:市区郡</span></th> <th>自宅電話番号</th>
<th><span>居住所:住所</span></th> <th>居住所:郵便番号</th>
<th>居住所:都道府県</th>
<th>居住所:市区群</th>
<th>居住所:住所</th>
<th>関連住所:郵便番号</th>
<th>関連住所:都道府県</th>
<th>関連住所:市区群</th>
<th>関連住所:住所</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -175,10 +178,10 @@
<td> <td>
@if(!empty($user->user_seq)) @if(!empty($user->user_seq))
<a href="{{ route('users_edit', ['seq' => $user->user_seq]) }}" target="_blank"> <a href="{{ route('users_edit', ['seq' => $user->user_seq]) }}" target="_blank">
{{ $user->user_name }} {{ $user->user_id }}: {{ $user->user_name }}
</a> </a>
@else @else
{{ $user->user_name }} {{ $user->user_id }}: {{ $user->user_name }}
@endif @endif
</td> </td>
<td>{{ $user->user_mobile }}</td> <td>{{ $user->user_mobile }}</td>
@ -187,29 +190,77 @@
<td>{{ $user->user_regident_pre }}</td> <td>{{ $user->user_regident_pre }}</td>
<td>{{ $user->user_regident_city }}</td> <td>{{ $user->user_regident_city }}</td>
<td>{{ $user->user_regident_add }}</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> </tr>
@endforeach @endforeach
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="d-flex align-items-center mb-2">
<div class="ms-auto">
{{ $users->appends(request()->except('page'))->links('pagination') }}
</div> </div>
</div> </div>
</div>
</form> {{-- 印刷処理用 JS --}}
<script> <script>
function handleStatusChange(actionType) { // タグ発送宛名印刷
var form = document.getElementById('statusForm'); function handlePrintLabels() {
document.getElementById('statusAction').value = actionType;
var checkboxes = document.querySelectorAll('input[name="ids[]"]:checked'); var checkboxes = document.querySelectorAll('input[name="ids[]"]:checked');
if (checkboxes.length === 0) { if (checkboxes.length === 0) {
alert('1件以上選択してください。'); $.alert({
title: '選択エラー',
content: '1件以上選択してください。'
});
return; 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 ids = Array.from(checkboxes).map(cb => cb.value);
var ajaxType = (actionType === 'to_issued') ? 'issued' : 'unissued'; var ajaxType = (actionType === 'to_issued') ? 'issued' : 'unissued';
fetch('/tagissue/check-status', { fetch('/tagissue/check-status', {
method: 'POST', method: 'POST',
headers: { headers: {
@ -221,41 +272,69 @@
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (ajaxType === 'issued' && data.alreadyIssued.length > 0) { if (ajaxType === 'issued' && data.alreadyIssued.length > 0) {
alert('以下のユーザーはすでにタグ発送済みです:\n' + data.alreadyIssued.join('\n')); $.alert({
title: '確認エラー',
content: '以下のユーザーはすでにタグ発送済みです:<br>' + data.alreadyIssued.join('<br>')
});
return; return;
} }
if (ajaxType === 'unissued' && data.alreadyUnissued && data.alreadyUnissued.length > 0) { if (ajaxType === 'unissued' && data.alreadyUnissued && data.alreadyUnissued.length > 0) {
alert('以下のユーザーはすでにタグ未発送です:\n' + data.alreadyUnissued.join('\n')); $.alert({
title: '確認エラー',
content: '以下のユーザーはすでにタグ未発送です:<br>' + data.alreadyUnissued.join('<br>')
});
return; 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(); form.submit();
} }
},
いいえ: function () { }
}
});
}) })
.catch(() => { .catch(() => {
alert('ステータス確認に失敗しました。'); $.alert({
title: '通信エラー',
content: 'ステータス確認に失敗しました。'
});
}); });
} }
</script>
</div>
</div>
</div>
</div>
</div>
<script>
// ソート挙動 // ソート挙動
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('th.sorting').forEach(function(th) { document.querySelectorAll('th[sort]').forEach(function (th) {
th.addEventListener('click', function () { th.addEventListener('click', function () {
var sort = th.getAttribute('sort'); 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 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', sort);
params.set('sort_type', nextType); params.set('sort_type', nextType);
window.location.search = params.toString(); window.location.search = params.toString();

View 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>お支払い完了後35分程度「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 サポートセンターTEL03-5856-4720<br>
受付時間 毎日12002200
</div>
</div>
@endforeach
</html>

View File

@ -705,7 +705,7 @@
@stack('scripts') @stack('scripts')
<!-- Main Footer --> <!-- Main Footer -->
<footer class="main-footer"> <footer class="main-footer" style="margin-left:270px;">
<strong>Copyright &copy; 2018 <a href="./index2.html">So-Manager for back office by so-rin <strong>Copyright &copy; 2018 <a href="./index2.html">So-Manager for back office by so-rin
Co.,Ltd.</a></strong> All rights reserved. Co.,Ltd.</a></strong> All rights reserved.
</footer> </footer>