diff --git a/README.md b/README.md index 9fa347d00..a38c93464 100644 --- a/README.md +++ b/README.md @@ -13,3 +13,24 @@ ## Week 2. 1단계 - 페이지 만들기 [🔗 link](https://edu.nextstep.camp/s/hazAC9xa/ls/QzV1ncxk) + +### 기능 목록 + +- [x] Header, Footer와 같은 공통 컴포넌트 만들기 (모든 페이지에서 Header와 Footer는 보여질 수 있게 적용) + +- 각 Url Path별로 페이지 만들기 (결과물과 스타일은 결과 링크 참고) + - [x] 메인 페이지 (/) + - [x] Theme 카테고리 섹션 추가 + - [x] heme 카테고리 Item 클릭 시 Theme 페이지(/theme/:themeKey)로 이동 + - [x] 실시간 급상승 선물랭킹 추가 + - [x] 필터 기능을 hooks를 사용하여 구현 (ex. 전체, 여성이, 남성이, 청소년이 / 받고 싶어한, 많이 선물한, 위시로 받은) + - [x] 상품 목록을 처음에는 6개만 보여지게 하기 + - [x] 더보기를 누르는 경우 상품 목록을 더 보여주기 (접기 버튼을 누르면 다시 6개만 보여지도록) + - [x] Theme 페이지(/theme/:themeKey) + - [x] Header 섹션 추가 + - [x] 재사용성을 고려하여 Header 섹션을 만들기 (themeKey에 따라 label, title, description, backgroundColor가 달라짐) + - [x] 상품 목록 섹션 추가 + - [x] 로그인 페이지(/login) + - [x] ID와 PW를 입력하면 로그인이 되도록 구현(ID와 PW는 아무 값을 입력해도 통과) + - [x] 나의 페이지(/my-account) + - [x] 로그아웃을 할 수있는 버튼을 추가 diff --git a/package-lock.json b/package-lock.json index 8f100a3a8..3ea093a5e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,8 @@ "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^6.24.1" }, "devDependencies": { "@craco/craco": "^7.1.0", @@ -6135,6 +6136,14 @@ "@babel/runtime": "^7.13.10" } }, + "node_modules/@remix-run/router": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.17.1.tgz", + "integrity": "sha512-mCOMec4BKd6BRGBZeSnGiIgwsbLGp3yhVqAD8H+PxiRNEHgDpZb8J1TnrSDlg97t0ySKMQJTHCWBCmBpSmkF6Q==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -28318,6 +28327,36 @@ } } }, + "node_modules/react-router": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.24.1.tgz", + "integrity": "sha512-PTXFXGK2pyXpHzVo3rR9H7ip4lSPZZc0bHG5CARmj65fTT6qG7sTngmb6lcYu1gf3y/8KxORoy9yn59pGpCnpg==", + "dependencies": { + "@remix-run/router": "1.17.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.24.1.tgz", + "integrity": "sha512-U19KtXqooqw967Vw0Qcn5cOvrX5Ejo9ORmOtJMzYWtCT4/WOfFLIZGGsVLxcd9UkBO0mSTZtXqhZBsWlHr7+Sg==", + "dependencies": { + "@remix-run/router": "1.17.1", + "react-router": "6.24.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/react-scripts": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", diff --git a/package.json b/package.json index 8a3e091c7..a50f5fb0e 100644 --- a/package.json +++ b/package.json @@ -25,11 +25,10 @@ "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^6.24.1" }, "devDependencies": { - "react-scripts": "5.0.1", - "typescript": "^4.9.5", "@craco/craco": "^7.1.0", "@emotion/eslint-plugin": "^11.11.0", "@storybook/addon-essentials": "^7.6.17", @@ -65,8 +64,10 @@ "eslint-plugin-storybook": "^0.8.0", "prettier": "^3.2.5", "prop-types": "^15.8.1", + "react-scripts": "5.0.1", "storybook": "^7.6.17", "tsconfig-paths-webpack-plugin": "^4.1.0", + "typescript": "^4.9.5", "webpack": "^5.90.3" }, "overrides": { diff --git a/src/App.tsx b/src/App.tsx index 1df5ce256..4a3a7704d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,18 +1,7 @@ -import styled from '@emotion/styled'; +import Routes from './Routes/index'; const App = () => { - const name = 'Josh Perez'; - - return ( -
- Hello, {name} -
- ); + return ; }; export default App; - -const Title = styled.h1` - font-size: 1.5em; - color: gray; -`; diff --git a/src/Routes/constants.ts b/src/Routes/constants.ts new file mode 100644 index 000000000..bad9faf26 --- /dev/null +++ b/src/Routes/constants.ts @@ -0,0 +1,6 @@ +export const ROUTE_PATHS = { + MAIN: '/', + THEME: '/theme/:themeKey', + LOGIN: '/login', + MY_ACCOUNT: '/my-account', +}; diff --git a/src/Routes/index.tsx b/src/Routes/index.tsx new file mode 100644 index 000000000..cfcb1c1fe --- /dev/null +++ b/src/Routes/index.tsx @@ -0,0 +1,45 @@ +import { BrowserRouter as Router, Route, Routes, useLocation } from 'react-router-dom'; + +import Footer from '@/components/Footer'; +import Header from '@/components/Header'; +import LoginPage from '@/pages/Login'; +import MainPage from '@/pages/Main'; +import MyAccountPage from '@/pages/MyAccount'; +import ThemePage from '@/pages/Theme'; + +import { ROUTE_PATHS } from './constants'; + +const RoutesComponent = () => { + return ( + + } /> + } /> + } /> + } /> + + ); +}; +const Layout = ({ children }: { children: React.ReactNode }) => { + const location = useLocation(); + const isLoginPage = location.pathname === ROUTE_PATHS.LOGIN; + + return ( +
+ {!isLoginPage &&
} + {children} + {!isLoginPage &&
} +
+ ); +}; + +const AppRoutes = () => { + return ( + + + + + + ); +}; + +export default AppRoutes; diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx new file mode 100644 index 000000000..324b549cf --- /dev/null +++ b/src/components/Footer.tsx @@ -0,0 +1,30 @@ +import styled from '@emotion/styled'; + +const FooterContainer = styled.footer` + padding: 28px 16px 88px; + width: 100%; + max-width: 100vw; + background-color: rgb(250, 250, 252); + display: flex; + justify-content: center; + + position: relative; +`; + +const FooterInner = styled.div` + display: flex; + justify-content: space-between; + width: 80%; +`; + +const Footer = () => { + return ( + + +

카카오톡 선물하기

+
+
+ ); +}; + +export default Footer; diff --git a/src/components/Header.tsx b/src/components/Header.tsx new file mode 100644 index 000000000..b270511db --- /dev/null +++ b/src/components/Header.tsx @@ -0,0 +1,80 @@ +import styled from '@emotion/styled'; +import { useEffect, useState } from 'react'; +import { Link } from 'react-router-dom'; + +import { Container } from '@/components/common/layouts/Container'; + +const HeaderInner = styled.div` + width: 100%; + max-width: 1024px; + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; +`; + +const Logo = styled.div` + display: flex; + align-items: center; + + img { + height: 54px; + } +`; + +const LoginContainer = styled.div` + display: flex; + align-items: center; +`; + +const Header = () => { + const [isLoggedIn, setIsLoggedIn] = useState(false); + + useEffect(() => { + const authToken = sessionStorage.getItem('authToken'); + setIsLoggedIn(!!authToken); // authToken이 존재하면 true, 아니면 false로 설정 + + // authToken 변경 시 sessionStorage 갱신 + window.addEventListener('storage', handleStorageChange); + + return () => { + window.removeEventListener('storage', handleStorageChange); + }; + }, []); + + const handleStorageChange = () => { + const authToken = sessionStorage.getItem('authToken'); + setIsLoggedIn(!!authToken); + window.location.reload(); + }; + + return ( + + + + + 선물하기 + + + {isLoggedIn ? ( + + +

내 계정

+
+ + ) : ( + + +

로그인

+
+ + )} +
+
+ ); +}; + +export default Header; diff --git a/src/components/MainPage/SelectFriend.tsx b/src/components/MainPage/SelectFriend.tsx new file mode 100644 index 000000000..6a12a91eb --- /dev/null +++ b/src/components/MainPage/SelectFriend.tsx @@ -0,0 +1,43 @@ +import styled from '@emotion/styled'; + +import { Image } from '@/components/common/Image/index'; +import { Container } from '@/components/common/layouts/Container'; + +const SelectFriendContainer = styled(Container)` + width: 100%; + display: flex; + justify-content: center; + align-items: center; + height: 150px; + background-color: rgb(250, 250, 252); +`; + +const SelectFriendInner = styled.section` + display: flex; + align-items: center; + width: 80%; + padding: 0px 16px; + + p { + padding-left: 16px; + font-size: 28px; + } +`; + +export const SelectFriend = () => ( + <> + + + 친구 선택 유도 아이콘 +

선물 받을 친구를 선택해주세요.

+
+
+ +); +export default SelectFriend; diff --git a/src/components/MainPage/ThemeGrid.tsx b/src/components/MainPage/ThemeGrid.tsx new file mode 100644 index 000000000..6bb382369 --- /dev/null +++ b/src/components/MainPage/ThemeGrid.tsx @@ -0,0 +1,192 @@ +import styled from '@emotion/styled'; +import { Link } from 'react-router-dom'; + +import { Image } from '@/components/common/Image/index'; + +import { Container } from '../common/layouts/Container'; +import { Grid } from '../common/layouts/Grid'; + +const ThemeItem = styled.section` + margin: 0 auto; + text-align: center; + padding: 25px 35px 24px; + max-width: 100%; +`; + +const ThemeImage = styled(Image)``; + +const Wrapper = styled(Grid)` + padding: 45px 0 23px 0; + margin: 0 + max-width: 100%; +`; + +const ThemeGrid = () => ( + + + + +

생일

+
+ + + + + +

졸업선물

+
+ + + + + +

스몰럭셔리

+
+ + + + + +

명품선물

+
+ + + + + +

결혼/집들이

+
+ + + + + +

따뜻한선물

+
+ + + + + +

가벼운선물

+
+ + + + + +

팬심저격

+
+ + + + + +

교환권

+
+ + + + + +

건강/비타민

+
+ + + + + +

과일/한우

+
+ + + + + +

출산/키즈

+
+ +
+); + +export const ThemeMenu = () => { + return ( + + + + ); +}; + +export default ThemeMenu; diff --git a/src/components/MainPage/TrendGiftRanking.tsx b/src/components/MainPage/TrendGiftRanking.tsx new file mode 100644 index 000000000..f7dff6a22 --- /dev/null +++ b/src/components/MainPage/TrendGiftRanking.tsx @@ -0,0 +1,160 @@ +import styled from '@emotion/styled'; +import { useState } from 'react'; + +import { Container } from '../common/layouts/Container'; +import { Grid } from '../common/layouts/Grid'; +import TrendGiftRankingGrid from './TrendGiftRankingGrid'; + +const Title = styled.h2` + color: rgb(0, 0, 0); + width: 100%; + text-align: center; + font-size: 35px; + line-height: 30px; + font-weight: 700; +`; + +const FiltersContainer = styled.div``; + +const Wrapper = styled(Grid)` + padding: 20px 0px 7px; + max-width: 100%; +`; + +const Icon = styled.div` + display: flex; + justify-content: center; + align-items: center; + width: 60px; + height: 60px; + border-radius: 24px; + font-size: 20px; + color: rgb(255, 255, 255); + font-weight: 700; + background-color: rgb(230, 241, 255); + transition: background-color 200ms ease 0s; +`; + +const FilterItem = styled.div` + margin: 0 auto; + text-align: center; + max-width: 100%; + cursor: pointer; + + &.active { + p { + color: rgb(70, 132, 233); + font-weight: 700; + } + + div { + background-color: rgb(70, 132, 233); + font-weight: 700; + color: white; + } + } +`; + +const FilterType = styled.div` + display: flex; + justify-content: center; + width: 100%; + height: 64px; + background-color: rgb(230, 241, 255); + margin: 0 auto; + border-radius: 12px; + border: 1px solid rgba(70, 132, 233, 0.1); + + button { + padding: 20px 30px; + color: rgba(70, 132, 233, 0.7); + font-weight: 400; + font-size: 22px; + cursor: pointer; + outline: none; + border: none; + background: none; + transition: color 0.2s ease-in-out; + } + + button.active { + color: rgba(70, 132, 233, 1); + font-weight: 700; + } +`; + +const TrendGiftRanking = () => { + const [genderFilter, setGenderFilter] = useState('ALL'); + const [rankingFilter, setRankingFilter] = useState('RECEIVING'); + + const handleGenderFilter = (filter: string) => { + setGenderFilter(filter); + }; + + const handleRankingFilter = (filter: string) => { + setRankingFilter(filter); + }; + + return ( + + 실시간 급상승 선물 랭킹 + + + handleGenderFilter('ALL')} + > + ALL +

전체

+
+ handleGenderFilter('FEMALE')} + > + 👩🏻‍🦳 +

여성이

+
+ handleGenderFilter('MALE')} + > + 👨🏻‍🦳 +

남성이

+
+ handleGenderFilter('TEENAGER')} + > + 👦🏻 +

청소년이

+
+
+ +
+ + + + + + +
+
+ ); +}; + +export default TrendGiftRanking; diff --git a/src/components/MainPage/TrendGiftRankingGrid.tsx b/src/components/MainPage/TrendGiftRankingGrid.tsx new file mode 100644 index 000000000..ee8eeba8a --- /dev/null +++ b/src/components/MainPage/TrendGiftRankingGrid.tsx @@ -0,0 +1,97 @@ +import styled from '@emotion/styled'; +import { useState } from 'react'; + +import { Button } from '../common/Button'; +import { RankingGoodsItems } from '../common/GoodsItem/Ranking'; +import { Container } from '../common/layouts/Container'; +import { Grid } from '../common/layouts/Grid'; + +const Wrapper = styled.div` + padding: 20px 0 80px 0; + max-width: 100%; +`; + +const GridContainer = styled(Grid)` + max-width: 100%; + width: 100%; +`; + +const sampleItem = { + rankingIndex: 1, + imageSrc: + 'https://st.kakaocdn.net/product/gift/product/20231030175450_53e90ee9708f45ffa45b3f7b4bc01c7c.jpg', + subtitle: 'BBQ', + title: 'BBQ 양념치킨+크림치즈볼+콜라1.25L', + amount: 29000, +}; + +const TrendGiftRankingGrid = ({ visibleItems }: { visibleItems: number }) => { + const rankingItems = Array.from({ length: 21 }, (_, index) => ({ + ...sampleItem, + rankingIndex: index + 1, + })); + + return ( + + {rankingItems.slice(0, visibleItems).map((item) => ( + + ))} + + ); +}; + +const ToggleButton = styled(Button)` + width: 100%; + max-width: 480px; + margin: 0 auto; + margin-top: 30px; + border-radius: 4px; + display: flex; + -webkit-box-pack: center; + justify-content: center; + -webkit-box-align: center; + align-items: center; + cursor: pointer; + height: 40px; + font-size: 15px; + box-shadow: rgb(204, 204, 204) 0px 0px 0px 1px inset; + background-color: white; + + &:hover { + background-color: rgb(248, 248, 248); + } +`; + +export const TrendGiftRankings = () => { + const [visibleItems, setVisibleItems] = useState(6); + + const handleShowMore = () => { + setVisibleItems(21); + }; + + const handleShowLess = () => { + setVisibleItems(6); + }; + + return ( + + + + {visibleItems === 6 ? ( + 더보기 + ) : ( + 접기 + )} + + + ); +}; + +export default TrendGiftRankings; diff --git a/src/components/ThemePage/ThemeGifts.tsx b/src/components/ThemePage/ThemeGifts.tsx new file mode 100644 index 000000000..a4949674d --- /dev/null +++ b/src/components/ThemePage/ThemeGifts.tsx @@ -0,0 +1,57 @@ +import styled from '@emotion/styled'; + +import { DefaultGoodsItems } from '@/components/common/GoodsItem/Default'; // 임의의 경로로 수정해주세요. + +import { Container } from '../common/layouts/Container'; +import { Grid } from '../common/layouts/Grid'; + +const Wrapper = styled.div` + padding: 20px 0 80px 0; + max-width: 100%; +`; + +const GridContainer = styled(Grid)` + max-width: 100%; + width: 100%; +`; + +const sampleItem = { + imageSrc: + 'https://st.kakaocdn.net/product/gift/product/20231030175450_53e90ee9708f45ffa45b3f7b4bc01c7c.jpg', + subtitle: 'BBQ', + title: 'BBQ 양념치킨+크림치즈볼+콜라1.25L', + amount: 29000, +}; + +const ThemeGiftGrid = () => { + const GoodsItems = Array.from({ length: 21 }, (_, index) => ({ + ...sampleItem, + goodsIndex: index + 1, + })); + + return ( + + {GoodsItems.map((item) => ( + + ))} + + ); +}; + +export const ThemeGifts = () => { + return ( + + + + + + ); +}; + +export default ThemeGifts; diff --git a/src/components/ThemePage/ThemeHeader.tsx b/src/components/ThemePage/ThemeHeader.tsx new file mode 100644 index 000000000..611ad9794 --- /dev/null +++ b/src/components/ThemePage/ThemeHeader.tsx @@ -0,0 +1,69 @@ +import styled from '@emotion/styled'; + +import { Container } from '../common/layouts/Container'; + +const HeaderWrapper = styled.header<{ backgroundColor: string }>` + background-color: ${(props) => props.backgroundColor}; + color: white; + p { + padding-top: 12px; + } +`; + +const Label = styled.p` + font-size: 20px; + color: rgba(255, 255, 255, 0.7); + font-weight: 700; +`; +const Title = styled.h1` + font-size: 30px; + line-height: 40px; + padding-top: 12px; + word-break: break-word; + font-weight: 700; + color: rgb(255, 255, 255); +`; + +const Description = styled.p` + font-size: 24px; + color: rgba(255, 255, 255, 0.55); +`; + +interface Theme { + key: string; + label: string; + title: string; + description: string; + backgroundColor: string; +} + +const themes: Theme[] = [ + { + key: 'life_small_gift', + label: '가벼운 선물', + title: '예산은 가볍게, 감동은 무겁게❤️', + description: `당신의 센스를 뽐내줄 부담 없는 선물`, + backgroundColor: 'rgb(75, 77, 80)', + }, +]; + +interface HeaderProps { + themeKey: string; +} +const Header = ({ themeKey }: HeaderProps) => { + const selectedTheme = themes.find((theme) => theme.key === themeKey) || themes[0]; // 기본 테마는 첫 번째로 설정 + + const { label, title, description, backgroundColor } = selectedTheme; + + return ( + + + + {title} + {description} + + + ); +}; + +export default Header; diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx new file mode 100644 index 000000000..8bd1996ba --- /dev/null +++ b/src/pages/Login.tsx @@ -0,0 +1,85 @@ +import styled from '@emotion/styled'; +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; + +import { Button } from '@/components/common/Button/index'; +import { UnderlineTextField } from '@/components/common/Form/Input/UnderlineTextField'; +import { ROUTE_PATHS } from '@/Routes/constants'; + +const Wrapper = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + height: 100vh; +`; + +const LoginSection = styled.section` + width: 100%; + max-width: 580px; + display: flex; + flex-direction: column; + align-items: center; + border: 1px solid rgba(0, 0, 0, 0.12); + padding: 60px 52px; +`; + +const LoginButton = styled(Button)` + max-width: 474px; + width: 100%; +`; + +const InputField = styled(UnderlineTextField)` + max-width: 474px; + width: 100%; + margin: 8px 0; +`; + +const LogoImage = styled.img` + margin-bottom: 16px; +`; + +const LoginPage = () => { + const [id, setId] = useState(''); + const [password, setPassword] = useState(''); + const navigate = useNavigate(); + + const handleLogin = (e: React.FormEvent) => { + e.preventDefault(); + if (!id || !password) { + alert('아이디와 비밀번호를 입력해주세요.'); + } else { + sessionStorage.setItem('authToken', id); + navigate(ROUTE_PATHS.MAIN); + } + }; + + return ( + + + +
+ setId(e.target.value)} + /> + setPassword(e.target.value)} + /> + 로그인 + +
+
+ ); +}; + +export default LoginPage; diff --git a/src/pages/Main.tsx b/src/pages/Main.tsx new file mode 100644 index 000000000..01cb355dd --- /dev/null +++ b/src/pages/Main.tsx @@ -0,0 +1,48 @@ +import styled from '@emotion/styled'; + +import { Button } from '@/components/common/Button'; +import { Container } from '@/components/common/layouts/Container'; +import { SelectFriend } from '@/components/MainPage/SelectFriend'; +import { ThemeMenu } from '@/components/MainPage/ThemeGrid'; +import TrendGiftRanking from '@/components/MainPage/TrendGiftRanking'; + +const AiSuggestButton = styled(Button)` + flex-direction: column; + margin: 16px 0 16px 0; + padding: 16px 0 16px 0; + width: 100%; + border-radius: 8px; + p { + color: rgba(0, 0, 0, 0.4); + } + h3 { + font-weight: 700; + } +`; + +const Wrapper = styled.div` + height: auto; + min-height: 100%; + left: 0; + right: 0; + top: 58px; +`; + +export const MainPage = () => { + return ( + + + + + +

AI가 추천하는 선물

+

선물을 추천받고 싶은 친구를 선택해주세요.

+
+
+
+ + + ); +}; + +export default MainPage; diff --git a/src/pages/MyAccount.tsx b/src/pages/MyAccount.tsx new file mode 100644 index 000000000..525378d50 --- /dev/null +++ b/src/pages/MyAccount.tsx @@ -0,0 +1,58 @@ +import styled from '@emotion/styled'; +import { useEffect, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; + +import { Button } from '@/components/common/Button'; +import { Container } from '@/components/common/layouts/Container'; + +const ProfileContainer = styled(Container)` + padding: 80px 0px 120px; +`; + +const ProfileName = styled.h1` + width: 100%; + display: flex; + align-items: center; + justify-content: center; + font-weight: 700; + font-size: 36px; +`; + +const LogoutButton = styled(Button)` + margin: 20px auto; + max-width: 200px; + max-height: 40px; + color: white; + background-color: rgb(68, 68, 68); + + &:hover { + background-color: rgb(85, 85, 85); + } +`; + +const MyAccount = () => { + const [userName, setUserName] = useState(''); + const navigate = useNavigate(); + + useEffect(() => { + const authToken = sessionStorage.getItem('authToken'); + if (authToken) { + setUserName(authToken); + } + }, []); + + const handleLogout = () => { + sessionStorage.removeItem('authToken'); + navigate('/'); + window.location.reload(); // 임시 + }; + + return ( + + {`${userName}님 안녕하세요!`} + 로그아웃 + + ); +}; + +export default MyAccount; diff --git a/src/pages/Theme.tsx b/src/pages/Theme.tsx new file mode 100644 index 000000000..b9fe83896 --- /dev/null +++ b/src/pages/Theme.tsx @@ -0,0 +1,24 @@ +import styled from '@emotion/styled'; +import { useParams } from 'react-router-dom'; + +import ThemeGiftGrid from '@/components/ThemePage/ThemeGifts'; +import Header from '@/components/ThemePage/ThemeHeader'; + +const Wrapper = styled.div` + height: auto; + min-height: 100%; +`; + +export const ThemePage = () => { + const { themeKey } = useParams<{ themeKey: string }>(); + + return ( + + {themeKey &&
} + + + + ); +}; + +export default ThemePage; diff --git a/src/styles/reset.css b/src/styles/reset.css index 6acb70655..9bad075af 100644 --- a/src/styles/reset.css +++ b/src/styles/reset.css @@ -87,3 +87,8 @@ button:disabled { border: 0; padding: 0; } + +a { + text-decoration: none; + color: black; +}