Files
sam-manage/resources/views/sales/managers/partials/edit-modal.blade.php
김보곤 2740af2f3c fix: [sales] '유치자' → '유치 파트너'로 공식 용어 통일
- 상품관리, 파트너관리, 가망고객, 승인관리 등 9개 파일 일괄 수정
2026-03-16 17:03:14 +09:00

354 lines
19 KiB
PHP

{{-- 영업파트너 수정 모달 내용 --}}
<div class="p-6 max-h-[80vh] overflow-y-auto">
<!-- 헤더 -->
<div class="flex justify-between items-start mb-6">
<div>
<h2 class="text-xl font-bold text-gray-800">영업파트너 수정</h2>
<p class="text-sm text-gray-500 mt-1">{{ $partner->name }} ({{ $partner->email }})</p>
</div>
<button type="button" onclick="typeof closeDetailModal !== 'undefined' ? closeDetailModal() : closePartnerModal()" class="text-gray-400 hover:text-gray-600">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<!-- 에러 메시지 영역 -->
<div id="edit-modal-errors" class="hidden mb-4 p-3 bg-red-50 border border-red-200 rounded-lg">
<div class="flex items-center gap-2 mb-1">
<svg class="w-4 h-4 text-red-500 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="text-sm font-medium text-red-700">입력 오류</span>
</div>
<ul id="edit-modal-error-list" class="text-sm text-red-600 ml-6 list-disc"></ul>
</div>
<!-- -->
<form id="edit-partner-form" action="{{ route('sales.managers.update', $partner->id) }}" method="POST" enctype="multipart/form-data">
@csrf
@method('PUT')
<!-- 기본 정보 -->
<div class="bg-gray-50 rounded-lg p-4 mb-4">
<h3 class="text-sm font-semibold text-gray-800 mb-3">기본 정보</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-xs font-medium text-gray-700 mb-1">로그인 ID</label>
<input type="text" value="{{ $partner->user_id ?? $partner->email }}" disabled
class="w-full px-3 py-2 border border-gray-300 rounded-lg bg-gray-100 text-gray-500 text-sm">
</div>
<div>
<label class="block text-xs font-medium text-gray-700 mb-1">이름 <span class="text-red-500">*</span></label>
<input type="text" name="name" value="{{ $partner->name }}" required
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm">
</div>
<div>
<label class="block text-xs font-medium text-gray-700 mb-1">이메일 <span class="text-red-500">*</span></label>
<input type="email" name="email" value="{{ $partner->email }}" required
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm">
</div>
<div>
<label class="block text-xs font-medium text-gray-700 mb-1">전화번호</label>
<input type="text" name="phone" value="{{ $partner->phone }}"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm">
</div>
<div>
<label class="block text-xs font-medium text-gray-700 mb-1">비밀번호</label>
<input type="password" name="password" placeholder="변경 시에만 입력"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm">
</div>
<div>
<label class="block text-xs font-medium text-gray-700 mb-1">비밀번호 확인</label>
<input type="password" name="password_confirmation" placeholder="변경 시에만 입력"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm">
</div>
</div>
</div>
<!-- 사업자 정보 (전자서명용) -->
<div class="bg-gray-50 rounded-lg p-4 mb-4">
<h3 class="text-sm font-semibold text-gray-800 mb-1">사업자 정보</h3>
<p class="text-xs text-gray-400 mb-3">전자서명 계약 자동으로 채워집니다 (선택사항)</p>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-xs font-medium text-gray-700 mb-1">상호</label>
<input type="text" name="company_name" value="{{ $partner->salesPartner?->company_name }}"
placeholder="예: (주)홍길동무역"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm">
</div>
<div>
<label class="block text-xs font-medium text-gray-700 mb-1">사업자등록번호</label>
<input type="text" name="biz_no" value="{{ $partner->salesPartner?->biz_no }}"
placeholder="000-00-00000"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm">
</div>
<div class="md:col-span-2">
<label class="block text-xs font-medium text-gray-700 mb-1">주소</label>
<input type="text" name="address" value="{{ $partner->salesPartner?->address }}"
placeholder="사업장 주소"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm">
</div>
</div>
</div>
<!-- 파트너 유형 -->
<div class="bg-gray-50 rounded-lg p-4 mb-4">
<h3 class="text-sm font-semibold text-gray-800 mb-3">파트너 유형</h3>
@if($partner->approval_status === 'pending')
<div class="flex gap-4">
<label class="flex items-center">
<input type="radio" name="partner_type" value="individual"
{{ ($partner->salesPartner?->partner_type ?? 'individual') === 'individual' ? 'checked' : '' }}
class="w-4 h-4 text-blue-600 border-gray-300 focus:ring-blue-500">
<span class="ml-2 text-sm text-gray-700">개인 (영업파트너)</span>
</label>
<label class="flex items-center">
<input type="radio" name="partner_type" value="corporate"
{{ ($partner->salesPartner?->partner_type ?? 'individual') === 'corporate' ? 'checked' : '' }}
class="w-4 h-4 text-blue-600 border-gray-300 focus:ring-blue-500">
<span class="ml-2 text-sm text-gray-700">단체 (그룹)</span>
<span class="ml-1 text-xs text-gray-400">단체 30%, 유치 3%</span>
</label>
</div>
@else
<div class="flex items-center gap-2">
@if(($partner->salesPartner?->partner_type ?? 'individual') === 'corporate')
<span class="px-2 py-1 text-xs font-medium rounded-full bg-amber-100 text-amber-800">단체</span>
<span class="text-xs text-gray-400">단체 30%, 유치 3%</span>
@else
<span class="px-2 py-1 text-xs font-medium rounded-full bg-sky-100 text-sky-800">개인</span>
@endif
<span class="text-xs text-gray-400">승인 후에는 변경할 없습니다</span>
</div>
@endif
</div>
<!-- 역할 -->
<div class="bg-gray-50 rounded-lg p-4 mb-4">
<h3 class="text-sm font-semibold text-gray-800 mb-3">역할 <span class="text-red-500">*</span></h3>
<div class="flex flex-wrap gap-4">
@foreach($roles as $role)
<label class="flex items-center">
<input type="checkbox" name="role_ids[]" value="{{ $role->id }}"
{{ in_array($role->id, $currentRoleIds) ? 'checked' : '' }}
class="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500">
<span class="ml-2 text-sm text-gray-700">
{{ $role->description ?? $role->name }}
</span>
</label>
@endforeach
</div>
</div>
<!-- 추천인 정보 -->
@if($partner->parent)
<div class="bg-gray-50 rounded-lg p-4 mb-4">
<h3 class="text-sm font-semibold text-gray-800 mb-2">추천인(유치 파트너)</h3>
<div class="px-3 py-2 bg-white border border-gray-200 rounded-lg text-sm text-gray-700">
{{ $partner->parent->name }} ({{ $partner->parent->email }})
</div>
<p class="mt-1 text-xs text-gray-500">추천인은 변경할 없습니다.</p>
</div>
@endif
<!-- 기존 첨부 서류 -->
@if($partner->salesDocuments->isNotEmpty())
<div class="bg-gray-50 rounded-lg p-4 mb-4">
<h3 class="text-sm font-semibold text-gray-800 mb-3">기존 첨부 서류</h3>
<div class="space-y-2">
@foreach($partner->salesDocuments as $document)
<div class="flex items-center justify-between p-2 bg-white rounded border">
<div class="flex items-center gap-2">
<span class="px-2 py-0.5 text-xs bg-gray-100 text-gray-600 rounded">{{ $document->document_type_label }}</span>
<span class="text-sm text-gray-700">{{ $document->original_name }}</span>
<span class="text-xs text-gray-400">{{ $document->formatted_size }}</span>
</div>
<button type="button" onclick="deleteDocument({{ $partner->id }}, {{ $document->id }})"
class="text-red-500 hover:text-red-700">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
</div>
@endforeach
</div>
</div>
@endif
<!-- 첨부 서류 -->
<div class="bg-gray-50 rounded-lg p-4 mb-4">
<h3 class="text-sm font-semibold text-gray-800 mb-3"> 서류 추가</h3>
<div id="modal-document-list" class="space-y-3">
<div class="flex items-center gap-3">
<select name="documents[0][document_type]"
class="w-32 px-2 py-1.5 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
@foreach($documentTypes as $value => $label)
<option value="{{ $value }}">{{ $label }}</option>
@endforeach
</select>
<input type="file" name="documents[0][file]" accept="image/*,.pdf,.doc,.docx"
class="flex-1 text-sm text-gray-500 file:mr-2 file:py-1.5 file:px-3 file:rounded-lg file:border-0 file:text-sm file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100">
<input type="text" name="documents[0][description]" placeholder="설명(선택)"
class="w-32 px-2 py-1.5 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
</div>
<button type="button" onclick="addDocumentRow()"
class="mt-3 text-sm text-blue-600 hover:text-blue-800 flex items-center gap-1">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
서류 추가
</button>
</div>
<!-- 푸터 버튼 -->
<div class="flex justify-end gap-3">
<button type="button" onclick="typeof closeDetailModal !== 'undefined' ? closeDetailModal() : closePartnerModal()"
class="px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition text-sm">
취소
</button>
<button type="submit" id="edit-submit-btn"
class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition text-sm">
수정
</button>
</div>
</form>
</div>
<script>
(function() {
let modalDocIndex = 1;
window.addDocumentRow = function() {
const container = document.getElementById('modal-document-list');
const html = `
<div class="flex items-center gap-3">
<select name="documents[${modalDocIndex}][document_type]"
class="w-32 px-2 py-1.5 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
@foreach($documentTypes as $value => $label)
<option value="{{ $value }}">{{ $label }}</option>
@endforeach
</select>
<input type="file" name="documents[${modalDocIndex}][file]" accept="image/*,.pdf,.doc,.docx"
class="flex-1 text-sm text-gray-500 file:mr-2 file:py-1.5 file:px-3 file:rounded-lg file:border-0 file:text-sm file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100">
<input type="text" name="documents[${modalDocIndex}][description]" placeholder="설명(선택)"
class="w-32 px-2 py-1.5 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
<button type="button" onclick="this.parentElement.remove()" class="text-red-500 hover:text-red-700">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
`;
container.insertAdjacentHTML('beforeend', html);
modalDocIndex++;
};
window.deleteDocument = function(partnerId, documentId) {
if (!confirm('이 서류를 삭제하시겠습니까?')) return;
fetch(`/sales/managers/${partnerId}/documents/${documentId}`, {
method: 'DELETE',
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
'Accept': 'application/json'
}
}).then(response => {
if (response.ok) {
openEditModal(partnerId);
}
});
};
// AJAX 폼 제출
const form = document.getElementById('edit-partner-form');
if (form) {
form.addEventListener('submit', function(e) {
e.preventDefault();
const errorsDiv = document.getElementById('edit-modal-errors');
const errorsList = document.getElementById('edit-modal-error-list');
const submitBtn = document.getElementById('edit-submit-btn');
// 에러 초기화
errorsDiv.classList.add('hidden');
errorsList.innerHTML = '';
submitBtn.disabled = true;
submitBtn.textContent = '저장 중...';
const formData = new FormData(form);
fetch(form.action, {
method: 'POST',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Accept': 'application/json',
},
body: formData
})
.then(response => {
if (response.status === 422) {
return response.json().then(data => {
// validation 에러 표시
const errors = data.errors || {};
const errorMessages = Object.values(errors).flat();
const errorLabels = {
'name': '이름',
'email': '이메일',
'phone': '전화번호',
'password': '비밀번호',
'role_ids': '역할',
};
errorMessages.forEach(msg => {
// 영문 필드명을 한글로 치환
let korMsg = msg;
for (const [eng, kor] of Object.entries(errorLabels)) {
korMsg = korMsg.replace(new RegExp(eng, 'gi'), kor);
}
// "The email has already been taken." → 한글 변환
korMsg = korMsg
.replace('The 이메일 has already been taken.', '이미 사용 중인 이메일입니다.')
.replace('The 이름 field is required.', '이름은 필수 입력입니다.')
.replace('The 이메일 field is required.', '이메일은 필수 입력입니다.')
.replace('The 역할 field is required.', '역할을 하나 이상 선택해야 합니다.');
const li = document.createElement('li');
li.textContent = korMsg;
errorsList.appendChild(li);
});
errorsDiv.classList.remove('hidden');
errorsDiv.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
throw new Error('validation');
});
}
if (!response.ok) {
throw new Error('서버 오류가 발생했습니다.');
}
// 성공: 페이지 새로고침
if (typeof showToast === 'function') {
showToast('영업파트너 정보가 수정되었습니다.', 'success');
}
window.location.reload();
})
.catch(error => {
if (error.message !== 'validation') {
errorsDiv.classList.remove('hidden');
const li = document.createElement('li');
li.textContent = error.message || '수정 중 오류가 발생했습니다.';
errorsList.appendChild(li);
}
})
.finally(() => {
submitBtn.disabled = false;
submitBtn.textContent = '수정';
});
});
}
})();
</script>