- layouts/app.blade.php에 SweetAlert2 CDN 및 전역 헬퍼 함수 추가 - showToast(): 토스트 알림 (success, error, warning, info) - showConfirm(): 확인 대화상자 - showDeleteConfirm(): 삭제 확인 (경고 아이콘) - showPermanentDeleteConfirm(): 영구 삭제 확인 (빨간색 경고) - showSuccess(), showError(): 성공/에러 알림 - 변환된 파일 목록 (48개 Blade 파일): - menus/* (6개), boards/* (2개), posts/* (3개) - daily-logs/* (3개), project-management/* (6개) - dev-tools/flow-tester/* (6개) - quote-formulas/* (4개), permission-analyze/* (1개) - archived-records/* (1개), profile/* (1개) - roles/* (3개), permissions/* (3개) - departments/* (3개), tenants/* (3개), users/* (3개) - 주요 개선사항: - Tailwind CSS 테마와 일관된 디자인 - 비동기 콜백 패턴으로 리팩토링 - 삭제/복원/영구삭제 각각 다른 스타일 적용
612 lines
26 KiB
PHP
612 lines
26 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">📋 게시판 수정: {{ $board->name }}</h1>
|
|
<a href="{{ route('boards.index') }}" class="text-gray-600 hover:text-gray-900">
|
|
← 목록으로
|
|
</a>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-3 gap-6">
|
|
<!-- 좌측: 기본 정보 폼 -->
|
|
<div class="col-span-2">
|
|
<div class="bg-white rounded-lg shadow-sm p-6">
|
|
<form id="boardForm" class="space-y-6">
|
|
@csrf
|
|
@method('PUT')
|
|
|
|
<!-- 기본 정보 -->
|
|
<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"
|
|
value="{{ $board->board_code }}"
|
|
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>
|
|
</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"
|
|
value="{{ $board->name }}"
|
|
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"
|
|
value="{{ $board->board_type }}"
|
|
list="boardTypeList"
|
|
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
|
<datalist id="boardTypeList">
|
|
<option value="notice">공지사항</option>
|
|
<option value="qna">1:1 문의</option>
|
|
<option value="faq">FAQ</option>
|
|
<option value="free">자유게시판</option>
|
|
<option value="gallery">갤러리</option>
|
|
<option value="download">자료실</option>
|
|
</datalist>
|
|
</div>
|
|
|
|
<!-- 에디터 타입 -->
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">에디터 타입</label>
|
|
<select name="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" {{ $board->editor_type === 'wysiwyg' ? 'selected' : '' }}>WYSIWYG (위지윅)</option>
|
|
<option value="markdown" {{ $board->editor_type === 'markdown' ? 'selected' : '' }}>Markdown</option>
|
|
<option value="text" {{ $board->editor_type === 'text' ? 'selected' : '' }}>텍스트</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- 설명 -->
|
|
<div class="col-span-2">
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">설명</label>
|
|
<textarea name="description" rows="3"
|
|
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">{{ $board->description }}</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" value="1"
|
|
{{ $board->allow_files ? '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"
|
|
value="{{ $board->max_file_count }}"
|
|
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"
|
|
value="{{ $board->max_file_size }}"
|
|
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">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 활성 상태 -->
|
|
<div>
|
|
<label class="flex items-center">
|
|
<input type="checkbox" name="is_active" value="1"
|
|
{{ $board->is_active ? '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-end 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>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 우측: 커스텀 필드 관리 -->
|
|
<div class="col-span-1">
|
|
<div class="bg-white rounded-lg shadow-sm p-6">
|
|
<div class="flex justify-between items-center mb-4">
|
|
<h2 class="text-lg font-medium text-gray-900">커스텀 필드</h2>
|
|
<button type="button" onclick="openFieldModal()"
|
|
class="text-sm text-blue-600 hover:text-blue-800">
|
|
+ 필드 추가
|
|
</button>
|
|
</div>
|
|
|
|
<!-- 필드 목록 -->
|
|
<div id="fieldList" class="space-y-2">
|
|
@forelse($board->fields as $field)
|
|
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
|
<div>
|
|
<span class="font-medium text-sm">{{ $field->name }}</span>
|
|
<span class="text-xs text-gray-500 ml-2">({{ $field->field_key }})</span>
|
|
<span class="text-xs px-2 py-0.5 bg-gray-200 rounded ml-2">{{ $field->field_type }}</span>
|
|
@if($field->is_required)
|
|
<span class="text-xs text-red-500 ml-1">*</span>
|
|
@endif
|
|
</div>
|
|
<div class="flex space-x-2">
|
|
<button type="button" onclick="editField({{ $field->id }})"
|
|
class="text-indigo-600 hover:text-indigo-900 text-sm">수정</button>
|
|
<button type="button" onclick="deleteField({{ $field->id }}, '{{ $field->name }}')"
|
|
class="text-red-600 hover:text-red-900 text-sm">삭제</button>
|
|
</div>
|
|
</div>
|
|
@empty
|
|
<p class="text-sm text-gray-500 text-center py-4">등록된 커스텀 필드가 없습니다.</p>
|
|
@endforelse
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 게시판 정보 -->
|
|
<div class="bg-white rounded-lg shadow-sm p-6 mt-6">
|
|
<h2 class="text-lg font-medium text-gray-900 mb-4">게시판 정보</h2>
|
|
<dl class="space-y-2 text-sm">
|
|
<div class="flex justify-between">
|
|
<dt class="text-gray-500">생성일</dt>
|
|
<dd class="text-gray-900">{{ $board->created_at->format('Y-m-d H:i') }}</dd>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<dt class="text-gray-500">수정일</dt>
|
|
<dd class="text-gray-900">{{ $board->updated_at->format('Y-m-d H:i') }}</dd>
|
|
</div>
|
|
@if($board->trashed())
|
|
<div class="flex justify-between">
|
|
<dt class="text-gray-500">삭제일</dt>
|
|
<dd class="text-red-600">{{ $board->deleted_at->format('Y-m-d H:i') }}</dd>
|
|
</div>
|
|
@endif
|
|
</dl>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 필드 추가 모달 (다중) -->
|
|
<div id="fieldModal" class="hidden fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
|
<div class="bg-white rounded-lg shadow-xl p-6" style="width: 700px; max-width: 90vw;">
|
|
<h3 id="fieldModalTitle" class="text-lg font-medium text-gray-900 mb-4">필드 추가</h3>
|
|
<form id="fieldForm">
|
|
<input type="hidden" id="fieldId" name="field_id">
|
|
|
|
<!-- 헤더 -->
|
|
<div class="flex gap-2 mb-2 text-sm font-medium text-gray-700">
|
|
<div style="width: 160px;">필드명 <span class="text-red-500">*</span></div>
|
|
<div style="width: 160px;">필드 키 <span class="text-red-500">*</span></div>
|
|
<div style="width: 120px;">타입 <span class="text-red-500">*</span></div>
|
|
<div style="width: 50px;" class="text-center">필수</div>
|
|
<div style="width: 30px;"></div>
|
|
</div>
|
|
|
|
<!-- 필드 행 컨테이너 -->
|
|
<div id="fieldRowsContainer" class="space-y-2 max-h-80 overflow-y-auto">
|
|
<!-- 동적으로 행이 추가됨 -->
|
|
</div>
|
|
|
|
<!-- 행 추가 버튼 -->
|
|
<button type="button" onclick="addFieldRow()"
|
|
class="mt-3 w-full py-2 border-2 border-dashed border-gray-300 rounded-lg text-gray-500 hover:border-blue-400 hover:text-blue-600 transition flex items-center justify-center gap-1">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
|
|
</svg>
|
|
필드 추가
|
|
</button>
|
|
|
|
<p class="text-xs text-gray-500 mt-2">* 필드 키: 영문 소문자와 언더스코어만 사용</p>
|
|
|
|
<div class="flex justify-end space-x-3 pt-4 mt-4 border-t">
|
|
<button type="button" onclick="closeFieldModal()"
|
|
class="px-4 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50">
|
|
취소
|
|
</button>
|
|
<button type="submit"
|
|
class="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg">
|
|
저장
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 필드 수정 모달 (단일) -->
|
|
<div id="fieldEditModal" class="hidden fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
|
<div class="bg-white rounded-lg shadow-xl w-full max-w-md mx-4 p-6">
|
|
<h3 class="text-lg font-medium text-gray-900 mb-4">필드 수정</h3>
|
|
<form id="fieldEditForm" class="space-y-4">
|
|
<input type="hidden" id="editFieldId">
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">필드명 <span class="text-red-500">*</span></label>
|
|
<input type="text" id="editFieldName"
|
|
class="w-full px-3 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-1">필드 키 <span class="text-red-500">*</span></label>
|
|
<input type="text" id="editFieldKey"
|
|
pattern="[a-z_]+"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
required>
|
|
<p class="text-xs text-gray-500 mt-1">영문 소문자와 언더스코어만</p>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">필드 타입 <span class="text-red-500">*</span></label>
|
|
<select id="editFieldType"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
required>
|
|
<option value="text">텍스트</option>
|
|
<option value="textarea">긴 텍스트</option>
|
|
<option value="number">숫자</option>
|
|
<option value="date">날짜</option>
|
|
<option value="select">선택 (드롭다운)</option>
|
|
<option value="checkbox">체크박스</option>
|
|
<option value="radio">라디오</option>
|
|
<option value="file">파일</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="flex items-center">
|
|
<input type="checkbox" id="editFieldRequired"
|
|
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 class="flex justify-end space-x-3 pt-4">
|
|
<button type="button" onclick="closeFieldEditModal()"
|
|
class="px-4 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50">
|
|
취소
|
|
</button>
|
|
<button type="submit"
|
|
class="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg">
|
|
저장
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
@endsection
|
|
|
|
@push('scripts')
|
|
<script>
|
|
const boardId = {{ $board->id }};
|
|
let fieldRowIndex = 0;
|
|
|
|
// 폼 제출 (게시판 수정)
|
|
document.getElementById('boardForm').addEventListener('submit', async function(e) {
|
|
e.preventDefault();
|
|
|
|
const form = e.target;
|
|
const formData = new FormData(form);
|
|
const errorDiv = document.getElementById('errorMessage');
|
|
|
|
// 체크박스 처리
|
|
formData.set('allow_files', form.allow_files.checked ? '1' : '0');
|
|
formData.set('is_active', form.is_active.checked ? '1' : '0');
|
|
|
|
try {
|
|
const response = await fetch(`/api/admin/boards/${boardId}`, {
|
|
method: 'PUT',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
|
'Accept': 'application/json'
|
|
},
|
|
body: JSON.stringify(Object.fromEntries(formData))
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (response.ok && data.success) {
|
|
showToast('게시판이 수정되었습니다.', 'success');
|
|
window.location.reload();
|
|
} else {
|
|
errorDiv.textContent = data.message || '게시판 수정에 실패했습니다.';
|
|
errorDiv.classList.remove('hidden');
|
|
}
|
|
} catch (error) {
|
|
errorDiv.textContent = '서버 오류가 발생했습니다.';
|
|
errorDiv.classList.remove('hidden');
|
|
}
|
|
});
|
|
|
|
// 필드 행 HTML 생성
|
|
function createFieldRowHtml(index) {
|
|
return `
|
|
<div class="flex gap-2 items-center field-row" data-index="${index}">
|
|
<input type="text" name="fields[${index}][name]" placeholder="필드명"
|
|
style="width: 160px;"
|
|
class="px-2 py-1.5 text-sm border border-gray-300 rounded focus:outline-none focus:ring-1 focus:ring-blue-500"
|
|
required>
|
|
<input type="text" name="fields[${index}][field_key]" placeholder="field_key"
|
|
pattern="[a-z_]+"
|
|
style="width: 160px;"
|
|
class="px-2 py-1.5 text-sm border border-gray-300 rounded focus:outline-none focus:ring-1 focus:ring-blue-500"
|
|
required>
|
|
<select name="fields[${index}][field_type]"
|
|
style="width: 120px;"
|
|
class="px-2 py-1.5 text-sm border border-gray-300 rounded focus:outline-none focus:ring-1 focus:ring-blue-500"
|
|
required>
|
|
<option value="text">텍스트</option>
|
|
<option value="textarea">긴 텍스트</option>
|
|
<option value="number">숫자</option>
|
|
<option value="date">날짜</option>
|
|
<option value="select">선택</option>
|
|
<option value="checkbox">체크박스</option>
|
|
<option value="radio">라디오</option>
|
|
<option value="file">파일</option>
|
|
</select>
|
|
<div style="width: 50px;" class="text-center">
|
|
<input type="checkbox" name="fields[${index}][is_required]" value="1"
|
|
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500">
|
|
</div>
|
|
<div style="width: 30px;" class="text-center">
|
|
<button type="button" onclick="removeFieldRow(this)"
|
|
class="text-red-500 hover:text-red-700 p-1">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
// 필드 행 추가
|
|
function addFieldRow() {
|
|
const container = document.getElementById('fieldRowsContainer');
|
|
container.insertAdjacentHTML('beforeend', createFieldRowHtml(fieldRowIndex));
|
|
fieldRowIndex++;
|
|
}
|
|
|
|
// 필드 행 삭제
|
|
function removeFieldRow(button) {
|
|
const row = button.closest('.field-row');
|
|
const container = document.getElementById('fieldRowsContainer');
|
|
|
|
// 최소 1개 행은 유지
|
|
if (container.querySelectorAll('.field-row').length > 1) {
|
|
row.remove();
|
|
} else {
|
|
showToast('최소 1개의 필드는 필요합니다.', 'warning');
|
|
}
|
|
}
|
|
|
|
// 필드 모달 열기 (추가용)
|
|
function openFieldModal() {
|
|
const modal = document.getElementById('fieldModal');
|
|
const container = document.getElementById('fieldRowsContainer');
|
|
|
|
// 컨테이너 초기화
|
|
container.innerHTML = '';
|
|
fieldRowIndex = 0;
|
|
|
|
// 기본 1개 행 추가
|
|
addFieldRow();
|
|
|
|
modal.classList.remove('hidden');
|
|
}
|
|
|
|
// 필드 모달 닫기
|
|
function closeFieldModal() {
|
|
document.getElementById('fieldModal').classList.add('hidden');
|
|
}
|
|
|
|
// 필드 수정 모달 열기
|
|
async function editField(fieldId) {
|
|
try {
|
|
const response = await fetch(`/api/admin/boards/${boardId}/fields`);
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
const field = data.data.find(f => f.id === fieldId);
|
|
if (field) {
|
|
document.getElementById('editFieldId').value = field.id;
|
|
document.getElementById('editFieldName').value = field.name;
|
|
document.getElementById('editFieldKey').value = field.field_key;
|
|
document.getElementById('editFieldType').value = field.field_type;
|
|
document.getElementById('editFieldRequired').checked = field.is_required;
|
|
document.getElementById('fieldEditModal').classList.remove('hidden');
|
|
}
|
|
}
|
|
} catch (error) {
|
|
showToast('필드 정보를 불러오는데 실패했습니다.', 'error');
|
|
}
|
|
}
|
|
|
|
// 필드 수정 모달 닫기
|
|
function closeFieldEditModal() {
|
|
document.getElementById('fieldEditModal').classList.add('hidden');
|
|
}
|
|
|
|
// 필드 삭제
|
|
async function deleteField(fieldId, fieldName) {
|
|
showDeleteConfirm(fieldName + ' 필드', async () => {
|
|
try {
|
|
const response = await fetch(`/api/admin/boards/${boardId}/fields/${fieldId}`, {
|
|
method: 'DELETE',
|
|
headers: {
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
|
'Accept': 'application/json'
|
|
}
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
showToast('필드가 삭제되었습니다.', 'success');
|
|
window.location.reload();
|
|
} else {
|
|
showToast(data.message || '필드 삭제에 실패했습니다.', 'error');
|
|
}
|
|
} catch (error) {
|
|
showToast('서버 오류가 발생했습니다.', 'error');
|
|
}
|
|
});
|
|
}
|
|
|
|
// 필드 추가 폼 제출 (다중)
|
|
document.getElementById('fieldForm').addEventListener('submit', async function(e) {
|
|
e.preventDefault();
|
|
|
|
const rows = document.querySelectorAll('#fieldRowsContainer .field-row');
|
|
const fields = [];
|
|
|
|
rows.forEach(row => {
|
|
const name = row.querySelector('input[name$="[name]"]').value.trim();
|
|
const fieldKey = row.querySelector('input[name$="[field_key]"]').value.trim();
|
|
const fieldType = row.querySelector('select[name$="[field_type]"]').value;
|
|
const isRequired = row.querySelector('input[name$="[is_required]"]').checked;
|
|
|
|
if (name && fieldKey) {
|
|
fields.push({
|
|
name,
|
|
field_key: fieldKey,
|
|
field_type: fieldType,
|
|
is_required: isRequired
|
|
});
|
|
}
|
|
});
|
|
|
|
if (fields.length === 0) {
|
|
showToast('최소 1개의 필드를 입력해주세요.', 'warning');
|
|
return;
|
|
}
|
|
|
|
// 순차적으로 저장
|
|
let successCount = 0;
|
|
let errorMessages = [];
|
|
|
|
for (const field of fields) {
|
|
try {
|
|
const response = await fetch(`/api/admin/boards/${boardId}/fields`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
|
'Accept': 'application/json'
|
|
},
|
|
body: JSON.stringify(field)
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
successCount++;
|
|
} else {
|
|
errorMessages.push(`${field.name}: ${data.message || '저장 실패'}`);
|
|
}
|
|
} catch (error) {
|
|
errorMessages.push(`${field.name}: 서버 오류`);
|
|
}
|
|
}
|
|
|
|
if (errorMessages.length > 0) {
|
|
showToast(`${successCount}개 저장 완료, ${errorMessages.length}개 실패`, 'warning');
|
|
}
|
|
|
|
if (successCount > 0) {
|
|
closeFieldModal();
|
|
window.location.reload();
|
|
}
|
|
});
|
|
|
|
// 필드 수정 폼 제출 (단일)
|
|
document.getElementById('fieldEditForm').addEventListener('submit', async function(e) {
|
|
e.preventDefault();
|
|
|
|
const fieldId = document.getElementById('editFieldId').value;
|
|
const formData = {
|
|
name: document.getElementById('editFieldName').value,
|
|
field_key: document.getElementById('editFieldKey').value,
|
|
field_type: document.getElementById('editFieldType').value,
|
|
is_required: document.getElementById('editFieldRequired').checked
|
|
};
|
|
|
|
try {
|
|
const response = await fetch(`/api/admin/boards/${boardId}/fields/${fieldId}`, {
|
|
method: 'PUT',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
|
'Accept': 'application/json'
|
|
},
|
|
body: JSON.stringify(formData)
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
showToast('필드가 수정되었습니다.', 'success');
|
|
closeFieldEditModal();
|
|
window.location.reload();
|
|
} else {
|
|
showToast(data.message || '필드 저장에 실패했습니다.', 'error');
|
|
}
|
|
} catch (error) {
|
|
showToast('서버 오류가 발생했습니다.', 'error');
|
|
}
|
|
});
|
|
|
|
// 모달 외부 클릭 시 닫기
|
|
document.getElementById('fieldModal').addEventListener('click', function(e) {
|
|
if (e.target === this) {
|
|
closeFieldModal();
|
|
}
|
|
});
|
|
|
|
document.getElementById('fieldEditModal').addEventListener('click', function(e) {
|
|
if (e.target === this) {
|
|
closeFieldEditModal();
|
|
}
|
|
});
|
|
</script>
|
|
@endpush
|