diff --git a/src/index.css b/src/index.css index f63b84ee..e0138297 100644 --- a/src/index.css +++ b/src/index.css @@ -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); @@ -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); +} diff --git a/src/layout/Header.tsx b/src/layout/Header.tsx index d2284779..95740484 100644 --- a/src/layout/Header.tsx +++ b/src/layout/Header.tsx @@ -19,7 +19,7 @@ const Header = () => { return (
-
+
{ return ( -
+
); @@ -21,7 +21,7 @@ const HistoryMenu = () => { 히스토리 {isOpen && data && ( -
    +
      {data?.map((item) => (
    • { 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 ( <>
      -
      - +
      + {!hideSidebar && ( +
      + +
      + )} +
      + +
      diff --git a/src/layout/Sidebar.tsx b/src/layout/Sidebar.tsx index a03c4914..981eaa2a 100644 --- a/src/layout/Sidebar.tsx +++ b/src/layout/Sidebar.tsx @@ -1,5 +1,41 @@ +import useContests from 'hooks/useContests'; +import { Link, useParams } from 'react-router-dom'; + const Sidebar = () => { - return
      ; + return ( +
      + +
      + ); +}; + +const SidebarHistoryMenu = () => { + const { data } = useContests(); + const { contestId } = useParams(); + + return ( +
      + {data && ( +
        + {data?.map((item) => { + const isActive = contestId === item.contestId.toString(); + return ( +
      • + + {item.contestName} + +
      • + ); + })} +
      + )} +
      + ); }; export default Sidebar; diff --git a/src/pages/admin/OngoingContestsTab.tsx b/src/pages/admin/OngoingContestsTab.tsx index 717b194e..ab7bd163 100644 --- a/src/pages/admin/OngoingContestsTab.tsx +++ b/src/pages/admin/OngoingContestsTab.tsx @@ -40,9 +40,8 @@ const OngoingContestsTab = () => {
      - - +
); diff --git a/src/pages/admin/ProjectSubmissionTable.tsx b/src/pages/admin/ProjectSubmissionTable.tsx index c196be49..2c0ea8e1 100644 --- a/src/pages/admin/ProjectSubmissionTable.tsx +++ b/src/pages/admin/ProjectSubmissionTable.tsx @@ -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 ( {submissions.map((item: Submission, idx: number) => ( - + {type === 'vote' && item.rank ? ( {item.rank} ) : ( @@ -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 ( +
+

좋아요 랭킹

+ {(['A', 'B', 'C', 'D'] as const).map((track) => ( +
+

{trackNames[track]}

+ + + +
+
+ ))} +
+ ); + } + return (

{type === 'project' ? '프로젝트 등록현황' : '좋아요 랭킹'}

diff --git a/src/pages/main/Notice.tsx b/src/pages/main/Notice.tsx index ebb51970..6cfc5b2d 100644 --- a/src/pages/main/Notice.tsx +++ b/src/pages/main/Notice.tsx @@ -22,8 +22,8 @@ const Notice = () => { 'https://cse.pusan.ac.kr/cse/14655/subview.do'; return (
- - 대회 로고 + + 대회 로고 {/* techweek: 기존 object-left */} {isLoading && } diff --git a/src/pages/project-viewer/LikeSection.tsx b/src/pages/project-viewer/LikeSection.tsx index d7fbc96a..95ad7ed7 100644 --- a/src/pages/project-viewer/LikeSection.tsx +++ b/src/pages/project-viewer/LikeSection.tsx @@ -48,6 +48,7 @@ const LikeSection = ({ contestId, teamId, isLiked }: LikeSectionProps) => { if (!isSignedIn) { toast('로그인이 필요해요.'); navigate('/signin'); + return; } if (likeMutation.isPending) return; likeMutation.mutate(!isLiked);