diff --git a/src/api/idea.ts b/src/api/idea.ts index 3172424..c3dd7e6 100644 --- a/src/api/idea.ts +++ b/src/api/idea.ts @@ -37,3 +37,18 @@ export const fetchIdeaSubjects = async () => { const response = await instance.get('/api/v1/idea-subjects/briefs'); return response.data; }; + +// 아이디어 리스트 조회 API +export const fetchIdeas = async ( + page: number, + size: number, + generation: number, + subjectId?: number, + isActive?: boolean, + isBookmarked?: boolean, +) => { + const response = await instance.get( + `/api/v1/users/ideas/overviews?page=${page}&size=${size}&generation=${generation}&subject-id=${subjectId}&is-active=${isActive}&is-bookmarked=${isBookmarked}`, + ); + return response.data; +}; diff --git a/src/components/hackathon/ideaDetail/ideaDetailInfo/TeamInfo.tsx b/src/components/hackathon/ideaDetail/ideaDetailInfo/TeamInfo.tsx index 7d6d8f9..89dc711 100644 --- a/src/components/hackathon/ideaDetail/ideaDetailInfo/TeamInfo.tsx +++ b/src/components/hackathon/ideaDetail/ideaDetailInfo/TeamInfo.tsx @@ -28,8 +28,6 @@ interface TeamInfoProps { } export default function TeamInfo({ requirements }: TeamInfoProps) { - // 목업 데이터 - return (
diff --git a/src/components/hackathon/ideaList/filter/FilterDropdown.tsx b/src/components/hackathon/ideaList/filter/ActiveFilterDropdown.tsx similarity index 55% rename from src/components/hackathon/ideaList/filter/FilterDropdown.tsx rename to src/components/hackathon/ideaList/filter/ActiveFilterDropdown.tsx index 81428c0..736bb45 100644 --- a/src/components/hackathon/ideaList/filter/FilterDropdown.tsx +++ b/src/components/hackathon/ideaList/filter/ActiveFilterDropdown.tsx @@ -2,28 +2,35 @@ import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem, Text } from '@goo import { useState } from 'react'; import styles from './styles.module.scss'; -interface FilterDropDownProps { - options: string[]; // 필터 옵션 - selectedValue: string; // 현재 선택된 값 - onChange: (value: string) => void; +interface ActiveFilterDropdownProps { + options: { label: string; value: boolean | undefined }[]; + selectedValue: boolean | undefined; + onChange: (value: boolean | undefined) => void; disabled?: boolean; } -export default function FilterDropdown({ options, selectedValue, onChange, disabled = false }: FilterDropDownProps) { +export default function ActiveFilterDropdown({ + options, + selectedValue, + onChange, + disabled = false, +}: ActiveFilterDropdownProps) { const [isOpen, setIsOpen] = useState(false); const toggle = () => !disabled && setIsOpen(!isOpen); + const selectedLabel = options.find((option) => option.value === selectedValue)?.label || '전체'; + return ( - {selectedValue || '전체'} + {selectedLabel} - {options.map((option, index) => ( - onChange(option)}> - {option} + {options.map((option) => ( + onChange(option.value)}> + {option.label} ))} diff --git a/src/components/hackathon/ideaList/filter/SubjectFilterDropdown.tsx b/src/components/hackathon/ideaList/filter/SubjectFilterDropdown.tsx new file mode 100644 index 0000000..a56d3e5 --- /dev/null +++ b/src/components/hackathon/ideaList/filter/SubjectFilterDropdown.tsx @@ -0,0 +1,40 @@ +import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem, Text } from '@goorm-dev/vapor-components'; +import { useState } from 'react'; +import styles from './styles.module.scss'; + +interface SubjectFilterDropdownProps { + options: { id: number; name: string }[]; + selectedValue: number; + onChange: (value: number) => void; + disabled?: boolean; +} + +export default function SubjectFilterDropdown({ + options, + selectedValue, + onChange, + disabled = false, +}: SubjectFilterDropdownProps) { + const [isOpen, setIsOpen] = useState(false); + const toggle = () => !disabled && setIsOpen(!isOpen); + + // 현재 선택된 주제의 이름 찾기 (id로 매칭) + const selectedTopicName = options.find((topic) => topic.id === selectedValue)?.name || '전체'; + + return ( + + + + {selectedTopicName} + + + + {options.map((option) => ( + onChange(option.id)}> + {option.name} + + ))} + + + ); +} diff --git a/src/pages/hackathon/IdeaList/IdeaList.tsx b/src/pages/hackathon/IdeaList/IdeaList.tsx index a9111f7..6243a78 100644 --- a/src/pages/hackathon/IdeaList/IdeaList.tsx +++ b/src/pages/hackathon/IdeaList/IdeaList.tsx @@ -1,34 +1,73 @@ -import { BasicPagination, Button } from '@goorm-dev/vapor-components'; +import { BasicPagination, Button, Spinner } from '@goorm-dev/vapor-components'; import NoAccess from '../../../components/hackathon/ideaList/noAccess/NoAccess'; import styles from './styles.module.scss'; import { EditIcon } from '@goorm-dev/gds-icons'; import IdeaListItem from '../../../components/hackathon/ideaList/ideaItem/IdeaListItem'; -import { useState } from 'react'; -import FilterDropdown from '../../../components/hackathon/ideaList/filter/FilterDropdown'; -import mockIdeas from './mockIdea'; // 목업 데이터 - -const isTeamBuilding = true; -// 목업 데이터 +import { useEffect, useState } from 'react'; +import { fetchIdeas, fetchIdeaSubjects } from '../../../api/idea'; +import ActiveFilterDropdown from '../../../components/hackathon/ideaList/filter/ActiveFilterDropdown'; +import SubjectFilterDropdown from '../../../components/hackathon/ideaList/filter/SubjectFilterDropdown'; export default function IdeaList() { - const hackathonTopics = ['전체', '해커톤 주제1', '해커톤 주제2', '해커톤 주제3']; - const statusOptions = ['전체', '모집 중', '모집 완료']; + // 주제 가져오기 + const [hackathonTopics, setHackathonTopics] = useState<{ id: number; name: string }[]>([]); + const [ideaList, setIdeaList] = useState(null); + const { ideas, page_info } = ideaList; + const [loading, setLoading] = useState(false); + + // 필터링 + const [selectedTopic, setSelectedTopic] = useState(0); + const [selectedStatus, setSelectedStatus] = useState(undefined); const [currentPage, setCurrentPage] = useState(1); - const [selectedTopic, setSelectedTopic] = useState('전체'); - const [selectedStatus, setSelectedStatus] = useState('모집 중'); + const statusOptions = [ + { label: '전체', value: undefined }, + { label: '모집 중', value: true }, + { label: '모집 완료', value: false }, + ]; + + // 주제 가져오는 api + useEffect(() => { + const loadTopics = async () => { + try { + const response = await fetchIdeaSubjects(); + const activeTopics = response.data.idea_subjects + .filter((topic: { is_active: boolean }) => topic.is_active) + .map((topic: { id: number; name: string }) => ({ id: topic.id, name: topic.name })); + + setHackathonTopics([{ id: null, name: '전체' }, ...activeTopics]); // "전체" 옵션 추가 + } catch (error) { + console.error('Error fetching idea subjects:', error); + } + }; + + loadTopics(); + }, []); + + // 아이디어 가져오는 api + useEffect(() => { + const loadIdeas = async () => { + setLoading(true); + try { + const subjectId = selectedTopic === 0 ? undefined : selectedTopic; + const isActive = selectedStatus === true ? undefined : selectedStatus === false; + + const response = await fetchIdeas(currentPage, projectsPerPage, 4, subjectId, isActive); + setIdeaList(response.data); + } catch (error) { + console.error('Error fetching ideas:', error); + } finally { + setLoading(false); + } + }; - const filteredIdeas = mockIdeas.filter((idea) => { - return ( - (selectedTopic === '전체' || idea.topic === selectedTopic) && - (selectedStatus === '전체' || idea.status === selectedStatus) - ); - }); + loadIdeas(); + }, [selectedTopic, selectedStatus, currentPage]); + // 팀빌딩 기간인지 + const isTeamBuilding = true; // 한 페이지당 보여질 페이지 수 const projectsPerPage = 8; - // 전체 페이지 개수 - const pageLength = Math.ceil(filteredIdeas.length / projectsPerPage); const handlePageChange = (page: number) => { setCurrentPage(page); @@ -38,52 +77,58 @@ export default function IdeaList() {
{/* 추후 이미지 */}
-
- {/* 필터링, 아이디어 등록 버튼 */} -
-
- - -
- + {loading ? ( +
+
- {/* 팀 빌딩 기간인지에 따라 달라지는 모습 */} - {isTeamBuilding ? ( -
- {filteredIdeas.map((idea) => ( - + {/* 필터링, 아이디어 등록 버튼 */} +
+
+ - ))} - - handlePageChange(currentPage)} - className={styles.basicPagination} - /> + setSelectedStatus(value)} + disabled={!isTeamBuilding} + /> +
+
- ) : ( - - )} -
+ {/* 팀 빌딩 기간인지에 따라 달라지는 모습 */} + {isTeamBuilding ? ( +
+ {ideas?.map((idea: any) => ( + + ))} + + handlePageChange(currentPage)} + className={styles.basicPagination} + /> +
+ ) : ( + + )} +
+ )}
); }