feat: [approvals] 양식 선택 시 설명 카드 표시
- 지출결의서/품의서 5종 선택 시 우측에 설명 카드 노출 - 드롭다운 30% + 설명 카드 70% 레이아웃 - 양식별 아이콘/색상/설명 텍스트 (사전승인 vs 사후보고 등) - create/edit 동일 적용
This commit is contained in:
@@ -14,22 +14,37 @@
|
||||
<div class="bg-white rounded-lg shadow-sm p-6">
|
||||
<h2 class="text-lg font-semibold text-gray-800 mb-4">문서 내용</h2>
|
||||
|
||||
{{-- 양식 선택 --}}
|
||||
{{-- 양식 선택 + 설명 카드 --}}
|
||||
<div class="mb-4">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">양식 <span class="text-red-500">*</span></label>
|
||||
<div class="flex items-center gap-2">
|
||||
<select id="form_id" class="flex-1 px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
@foreach($forms as $form)
|
||||
<option value="{{ $form->id }}">{{ $form->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<button type="button" id="expense-load-btn" style="display: none;" onclick="openExpenseLoadModal()"
|
||||
class="shrink-0 px-3 py-2 bg-amber-50 text-amber-700 hover:bg-amber-100 border border-amber-200 rounded-lg text-sm font-medium transition inline-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="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"/>
|
||||
</svg>
|
||||
불러오기
|
||||
</button>
|
||||
<div class="flex gap-3" style="align-items: flex-start;">
|
||||
<div style="width: 30%; min-width: 180px;" class="shrink-0">
|
||||
<div class="flex items-center gap-2">
|
||||
<select id="form_id" class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
@foreach($forms as $form)
|
||||
<option value="{{ $form->id }}">{{ $form->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<button type="button" id="expense-load-btn" style="display: none;" onclick="openExpenseLoadModal()"
|
||||
class="mt-2 w-full px-3 py-2 bg-amber-50 text-amber-700 hover:bg-amber-100 border border-amber-200 rounded-lg text-sm font-medium transition inline-flex items-center justify-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="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"/>
|
||||
</svg>
|
||||
불러오기
|
||||
</button>
|
||||
</div>
|
||||
<div id="form-description-card" style="flex: 1; display: none;">
|
||||
<div class="rounded-lg border p-3 text-sm transition-all" id="form-desc-inner">
|
||||
<div class="flex items-start gap-2">
|
||||
<div id="form-desc-icon" class="shrink-0 mt-0.5"></div>
|
||||
<div>
|
||||
<div id="form-desc-title" class="font-semibold text-sm mb-1"></div>
|
||||
<div id="form-desc-text" class="text-xs leading-relaxed"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -242,6 +257,73 @@ class="p-1 text-gray-400 hover:text-gray-600 transition">
|
||||
const linesData = @json($lines);
|
||||
let isExpenseForm = false;
|
||||
let isPurchaseRequestForm = false;
|
||||
|
||||
const formDescriptions = {
|
||||
expense: {
|
||||
title: '지출결의서',
|
||||
icon: '💰',
|
||||
color: 'border-amber-200 bg-amber-50',
|
||||
titleColor: 'text-amber-800',
|
||||
textColor: 'text-amber-700',
|
||||
text: '이미 발생한 지출에 대해 사후 보고하는 문서입니다. 법인카드 사용 내역, 계좌이체 등 실제 지출이 완료된 건에 대해 증빙자료와 함께 결재를 요청합니다.',
|
||||
},
|
||||
pr_expense: {
|
||||
title: '지출품의서',
|
||||
icon: '📋',
|
||||
color: 'border-orange-200 bg-orange-50',
|
||||
titleColor: 'text-orange-800',
|
||||
textColor: 'text-orange-700',
|
||||
text: '지출이 발생하기 전 사전 승인을 받는 문서입니다. 예산 범위 내에서 지출 항목과 금액을 기재하여 사전에 승락을 받습니다.',
|
||||
},
|
||||
pr_contract: {
|
||||
title: '계약체결품의서',
|
||||
icon: '📝',
|
||||
color: 'border-purple-200 bg-purple-50',
|
||||
titleColor: 'text-purple-800',
|
||||
textColor: 'text-purple-700',
|
||||
text: '외부 업체와의 계약 체결 전 승인을 받는 문서입니다. 계약 상대방, 계약 내용, 기간, 금액, 주요 조건 등을 명시하여 계약 진행에 대한 사전 승락을 받습니다.',
|
||||
},
|
||||
pr_purchase: {
|
||||
title: '구매품의서',
|
||||
icon: '🛒',
|
||||
color: 'border-blue-200 bg-blue-50',
|
||||
titleColor: 'text-blue-800',
|
||||
textColor: 'text-blue-700',
|
||||
text: '물품 구매 전 사전 승인을 받는 문서입니다. 구매할 품목, 수량, 단가, 납품업체 등을 기재하여 구매 진행에 대한 사전 승락을 받습니다.',
|
||||
},
|
||||
pr_trip: {
|
||||
title: '출장품의서',
|
||||
icon: '✈️',
|
||||
color: 'border-green-200 bg-green-50',
|
||||
titleColor: 'text-green-800',
|
||||
textColor: 'text-green-700',
|
||||
text: '출장 전 계획 승인을 받는 문서입니다. 출장지, 기간, 업무 내용, 예상 경비(교통비·숙박비·식비 등)를 기재하여 출장 진행에 대한 사전 승락을 받습니다.',
|
||||
},
|
||||
pr_settlement: {
|
||||
title: '비용정산품의서',
|
||||
icon: '🧾',
|
||||
color: 'border-teal-200 bg-teal-50',
|
||||
titleColor: 'text-teal-800',
|
||||
textColor: 'text-teal-700',
|
||||
text: '업무 수행 중 발생한 비용의 정산 승인을 받는 문서입니다. 사용일자별 항목과 금액을 기재하고, 법인카드 사용 또는 개인 선지출 여부를 명시합니다.',
|
||||
},
|
||||
};
|
||||
|
||||
function updateFormDescription(formId) {
|
||||
const code = formCodes[formId];
|
||||
const desc = formDescriptions[code];
|
||||
const card = document.getElementById('form-description-card');
|
||||
if (!desc) { card.style.display = 'none'; return; }
|
||||
|
||||
const inner = document.getElementById('form-desc-inner');
|
||||
inner.className = 'rounded-lg border p-3 text-sm transition-all ' + desc.color;
|
||||
document.getElementById('form-desc-icon').innerHTML = '<span style="font-size: 1.25rem;">' + desc.icon + '</span>';
|
||||
document.getElementById('form-desc-title').className = 'font-semibold text-sm mb-1 ' + desc.titleColor;
|
||||
document.getElementById('form-desc-title').textContent = desc.title;
|
||||
document.getElementById('form-desc-text').className = 'text-xs leading-relaxed ' + desc.textColor;
|
||||
document.getElementById('form-desc-text').textContent = desc.text;
|
||||
card.style.display = '';
|
||||
}
|
||||
let isLeaveForm = false;
|
||||
let isCertForm = false;
|
||||
let isCareerCertForm = false;
|
||||
@@ -620,12 +702,15 @@ function applyBodyTemplate(formId) {
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
setTimeout(updateApprovalLineSummary, 200);
|
||||
|
||||
// 초기 양식에 대한 폼 모드 전환
|
||||
switchFormMode(document.getElementById('form_id').value);
|
||||
// 초기 양식에 대한 폼 모드 전환 + 설명 카드
|
||||
const initialFormId = document.getElementById('form_id').value;
|
||||
switchFormMode(initialFormId);
|
||||
updateFormDescription(initialFormId);
|
||||
|
||||
// 양식 변경 시 본문 템플릿 자동 채움
|
||||
// 양식 변경 시 본문 템플릿 자동 채움 + 설명 카드 업데이트
|
||||
document.getElementById('form_id').addEventListener('change', function() {
|
||||
applyBodyTemplate(this.value);
|
||||
updateFormDescription(this.value);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -63,11 +63,26 @@
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">양식 <span class="text-red-500">*</span></label>
|
||||
<select id="form_id" class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
@foreach($forms as $form)
|
||||
<option value="{{ $form->id }}" {{ $approval->form_id == $form->id ? 'selected' : '' }}>{{ $form->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<div class="flex gap-3" style="align-items: flex-start;">
|
||||
<div style="width: 30%; min-width: 180px;" class="shrink-0">
|
||||
<select id="form_id" class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
@foreach($forms as $form)
|
||||
<option value="{{ $form->id }}" {{ $approval->form_id == $form->id ? 'selected' : '' }}>{{ $form->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div id="form-description-card" style="flex: 1; display: none;">
|
||||
<div class="rounded-lg border p-3 text-sm transition-all" id="form-desc-inner">
|
||||
<div class="flex items-start gap-2">
|
||||
<div id="form-desc-icon" class="shrink-0 mt-0.5"></div>
|
||||
<div>
|
||||
<div id="form-desc-title" class="font-semibold text-sm mb-1"></div>
|
||||
<div id="form-desc-text" class="text-xs leading-relaxed"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
@@ -268,6 +283,55 @@ class="px-6 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg text-sm fon
|
||||
let isPurchaseRequestForm = false;
|
||||
let isCertForm = false;
|
||||
|
||||
const formDescriptions = {
|
||||
expense: {
|
||||
title: '지출결의서', icon: '💰',
|
||||
color: 'border-amber-200 bg-amber-50', titleColor: 'text-amber-800', textColor: 'text-amber-700',
|
||||
text: '이미 발생한 지출에 대해 사후 보고하는 문서입니다. 법인카드 사용 내역, 계좌이체 등 실제 지출이 완료된 건에 대해 증빙자료와 함께 결재를 요청합니다.',
|
||||
},
|
||||
pr_expense: {
|
||||
title: '지출품의서', icon: '📋',
|
||||
color: 'border-orange-200 bg-orange-50', titleColor: 'text-orange-800', textColor: 'text-orange-700',
|
||||
text: '지출이 발생하기 전 사전 승인을 받는 문서입니다. 예산 범위 내에서 지출 항목과 금액을 기재하여 사전에 승락을 받습니다.',
|
||||
},
|
||||
pr_contract: {
|
||||
title: '계약체결품의서', icon: '📝',
|
||||
color: 'border-purple-200 bg-purple-50', titleColor: 'text-purple-800', textColor: 'text-purple-700',
|
||||
text: '외부 업체와의 계약 체결 전 승인을 받는 문서입니다. 계약 상대방, 계약 내용, 기간, 금액, 주요 조건 등을 명시하여 계약 진행에 대한 사전 승락을 받습니다.',
|
||||
},
|
||||
pr_purchase: {
|
||||
title: '구매품의서', icon: '🛒',
|
||||
color: 'border-blue-200 bg-blue-50', titleColor: 'text-blue-800', textColor: 'text-blue-700',
|
||||
text: '물품 구매 전 사전 승인을 받는 문서입니다. 구매할 품목, 수량, 단가, 납품업체 등을 기재하여 구매 진행에 대한 사전 승락을 받습니다.',
|
||||
},
|
||||
pr_trip: {
|
||||
title: '출장품의서', icon: '✈️',
|
||||
color: 'border-green-200 bg-green-50', titleColor: 'text-green-800', textColor: 'text-green-700',
|
||||
text: '출장 전 계획 승인을 받는 문서입니다. 출장지, 기간, 업무 내용, 예상 경비(교통비·숙박비·식비 등)를 기재하여 출장 진행에 대한 사전 승락을 받습니다.',
|
||||
},
|
||||
pr_settlement: {
|
||||
title: '비용정산품의서', icon: '🧾',
|
||||
color: 'border-teal-200 bg-teal-50', titleColor: 'text-teal-800', textColor: 'text-teal-700',
|
||||
text: '업무 수행 중 발생한 비용의 정산 승인을 받는 문서입니다. 사용일자별 항목과 금액을 기재하고, 법인카드 사용 또는 개인 선지출 여부를 명시합니다.',
|
||||
},
|
||||
};
|
||||
|
||||
function updateFormDescription(formId) {
|
||||
const code = formCodes[formId];
|
||||
const desc = formDescriptions[code];
|
||||
const card = document.getElementById('form-description-card');
|
||||
if (!desc) { card.style.display = 'none'; return; }
|
||||
|
||||
const inner = document.getElementById('form-desc-inner');
|
||||
inner.className = 'rounded-lg border p-3 text-sm transition-all ' + desc.color;
|
||||
document.getElementById('form-desc-icon').innerHTML = '<span style="font-size: 1.25rem;">' + desc.icon + '</span>';
|
||||
document.getElementById('form-desc-title').className = 'font-semibold text-sm mb-1 ' + desc.titleColor;
|
||||
document.getElementById('form-desc-title').textContent = desc.title;
|
||||
document.getElementById('form-desc-text').className = 'text-xs leading-relaxed ' + desc.textColor;
|
||||
document.getElementById('form-desc-text').textContent = desc.text;
|
||||
card.style.display = '';
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
if (!str) return '';
|
||||
const div = document.createElement('div');
|
||||
@@ -516,8 +580,10 @@ function applyBodyTemplate(formId) {
|
||||
|
||||
// 기존 HTML body 자동 감지 → 편집기 자동 활성화 + 결재선 요약 초기화
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// 초기 양식에 대한 폼 모드 전환
|
||||
switchFormMode(document.getElementById('form_id').value);
|
||||
// 초기 양식에 대한 폼 모드 전환 + 설명 카드
|
||||
const initialFormId = document.getElementById('form_id').value;
|
||||
switchFormMode(initialFormId);
|
||||
updateFormDescription(initialFormId);
|
||||
|
||||
// 재직증명서 기존 데이터 복원
|
||||
if (isCertForm) {
|
||||
@@ -566,9 +632,10 @@ function applyBodyTemplate(formId) {
|
||||
// Alpine 초기화 후 기존 결재선 요약 표시
|
||||
setTimeout(updateApprovalLineSummary, 200);
|
||||
|
||||
// 양식 변경 시 본문 템플릿 자동 채움
|
||||
// 양식 변경 시 본문 템플릿 자동 채움 + 설명 카드 업데이트
|
||||
document.getElementById('form_id').addEventListener('change', function() {
|
||||
applyBodyTemplate(this.value);
|
||||
updateFormDescription(this.value);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user