feat(MNG):문서 양식 편집 개선 및 이미지 업로드 API 연동

- DocumentTemplateApiController: 이미지 업로드 API 파일저장소 연동
- 양식 편집: 미리보기 모달 개선
- 문서 편집: UI 개선
- 빌드 에셋 업데이트

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-12 00:01:44 +09:00
parent b3891e93b7
commit 1969001338
7 changed files with 240 additions and 103 deletions

View File

@@ -15,6 +15,8 @@
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
class DocumentTemplateApiController extends Controller
@@ -514,7 +516,7 @@ public function duplicate(Request $request, int $id): JsonResponse
}
/**
* 이미지 업로드
* 이미지 업로드 (API 파일 저장소 연동)
*/
public function uploadImage(Request $request): JsonResponse
{
@@ -522,13 +524,71 @@ public function uploadImage(Request $request): JsonResponse
'image' => 'required|image|max:5120', // 5MB
]);
$path = $request->file('image')->store('document-templates', 'public');
$apiBaseUrl = config('services.api.base_url');
$apiKey = config('services.api.key');
return response()->json([
'success' => true,
'path' => $path,
'url' => asset('storage/'.$path),
]);
if (empty($apiBaseUrl)) {
return response()->json([
'success' => false,
'message' => 'API 서버 URL이 설정되지 않았습니다.',
], 500);
}
// API 토큰 교환
$tokenService = new \App\Services\ApiTokenService();
$userId = auth()->id();
$tenantId = session('selected_tenant_id', 1);
$tokenResult = $tokenService->exchangeToken($userId, $tenantId);
if (! $tokenResult['success']) {
Log::error('[DocumentTemplate] API 토큰 교환 실패', [
'error' => $tokenResult['error'] ?? '',
]);
return response()->json([
'success' => false,
'message' => 'API 인증 실패',
], 500);
}
// API 파일 업로드 호출
$image = $request->file('image');
try {
$response = Http::withToken($tokenResult['data']['access_token'])
->withHeaders(['X-API-KEY' => $apiKey])
->attach('file', file_get_contents($image->getRealPath()), $image->getClientOriginalName())
->post("{$apiBaseUrl}/api/v1/files/upload");
if ($response->successful() && $response->json('success')) {
$filePath = $response->json('data.file_path');
return response()->json([
'success' => true,
'path' => $filePath,
'url' => "{$apiBaseUrl}/storage/tenants/{$filePath}",
]);
}
Log::error('[DocumentTemplate] API 파일 업로드 실패', [
'status' => $response->status(),
'body' => $response->json(),
]);
return response()->json([
'success' => false,
'message' => 'API 파일 업로드 실패',
], 500);
} catch (\Exception $e) {
Log::error('[DocumentTemplate] API 파일 업로드 예외', [
'exception' => $e->getMessage(),
]);
return response()->json([
'success' => false,
'message' => '파일 업로드 중 오류가 발생했습니다.',
], 500);
}
}
/**

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -14,4 +14,4 @@
"src": "resources/js/app.js",
"isEntry": true
}
}
}

View File

@@ -977,7 +977,7 @@ class="flex-1 px-3 py-2 border border-gray-300 rounded-lg text-sm font-medium">
${section.image_path ? `
<div class="px-4 py-2 bg-blue-50 flex items-center justify-between">
<div class="flex items-center gap-2">
<img src="/storage/${section.image_path}" alt="섹션 이미지" class="h-16 rounded border border-gray-200">
<img src="${getSectionImageUrl(section.image_path)}" alt="섹션 이미지" class="h-16 rounded border border-gray-200">
<span class="text-xs text-gray-500">이미지 첨부됨</span>
</div>
<button onclick="removeSectionImage('${section.id}')" class="text-red-500 hover:text-red-700 text-xs">
@@ -1339,6 +1339,9 @@ function saveTemplate() {
template_links: templateState.template_links.filter(l => l.source_table)
};
// 디버그: 섹션별 image_path 확인
console.log('[saveTemplate] sections image_path:', data.sections.map(s => ({ id: s.id, title: s.title, image_path: s.image_path })));
const url = templateState.id
? `/api/admin/document-templates/${templateState.id}`
: '/api/admin/document-templates';
@@ -1452,10 +1455,20 @@ function uploadSectionImage(sectionId, input) {
}
function removeSectionImage(sectionId) {
const section = templateState.sections.find(s => s.id ==sectionId);
console.log('[removeSectionImage] called with sectionId:', sectionId);
const section = templateState.sections.find(s => s.id == sectionId);
console.log('[removeSectionImage] found section:', section ? section.id : 'NOT FOUND', 'image_path:', section?.image_path);
if (section) {
section.image_path = null;
renderSections();
try {
renderSections();
showToast('이미지가 삭제되었습니다. 저장 버튼을 눌러 반영하세요.', 'info');
} catch (e) {
console.error('[removeSectionImage] renderSections error:', e);
showToast('이미지 삭제 중 오류가 발생했습니다.', 'error');
}
} else {
console.warn('[removeSectionImage] section not found for id:', sectionId);
}
}
@@ -2157,6 +2170,17 @@ class="w-full px-2 py-1 border border-gray-200 rounded text-xs">`;
}
// ===== 유틸리티 =====
function getSectionImageUrl(imagePath) {
if (!imagePath) return '';
if (imagePath.startsWith('http://') || imagePath.startsWith('https://')) return imagePath;
// 새 API 업로드 형식 (tenant path: 1/temp/2026/02/xxx.jpg)
if (/^\d+\//.test(imagePath)) {
return 'http://api.sam.kr/storage/tenants/' + imagePath;
}
// 레거시 형식 (document-templates/xxx.jpg) - MNG 로컬 스토리지
return '/storage/' + imagePath;
}
function escapeHtml(text) {
if (!text) return '';
const div = document.createElement('div');

View File

@@ -35,6 +35,18 @@ function closePreviewModal() {
if (e.key === 'Escape') closePreviewModal();
});
// 이미지 URL 헬퍼 (API tenant storage vs MNG local storage)
function _previewImageUrl(imagePath) {
if (!imagePath) return '';
if (imagePath.startsWith('http://') || imagePath.startsWith('https://')) return imagePath;
// 새 API 업로드 형식 (tenant path: 1/temp/2026/02/xxx.jpg)
if (/^\d+\//.test(imagePath)) {
return 'http://api.sam.kr/storage/tenants/' + imagePath;
}
// 레거시 형식 (document-templates/xxx.jpg)
return '/storage/' + imagePath;
}
// HTML 이스케이프
function _previewEsc(text) {
if (!text) return '';
@@ -261,7 +273,7 @@ function buildDocumentPreviewHtml(data) {
<div class="mb-3">
<p class="text-sm font-bold mb-1">■ ${_previewEsc(s.title)}</p>
${s.image_path
? `<img src="/storage/${s.image_path}" alt="${_previewEsc(s.title)}" class="max-h-48 mx-auto border rounded">`
? `<img src="${_previewImageUrl(s.image_path)}" alt="${_previewEsc(s.title)}" class="max-h-48 mx-auto border rounded">`
: `<div class="border border-dashed border-gray-300 rounded p-6 text-center text-gray-400 text-xs">이미지 미등록 (편집에서 업로드)</div>`
}
</div>

View File

@@ -159,6 +159,22 @@ class="w-full rounded-lg border-gray-300 text-sm focus:border-blue-500 focus:rin
{{-- 검사 데이터 테이블 --}}
@if($section->items->count() > 0 && $template->columns->count() > 0)
@php
// 정규화 데이터 조회 헬퍼
$getData = function($sectionId, $colId, $rowIdx, $fieldKey) use ($document) {
if (!$document) return '';
return $document->data
->where('section_id', $sectionId)
->where('column_id', $colId)
->where('row_index', $rowIdx)
->where('field_key', $fieldKey)
->first()?->field_value ?? '';
};
// 레거시 fallback
$getLegacy = function($legacyKey) use ($document) {
if (!$document) return '';
return $document->data->where('field_key', $legacyKey)->first()?->field_value ?? '';
};
// 검사방식 코드→한글 매핑
$methodNames = [
'visual' => '육안검사',
@@ -362,19 +378,22 @@ class="px-2 py-2 text-center text-xs font-medium text-gray-600 uppercase border
@if($mType === 'checkbox')
@for($nIdx = 1; $nIdx <= $freqN; $nIdx++)
@php
$fieldKey = "s{$section->id}_r{$rowIndex}_c{$col->id}_n{$nIdx}";
$savedOK = $document?->data->where('field_key', $fieldKey . '_ok')->first()?->field_value ?? '';
$savedNG = $document?->data->where('field_key', $fieldKey . '_ng')->first()?->field_value ?? '';
$savedOK = $getData($section->id, $col->id, $rowIndex, "n{$nIdx}_ok");
if (!$savedOK) $savedOK = $getLegacy("s{$section->id}_r{$rowIndex}_c{$col->id}_n{$nIdx}_ok");
$savedNG = $getData($section->id, $col->id, $rowIndex, "n{$nIdx}_ng");
if (!$savedNG) $savedNG = $getLegacy("s{$section->id}_r{$rowIndex}_c{$col->id}_n{$nIdx}_ng");
@endphp
<td class="px-1 py-1 border border-gray-300 text-center">
<label class="inline-flex items-center gap-0.5 text-xs">
<input type="checkbox" name="section_data[{{ $fieldKey }}_ok]" value="OK" {{ $savedOK === 'OK' ? 'checked' : '' }}
<input type="checkbox" name="section_data[n{{ $nIdx }}_ok]" value="OK" {{ $savedOK === 'OK' ? 'checked' : '' }}
data-section-id="{{ $section->id }}" data-column-id="{{ $col->id }}" data-row-index="{{ $rowIndex }}"
data-field-key="n{{ $nIdx }}_ok"
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500 w-3 h-3">OK
</label><br>
<label class="inline-flex items-center gap-0.5 text-xs">
<input type="checkbox" name="section_data[{{ $fieldKey }}_ng]" value="NG" {{ $savedNG === 'NG' ? 'checked' : '' }}
<input type="checkbox" name="section_data[n{{ $nIdx }}_ng]" value="NG" {{ $savedNG === 'NG' ? 'checked' : '' }}
data-section-id="{{ $section->id }}" data-column-id="{{ $col->id }}" data-row-index="{{ $rowIndex }}"
data-field-key="n{{ $nIdx }}_ng"
class="rounded border-gray-300 text-red-600 focus:ring-red-500 w-3 h-3">NG
</label>
</td>
@@ -385,12 +404,13 @@ class="rounded border-gray-300 text-red-600 focus:ring-red-500 w-3 h-3">NG
@elseif($mType === 'numeric')
@for($nIdx = 1; $nIdx <= $freqN; $nIdx++)
@php
$fieldKey = "s{$section->id}_r{$rowIndex}_c{$col->id}_n{$nIdx}";
$savedVal = $document?->data->where('field_key', $fieldKey)->first()?->field_value ?? '';
$savedVal = $getData($section->id, $col->id, $rowIndex, "n{$nIdx}");
if (!$savedVal) $savedVal = $getLegacy("s{$section->id}_r{$rowIndex}_c{$col->id}_n{$nIdx}");
@endphp
<td class="px-1 py-1 border border-gray-300 text-center">
<input type="number" step="any" name="section_data[{{ $fieldKey }}]" value="{{ $savedVal }}"
<input type="number" step="any" name="section_data[n{{ $nIdx }}]" value="{{ $savedVal }}"
data-section-id="{{ $section->id }}" data-column-id="{{ $col->id }}" data-row-index="{{ $rowIndex }}"
data-field-key="n{{ $nIdx }}"
class="w-full text-center text-sm border-0 focus:ring-1 focus:ring-blue-400 rounded py-1"
placeholder="n{{ $nIdx }}">
</td>
@@ -400,35 +420,38 @@ class="w-full text-center text-sm border-0 focus:ring-1 focus:ring-blue-400 roun
@endif
@elseif($mType === 'single_value')
@php
$fieldKey = "s{$section->id}_r{$rowIndex}_c{$col->id}";
$savedVal = $document?->data->where('field_key', $fieldKey)->first()?->field_value ?? '';
$savedVal = $getData($section->id, $col->id, $rowIndex, 'value');
if (!$savedVal) $savedVal = $getLegacy("s{$section->id}_r{$rowIndex}_c{$col->id}");
@endphp
<td class="px-1 py-1 border border-gray-300 text-center" colspan="{{ $totalMeasCols }}">
<input type="text" name="section_data[{{ $fieldKey }}]" value="{{ $savedVal }}"
<input type="text" name="section_data[value]" value="{{ $savedVal }}"
data-section-id="{{ $section->id }}" data-column-id="{{ $col->id }}" data-row-index="{{ $rowIndex }}"
data-field-key="value"
class="w-full text-center text-sm border-0 focus:ring-1 focus:ring-blue-400 rounded py-1"
placeholder="입력">
</td>
@elseif($mType === 'substitute')
@php
$fieldKey = "s{$section->id}_r{$rowIndex}_c{$col->id}";
$savedVal = $document?->data->where('field_key', $fieldKey)->first()?->field_value ?? '';
$savedVal = $getData($section->id, $col->id, $rowIndex, 'value');
if (!$savedVal) $savedVal = $getLegacy("s{$section->id}_r{$rowIndex}_c{$col->id}");
@endphp
<td class="px-1 py-1 border border-gray-300 text-center text-gray-400 text-xs" colspan="{{ $totalMeasCols }}">
<input type="text" name="section_data[{{ $fieldKey }}]" value="{{ $savedVal }}"
<input type="text" name="section_data[value]" value="{{ $savedVal }}"
data-section-id="{{ $section->id }}" data-column-id="{{ $col->id }}" data-row-index="{{ $rowIndex }}"
data-field-key="value"
class="w-full text-center text-sm border-0 focus:ring-1 focus:ring-blue-400 rounded py-1"
placeholder="(입력)">
</td>
@else
@for($nIdx = 1; $nIdx <= $totalMeasCols; $nIdx++)
@php
$fieldKey = "s{$section->id}_r{$rowIndex}_c{$col->id}_n{$nIdx}";
$savedVal = $document?->data->where('field_key', $fieldKey)->first()?->field_value ?? '';
$savedVal = $getData($section->id, $col->id, $rowIndex, "n{$nIdx}");
if (!$savedVal) $savedVal = $getLegacy("s{$section->id}_r{$rowIndex}_c{$col->id}_n{$nIdx}");
@endphp
<td class="px-1 py-1 border border-gray-300 text-center">
<input type="text" name="section_data[{{ $fieldKey }}]" value="{{ $savedVal }}"
<input type="text" name="section_data[n{{ $nIdx }}]" value="{{ $savedVal }}"
data-section-id="{{ $section->id }}" data-column-id="{{ $col->id }}" data-row-index="{{ $rowIndex }}"
data-field-key="n{{ $nIdx }}"
class="w-full text-center text-sm border-0 focus:ring-1 focus:ring-blue-400 rounded py-1"
placeholder="n{{ $nIdx }}">
</td>
@@ -436,13 +459,14 @@ class="w-full text-center text-sm border-0 focus:ring-1 focus:ring-blue-400 roun
@endif
@elseif($col->column_type === 'select')
@php
$fieldKey = "s{$section->id}_r{$rowIndex}_c{$col->id}";
$savedVal = $document?->data->where('field_key', $fieldKey)->first()?->field_value ?? '';
$savedVal = $getData($section->id, $col->id, $rowIndex, 'value');
if (!$savedVal) $savedVal = $getLegacy("s{$section->id}_r{$rowIndex}_c{$col->id}");
$options = $template->footer_judgement_options ?? ['적합', '부적합'];
@endphp
<td class="px-1 py-1 border border-gray-300 text-center">
<select name="section_data[{{ $fieldKey }}]"
<select name="section_data[value]"
data-section-id="{{ $section->id }}" data-column-id="{{ $col->id }}" data-row-index="{{ $rowIndex }}"
data-field-key="value"
class="w-full text-center text-sm border-0 focus:ring-1 focus:ring-blue-400 rounded py-1">
<option value="">-</option>
@foreach($options as $opt)
@@ -452,12 +476,13 @@ class="w-full text-center text-sm border-0 focus:ring-1 focus:ring-blue-400 roun
</td>
@elseif($col->column_type === 'check')
@php
$fieldKey = "s{$section->id}_r{$rowIndex}_c{$col->id}";
$savedVal = $document?->data->where('field_key', $fieldKey)->first()?->field_value ?? '';
$savedVal = $getData($section->id, $col->id, $rowIndex, 'value');
if (!$savedVal) $savedVal = $getLegacy("s{$section->id}_r{$rowIndex}_c{$col->id}");
@endphp
<td class="px-1 py-1 border border-gray-300 text-center">
<input type="checkbox" name="section_data[{{ $fieldKey }}]" value="OK" {{ $savedVal === 'OK' ? 'checked' : '' }}
<input type="checkbox" name="section_data[value]" value="OK" {{ $savedVal === 'OK' ? 'checked' : '' }}
data-section-id="{{ $section->id }}" data-column-id="{{ $col->id }}" data-row-index="{{ $rowIndex }}"
data-field-key="value"
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500">
</td>
@else
@@ -484,12 +509,13 @@ class="rounded border-gray-300 text-blue-600 focus:ring-blue-500">
<td class="px-2 py-2 border border-gray-300 text-sm text-center">{{ $formatFrequency($item) }}</td>
@elseif($isJudgeCol)
@php
$fieldKey = "s{$section->id}_r{$rowIndex}_c{$col->id}";
$savedVal = $document?->data->where('field_key', $fieldKey)->first()?->field_value ?? '';
$savedVal = $getData($section->id, $col->id, $rowIndex, 'value');
if (!$savedVal) $savedVal = $getLegacy("s{$section->id}_r{$rowIndex}_c{$col->id}");
@endphp
<td class="px-1 py-1 border border-gray-300 text-center">
<input type="checkbox" name="section_data[{{ $fieldKey }}]" value="OK" {{ $savedVal === 'OK' ? 'checked' : '' }}
<input type="checkbox" name="section_data[value]" value="OK" {{ $savedVal === 'OK' ? 'checked' : '' }}
data-section-id="{{ $section->id }}" data-column-id="{{ $col->id }}" data-row-index="{{ $rowIndex }}"
data-field-key="value"
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500">
</td>
@else
@@ -504,12 +530,13 @@ class="rounded border-gray-300 text-blue-600 focus:ring-blue-500">
<td class="px-2 py-2 border border-gray-300 text-sm">{{ $staticValue }}</td>
@else
@php
$fieldKey = "s{$section->id}_r{$rowIndex}_c{$col->id}";
$savedVal = $document?->data->where('field_key', $fieldKey)->first()?->field_value ?? '';
$savedVal = $getData($section->id, $col->id, $rowIndex, 'value');
if (!$savedVal) $savedVal = $getLegacy("s{$section->id}_r{$rowIndex}_c{$col->id}");
@endphp
<td class="px-1 py-1 border border-gray-300 text-center">
<input type="text" name="section_data[{{ $fieldKey }}]" value="{{ $savedVal }}"
<input type="text" name="section_data[value]" value="{{ $savedVal }}"
data-section-id="{{ $section->id }}" data-column-id="{{ $col->id }}" data-row-index="{{ $rowIndex }}"
data-field-key="value"
class="w-full text-center text-sm border-0 focus:ring-1 focus:ring-blue-400 rounded py-1">
</td>
@endif
@@ -534,19 +561,22 @@ class="w-full text-center text-sm border-0 focus:ring-1 focus:ring-blue-400 roun
@if($mType === 'checkbox')
@for($nIdx = 1; $nIdx <= $freqN; $nIdx++)
@php
$fieldKey = "s{$section->id}_r{$rowIndex}_c{$col->id}_n{$nIdx}";
$savedOK = $document?->data->where('field_key', $fieldKey . '_ok')->first()?->field_value ?? '';
$savedNG = $document?->data->where('field_key', $fieldKey . '_ng')->first()?->field_value ?? '';
$savedOK = $getData($section->id, $col->id, $rowIndex, "n{$nIdx}_ok");
if (!$savedOK) $savedOK = $getLegacy("s{$section->id}_r{$rowIndex}_c{$col->id}_n{$nIdx}_ok");
$savedNG = $getData($section->id, $col->id, $rowIndex, "n{$nIdx}_ng");
if (!$savedNG) $savedNG = $getLegacy("s{$section->id}_r{$rowIndex}_c{$col->id}_n{$nIdx}_ng");
@endphp
<td class="px-1 py-1 border border-gray-300 text-center">
<label class="inline-flex items-center gap-0.5 text-xs">
<input type="checkbox" name="section_data[{{ $fieldKey }}_ok]" value="OK" {{ $savedOK === 'OK' ? 'checked' : '' }}
<input type="checkbox" name="section_data[n{{ $nIdx }}_ok]" value="OK" {{ $savedOK === 'OK' ? 'checked' : '' }}
data-section-id="{{ $section->id }}" data-column-id="{{ $col->id }}" data-row-index="{{ $rowIndex }}"
data-field-key="n{{ $nIdx }}_ok"
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500 w-3 h-3">OK
</label><br>
<label class="inline-flex items-center gap-0.5 text-xs">
<input type="checkbox" name="section_data[{{ $fieldKey }}_ng]" value="NG" {{ $savedNG === 'NG' ? 'checked' : '' }}
<input type="checkbox" name="section_data[n{{ $nIdx }}_ng]" value="NG" {{ $savedNG === 'NG' ? 'checked' : '' }}
data-section-id="{{ $section->id }}" data-column-id="{{ $col->id }}" data-row-index="{{ $rowIndex }}"
data-field-key="n{{ $nIdx }}_ng"
class="rounded border-gray-300 text-red-600 focus:ring-red-500 w-3 h-3">NG
</label>
</td>
@@ -557,12 +587,13 @@ class="rounded border-gray-300 text-red-600 focus:ring-red-500 w-3 h-3">NG
@elseif($mType === 'numeric')
@for($nIdx = 1; $nIdx <= $freqN; $nIdx++)
@php
$fieldKey = "s{$section->id}_r{$rowIndex}_c{$col->id}_n{$nIdx}";
$savedVal = $document?->data->where('field_key', $fieldKey)->first()?->field_value ?? '';
$savedVal = $getData($section->id, $col->id, $rowIndex, "n{$nIdx}");
if (!$savedVal) $savedVal = $getLegacy("s{$section->id}_r{$rowIndex}_c{$col->id}_n{$nIdx}");
@endphp
<td class="px-1 py-1 border border-gray-300 text-center">
<input type="number" step="any" name="section_data[{{ $fieldKey }}]" value="{{ $savedVal }}"
<input type="number" step="any" name="section_data[n{{ $nIdx }}]" value="{{ $savedVal }}"
data-section-id="{{ $section->id }}" data-column-id="{{ $col->id }}" data-row-index="{{ $rowIndex }}"
data-field-key="n{{ $nIdx }}"
class="w-full text-center text-sm border-0 focus:ring-1 focus:ring-blue-400 rounded py-1"
placeholder="n{{ $nIdx }}">
</td>
@@ -572,35 +603,38 @@ class="w-full text-center text-sm border-0 focus:ring-1 focus:ring-blue-400 roun
@endif
@elseif($mType === 'single_value')
@php
$fieldKey = "s{$section->id}_r{$rowIndex}_c{$col->id}";
$savedVal = $document?->data->where('field_key', $fieldKey)->first()?->field_value ?? '';
$savedVal = $getData($section->id, $col->id, $rowIndex, 'value');
if (!$savedVal) $savedVal = $getLegacy("s{$section->id}_r{$rowIndex}_c{$col->id}");
@endphp
<td class="px-1 py-1 border border-gray-300 text-center" colspan="{{ $totalMeasCols }}">
<input type="text" name="section_data[{{ $fieldKey }}]" value="{{ $savedVal }}"
<input type="text" name="section_data[value]" value="{{ $savedVal }}"
data-section-id="{{ $section->id }}" data-column-id="{{ $col->id }}" data-row-index="{{ $rowIndex }}"
data-field-key="value"
class="w-full text-center text-sm border-0 focus:ring-1 focus:ring-blue-400 rounded py-1"
placeholder="입력">
</td>
@elseif($mType === 'substitute')
@php
$fieldKey = "s{$section->id}_r{$rowIndex}_c{$col->id}";
$savedVal = $document?->data->where('field_key', $fieldKey)->first()?->field_value ?? '';
$savedVal = $getData($section->id, $col->id, $rowIndex, 'value');
if (!$savedVal) $savedVal = $getLegacy("s{$section->id}_r{$rowIndex}_c{$col->id}");
@endphp
<td class="px-1 py-1 border border-gray-300 text-center text-gray-400 text-xs" colspan="{{ $totalMeasCols }}">
<input type="text" name="section_data[{{ $fieldKey }}]" value="{{ $savedVal }}"
<input type="text" name="section_data[value]" value="{{ $savedVal }}"
data-section-id="{{ $section->id }}" data-column-id="{{ $col->id }}" data-row-index="{{ $rowIndex }}"
data-field-key="value"
class="w-full text-center text-sm border-0 focus:ring-1 focus:ring-blue-400 rounded py-1"
placeholder="(입력)">
</td>
@else
@for($nIdx = 1; $nIdx <= $totalMeasCols; $nIdx++)
@php
$fieldKey = "s{$section->id}_r{$rowIndex}_c{$col->id}_n{$nIdx}";
$savedVal = $document?->data->where('field_key', $fieldKey)->first()?->field_value ?? '';
$savedVal = $getData($section->id, $col->id, $rowIndex, "n{$nIdx}");
if (!$savedVal) $savedVal = $getLegacy("s{$section->id}_r{$rowIndex}_c{$col->id}_n{$nIdx}");
@endphp
<td class="px-1 py-1 border border-gray-300 text-center">
<input type="text" name="section_data[{{ $fieldKey }}]" value="{{ $savedVal }}"
<input type="text" name="section_data[n{{ $nIdx }}]" value="{{ $savedVal }}"
data-section-id="{{ $section->id }}" data-column-id="{{ $col->id }}" data-row-index="{{ $rowIndex }}"
data-field-key="n{{ $nIdx }}"
class="w-full text-center text-sm border-0 focus:ring-1 focus:ring-blue-400 rounded py-1"
placeholder="n{{ $nIdx }}">
</td>
@@ -608,13 +642,14 @@ class="w-full text-center text-sm border-0 focus:ring-1 focus:ring-blue-400 roun
@endif
@elseif($col->column_type === 'select')
@php
$fieldKey = "s{$section->id}_r{$rowIndex}_c{$col->id}";
$savedVal = $document?->data->where('field_key', $fieldKey)->first()?->field_value ?? '';
$savedVal = $getData($section->id, $col->id, $rowIndex, 'value');
if (!$savedVal) $savedVal = $getLegacy("s{$section->id}_r{$rowIndex}_c{$col->id}");
$options = $template->footer_judgement_options ?? ['적합', '부적합'];
@endphp
<td class="px-1 py-1 border border-gray-300 text-center">
<select name="section_data[{{ $fieldKey }}]"
<select name="section_data[value]"
data-section-id="{{ $section->id }}" data-column-id="{{ $col->id }}" data-row-index="{{ $rowIndex }}"
data-field-key="value"
class="w-full text-center text-sm border-0 focus:ring-1 focus:ring-blue-400 rounded py-1">
<option value="">-</option>
@foreach($options as $opt)
@@ -624,12 +659,13 @@ class="w-full text-center text-sm border-0 focus:ring-1 focus:ring-blue-400 roun
</td>
@elseif($col->column_type === 'check')
@php
$fieldKey = "s{$section->id}_r{$rowIndex}_c{$col->id}";
$savedVal = $document?->data->where('field_key', $fieldKey)->first()?->field_value ?? '';
$savedVal = $getData($section->id, $col->id, $rowIndex, 'value');
if (!$savedVal) $savedVal = $getLegacy("s{$section->id}_r{$rowIndex}_c{$col->id}");
@endphp
<td class="px-1 py-1 border border-gray-300 text-center">
<input type="checkbox" name="section_data[{{ $fieldKey }}]" value="OK" {{ $savedVal === 'OK' ? 'checked' : '' }}
<input type="checkbox" name="section_data[value]" value="OK" {{ $savedVal === 'OK' ? 'checked' : '' }}
data-section-id="{{ $section->id }}" data-column-id="{{ $col->id }}" data-row-index="{{ $rowIndex }}"
data-field-key="value"
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500">
</td>
@else
@@ -664,12 +700,13 @@ class="rounded border-gray-300 text-blue-600 focus:ring-blue-500">
<td class="px-2 py-2 border border-gray-300 text-sm text-center">{{ $formatFrequency($item) }}</td>
@elseif($isJudgeCol)
@php
$fieldKey = "s{$section->id}_r{$rowIndex}_c{$col->id}";
$savedVal = $document?->data->where('field_key', $fieldKey)->first()?->field_value ?? '';
$savedVal = $getData($section->id, $col->id, $rowIndex, 'value');
if (!$savedVal) $savedVal = $getLegacy("s{$section->id}_r{$rowIndex}_c{$col->id}");
@endphp
<td class="px-1 py-1 border border-gray-300 text-center">
<input type="checkbox" name="section_data[{{ $fieldKey }}]" value="OK" {{ $savedVal === 'OK' ? 'checked' : '' }}
<input type="checkbox" name="section_data[value]" value="OK" {{ $savedVal === 'OK' ? 'checked' : '' }}
data-section-id="{{ $section->id }}" data-column-id="{{ $col->id }}" data-row-index="{{ $rowIndex }}"
data-field-key="value"
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500">
</td>
@else
@@ -684,12 +721,13 @@ class="rounded border-gray-300 text-blue-600 focus:ring-blue-500">
<td class="px-2 py-2 border border-gray-300 text-sm">{{ $staticValue }}</td>
@else
@php
$fieldKey = "s{$section->id}_r{$rowIndex}_c{$col->id}";
$savedVal = $document?->data->where('field_key', $fieldKey)->first()?->field_value ?? '';
$savedVal = $getData($section->id, $col->id, $rowIndex, 'value');
if (!$savedVal) $savedVal = $getLegacy("s{$section->id}_r{$rowIndex}_c{$col->id}");
@endphp
<td class="px-1 py-1 border border-gray-300 text-center">
<input type="text" name="section_data[{{ $fieldKey }}]" value="{{ $savedVal }}"
<input type="text" name="section_data[value]" value="{{ $savedVal }}"
data-section-id="{{ $section->id }}" data-column-id="{{ $col->id }}" data-row-index="{{ $rowIndex }}"
data-field-key="value"
class="w-full text-center text-sm border-0 focus:ring-1 focus:ring-blue-400 rounded py-1">
</td>
@endif
@@ -713,10 +751,11 @@ class="w-full text-center text-sm border-0 focus:ring-1 focus:ring-blue-400 roun
{{ $template->footer_remark_label ?? '비고' }}
</label>
@php
$remarkKey = 'footer_remark';
$remarkVal = $document?->data->where('field_key', $remarkKey)->first()?->field_value ?? '';
$remarkVal = $document?->data->where('field_key', 'remark')->first()?->field_value
?? $document?->data->where('field_key', 'footer_remark')->first()?->field_value
?? '';
@endphp
<textarea name="data[{{ $remarkKey }}]" rows="3"
<textarea name="data[remark]" rows="3"
class="w-full rounded-lg border-gray-300 text-sm focus:border-blue-500 focus:ring-blue-500">{{ $remarkVal }}</textarea>
</div>
<div>
@@ -724,11 +763,12 @@ class="w-full rounded-lg border-gray-300 text-sm focus:border-blue-500 focus:rin
{{ $template->footer_judgement_label ?? '종합판정' }}
</label>
@php
$judgementKey = 'footer_judgement';
$judgementVal = $document?->data->where('field_key', $judgementKey)->first()?->field_value ?? '';
$judgementVal = $document?->data->where('field_key', 'overall_result')->first()?->field_value
?? $document?->data->where('field_key', 'footer_judgement')->first()?->field_value
?? '';
$judgementOptions = $template->footer_judgement_options ?? ['적합', '부적합'];
@endphp
<select name="data[{{ $judgementKey }}]"
<select name="data[overall_result]"
class="w-full rounded-lg border-gray-300 text-sm focus:border-blue-500 focus:ring-blue-500">
<option value="">선택하세요</option>
@foreach($judgementOptions as $opt)
@@ -821,35 +861,29 @@ function itemSearch(fieldKey, savedValue) {
}
}
// 섹션 테이블 데이터 수집 (section_data[field_key])
for (const [key, value] of formData.entries()) {
if (key.startsWith('section_data[') && key.endsWith(']')) {
const fieldKey = key.slice(13, -1);
const el = form.querySelector(`[name="${key}"]`);
// 체크박스는 체크 안된 경우 skip
if (el && el.type === 'checkbox' && !el.checked) continue;
data.data.push({
field_key: fieldKey,
field_value: value,
section_id: el?.dataset?.sectionId ? parseInt(el.dataset.sectionId) : null,
column_id: el?.dataset?.columnId ? parseInt(el.dataset.columnId) : null,
row_index: el?.dataset?.rowIndex ? parseInt(el.dataset.rowIndex) : 0,
});
}
}
// 섹션 테이블 데이터 수집 (data-field-key 기반)
form.querySelectorAll('[data-field-key]').forEach(el => {
const fieldKey = el.dataset.fieldKey;
const sectionId = el.dataset.sectionId ? parseInt(el.dataset.sectionId) : null;
const columnId = el.dataset.columnId ? parseInt(el.dataset.columnId) : null;
const rowIndex = el.dataset.rowIndex ? parseInt(el.dataset.rowIndex) : 0;
// 체크 안된 체크박스도 빈 값으로 전송
form.querySelectorAll('input[type="checkbox"][name^="section_data["]').forEach(cb => {
if (!cb.checked) {
const fieldKey = cb.name.slice(13, -1);
data.data.push({
field_key: fieldKey,
field_value: '',
section_id: cb.dataset?.sectionId ? parseInt(cb.dataset.sectionId) : null,
column_id: cb.dataset?.columnId ? parseInt(cb.dataset.columnId) : null,
row_index: cb.dataset?.rowIndex ? parseInt(cb.dataset.rowIndex) : 0,
});
let fieldValue;
if (el.type === 'checkbox') {
fieldValue = el.checked ? el.value : '';
} else if (el.tagName === 'SELECT') {
fieldValue = el.value;
} else {
fieldValue = el.value;
}
data.data.push({
field_key: fieldKey,
field_value: fieldValue,
section_id: sectionId,
column_id: columnId,
row_index: rowIndex,
});
});
const isCreate = {{ $isCreate ? 'true' : 'false' }};