feat:영업관리 사이드바 메뉴 추가 및 담당자 자동등록 기능

- MngMenuSeeder에 영업관리 메뉴 그룹 추가
  - 영업담당자 관리 (/sales/managers)
  - 가망고객 관리 (/sales/prospects)
  - 영업실적 관리 (/sales/records)
- 담당자 등록 화면에 번개 아이콘 자동입력 기능 추가
  - 랜덤 샘플 데이터 자동 채우기

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
pro
2026-01-26 11:28:10 +09:00
parent d39028d92a
commit e271a3fd15
2 changed files with 91 additions and 9 deletions

View File

@@ -639,6 +639,46 @@ protected function seedMainMenus(): void
'options' => ['route_name' => 'credit.inquiry.index', 'section' => 'main'],
]);
// ========================================
// 영업관리 그룹
// ========================================
$salesGroup = $this->createMenu([
'name' => '영업관리',
'url' => '#',
'icon' => 'briefcase',
'sort_order' => $sortOrder++,
'options' => [
'section' => 'main',
'meta' => ['group_id' => 'sales-group'],
],
]);
$salesSubOrder = 0;
$this->createMenu([
'parent_id' => $salesGroup->id,
'name' => '영업담당자 관리',
'url' => '/sales/managers',
'icon' => 'users',
'sort_order' => $salesSubOrder++,
'options' => ['route_name' => 'sales.managers.index', 'section' => 'main'],
]);
$this->createMenu([
'parent_id' => $salesGroup->id,
'name' => '가망고객 관리',
'url' => '/sales/prospects',
'icon' => 'user-group',
'sort_order' => $salesSubOrder++,
'options' => ['route_name' => 'sales.prospects.index', 'section' => 'main'],
]);
$this->createMenu([
'parent_id' => $salesGroup->id,
'name' => '영업실적 관리',
'url' => '/sales/records',
'icon' => 'chart-bar',
'sort_order' => $salesSubOrder++,
'options' => ['route_name' => 'sales.records.index', 'section' => 'main'],
]);
// ========================================
// 시스템 그룹
// ========================================

View File

@@ -12,17 +12,29 @@
</svg>
목록으로
</a>
<h1 class="text-2xl font-bold text-gray-800">영업담당자 등록</h1>
<div class="flex items-center gap-3">
<h1 class="text-2xl font-bold text-gray-800">영업담당자 등록</h1>
<button type="button" id="fillTestDataBtn"
class="p-2 bg-amber-50 text-amber-600 rounded-lg hover:bg-amber-100 transition-all border border-amber-200 group relative"
title="샘플 데이터 자동 입력">
<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="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
<span class="absolute -top-10 left-1/2 -translate-x-1/2 bg-gray-800 text-white text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap pointer-events-none z-50">
랜덤 데이터 채우기
</span>
</button>
</div>
</div>
<!-- -->
<form action="{{ route('sales.managers.store') }}" method="POST" class="bg-white rounded-lg shadow-sm p-6 space-y-6">
<form action="{{ route('sales.managers.store') }}" method="POST" id="managerForm" class="bg-white rounded-lg shadow-sm p-6 space-y-6">
@csrf
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">로그인 ID <span class="text-red-500">*</span></label>
<input type="text" name="member_id" value="{{ old('member_id') }}" required
<input type="text" name="member_id" id="member_id" value="{{ old('member_id') }}" required
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 @error('member_id') border-red-500 @enderror">
@error('member_id')
<p class="mt-1 text-sm text-red-500">{{ $message }}</p>
@@ -31,7 +43,7 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none foc
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">비밀번호 <span class="text-red-500">*</span></label>
<input type="password" name="password" required
<input type="text" name="password" id="password" required
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 @error('password') border-red-500 @enderror">
@error('password')
<p class="mt-1 text-sm text-red-500">{{ $message }}</p>
@@ -42,7 +54,7 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none foc
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">이름 <span class="text-red-500">*</span></label>
<input type="text" name="name" value="{{ old('name') }}" required
<input type="text" name="name" id="name" value="{{ old('name') }}" required
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 @error('name') border-red-500 @enderror">
@error('name')
<p class="mt-1 text-sm text-red-500">{{ $message }}</p>
@@ -51,7 +63,7 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none foc
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">역할 <span class="text-red-500">*</span></label>
<select name="role" required
<select name="role" id="role" required
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 @error('role') border-red-500 @enderror">
<option value="manager" {{ old('role') === 'manager' ? 'selected' : '' }}>매니저</option>
<option value="sales_admin" {{ old('role') === 'sales_admin' ? 'selected' : '' }}>영업관리</option>
@@ -66,13 +78,13 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none foc
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">전화번호</label>
<input type="text" name="phone" value="{{ old('phone') }}"
<input type="text" name="phone" id="phone" value="{{ old('phone') }}"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">이메일</label>
<input type="email" name="email" value="{{ old('email') }}"
<input type="email" name="email" id="email" value="{{ old('email') }}"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
</div>
@@ -92,7 +104,7 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none foc
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">비고</label>
<textarea name="remarks" rows="3"
<textarea name="remarks" id="remarks" rows="3"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">{{ old('remarks') }}</textarea>
</div>
@@ -109,3 +121,33 @@ class="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition"
</form>
</div>
@endsection
@push('scripts')
<script>
document.getElementById('fillTestDataBtn').addEventListener('click', function() {
const sampleNames = ['한효주', '공유', '이동욱', '김고은', '신세경', '박서준', '김수현', '수지', '남주혁', '성시경', '아이유', '조세호'];
const sampleIds = ['sales_pro', 'mkt_king', 'deal_maker', 'win_win', 'growth_lab', 'biz_hero', 'ace_mgr', 'top_tier'];
const randomItem = (arr) => arr[Math.floor(Math.random() * arr.length)];
const randomNum = (len) => Array.from({length: len}, () => Math.floor(Math.random() * 10)).join('');
const formatPhone = (val) => {
const clean = val.replace(/[^0-9]/g, '');
if (clean.length === 11) {
return clean.slice(0, 3) + '-' + clean.slice(3, 7) + '-' + clean.slice(7);
}
return val;
};
const name = randomItem(sampleNames);
const idSuffix = randomNum(3);
document.getElementById('member_id').value = randomItem(sampleIds) + idSuffix;
document.getElementById('password').value = 'password123';
document.getElementById('name').value = name;
document.getElementById('phone').value = formatPhone('010' + randomNum(8));
document.getElementById('email').value = 'manager_' + randomNum(4) + '@example.com';
document.getElementById('remarks').value = 'MNG 시스템 생성 샘플 데이터';
});
</script>
@endpush