all()); \Log::info('Board request query:', $request->query()); $filters = $request->only(['search', 'board_type', 'is_active', 'trashed', 'sort_by', 'sort_direction']); \Log::info('Board filters:', $filters); $boards = $this->boardService->getAllBoards($filters, 15); // HTMX 요청이면 HTML 파셜 반환 if ($request->header('HX-Request')) { return view('boards.partials.table', compact('boards')); } // 일반 요청이면 JSON return response()->json([ 'success' => true, 'data' => $boards, ]); } /** * 게시판 통계 */ public function stats(): JsonResponse { $stats = $this->boardService->getBoardStats(); return response()->json([ 'success' => true, 'data' => $stats, ]); } /** * 게시판 상세 조회 */ public function show(int $id): JsonResponse { // systemOnly=false: 테넌트 게시판도 조회 가능 $board = $this->boardService->getBoardById($id, true, false); if (! $board) { return response()->json([ 'success' => false, 'message' => '게시판을 찾을 수 없습니다.', ], 404); } return response()->json([ 'success' => true, 'data' => $board, ]); } /** * 게시판 생성 */ public function store(Request $request): JsonResponse { $validated = $request->validate([ 'board_code' => 'required|string|max:50', 'name' => 'required|string|max:100', 'board_type' => 'nullable|string|max:50', 'description' => 'nullable|string|max:500', 'editor_type' => 'nullable|string|in:wysiwyg,markdown,text', 'allow_files' => 'nullable|boolean', 'max_file_count' => 'nullable|integer|min:0|max:100', 'max_file_size' => 'nullable|integer|min:0', 'extra_settings' => 'nullable|array', 'is_active' => 'nullable|boolean', ]); // 코드 중복 체크 if ($this->boardService->isCodeExists($validated['board_code'])) { return response()->json([ 'success' => false, 'message' => '이미 사용 중인 게시판 코드입니다.', ], 422); } $board = $this->boardService->createBoard($validated); return response()->json([ 'success' => true, 'message' => '게시판이 생성되었습니다.', 'data' => $board, ]); } /** * 게시판 수정 (시스템/테넌트 공통) */ public function update(Request $request, int $id): JsonResponse { $validated = $request->validate([ 'board_code' => 'required|string|max:50', 'name' => 'required|string|max:100', 'board_type' => 'nullable|string|max:50', 'description' => 'nullable|string|max:500', 'editor_type' => 'nullable|string|in:wysiwyg,markdown,text', 'allow_files' => 'nullable|boolean', 'max_file_count' => 'nullable|integer|min:0|max:100', 'max_file_size' => 'nullable|integer|min:0', 'extra_settings' => 'nullable|array', 'is_active' => 'nullable|boolean', ]); // 기존 게시판 조회 (systemOnly=false) $board = $this->boardService->getBoardById($id, true, false); if (! $board) { return response()->json([ 'success' => false, 'message' => '게시판을 찾을 수 없습니다.', ], 404); } // 코드 중복 체크 (자신 제외, 시스템/테넌트 구분) if ($board->is_system) { if ($this->boardService->isCodeExists($validated['board_code'], $id)) { return response()->json([ 'success' => false, 'message' => '이미 사용 중인 게시판 코드입니다.', ], 422); } } else { if ($this->boardService->isTenantCodeExists($validated['board_code'], $board->tenant_id, $id)) { return response()->json([ 'success' => false, 'message' => '해당 테넌트에서 이미 사용 중인 게시판 코드입니다.', ], 422); } } // 시스템/테넌트 공통 수정 메서드 사용 $this->boardService->updateAnyBoard($id, $validated); return response()->json([ 'success' => true, 'message' => '게시판이 수정되었습니다.', ]); } /** * 게시판 삭제 (Soft Delete) - 연결된 메뉴도 함께 삭제 */ public function destroy(int $id): JsonResponse { $this->boardService->deleteAnyBoard($id); return response()->json([ 'success' => true, 'message' => '게시판이 삭제되었습니다.', ]); } /** * 게시판 복원 - 연결된 메뉴도 함께 복원 */ public function restore(int $id): JsonResponse { $this->boardService->restoreAnyBoard($id); return response()->json([ 'success' => true, 'message' => '게시판이 복원되었습니다.', ]); } /** * 게시판 영구 삭제 - 연결된 메뉴도 함께 영구 삭제 */ public function forceDestroy(int $id): JsonResponse { $this->boardService->forceDeleteAnyBoard($id); return response()->json([ 'success' => true, 'message' => '게시판이 영구 삭제되었습니다.', ]); } /** * 게시판 활성/비활성 토글 */ public function toggleActive(int $id): JsonResponse { $board = $this->boardService->toggleActive($id); return response()->json([ 'success' => true, 'message' => $board->is_active ? '게시판이 활성화되었습니다.' : '게시판이 비활성화되었습니다.', 'data' => ['is_active' => $board->is_active], ]); } // ========================================================================= // 필드 관리 API // ========================================================================= /** * 게시판 필드 목록 */ public function fields(int $id): JsonResponse { $fields = $this->boardService->getBoardFields($id); return response()->json([ 'success' => true, 'data' => $fields, ]); } /** * 게시판 필드 추가 */ public function storeField(Request $request, int $id): JsonResponse { $validated = $request->validate([ 'name' => 'required|string|max:100', 'field_key' => 'required|string|max:50', 'field_type' => 'required|string|in:text,number,select,date,textarea,checkbox,radio,file', 'field_meta' => 'nullable|array', 'is_required' => 'nullable|boolean', 'sort_order' => 'nullable|integer', ]); $field = $this->boardService->addBoardField($id, $validated); return response()->json([ 'success' => true, 'message' => '필드가 추가되었습니다.', 'data' => $field, ]); } /** * 게시판 필드 수정 */ public function updateField(Request $request, int $id, int $fieldId): JsonResponse { $validated = $request->validate([ 'name' => 'required|string|max:100', 'field_key' => 'required|string|max:50', 'field_type' => 'required|string|in:text,number,select,date,textarea,checkbox,radio,file', 'field_meta' => 'nullable|array', 'is_required' => 'nullable|boolean', 'sort_order' => 'nullable|integer', ]); $this->boardService->updateBoardField($fieldId, $validated); return response()->json([ 'success' => true, 'message' => '필드가 수정되었습니다.', ]); } /** * 게시판 필드 삭제 */ public function destroyField(int $id, int $fieldId): JsonResponse { $this->boardService->deleteBoardField($fieldId); return response()->json([ 'success' => true, 'message' => '필드가 삭제되었습니다.', ]); } /** * 게시판 필드 순서 변경 */ public function reorderFields(Request $request, int $id): JsonResponse { $validated = $request->validate([ 'field_ids' => 'required|array', 'field_ids.*' => 'integer', ]); $this->boardService->reorderBoardFields($id, $validated['field_ids']); return response()->json([ 'success' => true, 'message' => '필드 순서가 변경되었습니다.', ]); } // ========================================================================= // 템플릿 API // ========================================================================= /** * 템플릿 목록 조회 */ public function templates(Request $request): JsonResponse { $type = $request->get('type', 'all'); // system, tenant, all $templates = $this->boardService->getTemplates($type); $baseFields = $this->boardService->getBaseFields(); return response()->json([ 'success' => true, 'data' => [ 'templates' => $templates, 'base_fields' => $baseFields, ], ]); } /** * 특정 템플릿 상세 조회 */ public function templateDetail(string $type, string $key): JsonResponse { $template = $this->boardService->getTemplate($type, $key); if (! $template) { return response()->json([ 'success' => false, 'message' => '템플릿을 찾을 수 없습니다.', ], 404); } return response()->json([ 'success' => true, 'data' => $template, ]); } /** * 템플릿 기반 게시판 생성 */ public function storeFromTemplate(Request $request): JsonResponse { $validated = $request->validate([ 'board_code' => 'required|string|max:50', 'name' => 'required|string|max:100', 'template_type' => 'nullable|string|in:system,tenant', 'template_key' => 'nullable|string|max:50', 'tenant_id' => 'nullable|integer|exists:tenants,id', 'board_type' => 'nullable|string|max:50', 'description' => 'nullable|string|max:500', 'editor_type' => 'nullable|string|in:wysiwyg,markdown,text', 'allow_files' => 'nullable|boolean', 'max_file_count' => 'nullable|integer|min:0|max:100', 'max_file_size' => 'nullable|integer|min:0', 'extra_settings' => 'nullable|array', 'is_active' => 'nullable|boolean', ]); // 코드 중복 체크 if (! empty($validated['tenant_id'])) { // 테넌트 게시판 if ($this->boardService->isTenantCodeExists($validated['board_code'], $validated['tenant_id'])) { return response()->json([ 'success' => false, 'message' => '해당 테넌트에서 이미 사용 중인 게시판 코드입니다.', ], 422); } } else { // 시스템 게시판 if ($this->boardService->isCodeExists($validated['board_code'])) { return response()->json([ 'success' => false, 'message' => '이미 사용 중인 게시판 코드입니다.', ], 422); } } $board = $this->boardService->createBoardFromTemplate( $validated, $validated['template_type'] ?? null, $validated['template_key'] ?? null ); return response()->json([ 'success' => true, 'message' => '게시판이 생성되었습니다.', 'data' => $board, ]); } // ========================================================================= // 테넌트 관련 API // ========================================================================= /** * 테넌트 목록 조회 (드롭다운용) */ public function tenants(): JsonResponse { $tenants = $this->boardService->getTenantList(); return response()->json([ 'success' => true, 'data' => $tenants, ]); } /** * 테넌트 게시판 코드 중복 체크 */ public function checkTenantCode(Request $request): JsonResponse { $validated = $request->validate([ 'code' => 'required|string|max:50', 'tenant_id' => 'required|integer|exists:tenants,id', 'exclude_id' => 'nullable|integer', ]); $exists = $this->boardService->isTenantCodeExists( $validated['code'], $validated['tenant_id'], $validated['exclude_id'] ?? null ); return response()->json([ 'success' => true, 'data' => ['exists' => $exists], ]); } }