Skip to content

Commit

Permalink
Merge pull request #236 from urinaner/feature/235
Browse files Browse the repository at this point in the history
[FE] [FEAT] 세미나 CRUD 페이지 구현
  • Loading branch information
pillow12360 authored Jan 3, 2025
2 parents 3d819be + afd02a7 commit 630e45f
Show file tree
Hide file tree
Showing 17 changed files with 1,852 additions and 94 deletions.
22 changes: 22 additions & 0 deletions frontend/src/AppContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ import mainImage from './assets/images/main_picture.svg';
import Curriculum from './pages/Undergraduate/Curriculum/Curriculum';

import NotFound from './components/Notfound/NotFound';
import SeminarList from './pages/Seminar/SeminarList';
import SeminarDetail from './pages/Seminar/SeminarDetail';
import SeminarCreate from './pages/Seminar/SeminarCreate';
import SeminarEdit from './pages/Seminar/SeminarEdit';

interface PageTransitionProps {
children: React.ReactNode;
Expand Down Expand Up @@ -212,6 +216,8 @@ function AppContent() {
path="/seminar-rooms/reservation"
element={<Reservation />}
/>
<Route path="/news/seminar" element={<SeminarList />} />
<Route path="/news/seminar/:id" element={<SeminarDetail />} />
<Route path="/news/thesis" element={<ThesisList />} />
<Route path="/news/thesis/:id" element={<ThesisDetail />} />

Expand Down Expand Up @@ -264,6 +270,22 @@ function AppContent() {
</ProtectedRoute>
}
/>
<Route
path="/news/seminar/create"
element={
<ProtectedRoute requireAuth requireAdmin>
<SeminarCreate />
</ProtectedRoute>
}
/>
<Route
path="/news/seminar/edit/:id"
element={
<ProtectedRoute requireAuth requireAdmin>
<SeminarEdit />
</ProtectedRoute>
}
/>
<Route path="*" element={<NotFound />} />
</Routes>
</PageTransition>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const Header: React.FC = () => {
}}
>
<HeaderInner>
<Logo />
<Logo compact={isMobile} />
<HeaderNav>
{isMobile ? (
<MobileMenu />
Expand Down
22 changes: 22 additions & 0 deletions frontend/src/components/Header/HeaderStyle.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import styled from 'styled-components';
import { motion } from 'framer-motion';

export const HeaderContainer = styled(motion.header)<{
$isDropdownOpen: boolean;
}>`
Expand Down Expand Up @@ -28,6 +29,7 @@ export const HeaderInner = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
@media (max-width: 768px) {
padding: 0 1rem;
Expand Down Expand Up @@ -69,3 +71,23 @@ export const HeaderActions = styled.div`
gap: 1rem;
color: white;
`;

export const MobileTitle = styled.h1`
display: none;
font-size: 1.1rem;
font-weight: 600;
color: white;
margin: 0;
@media (max-width: 768px) {
display: block;
position: absolute;
left: 50%;
transform: translateX(-50%);
width: 100%;
text-align: center;
white-space: nowrap;
pointer-events: none; // 텍스트 뒤의 요소들과 상호작용 가능하도록
z-index: 1; // 다른 요소들 위에 표시
}
`;
4 changes: 4 additions & 0 deletions frontend/src/components/Header/Logo/Logo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
LogoTitle,
LogoWrapper,
Department,
MobileLogoTitle,
} from './LogoStyle';

interface LogoProps {
Expand All @@ -27,6 +28,9 @@ const Logo: React.FC<LogoProps> = ({ compact = false }) => {
<Department>바이오융합공학전공</Department>
</LogoTitle>
)}
{compact && (
<MobileLogoTitle>세종대학교 바이오융합공학전공</MobileLogoTitle>
)}
</LogoWrapper>
</LogoLink>
</LogoContainer>
Expand Down
27 changes: 19 additions & 8 deletions frontend/src/components/Header/Logo/LogoStyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const LogoWrapper = styled.div`
flex-direction: row;
align-items: center;
gap: 16px;
position: relative;
`;

export const LogoImage = styled.div`
Expand Down Expand Up @@ -58,14 +59,6 @@ export const LogoTitle = styled.div`
letter-spacing: 0.02em;
text-transform: uppercase;
opacity: 0.95;
@media (max-width: 768px) {
font-size: 0.8rem;
}
@media (max-width: 480px) {
display: none;
}
`;

export const Department = styled.span`
Expand All @@ -78,3 +71,21 @@ export const Department = styled.span`
font-size: 1.1rem;
}
`;

export const MobileLogoTitle = styled.div`
position: fixed;
left: 50%;
transform: translateX(-50%);
white-space: nowrap;
font-size: 1.1rem;
font-weight: 600;
color: white;
width: 100%;
text-align: center;
pointer-events: none;
z-index: 1;
@media (min-width: 769px) {
display: none;
}
`;
39 changes: 38 additions & 1 deletion frontend/src/components/Header/MobileMenu/MobileMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import React, { useState } from 'react';
import React, { useState, useContext } from 'react';
import { Menu, ChevronDown } from 'lucide-react';
import { useNavigate } from 'react-router-dom';
import { AuthContext } from '../../../context/AuthContext';
import { ReactComponent as UserIcon } from '../../../assets/images/user-icon.svg';
import { navItems } from '../constants';
import {
MobileMenuButton,
Expand All @@ -8,18 +11,36 @@ import {
MobileMenuTitle,
MobileSubMenu,
MobileSubMenuItem,
MobileAuthSection,
MobileLoginButton,
MobileUserProfile,
MobileLogoutButton,
} from './MobileMenuStyle';

const MobileMenu = () => {
const [isOpen, setIsOpen] = useState(false);
const [activeIndices, setActiveIndices] = useState<number[]>([]);
const navigate = useNavigate();
const auth = useContext(AuthContext);

const toggleSubmenu = (index: number) => {
setActiveIndices((prev) =>
prev.includes(index) ? prev.filter((i) => i !== index) : [...prev, index],
);
};

const handleSignIn = () => {
navigate('/signin');
setIsOpen(false);
};

const handleSignOut = async () => {
if (auth?.signout) {
await auth.signout();
setIsOpen(false);
}
};

return (
<>
<MobileMenuButton onClick={() => setIsOpen(!isOpen)}>
Expand Down Expand Up @@ -50,6 +71,22 @@ const MobileMenu = () => {
</MobileSubMenu>
</MobileMenuItem>
))}

<MobileAuthSection>
{auth?.isAuthenticated ? (
<>
<MobileUserProfile>
<UserIcon />
<span>{auth.user}</span>
</MobileUserProfile>
<MobileLogoutButton onClick={handleSignOut}>
로그아웃
</MobileLogoutButton>
</>
) : (
<MobileLoginButton onClick={handleSignIn}>로그인</MobileLoginButton>
)}
</MobileAuthSection>
</MobileMenuWrapper>
</>
);
Expand Down
47 changes: 47 additions & 0 deletions frontend/src/components/Header/MobileMenu/MobileMenuStyle.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import styled from 'styled-components';
import { Link } from 'react-router-dom';

export const MobileMenuButton = styled.button`
padding: 0.5rem;
background: none;
Expand All @@ -21,6 +22,7 @@ export const MobileMenuButton = styled.button`
stroke-width: 2px;
}
`;

export const MobileMenuWrapper = styled.div<{ isOpen: boolean }>`
display: none;
Expand Down Expand Up @@ -86,3 +88,48 @@ export const MobileSubMenuItem = styled(Link)`
background-color: ${({ theme }) => theme.colors.primary.crimsonDark};
}
`;

export const MobileAuthSection = styled.div`
padding: 1rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
margin-top: auto;
`;

export const MobileLoginButton = styled.button`
width: 100%;
padding: 0.8rem;
background-color: transparent;
border: 1px solid white;
color: white;
border-radius: 4px;
font-size: 1rem;
cursor: pointer;
transition: all 0.2s ease;
&:hover {
background-color: rgba(255, 255, 255, 0.1);
}
`;

export const MobileUserProfile = styled.div`
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.8rem;
color: white;
svg {
width: 24px;
height: 24px;
}
`;

export const MobileLogoutButton = styled(MobileLoginButton)`
margin-top: 0.5rem;
color: #ff6b6b;
border-color: #ff6b6b;
&:hover {
background-color: rgba(255, 107, 107, 0.1);
}
`;
4 changes: 2 additions & 2 deletions frontend/src/components/Header/Navigation/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,14 @@ const navigationItems: NavigationItem[] = [
path: '/news/noticeboard',
menuItems: [
{ name: '공지사항', path: '/news/noticeboard' },
{ name: '세미나', path: '/news' },
{ name: '세미나', path: '/news/seminar' },
{ name: '연구논문', path: '/news/thesis' },
],
},
{
title: '⏱ 서비스',
path: '/seminar-rooms/reservation',
menuItems: [{ name: '예약 페이지', path: '/seminar-rooms/reservation' }],
menuItems: [{ name: '세미나실 예약', path: '/seminar-rooms/reservation' }],
},
];

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Header/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const navItems = [
path: '/news/noticeboard',
menuItems: [
{ name: '공지사항', path: '/news/noticeboard' },
{ name: '세미나', path: '/news' },
{ name: '세미나', path: '/news/seminar' },
{ name: '연구 논문', path: '/news/thesis' },
],
},
Expand Down
30 changes: 30 additions & 0 deletions frontend/src/config/apiConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ interface ThesisEndpoint {
getFormData: (thesisReqDto: ThesisReqDto, file?: File | null) => FormData;
}

export interface SeminarDto {
name: string;
writer: string;
place: string;
startDate: string;
endDate: string;
speaker: string;
company: string;
}

export const apiEndpoints = {
thesis: {
list: `${API_URL}/api/thesis`,
Expand Down Expand Up @@ -236,6 +246,26 @@ export const apiEndpoints = {
getByCategory: (category: string, page: number, size: number) =>
`${API_URL}/api/board/category/${category}?page=${page}&size=${size}`,
},

seminar: {
list: `${API_URL}/api/seminar`,
listWithPage: (page: number, size: number, sortDirection?: string) => {
const params = new URLSearchParams({
page: page.toString(),
size: size.toString(),
});
if (sortDirection) {
params.append('sortDirection', sortDirection);
}
return `${API_URL}/api/seminar?${params.toString()}`;
},
get: (seminarId: string | number) => `${API_URL}/api/seminar/${seminarId}`,
create: `${API_URL}/api/seminar`,
update: (seminarId: string | number) =>
`${API_URL}/api/seminar/${seminarId}`,
delete: (seminarId: string | number) =>
`${API_URL}/api/seminar/${seminarId}`,
},
};

export default API_URL;
6 changes: 6 additions & 0 deletions frontend/src/constants/pageContents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ export const PAGE_CONTENTS = {
image: newsImg,
path: '/news/thesis',
},
seminar: {
title: '세미나',
description: '바이오융합공학전공 세미나 조회',
image: newsImg,
path: '/news/seminar',
},
},
},
seminar: {
Expand Down
Loading

0 comments on commit 630e45f

Please sign in to comment.