From 26c3f2ca4dd4724fe636674abc634f92e2c99b3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Tue, 10 Feb 2026 20:50:26 +0900 Subject: [PATCH] =?UTF-8?q?feat:=EC=9C=A0=ED=98=95=EC=97=90=20=EB=A9=B4?= =?UTF-8?q?=EC=A0=91=20=EC=B6=94=EA=B0=80=20+=20=EC=BB=A4=EC=8A=A4?= =?UTF-8?q?=ED=85=80=20=EC=9C=A0=ED=98=95=20=EB=8F=99=EC=A0=81=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Schedule 모델에 TYPE_INTERVIEW(면접) 추가 (보라색) - 유형 select 옆에 [+] 버튼 → 커스텀 유형 직접 입력 가능 - 컨트롤러 validation을 string|max:50으로 변경 (동적 유형 허용) - 달력 뱃지 색상을 인라인 스타일로 변경 (Tailwind JIT 미빌드 대응) - 수정 모달에서 커스텀 유형도 select에 자동 추가 Co-Authored-By: Claude Opus 4.6 --- .../DashboardCalendarController.php | 4 +- app/Models/System/Schedule.php | 19 ++++ resources/views/dashboard/index.blade.php | 107 ++++++++++++++++-- .../dashboard/partials/calendar.blade.php | 7 +- 4 files changed, 121 insertions(+), 16 deletions(-) diff --git a/app/Http/Controllers/DashboardCalendarController.php b/app/Http/Controllers/DashboardCalendarController.php index be121e0e..e71bf6b4 100644 --- a/app/Http/Controllers/DashboardCalendarController.php +++ b/app/Http/Controllers/DashboardCalendarController.php @@ -58,7 +58,7 @@ public function store(Request $request): JsonResponse 'start_time' => 'nullable|date_format:H:i', 'end_time' => 'nullable|date_format:H:i', 'is_all_day' => 'boolean', - 'type' => 'required|in:event,meeting,notice,other', + 'type' => 'required|string|max:50', 'color' => 'nullable|string|max:20', ]); @@ -116,7 +116,7 @@ public function update(Request $request, int $id): JsonResponse 'start_time' => 'nullable|date_format:H:i', 'end_time' => 'nullable|date_format:H:i', 'is_all_day' => 'boolean', - 'type' => 'required|in:event,meeting,notice,other', + 'type' => 'required|string|max:50', 'color' => 'nullable|string|max:20', ]); diff --git a/app/Models/System/Schedule.php b/app/Models/System/Schedule.php index faa0588f..1d49b0d0 100644 --- a/app/Models/System/Schedule.php +++ b/app/Models/System/Schedule.php @@ -48,12 +48,14 @@ class Schedule extends Model public const TYPE_EVENT = 'event'; public const TYPE_MEETING = 'meeting'; + public const TYPE_INTERVIEW = 'interview'; public const TYPE_NOTICE = 'notice'; public const TYPE_OTHER = 'other'; public const TYPES = [ self::TYPE_EVENT => '일정', self::TYPE_MEETING => '회의', + self::TYPE_INTERVIEW => '면접', self::TYPE_NOTICE => '공지', self::TYPE_OTHER => '기타', ]; @@ -61,6 +63,7 @@ class Schedule extends Model public const TYPE_COLORS = [ self::TYPE_EVENT => ['bg' => 'bg-emerald-50', 'text' => 'text-emerald-700', 'border' => 'border-emerald-200'], self::TYPE_MEETING => ['bg' => 'bg-blue-50', 'text' => 'text-blue-700', 'border' => 'border-blue-200'], + self::TYPE_INTERVIEW => ['bg' => 'bg-violet-50', 'text' => 'text-violet-700', 'border' => 'border-violet-200'], self::TYPE_NOTICE => ['bg' => 'bg-amber-50', 'text' => 'text-amber-700', 'border' => 'border-amber-200'], self::TYPE_OTHER => ['bg' => 'bg-gray-50', 'text' => 'text-gray-700', 'border' => 'border-gray-200'], ]; @@ -98,4 +101,20 @@ public function getTypeColorsAttribute(): array { return self::TYPE_COLORS[$this->type] ?? self::TYPE_COLORS[self::TYPE_OTHER]; } + + /** + * 인라인 스타일로 타입 색상 반환 (Tailwind JIT 미빌드 대응) + */ + public function getTypeStyleAttribute(): string + { + $styles = [ + 'event' => 'background:#ecfdf5;color:#047857;border-color:#a7f3d0;', + 'meeting' => 'background:#eff6ff;color:#1d4ed8;border-color:#bfdbfe;', + 'interview' => 'background:#f5f3ff;color:#6d28d9;border-color:#ddd6fe;', + 'notice' => 'background:#fffbeb;color:#b45309;border-color:#fde68a;', + 'other' => 'background:#f9fafb;color:#374151;border-color:#e5e7eb;', + ]; + + return $styles[$this->type] ?? 'background:#f0fdf4;color:#15803d;border-color:#bbf7d0;'; + } } diff --git a/resources/views/dashboard/index.blade.php b/resources/views/dashboard/index.blade.php index 77a508cd..0aa49e56 100644 --- a/resources/views/dashboard/index.blade.php +++ b/resources/views/dashboard/index.blade.php @@ -112,14 +112,37 @@ class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:rin
- - + +
+ + +
+ +
@@ -267,7 +290,20 @@ function openCreateModal(dateKey) { document.getElementById('modal-title').textContent = '일정 수정'; document.getElementById('schedule-id').value = s.id; document.getElementById('schedule-title').value = s.title || ''; - document.getElementById('schedule-type').value = s.type || 'event'; + // 커스텀 타입이면 select에 동적 추가 + const typeSelect = document.getElementById('schedule-type'); + const typeValue = s.type || 'event'; + let typeExists = false; + for (let i = 0; i < typeSelect.options.length; i++) { + if (typeSelect.options[i].value === typeValue) { typeExists = true; break; } + } + if (!typeExists) { + const opt = document.createElement('option'); + opt.value = typeValue; + opt.textContent = typeValue; + typeSelect.appendChild(opt); + } + typeSelect.value = typeValue; document.getElementById('schedule-start-date').value = s.start_date ? s.start_date.split('T')[0] : ''; document.getElementById('schedule-end-date').value = s.end_date ? s.end_date.split('T')[0] : ''; document.getElementById('schedule-all-day').checked = s.is_all_day; @@ -594,6 +630,59 @@ function formatFileSize(bytes) { return (bytes / (1024 * 1024)).toFixed(1) + ' MB'; } + // ========================================================================= + // 커스텀 유형 추가 + // ========================================================================= + + function showAddTypeInput() { + document.getElementById('custom-type-input').classList.remove('hidden'); + document.getElementById('btn-add-type').classList.add('hidden'); + document.getElementById('new-type-name').focus(); + } + + function hideAddTypeInput() { + document.getElementById('custom-type-input').classList.add('hidden'); + document.getElementById('btn-add-type').classList.remove('hidden'); + document.getElementById('new-type-name').value = ''; + } + + function addCustomType() { + const input = document.getElementById('new-type-name'); + const name = input.value.trim(); + if (!name) { input.focus(); return; } + + const select = document.getElementById('schedule-type'); + const value = name.toLowerCase().replace(/\s+/g, '_'); + + // 중복 확인 + for (let i = 0; i < select.options.length; i++) { + if (select.options[i].value === value || select.options[i].text === name) { + select.value = select.options[i].value; + hideAddTypeInput(); + return; + } + } + + const option = document.createElement('option'); + option.value = value; + option.textContent = name; + select.appendChild(option); + select.value = value; + + hideAddTypeInput(); + } + + // Enter 키로 유형 추가 + document.addEventListener('DOMContentLoaded', () => { + const inp = document.getElementById('new-type-name'); + if (inp) { + inp.addEventListener('keydown', (e) => { + if (e.key === 'Enter') { e.preventDefault(); addCustomType(); } + if (e.key === 'Escape') { hideAddTypeInput(); } + }); + } + }); + // ========================================================================= // 달력 갱신 // ========================================================================= diff --git a/resources/views/dashboard/partials/calendar.blade.php b/resources/views/dashboard/partials/calendar.blade.php index cac3b39f..4eb36298 100644 --- a/resources/views/dashboard/partials/calendar.blade.php +++ b/resources/views/dashboard/partials/calendar.blade.php @@ -124,12 +124,9 @@ class="text-gray-300 hover:text-emerald-600 hover:bg-emerald-50 rounded transiti {{-- 일정 목록 --}}
@foreach($daySchedules as $schedule) - @php - $colors = $schedule->type_colors; - @endphp