Skip to content

Commit

Permalink
✨ Feat: 아이디어 리스트 api 연결 테스트 (#71)
Browse files Browse the repository at this point in the history
  • Loading branch information
GraceKim527 committed Feb 14, 2025
1 parent 73826e8 commit 24e5e29
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 73 deletions.
15 changes: 15 additions & 0 deletions src/api/idea.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ interface TeamInfoProps {
}

export default function TeamInfo({ requirements }: TeamInfoProps) {
// 목업 데이터

return (
<div className={styles.container}>
<Text as="h5" typography="heading5" color="gray-900">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<Dropdown direction="down" size="lg" isOpen={isOpen} toggle={toggle} disabled={disabled}>
<DropdownToggle caret color="select" className={styles.dropdown} disabled={disabled}>
<Text typography="body2" fontWeight="medium" className={styles.textSelect}>
{selectedValue || '전체'}
{selectedLabel}
</Text>
</DropdownToggle>
<DropdownMenu>
{options.map((option, index) => (
<DropdownItem key={index} onClick={() => onChange(option)}>
{option}
{options.map((option) => (
<DropdownItem key={option.label} onClick={() => onChange(option.value)}>
{option.label}
</DropdownItem>
))}
</DropdownMenu>
Expand Down
40 changes: 40 additions & 0 deletions src/components/hackathon/ideaList/filter/SubjectFilterDropdown.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Dropdown direction="down" size="lg" isOpen={isOpen} toggle={toggle} disabled={disabled}>
<DropdownToggle caret color="select" className={styles.dropdown} disabled={disabled}>
<Text typography="body2" fontWeight="medium" className={styles.textSelect}>
{selectedTopicName}
</Text>
</DropdownToggle>
<DropdownMenu>
{options.map((option) => (
<DropdownItem key={option.id} onClick={() => onChange(option.id)}>
{option.name}
</DropdownItem>
))}
</DropdownMenu>
</Dropdown>
);
}
169 changes: 107 additions & 62 deletions src/pages/hackathon/IdeaList/IdeaList.tsx
Original file line number Diff line number Diff line change
@@ -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<any>(null);
const { ideas, page_info } = ideaList;
const [loading, setLoading] = useState(false);

// 필터링
const [selectedTopic, setSelectedTopic] = useState<number>(0);
const [selectedStatus, setSelectedStatus] = useState<boolean | undefined>(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);
Expand All @@ -38,52 +77,58 @@ export default function IdeaList() {
<div className={styles.mainContainer}>
{/* 추후 이미지 */}
<div className={styles.imgBox}></div>
<div className={styles.listContainer}>
{/* 필터링, 아이디어 등록 버튼 */}
<div className={styles.listHeader}>
<div className={styles.dropdownWrap}>
<FilterDropdown
options={hackathonTopics}
selectedValue={selectedTopic}
onChange={setSelectedTopic}
disabled={!isTeamBuilding}
/>
<FilterDropdown
options={statusOptions}
selectedValue={selectedStatus}
onChange={setSelectedStatus}
disabled={!isTeamBuilding}
/>
</div>
<Button icon={EditIcon} active={false} size="lg" href="/hackathon/create/step1" className={styles.noneBtn}>
아이디어 등록
</Button>
{loading ? (
<div className={styles.loadingContainer}>
<Spinner />
</div>
{/* 팀 빌딩 기간인지에 따라 달라지는 모습 */}
{isTeamBuilding ? (
<div className={styles.ideaListWrap}>
{filteredIdeas.map((idea) => (
<IdeaListItem
key={idea.id}
topic={idea.topic}
title={idea.title}
description={idea.description}
status={idea.status}
) : (
<div className={styles.listContainer}>
{/* 필터링, 아이디어 등록 버튼 */}
<div className={styles.listHeader}>
<div className={styles.dropdownWrap}>
<SubjectFilterDropdown
options={hackathonTopics}
selectedValue={selectedTopic}
onChange={setSelectedTopic}
disabled={!isTeamBuilding}
/>
))}

<BasicPagination
page={currentPage}
limitCount={projectsPerPage}
pageCount={pageLength}
onPageChangeHandler={(currentPage: number) => handlePageChange(currentPage)}
className={styles.basicPagination}
/>
<ActiveFilterDropdown
options={statusOptions}
selectedValue={selectedStatus}
onChange={(value) => setSelectedStatus(value)}
disabled={!isTeamBuilding}
/>
</div>
<Button icon={EditIcon} active={false} size="lg" href="/hackathon/create/step1" className={styles.noneBtn}>
아이디어 등록
</Button>
</div>
) : (
<NoAccess />
)}
</div>
{/* 팀 빌딩 기간인지에 따라 달라지는 모습 */}
{isTeamBuilding ? (
<div className={styles.ideaListWrap}>
{ideas?.map((idea: any) => (
<IdeaListItem
key={idea.id}
topic={idea.subject}
title={idea.title}
description={idea.summary}
status={idea.is_active}
/>
))}

<BasicPagination
page={page_info?.current_page}
limitCount={projectsPerPage}
pageCount={page_info?.total_pages}
onPageChangeHandler={(currentPage: number) => handlePageChange(currentPage)}
className={styles.basicPagination}
/>
</div>
) : (
<NoAccess />
)}
</div>
)}
</div>
);
}

0 comments on commit 24e5e29

Please sign in to comment.