From 28cb357e36741e0b9665b2f39958c0cc56fd993e Mon Sep 17 00:00:00 2001 From: pro Date: Thu, 22 Jan 2026 15:09:54 +0900 Subject: [PATCH] =?UTF-8?q?feat:=ED=9A=8C=EC=9B=90=EC=82=AC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EC=8B=9C=20=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 수정 모달에서 비밀번호 선택적 입력 가능 - 기존 Hash 저장 데이터를 새로운 encrypted 방식으로 업데이트 가능 - 비밀번호 입력 시에만 업데이트, 빈 값이면 기존 유지 --- .../Barobill/BarobillMemberController.php | 8 + .../views/barobill/members/index.blade.php | 324 +++++++++++++++++- .../members/partials/modal-form.blade.php | 7 +- 3 files changed, 331 insertions(+), 8 deletions(-) diff --git a/app/Http/Controllers/Api/Admin/Barobill/BarobillMemberController.php b/app/Http/Controllers/Api/Admin/Barobill/BarobillMemberController.php index cb471798..54afab76 100644 --- a/app/Http/Controllers/Api/Admin/Barobill/BarobillMemberController.php +++ b/app/Http/Controllers/Api/Admin/Barobill/BarobillMemberController.php @@ -198,6 +198,8 @@ public function show(Request $request, int $id): JsonResponse /** * 회원사 수정 + * + * 비밀번호는 선택적으로 업데이트 가능 (기존 Hash 저장 데이터 호환) */ public function update(Request $request, int $id): JsonResponse { @@ -220,8 +222,14 @@ public function update(Request $request, int $id): JsonResponse 'manager_email' => 'nullable|email|max:100', 'manager_hp' => 'nullable|string|max:20', 'status' => 'nullable|in:active,inactive,pending', + 'barobill_pwd' => 'nullable|string|max:255', // 비밀번호 선택적 업데이트 ]); + // 비밀번호가 비어있으면 제외 (기존 값 유지) + if (empty($validated['barobill_pwd'])) { + unset($validated['barobill_pwd']); + } + $member->update($validated); return response()->json([ diff --git a/resources/views/barobill/members/index.blade.php b/resources/views/barobill/members/index.blade.php index 98c217e7..4d5b0ab2 100644 --- a/resources/views/barobill/members/index.blade.php +++ b/resources/views/barobill/members/index.blade.php @@ -3,8 +3,9 @@ @section('title', '회원사관리') @section('content') +
-
+

회원사관리

바로빌 연동 회원사를 관리합니다

@@ -26,11 +27,12 @@ class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg transition hx-get="/api/admin/barobill/members/stats" hx-trigger="load, memberUpdated from:body" hx-headers='{"X-CSRF-TOKEN": "{{ csrf_token() }}"}' - class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6"> + class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6 flex-shrink-0"> @include('barobill.members.partials.stats-skeleton')
+
@@ -57,6 +59,7 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none foc
+
+ class="bg-white rounded-lg shadow-sm overflow-hidden flex-1 flex flex-col min-h-0">
+
@include('barobill.members.partials.modal-form') + + + + @endsection @push('scripts') @@ -101,7 +140,11 @@ class="bg-white rounded-lg shadow-sm overflow-hidden"> this.resetForm(); document.getElementById('modalTitle').textContent = '회원사 등록'; document.getElementById('submitBtn').textContent = '등록하기'; + // 비밀번호 필드 표시 (필수 입력) document.getElementById('passwordFields').classList.remove('hidden'); + document.getElementById('barobillIdField').classList.remove('hidden'); // 아이디 표시 + document.getElementById('pwdRequired').classList.remove('hidden'); // 필수 표시 + document.getElementById('pwdHint').classList.add('hidden'); // 힌트 숨김 this.modal.classList.remove('hidden'); }, @@ -111,7 +154,11 @@ class="bg-white rounded-lg shadow-sm overflow-hidden"> this.resetForm(); document.getElementById('modalTitle').textContent = '회원사 수정'; document.getElementById('submitBtn').textContent = '수정하기'; - document.getElementById('passwordFields').classList.add('hidden'); + // 비밀번호 필드 표시 (선택적 입력) + document.getElementById('passwordFields').classList.remove('hidden'); + document.getElementById('barobillIdField').classList.add('hidden'); // 아이디는 숨김 + document.getElementById('pwdRequired').classList.add('hidden'); // 필수 표시 숨김 + document.getElementById('pwdHint').classList.remove('hidden'); // 힌트 표시 // 데이터 로드 fetch(`/api/admin/barobill/members/${id}`, { @@ -164,7 +211,10 @@ class="bg-white rounded-lg shadow-sm overflow-hidden"> if (this.isEditing) { delete data.biz_no; delete data.barobill_id; - delete data.barobill_pwd; + // 비밀번호가 비어있으면 제외 (서버에서도 빈 값은 무시) + if (!data.barobill_pwd) { + delete data.barobill_pwd; + } } const url = this.isEditing @@ -240,6 +290,270 @@ class="bg-white rounded-lg shadow-sm overflow-hidden"> }); }; + // 바로빌 서비스 관리 + const BarobillService = { + currentMemberId: null, + currentMemberName: '', + currentAction: '', + currentTitle: '', + + // 액션별 엔드포인트 매핑 + endpoints: { + // 계좌 관련 + 'bank_account': 'bank-account-url', + 'bank_account_manage': 'bank-account-manage-url', + 'bank_account_log': 'bank-account-log-url', + // 카드 관련 + 'card': 'card-url', + 'card_manage': 'card-manage-url', + 'card_log': 'card-log-url', + // 전자세금계산서 관련 + 'certificate': 'certificate-url', + 'tax_invoice': 'tax-invoice-url', + 'tax_invoice_list': 'tax-invoice-list-url', + // 공통 + 'cash_charge': 'cash-charge-url', + }, + + // 통합 URL 열기 함수 (비밀번호 입력 없이 바로 호출) + async openUrl(memberId, memberName, action, title) { + const endpoint = this.endpoints[action]; + if (!endpoint) { + showToast('잘못된 액션입니다.', 'error'); + return; + } + + try { + const res = await fetch(`/api/admin/barobill/members/${memberId}/${endpoint}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-TOKEN': '{{ csrf_token() }}', + 'Accept': 'application/json' + }, + body: JSON.stringify({}) + }); + + const result = await res.json(); + + if (result.success && result.data?.url) { + window.open(result.data.url, '_blank', 'width=1000,height=700'); + } else { + showToast(result.message || result.error || 'URL을 가져오는데 실패했습니다.', 'error'); + } + } catch (error) { + showToast('통신 오류가 발생했습니다.', 'error'); + } + }, + + // 상태 조회 모달 열기 + async showStatus(memberId, memberName) { + document.getElementById('statusModalMemberName').textContent = memberName; + document.getElementById('statusModalContent').innerHTML = ` +
+
+
+ `; + document.getElementById('statusModal').classList.remove('hidden'); + + try { + // 병렬로 상태 조회 (인증서, 잔액, 계좌, 카드) + const [certRes, balanceRes, accountsRes, cardsRes] = await Promise.all([ + fetch(`/api/admin/barobill/members/${memberId}/certificate-status`, { + headers: { 'X-CSRF-TOKEN': '{{ csrf_token() }}', 'Accept': 'application/json' } + }), + fetch(`/api/admin/barobill/members/${memberId}/balance`, { + headers: { 'X-CSRF-TOKEN': '{{ csrf_token() }}', 'Accept': 'application/json' } + }), + fetch(`/api/admin/barobill/members/${memberId}/bank-accounts`, { + headers: { 'X-CSRF-TOKEN': '{{ csrf_token() }}', 'Accept': 'application/json' } + }), + fetch(`/api/admin/barobill/members/${memberId}/cards`, { + headers: { 'X-CSRF-TOKEN': '{{ csrf_token() }}', 'Accept': 'application/json' } + }), + ]); + + const cert = await certRes.json(); + const balance = await balanceRes.json(); + const accounts = await accountsRes.json(); + const cards = await cardsRes.json(); + + this.renderStatusContent(cert, balance, accounts, cards); + } catch (error) { + document.getElementById('statusModalContent').innerHTML = ` +
+

상태 조회 중 오류가 발생했습니다.

+
+ `; + } + }, + + renderStatusContent(cert, balance, accounts, cards) { + const certData = cert.success ? cert.data : {}; + const balanceData = balance.success ? balance.data : {}; + + let html = ` +
+ +
+

+ + + + 공동인증서 (전자세금계산서용) +

+
+
+ 상태 +

+ ${certData.is_valid ? '유효' : '미등록 또는 만료'} +

+
+
+ 만료일 +

${certData.expire_date || '-'}

+
+
+
+ + +
+

+ + + + 충전 잔액 +

+

+ ${balanceData.balance !== undefined ? Number(balanceData.balance).toLocaleString() + '원' : '-'} +

+
+ + +
+

+ + + + 등록 계좌 (빠른조회 서비스) +

+ `; + + if (accounts.success && accounts.data) { + const accountList = Array.isArray(accounts.data) ? accounts.data : + (accounts.data.BankAccountInfo ? [accounts.data.BankAccountInfo] : []); + + if (accountList.length > 0) { + html += `
`; + accountList.forEach(acc => { + html += ` +
+ ${acc.Bank || acc.BankCode || '은행'} + ${acc.BankAccountNum || acc.AccountNum || '-'} +
+ `; + }); + html += `
`; + } else { + html += `

등록된 계좌가 없습니다.

`; + } + } else { + html += `

계좌 정보를 불러올 수 없습니다.

`; + } + + html += `
`; + + // 등록 카드 + html += ` +
+

+ + + + 등록 카드 +

+ `; + + if (cards && cards.success && cards.data) { + const cardList = Array.isArray(cards.data) ? cards.data : + (cards.data.CardInfo ? [cards.data.CardInfo] : []); + + if (cardList.length > 0) { + html += `
`; + cardList.forEach(card => { + const cardNum = card.CardNum || card.cardNum || '-'; + const maskedNum = cardNum.length > 8 ? cardNum.slice(0, 4) + '-****-****-' + cardNum.slice(-4) : cardNum; + html += ` +
+ ${card.CardCompanyName || card.cardCompanyName || '카드사'} + ${maskedNum} +
+ `; + }); + html += `
`; + } else { + html += `

등록된 카드가 없습니다.

`; + } + } else { + html += `

카드 정보를 불러올 수 없습니다.

`; + } + + html += ` +
+
+ `; + + document.getElementById('statusModalContent').innerHTML = html; + }, + + closeStatusModal() { + document.getElementById('statusModal').classList.add('hidden'); + } + }; + + // 바로빌 드롭다운 토글 (vanilla JS) + let currentOpenDropdown = null; + + function toggleBarobillDropdown(button) { + const dropdown = button.closest('.barobill-dropdown'); + const menu = dropdown.querySelector('.barobill-menu'); + const isOpen = !menu.classList.contains('hidden'); + + // 다른 열린 드롭다운 닫기 + closeBarobillDropdown(); + + if (!isOpen) { + menu.classList.remove('hidden'); + currentOpenDropdown = dropdown; + } + } + + function closeBarobillDropdown() { + if (currentOpenDropdown) { + const menu = currentOpenDropdown.querySelector('.barobill-menu'); + if (menu) menu.classList.add('hidden'); + currentOpenDropdown = null; + } + // 모든 드롭다운 닫기 + document.querySelectorAll('.barobill-menu').forEach(menu => { + menu.classList.add('hidden'); + }); + } + + // 바깥 클릭 시 드롭다운 닫기 + document.addEventListener('click', function(e) { + if (!e.target.closest('.barobill-dropdown')) { + closeBarobillDropdown(); + } + }); + + // ESC 키로 드롭다운 닫기 + document.addEventListener('keydown', function(e) { + if (e.key === 'Escape') { + closeBarobillDropdown(); + } + }); + // 초기화 document.addEventListener('DOMContentLoaded', function() { MemberModal.init(); diff --git a/resources/views/barobill/members/partials/modal-form.blade.php b/resources/views/barobill/members/partials/modal-form.blade.php index e3239e77..bd66fe9b 100644 --- a/resources/views/barobill/members/partials/modal-form.blade.php +++ b/resources/views/barobill/members/partials/modal-form.blade.php @@ -76,15 +76,16 @@ class="w-10 h-10 flex items-center justify-center rounded-full text-gray-600 hov
- +
-
+
- + +