diff --git a/src/components/atoms/BottomSection.tsx b/src/components/atoms/BottomSection.tsx new file mode 100644 index 0000000..bd35e1c --- /dev/null +++ b/src/components/atoms/BottomSection.tsx @@ -0,0 +1,22 @@ +import styled from "styled-components"; +import theme, { windowSize } from "@src/styles/theme"; + +interface BottomProps { + height?: string; +} +export const BottomSection = styled.div` + position: fixed; + bottom: 0; + left: 0; + right: 0; + height: ${({ height }) => height ?? "68px"}; + background: #fff; + border-top: 1px solid ${theme.color.gray2}; + z-index: 9999; + ${theme.window.tab} { + width: ${windowSize.mobile}; + left: auto; + right: auto; + } +`; +export default {}; diff --git a/src/components/atoms/Button.tsx b/src/components/atoms/Button.tsx index 1af31b6..34b84ca 100644 --- a/src/components/atoms/Button.tsx +++ b/src/components/atoms/Button.tsx @@ -9,19 +9,33 @@ export interface ButtonStyleProps extends BaseProps { filled?: boolean; width?: string; height?: string; - color: "gray" | "primary" | "white"; + color?: "gray" | "primary" | "white" | string; + shape?: "rounded" | "full-rounded"; + textAlign?: "left" | "center"; } export interface InputLikeButtonStyleProps extends ButtonStyleProps { selected?: boolean; } -const BaseButton = css` +export const BaseButton = css` ${BaseStyleProps}; + border-radius: ${({ shape }) => { + switch (shape) { + case "rounded": + return "12px"; + case "full-rounded": + return "100px"; + default: + return "12px"; + } + }}; + + text-align: ${({ textAlign = "center" }) => textAlign}; + border: none; height: ${({ height }) => height || "58px"}; - text-align: center; cursor: pointer; user-select: none; `; @@ -29,9 +43,7 @@ const BaseButton = css` export const Button = styled.button` ${BaseButton}; - border-radius: 12px; - - background: ${({ color, disabled, filled = true }) => { + background: ${({ color = "gray", disabled, filled = true }) => { if (disabled) return theme.color.gray2; if (!filled) return "#fff"; switch (color) { @@ -39,8 +51,10 @@ export const Button = styled.button` return "#FFF"; case "primary": return Color.mainGradient; - default: + case "gray": return theme.color.gray2; + default: + return color; } }}; font-weight: ${({ color }) => { diff --git a/src/components/atoms/CategoryTag.ts b/src/components/atoms/CategoryTag.ts new file mode 100644 index 0000000..15d3b13 --- /dev/null +++ b/src/components/atoms/CategoryTag.ts @@ -0,0 +1,16 @@ +import styled from "styled-components"; +import theme, { FontSize } from "@src/styles/theme"; +import { BaseButton, ButtonStyleProps } from "@src/components/atoms/Button"; + +export const CategoryTag = styled.button` + ${BaseButton}; + height: 32px; + background-color: ${theme.color.gray1}; + font-size: ${FontSize.Small}; + font-weight: 500; + word-break: keep-all; + padding: 0 15px; + border-radius: 20px; +`; + +export default { CategoryTag }; diff --git a/src/components/atoms/Divider.tsx b/src/components/atoms/Divider.tsx new file mode 100644 index 0000000..eba15ae --- /dev/null +++ b/src/components/atoms/Divider.tsx @@ -0,0 +1,14 @@ +import styled from "styled-components"; +import theme from "@src/styles/theme"; + +interface DividerProps { + my?: string; +} +export const BoldDivider = styled.hr` + border: 4px solid ${theme.color.gray1}; + margin: ${({ my = "0" }) => my} 0; +`; +export const LightDivider = styled.hr` + border: 0.5px solid ${theme.color.gray1}; + margin: ${({ my = "0" }) => my} 0; +`; diff --git a/src/components/atoms/Image.tsx b/src/components/atoms/Image.tsx new file mode 100644 index 0000000..a5b39d9 --- /dev/null +++ b/src/components/atoms/Image.tsx @@ -0,0 +1,22 @@ +import { ImgHTMLAttributes, useCallback, useState } from "react"; + +interface Props extends ImgHTMLAttributes { + fallback?: string; +} + +Image.defaultProps = { + fallback: + "https://cdn.pixabay.com/photo/2021/09/01/16/09/cake-6591719__340.jpg", +}; + +function Image({ fallback, src, alt, ...props }: Props) { + const [imgSrc, setImgSrc] = useState(src); + const onError = useCallback(() => setImgSrc(fallback), [fallback]); + + return ( + // eslint-disable-next-line react/jsx-props-no-spreading + {alt} + ); +} + +export default Image; diff --git a/src/components/atoms/LinkButton.tsx b/src/components/atoms/TextButton.tsx similarity index 74% rename from src/components/atoms/LinkButton.tsx rename to src/components/atoms/TextButton.tsx index 77189a3..fb0aeb1 100644 --- a/src/components/atoms/LinkButton.tsx +++ b/src/components/atoms/TextButton.tsx @@ -1,11 +1,14 @@ import styled from "styled-components"; import theme from "@src/styles/theme"; +import { BaseProps, BaseStyleProps } from "@src/styles/common"; -export type TextButtonStyleProps = { - color: "gray" | "primary" | "white" | "black"; -}; +export interface TextButtonStyleProps extends BaseProps { + color?: "gray" | "primary" | "white" | "black"; +} export const TextButton = styled.button` + ${BaseStyleProps}; + background: transparent; border: none; width: 100%; diff --git a/src/components/icon/Cake.icon.tsx b/src/components/icon/Cake.icon.tsx new file mode 100644 index 0000000..b4f4c0f --- /dev/null +++ b/src/components/icon/Cake.icon.tsx @@ -0,0 +1,74 @@ +function CakeIcon() { + return ( + + + + + + + + + + + + + + + ); +} + +export default CakeIcon; diff --git a/src/components/icon/CategoryMenu.icon.tsx b/src/components/icon/CategoryMenu.icon.tsx new file mode 100644 index 0000000..616dcad --- /dev/null +++ b/src/components/icon/CategoryMenu.icon.tsx @@ -0,0 +1,21 @@ +function CategoryMenuIcon() { + return ( + + + + + + + + + + ); +} + +export default CategoryMenuIcon; diff --git a/src/components/icon/ColoredCalendar.icon.tsx b/src/components/icon/ColoredCalendar.icon.tsx new file mode 100644 index 0000000..0d30075 --- /dev/null +++ b/src/components/icon/ColoredCalendar.icon.tsx @@ -0,0 +1,29 @@ +function ColoredCalendarIcon() { + return ( + + + + + + + ); +} + +export default ColoredCalendarIcon; diff --git a/src/components/icon/ColoredSearch.icon.tsx b/src/components/icon/ColoredSearch.icon.tsx new file mode 100644 index 0000000..6e217fe --- /dev/null +++ b/src/components/icon/ColoredSearch.icon.tsx @@ -0,0 +1,26 @@ +function ColoredSearchIcon() { + return ( + + + + + ); +} +export default ColoredSearchIcon; diff --git a/src/components/icon/Home.icon.tsx b/src/components/icon/Home.icon.tsx new file mode 100644 index 0000000..43c9b9e --- /dev/null +++ b/src/components/icon/Home.icon.tsx @@ -0,0 +1,27 @@ +function HomeIcon() { + return ( + + + + + ); +} + +export default HomeIcon; diff --git a/src/components/icon/MyPage.icon.tsx b/src/components/icon/MyPage.icon.tsx new file mode 100644 index 0000000..0dde740 --- /dev/null +++ b/src/components/icon/MyPage.icon.tsx @@ -0,0 +1,46 @@ +function MyPageIcon() { + return ( + + + + + + + + + ); +} + +export default MyPageIcon; diff --git a/src/components/icon/Person.icon.tsx b/src/components/icon/Person.icon.tsx new file mode 100644 index 0000000..9dcbb3c --- /dev/null +++ b/src/components/icon/Person.icon.tsx @@ -0,0 +1,32 @@ +function PersonIcon() { + return ( + + + + + + + + ); +} + +export default PersonIcon; diff --git a/src/components/icon/StudyManage.icon.tsx b/src/components/icon/StudyManage.icon.tsx new file mode 100644 index 0000000..a62e569 --- /dev/null +++ b/src/components/icon/StudyManage.icon.tsx @@ -0,0 +1,50 @@ +function StudyManageIcon() { + return ( + + + + + + + + + + + + + ); +} + +export default StudyManageIcon; diff --git a/src/components/molecules/Category.component.tsx b/src/components/molecules/CategorySelectComponent.tsx similarity index 96% rename from src/components/molecules/Category.component.tsx rename to src/components/molecules/CategorySelectComponent.tsx index ffe879d..c1b0de6 100644 --- a/src/components/molecules/Category.component.tsx +++ b/src/components/molecules/CategorySelectComponent.tsx @@ -71,7 +71,7 @@ type CategoryWrapperProp = { onClick: MouseEventHandler; }; -function CategoryComponent({ +function CategorySelectComponent({ selected = false, img = null, name = "", @@ -91,4 +91,4 @@ function CategoryComponent({ ); } -export default CategoryComponent; +export default CategorySelectComponent; diff --git a/src/components/molecules/StudyListElement.component.tsx b/src/components/molecules/StudyListElement.component.tsx new file mode 100644 index 0000000..34ebec1 --- /dev/null +++ b/src/components/molecules/StudyListElement.component.tsx @@ -0,0 +1,153 @@ +import { useMemo } from "react"; +import styled from "styled-components"; + +// components +import { StudyListElement } from "@src/models/dto/study.dto"; +import PersonIcon from "@src/components/icon/Person.icon"; +import ColoredCalendarIcon from "@src/components/icon/ColoredCalendar.icon"; + +// utils +import { dateToFormatted } from "@src/utils/dayjs.util"; + +// styles +import theme, { FontSize, Padding } from "@src/styles/theme"; +import Image from "@src/components/atoms/Image"; + +export interface StudyListElementComponentProps { + study: StudyListElement; +} + +const Container = styled.div` + display: grid; + grid-template-columns: auto 100px; + width: 100%; + cursor: pointer; + user-select: none; + padding: ${Padding.pageX}; +`; +const ContentsWrapper = styled.div` + width: 100%; + + h3 { + font-weight: 700; + font-size: ${FontSize.PrimaryDescription}; + margin-bottom: 5px; + } +`; +const InfoWrapper = styled.div` + display: flex; + margin-bottom: 10px; + font-size: ${FontSize.Small}; +`; +const IconPrefixWrapper = styled.span` + display: flex; + align-items: center; + margin-right: 15px; + svg { + margin-right: 5px; + } +`; +const CategoryWrapper = styled.div` + display: flex; + align-items: center; + font-size: ${FontSize.Small}; + &:not(:last-child) { + margin-bottom: 10px; + } +`; +const Category = styled.span` + font-size: ${FontSize.Small}; + padding-top: 2px; + + &:not(:last-child) { + ::after { + content: ","; + margin-right: 4px; + } + } +`; +const CategoryTypeTag = styled.span` + background-color: ${theme.color.point}; + color: #fff; + font-weight: 700; + font-size: ${FontSize.Small}; + text-align: center; + padding-top: 2px; + width: 40px; + height: 20px; + border-radius: 20px; + margin-right: 7px; +`; +const ImageWrapper = styled.div` + width: 100px; + height: 100px; + overflow: hidden; + + border-radius: 6px; + + display: flex; + justify-content: center; + align-items: center; + + img { + width: auto; + } +`; +const S = { + Container, + ContentsWrapper, + InfoWrapper, + IconPrefixWrapper, + CategoryWrapper, + CategoryTypeTag, + Category, + ImageWrapper, +}; +function StudyListElementComponent({ study }: StudyListElementComponentProps) { + const date = useMemo( + () => + study.startDate && study.endDate + ? `${dateToFormatted(study.startDate)} - ${dateToFormatted( + study.endDate, + )}` + : "협의 후 결정", + [study], + ); + const people = useMemo(() => `${study.peopleCnt || 1}명`, [study]); + return ( + + +

{study.title}

+ + + + {people} + + + + {date} + + + + GIVE + {study.give.length === 0 ? "아직 없어요" : ""} + {study.give.map((x) => ( + {x} + ))} + + + TAKE + {study.take.length === 0 ? "다 좋아요!" : ""} + {study.take.map((x) => ( + {x} + ))} + +
+ + {study.title} + +
+ ); +} + +export default StudyListElementComponent; diff --git a/src/components/molecules/TitleHeader.component.tsx b/src/components/molecules/TitleHeader.component.tsx index 406b7cf..33eab8b 100644 --- a/src/components/molecules/TitleHeader.component.tsx +++ b/src/components/molecules/TitleHeader.component.tsx @@ -2,7 +2,7 @@ import Link from "next/link"; import LeftArrowIcon from "@src/components/icon/LeftArrow.icon"; import styled from "styled-components"; -import { FontSize, Padding } from "@src/styles/theme"; +import theme, { FontSize, Padding, windowSize } from "@src/styles/theme"; const Container = styled.div` display: flex; @@ -18,6 +18,12 @@ const Container = styled.div` height: 60px; z-index: 9999; + + ${theme.window.tab} { + width: ${windowSize.mobile}; + left: auto; + right: auto; + } `; const Title = styled.p` diff --git a/src/components/organs/BottomNavigation.component.tsx b/src/components/organs/BottomNavigation.component.tsx new file mode 100644 index 0000000..3ea4c32 --- /dev/null +++ b/src/components/organs/BottomNavigation.component.tsx @@ -0,0 +1,62 @@ +import Link from "next/link"; +import { BottomSection } from "@src/components/atoms/BottomSection"; + +// components +import HomeIcon from "@src/components/icon/Home.icon"; +import CategoryMenuIcon from "@src/components/icon/CategoryMenu.icon"; +import StudyManageIcon from "@src/components/icon/StudyManage.icon"; +import MyPageIcon from "@src/components/icon/MyPage.icon"; + +// styles +import styled from "styled-components"; +import { BaseButton, ButtonStyleProps } from "@src/components/atoms/Button"; +import { FontSize } from "@src/styles/theme"; + +const NavWrapper = styled.div` + display: flex; + align-items: center; + width: 100%; + height: 100%; +`; +const NavButton = styled.a` + ${BaseButton}; + background-color: #fff; + width: 25%; + p { + font-size: ${FontSize.Small}; + } +`; +function BottomNavigationComponent() { + return ( + + + + + +

메인

+
+ + + + +

카테고리

+
+ + + + +

스터디 관리

+
+ + + + +

마이페이지

+
+ +
+
+ ); +} + +export default BottomNavigationComponent; diff --git a/src/components/organs/signup/SelectCategoryStep.component.tsx b/src/components/organs/signup/SelectCategoryStep.component.tsx index 369b1f7..c3ae4a6 100644 --- a/src/components/organs/signup/SelectCategoryStep.component.tsx +++ b/src/components/organs/signup/SelectCategoryStep.component.tsx @@ -7,7 +7,7 @@ import { useStores } from "@src/store/root.store"; import { CategoryType } from "@src/constant/enum.constant"; // component -import CategoryComponent from "@src/components/molecules/Category.component"; +import CategorySelectComponent from "@src/components/molecules/CategorySelectComponent"; import { Button } from "@src/components/atoms/Button"; // styles @@ -54,7 +54,7 @@ const SelectCategoryStepComponent = observer( categoryStore.categoryList.map((x) => { const onClick = () => onSelect(type, x.id); return ( - y === x.id) !== undefined} diff --git a/src/hooks/safeHydrate.hoc.tsx b/src/hooks/safeHydrate.hoc.tsx new file mode 100644 index 0000000..bfa7681 --- /dev/null +++ b/src/hooks/safeHydrate.hoc.tsx @@ -0,0 +1,9 @@ +function SafeHydrateHoc({ children }) { + return ( +
+ {typeof window === "undefined" ? null : children} +
+ ); +} + +export default SafeHydrateHoc; diff --git a/src/hooks/useInfiniteLoading.hook.ts b/src/hooks/useInfiniteLoading.hook.ts new file mode 100644 index 0000000..eba37a4 --- /dev/null +++ b/src/hooks/useInfiniteLoading.hook.ts @@ -0,0 +1,44 @@ +import { useCallback, useEffect, useRef, useState } from "react"; + +const useInfiniteLoading = ({ getItems, pageToLoad = 0 }) => { + const [items, setItems] = useState([]); + const [page, setPage] = useState(pageToLoad); + const initialPageLoaded = useRef(false); + const [hasMore, setHasMore] = useState(true); + + const loadItems = useCallback( + async (nextPage) => { + /* 3 */ + const data = await getItems(nextPage); + if (data.length === 0) setHasMore(false); + else { + setHasMore(true); /* 4 */ + setItems((prevItems) => [...prevItems, ...data]); + } + }, + [getItems], + ); + + const onNext = useCallback(() => { + setPage(page + 1); + loadItems(page + 1); + }, [loadItems, page]); + + useEffect(() => { + if (initialPageLoaded.current) { + return; + } + + loadItems(pageToLoad); /* 5 */ + initialPageLoaded.current = true; + }, [loadItems, pageToLoad]); + + return { + items, + hasMore, + loadItems, + onNext, + }; +}; + +export default useInfiniteLoading; diff --git a/src/hooks/withAuthentication.hoc.tsx b/src/hooks/withAuthentication.hoc.tsx new file mode 100644 index 0000000..428739f --- /dev/null +++ b/src/hooks/withAuthentication.hoc.tsx @@ -0,0 +1,31 @@ +import { useStores } from "@src/store/root.store"; +import { useEffect, useMemo } from "react"; +import GuestMainTemplate from "@src/templates/GuestMain.template"; +import { useRouter } from "next/router"; + +const allowedOnlyToGuest = ["/", "/login", "/signup"]; + +function WithAuthenticationHoc({ children }) { + const { userStore } = useStores(); + const router = useRouter(); + + useEffect(() => userStore.isAuthenticated()); + + const component = useMemo(() => { + const unauthenticated = !userStore.authenticated; + const isAllowedForGuest = allowedOnlyToGuest.find( + (x) => x === router.pathname, + ); + if (isAllowedForGuest) { + if (unauthenticated) return children; + + router.push("/"); + return children; + } + return unauthenticated ? : children; + }, [userStore.authenticated, children, router]); + + return component; +} + +export default WithAuthenticationHoc; diff --git a/src/models/dto/study.dto.d.ts b/src/models/dto/study.dto.d.ts index e66b6eb..e8097ab 100644 --- a/src/models/dto/study.dto.d.ts +++ b/src/models/dto/study.dto.d.ts @@ -15,4 +15,15 @@ export interface CreateStudyDto { take: string[]; } +export interface StudyListElement { + id: number; + title: string; + peopleCnt: number; + startDate: string; + endDate: string; + give: string[]; + take: string[]; + img: string; +} + export default {}; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index c0bc979..12dab54 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -7,6 +7,8 @@ import GlobalStyle from "@src/styles/globals"; import theme, { windowSize } from "@src/styles/theme"; import GlobalFonts from "@src/styles/fonts"; import "../styles/variables.less"; +import WithAuthenticationHoc from "@src/hooks/withAuthentication.hoc"; +import SafeHydrateHoc from "@src/hooks/safeHydrate.hoc"; const Container = styled.div` ${({ theme: defaultTheme }) => css` @@ -45,7 +47,11 @@ function MyApp({ Component, pageProps }: AppProps) { - + + + + + diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 614bf93..5bd6e9c 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,26 +1,7 @@ -import GuestMainTemplate from "@src/templates/GuestMain.template"; -import { useStores } from "@src/store/root.store"; -import { useEffect, useMemo, useState } from "react"; -import UserMainTemplate from "@src/templates/UserMain.template"; +import UserMainPage from "@src/pages/main"; function MainPage() { - const { userStore } = useStores(); - - const [email, setEmail] = useState(""); - // TODO :: authenticated 확인을 HOC로 빼내기 - const authenticated = useMemo(() => userStore.isAuthenticated(), [userStore]); - - useEffect(() => { - if (authenticated) { - userStore.test().then((x) => setEmail(x)); - } - }, [authenticated, userStore]); - - return authenticated ? ( - - ) : ( - - ); + return ; } export default MainPage; diff --git a/src/pages/main.tsx b/src/pages/main.tsx new file mode 100644 index 0000000..b2cdb16 --- /dev/null +++ b/src/pages/main.tsx @@ -0,0 +1,28 @@ +// lib +import useInfiniteLoading from "@src/hooks/useInfiniteLoading.hook"; +import { useStores } from "@src/store/root.store"; +// components +import UserMainTemplate from "@src/templates/UserMain.template"; + +function UserMainPage() { + const { studyStore } = useStores(); + + const { + items: studyList, + hasMore, + onNext, + } = useInfiniteLoading({ + getItems: (p) => studyStore.getStudyFeed(p), + pageToLoad: 0, + }); + + return ( + + ); +} + +export default UserMainPage; diff --git a/src/services/Study.service.ts b/src/services/Study.service.ts index 3f66eac..f9fae86 100644 --- a/src/services/Study.service.ts +++ b/src/services/Study.service.ts @@ -1,5 +1,5 @@ import BaseHttpService from "@src/services/BaseHttp.service"; -import { CreateStudyDto } from "@src/models/dto/study.dto"; +import { CreateStudyDto, StudyListElement } from "@src/models/dto/study.dto"; export default class StudyService extends BaseHttpService { prefix = "/study"; @@ -7,4 +7,10 @@ export default class StudyService extends BaseHttpService { async createStudy(createStudyDto: CreateStudyDto): Promise { return (await this.post(this.prefix, createStudyDto)) as string; } + + async getStudyFeed(page: number): Promise { + return (await this.get( + `/page?page=${page}`, + )) as StudyListElement[]; + } } diff --git a/src/store/study.store.ts b/src/store/study.store.ts index 65714e5..97f4399 100644 --- a/src/store/study.store.ts +++ b/src/store/study.store.ts @@ -1,7 +1,7 @@ import { RootStore } from "@src/store/root.store"; import { makeAutoObservable } from "mobx"; import StudyService from "@src/services/Study.service"; -import { CreateStudyDto } from "@src/models/dto/study.dto"; +import { CreateStudyDto, StudyListElement } from "@src/models/dto/study.dto"; export default class StudyStore { private readonly rootStore: RootStore; @@ -18,4 +18,8 @@ export default class StudyStore { async createStudy(props: CreateStudyDto) { return (await this.studyService.createStudy(props)) as string; } + + async getStudyFeed(page: number): Promise { + return (await this.studyService.getStudyFeed(page)) as StudyListElement[]; + } } diff --git a/src/store/user.store.ts b/src/store/user.store.ts index eab670d..0eac2c6 100644 --- a/src/store/user.store.ts +++ b/src/store/user.store.ts @@ -13,6 +13,10 @@ export default class UserStore { private readonly authService: AuthService; + authLoading = true; + + authenticated = false; + constructor(rootStore: RootStore, authService: AuthService) { this.authService = authService; this.rootStore = rootStore; @@ -42,6 +46,7 @@ export default class UserStore { isAuthenticated() { const token = this.authService.accessToken; - return token !== null && token.length > 0; + this.authLoading = false; + this.authenticated = token !== null && token.length > 0; } } diff --git a/src/stories/atoms/Category.stories.tsx b/src/stories/atoms/Category.stories.tsx new file mode 100644 index 0000000..dcf90f1 --- /dev/null +++ b/src/stories/atoms/Category.stories.tsx @@ -0,0 +1,15 @@ +import { CategoryTag } from "@src/components/atoms/CategoryTag"; + +export default { + title: "atoms/Category", + component: CategoryTag, +}; + +const Template = ({ contents }) => ( + {contents} +); + +export const Category = Template.bind({}); +Category.args = { + contents: "일러스트", +}; diff --git a/src/stories/molcules/StudyListElement.stories.tsx b/src/stories/molcules/StudyListElement.stories.tsx new file mode 100644 index 0000000..47e186c --- /dev/null +++ b/src/stories/molcules/StudyListElement.stories.tsx @@ -0,0 +1,22 @@ +import StudyListElementComponent from "@src/components/molecules/StudyListElement.component"; +import { StudyListElement as StudyListElementDto } from "@src/models/dto/study.dto"; + +export default { + title: "molecules/Study List Element", + component: StudyListElementComponent, +}; + +const studySample: StudyListElementDto = { + id: 1, + title: "애프터 이펙트 알려주실 분 구합니다.", + peopleCnt: 1, + startDate: "2021-11-07T00:00:00.000+00:00", + endDate: "2021-11-09T00:00:00.000+00:00", + give: ["요리", "베이킹"], + take: ["운동", "영상 편집"], + img: "https://cdn.pixabay.com/photo/2021/09/01/16/09/cake-6591719__340.jpg", +}; + +const Template = () => ; + +export const StudyListElement = Template.bind({}); diff --git a/src/stories/molcules/TitleHeader.stories.tsx b/src/stories/molcules/TitleHeader.stories.tsx index 3c22243..a548310 100644 --- a/src/stories/molcules/TitleHeader.stories.tsx +++ b/src/stories/molcules/TitleHeader.stories.tsx @@ -1,7 +1,7 @@ import TitleHeaderComponent from "@src/components/molecules/TitleHeader.component"; export default { - title: "molecules", + title: "molecules/Title Header", component: TitleHeaderComponent, }; diff --git a/src/stories/organs/BottomNavigation.stories.tsx b/src/stories/organs/BottomNavigation.stories.tsx new file mode 100644 index 0000000..b070299 --- /dev/null +++ b/src/stories/organs/BottomNavigation.stories.tsx @@ -0,0 +1,10 @@ +import BottomNavigationComponent from "@src/components/organs/BottomNavigation.component"; + +export default { + title: "organs/Bottom Navigation", + component: BottomNavigationComponent, +}; + +const Template = () => ; + +export const BottomNavigation = Template.bind({}); diff --git a/src/stories/templates/UserMain.stories.tsx b/src/stories/templates/UserMain.stories.tsx new file mode 100644 index 0000000..4dc40ab --- /dev/null +++ b/src/stories/templates/UserMain.stories.tsx @@ -0,0 +1,33 @@ +import UserMainTemplate from "@src/templates/UserMain.template"; +import { StudyListElement as StudyListElementDto } from "@src/models/dto/study.dto"; + +export default { + title: "template/User Main", + component: UserMainTemplate, +}; + +const studySample: StudyListElementDto = { + id: 1, + title: "애프터 이펙트 알려주실 분 구합니다.", + peopleCnt: 1, + startDate: "2021-11-07T00:00:00.000+00:00", + endDate: "2021-11-09T00:00:00.000+00:00", + give: ["요리", "베이킹"], + take: ["운동", "영상 편집"], + img: "https://cdn.pixabay.com/photo/2021/09/01/16/09/cake-6591719__340.jpg", +}; +const Template = ({ studyList = [] }) => ( + +); + +export const UserMain = Template.bind({}); +UserMain.args = { + studyList: [ + studySample, + studySample, + studySample, + studySample, + studySample, + studySample, + ], +}; diff --git a/src/styles/theme.ts b/src/styles/theme.ts index 4cb71cf..47ab551 100644 --- a/src/styles/theme.ts +++ b/src/styles/theme.ts @@ -14,10 +14,10 @@ const theme: DefaultTheme = { point: "#F95E66", // custom black: "#000000", gray5: "#444444", - gray4: "#767676", + gray4: "#a6a29d", gray3: "#bdbdbd", // custom gray2: "#e9e9e9", // custom - gray1: "#E5E5E5", + gray1: "#f6f6f6", gray0: "#FAFAFA", white: "#FFFFFF", success: "#22bb33", diff --git a/src/templates/GuestMain.template.tsx b/src/templates/GuestMain.template.tsx index 6c2720a..f01632a 100644 --- a/src/templates/GuestMain.template.tsx +++ b/src/templates/GuestMain.template.tsx @@ -3,7 +3,7 @@ import Link from "next/link"; // components import AppTitleComponent from "@src/components/molecules/AppTitle.component"; import { Button } from "@src/components/atoms/Button"; -import { TextButton } from "@src/components/atoms/LinkButton"; +import { TextButton } from "@src/components/atoms/TextButton"; // styles import SGuestMain from "@src/styles/template/GuestMain.styles"; diff --git a/src/templates/LoginPage.template.tsx b/src/templates/LoginPage.template.tsx index 02e596b..1f6100b 100644 --- a/src/templates/LoginPage.template.tsx +++ b/src/templates/LoginPage.template.tsx @@ -3,7 +3,7 @@ import Link from "next/link"; // components import AppTitleComponent from "@src/components/molecules/AppTitle.component"; -import { TextButton } from "@src/components/atoms/LinkButton"; +import { TextButton } from "@src/components/atoms/TextButton"; import { DividedInput } from "@src/components/atoms/Input"; import { Button } from "@src/components/atoms/Button"; diff --git a/src/templates/SignupPage.template.tsx b/src/templates/SignupPage.template.tsx index a9d239c..262d6c0 100644 --- a/src/templates/SignupPage.template.tsx +++ b/src/templates/SignupPage.template.tsx @@ -14,7 +14,7 @@ import SelectCategoryStepComponent from "@src/components/organs/signup/SelectCat import SignupCompleteStepComponent from "@src/components/organs/signup/SignupCompleteStep.component"; import LeftArrowIcon from "@src/components/icon/LeftArrow.icon"; import { Button } from "@src/components/atoms/Button"; -import { TextButton } from "@src/components/atoms/LinkButton"; +import { TextButton } from "@src/components/atoms/TextButton"; // styles import { FontSize, Padding } from "@src/styles/theme"; diff --git a/src/templates/StudyCreate.template.tsx b/src/templates/StudyCreate.template.tsx index c7d93ee..d544269 100644 --- a/src/templates/StudyCreate.template.tsx +++ b/src/templates/StudyCreate.template.tsx @@ -11,11 +11,13 @@ import { LightUnderlineInput } from "@src/components/atoms/Input"; import DatePicker from "@src/components/atoms/DatePicker"; import Select from "@src/components/atoms/Select"; import TitleHeaderComponent from "@src/components/molecules/TitleHeader.component"; +import AutocompleteCategoryComponent from "@src/components/molecules/AutocompleteCategory.component"; // styles import theme, { Padding } from "@src/styles/theme"; import { LightUnderline, NoScroll } from "@src/styles/common"; -import AutocompleteCategoryComponent from "@src/components/molecules/AutocompleteCategory.component"; +import { BottomSection } from "@src/components/atoms/BottomSection"; +import { BoldDivider } from "@src/components/atoms/Divider"; const Container = styled.div` padding-top: 60px; @@ -56,11 +58,6 @@ const Textarea = styled.textarea` padding: 8px; `; -const BorderLine = styled.hr` - border: 4px solid ${theme.color.gray2}; - margin: 20px 0; -`; - const MidLine = styled.hr` border: 1px solid ${theme.color.gray3}; border-radius: 0; @@ -72,16 +69,7 @@ const WithPrefixIcon = styled.div` justify-content: center; align-items: center; `; -const Bottom = styled.div` - position: fixed; - bottom: 0; - left: 0; - right: 0; - height: 68px; - background: #fff; - border-top: 1px solid ${theme.color.gray3}; - z-index: 9999; -`; + const SubmitWrapper = styled.div` position: absolute; right: ${Padding.pageX}; @@ -187,7 +175,7 @@ function StudyCreateTemplate({ onChange={onChange} /> - +