Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--sidebar: oklch(0.985 0 0);
--sidebar-width: 280px;
--sidebar-height: calc(100vh - 80px);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
Expand Down Expand Up @@ -220,3 +222,15 @@
left: 100%;
}
}

.w-sidebar {
width: var(--sidebar-width);
}

.h-sidebar {
height: var(--sidebar-height);
}

.min-w-sidebar {
min-width: var(--sidebar-width);
}
2 changes: 1 addition & 1 deletion src/layout/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const Header = () => {

return (
<header className="border-lightGray lg:h-header md:h-header xs:h-8 z-20 flex w-full min-w-[350px] items-center justify-between border-b bg-white px-4 py-2 sm:h-20">
<div className="mx-auto flex w-full items-center justify-between gap-4 px-4 sm:px-8 sm:pt-4 md:gap-8 lg:gap-16 lg:px-16">
<div className="mx-auto flex w-full items-center justify-between gap-4 px-4 sm:pt-4 sm:pr-8 md:gap-8 lg:gap-16 lg:pr-16">
<Link to="/" className="sm:-translate-y-1.5 md:-translate-y-[7px] lg:-translate-y-2">
<img
className="w-auto max-sm:hidden sm:h-8 md:h-9 lg:h-10"
Expand Down
4 changes: 2 additions & 2 deletions src/layout/HeaderMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Link } from 'react-router-dom';

const HeaderMenu = () => {
return (
<div className="flex-1 font-semibold md:text-lg lg:text-xl">
<div className="flex-1 font-semibold md:text-lg lg:hidden lg:text-xl">
<HistoryMenu />
</div>
);
Expand All @@ -21,7 +21,7 @@ const HistoryMenu = () => {
히스토리
</button>
{isOpen && data && (
<ul className="border-subGreen absolute z-50 w-fit border-2 bg-white text-base font-normal text-nowrap">
<ul className="border-subGreen absolute z-9999 w-fit border-2 bg-white text-base font-normal text-nowrap">
{data?.map((item) => (
<li key={item.contestId}>
<Link
Expand Down
21 changes: 19 additions & 2 deletions src/layout/MainLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { useLocation } from 'react-router-dom';

import Header from './Header';
import Footer from './Footer';
import Sidebar from './Sidebar';
import FullContainer from './FullContainer';
import { Toaster } from '@components/Toaster';
import useAuthInit from 'hooks/useAuthInit';
Expand All @@ -8,13 +11,27 @@ import useScrollToTop from 'hooks/useScrollToTop';
const MainLayout = () => {
const { isAuthInit } = useAuthInit();
useScrollToTop();
const location = useLocation();
const hideSidebar =
location.pathname.startsWith('/signin') ||
location.pathname.startsWith('/signup') ||
location.pathname.startsWith('/oauth') ||
location.pathname.startsWith('/find');

if (!isAuthInit) return <></>;
return (
<>
<div className="flex min-h-screen flex-col">
<Header />
<div>
<FullContainer />
<div className="flex">
{!hideSidebar && (
<div className="self-start">
<Sidebar />
</div>
)}
<div className="flex-1">
<FullContainer />
</div>
</div>
<Footer />
</div>
Expand Down
38 changes: 37 additions & 1 deletion src/layout/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,41 @@
import useContests from 'hooks/useContests';
import { Link, useParams } from 'react-router-dom';

const Sidebar = () => {
return <div className="h-sidebar w-sidebar min-w-sidebar z-10"></div>;
return (
<div className="w-sidebar min-w-sidebar border-lightGray z-10 hidden border-r border-b bg-white lg:block">
<SidebarHistoryMenu />
</div>
);
};

const SidebarHistoryMenu = () => {
const { data } = useContests();
const { contestId } = useParams();

return (
<div className="w-full">
{data && (
<ul className="divide-lightGray divide-y">
{data?.map((item) => {
const isActive = contestId === item.contestId.toString();
return (
<li key={item.contestId}>
<Link
to={`/contest/${item.contestId}`}
className={`block truncate p-3 px-5 text-base text-nowrap transition-colors duration-200 ease-in ${
isActive ? 'bg-mainGreen text-white' : 'hover:text-mainGreen hover:bg-whiteGray'
}`}
>
{item.contestName}
</Link>
</li>
);
})}
</ul>
)}
</div>
);
};

export default Sidebar;
3 changes: 1 addition & 2 deletions src/pages/admin/OngoingContestsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,8 @@ const OngoingContestsTab = () => {
<div className="my-6 border-t border-gray-200"></div>
<ProjectSortToggle />
</div>
<ProjectSubmissionTable submissions={dashboardData} type="project" />
<ProjectSubmissionTable submissions={rankingData} type="vote" />
<VoteRate />
<ProjectSubmissionTable submissions={rankingData} type="vote" />
</div>
</>
);
Expand Down
67 changes: 66 additions & 1 deletion src/pages/admin/ProjectSubmissionTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,51 @@ const TableHead = ({ type }: { type: 'project' | 'vote' }) => {
);
};

const getTrackBackgroundColor = (teamName: string) => {
if (teamName.startsWith('A')) return 'bg-[#FCD63E]/50';
if (teamName.startsWith('B')) return 'bg-[#36D659]/50';
if (teamName.startsWith('C')) return 'bg-[#FFA962]/50';
if (teamName.startsWith('D')) return 'bg-[#6DB7FF]/50';
return 'bg-white';
};

const getTrack = (teamName: string): 'A' | 'B' | 'C' | 'D' | null => {
const firstChar = teamName.charAt(0).toUpperCase();
if (['A', 'B', 'C', 'D'].includes(firstChar)) {
return firstChar as 'A' | 'B' | 'C' | 'D';
}
return null;
};

const groupByTrack = (submissions: Submission[]) => {
const tracks: Record<'A' | 'B' | 'C' | 'D', Submission[]> = { A: [], B: [], C: [], D: [] };

submissions.forEach((item) => {
const track = getTrack(item.teamName);
if (track) {
tracks[track].push(item);
}s
});

// 각 분과별로 등수 재계산
Object.keys(tracks).forEach((track) => {
tracks[track as 'A' | 'B' | 'C' | 'D'] = tracks[track as 'A' | 'B' | 'C' | 'D'].map((item, idx) => ({
...item,
rank: idx + 1,
}));
});

return tracks;
};

const TableBody = ({ submissions, type }: Props) => {
return (
<tbody>
{submissions.map((item: Submission, idx: number) => (
<tr key={`${item.teamName}-${item.projectName}-${idx}`} className="">
<tr
key={`${item.teamName}-${item.projectName}-${idx}`}
className={type === 'vote' ? getTrackBackgroundColor(item.teamName) : ''}
>
{type === 'vote' && item.rank ? (
<td className="w-[8%] border-r border-b border-gray-300 p-2 py-3 pl-4 text-sm">{item.rank}</td>
) : (
Expand Down Expand Up @@ -71,6 +111,31 @@ const TableBody = ({ submissions, type }: Props) => {
};

const ProjectSubmissionTable = ({ submissions, type }: Props) => {
if (type === 'vote') {
const trackGroups = groupByTrack(submissions);
const trackNames: Record<'A' | 'B' | 'C' | 'D', string> = {
A: 'A분과',
B: 'B분과',
C: 'C분과',
D: 'D분과',
};

return (
<section className="mb-8 min-w-[350px]">
<h2 className="mb-8 text-2xl font-bold">좋아요 랭킹</h2>
{(['A', 'B', 'C', 'D'] as const).map((track) => (
<div key={track} className="mb-8 last:mb-0">
<h3 className="mb-4 text-xl font-semibold">{trackNames[track]}</h3>
<table className="w-full border-collapse bg-white">
<TableHead type={type} />
<TableBody submissions={trackGroups[track]} type={type} />
</table>
</div>
))}
</section>
);
}

return (
<section className="mb-8 min-w-[350px]">
<h2 className="mb-8 text-2xl font-bold">{type === 'project' ? '프로젝트 등록현황' : '좋아요 랭킹'}</h2>
Expand Down
4 changes: 2 additions & 2 deletions src/pages/main/Notice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ const Notice = () => {
'https://cse.pusan.ac.kr/cse/14655/subview.do';
return (
<div className="flex flex-col gap-4">
<a href={BANNER_URL} target="_blank" className="flex min-h-25">
<img src={banner} alt="대회 로고" className="flex cursor-pointer object-cover object-center" />
<a href={BANNER_URL} target="_blank" className="block w-full">
<img src={banner} alt="대회 로고" className="w-full h-auto cursor-pointer object-cover" />
{/* techweek: 기존 object-left */}
</a>
{isLoading && <NoticeListSkeleton />}
Expand Down
1 change: 1 addition & 0 deletions src/pages/project-viewer/LikeSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const LikeSection = ({ contestId, teamId, isLiked }: LikeSectionProps) => {
if (!isSignedIn) {
toast('로그인이 필요해요.');
navigate('/signin');
return;
}
if (likeMutation.isPending) return;
likeMutation.mutate(!isLiked);
Expand Down