Skip to content

Commit

Permalink
feat: 메인페이지 디자인
Browse files Browse the repository at this point in the history
  • Loading branch information
gogumalatte committed Nov 19, 2024
1 parent 019cbbe commit 53ff337
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 146 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"preview": "vite preview"
},
"dependencies": {
"@chakra-ui/icons": "^2.2.4",
"@chakra-ui/react": "2.8.2",
"axios": "^1.7.7",
"framer-motion": "11.0.3",
Expand Down
14 changes: 14 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

179 changes: 35 additions & 144 deletions src/Main/MainPage.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,15 @@
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import {
Box,
Button,
Heading,
VStack,
Center,
Text,
Spinner,
keyframes,
} from "@chakra-ui/react";
import api from "../api/interceptor"; // interceptor.ts에서 설정한 API 인스턴스 가져오기

type Content = {
id: number;
showId: string;
type: string;
title: string;
director: string;
cast: string;
country: string;
dateAdded: string;
releaseYear: string;
rating: string;
duration: string;
listedIn: string;
description: string;
};

const glitchKeyframes = keyframes`
0% { transform: translate(0, 0); }
20% { transform: translate(-5px, 5px); }
40% { transform: translate(5px, -5px); }
60% { transform: translate(-3px, 3px); }
80% { transform: translate(3px, -3px); }
100% { transform: translate(0, 0); }
`;
import { Box } from "@chakra-ui/react";
import RecommendedContents from "./components/RecommendedContents";
import RandomContents from "./components/RandomContents";
import SearchContents from "./components/SearchContents";
import NavBar from "./components/NavBar";
import UnauthorizedAccess from "./components/UnauthorizedAccess";

function MainPage(): JSX.Element {
const [isLoggedIn, setIsLoggedIn] = useState<boolean>(false);
const [randomContents, setRandomContents] = useState<Content[] | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [redirectCountdown, setRedirectCountdown] = useState<number>(10); // 10초 카운트다운
const [activeTab, setActiveTab] = useState<string>("추천 콘텐츠");
const navigate = useNavigate();

useEffect(() => {
Expand All @@ -50,123 +18,46 @@ function MainPage(): JSX.Element {

if (accessToken && refreshToken) {
setIsLoggedIn(true);
fetchRandomContents(); // 랜덤 콘텐츠를 가져옴
} else {
setIsLoggedIn(false);

// 10초 카운트다운 설정
const interval = setInterval(() => {
setRedirectCountdown((prev) => {
if (prev <= 1) {
clearInterval(interval); // 카운트다운 종료
navigate("/"); // 로그인 페이지로 이동
}
return prev - 1;
});
}, 1000);

return () => clearInterval(interval); // 컴포넌트 언마운트 시 interval 클리어
}
}, [navigate]);

const fetchRandomContents = async () => {
setIsLoading(true);
try {
const response = await api.get("/api/random/3"); // interceptor에서 Authorization 헤더 자동 추가
const contents = response.data.map(
(item: { content: Content }) => item.content
); // content만 추출
setRandomContents(contents);
} catch (error) {
console.error("랜덤 콘텐츠를 가져오는 중 오류 발생:", error);
} finally {
setIsLoading(false);
}
};

const handleLogout = (): void => {
localStorage.clear();
setIsLoggedIn(false);
navigate("/");
};

if (!isLoggedIn) {
return (
<Box
background={"black"}
height={"100vh"}
alignContent={"center"}
overflow={"hidden"}
>
<UnauthorizedAccess />
</Box>
);
}

return (
<Center
minHeight="100vh"
bg={isLoggedIn ? "gray.50" : "black"}
padding={4}
animation={isLoggedIn ? undefined : `${glitchKeyframes} 0.1s infinite`}
>
{isLoggedIn ? (
<VStack
spacing={6}
boxShadow="lg"
p={8}
rounded="md"
bg="white"
maxWidth="800px"
textAlign="center"
>
<Heading size="lg" color="teal.500">
환영합니다! 메인 페이지입니다.
</Heading>
<Button colorScheme="teal" onClick={handleLogout}>
로그아웃
</Button>
<Box width="100%">
<Heading size="md" mb={4}>
랜덤 콘텐츠
</Heading>
{isLoading ? (
<Center>
<Spinner size="lg" />
</Center>
) : randomContents ? (
randomContents.map((content) => (
<Box
key={content.id}
p={4}
mb={4}
boxShadow="md"
rounded="lg"
bg="gray.100"
textAlign="left"
>
<Text fontWeight="bold" mb={2}>
{content.title} ({content.releaseYear})
</Text>
<Text>감독: {content.director || "정보 없음"}</Text>
<Text>출연진: {content.cast || "정보 없음"}</Text>
<Text>국가: {content.country || "정보 없음"}</Text>
<Text>장르: {content.listedIn || "정보 없음"}</Text>
<Text fontSize="sm" mt={2}>
{content.description || "설명이 없습니다."}
</Text>
<Text fontSize="sm" color="gray.500" mt={2}>
추가 날짜: {content.dateAdded}
</Text>
</Box>
))
) : (
<Text>표시할 콘텐츠가 없습니다.</Text>
)}
</Box>
</VStack>
) : (
<Box
textAlign="center"
animation={`${glitchKeyframes} 0.1s infinite`}
color="white"
>
<Text fontSize={["lg", "xl", "3xl"]} fontWeight="bold" mb={2}>
🤬올바른 경로로 접속하지 않았습니다.
</Text>
<Text fontSize={["md", "lg", "2xl"]} color="red.500">
{redirectCountdown}초 뒤 당신은 사망합니다.
</Text>
</Box>
)}
</Center>
<Box>
{/* Navigation Bar */}
<NavBar
activeTab={activeTab}
setActiveTab={setActiveTab}
handleLogout={handleLogout}
/>

{/* Main Content */}
<Box marginTop="5rem" padding="2rem">
{activeTab === "추천 콘텐츠" && <RecommendedContents />}
{activeTab === "랜덤 콘텐츠" && <RandomContents />}
{activeTab === "검색" && <SearchContents />}
</Box>
</Box>
);
}

Expand Down
103 changes: 103 additions & 0 deletions src/Main/components/NavBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { useEffect, useState } from "react";
import {
Flex,
Text,
Menu,
MenuButton,
MenuList,
MenuItem,
IconButton,
} from "@chakra-ui/react";
import { HamburgerIcon } from "@chakra-ui/icons";
import api from "../../api/interceptor";

interface NavBarProps {
activeTab: string;
setActiveTab: (tab: string) => void;
handleLogout: () => void;
}

const NavBar = ({
activeTab,
setActiveTab,
handleLogout,
}: NavBarProps): JSX.Element => {
const [userName, setUserName] = useState<string>("");

useEffect(() => {
const fetchUserName = async () => {
try {
const response = await api.get("/api/members/info");
const name = response.data.name;
setUserName(name);
} catch (error) {
console.error("사용자 정보를 가져오는 중 오류 발생:", error);
}
};

fetchUserName();
}, []);

return (
<Flex
as="nav"
justify="space-between"
align="center"
padding="1rem"
bg="white"
boxShadow="md"
position="fixed"
top="0"
width="100%"
zIndex="1000"
>
<Flex align="center" gap="2rem">
<Text
cursor="pointer"
fontSize={["sm", "md", "lg"]}
fontWeight="bold"
color={activeTab === "추천 콘텐츠" ? "black" : "teal.500"}
borderBottom={activeTab === "추천 콘텐츠" ? "2px solid teal" : "none"}
onClick={() => setActiveTab("추천 콘텐츠")}
>
추천 콘텐츠
</Text>
<Text
cursor="pointer"
fontSize={["sm", "md", "lg"]}
fontWeight="bold"
color={activeTab === "랜덤 콘텐츠" ? "black" : "teal.500"}
borderBottom={activeTab === "랜덤 콘텐츠" ? "2px solid teal" : "none"}
onClick={() => setActiveTab("랜덤 콘텐츠")}
>
랜덤 콘텐츠
</Text>
<Text
cursor="pointer"
fontSize={["sm", "md", "lg"]}
fontWeight="bold"
color={activeTab === "검색" ? "black" : "teal.500"}
borderBottom={activeTab === "검색" ? "2px solid teal" : "none"}
onClick={() => setActiveTab("검색")}
>
검색
</Text>
</Flex>
<Menu>
<MenuButton
as={IconButton}
aria-label="Options"
icon={<HamburgerIcon />}
variant="outline"
/>
<MenuList>
{/* 사용자 이름 표시 */}
<MenuItem>{userName ? `${userName} 님` : "사용자 님"}</MenuItem>
<MenuItem onClick={handleLogout}>로그아웃</MenuItem>
</MenuList>
</Menu>
</Flex>
);
};

export default NavBar;
5 changes: 5 additions & 0 deletions src/Main/components/RandomContents.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function RandomContents(): JSX.Element {
return <div>랜덤 콘텐츠 컴포넌트 내용</div>;
}

export default RandomContents;
5 changes: 5 additions & 0 deletions src/Main/components/RecommendedContents.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function RecommendedContents(): JSX.Element {
return <div>추천 콘텐츠 컴포넌트 내용</div>;
}

export default RecommendedContents;
5 changes: 5 additions & 0 deletions src/Main/components/SearchContents.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function SearchContents(): JSX.Element {
return <div>검색 콘텐츠 컴포넌트 내용</div>;
}

export default SearchContents;
Loading

0 comments on commit 53ff337

Please sign in to comment.