diff --git a/app/Repositories/Admin/AdminUserRepository.php b/app/Repositories/Admin/AdminUserRepository.php index 6d69aa4..6ec8839 100644 --- a/app/Repositories/Admin/AdminUserRepository.php +++ b/app/Repositories/Admin/AdminUserRepository.php @@ -550,6 +550,7 @@ final class AdminUserRepository foreach ($rows as $r) { $id = (int)$r->id; $map[$id] = [ + 'id' => (int)$r->id, 'email' => (string)($r->email ?? ''), 'name' => (string)($r->name ?? ''), 'nick' => (string)($r->nickname ?? ''), diff --git a/app/Services/Admin/Member/AdminMemberService.php b/app/Services/Admin/Member/AdminMemberService.php index 45df31d..0293980 100644 --- a/app/Services/Admin/Member/AdminMemberService.php +++ b/app/Services/Admin/Member/AdminMemberService.php @@ -78,18 +78,29 @@ final class AdminMemberService $plainPhone = $this->plainPhone((string)($member->cell_phone ?? '')); $phoneDisplay = $this->formatPhone($plainPhone); - $adminMemo = $this->decodeJsonArray($member->admin_memo ?? null); - $modifyLog = $this->decodeJsonArray($member->modify_log ?? null); - $modifyLog = array_reverse($modifyLog); + // ✅ 레거시 JSON 파싱 (admin_memo / modify_log) + $adminMemoList = $this->legacyAdminMemoList($member->admin_memo ?? null); // old -> new normalize + $stateLogList = $this->legacyStateLogList($member->modify_log ?? null); // old -> new normalize - $actorIds = []; + // 화면은 최신이 위로 오게 + $adminMemo = array_reverse($adminMemoList); + $modifyLog = array_reverse($stateLogList); + + // ✅ adminMap 대상 admin_num 수집 + $adminSet = []; + + foreach ($adminMemo as $it) { + $aid = (int)($it['admin_num'] ?? 0); + if ($aid > 0) $adminSet[$aid] = true; + } foreach ($modifyLog as $it) { - $aid = (int)($it['actor_admin_id'] ?? 0); - if ($aid > 0) $actorIds[] = $aid; + $aid = (int)($it['admin_num'] ?? 0); + if ($aid > 0) $adminSet[$aid] = true; } + $adminIds = array_keys($adminSet); - // 인증/주소/로그 + // 인증/주소/로그 (기존 그대로) $authRows = array_values(array_filter( $this->repo->getAuthRowsForMember($memNo), fn($r)=> in_array((string)($r['auth_type'] ?? ''), self::AUTH_TYPES_SHOW, true) @@ -102,7 +113,7 @@ final class AdminMemberService // 계좌 표시(수정 불가 / 표시만) $bank = $this->buildBankDisplay($member, $authInfo); - $adminMap = $this->adminRepo->getMetaMapByIds($actorIds); + $adminMap = $this->adminRepo->getMetaMapByIds($adminIds); return [ 'member' => $member, @@ -110,9 +121,12 @@ final class AdminMemberService 'plainPhone' => $plainPhone, 'phoneDisplay' => $phoneDisplay, + // ✅ 레거시 기반 결과 'adminMemo' => $adminMemo, 'modifyLog' => $modifyLog, + 'adminMap' => $adminMap, + // 기존 그대로 'authRows' => $authRows, 'authInfo' => $authInfo, 'authLogs' => $authLogs, @@ -122,30 +136,29 @@ final class AdminMemberService 'genderMap' => $this->genderMap(), 'nativeMap' => $this->nativeMap(), 'corpMap' => $this->corpMap(), - 'bank' => $bank, - - 'modifyLog' => $modifyLog, - 'adminMap' => $adminMap, ]; } + /** - * ✅ 업데이트 허용: stat_3(1~3만), cell_corp, cell_phone - * ❌ 금지: 이름/이메일/수신동의/계좌/기타 + * 업데이트 허용: stat_3(1~3만), cell_corp, cell_phone + * 금지: 이름/이메일/수신동의/계좌/기타 */ public function updateMember(int $memNo, array $input, int $actorAdminId, string $ip = '', string $ua = ''): array { try { - return DB::transaction(function () use ($memNo, $input, $actorAdminId, $ip, $ua) { + return DB::transaction(function () use ($memNo, $input, $actorAdminId) { + $before = $this->repo->lockMemberForUpdate($memNo); if (!$before) return $this->fail('회원을 찾을 수 없습니다.'); - $beforeArr = (array)$before; $data = []; - $changes = []; - // ✅ stat_3: 1~3만 변경 허용, 4~6은 시스템 상태로 변경 금지 + // ✅ 기존 modify_log에서 레거시 state_log[] 뽑기 + $stateLog = $this->legacyStateLogList($before->modify_log ?? null); + + // ✅ stat_3 변경 (1~3만 변경 허용, 4~6 금지 정책 유지) if (array_key_exists('stat_3', $input)) { $s3 = (string)($input['stat_3'] ?? ''); if (!in_array($s3, ['1','2','3','4','5','6'], true)) { @@ -155,34 +168,68 @@ final class AdminMemberService return $this->fail('4~6 상태는 시스템 상태로 관리자 변경이 불가합니다.'); } - if ($s3 !== (string)($before->stat_3 ?? '')) { + $beforeS3 = (string)($before->stat_3 ?? ''); + if ($s3 !== $beforeS3) { $data['stat_3'] = $s3; $data['dt_stat_3'] = now()->format('Y-m-d H:i:s'); + + // ✅ 레거시 형식 로그 + $stateLog[] = [ + 'when' => now()->format('y-m-d H:i:s'), + 'after' => $s3, + 'title' => '회원상태 접근권한 변경', + 'before' => $beforeS3, + 'admin_num' => (string)$actorAdminId, + ]; } } - // ✅ 통신사 + // ✅ 통신사 변경 if (array_key_exists('cell_corp', $input)) { $corp = (string)($input['cell_corp'] ?? 'n'); $allowed = ['n','01','02','03','04','05','06']; if (!in_array($corp, $allowed, true)) return $this->fail('통신사 코드가 올바르지 않습니다.'); - if ($corp !== (string)($before->cell_corp ?? 'n')) $data['cell_corp'] = $corp; + + $beforeCorp = (string)($before->cell_corp ?? 'n'); + if ($corp !== $beforeCorp) { + $data['cell_corp'] = $corp; + + $stateLog[] = [ + 'when' => now()->format('y-m-d H:i:s'), + 'after' => $corp, + 'title' => '통신사 변경', + 'before' => $beforeCorp, + 'admin_num' => (string)$actorAdminId, + ]; + } } - // ✅ 휴대폰(암호화 저장) + // ✅ 휴대폰 변경(암호화 저장) if (array_key_exists('cell_phone', $input)) { $raw = trim((string)($input['cell_phone'] ?? '')); - if ($raw === '') { - // 전화번호 비우는 것 자체는 허용하되 운영 정책에 따라 막고 싶으면 여기서 fail 처리 - $enc = ''; - } else { + + $enc = ''; + $afterPlain = ''; + if ($raw !== '') { $phone = $this->normalizeKrPhone($raw); if ($phone === '') return $this->fail('휴대폰 번호 형식이 올바르지 않습니다.'); $enc = $this->encryptPhone($phone); + $afterPlain = $phone; } - if ((string)($before->cell_phone ?? '') !== $enc) { + $beforeEnc = (string)($before->cell_phone ?? ''); + if ($beforeEnc !== $enc) { $data['cell_phone'] = $enc; + + // 로그는 마스킹(평문 full 저장 원하면 여기만 변경) + $beforePlain = $this->plainPhone($beforeEnc); + $stateLog[] = [ + 'when' => now()->format('y-m-d H:i:s'), + 'after' => $this->maskPhoneForLog($afterPlain), + 'title' => '휴대폰번호 변경', + 'before' => $this->maskPhoneForLog($beforePlain), + 'admin_num' => (string)$actorAdminId, + ]; } } @@ -190,25 +237,12 @@ final class AdminMemberService return $this->ok('변경사항이 없습니다.'); } - foreach ($data as $k => $v) { - $beforeVal = $beforeArr[$k] ?? null; - if ((string)$beforeVal !== (string)$v) { - $changes[$k] = ['before' => $beforeVal, 'after' => $v]; - } - } + // ✅ 레거시 modify_log 저장: 반드시 {"state_log":[...]} + $stateLog = $this->trimLegacyList($stateLog, 300); + $data['modify_log'] = $this->encodeJsonObjectOrNull(['state_log' => $stateLog]); - // modify_log append - $modify = $this->decodeJsonArray($before->modify_log ?? null); - $modify = $this->appendJson($modify, [ - 'ts' => now()->format('Y-m-d H:i:s'), - 'actor_admin_id' => $actorAdminId, - 'action' => 'member_update', - 'ip' => $ip, - 'ua' => $ua, - 'changes' => $changes, - ], 300); - - $data['modify_log'] = $this->encodeJsonOrNull($modify); + // 최근정보변경일시 + $data['dt_mod'] = now()->format('Y-m-d H:i:s'); $ok = $this->repo->updateMember($memNo, $data); if (!$ok) return $this->fail('저장에 실패했습니다.'); @@ -220,6 +254,7 @@ final class AdminMemberService } } + // ------------------------- // Maps // ------------------------- @@ -345,31 +380,6 @@ final class AdminMemberService return str_repeat('*', max(0, mb_strlen($d)-4)).mb_substr($d, -4); } - // ------------------------- - // JSON helpers - // ------------------------- - private function decodeJsonArray($jsonOrNull): array - { - if ($jsonOrNull === null) return []; - $s = trim((string)$jsonOrNull); - if ($s === '') return []; - $arr = json_decode($s, true); - return is_array($arr) ? $arr : []; - } - - private function encodeJsonOrNull(array $arr): ?string - { - if (empty($arr)) return null; - return json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); - } - - private function appendJson(array $list, array $entry, int $max = 200): array - { - array_unshift($list, $entry); - if (count($list) > $max) $list = array_slice($list, 0, $max); - return $list; - } - public function addMemo(int $memNo, string $memo, int $actorAdminId, string $ip = '', string $ua = ''): array { $memo = trim($memo); @@ -377,39 +387,28 @@ final class AdminMemberService if (mb_strlen($memo) > 1000) return $this->fail('메모는 1000자 이내로 입력해 주세요.'); try { - return DB::transaction(function () use ($memNo, $memo, $actorAdminId, $ip, $ua) { + return DB::transaction(function () use ($memNo, $memo, $actorAdminId) { - // row lock $before = $this->repo->lockMemberForUpdate($memNo); if (!$before) return $this->fail('회원을 찾을 수 없습니다.'); - // 기존 memo json - $adminMemo = $this->decodeJsonArray($before->admin_memo ?? null); + // ✅ 기존 admin_memo에서 레거시 memo[] 뽑기 + $list = $this->legacyAdminMemoList($before->admin_memo ?? null); - // append - $adminMemo = $this->appendJson($adminMemo, [ - 'ts' => now()->format('Y-m-d H:i:s'), - 'actor_admin_id' => $actorAdminId, - 'ip' => $ip, - 'ua' => $ua, - 'memo' => $memo, - ], 300); + // ✅ append (레거시 키 유지) + $list[] = [ + 'memo' => $memo, + 'when' => now()->format('y-m-d H:i:s'), // 레거시(2자리년도) + 'admin_num' => (string)$actorAdminId, + ]; + $list = $this->trimLegacyList($list, 300); - // (선택) modify_log에도 남기기 - $modify = $this->decodeJsonArray($before->modify_log ?? null); - $modify = $this->appendJson($modify, [ - 'ts' => now()->format('Y-m-d H:i:s'), - 'actor_admin_id' => $actorAdminId, - 'action' => 'admin_memo_add', - 'ip' => $ip, - 'ua' => $ua, - 'changes' => ['admin_memo' => ['before' => null, 'after' => 'added']], - ], 300); + // ✅ admin_memo는 반드시 {"memo":[...]} 형태 + $obj = ['memo' => $list]; $data = [ - 'admin_memo' => $this->encodeJsonOrNull($adminMemo), - 'modify_log' => $this->encodeJsonOrNull($modify), - 'dt_mod' => now()->format('Y-m-d H:i:s'), // 최근정보변경일시 반영 + 'admin_memo' => $this->encodeJsonObjectOrNull($obj), + 'dt_mod' => now()->format('Y-m-d H:i:s'), ]; $ok = $this->repo->updateMember($memNo, $data); @@ -423,6 +422,114 @@ final class AdminMemberService } + private function ok(string $msg): array { return ['ok'=>true,'message'=>$msg]; } private function fail(string $msg): array { return ['ok'=>false,'message'=>$msg]; } + + private function decodeJsonObject($jsonOrNull): array + { + if ($jsonOrNull === null) return []; + $s = trim((string)$jsonOrNull); + if ($s === '') return []; + $arr = json_decode($s, true); + return is_array($arr) ? $arr : []; + } + + private function encodeJsonObjectOrNull(array $obj): ?string + { + if (empty($obj)) return null; + return json_encode($obj, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + } + +// ✅ admin_memo: {"memo":[{memo,when,admin_num}, ...]} + private function legacyAdminMemoList($adminMemoJson): array + { + $obj = $this->decodeJsonObject($adminMemoJson); + + if (isset($obj['memo']) && is_array($obj['memo'])) { + return $obj['memo']; + } + + // 혹시 예전에 잘못 저장된 list 형태도 최대한 복구해서 보여주기 + if (is_array($obj) && array_is_list($obj)) { + $out = []; + foreach ($obj as $it) { + if (!is_array($it)) continue; + $memo = (string)($it['memo'] ?? ''); + if ($memo === '') continue; + + $out[] = [ + 'memo' => $memo, + 'when' => $this->legacyWhen((string)($it['when'] ?? $it['ts'] ?? '')), + 'admin_num' => (string)($it['admin_num'] ?? $it['actor_admin_id'] ?? ''), + ]; + } + return $out; + } + + return []; + } + +// ✅ modify_log: {"state_log":[{when,after,title,before,admin_num}, ...]} + private function legacyStateLogList($modifyLogJson): array + { + $obj = $this->decodeJsonObject($modifyLogJson); + + if (isset($obj['state_log']) && is_array($obj['state_log'])) { + return $obj['state_log']; + } + + // 혹시 예전에 잘못 저장된 list 형태도 복구 시도 + if (is_array($obj) && array_is_list($obj)) { + $out = []; + foreach ($obj as $it) { + if (!is_array($it)) continue; + + $out[] = [ + 'when' => $this->legacyWhen((string)($it['when'] ?? $it['ts'] ?? '')), + 'after' => (string)($it['after'] ?? ''), + 'title' => (string)($it['title'] ?? $it['action'] ?? '변경'), + 'before' => (string)($it['before'] ?? ''), + 'admin_num' => (string)($it['admin_num'] ?? $it['actor_admin_id'] ?? ''), + ]; + } + return $out; + } + + return []; + } + + private function legacyWhen(string $any): string + { + $any = trim($any); + if ($any === '') return now()->format('y-m-d H:i:s'); + + if (preg_match('/^\d{2}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}$/', $any)) return $any; + + if (preg_match('/^\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}$/', $any)) { + try { return \Carbon\Carbon::parse($any)->format('y-m-d H:i:s'); } + catch (\Throwable $e) { return now()->format('y-m-d H:i:s'); } + } + + try { return \Carbon\Carbon::parse($any)->format('y-m-d H:i:s'); } + catch (\Throwable $e) { return now()->format('y-m-d H:i:s'); } + } + + private function trimLegacyList(array $list, int $max = 300): array + { + $n = count($list); + if ($n <= $max) return $list; + return array_slice($list, $n - $max); + } + + private function maskPhoneForLog(string $digits): string + { + $d = preg_replace('/\D+/', '', $digits) ?? ''; + if ($d === '') return ''; + if (strlen($d) <= 4) return str_repeat('*', strlen($d)); + return substr($d, 0, 3) . str_repeat('*', max(0, strlen($d) - 7)) . substr($d, -4); + } + + + } diff --git a/app/Services/Admin/Qna/AdminQnaService.php b/app/Services/Admin/Qna/AdminQnaService.php index 389d156..68b304a 100644 --- a/app/Services/Admin/Qna/AdminQnaService.php +++ b/app/Services/Admin/Qna/AdminQnaService.php @@ -17,11 +17,6 @@ final class AdminQnaService private readonly AdminQnaRepository $repo, ) {} - private function t(array &$timers, string $key, float $start): void - { - $timers[$key] = round((microtime(true) - $start) * 1000, 1); // ms - } - public function stateLabels(): array { return [ diff --git a/resources/views/admin/members/index.blade.php b/resources/views/admin/members/index.blade.php index 89142af..7830070 100644 --- a/resources/views/admin/members/index.blade.php +++ b/resources/views/admin/members/index.blade.php @@ -24,6 +24,8 @@ .lbtn:hover{background:rgba(255,255,255,.10);text-decoration:none;} .lbtn--ghost{background:transparent;} .lbtn--sm{padding:7px 10px;font-size:12px;border-radius:11px;} + .lbtn--primary{background:rgba(59,130,246,.88);border-color:rgba(59,130,246,.95);color:#fff;} + .lbtn--primary:hover{background:rgba(59,130,246,.98);} .pill{display:inline-flex;align-items:center;gap:6px;padding:6px 10px;border-radius:999px;font-size:12px; border:1px solid rgba(255,255,255,.10);background:rgba(255,255,255,.06);} @@ -312,9 +314,8 @@ - -
- {{ $page->links() }} +
+ {{ $page->onEachSide(1)->links('vendor.pagination.admin') }}
diff --git a/resources/views/admin/members/show.blade.php b/resources/views/admin/members/show.blade.php index 6dd3097..90a2890 100644 --- a/resources/views/admin/members/show.blade.php +++ b/resources/views/admin/members/show.blade.php @@ -48,6 +48,15 @@ .warnbox{border:1px solid rgba(245,158,11,.35);background:rgba(245,158,11,.10);border-radius:16px;padding:12px;} .warnbox b{font-weight:900;} + + /* legacy log list */ + .mlog{margin-top:12px; max-height:320px; overflow:auto; padding-right:6px;} + .mlog__item{padding:10px 12px; border:1px solid rgba(255,255,255,.08); background:rgba(255,255,255,.03); border-radius:14px;} + .mlog__item + .mlog__item{margin-top:10px;} + .mlog__meta{display:flex; gap:8px; flex-wrap:wrap; align-items:center;} + .mlog__meta .mono{font-size:12px;} + .mlog__body{margin-top:8px;font-size:13px;line-height:1.6;} + .arrow{opacity:.7; padding:0 6px;} @endpush @@ -77,9 +86,6 @@ $rcvE = (string)($member->rcv_email ?? 'n'); $rcvS = (string)($member->rcv_sms ?? 'n'); - - // stat_3 select: 4~6은 disabled - $editableOptions = ['1','2','3']; @endphp
@@ -95,7 +101,7 @@
+ href="{{ route('admin.members.index', request()->only(['qf','q','stat_3','date_from','date_to','page'])) }}"> ← 목록 @@ -183,17 +189,13 @@ -
- ※ 4~6은 시스템 상태로 변경 불가 -
+
※ 4~6은 시스템 상태로 변경 불가
@@ -201,18 +203,14 @@
- +
@@ -254,9 +252,7 @@ @php $st = (string)($r['auth_state'] ?? 'N'); @endphp {{ $r['auth_type'] ?? '-' }} - - ● {{ $st }} - + ● {{ $st }} {{ $r['auth_date'] ?? '-' }} @empty @@ -293,12 +289,10 @@
- {{-- 관리자 메모 --}} + {{-- ✅ 관리자 메모 (레거시: when/admin_num/memo) --}}
-
-
관리자 메모
-
+
관리자 메모
@@ -311,22 +305,20 @@
@forelse($adminMemo as $it) + @php + $aid = (int)($it['admin_num'] ?? 0); + $am = $aid > 0 ? ($adminMap[$aid] ?? null) : null; + + $aEmail = is_array($am) ? trim((string)($am['email'] ?? '')) : ''; + $aName = is_array($am) ? trim((string)($am['name'] ?? '')) : ''; + + $aDisp = trim(($aEmail !== '' ? $aEmail : '-')." / ".($aName !== '' ? $aName : '-')); + @endphp +
- {{ $it['ts'] ?? '-' }} - @php - $aid = (int)($it['actor_admin_id'] ?? 0); - $am = $aid > 0 ? ($adminMap[$aid] ?? null) : null; - - $aEmail = is_array($am) ? trim((string)($am['email'] ?? '')) : ''; - $aName = is_array($am) ? trim((string)($am['name'] ?? '')) : ''; - - // 출력: email / 이름 - $aDisp = trim(($aEmail !== '' ? $aEmail : '-')." / ".($aName !== '' ? $aName : '-')); - @endphp - + {{ $it['when'] ?? '-' }} admin : {{ $aDisp }} - @if(!empty($it['ip'])) {{ $it['ip'] }} @endif
{{ $it['memo'] ?? '' }}
@@ -337,79 +329,41 @@
- {{-- 관리자 변경이력(modify_log) --}} + {{-- ✅ 관리자 변경이력 (레거시: state_log) --}}
관리자 변경이력
-
- 너무 길면 아래 영역이 스크롤됩니다. -
+
너무 길면 아래 영역이 스크롤됩니다.
- -
@forelse($modifyLog as $it) @php - $aid = (int)($it['actor_admin_id'] ?? 0); + $aid = (int)($it['admin_num'] ?? 0); $am = $aid > 0 ? ($adminMap[$aid] ?? null) : null; - $aLabel = '-'; - if (is_array($am)) { - $name = trim((string)($am['name'] ?? '')); - $email = trim((string)($am['email'] ?? '')); - $aLabel = $email." / ".$name; - } + + $aEmail = is_array($am) ? trim((string)($am['email'] ?? '')) : ''; + $aName = is_array($am) ? trim((string)($am['name'] ?? '')) : ''; + $aDisp = trim(($aEmail !== '' ? $aEmail : '-')." / ".($aName !== '' ? $aName : '-')); + + $title = (string)($it['title'] ?? '변경'); + $before = (string)($it['before'] ?? ''); + $after = (string)($it['after'] ?? ''); @endphp
- {{ $it['ts'] ?? '-' }} - - admin : {!! ' '.$aLabel !!} - - @if(!empty($it['action'])) {{ $it['action'] }} @endif - @if(!empty($it['ip'])) {{ $it['ip'] }} @endif + {{ $it['when'] ?? '-' }} + admin : {{ $aDisp }} + {{ $title }}
- @if(!empty($it['changes']) && is_array($it['changes'])) -
- @foreach($it['changes'] as $k=>$chg) - @php - $b = $chg['before'] ?? null; - $a = $chg['after'] ?? null; - - $before = is_scalar($b) ? (string)$b : '[...]'; - $after = is_scalar($a) ? (string)$a : '[...]'; - @endphp - -
- {{ $k }} - - {{ $before }} - - {{ $after }} - -
- @endforeach -
- @else -
변경 상세 없음
- @endif +
+ {{ $before !== '' ? $before : '-' }} + + {{ $after !== '' ? $after : '-' }} +
@empty
변경이력이 없습니다.
@@ -418,21 +372,15 @@
- - - {{-- 하단 액션바 --}}
+ href="{{ route('admin.members.index', request()->only(['qf','q','stat_3','date_from','date_to','page'])) }}"> ← 뒤로가기
-
diff --git a/resources/views/admin/partials/sidebar.blade.php b/resources/views/admin/partials/sidebar.blade.php index 7ceaaf1..9ebf0a5 100644 --- a/resources/views/admin/partials/sidebar.blade.php +++ b/resources/views/admin/partials/sidebar.blade.php @@ -18,7 +18,8 @@ 'title' => '회원/정책', 'items' => [ ['label' => '회원 관리', 'route' => 'admin.members.index','roles' => ['super_admin','support']], - ['label' => '회원가입 필터 설정', 'route' => 'admin.signup-filter.index','roles' => ['super_admin','support']], + ['label' => '회원 데이터추출', 'route' => 'admin.signup-filter.index','roles' => ['super_admin','support']], + ['label' => '로그인/가입 아이피 필터설정', 'route' => 'admin.signup-filter.index','roles' => ['super_admin','support']], ['label' => '블랙리스트/제재', 'route' => 'admin.sanctions.index','roles' => ['super_admin','support']], ['label' => '마케팅 수신동의', 'route' => 'admin.marketing.index','roles' => ['super_admin','support']], ], diff --git a/resources/views/admin/qna/index.blade.php b/resources/views/admin/qna/index.blade.php index bad8976..a9fa0dd 100644 --- a/resources/views/admin/qna/index.blade.php +++ b/resources/views/admin/qna/index.blade.php @@ -9,7 +9,6 @@ @push('head') @endpush @@ -202,7 +202,6 @@
- {{-- ✅ 공지사항과 동일한 커스텀 페이징 + 쿼리 유지 --}} {{ $rows->onEachSide(1)->links('vendor.pagination.admin') }}