-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: 클라이언트의 헤더에서 '문제 해결 경진대회'를 '문제해결 경진대회'로 수정 #232 * refactor: 대회 등록 페이지 리펙토링 #232 * feat: 해커톤 관리 타입 정의 #232 * feat: 드롭 박스 focus out 되었을 때, 닫히도록 수정 #232 * feat: 해커톤 검색창 컴포넌트 구현 #232 * feat: 해커톤 테이블 컴포넌트 등록 #232 * feat: 해커톤 관리 페이지 구현 #232 * refactor: import 구문 순서 정리 #232 * feat: 해커톤 목록 페이지에 페이지네이션 추가 #232 * design: table의 최소 height 고정 #232 * chore: 사용하지 않는 HackathonDTO에서 속성 삭제 #232
- Loading branch information
Showing
10 changed files
with
377 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,150 @@ | ||
export default function ContestListPage() { | ||
import { headers } from 'next/headers'; | ||
|
||
import PageTitle from '@/components/common/PageTitle'; | ||
import AdminHackathonManageTable from '@/components/ui/admin/hackathon/AdminHackathonManageTable'; | ||
import AdminHackathonSearchBox from '@/components/ui/admin/hackathon/AdminHackathonSearchBox'; | ||
|
||
import { HackathonManagePageableDto } from '@/types/common.dto'; | ||
import { getAuthFromCookie } from '@/lib/utils/auth'; | ||
import { AuthSliceState } from '@/store/auth.slice'; | ||
import Pagination from '@/components/common/Pagination'; | ||
|
||
export interface HackathonListPageProps { | ||
searchParams?: { [key: string]: string | undefined }; | ||
} | ||
|
||
export default function HackathonListPage({ searchParams }: HackathonListPageProps) { | ||
const headersList = headers(); | ||
const pathname = headersList.get('x-pathname') || ''; | ||
|
||
const auth: AuthSliceState = getAuthFromCookie(); | ||
|
||
const page = searchParams?.page ? parseInt(searchParams.page, 10) : 1; | ||
const keyword = searchParams?.keyword ? searchParams.keyword : ''; | ||
|
||
// TODO: api 연결 | ||
const hackathonInfos: HackathonManagePageableDto = { | ||
totalPages: hackathonInfoContent.length / 8, | ||
totalElements: hackathonInfoContent.length, | ||
size: 8, | ||
number: page, | ||
pageable: '{"page":1,"size":8}', | ||
numberOfElements: 0, | ||
empty: false, | ||
content: hackathonInfoContent, | ||
}; | ||
|
||
return ( | ||
<div className="w-full"> | ||
<div className="flex h-40 w-full items-center justify-center text-comment">개발 중인 기능입니다.</div> | ||
<PageTitle title="해커톤 목록" /> | ||
<AdminHackathonSearchBox count={hackathonInfos.content.length} page={page} keyword={keyword} status={1} /> | ||
<AdminHackathonManageTable hackathonInfos={hackathonInfos.content.slice(8 * (page - 1), 8 * page)} /> | ||
<Pagination | ||
currentPage={page} | ||
totalItems={hackathonInfos.totalElements} | ||
pathname={pathname} | ||
pageSize={8} | ||
query={JSON.stringify(searchParams)} | ||
/> | ||
</div> | ||
); | ||
} | ||
|
||
const hackathonInfoContent = [ | ||
{ | ||
id: 1, | ||
title: '제 8회 2024-2 PNU SW+X 문제해결 경진대회', | ||
hackathonStartDate: '2024-09-01', | ||
hackathonEndDate: '2024-12-01', | ||
teamCode: '1234', | ||
isActive: false, | ||
}, | ||
{ | ||
id: 2, | ||
title: '제 7회 2024-2 PNU SW+X 문제해결 경진대회', | ||
hackathonStartDate: '2024-09-01', | ||
hackathonEndDate: '2024-12-01', | ||
teamCode: '1234', | ||
isActive: false, | ||
}, | ||
{ | ||
id: 3, | ||
title: '제 7회 2024-2 PNU SW+X 문제해결 경진대회', | ||
hackathonStartDate: '2024-09-01', | ||
hackathonEndDate: '2024-12-01', | ||
teamCode: '1234', | ||
isActive: false, | ||
}, | ||
{ | ||
id: 4, | ||
title: '제 7회 2024-2 PNU SW+X 문제해결 경진대회', | ||
hackathonStartDate: '2024-09-01', | ||
hackathonEndDate: '2024-12-01', | ||
teamCode: '1234', | ||
isActive: false, | ||
}, | ||
{ | ||
id: 5, | ||
title: '제 7회 2024-2 PNU SW+X 문제해결 경진대회', | ||
hackathonStartDate: '2024-09-01', | ||
hackathonEndDate: '2024-12-01', | ||
teamCode: '1234', | ||
isActive: false, | ||
}, | ||
{ | ||
id: 6, | ||
title: '제 7회 2024-2 PNU SW+X 문제해결 경진대회', | ||
hackathonStartDate: '2024-09-01', | ||
hackathonEndDate: '2024-12-01', | ||
teamCode: '1234', | ||
isActive: false, | ||
}, | ||
{ | ||
id: 7, | ||
title: '제 7회 2024-2 PNU SW+X 문제해결 경진대회', | ||
hackathonStartDate: '2024-09-01', | ||
hackathonEndDate: '2024-12-01', | ||
teamCode: '1234', | ||
isActive: false, | ||
}, | ||
{ | ||
id: 8, | ||
title: '제 7회 2024-2 PNU SW+X 문제해결 경진대회', | ||
hackathonStartDate: '2024-09-01', | ||
hackathonEndDate: '2024-12-01', | ||
teamCode: '1234', | ||
isActive: false, | ||
}, | ||
{ | ||
id: 9, | ||
title: '제 7회 2024-2 PNU SW+X 문제해결 경진대회', | ||
hackathonStartDate: '2024-09-01', | ||
hackathonEndDate: '2024-12-01', | ||
teamCode: '1234', | ||
isActive: false, | ||
}, | ||
{ | ||
id: 10, | ||
title: '제 7회 2024-2 PNU SW+X 문제해결 경진대회', | ||
hackathonStartDate: '2024-09-01', | ||
hackathonEndDate: '2024-12-01', | ||
teamCode: '1234', | ||
isActive: false, | ||
}, | ||
{ | ||
id: 11, | ||
title: '제 7회 2024-2 PNU SW+X 문제해결 경진대회', | ||
hackathonStartDate: '2024-09-01', | ||
hackathonEndDate: '2024-12-01', | ||
teamCode: '1234', | ||
isActive: false, | ||
}, | ||
{ | ||
id: 12, | ||
title: '제 7회 2024-2 PNU SW+X 문제해결 경진대회', | ||
hackathonStartDate: '2024-09-01', | ||
hackathonEndDate: '2024-12-01', | ||
teamCode: '1234', | ||
isActive: false, | ||
}, | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
117 changes: 117 additions & 0 deletions
117
frontend/src/components/ui/admin/hackathon/AdminHackathonManageTable.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
'use client'; | ||
|
||
import { useEffect, useState } from 'react'; | ||
import { useRouter } from 'next/navigation'; | ||
|
||
import { MdDeleteForever } from '@react-icons/all-files/md/MdDeleteForever'; | ||
import { MdFileDownload } from '@react-icons/all-files/md/MdFileDownload'; | ||
import { MdEdit } from '@react-icons/all-files/md/MdEdit'; | ||
|
||
import { HackathonManageDto } from '@/types/common.dto'; | ||
|
||
export interface AdminHackathonManageTableProps { | ||
hackathonInfos: HackathonManageDto[]; | ||
} | ||
|
||
export default function AdminHackathonManageTable({ hackathonInfos }: AdminHackathonManageTableProps) { | ||
const [hackathons, setHackathon] = useState<HackathonManageDto[]>([]); | ||
const router = useRouter(); | ||
|
||
function handleActiveStatusClick(hackathonId: number) { | ||
console.log(hackathonId); | ||
// TODO: API 연결 | ||
setHackathon((prev) => | ||
prev.map((hackathon) => | ||
hackathon.id === hackathonId ? { ...hackathon, isActive: !hackathon.isActive } : hackathon, | ||
), | ||
); | ||
} | ||
|
||
function handleEditContestClick(hackathonId: number) { | ||
router.push(`/admin/hackathon/${hackathonId}`); | ||
} | ||
|
||
function handleDeleteContestClick(selectedHackathon: HackathonManageDto) { | ||
const willDelete = window.confirm(selectedHackathon.title + '을/를 정말 삭제하겠습니까?'); | ||
if (!willDelete) return; | ||
|
||
// TODO: API 연결 | ||
setHackathon((prev) => | ||
prev.map((hackathon) => (hackathon.id === selectedHackathon.id ? null : hackathon)).filter((v) => v !== null), | ||
); | ||
} | ||
|
||
function handleDownloadVoteResultClick(hackathonId: number) { | ||
// TODO: API 연결 | ||
console.log(hackathonId, 'clicked'); | ||
} | ||
|
||
useEffect(() => { | ||
setHackathon(hackathonInfos); | ||
}, [hackathonInfos, setHackathon]); | ||
|
||
return ( | ||
<div className="min-h-[380px]"> | ||
<table className="my-4 w-full table-fixed text-center text-sm [&_*]:cursor-default"> | ||
<thead className="border-y-2 border-admin-border [&_th]:px-2 [&_th]:py-4"> | ||
<tr> | ||
<th className="w-20">대회ID</th> | ||
<th>대회명</th> | ||
<th className="w-60">대회기간</th> | ||
<th className="w-32">활성 여부</th> | ||
<th className="w-20">대회 수정</th> | ||
<th className="w-20">대회 삭제</th> | ||
<th className="w-20">투표 결과</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{hackathons.map((hackathon) => { | ||
return ( | ||
<tr key={hackathon.id} className="border-b border-admin-border"> | ||
<td>{hackathon.id}</td> | ||
<td className="py-4 pl-3 text-left font-semibold">{hackathon.title}</td> | ||
<td className="text-sm"> | ||
{hackathon.hackathonStartDate} ~ {hackathon.hackathonEndDate} | ||
</td> | ||
<td> | ||
<button onClick={() => handleActiveStatusClick(hackathon.id)} className="relative mt-1"> | ||
<div | ||
className={`h-6 w-12 rounded-lg transition-colors ${hackathon.isActive ? 'bg-green-400' : 'bg-gray-300'}`} | ||
/> | ||
<div | ||
className={`absolute top-1 h-4 w-4 rounded-full bg-white transition-all ${hackathon.isActive ? 'left-7' : 'left-1'}`} | ||
/> | ||
</button> | ||
</td> | ||
<td> | ||
<button | ||
onClick={() => handleEditContestClick(hackathon.id)} | ||
className="rounded-sm px-3 py-2 text-lg text-admin-secondary-main hover:bg-gray-200 hover:text-admin-primary-main" | ||
> | ||
<MdEdit /> | ||
</button> | ||
</td> | ||
<td> | ||
<button | ||
onClick={() => handleDeleteContestClick(hackathon)} | ||
className="rounded-sm px-3 py-2 text-lg text-admin-secondary-main hover:bg-gray-200 hover:text-admin-semantic-error-light" | ||
> | ||
<MdDeleteForever /> | ||
</button> | ||
</td> | ||
<td> | ||
<button | ||
onClick={() => handleDownloadVoteResultClick(hackathon.id)} | ||
className="rounded-sm px-3 py-2 text-lg text-admin-secondary-main hover:bg-gray-200 hover:text-admin-secondary-dark" | ||
> | ||
<MdFileDownload /> | ||
</button> | ||
</td> | ||
</tr> | ||
); | ||
})} | ||
</tbody> | ||
</table> | ||
</div> | ||
); | ||
} |
Oops, something went wrong.