Files
sam-manage/resources/views/boards/create.blade.php
kent 817690f544 feat: 게시판 템플릿 기반 생성 기능 및 SVG 아이콘 적용
- 게시판 템플릿 설정 파일 추가 (config/board_templates.php)
  - 시스템 템플릿: 공지사항, 1:1문의, FAQ, 팝업공지
  - 테넌트 템플릿: 자유게시판, 갤러리, 자료실, 공지사항, Q&A
- BoardService 템플릿 관련 메서드 추가
- BoardController 템플릿 API 엔드포인트 추가
- 게시판 생성 UI 3단계 위자드로 개선
- 모든 템플릿 아이콘을 이모지에서 SVG path로 변경

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 23:28:43 +09:00

645 lines
29 KiB
PHP

@extends('layouts.app')
@section('title', '게시판 생성')
@section('content')
<!-- 페이지 헤더 -->
<div class="flex justify-between items-center mb-6">
<h1 class="text-2xl font-bold text-gray-800"> 게시판 생성</h1>
<a href="{{ route('boards.index') }}" class="text-gray-600 hover:text-gray-900">
&larr; 목록으로
</a>
</div>
<!-- 스텝 인디케이터 -->
<div class="mb-8">
<div class="flex items-center justify-center space-x-4">
<div id="step1-indicator" class="flex items-center">
<div class="w-8 h-8 rounded-full bg-blue-600 text-white flex items-center justify-center text-sm font-medium">1</div>
<span class="ml-2 text-sm font-medium text-blue-600">게시판 유형</span>
</div>
<div class="w-16 h-0.5 bg-gray-300" id="step-line-1"></div>
<div id="step2-indicator" class="flex items-center">
<div class="w-8 h-8 rounded-full bg-gray-300 text-gray-600 flex items-center justify-center text-sm font-medium">2</div>
<span class="ml-2 text-sm font-medium text-gray-500">템플릿 선택</span>
</div>
<div class="w-16 h-0.5 bg-gray-300" id="step-line-2"></div>
<div id="step3-indicator" class="flex items-center">
<div class="w-8 h-8 rounded-full bg-gray-300 text-gray-600 flex items-center justify-center text-sm font-medium">3</div>
<span class="ml-2 text-sm font-medium text-gray-500">상세 설정</span>
</div>
</div>
</div>
<!-- Step 1: 게시판 유형 선택 -->
<div id="step1" class="bg-white rounded-lg shadow-sm p-6">
<h2 class="text-lg font-medium text-gray-900 mb-6">게시판 유형을 선택하세요</h2>
<div class="grid grid-cols-2 gap-6">
<!-- 시스템 게시판 -->
<div class="border-2 border-gray-200 rounded-lg p-6 cursor-pointer hover:border-blue-500 hover:bg-blue-50 transition"
onclick="selectBoardScope('system')" id="scope-system">
<div class="flex items-center mb-4">
<div class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center">
<svg class="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4"></path>
</svg>
</div>
<div class="ml-4">
<h3 class="text-lg font-semibold text-gray-900">시스템 게시판</h3>
<p class="text-sm text-gray-500">모든 테넌트 공용</p>
</div>
</div>
<p class="text-sm text-gray-600">
본사에서 관리하며 모든 테넌트가 접근할 있는 게시판입니다.
<br>: 공지사항, 1:1 문의, FAQ, 팝업 공지
</p>
</div>
<!-- 테넌트 게시판 -->
<div class="border-2 border-gray-200 rounded-lg p-6 cursor-pointer hover:border-green-500 hover:bg-green-50 transition"
onclick="selectBoardScope('tenant')" id="scope-tenant">
<div class="flex items-center mb-4">
<div class="w-12 h-12 bg-green-100 rounded-lg flex items-center justify-center">
<svg class="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
</svg>
</div>
<div class="ml-4">
<h3 class="text-lg font-semibold text-gray-900">테넌트 게시판</h3>
<p class="text-sm text-gray-500">특정 테넌트 전용</p>
</div>
</div>
<p class="text-sm text-gray-600">
특정 테넌트 내부에서만 사용하는 게시판입니다.
<br>: 자유게시판, 갤러리, 자료실
</p>
</div>
</div>
<!-- 테넌트 선택 (테넌트 게시판 선택 표시) -->
<div id="tenant-selector" class="mt-6 hidden">
<label class="block text-sm font-medium text-gray-700 mb-2">
테넌트 선택 <span class="text-red-500">*</span>
</label>
<select id="tenant_id" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500">
<option value="">테넌트를 선택하세요</option>
</select>
</div>
</div>
<!-- Step 2: 템플릿 선택 -->
<div id="step2" class="bg-white rounded-lg shadow-sm p-6 hidden">
<h2 class="text-lg font-medium text-gray-900 mb-2">템플릿을 선택하세요</h2>
<p class="text-sm text-gray-500 mb-6">템플릿을 선택하면 기본 설정과 필드가 자동으로 구성됩니다. 템플릿으로 직접 구성할 수도 있습니다.</p>
<!-- 시스템 템플릿 -->
<div id="system-templates" class="hidden">
<h3 class="text-sm font-medium text-gray-700 mb-4">시스템 게시판 템플릿</h3>
<div class="grid grid-cols-4 gap-4" id="system-template-cards"></div>
</div>
<!-- 테넌트 템플릿 -->
<div id="tenant-templates" class="hidden">
<h3 class="text-sm font-medium text-gray-700 mb-4">테넌트 게시판 템플릿</h3>
<div class="grid grid-cols-4 gap-4" id="tenant-template-cards"></div>
</div>
<!-- 템플릿 -->
<div class="mt-6 pt-6 border-t border-gray-200">
<div class="border-2 border-dashed border-gray-300 rounded-lg p-4 cursor-pointer hover:border-gray-400 hover:bg-gray-50 transition text-center"
onclick="selectTemplate(null, null)">
<div class="text-gray-400 text-2xl mb-2">+</div>
<div class="text-sm font-medium text-gray-600"> 템플릿</div>
<div class="text-xs text-gray-400">직접 모든 설정 구성</div>
</div>
</div>
<!-- 뒤로 버튼 -->
<div class="mt-6 flex justify-start">
<button type="button" onclick="goToStep(1)" class="px-4 py-2 text-gray-600 hover:text-gray-900">
&larr; 이전 단계
</button>
</div>
</div>
<!-- Step 3: 상세 설정 -->
<div id="step3" class="bg-white rounded-lg shadow-sm p-6 hidden">
<form id="boardForm" class="space-y-6">
@csrf
<!-- 선택된 정보 표시 -->
<div class="bg-gray-50 rounded-lg p-4 mb-6">
<div class="flex items-center justify-between">
<div>
<span class="text-sm text-gray-500">선택된 유형:</span>
<span id="selected-scope-label" class="ml-2 font-medium text-gray-900"></span>
<span id="selected-tenant-label" class="ml-2 text-sm text-gray-600"></span>
</div>
<div>
<span class="text-sm text-gray-500">템플릿:</span>
<span id="selected-template-label" class="ml-2 font-medium text-gray-900"></span>
</div>
</div>
</div>
<!-- 기본 필드 안내 -->
<div class="bg-blue-50 border border-blue-200 rounded-lg p-4">
<h3 class="text-sm font-medium text-blue-800 mb-2">기본 필드 (자동 포함)</h3>
<div class="grid grid-cols-3 gap-2 text-sm text-blue-700">
<div>- 제목 (title) <span class="text-red-500">*</span></div>
<div>- 내용 (content) <span class="text-red-500">*</span></div>
<div>- 작성자 (user_id)</div>
<div>- 조회수 (views)</div>
<div>- 공지 여부 (is_notice)</div>
<div>- 비밀글 여부 (is_secret)</div>
</div>
<p class="mt-2 text-xs text-blue-600"> 필드는 모든 게시판에 기본으로 포함됩니다. 아래에서 추가 커스텀 필드를 설정할 있습니다.</p>
</div>
<!-- 기본 정보 -->
<div class="border-b border-gray-200 pb-6">
<h2 class="text-lg font-medium text-gray-900 mb-4">기본 정보</h2>
<div class="grid grid-cols-2 gap-6">
<!-- 게시판 코드 -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">
게시판 코드 <span class="text-red-500">*</span>
</label>
<input type="text" name="board_code" id="board_code"
placeholder="영문 소문자, 숫자, 하이픈만 사용"
pattern="[a-z0-9-]+"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
required>
<p class="mt-1 text-sm text-gray-500">URL에 사용됩니다 (: notice, qna, faq)</p>
</div>
<!-- 게시판명 -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">
게시판명 <span class="text-red-500">*</span>
</label>
<input type="text" name="name" id="board_name"
placeholder="공지사항"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
required>
</div>
<!-- 게시판 유형 -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">게시판 유형</label>
<input type="text" name="board_type" id="board_type"
placeholder="notice, qna, faq, free 등"
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>
<label class="block text-sm font-medium text-gray-700 mb-2">에디터 타입</label>
<select name="editor_type" id="editor_type"
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="wysiwyg">WYSIWYG (위지윅)</option>
<option value="markdown">Markdown</option>
<option value="text">텍스트</option>
</select>
</div>
<!-- 설명 -->
<div class="col-span-2">
<label class="block text-sm font-medium text-gray-700 mb-2">설명</label>
<textarea name="description" id="description" rows="3"
placeholder="게시판에 대한 간단한 설명"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"></textarea>
</div>
</div>
</div>
<!-- 파일 설정 -->
<div class="border-b border-gray-200 pb-6">
<h2 class="text-lg font-medium text-gray-900 mb-4">파일 첨부 설정</h2>
<div class="grid grid-cols-3 gap-6">
<!-- 파일 첨부 허용 -->
<div>
<label class="flex items-center">
<input type="checkbox" name="allow_files" id="allow_files" value="1" checked
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500">
<span class="ml-2 text-sm text-gray-700">파일 첨부 허용</span>
</label>
</div>
<!-- 최대 파일 -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">최대 파일 </label>
<input type="number" name="max_file_count" id="max_file_count" value="5" min="0" max="100"
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>
<label class="block text-sm font-medium text-gray-700 mb-2">최대 파일 크기 (KB)</label>
<input type="number" name="max_file_size" id="max_file_size" value="20480" min="0"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
<p class="mt-1 text-sm text-gray-500">20480 = 20MB</p>
</div>
</div>
</div>
<!-- 템플릿 기본 필드 (템플릿 선택 표시) -->
<div id="template-fields-section" class="border-b border-gray-200 pb-6 hidden">
<h2 class="text-lg font-medium text-gray-900 mb-4">커스텀 필드 (템플릿 기본값)</h2>
<p class="text-sm text-gray-500 mb-4">선택한 템플릿에 포함된 기본 커스텀 필드입니다. 게시판 생성 수정/추가할 있습니다.</p>
<div id="template-fields-list" class="space-y-2"></div>
</div>
<!-- 활성 상태 -->
<div>
<label class="flex items-center">
<input type="checkbox" name="is_active" id="is_active" value="1" checked
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500">
<span class="ml-2 text-sm text-gray-700">활성화</span>
</label>
</div>
<!-- 에러 메시지 -->
<div id="errorMessage" class="hidden bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-lg"></div>
<!-- 버튼 -->
<div class="flex justify-between">
<button type="button" onclick="goToStep(2)" class="px-4 py-2 text-gray-600 hover:text-gray-900">
&larr; 이전 단계
</button>
<div class="space-x-4">
<a href="{{ route('boards.index') }}"
class="px-6 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 transition">
취소
</a>
<button type="submit"
class="px-6 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition">
생성
</button>
</div>
</div>
</form>
</div>
<!-- 생성 완료 모달 -->
<div id="successModal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50">
<div class="bg-white rounded-lg shadow-xl max-w-md w-full mx-4 p-6">
<div class="text-center">
<div class="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4">
<svg class="w-8 h-8 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
</svg>
</div>
<h3 class="text-lg font-semibold text-gray-900 mb-2">게시판이 생성되었습니다!</h3>
<p class="text-sm text-gray-600 mb-4" id="success-board-name"></p>
</div>
<div class="bg-gray-50 rounded-lg p-4 mb-4">
<h4 class="text-sm font-medium text-gray-700 mb-2">API 엔드포인트</h4>
<div class="text-xs font-mono bg-white rounded p-2 border border-gray-200">
<div class="mb-1"><span class="text-green-600">GET</span> /api/v1/boards/<span id="success-board-code" class="text-blue-600"></span></div>
<div class="mb-1"><span class="text-blue-600">POST</span> /api/v1/boards/<span class="board-code-placeholder text-blue-600"></span>/posts</div>
<div><span class="text-green-600">GET</span> /api/v1/boards/<span class="board-code-placeholder text-blue-600"></span>/posts</div>
</div>
</div>
<div class="flex space-x-3">
<a href="{{ route('boards.index') }}" class="flex-1 px-4 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 text-center">
목록으로
</a>
<button onclick="closeSuccessModal()" class="flex-1 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg">
커스텀 필드 추가
</button>
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
// 상태 변수
let currentStep = 1;
let selectedScope = null; // 'system' or 'tenant'
let selectedTenantId = null;
let selectedTemplateType = null;
let selectedTemplateKey = null;
let templates = { system: {}, tenant: {} };
let tenants = [];
let createdBoardId = null;
// 초기화
document.addEventListener('DOMContentLoaded', async function() {
await loadTemplates();
await loadTenants();
});
// 템플릿 로드
async function loadTemplates() {
try {
const response = await fetch('/api/admin/boards/templates', {
headers: { 'Accept': 'application/json' }
});
const data = await response.json();
if (data.success) {
templates = data.data.templates;
renderTemplateCards();
}
} catch (error) {
console.error('템플릿 로드 실패:', error);
}
}
// 테넌트 목록 로드
async function loadTenants() {
try {
const response = await fetch('/api/admin/boards/tenants', {
headers: { 'Accept': 'application/json' }
});
const data = await response.json();
if (data.success) {
tenants = data.data;
renderTenantOptions();
}
} catch (error) {
console.error('테넌트 로드 실패:', error);
}
}
// 테넌트 옵션 렌더링
function renderTenantOptions() {
const select = document.getElementById('tenant_id');
tenants.forEach(tenant => {
const option = document.createElement('option');
option.value = tenant.id;
option.textContent = `${tenant.company_name} (${tenant.code})`;
select.appendChild(option);
});
}
// 템플릿 카드 렌더링
function renderTemplateCards() {
// 시스템 템플릿
const systemContainer = document.getElementById('system-template-cards');
systemContainer.innerHTML = '';
Object.entries(templates.system || {}).forEach(([key, template]) => {
systemContainer.innerHTML += createTemplateCard('system', key, template);
});
// 테넌트 템플릿
const tenantContainer = document.getElementById('tenant-template-cards');
tenantContainer.innerHTML = '';
Object.entries(templates.tenant || {}).forEach(([key, template]) => {
tenantContainer.innerHTML += createTemplateCard('tenant', key, template);
});
}
// 템플릿 카드 HTML 생성
function createTemplateCard(type, key, template) {
const colorClass = type === 'system' ? 'blue' : 'green';
const defaultIconPath = 'M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z';
const iconPath = template.icon || defaultIconPath;
return `
<div class="border-2 border-gray-200 rounded-lg p-4 cursor-pointer hover:border-${colorClass}-500 hover:bg-${colorClass}-50 transition text-center"
onclick="selectTemplate('${type}', '${key}')">
<div class="mb-2">
<svg class="w-8 h-8 mx-auto text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="${iconPath}" />
</svg>
</div>
<div class="text-sm font-medium text-gray-900">${template.name}</div>
<div class="text-xs text-gray-500 mt-1">${template.description?.substring(0, 30) || ''}...</div>
${template.default_fields?.length ? `<div class="text-xs text-${colorClass}-600 mt-2">필드 ${template.default_fields.length} 포함</div>` : ''}
</div>
`;
}
// 게시판 유형 선택
function selectBoardScope(scope) {
selectedScope = scope;
// UI 업데이트
document.getElementById('scope-system').classList.remove('border-blue-500', 'bg-blue-50');
document.getElementById('scope-tenant').classList.remove('border-green-500', 'bg-green-50');
if (scope === 'system') {
document.getElementById('scope-system').classList.add('border-blue-500', 'bg-blue-50');
document.getElementById('tenant-selector').classList.add('hidden');
selectedTenantId = null;
goToStep(2);
} else {
document.getElementById('scope-tenant').classList.add('border-green-500', 'bg-green-50');
document.getElementById('tenant-selector').classList.remove('hidden');
}
}
// 테넌트 선택 이벤트
document.getElementById('tenant_id')?.addEventListener('change', function(e) {
selectedTenantId = e.target.value;
if (selectedTenantId) {
goToStep(2);
}
});
// 템플릿 선택
function selectTemplate(type, key) {
selectedTemplateType = type;
selectedTemplateKey = key;
// 템플릿 설정 적용
if (type && key && templates[type]?.[key]) {
const template = templates[type][key];
applyTemplateSettings(template);
} else {
// 빈 템플릿 - 기본값
resetFormToDefaults();
}
goToStep(3);
}
// 템플릿 설정 적용
function applyTemplateSettings(template) {
document.getElementById('board_code').value = template.board_type || '';
document.getElementById('board_name').value = template.name || '';
document.getElementById('board_type').value = template.board_type || '';
document.getElementById('description').value = template.description || '';
document.getElementById('editor_type').value = template.editor_type || 'wysiwyg';
document.getElementById('allow_files').checked = template.allow_files !== false;
document.getElementById('max_file_count').value = template.max_file_count || 5;
document.getElementById('max_file_size').value = template.max_file_size || 20480;
// 템플릿 기본 필드 표시
if (template.default_fields?.length) {
document.getElementById('template-fields-section').classList.remove('hidden');
const fieldsList = document.getElementById('template-fields-list');
fieldsList.innerHTML = template.default_fields.map(field => `
<div class="flex items-center justify-between bg-gray-50 rounded px-4 py-2">
<div>
<span class="font-medium text-gray-900">${field.name}</span>
<span class="text-gray-500 text-sm ml-2">(${field.field_key})</span>
</div>
<div class="flex items-center space-x-4 text-sm">
<span class="text-gray-600">${field.field_type}</span>
${field.is_required ? '<span class="text-red-500">필수</span>' : '<span class="text-gray-400">선택</span>'}
</div>
</div>
`).join('');
} else {
document.getElementById('template-fields-section').classList.add('hidden');
}
}
// 폼 기본값으로 리셋
function resetFormToDefaults() {
document.getElementById('board_code').value = '';
document.getElementById('board_name').value = '';
document.getElementById('board_type').value = '';
document.getElementById('description').value = '';
document.getElementById('editor_type').value = 'wysiwyg';
document.getElementById('allow_files').checked = true;
document.getElementById('max_file_count').value = 5;
document.getElementById('max_file_size').value = 20480;
document.getElementById('template-fields-section').classList.add('hidden');
}
// 스텝 이동
function goToStep(step) {
currentStep = step;
// 모든 스텝 숨기기
document.getElementById('step1').classList.add('hidden');
document.getElementById('step2').classList.add('hidden');
document.getElementById('step3').classList.add('hidden');
// 현재 스텝 표시
document.getElementById(`step${step}`).classList.remove('hidden');
// 인디케이터 업데이트
updateStepIndicators(step);
// 스텝별 추가 처리
if (step === 2) {
// 템플릿 표시
document.getElementById('system-templates').classList.toggle('hidden', selectedScope !== 'system');
document.getElementById('tenant-templates').classList.toggle('hidden', selectedScope !== 'tenant');
}
if (step === 3) {
// 선택 정보 라벨 업데이트
const scopeLabel = selectedScope === 'system' ? '시스템 게시판' : '테넌트 게시판';
document.getElementById('selected-scope-label').textContent = scopeLabel;
if (selectedScope === 'tenant' && selectedTenantId) {
const tenant = tenants.find(t => t.id == selectedTenantId);
document.getElementById('selected-tenant-label').textContent = tenant ? `(${tenant.company_name})` : '';
} else {
document.getElementById('selected-tenant-label').textContent = '';
}
const templateLabel = selectedTemplateKey
? templates[selectedTemplateType]?.[selectedTemplateKey]?.name || selectedTemplateKey
: '빈 템플릿';
document.getElementById('selected-template-label').textContent = templateLabel;
}
}
// 스텝 인디케이터 업데이트
function updateStepIndicators(currentStep) {
for (let i = 1; i <= 3; i++) {
const indicator = document.getElementById(`step${i}-indicator`);
const circle = indicator.querySelector('div');
const text = indicator.querySelector('span');
const line = document.getElementById(`step-line-${i-1}`);
if (i < currentStep) {
// 완료된 스텝
circle.className = 'w-8 h-8 rounded-full bg-green-600 text-white flex items-center justify-center text-sm font-medium';
text.className = 'ml-2 text-sm font-medium text-green-600';
if (line) line.className = 'w-16 h-0.5 bg-green-600';
} else if (i === currentStep) {
// 현재 스텝
circle.className = 'w-8 h-8 rounded-full bg-blue-600 text-white flex items-center justify-center text-sm font-medium';
text.className = 'ml-2 text-sm font-medium text-blue-600';
if (line) line.className = 'w-16 h-0.5 bg-blue-600';
} else {
// 미완료 스텝
circle.className = 'w-8 h-8 rounded-full bg-gray-300 text-gray-600 flex items-center justify-center text-sm font-medium';
text.className = 'ml-2 text-sm font-medium text-gray-500';
if (line) line.className = 'w-16 h-0.5 bg-gray-300';
}
}
}
// 폼 제출
document.getElementById('boardForm').addEventListener('submit', async function(e) {
e.preventDefault();
const form = e.target;
const errorDiv = document.getElementById('errorMessage');
const payload = {
board_code: form.board_code.value,
name: form.name.value,
board_type: form.board_type.value || null,
description: form.description.value || null,
editor_type: form.editor_type.value,
allow_files: form.allow_files.checked,
max_file_count: parseInt(form.max_file_count.value),
max_file_size: parseInt(form.max_file_size.value),
is_active: form.is_active.checked,
template_type: selectedTemplateType,
template_key: selectedTemplateKey,
tenant_id: selectedScope === 'tenant' ? selectedTenantId : null
};
try {
const response = await fetch('/api/admin/boards/create-from-template', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}',
'Accept': 'application/json'
},
body: JSON.stringify(payload)
});
const data = await response.json();
if (response.ok && data.success) {
createdBoardId = data.data.id;
showSuccessModal(data.data);
} else {
errorDiv.textContent = data.message || '게시판 생성에 실패했습니다.';
errorDiv.classList.remove('hidden');
}
} catch (error) {
errorDiv.textContent = '서버 오류가 발생했습니다.';
errorDiv.classList.remove('hidden');
}
});
// 성공 모달 표시
function showSuccessModal(board) {
document.getElementById('success-board-name').textContent = `"${board.name}" 게시판`;
document.getElementById('success-board-code').textContent = board.board_code;
document.querySelectorAll('.board-code-placeholder').forEach(el => {
el.textContent = board.board_code;
});
document.getElementById('successModal').classList.remove('hidden');
document.getElementById('successModal').classList.add('flex');
}
// 성공 모달 닫기
function closeSuccessModal() {
document.getElementById('successModal').classList.add('hidden');
document.getElementById('successModal').classList.remove('flex');
// 커스텀 필드 추가를 위해 편집 페이지로 이동
if (createdBoardId) {
window.location.href = `/boards/${createdBoardId}/edit`;
}
}
</script>
@endpush