From dbe98a1c1899e3246e5aef7c2909753f84ba7d18 Mon Sep 17 00:00:00 2001 From: zerith Date: Fri, 19 Dec 2025 09:14:09 +0900 Subject: [PATCH 01/10] =?UTF-8?q?fix=20:=20=ED=94=84=EB=A1=A0=ED=8A=B8=20?= =?UTF-8?q?=EB=B0=B0=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/Pages/channel/channelApproval.tsx | 42 +--- frontend/src/Pages/channel/channeljoin.tsx | 185 +++++++++++++----- frontend/src/Pages/communityPage/CP.tsx | 126 ++++++++---- .../src/Pages/communityPage/ChannelPage.tsx | 74 ++++--- frontend/src/types/channel.ts | 2 +- 5 files changed, 276 insertions(+), 153 deletions(-) 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..d0caa26b 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() {

채널 생성

- - -