236 lines
10 KiB
PHP
236 lines
10 KiB
PHP
@extends('admin.layouts.app')
|
|
|
|
@section('title', '다날 휴대폰 본인인증 로그')
|
|
@section('page_title', '다날 휴대폰 본인인증 로그')
|
|
@section('page_desc', 'mem_no / 전화번호로 조회 (이름/CI/DI 등 개인정보 미노출)')
|
|
@section('content_class', 'a-content--full')
|
|
|
|
@push('head')
|
|
<style>
|
|
.bar{display:flex;justify-content:space-between;align-items:flex-end;gap:12px;flex-wrap:wrap;}
|
|
.filters{display:flex;gap:8px;flex-wrap:wrap;align-items:flex-end;}
|
|
.filters .inp{width:220px;}
|
|
.lbtn{padding:8px 12px;font-size:13px;border-radius:12px;line-height:1.1;text-decoration:none;display:inline-flex;align-items:center;justify-content:center;gap:6px;
|
|
border:1px solid rgba(255,255,255,.10);background:rgba(255,255,255,.06);color:inherit;cursor:pointer;}
|
|
.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);}
|
|
.pill--ok{border-color:rgba(34,197,94,.35);background:rgba(34,197,94,.10);}
|
|
.pill--bad{border-color:rgba(244,63,94,.35);background:rgba(244,63,94,.10);}
|
|
.pill--muted{opacity:.9;}
|
|
.mono{padding:4px 8px;border-radius:10px;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.10);
|
|
font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:12px;display:inline-block;}
|
|
.mono a{color:#fff;text-decoration:none;}
|
|
.mono a:hover{text-decoration:underline;}
|
|
|
|
/* modal */
|
|
.mback{position:fixed;inset:0;display:none;align-items:center;justify-content:center;z-index:9999;
|
|
background:rgba(0,0,0,.55);backdrop-filter:blur(2px);}
|
|
.mback.is-open{display:flex;}
|
|
.modalx{width:min(900px, 92vw);max-height:88vh;overflow:auto;border-radius:18px;
|
|
border:1px solid rgba(255,255,255,.12);background:rgba(18,18,18,.96);box-shadow:0 20px 60px rgba(0,0,0,.45);}
|
|
.modalx__head{display:flex;justify-content:space-between;align-items:flex-start;gap:10px;padding:16px 16px 10px;border-bottom:1px solid rgba(255,255,255,.10);}
|
|
.modalx__title{font-weight:900;font-size:16px;}
|
|
.modalx__body{padding:16px;}
|
|
.kv{display:grid;grid-template-columns:140px 1fr;gap:8px 12px;font-size:13px;margin-bottom:14px;}
|
|
.kv .k{opacity:.75;}
|
|
.pre{white-space:pre;overflow:auto;max-height:52vh;padding:12px;border-radius:14px;
|
|
border:1px solid rgba(255,255,255,.10);background:rgba(255,255,255,.04);
|
|
font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:12px;line-height:1.55;}
|
|
</style>
|
|
@endpush
|
|
|
|
@section('content')
|
|
@php
|
|
$memNo = (string)($filters['mem_no'] ?? '');
|
|
$phone = (string)($filters['phone'] ?? '');
|
|
$indexUrl = route('admin.systemlog.member-danalauthtel-logs', [], false);
|
|
@endphp
|
|
|
|
<div class="a-card" style="padding:16px; margin-bottom:16px;">
|
|
<div class="bar">
|
|
<div>
|
|
<div style="font-weight:900;font-size:16px;">다날 휴대폰 본인인증 로그</div>
|
|
<div class="a-muted" style="font-size:12px;margin-top:4px;">mem_no / 전화번호만 검색 · 이름/CI/DI 등은 표시하지 않음</div>
|
|
</div>
|
|
|
|
<form method="GET" action="{{ $indexUrl }}" class="filters">
|
|
<div>
|
|
<label class="a-muted" style="font-size:12px;">mem_no</label>
|
|
<input class="a-input inp" name="mem_no" value="{{ $memNo }}" placeholder="예: 70687">
|
|
</div>
|
|
<div>
|
|
<label class="a-muted" style="font-size:12px;">전화번호</label>
|
|
<input class="a-input inp" name="phone" value="{{ $phone }}" placeholder="예: 01084492969">
|
|
</div>
|
|
|
|
<div style="display:flex;gap:8px;">
|
|
<button class="lbtn lbtn--ghost" type="submit">검색</button>
|
|
<a class="lbtn lbtn--ghost" href="{{ $indexUrl }}">초기화</a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="a-card" style="padding:16px;">
|
|
<div class="a-muted" style="margin-bottom:10px;">총 <b>{{ $page->total() }}</b>건</div>
|
|
|
|
<div style="overflow:auto;">
|
|
<table class="a-table table" style="width:100%; min-width:1100px;">
|
|
<thead>
|
|
<tr>
|
|
<th style="width:90px;">SEQ</th>
|
|
<th style="width:90px;">구분</th>
|
|
<th style="width:110px;">결과</th>
|
|
<th style="width:160px;">회원번호</th>
|
|
<th style="width:210px;">전화번호</th>
|
|
<th>TID</th>
|
|
<th style="width:200px;">인증시간</th>
|
|
<th style="width:140px;"></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@forelse(($rows ?? []) as $r)
|
|
@php
|
|
$seq = (int)($r['seq'] ?? 0);
|
|
$g = (string)($r['gubun'] ?? '');
|
|
$rc = (string)($r['res_code'] ?? '');
|
|
$mno = (int)($r['mem_no'] ?? 0);
|
|
$tid = (string)($r['TID'] ?? '');
|
|
$dt = (string)($r['rgdate'] ?? '');
|
|
|
|
$gLabel = ($g === 'J') ? '가입' : (($g === 'M') ? '수정' : '-');
|
|
$resOk = ($rc === '0000');
|
|
$resCls = $resOk ? 'pill--ok' : 'pill--bad';
|
|
|
|
$phoneDisp = (string)($r['phone_display'] ?? '');
|
|
$memberUrl = $mno > 0 ? ('/members/'.$mno) : '';
|
|
@endphp
|
|
|
|
<tr>
|
|
<td class="a-muted">{{ $seq }}</td>
|
|
<td><span class="pill">{{ $gLabel }}</span></td>
|
|
<td><span class="pill {{ $resCls }}">{{ $rc !== '' ? $rc : '-' }}</span></td>
|
|
<td>
|
|
@if($mno > 0)
|
|
<span class="mono"><a href="{{ $memberUrl }}">{{ $mno }}</a></span>
|
|
@else
|
|
<span class="pill pill--muted">가입차단/미생성</span>
|
|
@endif
|
|
</td>
|
|
<td>
|
|
@if($phoneDisp !== '')
|
|
<span class="mono">{{ $phoneDisp }}</span>
|
|
@else
|
|
<span class="a-muted">-</span>
|
|
@endif
|
|
</td>
|
|
<td><span class="mono">{{ $tid !== '' ? $tid : '-' }}</span></td>
|
|
<td class="a-muted">{{ $dt !== '' ? $dt : '-' }}</td>
|
|
<td style="text-align:right;">
|
|
<button type="button"
|
|
class="lbtn lbtn--ghost lbtn--sm btnJson"
|
|
data-seq="{{ $seq }}">
|
|
JSON 보기
|
|
</button>
|
|
|
|
{{-- row별 JSON(모달용) - 안전하게 script에 저장 --}}
|
|
<script type="application/json" id="info-json-{{ $seq }}">
|
|
{!! json_encode(($r['info_sanitized'] ?? []), JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES|JSON_HEX_TAG|JSON_HEX_APOS|JSON_HEX_AMP|JSON_HEX_QUOT) !!}
|
|
</script>
|
|
</td>
|
|
</tr>
|
|
@empty
|
|
<tr><td colspan="8" class="a-muted" style="padding:16px;">데이터가 없습니다.</td></tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div style="margin-top:12px;">
|
|
{{ $page->onEachSide(1)->links('vendor.pagination.admin') }}
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Modal --}}
|
|
<div class="mback" id="jsonModalBack" aria-hidden="true">
|
|
<div class="modalx" role="dialog" aria-modal="true" aria-labelledby="jsonModalTitle">
|
|
<div class="modalx__head">
|
|
<div>
|
|
<div class="modalx__title" id="jsonModalTitle">JSON 상세</div>
|
|
<div class="a-muted" style="font-size:12px;margin-top:4px;">CI/DI/이름/생년월일/성별/이메일은 표시하지 않습니다.</div>
|
|
</div>
|
|
<button class="lbtn lbtn--ghost lbtn--sm" type="button" id="btnCloseJsonModal">닫기 ✕</button>
|
|
</div>
|
|
|
|
<div class="modalx__body">
|
|
<div class="kv" id="jsonKv"></div>
|
|
<div class="pre" id="jsonPre">{}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
(function(){
|
|
const back = document.getElementById('jsonModalBack');
|
|
const btnClose = document.getElementById('btnCloseJsonModal');
|
|
const kv = document.getElementById('jsonKv');
|
|
const pre = document.getElementById('jsonPre');
|
|
|
|
const open = () => {
|
|
back.classList.add('is-open');
|
|
back.setAttribute('aria-hidden', 'false');
|
|
document.body.style.overflow = 'hidden';
|
|
};
|
|
const close = () => {
|
|
back.classList.remove('is-open');
|
|
back.setAttribute('aria-hidden', 'true');
|
|
document.body.style.overflow = '';
|
|
};
|
|
|
|
const setKV = (obj) => {
|
|
const pairs = [
|
|
['gubun', obj.gubun || '-'],
|
|
['res_code', obj.res_code || '-'],
|
|
['mem_no', obj.mem_no || '-'],
|
|
['mobile_number', obj.mobile_number || '-'],
|
|
['TID', obj.TID || '-'],
|
|
['RETURNCODE', obj.RETURNCODE || '-'],
|
|
];
|
|
kv.innerHTML = pairs.map(([k,v]) => `
|
|
<div class="k">${k}</div><div class="v"><span class="mono">${String(v)}</span></div>
|
|
`).join('');
|
|
};
|
|
|
|
document.querySelectorAll('.btnJson').forEach(btn => {
|
|
btn.addEventListener('click', () => {
|
|
const seq = btn.getAttribute('data-seq');
|
|
const el = document.getElementById('info-json-' + seq);
|
|
if (!el) return;
|
|
|
|
let obj = {};
|
|
try {
|
|
obj = JSON.parse(el.textContent || '{}') || {};
|
|
} catch(e) {
|
|
obj = {};
|
|
}
|
|
|
|
setKV(obj);
|
|
pre.textContent = JSON.stringify(obj, null, 2);
|
|
|
|
open();
|
|
});
|
|
});
|
|
|
|
btnClose?.addEventListener('click', close);
|
|
back?.addEventListener('click', (e) => { if (e.target === back) close(); });
|
|
document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && back.classList.contains('is-open')) close(); });
|
|
})();
|
|
</script>
|
|
@endsection
|