diff --git a/backend/src/main/java/com/but/rebloom/domain/channel/controller/ChannelController.java b/backend/src/main/java/com/but/rebloom/domain/channel/controller/ChannelController.java index 90383070..4305afc1 100644 --- a/backend/src/main/java/com/but/rebloom/domain/channel/controller/ChannelController.java +++ b/backend/src/main/java/com/but/rebloom/domain/channel/controller/ChannelController.java @@ -76,4 +76,4 @@ public ResponseEntity> rejectChannel(@PathVariable Long channe channelUseCase.rejectChannel(channelId); return ResponseEntity.ok(ApiResponse.success()); } -} +} \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index e9e148b9..6f2d5bb6 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -61,7 +61,6 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -1466,7 +1465,6 @@ "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -1552,7 +1550,6 @@ "integrity": "sha512-6m1I5RmHBGTnUGS113G04DMu3CpSdxCAU/UvtjNWL4Nuf3MW9tQhiJqRlHzChIkhy6kZSAQmc+I1bcGjE3yNKg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.3", "@typescript-eslint/types": "8.46.3", @@ -1805,7 +1802,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1924,7 +1920,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.19", "caniuse-lite": "^1.0.30001751", @@ -2192,7 +2187,6 @@ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -3032,7 +3026,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -3042,7 +3035,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -3366,7 +3358,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -3425,7 +3416,6 @@ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3505,7 +3495,6 @@ "integrity": "sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -3599,7 +3588,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, diff --git a/frontend/src/Pages/channel/channelApproval.tsx b/frontend/src/Pages/channel/channelApproval.tsx index 232ca97e..52c18c46 100644 --- a/frontend/src/Pages/channel/channelApproval.tsx +++ b/frontend/src/Pages/channel/channelApproval.tsx @@ -11,6 +11,7 @@ import { import RebloomLogo from '../../assets/images/Rebloom-logo.svg'; import CloseIcon from '../../assets/images/close.svg'; import { useNavigate } from 'react-router-dom'; +import { channelApi } from '../../api/channel'; interface ChannelRequest { channelId: number; @@ -27,25 +28,16 @@ function ChannelApproval() { const [requests, setRequests] = useState([]); const [expandedId, setExpandedId] = useState(null); - const token = localStorage.getItem('token'); - useEffect(() => { const fetchPendingChannels = async () => { try { - const res = await fetch('/channel/admin/find/pending', { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${token}`, - }, - }); - const data = await res.json(); - if (data.success && data.data?.responses) { - const channels: ChannelRequest[] = data.data.responses.map((item: any) => ({ + const response = await channelApi.getPendingChannels(); + if (response.success && response.data?.responses) { + const channels: ChannelRequest[] = response.data.responses.map((item) => ({ channelId: item.channelId, channelName: item.channelName, channelIntro: item.channelIntro, - channelDescription: item.channelDescription, + channelDescription: '', userId: item.userId, requestType: 'CREATE', channelStatus: 'WAITING', @@ -58,7 +50,7 @@ function ChannelApproval() { }; fetchPendingChannels(); - }, [token]); + }, []); const toggleExpand = (id: number) => { setExpandedId(prev => (prev === id ? null : id)); @@ -66,15 +58,8 @@ function ChannelApproval() { const handleApprove = async (channelId: number) => { try { - const res = await fetch(`/channel/admin/approve/${channelId}`, { - method: 'PATCH', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${token}`, - }, - }); - const data = await res.json(); - if (data.success) { + const response = await channelApi.approveChannel(channelId); + if (response.success) { setRequests(prev => prev.map(req => req.channelId === channelId ? { ...req, channelStatus: 'APPROVED' } : req @@ -88,15 +73,8 @@ function ChannelApproval() { const handleReject = async (channelId: number) => { try { - const res = await fetch(`/channel/admin/reject/${channelId}`, { - method: 'PATCH', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${token}`, - }, - }); - const data = await res.json(); - if (data.success) { + const response = await channelApi.rejectChannel(channelId); + if (response.success) { setRequests(prev => prev.map(req => req.channelId === channelId ? { ...req, channelStatus: 'REJECTED' } : req diff --git a/frontend/src/Pages/channel/channeljoin.tsx b/frontend/src/Pages/channel/channeljoin.tsx index 0b5efb39..e3436d5e 100644 --- a/frontend/src/Pages/channel/channeljoin.tsx +++ b/frontend/src/Pages/channel/channeljoin.tsx @@ -9,18 +9,25 @@ import { Input, TextArea, SubmitButton, + Select, } from './cj'; import RebloomLogo from '../../assets/images/Rebloom-logo.svg'; import CloseIcon from '../../assets/images/close.svg'; import { useNavigate } from 'react-router-dom'; -import { useState } from 'react'; +import { useState, useEffect } from 'react'; +import { channelApi } from '../../api/channel'; +import { hobbyApi } from '../../api/hobby'; +import type { GetHobbyResponse } from '../../types/hobby'; +import type { CreateChannelRequest } from '../../types/channel'; interface ChannelForm { channelTitle: string; channelIntro: string; channelDescription: string; - userEmail: string; + channelLinkedHobby1: number; + channelLinkedHobby2?: number; + channelLinkedHobby3?: number; } function ChannelJoin() { @@ -29,41 +36,83 @@ function ChannelJoin() { channelTitle: '', channelIntro: '', channelDescription: '', - userEmail: '', + channelLinkedHobby1: 0, + channelLinkedHobby2: undefined, + channelLinkedHobby3: undefined, }); const [status, setStatus] = useState(''); const [loading, setLoading] = useState(false); + const [hobbies, setHobbies] = useState([]); - const handleChange = (e: React.ChangeEvent) => { + useEffect(() => { + const fetchHobbies = async () => { + try { + const response = await hobbyApi.findAllHobbies(); + if (response.success && response.data) { + setHobbies(response.data.hobbies); + } + } catch (error) { + console.error('취미 목록 조회 실패:', error); + } + }; + fetchHobbies(); + }, []); + + const handleChange = (e: React.ChangeEvent) => { const { name, value } = e.target; - setForm({ ...form, [name]: value }); + if (name.startsWith('channelLinkedHobby')) { + setForm({ ...form, [name]: value ? Number(value) : undefined }); + } else { + setForm({ ...form, [name]: value }); + } }; const handleSubmit = async () => { - if (!form.channelTitle || !form.userEmail) { - setStatus('채널 이름과 이메일은 필수입니다.'); + if (!form.channelTitle || !form.channelIntro || !form.channelDescription) { + setStatus('채널 이름, 소개, 상세 설명은 필수입니다.'); return; } - setLoading(true); - - const localChannels = JSON.parse(localStorage.getItem('channels') || '[]'); + if (!form.channelLinkedHobby1) { + setStatus('최소 하나의 취미를 선택해주세요.'); + return; + } - const newChannel = { - channelId: Date.now(), // 임시 ID - channelName: form.channelTitle, - channelIntro: form.channelIntro, - channelDescription: form.channelDescription, - userId: form.userEmail, - requestType: 'CREATE', // 승인 목록에 표시 - channelStatus: 'PENDING', - }; + setLoading(true); + setStatus(''); - localStorage.setItem('channels', JSON.stringify([...localChannels, newChannel])); - // setStatus(`✅ 채널 생성 완료: ${form.channelTitle}`); - setForm({ channelTitle: '', channelIntro: '', channelDescription: '', userEmail: '' }); + try { + const request: CreateChannelRequest = { + channelTitle: form.channelTitle, + channelIntro: form.channelIntro, + channelDescription: form.channelDescription, + channelLinkedHobby1: form.channelLinkedHobby1, + channelLinkedHobby2: form.channelLinkedHobby2, + channelLinkedHobby3: form.channelLinkedHobby3, + }; - setLoading(false); + const response = await channelApi.createChannel(request); + + if (response.success) { + setStatus('✅ 채널이 생성되었습니다!'); + setForm({ + channelTitle: '', + channelIntro: '', + channelDescription: '', + channelLinkedHobby1: 0, + channelLinkedHobby2: undefined, + channelLinkedHobby3: undefined, + }); + setTimeout(() => navigate('/community'), 2000); + } else { + setStatus('채널 생성에 실패했습니다. 다시 시도해주세요.'); + } + } catch (error: any) { + console.error('채널 생성 오류:', error); + setStatus(error.message || '채널 생성 중 오류가 발생했습니다.'); + } finally { + setLoading(false); + } }; return ( @@ -79,36 +128,68 @@ function ChannelJoin() {

채널 생성

- - -