From 05321c8839ddad117d4b3882e43c926d6b9e3bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Fri, 6 Mar 2026 23:00:22 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20[approval]=20=EC=9D=B4=EC=82=AC?= =?UTF-8?q?=ED=9A=8C=EC=9D=98=EC=82=AC=EB=A1=9D=20=EC=96=91=EC=8B=9D=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 이사회의사록 전용 폼(_board-minutes-form.blade.php) 생성 - 이사회의사록 읽기전용 뷰(_board-minutes-show.blade.php) 생성 - Alpine.js 의안/서명란 동적 추가/삭제 기능 - 테넌트 정보에서 회사명/대표자 자동 채움 - create/edit/show 페이지 통합 - 미리보기/인쇄 기능 포함 - 인사/근태 카테고리에 배치 --- resources/views/approvals/create.blade.php | 179 ++++++++++++- resources/views/approvals/edit.blade.php | 146 ++++++++++- .../partials/_board-minutes-form.blade.php | 235 ++++++++++++++++++ .../partials/_board-minutes-show.blade.php | 189 ++++++++++++++ resources/views/approvals/show.blade.php | 2 + 5 files changed, 746 insertions(+), 5 deletions(-) create mode 100644 resources/views/approvals/partials/_board-minutes-form.blade.php create mode 100644 resources/views/approvals/partials/_board-minutes-show.blade.php diff --git a/resources/views/approvals/create.blade.php b/resources/views/approvals/create.blade.php index 34d720a4..cdbe209a 100644 --- a/resources/views/approvals/create.blade.php +++ b/resources/views/approvals/create.blade.php @@ -140,6 +140,11 @@ class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline- 'tenantInfo' => $tenantInfo ?? [], ]) + {{-- 이사회의사록 전용 폼 --}} + @include('approvals.partials._board-minutes-form', [ + 'tenantInfo' => $tenantInfo ?? [], + ]) + {{-- 지출결의서 전용 폼 --}} @include('approvals.partials._expense-form', [ 'initialData' => [], @@ -324,6 +329,11 @@ class="p-1 text-gray-400 hover:text-gray-600 transition"> color: 'border-amber-200 bg-amber-50', titleColor: 'text-amber-800', textColor: 'text-amber-600', text: '법인의 업무를 대리인에게 위임하는 문서입니다. 위임인(회사), 수임인(대리인), 위임사항, 위임기간을 기재하며, 승인 후 위임장으로 사용할 수 있습니다.', }, + board_minutes: { + title: '이사회의사록', icon: '📋', + color: 'border-slate-300 bg-slate-50', titleColor: 'text-slate-800', textColor: 'text-slate-600', + text: '이사회 개최 내용을 기록하는 공식 문서입니다. 일시, 장소, 출석현황, 의안, 의사경과, 기명날인 등을 기재하며, 법적 효력을 갖는 회의록입니다.', + }, pr_expense: { title: '지출품의서', icon: '📋', color: 'border-orange-200 bg-orange-50', titleColor: 'text-orange-800', textColor: 'text-orange-700', @@ -354,7 +364,7 @@ class="p-1 text-gray-400 hover:text-gray-600 transition"> // 2단계 분류 정의 (코드 → 카테고리) const formCategoryMap = { BUSINESS_DRAFT: '일반', - leave: '인사/근태', attendance_request: '인사/근태', resignation: '인사/근태', reason_report: '인사/근태', delegation: '인사/근태', + leave: '인사/근태', attendance_request: '인사/근태', resignation: '인사/근태', reason_report: '인사/근태', delegation: '인사/근태', board_minutes: '인사/근태', employment_cert: '증명서', career_cert: '증명서', appointment_cert: '증명서', seal_usage: '증명서', pr_expense: '품의', pr_contract: '품의', pr_purchase: '품의', pr_trip: '품의', pr_settlement: '품의', expense: '재무', @@ -443,6 +453,7 @@ function updateFormDescription(formId) { let isResignationForm = false; let isSealUsageForm = false; let isDelegationForm = false; +let isBoardMinutesForm = false; // 양식코드별 표시할 유형 목록 const leaveTypesByFormCode = { @@ -693,6 +704,7 @@ function switchFormMode(formId) { const resignationContainer = document.getElementById('resignation-form-container'); const sealUsageContainer = document.getElementById('seal-usage-form-container'); const delegationContainer = document.getElementById('delegation-form-container'); + const boardMinutesContainer = document.getElementById('board-minutes-form-container'); const bodyArea = document.getElementById('body-area'); const expenseLoadBtn = document.getElementById('expense-load-btn'); @@ -708,6 +720,7 @@ function switchFormMode(formId) { resignationContainer.style.display = 'none'; sealUsageContainer.style.display = 'none'; delegationContainer.style.display = 'none'; + boardMinutesContainer.style.display = 'none'; expenseLoadBtn.style.display = 'none'; bodyArea.style.display = 'none'; isExpenseForm = false; @@ -719,6 +732,7 @@ function switchFormMode(formId) { isResignationForm = false; isSealUsageForm = false; isDelegationForm = false; + isBoardMinutesForm = false; if (code === 'expense') { isExpenseForm = true; @@ -784,6 +798,9 @@ function switchFormMode(formId) { } else if (code === 'delegation') { isDelegationForm = true; delegationContainer.style.display = ''; + } else if (code === 'board_minutes') { + isBoardMinutesForm = true; + boardMinutesContainer.style.display = ''; } else { bodyArea.style.display = ''; } @@ -794,7 +811,7 @@ function applyBodyTemplate(formId) { switchFormMode(formId); // 전용 폼이면 제목을 양식명으로 설정하고 body template 적용 건너뜀 - if (isExpenseForm || isPurchaseRequestForm || isLeaveForm || isCertForm || isCareerCertForm || isAppointmentCertForm || isResignationForm || isSealUsageForm || isDelegationForm) { + if (isExpenseForm || isPurchaseRequestForm || isLeaveForm || isCertForm || isCareerCertForm || isAppointmentCertForm || isResignationForm || isSealUsageForm || isDelegationForm || isBoardMinutesForm) { const titleEl = document.getElementById('title'); const formSelect = document.getElementById('form_id'); titleEl.value = formSelect.options[formSelect.selectedIndex].text; @@ -1011,6 +1028,52 @@ function applyBodyTemplate(formId) { company_address: document.getElementById('su-company-address').value, }; formBody = null; + } else if (isBoardMinutesForm) { + const bmDatetime = document.getElementById('bm-meeting-datetime').value; + if (!bmDatetime) { + showToast('일시를 입력해주세요.', 'warning'); + return; + } + const bmPlace = document.getElementById('bm-meeting-place').value.trim(); + if (!bmPlace) { + showToast('장소를 입력해주세요.', 'warning'); + return; + } + const bmChairman = document.getElementById('bm-chairman-name').value.trim(); + if (!bmChairman) { + showToast('의장(대표이사) 성명을 입력해주세요.', 'warning'); + return; + } + + const bmContainer = document.getElementById('board-minutes-form-container'); + const bmAlpine = bmContainer._x_dataStack?.[0]; + const bmAgendas = bmAlpine ? bmAlpine.agendas : []; + const bmSigners = bmAlpine ? bmAlpine.signers : []; + + if (bmAgendas.length === 0 || !bmAgendas[0].title.trim()) { + showToast('의안을 1건 이상 입력해주세요.', 'warning'); + return; + } + + formContent = { + meeting_datetime: bmDatetime, + meeting_place: bmPlace, + total_directors: parseInt(document.getElementById('bm-total-directors').value) || 0, + present_directors: parseInt(document.getElementById('bm-present-directors').value) || 0, + total_auditors: parseInt(document.getElementById('bm-total-auditors').value) || 0, + present_auditors: parseInt(document.getElementById('bm-present-auditors').value) || 0, + chairman_name: bmChairman, + agendas: bmAgendas.filter(a => a.title.trim()).map(a => ({ no: a.no, title: a.title.trim(), result: a.result.trim() })), + proceedings: document.getElementById('bm-proceedings').value.trim(), + closing_time: document.getElementById('bm-closing-time').value, + signers: bmSigners.filter(s => s.name.trim()).map(s => ({ role: s.role.trim(), name: s.name.trim() })), + company_name: document.getElementById('bm-company-name').value, + business_num: document.getElementById('bm-business-num').value, + ceo_name: document.getElementById('bm-ceo-name').value, + company_address: document.getElementById('bm-company-address').value, + meeting_date: bmDatetime.split('T')[0], + }; + formBody = null; } else if (isDelegationForm) { const dlAgentName = document.getElementById('dl-agent-name').value.trim(); if (!dlAgentName) { @@ -2053,6 +2116,118 @@ function closeDelegationPreview() { document.body.style.overflow = ''; } +// ── 이사회의사록 미리보기 ── +function buildBoardMinutesPreviewHtml(data) { + const dt = data.meeting_datetime || ''; + let dateStr = ''; + if (dt) { + const d = new Date(dt); + const year = d.getFullYear(); + const month = d.getMonth() + 1; + const day = d.getDate(); + const hour = d.getHours(); + const min = String(d.getMinutes()).padStart(2, '0'); + const ampm = hour < 12 ? '오전' : '오후'; + const h12 = hour % 12 || 12; + dateStr = `${year}년 ${month}월 ${day}일 ${ampm} ${h12}시 ${min}분`; + } + + const agendas = data.agendas || []; + let agendasHtml = agendas.map(a => + `
+
[제${a.no}호 의안: ${a.title}]
+ ${a.result ? `
* ${a.result}
` : ''} +
` + ).join(''); + + const signers = data.signers || []; + let signersHtml = signers.map(s => + `
+ ${s.role}: + ${s.name} + (인) ㊞ +
` + ).join(''); + + return ` +
+

이 사 회 의 사 록

+
+
+

1. 일 시: ${dateStr}

+

2. 장 소: ${data.meeting_place || ''}

+

3. 출석이사 및 감사:

+
+

* 이사 총수: ${data.total_directors || 0}명 (출석이사: ${data.present_directors || 0}명)

+

* 감사 총수: ${data.total_auditors || 0}명 (출석감사: ${data.present_auditors || 0}명)

+
+

4. 의 안:

+
+ ${agendas.map(a => `

${a.title}

`).join('')} +
+

5. 의사 경과 및 결과:

+
+ ${data.proceedings ? `

${data.proceedings}

` : `

의장(대표이사 ${data.chairman_name || ''})은 위와 같이 법정 수에 달하는 이사가 출석하였으므로 본 이사회가 적법하게 성립되었음을 선언하고, 다음의 의안을 상정하여 승인을 구하다.

`} + ${agendasHtml} +
+

6. 폐 회:

+
+

의장은 이상으로써 의안 전부의 심의를 종료하였으므로 폐회를 선언하다.

+ ${data.closing_time ? `

(폐회 시각: ${data.closing_time})

` : ''} +
+
+
+

위 의사의 경과와 결과를 명확히 하기 위하여 이 의사록을 작성하고,

+

의장과 출석한 이사 및 감사가 아래와 같이 기명날인한다.

+

${data.meeting_date || ''}

+

${data.company_name || ''}

+
+
+ ${signersHtml} +
+ `; +} + +function openBoardMinutesPreview() { + const bmContainer = document.getElementById('board-minutes-form-container'); + const bmAlpine = bmContainer._x_dataStack?.[0]; + const data = { + meeting_datetime: document.getElementById('bm-meeting-datetime').value, + meeting_place: document.getElementById('bm-meeting-place').value, + total_directors: parseInt(document.getElementById('bm-total-directors').value) || 0, + present_directors: parseInt(document.getElementById('bm-present-directors').value) || 0, + total_auditors: parseInt(document.getElementById('bm-total-auditors').value) || 0, + present_auditors: parseInt(document.getElementById('bm-present-auditors').value) || 0, + chairman_name: document.getElementById('bm-chairman-name').value, + agendas: bmAlpine ? bmAlpine.agendas.filter(a => a.title.trim()) : [], + proceedings: document.getElementById('bm-proceedings').value, + closing_time: document.getElementById('bm-closing-time').value, + signers: bmAlpine ? bmAlpine.signers.filter(s => s.name.trim()) : [], + company_name: document.getElementById('bm-company-name').value, + meeting_date: (document.getElementById('bm-meeting-datetime').value || '').split('T')[0], + }; + document.getElementById('board-minutes-preview-content').innerHTML = buildBoardMinutesPreviewHtml(data); + document.getElementById('board-minutes-preview-modal').style.display = ''; + document.body.style.overflow = 'hidden'; +} + +function closeBoardMinutesPreview() { + document.getElementById('board-minutes-preview-modal').style.display = 'none'; + document.body.style.overflow = ''; +} + +function printBoardMinutesPreview() { + const content = document.getElementById('board-minutes-preview-content').innerHTML; + const win = window.open('', '_blank'); + win.document.write('이사회의사록'); + win.document.write(''); + win.document.write(''); + win.document.write(content); + win.document.write(''); + win.document.close(); + win.print(); +} + function printDelegationPreview() { const content = document.getElementById('delegation-preview-content').innerHTML; const win = window.open('', '_blank'); diff --git a/resources/views/approvals/edit.blade.php b/resources/views/approvals/edit.blade.php index afa7e8d4..d3434a92 100644 --- a/resources/views/approvals/edit.blade.php +++ b/resources/views/approvals/edit.blade.php @@ -158,6 +158,11 @@ class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline- 'tenantInfo' => $tenantInfo ?? [], ]) + {{-- 이사회의사록 전용 폼 --}} + @include('approvals.partials._board-minutes-form', [ + 'tenantInfo' => $tenantInfo ?? [], + ]) + {{-- 지출결의서 전용 폼 --}} @php $existingFiles = []; @@ -296,6 +301,7 @@ class="px-6 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg text-sm fon let isCertForm = false; let isSealUsageForm = false; let isDelegationForm = false; +let isBoardMinutesForm = false; const formDescriptions = { BUSINESS_DRAFT: { @@ -353,6 +359,11 @@ class="px-6 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg text-sm fon color: 'border-amber-200 bg-amber-50', titleColor: 'text-amber-800', textColor: 'text-amber-600', text: '법인의 업무를 대리인에게 위임하는 문서입니다. 위임인(회사), 수임인(대리인), 위임사항, 위임기간을 기재하며, 승인 후 위임장으로 사용할 수 있습니다.', }, + board_minutes: { + title: '이사회의사록', icon: '📋', + color: 'border-slate-300 bg-slate-50', titleColor: 'text-slate-800', textColor: 'text-slate-600', + text: '이사회 개최 내용을 기록하는 공식 문서입니다. 일시, 장소, 출석현황, 의안, 의사경과, 기명날인 등을 기재하며, 법적 효력을 갖는 회의록입니다.', + }, pr_expense: { title: '지출품의서', icon: '📋', color: 'border-orange-200 bg-orange-50', titleColor: 'text-orange-800', textColor: 'text-orange-700', @@ -383,7 +394,7 @@ class="px-6 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg text-sm fon // 2단계 분류 정의 const formCategoryMap = { BUSINESS_DRAFT: '일반', - leave: '인사/근태', attendance_request: '인사/근태', resignation: '인사/근태', reason_report: '인사/근태', delegation: '인사/근태', + leave: '인사/근태', attendance_request: '인사/근태', resignation: '인사/근태', reason_report: '인사/근태', delegation: '인사/근태', board_minutes: '인사/근태', employment_cert: '증명서', career_cert: '증명서', appointment_cert: '증명서', seal_usage: '증명서', pr_expense: '품의', pr_contract: '품의', pr_purchase: '품의', pr_trip: '품의', pr_settlement: '품의', expense: '재무', @@ -625,6 +636,7 @@ function switchFormMode(formId) { const certContainer = document.getElementById('cert-form-container'); const sealUsageContainer = document.getElementById('seal-usage-form-container'); const delegationContainer = document.getElementById('delegation-form-container'); + const boardMinutesContainer = document.getElementById('board-minutes-form-container'); const bodyArea = document.getElementById('body-area'); expenseContainer.style.display = 'none'; @@ -632,12 +644,14 @@ function switchFormMode(formId) { certContainer.style.display = 'none'; sealUsageContainer.style.display = 'none'; delegationContainer.style.display = 'none'; + boardMinutesContainer.style.display = 'none'; bodyArea.style.display = 'none'; isExpenseForm = false; isPurchaseRequestForm = false; isCertForm = false; isSealUsageForm = false; isDelegationForm = false; + isBoardMinutesForm = false; if (code === 'expense') { isExpenseForm = true; @@ -660,6 +674,9 @@ function switchFormMode(formId) { } else if (code === 'delegation') { isDelegationForm = true; delegationContainer.style.display = ''; + } else if (code === 'board_minutes') { + isBoardMinutesForm = true; + boardMinutesContainer.style.display = ''; } else { bodyArea.style.display = ''; } @@ -670,7 +687,7 @@ function applyBodyTemplate(formId) { switchFormMode(formId); // 전용 폼이면 제목만 자동 설정하고 body template 적용 건너뜀 - if (isExpenseForm || isPurchaseRequestForm || isCertForm || isSealUsageForm || isDelegationForm) { + if (isExpenseForm || isPurchaseRequestForm || isCertForm || isSealUsageForm || isDelegationForm || isBoardMinutesForm) { const titleEl = document.getElementById('title'); if (!titleEl.value.trim()) { const formSelect = document.getElementById('form_id'); @@ -792,8 +809,30 @@ function applyBodyTemplate(formId) { } } + // 이사회의사록 기존 데이터 복원 + if (isBoardMinutesForm) { + const bmContent = @json($approval->content ?? []); + if (bmContent.meeting_datetime) { + document.getElementById('bm-meeting-datetime').value = bmContent.meeting_datetime || ''; + document.getElementById('bm-meeting-place').value = bmContent.meeting_place || ''; + document.getElementById('bm-total-directors').value = bmContent.total_directors || 0; + document.getElementById('bm-present-directors').value = bmContent.present_directors || 0; + document.getElementById('bm-total-auditors').value = bmContent.total_auditors || 0; + document.getElementById('bm-present-auditors').value = bmContent.present_auditors || 0; + document.getElementById('bm-chairman-name').value = bmContent.chairman_name || ''; + document.getElementById('bm-proceedings').value = bmContent.proceedings || ''; + document.getElementById('bm-closing-time').value = bmContent.closing_time || ''; + + const bmAlpine = document.getElementById('board-minutes-form-container')._x_dataStack?.[0]; + if (bmAlpine) { + if (bmContent.agendas && bmContent.agendas.length > 0) bmAlpine.agendas = bmContent.agendas; + if (bmContent.signers && bmContent.signers.length > 0) bmAlpine.signers = bmContent.signers; + } + } + } + // 전용 폼이 아닌 경우에만 Quill 편집기 자동 활성화 - if (!isExpenseForm && !isPurchaseRequestForm && !isCertForm && !isSealUsageForm && !isDelegationForm) { + if (!isExpenseForm && !isPurchaseRequestForm && !isCertForm && !isSealUsageForm && !isDelegationForm && !isBoardMinutesForm) { const existingBody = document.getElementById('body').value; if (/<[a-z][\s\S]*>/i.test(existingBody)) { document.getElementById('useEditor').checked = true; @@ -890,6 +929,38 @@ function applyBodyTemplate(formId) { company_address: document.getElementById('su-company-address').value, }; formBody = null; + } else if (isBoardMinutesForm) { + const bmDatetime = document.getElementById('bm-meeting-datetime').value; + if (!bmDatetime) { showToast('일시를 입력해주세요.', 'warning'); return; } + const bmPlace = document.getElementById('bm-meeting-place').value.trim(); + if (!bmPlace) { showToast('장소를 입력해주세요.', 'warning'); return; } + const bmChairman = document.getElementById('bm-chairman-name').value.trim(); + if (!bmChairman) { showToast('의장(대표이사) 성명을 입력해주세요.', 'warning'); return; } + + const bmContainer = document.getElementById('board-minutes-form-container'); + const bmAlpine = bmContainer._x_dataStack?.[0]; + const bmAgendas = bmAlpine ? bmAlpine.agendas : []; + const bmSigners = bmAlpine ? bmAlpine.signers : []; + + formContent = { + meeting_datetime: bmDatetime, + meeting_place: bmPlace, + total_directors: parseInt(document.getElementById('bm-total-directors').value) || 0, + present_directors: parseInt(document.getElementById('bm-present-directors').value) || 0, + total_auditors: parseInt(document.getElementById('bm-total-auditors').value) || 0, + present_auditors: parseInt(document.getElementById('bm-present-auditors').value) || 0, + chairman_name: bmChairman, + agendas: bmAgendas.filter(a => a.title.trim()).map(a => ({ no: a.no, title: a.title.trim(), result: a.result.trim() })), + proceedings: document.getElementById('bm-proceedings').value.trim(), + closing_time: document.getElementById('bm-closing-time').value, + signers: bmSigners.filter(s => s.name.trim()).map(s => ({ role: s.role.trim(), name: s.name.trim() })), + company_name: document.getElementById('bm-company-name').value, + business_num: document.getElementById('bm-business-num').value, + ceo_name: document.getElementById('bm-ceo-name').value, + company_address: document.getElementById('bm-company-address').value, + meeting_date: bmDatetime.split('T')[0], + }; + formBody = null; } else if (isDelegationForm) { const dlAgentName = document.getElementById('dl-agent-name').value.trim(); if (!dlAgentName) { @@ -1246,6 +1317,75 @@ function printSealUsagePreview() { win.print(); } +// ── 이사회의사록 미리보기 ── +function buildBoardMinutesPreviewHtml(data) { + const dt = data.meeting_datetime || ''; + let dateStr = ''; + if (dt) { + const d = new Date(dt); + const year = d.getFullYear(); + const month = d.getMonth() + 1; + const day = d.getDate(); + const hour = d.getHours(); + const min = String(d.getMinutes()).padStart(2, '0'); + const ampm = hour < 12 ? '오전' : '오후'; + const h12 = hour % 12 || 12; + dateStr = `${year}년 ${month}월 ${day}일 ${ampm} ${h12}시 ${min}분`; + } + const agendas = data.agendas || []; + let agendasHtml = agendas.map(a => + `
[제${a.no}호 의안: ${a.title}]
${a.result ? `
* ${a.result}
` : ''}
` + ).join(''); + const signers = data.signers || []; + let signersHtml = signers.map(s => + `
${s.role}:${s.name}(인) ㊞
` + ).join(''); + return `

이 사 회 의 사 록

+

1. 일 시: ${dateStr}

2. 장 소: ${data.meeting_place || ''}

+

3. 출석이사 및 감사:

* 이사 총수: ${data.total_directors || 0}명 (출석이사: ${data.present_directors || 0}명)

* 감사 총수: ${data.total_auditors || 0}명 (출석감사: ${data.present_auditors || 0}명)

+

4. 의 안:

${agendas.map(a => `

${a.title}

`).join('')}
+

5. 의사 경과 및 결과:

${data.proceedings ? `

${data.proceedings}

` : `

의장(대표이사 ${data.chairman_name || ''})은 위와 같이 법정 수에 달하는 이사가 출석하였으므로 본 이사회가 적법하게 성립되었음을 선언하고, 다음의 의안을 상정하여 승인을 구하다.

`}${agendasHtml}
+

6. 폐 회:

의장은 이상으로써 의안 전부의 심의를 종료하였으므로 폐회를 선언하다.

${data.closing_time ? `

(폐회 시각: ${data.closing_time})

` : ''}
+

위 의사의 경과와 결과를 명확히 하기 위하여 이 의사록을 작성하고,

의장과 출석한 이사 및 감사가 아래와 같이 기명날인한다.

${data.meeting_date || ''}

${data.company_name || ''}

+
${signersHtml}
`; +} + +function openBoardMinutesPreview() { + const bmContainer = document.getElementById('board-minutes-form-container'); + const bmAlpine = bmContainer._x_dataStack?.[0]; + const data = { + meeting_datetime: document.getElementById('bm-meeting-datetime').value, + meeting_place: document.getElementById('bm-meeting-place').value, + total_directors: parseInt(document.getElementById('bm-total-directors').value) || 0, + present_directors: parseInt(document.getElementById('bm-present-directors').value) || 0, + total_auditors: parseInt(document.getElementById('bm-total-auditors').value) || 0, + present_auditors: parseInt(document.getElementById('bm-present-auditors').value) || 0, + chairman_name: document.getElementById('bm-chairman-name').value, + agendas: bmAlpine ? bmAlpine.agendas.filter(a => a.title.trim()) : [], + proceedings: document.getElementById('bm-proceedings').value, + closing_time: document.getElementById('bm-closing-time').value, + signers: bmAlpine ? bmAlpine.signers.filter(s => s.name.trim()) : [], + company_name: document.getElementById('bm-company-name').value, + meeting_date: (document.getElementById('bm-meeting-datetime').value || '').split('T')[0], + }; + document.getElementById('board-minutes-preview-content').innerHTML = buildBoardMinutesPreviewHtml(data); + document.getElementById('board-minutes-preview-modal').style.display = ''; + document.body.style.overflow = 'hidden'; +} + +function closeBoardMinutesPreview() { + document.getElementById('board-minutes-preview-modal').style.display = 'none'; + document.body.style.overflow = ''; +} + +function printBoardMinutesPreview() { + const content = document.getElementById('board-minutes-preview-content').innerHTML; + const win = window.open('', '_blank'); + win.document.write('이사회의사록' + content + ''); + win.document.close(); + win.print(); +} + // ── 위임장 미리보기 ── function buildDelegationPreviewHtml(data) { return ` diff --git a/resources/views/approvals/partials/_board-minutes-form.blade.php b/resources/views/approvals/partials/_board-minutes-form.blade.php new file mode 100644 index 00000000..4d4e2601 --- /dev/null +++ b/resources/views/approvals/partials/_board-minutes-form.blade.php @@ -0,0 +1,235 @@ +{{-- + 이사회의사록 전용 폼 + Props: + $tenantInfo (array) - 테넌트(회사) 정보 +--}} +@php + $tenantInfo = $tenantInfo ?? []; +@endphp + + + +{{-- 이사회의사록 미리보기 모달 --}} + diff --git a/resources/views/approvals/partials/_board-minutes-show.blade.php b/resources/views/approvals/partials/_board-minutes-show.blade.php new file mode 100644 index 00000000..7e36b944 --- /dev/null +++ b/resources/views/approvals/partials/_board-minutes-show.blade.php @@ -0,0 +1,189 @@ +{{-- + 이사회의사록 읽기전용 렌더링 + Props: + $content (array) - approvals.content JSON +--}} +
+ {{-- 미리보기 버튼 --}} +
+ +
+ + {{-- 1. 일시 및 장소 --}} +
+
+

1. 일시 및 장소

+
+
+
+
+ 일시 +
{{ $content['meeting_datetime'] ?? '-' }}
+
+
+ 장소 +
{{ $content['meeting_place'] ?? '-' }}
+
+
+
+
+ + {{-- 2. 출석이사 및 감사 --}} +
+
+

2. 출석이사 및 감사

+
+
+
+
+ 이사 총수 +
{{ $content['total_directors'] ?? '-' }}명
+
+
+ 출석이사 +
{{ $content['present_directors'] ?? '-' }}명
+
+
+ 감사 총수 +
{{ $content['total_auditors'] ?? '-' }}명
+
+
+ 출석감사 +
{{ $content['present_auditors'] ?? '-' }}명
+
+
+
+
+ + {{-- 3. 의안 --}} +
+
+

3. 의안

+
+
+ @foreach(($content['agendas'] ?? []) as $agenda) +
+ + #{{ $agenda['no'] ?? $loop->iteration }} + +
+
{{ $agenda['title'] ?? '-' }}
+ @if(!empty($agenda['result'])) +
{{ $agenda['result'] }}
+ @endif +
+
+ @endforeach +
+
+ + {{-- 4. 의사 경과 및 결과 --}} +
+
+

4. 의사 경과 및 결과

+
+
+
+ 의장(대표이사) +
{{ $content['chairman_name'] ?? '-' }}
+
+ @if(!empty($content['proceedings'])) +
{{ $content['proceedings'] }}
+ @endif +
+
+ + {{-- 5. 폐회 --}} + @if(!empty($content['closing_time'])) +
+
+

5. 폐회

+
+
+ 폐회 시각 +
{{ $content['closing_time'] }}
+
+
+ @endif + + {{-- 6. 기명날인 --}} + @if(!empty($content['signers'])) +
+
+

6. 기명날인

+
+
+
+ @foreach($content['signers'] as $signer) +
+ {{ $signer['role'] ?? '' }} + {{ $signer['name'] ?? '' }} + (인) +
+ @endforeach +
+
+
+ @endif +
+ +{{-- 미리보기 모달 --}} + + +@push('scripts') + +@endpush diff --git a/resources/views/approvals/show.blade.php b/resources/views/approvals/show.blade.php index 6c5da660..6f62744e 100644 --- a/resources/views/approvals/show.blade.php +++ b/resources/views/approvals/show.blade.php @@ -117,6 +117,8 @@ class="bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded-lg transition @include('approvals.partials._seal-usage-show', ['content' => $approval->content]) @elseif(!empty($approval->content) && $approval->form?->code === 'delegation') @include('approvals.partials._delegation-show', ['content' => $approval->content]) + @elseif(!empty($approval->content) && $approval->form?->code === 'board_minutes') + @include('approvals.partials._board-minutes-show', ['content' => $approval->content]) @elseif($approval->body && preg_match('/<[a-z][\s\S]*>/i', $approval->body))
{!! strip_tags($approval->body, '