feat: 수입검사 성적서 템플릿 시더 및 미리보기 구현
- InspectionTemplateSeeder: 검사항목 4개(겉모양, 두께, 폭, 길이) 생성 - 템플릿 미리보기를 React 성적서 양식과 동일한 형태로 구현 - 헤더: 로고, 제목, 결재란 - 기본정보 테이블 (목업 데이터) - 검사항목 테이블: NO, 검사항목, 검사기준, 검사방식, 검사주기, 측정값(n1,n2,n3), 판정 - 종합판정 영역 - 문서 목록/상세/편집 뷰 개선 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
78
database/seeders/InspectionTemplateSeeder.php
Normal file
78
database/seeders/InspectionTemplateSeeder.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\DocumentTemplate;
|
||||
use App\Models\DocumentTemplateApprovalLine;
|
||||
use App\Models\DocumentTemplateBasicField;
|
||||
use App\Models\DocumentTemplateColumn;
|
||||
use App\Models\DocumentTemplateSection;
|
||||
use App\Models\DocumentTemplateSectionItem;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class InspectionTemplateSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* 수입검사 성적서 템플릿 - 검사항목 테이블만
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$tenantId = 1;
|
||||
$this->cleanupExisting($tenantId);
|
||||
|
||||
// 템플릿 생성
|
||||
$template = DocumentTemplate::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'name' => '철제품 수입검사 성적서',
|
||||
'category' => '품질/수입검사',
|
||||
'title' => '수입검사 성적서',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
// 검사항목 섹션
|
||||
$section = DocumentTemplateSection::create([
|
||||
'template_id' => $template->id,
|
||||
'title' => '검사 항목',
|
||||
'sort_order' => 1,
|
||||
]);
|
||||
|
||||
// 검사항목 (React 모달과 동일)
|
||||
$items = [
|
||||
['item' => '겉모양', 'standard' => '외관 이상 없음', 'method' => '육안'],
|
||||
['item' => '두께', 'standard' => 't 1.0', 'method' => '계측'],
|
||||
['item' => '폭', 'standard' => 'W 1,000mm', 'method' => '계측'],
|
||||
['item' => '길이', 'standard' => 'L 2,000mm', 'method' => '계측'],
|
||||
];
|
||||
|
||||
foreach ($items as $i => $item) {
|
||||
DocumentTemplateSectionItem::create([
|
||||
'section_id' => $section->id,
|
||||
'item' => $item['item'],
|
||||
'standard' => $item['standard'],
|
||||
'method' => $item['method'],
|
||||
'sort_order' => $i + 1,
|
||||
]);
|
||||
}
|
||||
|
||||
$this->command->info("✅ 템플릿 생성 완료 (ID: {$template->id})");
|
||||
}
|
||||
|
||||
private function cleanupExisting(int $tenantId): void
|
||||
{
|
||||
$existing = DocumentTemplate::where('tenant_id', $tenantId)
|
||||
->where('name', '철제품 수입검사 성적서')
|
||||
->first();
|
||||
|
||||
if ($existing) {
|
||||
DocumentTemplateColumn::where('template_id', $existing->id)->delete();
|
||||
$sections = DocumentTemplateSection::where('template_id', $existing->id)->get();
|
||||
foreach ($sections as $section) {
|
||||
DocumentTemplateSectionItem::where('section_id', $section->id)->delete();
|
||||
}
|
||||
DocumentTemplateSection::where('template_id', $existing->id)->delete();
|
||||
DocumentTemplateBasicField::where('template_id', $existing->id)->delete();
|
||||
DocumentTemplateApprovalLine::where('template_id', $existing->id)->delete();
|
||||
$existing->forceDelete();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -588,90 +588,122 @@ function closePreviewModal() {
|
||||
}
|
||||
|
||||
function generatePreviewHtml() {
|
||||
const title = document.getElementById('title').value || '검사 성적서';
|
||||
const companyName = '{{ $tenant?->company_name ?? "회사명" }}';
|
||||
const title = document.getElementById('title').value || '수입검사 성적서';
|
||||
|
||||
// 검사항목 행 생성
|
||||
const renderItems = () => {
|
||||
if (templateState.sections.length === 0 || templateState.sections[0].items.length === 0) {
|
||||
return `<tr><td colspan="10" class="text-center py-4 text-gray-400">검사항목이 없습니다.</td></tr>`;
|
||||
}
|
||||
return templateState.sections[0].items.map((item, idx) => `
|
||||
<tr>
|
||||
<td class="border border-gray-400 px-2 py-1.5 text-center">${idx + 1}</td>
|
||||
<td class="border border-gray-400 px-2 py-1.5">${escapeHtml(item.item)}</td>
|
||||
<td class="border border-gray-400 px-2 py-1.5">${escapeHtml(item.standard)}</td>
|
||||
<td class="border border-gray-400 px-2 py-1.5 text-center">${escapeHtml(item.method)}</td>
|
||||
<td class="border border-gray-400 px-2 py-1.5 text-center">LOT</td>
|
||||
<td class="border border-gray-400 px-2 py-1.5 text-center"><input type="radio" name="j${idx}_1"> OK <input type="radio" name="j${idx}_1"> NG</td>
|
||||
<td class="border border-gray-400 px-2 py-1.5 text-center">-</td>
|
||||
<td class="border border-gray-400 px-2 py-1.5 text-center">-</td>
|
||||
<td class="border border-gray-400 px-2 py-1.5 text-center">-</td>
|
||||
<td class="border border-gray-400 px-2 py-1.5 text-center">-</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
};
|
||||
|
||||
// 실제 React 성적서 양식과 동일한 형태
|
||||
return `
|
||||
<div class="bg-white p-8 border" style="font-family: 'Malgun Gothic', sans-serif;">
|
||||
<div class="text-center mb-6">
|
||||
<h1 class="text-2xl font-bold">${escapeHtml(title)}</h1>
|
||||
<p class="text-gray-600 mt-1">${escapeHtml(companyName)}</p>
|
||||
<div class="bg-white p-6" style="font-family: 'Malgun Gothic', sans-serif; font-size: 12px; max-width: 900px; margin: 0 auto;">
|
||||
<!-- 헤더: 로고 + 제목 + 결재란 -->
|
||||
<div class="flex justify-between items-start mb-4">
|
||||
<!-- 로고 -->
|
||||
<div class="text-center" style="width: 80px;">
|
||||
<div class="text-2xl font-bold">KD</div>
|
||||
<div class="text-xs">경동기업</div>
|
||||
</div>
|
||||
<!-- 제목 -->
|
||||
<div class="flex-1 text-center">
|
||||
<h1 class="text-xl font-bold tracking-widest">${escapeHtml(title)}</h1>
|
||||
</div>
|
||||
<!-- 결재란 -->
|
||||
<div>
|
||||
<table class="border-collapse text-xs" style="width: 120px;">
|
||||
<tr>
|
||||
<td class="border border-gray-400 px-2 py-1 bg-gray-100">담당</td>
|
||||
<td class="border border-gray-400 px-2 py-1 bg-gray-100">부서장</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="border border-gray-400 px-2 py-1">결재</td>
|
||||
<td class="border border-gray-400 px-2 py-1">노원호</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="text-right text-xs mt-1">참고일자: 2026-01-29</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${templateState.approval_lines.length > 0 ? `
|
||||
<div class="flex justify-end mb-6">
|
||||
<table class="border-collapse border border-gray-400 text-sm">
|
||||
<tr>
|
||||
${templateState.approval_lines.map(line => `
|
||||
<td class="border border-gray-400 px-4 py-1 text-center font-medium bg-gray-100">${escapeHtml(line.name)}</td>
|
||||
`).join('')}
|
||||
</tr>
|
||||
<tr>
|
||||
${templateState.approval_lines.map(line => `
|
||||
<td class="border border-gray-400 px-4 py-6 text-center"></td>
|
||||
`).join('')}
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
` : ''}
|
||||
<!-- 기본 정보 테이블 -->
|
||||
<table class="w-full border-collapse text-xs mb-4">
|
||||
<tr>
|
||||
<td class="border border-gray-400 px-2 py-1.5 bg-gray-100 font-medium" style="width:80px">품 명</td>
|
||||
<td class="border border-gray-400 px-2 py-1.5" colspan="2">SUS304 스테인리스 판재</td>
|
||||
<td class="border border-gray-400 px-2 py-1.5 bg-gray-100 font-medium" style="width:100px">납품업체<br>(제조업체)</td>
|
||||
<td class="border border-gray-400 px-2 py-1.5">(주)대한철강</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="border border-gray-400 px-2 py-1.5 bg-gray-100 font-medium">규 격<br>(두께*너비<br>*길이)</td>
|
||||
<td class="border border-gray-400 px-2 py-1.5" colspan="2">1000×2000×3T</td>
|
||||
<td class="border border-gray-400 px-2 py-1.5 bg-gray-100 font-medium">로트번호</td>
|
||||
<td class="border border-gray-400 px-2 py-1.5">LOT-2026-001</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="border border-gray-400 px-2 py-1.5 bg-gray-100 font-medium">자재번호</td>
|
||||
<td class="border border-gray-400 px-2 py-1.5" colspan="2">PE02RB</td>
|
||||
<td class="border border-gray-400 px-2 py-1.5 bg-gray-100 font-medium">검사일자</td>
|
||||
<td class="border border-gray-400 px-2 py-1.5">01/29</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="border border-gray-400 px-2 py-1.5 bg-gray-100 font-medium">로트크기</td>
|
||||
<td class="border border-gray-400 px-2 py-1.5">200</td>
|
||||
<td class="border border-gray-400 px-2 py-1.5 text-center">매</td>
|
||||
<td class="border border-gray-400 px-2 py-1.5 bg-gray-100 font-medium">검사자</td>
|
||||
<td class="border border-gray-400 px-2 py-1.5">노원호 <input type="checkbox" checked class="ml-2"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
${templateState.sections.map(section => `
|
||||
<div class="mb-6">
|
||||
<h3 class="font-bold text-lg mb-2 border-b-2 border-gray-800 pb-1">${escapeHtml(section.title)}</h3> ${section.image_path ? `
|
||||
<div class="mb-3">
|
||||
<img src="/storage/${section.image_path}" alt="${escapeHtml(section.title)}" class="max-w-full h-auto border border-gray-300 rounded">
|
||||
</div>
|
||||
` : ''}
|
||||
${section.items.length > 0 ? `
|
||||
<table class="w-full border-collapse border border-gray-400 text-sm">
|
||||
<thead>
|
||||
<tr class="bg-gray-100">
|
||||
<th class="border border-gray-400 px-2 py-1">구분</th>
|
||||
<th class="border border-gray-400 px-2 py-1">검사항목</th>
|
||||
<th class="border border-gray-400 px-2 py-1">검사기준</th>
|
||||
<th class="border border-gray-400 px-2 py-1">검사방법</th>
|
||||
<th class="border border-gray-400 px-2 py-1">검사주기</th>
|
||||
<th class="border border-gray-400 px-2 py-1">관련규정</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${section.items.map(item => `
|
||||
<tr>
|
||||
<td class="border border-gray-400 px-2 py-1">${escapeHtml(item.category)}</td>
|
||||
<td class="border border-gray-400 px-2 py-1">${escapeHtml(item.item)}</td>
|
||||
<td class="border border-gray-400 px-2 py-1">${escapeHtml(item.standard)}</td>
|
||||
<td class="border border-gray-400 px-2 py-1">${escapeHtml(item.method)}</td>
|
||||
<td class="border border-gray-400 px-2 py-1">${escapeHtml(item.frequency)}</td>
|
||||
<td class="border border-gray-400 px-2 py-1">${escapeHtml(item.regulation)}</td>
|
||||
</tr>
|
||||
`).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
` : '<p class="text-gray-400">항목 없음</p>'}
|
||||
</div>
|
||||
`).join('')}
|
||||
<!-- 검사 항목 테이블 -->
|
||||
<table class="w-full border-collapse text-xs">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border border-gray-400 px-2 py-1.5 bg-gray-100" style="width:30px" rowspan="2">NO</th>
|
||||
<th class="border border-gray-400 px-2 py-1.5 bg-gray-100" style="width:80px" rowspan="2">검사항목</th>
|
||||
<th class="border border-gray-400 px-2 py-1.5 bg-gray-100" rowspan="2">검사기준</th>
|
||||
<th class="border border-gray-400 px-2 py-1.5 bg-gray-100" style="width:60px" rowspan="2">검사방식</th>
|
||||
<th class="border border-gray-400 px-2 py-1.5 bg-gray-100" style="width:60px" rowspan="2">검사주기</th>
|
||||
<th class="border border-gray-400 px-2 py-1.5 bg-gray-100" colspan="3">측정값</th>
|
||||
<th class="border border-gray-400 px-2 py-1.5 bg-gray-100" style="width:50px" rowspan="2">판정<br>(적/부)</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="border border-gray-400 px-1 py-1 bg-gray-100" style="width:50px">n1</th>
|
||||
<th class="border border-gray-400 px-1 py-1 bg-gray-100" style="width:50px">n2</th>
|
||||
<th class="border border-gray-400 px-1 py-1 bg-gray-100" style="width:50px">n3</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${renderItems()}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
${templateState.columns.length > 0 ? `
|
||||
<div class="mb-6">
|
||||
<h3 class="font-bold text-lg mb-2 border-b-2 border-gray-800 pb-1">검사 데이터</h3>
|
||||
<table class="w-full border-collapse border border-gray-400 text-sm">
|
||||
<thead>
|
||||
<tr class="bg-gray-100">
|
||||
${templateState.columns.map(col => `
|
||||
<th class="border border-gray-400 px-2 py-1" style="width: ${col.width}">${escapeHtml(col.label)}</th>
|
||||
`).join('')}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
${templateState.columns.map(() => `
|
||||
<td class="border border-gray-400 px-2 py-4"></td>
|
||||
`).join('')}
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
` : ''}
|
||||
<!-- 종합판정 -->
|
||||
<div class="flex justify-end mt-4">
|
||||
<table class="border-collapse text-xs">
|
||||
<tr>
|
||||
<td class="border border-gray-400 px-4 py-2 bg-gray-100 font-medium">종합판정</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="border border-gray-400 px-4 py-3 text-center text-gray-400">미완료</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
@section('title', $isCreate ? '새 문서 작성' : '문서 수정')
|
||||
|
||||
@section('content')
|
||||
<div class="p-6">
|
||||
{{-- 헤더 --}}
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<div class="max-w-7xl mx-auto">
|
||||
<!-- 헤더 -->
|
||||
<div class="flex flex-col lg:flex-row lg:justify-between lg:items-center gap-4 mb-6">
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold text-gray-800">{{ $isCreate ? '새 문서 작성' : '문서 수정' }}</h1>
|
||||
<p class="text-sm text-gray-500 mt-1">
|
||||
<p class="text-sm text-gray-500 mt-1 hidden sm:block">
|
||||
@if($document)
|
||||
{{ $document->document_no }} - {{ $document->title }}
|
||||
@else
|
||||
@@ -16,13 +16,12 @@
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
<a href="{{ route('documents.index') }}"
|
||||
class="inline-flex items-center px-4 py-2 bg-gray-100 text-gray-700 text-sm font-medium rounded-lg hover:bg-gray-200 transition-colors">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"/>
|
||||
</svg>
|
||||
목록으로
|
||||
</a>
|
||||
<div class="flex items-center gap-2">
|
||||
<a href="{{ route('documents.index') }}"
|
||||
class="bg-gray-200 hover:bg-gray-300 text-gray-700 px-4 py-2 rounded-lg transition">
|
||||
목록
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 템플릿 선택 (생성 시) --}}
|
||||
|
||||
@@ -3,70 +3,68 @@
|
||||
@section('title', '문서 관리')
|
||||
|
||||
@section('content')
|
||||
<div class="p-6">
|
||||
{{-- 헤더 --}}
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<!-- 페이지 헤더 -->
|
||||
<div class="flex flex-col lg:flex-row lg:justify-between lg:items-center gap-4 mb-6">
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold text-gray-800">문서 관리</h1>
|
||||
<p class="text-sm text-gray-500 mt-1">작성된 문서를 관리합니다.</p>
|
||||
<p class="text-sm text-gray-500 mt-1 hidden sm:block">
|
||||
작성된 문서를 관리합니다.
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-2 sm:gap-3">
|
||||
<a href="{{ route('documents.create') }}"
|
||||
class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg transition flex items-center gap-2">
|
||||
<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="M12 4v16m8-8H4" />
|
||||
</svg>
|
||||
새 문서 작성
|
||||
</a>
|
||||
</div>
|
||||
<a href="{{ route('documents.create') }}"
|
||||
class="inline-flex items-center px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-lg hover:bg-blue-700 transition-colors">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
|
||||
</svg>
|
||||
새 문서 작성
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{{-- 필터 --}}
|
||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-4 mb-6">
|
||||
<form id="filterForm" class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
{{-- 상태 필터 --}}
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">상태</label>
|
||||
<select name="status" class="w-full rounded-lg border-gray-300 text-sm focus:border-blue-500 focus:ring-blue-500">
|
||||
<option value="">전체</option>
|
||||
<!-- 필터 영역 -->
|
||||
<x-filter-collapsible id="filterForm">
|
||||
<form id="filterForm" class="flex flex-wrap gap-2 sm:gap-4">
|
||||
<input type="hidden" name="per_page" id="perPageInput" value="15">
|
||||
<input type="hidden" name="page" id="pageInput" value="1">
|
||||
|
||||
<!-- 검색 -->
|
||||
<div class="flex-1 min-w-0 w-full sm:w-auto">
|
||||
<input type="text"
|
||||
name="search"
|
||||
placeholder="문서번호, 제목으로 검색..."
|
||||
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 class="w-full sm:w-32">
|
||||
<select name="status" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
<option value="">전체 상태</option>
|
||||
@foreach($statuses as $value => $label)
|
||||
<option value="{{ $value }}">{{ $label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{-- 템플릿 필터 --}}
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">템플릿</label>
|
||||
<select name="template_id" class="w-full rounded-lg border-gray-300 text-sm focus:border-blue-500 focus:ring-blue-500">
|
||||
<option value="">전체</option>
|
||||
<!-- 템플릿 필터 -->
|
||||
<div class="w-full sm:w-40">
|
||||
<select name="template_id" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
<option value="">전체 템플릿</option>
|
||||
@foreach($templates as $template)
|
||||
<option value="{{ $template->id }}">{{ $template->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{-- 검색 --}}
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">검색</label>
|
||||
<input type="text" name="search" placeholder="문서번호, 제목"
|
||||
class="w-full rounded-lg border-gray-300 text-sm focus:border-blue-500 focus:ring-blue-500">
|
||||
</div>
|
||||
|
||||
{{-- 버튼 --}}
|
||||
<div class="flex items-end gap-2">
|
||||
<button type="submit"
|
||||
class="px-4 py-2 bg-gray-800 text-white text-sm font-medium rounded-lg hover:bg-gray-900 transition-colors">
|
||||
검색
|
||||
</button>
|
||||
<button type="reset"
|
||||
class="px-4 py-2 bg-gray-100 text-gray-700 text-sm font-medium rounded-lg hover:bg-gray-200 transition-colors">
|
||||
초기화
|
||||
</button>
|
||||
</div>
|
||||
<!-- 검색 버튼 -->
|
||||
<button type="submit" class="bg-gray-600 hover:bg-gray-700 text-white px-6 py-2 rounded-lg transition w-full sm:w-auto">
|
||||
검색
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</x-filter-collapsible>
|
||||
|
||||
{{-- 문서 목록 --}}
|
||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200">
|
||||
<!-- 문서 목록 테이블 -->
|
||||
<div class="bg-white rounded-lg shadow-sm overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
@@ -81,17 +79,22 @@ class="px-4 py-2 bg-gray-100 text-gray-700 text-sm font-medium rounded-lg hover:
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="documentList" class="bg-white divide-y divide-gray-200">
|
||||
{{-- HTMX로 로드 --}}
|
||||
<!-- 로딩 스피너 -->
|
||||
<tr id="loadingRow">
|
||||
<td colspan="7" class="px-6 py-12 text-center">
|
||||
<div class="flex justify-center items-center">
|
||||
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{{-- 페이지네이션 --}}
|
||||
<!-- 페이지네이션 -->
|
||||
<div id="pagination" class="px-6 py-4 border-t border-gray-200">
|
||||
{{-- HTMX로 로드 --}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
@@ -100,15 +103,13 @@ class="px-4 py-2 bg-gray-100 text-gray-700 text-sm font-medium rounded-lg hover:
|
||||
loadDocuments();
|
||||
|
||||
// 필터 폼 제출
|
||||
document.getElementById('filterForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
loadDocuments();
|
||||
});
|
||||
|
||||
// 초기화 버튼
|
||||
document.getElementById('filterForm').addEventListener('reset', function() {
|
||||
setTimeout(() => loadDocuments(), 10);
|
||||
});
|
||||
const filterForm = document.getElementById('filterForm');
|
||||
if (filterForm) {
|
||||
filterForm.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
loadDocuments();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function loadDocuments(page = 1) {
|
||||
@@ -117,6 +118,17 @@ function loadDocuments(page = 1) {
|
||||
const params = new URLSearchParams(formData);
|
||||
params.set('page', page);
|
||||
|
||||
// 로딩 표시
|
||||
document.getElementById('documentList').innerHTML = `
|
||||
<tr>
|
||||
<td colspan="7" class="px-6 py-12 text-center">
|
||||
<div class="flex justify-center items-center">
|
||||
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
|
||||
fetch(`/api/admin/documents?${params.toString()}`, {
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
|
||||
@@ -3,29 +3,25 @@
|
||||
@section('title', '문서 상세')
|
||||
|
||||
@section('content')
|
||||
<div class="p-6">
|
||||
{{-- 헤더 --}}
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<!-- 헤더 -->
|
||||
<div class="flex flex-col lg:flex-row lg:justify-between lg:items-center gap-4 mb-6">
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold text-gray-800">문서 상세</h1>
|
||||
<p class="text-sm text-gray-500 mt-1">{{ $document->document_no }} - {{ $document->title }}</p>
|
||||
<p class="text-sm text-gray-500 mt-1 hidden sm:block">{{ $document->document_no }} - {{ $document->title }}</p>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<div class="flex flex-wrap items-center gap-2 sm:gap-3">
|
||||
@if($document->status === 'DRAFT' || $document->status === 'REJECTED')
|
||||
<a href="{{ route('documents.edit', $document->id) }}"
|
||||
class="inline-flex items-center px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-lg hover:bg-blue-700 transition-colors">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg transition flex items-center gap-2">
|
||||
<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="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
|
||||
</svg>
|
||||
수정
|
||||
</a>
|
||||
@endif
|
||||
<a href="{{ route('documents.index') }}"
|
||||
class="inline-flex items-center px-4 py-2 bg-gray-100 text-gray-700 text-sm font-medium rounded-lg hover:bg-gray-200 transition-colors">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"/>
|
||||
</svg>
|
||||
목록으로
|
||||
class="bg-gray-200 hover:bg-gray-300 text-gray-700 px-4 py-2 rounded-lg transition">
|
||||
목록
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -196,5 +192,4 @@ class="text-sm text-blue-600 hover:text-blue-800">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
Reference in New Issue
Block a user