- 기본정보에 주민등록번호 필드 추가 - 급여이체정보 섹션 추가 (이체은행, 예금주, 계좌번호) - 부양가족 정보 섹션 추가 (동적 행 추가/삭제) - 첨부파일 업로드/다운로드/삭제 기능 추가 - 은행 목록 config/banks.php 설정 파일 생성 - show 페이지 주민등록번호 뒷자리 마스킹 처리
491 lines
26 KiB
PHP
491 lines
26 KiB
PHP
@extends('layouts.app')
|
|
|
|
@section('title', '사원 등록')
|
|
|
|
@section('content')
|
|
<div class="container mx-auto px-4 py-6 max-w-2xl">
|
|
{{-- 페이지 헤더 --}}
|
|
<div class="mb-6">
|
|
<a href="{{ route('hr.employees.index') }}" class="text-sm text-gray-500 hover:text-gray-700 inline-flex items-center gap-1 mb-2">
|
|
<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="M15 19l-7-7 7-7"/>
|
|
</svg>
|
|
사원 목록으로
|
|
</a>
|
|
<h1 class="text-2xl font-bold text-gray-800">사원 등록</h1>
|
|
</div>
|
|
|
|
{{-- 등록 폼 --}}
|
|
<div class="bg-white rounded-lg shadow-sm p-6">
|
|
<form id="employeeForm"
|
|
hx-post="{{ route('api.admin.hr.employees.store') }}"
|
|
hx-headers='{"X-CSRF-TOKEN": "{{ csrf_token() }}", "Accept": "application/json"}'
|
|
hx-target="#form-message"
|
|
hx-swap="innerHTML"
|
|
class="space-y-6">
|
|
|
|
<div id="form-message"></div>
|
|
|
|
{{-- 기존 직원 불러오기 --}}
|
|
<div x-data="userSearch()" class="border border-blue-200 bg-blue-50 rounded-lg p-4">
|
|
<h3 class="text-sm font-semibold text-blue-800 mb-3">기존 직원 불러오기</h3>
|
|
|
|
<template x-if="!selectedUser">
|
|
<div class="relative">
|
|
<div class="relative">
|
|
<svg class="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
|
|
</svg>
|
|
<input type="text"
|
|
x-model="query"
|
|
@input.debounce.300ms="search()"
|
|
@focus="onFocus()"
|
|
@click.outside="showDropdown = false"
|
|
placeholder="이름, 이메일, 연락처로 검색..."
|
|
class="w-full pl-10 pr-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white text-sm">
|
|
</div>
|
|
|
|
{{-- 검색 결과 드롭다운 --}}
|
|
<div x-show="showDropdown"
|
|
x-transition
|
|
class="absolute z-10 mt-1 w-full bg-white border border-gray-200 rounded-lg shadow-lg max-h-60 overflow-y-auto">
|
|
<template x-if="loading">
|
|
<div class="px-4 py-3 text-sm text-gray-500 text-center">검색 중...</div>
|
|
</template>
|
|
<template x-if="!loading && users.length === 0">
|
|
<div class="px-4 py-3 text-sm text-gray-500 text-center">검색 결과가 없습니다</div>
|
|
</template>
|
|
<template x-for="user in users" :key="user.id">
|
|
<button type="button"
|
|
@click="selectUser(user)"
|
|
class="w-full text-left px-4 py-2.5 hover:bg-blue-50 border-b border-gray-100 last:border-0 transition-colors">
|
|
<div class="text-sm font-medium text-gray-800" x-text="user.name"></div>
|
|
<div class="text-xs text-gray-500">
|
|
<span x-show="user.email" x-text="user.email"></span>
|
|
<span x-show="user.email && user.phone"> · </span>
|
|
<span x-show="user.phone" x-text="user.phone"></span>
|
|
</div>
|
|
</button>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
{{-- 선택된 사용자 표시 --}}
|
|
<template x-if="selectedUser">
|
|
<div class="flex items-center justify-between bg-white border border-green-300 rounded-lg px-4 py-2.5">
|
|
<div class="flex items-center gap-2">
|
|
<svg class="w-5 h-5 text-green-600 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
|
</svg>
|
|
<div>
|
|
<span class="text-sm font-medium text-gray-800" x-text="selectedUser.name"></span>
|
|
<span class="text-xs text-gray-500 ml-1" x-text="selectedUser.email"></span>
|
|
</div>
|
|
</div>
|
|
<button type="button" @click="clearSelection()"
|
|
class="text-xs text-red-500 hover:text-red-700 font-medium shrink-0">
|
|
선택 해제
|
|
</button>
|
|
</div>
|
|
</template>
|
|
|
|
<input type="hidden" name="existing_user_id" :value="selectedUser ? selectedUser.id : ''">
|
|
<p class="text-xs text-blue-600 mt-2">이미 시스템에 등록된 사용자를 사원으로 추가할 수 있습니다.</p>
|
|
</div>
|
|
|
|
{{-- 기본 정보 --}}
|
|
<div class="border-b border-gray-200 pb-4 mb-4">
|
|
<h2 class="text-lg font-semibold text-gray-700">기본 정보</h2>
|
|
</div>
|
|
|
|
{{-- 이름 --}}
|
|
<div>
|
|
<label for="name" class="block text-sm font-medium text-gray-700 mb-1">
|
|
이름 <span class="text-red-500">*</span>
|
|
</label>
|
|
<input type="text" name="name" id="name" required
|
|
placeholder="홍길동"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
|
</div>
|
|
|
|
{{-- 이메일 / 연락처 --}}
|
|
<div class="flex gap-4" style="flex-wrap: wrap;">
|
|
<div style="flex: 1 1 200px;">
|
|
<label for="email" class="block text-sm font-medium text-gray-700 mb-1">이메일</label>
|
|
<input type="email" name="email" id="email"
|
|
placeholder="user@example.com"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
|
</div>
|
|
<div style="flex: 1 1 200px;">
|
|
<label for="phone" class="block text-sm font-medium text-gray-700 mb-1">연락처</label>
|
|
<input type="text" name="phone" id="phone"
|
|
placeholder="010-1234-5678"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
|
</div>
|
|
</div>
|
|
|
|
{{-- 주민등록번호 --}}
|
|
<div>
|
|
<label for="resident_number" class="block text-sm font-medium text-gray-700 mb-1">주민등록번호</label>
|
|
<input type="text" name="resident_number" id="resident_number"
|
|
placeholder="000000-0000000"
|
|
maxlength="14"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
|
</div>
|
|
|
|
{{-- 근무 정보 --}}
|
|
<div class="border-b border-gray-200 pb-4 mb-4 mt-8">
|
|
<h2 class="text-lg font-semibold text-gray-700">근무 정보</h2>
|
|
</div>
|
|
|
|
{{-- 부서 --}}
|
|
<div>
|
|
<label for="department_id" class="block text-sm font-medium text-gray-700 mb-1">부서</label>
|
|
<select name="department_id" id="department_id"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
|
<option value="">선택하세요</option>
|
|
@foreach($departments as $dept)
|
|
<option value="{{ $dept->id }}">{{ $dept->name }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
|
|
{{-- 직급 / 직책 --}}
|
|
<div class="flex gap-4" style="flex-wrap: wrap;">
|
|
<div style="flex: 1 1 200px;">
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">직급</label>
|
|
<div class="flex gap-2">
|
|
<select name="position_key" id="position_key"
|
|
class="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
|
<option value="">선택하세요</option>
|
|
@foreach($ranks as $rank)
|
|
<option value="{{ $rank->key }}">{{ $rank->name }}</option>
|
|
@endforeach
|
|
</select>
|
|
<button type="button" onclick="openPositionModal('rank')"
|
|
class="shrink-0 w-9 h-9 flex items-center justify-center border border-gray-300 rounded-lg text-gray-500 hover:bg-blue-50 hover:text-blue-600 hover:border-blue-300 transition-colors"
|
|
title="직급 추가">
|
|
<svg class="w-5 h-5" 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>
|
|
<div style="flex: 1 1 200px;">
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">직책</label>
|
|
<div class="flex gap-2">
|
|
<select name="job_title_key" id="job_title_key"
|
|
class="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
|
<option value="">선택하세요</option>
|
|
@foreach($titles as $title)
|
|
<option value="{{ $title->key }}">{{ $title->name }}</option>
|
|
@endforeach
|
|
</select>
|
|
<button type="button" onclick="openPositionModal('title')"
|
|
class="shrink-0 w-9 h-9 flex items-center justify-center border border-gray-300 rounded-lg text-gray-500 hover:bg-blue-50 hover:text-blue-600 hover:border-blue-300 transition-colors"
|
|
title="직책 추가">
|
|
<svg class="w-5 h-5" 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>
|
|
</div>
|
|
|
|
{{-- 입사일 / 퇴직일 / 상태 --}}
|
|
<div class="flex gap-4" style="flex-wrap: wrap;">
|
|
<div style="flex: 1 1 150px;">
|
|
<label for="hire_date" class="block text-sm font-medium text-gray-700 mb-1">입사일</label>
|
|
<input type="date" name="hire_date" id="hire_date"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
|
</div>
|
|
<div style="flex: 1 1 150px;">
|
|
<label for="resign_date" class="block text-sm font-medium text-gray-700 mb-1">퇴직일</label>
|
|
<input type="date" name="resign_date" id="resign_date"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
|
</div>
|
|
<div style="flex: 1 1 150px;">
|
|
<label for="employee_status" class="block text-sm font-medium text-gray-700 mb-1">재직상태</label>
|
|
<select name="employee_status" id="employee_status"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
|
<option value="active" selected>재직</option>
|
|
<option value="leave">휴직</option>
|
|
<option value="resigned">퇴직</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- 주소 --}}
|
|
<div>
|
|
<label for="address" class="block text-sm font-medium text-gray-700 mb-1">주소</label>
|
|
<input type="text" name="address" id="address"
|
|
placeholder="주소를 입력하세요"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
|
</div>
|
|
|
|
{{-- 비상연락처 --}}
|
|
<div>
|
|
<label for="emergency_contact" class="block text-sm font-medium text-gray-700 mb-1">비상연락처</label>
|
|
<input type="text" name="emergency_contact" id="emergency_contact"
|
|
placeholder="긴급 시 연락할 전화번호"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
|
</div>
|
|
|
|
{{-- 급여이체정보 --}}
|
|
<div class="border-b border-gray-200 pb-4 mb-4 mt-8">
|
|
<h2 class="text-lg font-semibold text-gray-700">급여이체정보</h2>
|
|
</div>
|
|
|
|
<div>
|
|
<label for="bank_account_bank_code" class="block text-sm font-medium text-gray-700 mb-1">이체은행</label>
|
|
<select name="bank_account[bank_code]" id="bank_account_bank_code"
|
|
onchange="this.form['bank_account[bank_name]'].value = this.options[this.selectedIndex].text !== '선택하세요' ? this.options[this.selectedIndex].text : ''"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
|
<option value="">선택하세요</option>
|
|
@foreach(config('banks', []) as $code => $name)
|
|
<option value="{{ $code }}">{{ $name }}</option>
|
|
@endforeach
|
|
</select>
|
|
<input type="hidden" name="bank_account[bank_name]" value="">
|
|
</div>
|
|
|
|
<div class="flex gap-4" style="flex-wrap: wrap;">
|
|
<div style="flex: 1 1 200px;">
|
|
<label for="bank_account_account_holder" class="block text-sm font-medium text-gray-700 mb-1">예금주</label>
|
|
<input type="text" name="bank_account[account_holder]" id="bank_account_account_holder"
|
|
placeholder="예금주명"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
|
</div>
|
|
<div style="flex: 1 1 200px;">
|
|
<label for="bank_account_account_number" class="block text-sm font-medium text-gray-700 mb-1">계좌번호</label>
|
|
<input type="text" name="bank_account[account_number]" id="bank_account_account_number"
|
|
placeholder="숫자만 입력"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
|
</div>
|
|
</div>
|
|
|
|
{{-- 부양가족 정보 --}}
|
|
<div class="border-b border-gray-200 pb-4 mb-4 mt-8">
|
|
<h2 class="text-lg font-semibold text-gray-700">부양가족 정보</h2>
|
|
</div>
|
|
|
|
<div x-data="dependentsManager()">
|
|
<template x-for="(dep, index) in dependents" :key="index">
|
|
<div class="border border-gray-200 rounded-lg p-4 mb-3 relative">
|
|
<button type="button" @click="removeDependent(index)"
|
|
class="absolute top-2 right-2 text-red-400 hover:text-red-600 transition-colors" title="삭제">
|
|
<svg class="w-5 h-5" 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 class="text-xs font-medium text-gray-500 mb-2" x-text="'부양가족 ' + (index + 1)"></div>
|
|
<div class="flex gap-3 mb-2" style="flex-wrap: wrap;">
|
|
<div style="flex: 1 1 120px;">
|
|
<input type="text" :name="'dependents['+index+'][name]'" x-model="dep.name"
|
|
placeholder="이름" class="w-full px-2 py-1.5 border border-gray-300 rounded text-sm focus:ring-1 focus:ring-blue-500">
|
|
</div>
|
|
<div style="flex: 0 0 100px;">
|
|
<select :name="'dependents['+index+'][nationality]'" x-model="dep.nationality"
|
|
class="w-full px-2 py-1.5 border border-gray-300 rounded text-sm focus:ring-1 focus:ring-blue-500">
|
|
<option value="korean">내국인</option>
|
|
<option value="foreigner">외국인</option>
|
|
</select>
|
|
</div>
|
|
<div style="flex: 1 1 150px;">
|
|
<input type="text" :name="'dependents['+index+'][resident_number]'" x-model="dep.resident_number"
|
|
placeholder="주민등록번호" maxlength="14" class="w-full px-2 py-1.5 border border-gray-300 rounded text-sm focus:ring-1 focus:ring-blue-500">
|
|
</div>
|
|
</div>
|
|
<div class="flex gap-3 items-center" style="flex-wrap: wrap;">
|
|
<div style="flex: 0 0 100px;">
|
|
<select :name="'dependents['+index+'][relationship]'" x-model="dep.relationship"
|
|
class="w-full px-2 py-1.5 border border-gray-300 rounded text-sm focus:ring-1 focus:ring-blue-500">
|
|
<option value="">관계</option>
|
|
<option value="spouse">배우자</option>
|
|
<option value="child">자녀</option>
|
|
<option value="parent">부모</option>
|
|
<option value="sibling">형제자매</option>
|
|
<option value="grandparent">조부모</option>
|
|
<option value="other">기타</option>
|
|
</select>
|
|
</div>
|
|
<label class="inline-flex items-center gap-1 text-sm text-gray-600 cursor-pointer">
|
|
<input type="hidden" :name="'dependents['+index+'][is_disabled]'" value="0">
|
|
<input type="checkbox" :name="'dependents['+index+'][is_disabled]'" x-model="dep.is_disabled" value="1"
|
|
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500">
|
|
장애인
|
|
</label>
|
|
<label class="inline-flex items-center gap-1 text-sm text-gray-600 cursor-pointer">
|
|
<input type="hidden" :name="'dependents['+index+'][is_dependent]'" value="0">
|
|
<input type="checkbox" :name="'dependents['+index+'][is_dependent]'" x-model="dep.is_dependent" value="1"
|
|
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500">
|
|
피부양자적용
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<button type="button" @click="addDependent()"
|
|
class="w-full py-2 border-2 border-dashed border-gray-300 rounded-lg text-sm text-gray-500 hover:border-blue-400 hover:text-blue-600 transition-colors">
|
|
+ 부양가족 추가
|
|
</button>
|
|
</div>
|
|
|
|
{{-- 첨부파일 안내 --}}
|
|
<div class="border-b border-gray-200 pb-4 mb-4 mt-8">
|
|
<h2 class="text-lg font-semibold text-gray-700">첨부파일</h2>
|
|
</div>
|
|
<div class="bg-gray-50 border border-gray-200 rounded-lg p-4">
|
|
<p class="text-sm text-gray-500">사원 등록 후 상세/수정 페이지에서 파일을 업로드할 수 있습니다.</p>
|
|
</div>
|
|
|
|
{{-- 버튼 --}}
|
|
<div class="flex justify-end gap-3 pt-4 border-t">
|
|
<a href="{{ route('hr.employees.index') }}"
|
|
class="px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors">
|
|
취소
|
|
</a>
|
|
<button type="submit"
|
|
class="px-6 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors">
|
|
등록
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{{-- 직급/직책 추가 모달 --}}
|
|
@include('hr.employees.partials.position-add-modal')
|
|
@endsection
|
|
|
|
@push('scripts')
|
|
<script>
|
|
function userSearch() {
|
|
return {
|
|
query: '',
|
|
users: [],
|
|
selectedUser: null,
|
|
showDropdown: false,
|
|
loading: false,
|
|
|
|
async search() {
|
|
this.loading = true;
|
|
this.showDropdown = true;
|
|
try {
|
|
const res = await fetch(`{{ route('api.admin.hr.employees.search-users') }}?q=${encodeURIComponent(this.query)}`, {
|
|
headers: { 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest' }
|
|
});
|
|
const json = await res.json();
|
|
this.users = json.data || [];
|
|
} catch (e) {
|
|
this.users = [];
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
onFocus() {
|
|
if (this.users.length === 0) this.search();
|
|
else this.showDropdown = true;
|
|
},
|
|
|
|
selectUser(user) {
|
|
this.selectedUser = user;
|
|
this.showDropdown = false;
|
|
this.query = '';
|
|
|
|
const nameEl = document.getElementById('name');
|
|
const emailEl = document.getElementById('email');
|
|
const phoneEl = document.getElementById('phone');
|
|
|
|
nameEl.value = user.name || '';
|
|
emailEl.value = user.email || '';
|
|
phoneEl.value = user.phone || '';
|
|
|
|
[nameEl, emailEl, phoneEl].forEach(el => {
|
|
el.readOnly = true;
|
|
el.classList.add('bg-gray-100', 'text-gray-500');
|
|
});
|
|
},
|
|
|
|
clearSelection() {
|
|
this.selectedUser = null;
|
|
|
|
const nameEl = document.getElementById('name');
|
|
const emailEl = document.getElementById('email');
|
|
const phoneEl = document.getElementById('phone');
|
|
|
|
[nameEl, emailEl, phoneEl].forEach(el => {
|
|
el.value = '';
|
|
el.readOnly = false;
|
|
el.classList.remove('bg-gray-100', 'text-gray-500');
|
|
});
|
|
}
|
|
};
|
|
}
|
|
|
|
function dependentsManager() {
|
|
return {
|
|
dependents: [],
|
|
addDependent() {
|
|
this.dependents.push({
|
|
name: '', nationality: 'korean', resident_number: '',
|
|
relationship: '', is_disabled: false, is_dependent: false
|
|
});
|
|
},
|
|
removeDependent(index) {
|
|
this.dependents.splice(index, 1);
|
|
}
|
|
};
|
|
}
|
|
|
|
document.body.addEventListener('htmx:afterRequest', function(event) {
|
|
if (event.detail.elt.id !== 'employeeForm') return;
|
|
|
|
const xhr = event.detail.xhr;
|
|
try {
|
|
const response = JSON.parse(xhr.responseText);
|
|
|
|
if (response.success) {
|
|
showToast(response.message || '사원이 등록되었습니다.', 'success');
|
|
window.location.href = '{{ route('hr.employees.index') }}';
|
|
return;
|
|
}
|
|
|
|
// 서버에서 success: false 응답
|
|
let msg = response.message || '저장에 실패했습니다.';
|
|
if (response.error) msg += '\n' + response.error;
|
|
showToast(msg, 'error', 5000);
|
|
} catch (e) {
|
|
if (!event.detail.successful) {
|
|
showToast('서버 오류가 발생했습니다. (HTTP ' + xhr.status + ')', 'error', 5000);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Validation 에러 (422) 처리
|
|
document.body.addEventListener('htmx:responseError', function(event) {
|
|
if (event.detail.elt.id !== 'employeeForm') return;
|
|
|
|
const xhr = event.detail.xhr;
|
|
try {
|
|
const response = JSON.parse(xhr.responseText);
|
|
|
|
if (xhr.status === 422 && response.errors) {
|
|
const messages = [];
|
|
for (const field in response.errors) {
|
|
messages.push(response.errors[field].join(', '));
|
|
}
|
|
showToast(messages.join('\n'), 'error', 6000);
|
|
} else {
|
|
let msg = response.message || '오류가 발생했습니다.';
|
|
if (response.error) msg += '\n' + response.error;
|
|
showToast(msg, 'error', 5000);
|
|
}
|
|
} catch (e) {
|
|
showToast('서버 오류가 발생했습니다. (HTTP ' + xhr.status + ')', 'error', 5000);
|
|
}
|
|
});
|
|
</script>
|
|
@endpush
|