389 lines
18 KiB
PHP
389 lines
18 KiB
PHP
@extends('admin.layouts.app')
|
|
|
|
@section('title', '회원 상세')
|
|
@section('page_title', '회원 상세')
|
|
@section('page_desc', '회원상태/전화번호 변경/메모/변경이력을 관리합니다.')
|
|
|
|
@push('head')
|
|
<style>
|
|
/* members show only */
|
|
.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--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);}
|
|
.lbtn--danger{background:rgba(244,63,94,.88);border-color:rgba(244,63,94,.95);color:#fff;}
|
|
.lbtn--danger:hover{background:rgba(244,63,94,.98);}
|
|
.lbtn--ghost{background:transparent;}
|
|
.lbtn--sm{padding:7px 10px;font-size:12px;border-radius:11px;}
|
|
.lbtn--wide{padding:10px 14px;font-weight:800;}
|
|
|
|
.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,.12);}
|
|
.pill--bad{border-color:rgba(244,63,94,.35);background:rgba(244,63,94,.10);}
|
|
.pill--warn{border-color:rgba(245,158,11,.35);background:rgba(245,158,11,.12);}
|
|
.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;}
|
|
|
|
.kvgrid{display:grid;grid-template-columns:1fr;gap:12px;}
|
|
@media (min-width: 980px){ .kvgrid{grid-template-columns:1fr 1fr 1fr;} }
|
|
.kv{padding:14px;border-radius:16px;border:1px solid rgba(255,255,255,.10);background:rgba(255,255,255,.04);}
|
|
.kv .k{font-size:12px;opacity:.8;margin-bottom:6px;}
|
|
.kv .v{font-weight:900;}
|
|
|
|
.actions{position:sticky;bottom:10px;z-index:5;margin-top:12px;
|
|
display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap;align-items:center;
|
|
padding:12px;border-radius:16px;border:1px solid rgba(255,255,255,.10);background:rgba(0,0,0,.25);backdrop-filter:blur(10px);}
|
|
.actions__right{display:flex;gap:8px;flex-wrap:wrap;align-items:center;}
|
|
|
|
.memo{border:1px solid rgba(255,255,255,.10);background:rgba(255,255,255,.04);border-radius:16px;padding:12px;}
|
|
.memo__item{padding:10px;border-radius:12px;border:1px solid rgba(255,255,255,.08);background:rgba(255,255,255,.03);}
|
|
.memo__meta{font-size:12px;opacity:.8;margin-bottom:6px;display:flex;gap:10px;flex-wrap:wrap;}
|
|
|
|
.grid2{display:grid;grid-template-columns:1fr;gap:12px;}
|
|
@media (min-width: 980px){ .grid2{grid-template-columns:1fr 1fr;} }
|
|
|
|
.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;}
|
|
</style>
|
|
@endpush
|
|
|
|
@section('content')
|
|
@php
|
|
$no = (int)($member->mem_no ?? 0);
|
|
|
|
$s3 = (string)($member->stat_3 ?? '1');
|
|
$s3Label = $stat3Map[$s3] ?? ('stat_3='.$s3);
|
|
$sPill = 'pill--muted';
|
|
if ($s3==='1') $sPill='pill--ok';
|
|
elseif($s3==='2') $sPill='pill--warn';
|
|
elseif($s3==='3') $sPill='pill--bad';
|
|
|
|
$g = (string)($member->gender ?? 'n');
|
|
$n = (string)($member->native ?? 'n');
|
|
$gender = $genderMap[$g] ?? '-';
|
|
$native = $nativeMap[$n] ?? '-';
|
|
|
|
$birth = (string)($member->birth ?? '');
|
|
$birth = ($birth && $birth !== '0000-00-00') ? $birth : '-';
|
|
|
|
$dtReg = $member->dt_reg ?? '-';
|
|
$dtMod = $member->dt_mod ?? '-';
|
|
$dtLogin = $member->dt_login ?? '-';
|
|
$failCnt = (int)($member->login_fail_cnt ?? 0);
|
|
|
|
$rcvE = (string)($member->rcv_email ?? 'n');
|
|
$rcvS = (string)($member->rcv_sms ?? 'n');
|
|
@endphp
|
|
|
|
<div class="a-card" style="padding:16px; margin-bottom:16px;">
|
|
<div style="display:flex; justify-content:space-between; align-items:flex-start; gap:12px; flex-wrap:wrap;">
|
|
<div>
|
|
<div style="font-weight:900; font-size:16px;">회원 상세</div>
|
|
<div class="a-muted" style="font-size:12px; margin-top:4px;">
|
|
#{{ $no }} / <b>{{ $member->name ?? '-' }}</b>
|
|
@if(!empty($member->email))
|
|
· <span class="mono">{{ $member->email }}</span>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<a class="lbtn lbtn--ghost lbtn--sm"
|
|
href="{{ route('admin.members.index', request()->only(['qf','q','stat_3','date_from','date_to','page'])) }}">
|
|
← 목록
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- KV --}}
|
|
<div class="kvgrid" style="margin-bottom:16px;">
|
|
<div class="kv">
|
|
<div class="k">회원상태</div>
|
|
<div class="v"><span class="pill {{ $sPill }}">● {{ $s3Label }}</span></div>
|
|
</div>
|
|
|
|
<div class="kv">
|
|
<div class="k">성명/기본정보</div>
|
|
<div class="v">
|
|
{{ $member->name ?? '-' }}
|
|
<div class="a-muted" style="font-size:12px;margin-top:6px;">
|
|
성별: {{ $gender }} · 생년월일: {{ $birth }} · {{ $native }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="kv">
|
|
<div class="k">휴대폰(통신사)</div>
|
|
<div class="v">
|
|
<span class="mono">{{ $corpLabel ?? '-' }}</span>
|
|
<span class="mono">{{ $phoneDisplay ?? '-' }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="kv">
|
|
<div class="k">수신동의</div>
|
|
<div class="v">
|
|
<span class="mono">Email : {{ $rcvE }}</span>
|
|
<span class="mono">SMS : {{ $rcvS }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="kv">
|
|
<div class="k">로그인 실패횟수</div>
|
|
<div class="v">{{ $failCnt }}</div>
|
|
</div>
|
|
|
|
<div class="kv">
|
|
<div class="k">최근로그인 일시</div>
|
|
<div class="v">{{ $dtLogin }}</div>
|
|
</div>
|
|
|
|
<div class="kv">
|
|
<div class="k">가입일시</div>
|
|
<div class="v">{{ $dtReg }}</div>
|
|
</div>
|
|
|
|
<div class="kv">
|
|
<div class="k">최근정보변경 일시</div>
|
|
<div class="v">{{ $dtMod }}</div>
|
|
</div>
|
|
|
|
<div class="kv">
|
|
<div class="k">계좌정보</div>
|
|
<div class="v">
|
|
@if(($bank['has'] ?? false))
|
|
<div><span class="mono">{{ $bank['bank_name'] ?? '-' }}</span></div>
|
|
<div style="margin-top:6px;"><span class="mono">{{ $bank['account'] ?? '-' }}</span></div>
|
|
<div class="a-muted" style="font-size:12px;margin-top:6px;">예금주: <b>{{ $bank['holder'] ?? '-' }}</b></div>
|
|
@else
|
|
<span class="a-muted">등록안됨</span>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid2" style="margin-bottom:16px;">
|
|
{{-- 수정 폼: stat_3(1~3), 통신사, 전화번호만 --}}
|
|
<form id="memberEditForm"
|
|
method="POST"
|
|
action="{{ route('admin.members.update', ['memNo'=>$no]) }}"
|
|
onsubmit="this.querySelector('button[type=submit][data-submit=save]')?.setAttribute('disabled','disabled');">
|
|
@csrf
|
|
|
|
<div class="a-card" style="padding:16px;">
|
|
<div style="font-weight:900; margin-bottom:10px;">접근상태/전화번호 변경</div>
|
|
|
|
<div class="a-field" style="margin-bottom:12px;">
|
|
<label class="a-label">회원상태</label>
|
|
<select class="a-input" name="stat_3">
|
|
@foreach($stat3Map as $k=>$label)
|
|
@php $disabled = in_array((string)$k, ['4','5','6'], true) ? 'disabled' : ''; @endphp
|
|
<option value="{{ $k }}" {{ (string)old('stat_3', $s3)===(string)$k ? 'selected' : '' }} {{ $disabled }}>
|
|
{{ $label }}
|
|
</option>
|
|
@endforeach
|
|
</select>
|
|
<div class="a-muted" style="font-size:12px;margin-top:6px;">※ 4~6은 시스템 상태로 변경 불가</div>
|
|
</div>
|
|
|
|
<div class="a-field" style="margin-bottom:12px;">
|
|
<label class="a-label">통신사</label>
|
|
<select class="a-input" name="cell_corp">
|
|
@php $corpSel = (string)old('cell_corp', $member->cell_corp ?? 'n'); @endphp
|
|
@foreach($corpMap as $k=>$label)
|
|
<option value="{{ $k }}" {{ $corpSel===(string)$k ? 'selected' : '' }}>{{ $label }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
|
|
<div class="a-field">
|
|
<label class="a-label">전화번호(숫자만)</label>
|
|
<input class="a-input" name="cell_phone" value="{{ old('cell_phone', $plainPhone ?? '') }}" placeholder="01000000000">
|
|
</div>
|
|
|
|
<div class="warnbox" style="margin-top:12px;">
|
|
<div style="font-weight:900;margin-bottom:6px;">주의사항 : 꼭 아래 내용 회원에게 수신 후 처리해 주시기 바랍니다.</div>
|
|
<div style="font-size:13px;line-height:1.7;">
|
|
- 이름: <b>홍길동</b> (현재 인증받은 회원성명과 동일해야 함)<br>
|
|
- 핀포유 가입 이메일: <b>test@test.com</b><br>
|
|
- 연락 가능한 전화번호: <b>010-000-0000</b><br><br>
|
|
|
|
<b>첨부파일</b> (아래 이메일 첨부파일 안내를 확인해주세요.)<br>
|
|
1. 신분증 사진<br>
|
|
2. 신분증을 들고 있는 본인 사진(비대면 실명확인)<br>
|
|
3. 통신사 이용계약증명서 사진(화면캡쳐 이미지 사용불가)
|
|
</div>
|
|
</div>
|
|
|
|
<div class="a-muted" style="font-size:12px;margin-top:10px;">
|
|
※ 이름/이메일/수신동의/계좌정보는 변경 불가
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
{{-- 인증/주소 --}}
|
|
<div class="a-card" style="padding:16px;">
|
|
<div style="font-weight:900; margin-bottom:10px;">인증/주소</div>
|
|
|
|
<div style="margin-bottom:14px;">
|
|
<div style="overflow:auto;">
|
|
<table class="a-table" style="width:100%; min-width:520px;">
|
|
<thead>
|
|
<tr>
|
|
<th style="width:120px;">type</th>
|
|
<th style="width:90px;">state</th>
|
|
<th>date</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@forelse($authRows as $r)
|
|
@php $st = (string)($r['auth_state'] ?? 'N'); @endphp
|
|
<tr>
|
|
<td><span class="mono">{{ $r['auth_type'] ?? '-' }}</span></td>
|
|
<td><span class="pill {{ $st==='Y' ? 'pill--ok' : 'pill--muted' }}">● {{ $st }}</span></td>
|
|
<td class="a-muted">{{ $r['auth_date'] ?? '-' }}</td>
|
|
</tr>
|
|
@empty
|
|
<tr><td colspan="3" class="a-muted" style="padding:12px;">인증 내역이 없습니다.</td></tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<div class="a-muted" style="font-size:12px;margin-bottom:8px;">주소</div>
|
|
<div style="display:grid; gap:10px;">
|
|
@forelse($addresses as $a)
|
|
<div class="memo__item">
|
|
<div class="memo__meta">
|
|
<span class="mono">#{{ $a['seq'] ?? '-' }}</span>
|
|
<span class="mono">gubun={{ $a['gubun'] ?? '-' }}</span>
|
|
<span class="mono">shipping={{ $a['shipping'] ?? '-' }}</span>
|
|
<span class="a-muted">{{ $a['date'] ?? '-' }}</span>
|
|
</div>
|
|
<div style="font-size:13px;">
|
|
({{ $a['zipNo'] ?? '' }})
|
|
{{ $a['roadAddrPart1'] ?? '' }}
|
|
{{ $a['jibunAddr'] ?? '' }}
|
|
{{ $a['addrDetail'] ?? '' }}
|
|
</div>
|
|
</div>
|
|
@empty
|
|
<div class="a-muted">주소가 없습니다.</div>
|
|
@endforelse
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- 관리자 메모 (레거시: when/admin_num/memo) --}}
|
|
<div class="a-card" style="padding:16px; margin-bottom:16px;">
|
|
<div style="display:flex;justify-content:space-between;align-items:flex-end;gap:12px;flex-wrap:wrap;">
|
|
<div><div style="font-weight:900;">관리자 메모</div></div>
|
|
|
|
<form method="POST" action="{{ route('admin.members.memo.add', ['memNo'=>$no]) }}"
|
|
style="display:flex;gap:8px;flex-wrap:wrap;align-items:flex-end;">
|
|
@csrf
|
|
<input class="a-input" name="memo" placeholder="메모 입력 (최대 1000자)" style="width:360px;max-width:70vw;">
|
|
<button class="lbtn lbtn--primary" type="submit">추가</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="memo" style="margin-top:12px;">
|
|
<div style="display:grid; gap:10px;">
|
|
@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
|
|
|
|
<div class="memo__item">
|
|
<div class="memo__meta">
|
|
<span class="mono">{{ $it['when'] ?? '-' }}</span>
|
|
<span class="mono">admin : {{ $aDisp }}</span>
|
|
</div>
|
|
<div style="white-space:pre-wrap; font-size:13px;">{{ $it['memo'] ?? '' }}</div>
|
|
</div>
|
|
@empty
|
|
<div class="a-muted">메모가 없습니다.</div>
|
|
@endforelse
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- 관리자 변경이력 (레거시: state_log) --}}
|
|
<div class="a-card" style="padding:16px; margin-bottom:16px;">
|
|
<div style="display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap;">
|
|
<div style="font-weight:900;">관리자 변경이력</div>
|
|
<div class="a-muted" style="font-size:12px;">너무 길면 아래 영역이 스크롤됩니다.</div>
|
|
</div>
|
|
|
|
<div class="mlog">
|
|
<div style="display:grid; gap:10px;">
|
|
@forelse($modifyLog 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 : '-'));
|
|
|
|
$title = (string)($it['title'] ?? '변경');
|
|
$before = (string)($it['before'] ?? '');
|
|
$after = (string)($it['after'] ?? '');
|
|
@endphp
|
|
|
|
<div class="mlog__item">
|
|
<div class="mlog__meta">
|
|
<span class="mono">{{ $it['when'] ?? '-' }}</span>
|
|
<span class="mono">admin : {{ $aDisp }}</span>
|
|
<span class="mono">{{ $title }}</span>
|
|
</div>
|
|
|
|
<div class="mlog__body">
|
|
<b>{{ $before !== '' ? $before : '-' }}</b>
|
|
<span class="arrow">→</span>
|
|
<b>{{ $after !== '' ? $after : '-' }}</b>
|
|
</div>
|
|
</div>
|
|
@empty
|
|
<div class="a-muted">변경이력이 없습니다.</div>
|
|
@endforelse
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- 하단 액션바 --}}
|
|
<div class="actions">
|
|
<a class="lbtn lbtn--ghost"
|
|
href="{{ route('admin.members.index', request()->only(['qf','q','stat_3','date_from','date_to','page'])) }}">
|
|
← 뒤로가기
|
|
</a>
|
|
|
|
<div class="actions__right">
|
|
<button class="lbtn lbtn--primary lbtn--wide" form="memberEditForm" type="submit" data-submit="save">
|
|
저장
|
|
</button>
|
|
</div>
|
|
</div>
|
|
@endsection
|