diff --git a/components/common/Header.tsx b/components/common/Header.tsx index cc19c284e..7d75e4c8d 100644 --- a/components/common/Header.tsx +++ b/components/common/Header.tsx @@ -1,28 +1,53 @@ -import { useState } from "react"; -import useFetch from "@/src/hook/useFetch"; -import { IHeaderUserLoginInfoApi } from "./interface"; -import { BASE_URL } from "@/src/constant/api"; +import { useEffect, useState } from "react"; +import axios from "@/lib/axios"; import { Email, HeaderControl, HeaderInner, HeaderLogo, HeaderUserInfo, HeaderWrap } from "./headerStyle"; -import Link from "next/link"; +import { useRouter } from "next/router"; import { Profile } from "@/styles/commonStyle"; import LinkButton from "./atoms/LinkButton"; +import Link from "next/link"; const logo = '/assets/logo/logo.svg'; -function Header() { - // const { pathname } = useLocation(); - const { value } = useFetch(BASE_URL); +export interface IHeaderUser { + id:number, + email:string, + name?:string, + image_source?:string, + created_at?:string, + auth_id:string +} + +export interface IHeaderUserLoginInfoApi { + userInfo?: { + data: IHeaderUser[]; + }; +} + +export async function getStaticProps() { + const res = await axios.get(``); + const userInfo = res.data; + + return { + props:{ + userInfo, + } + } +} + +function Header({userInfo}:IHeaderUserLoginInfoApi) { + const {pathname} = useRouter(); const [fixed, setFixed] = useState(true); - // useEffect(() => { - // if (pathname === '/folder') { - // setFixed(false); - // } - // }, [pathname]); - const userInfo = value?.data[0] ?? undefined; + + useEffect(() => { + if (pathname === '/folder') { + setFixed(false); + } + }, [pathname]); + return ( - + linkbrary @@ -31,7 +56,7 @@ function Header() { {userInfo ? ( - {userInfo?.email} + {userInfo?.data[0].email} ) : ( diff --git a/components/common/atoms/Button.tsx b/components/common/atoms/Button.tsx index a8a6ca6f7..dfb50c4a5 100644 --- a/components/common/atoms/Button.tsx +++ b/components/common/atoms/Button.tsx @@ -1,11 +1,12 @@ +import { ButtonHTMLAttributes } from 'react'; import { ButtonModule } from './buttonStyle'; interface IButtonModule { children: React.ReactNode; $btnClass: string; $BeforButtonIcon?: string; - $id?: string | number; + $id?: string; $afterButtonIcon?: string; - $type?: 'button' | 'reset' | 'submit' | undefined; + $type?: ButtonHTMLAttributes['type']; onclick?: () => void; } diff --git a/components/common/atoms/CheckBox.tsx b/components/common/atoms/CheckBox.tsx index 5374db9d7..31f1b8225 100644 --- a/components/common/atoms/CheckBox.tsx +++ b/components/common/atoms/CheckBox.tsx @@ -6,7 +6,7 @@ interface ICheckBoxData { } function CheckBox({ $data }: ICheckBoxData) { - if (typeof $data) { + if ($data?.data) { return ( {$data && diff --git a/components/common/atoms/Input.tsx b/components/common/atoms/Input.tsx index 54a6a09d2..5325a76f4 100644 --- a/components/common/atoms/Input.tsx +++ b/components/common/atoms/Input.tsx @@ -2,6 +2,7 @@ import { ChangeEvent, ReactNode, useEffect, useRef, useState } from 'react'; import { InputModule } from './inputStyle'; import Button from './Button'; import { SearchResults } from '@/pages/folder/folderStyle'; +import { Relative } from '@/styles/commonStyle'; interface IButtonModule { $type?: string; @@ -47,7 +48,7 @@ function Input({ return ( <> -
+ )} -
+ {value && {value}으로 검색한 결과입니다.} ); diff --git a/components/common/atoms/LinkButton.tsx b/components/common/atoms/LinkButton.tsx index 3d262fc6c..ef565e915 100644 --- a/components/common/atoms/LinkButton.tsx +++ b/components/common/atoms/LinkButton.tsx @@ -3,15 +3,17 @@ interface IButtonModule { children: React.ReactNode; $link: string; $linkClass: string; + $target?: string; } export default function LinkButton({ children, $link, $linkClass, + $target="_self" }: IButtonModule) { return ( - + {children} ); diff --git a/components/common/atoms/LinkButtonStyle.ts b/components/common/atoms/LinkButtonStyle.ts index 65518d78d..a406993d1 100644 --- a/components/common/atoms/LinkButtonStyle.ts +++ b/components/common/atoms/LinkButtonStyle.ts @@ -1,6 +1,7 @@ import styled from "styled-components"; import { theme } from "@/styles/theme"; import Link from "next/link"; +import { font24 } from "@/styles/commonStyle"; export const LinkModule = styled(Link)` @@ -21,6 +22,11 @@ export const LinkModule = styled(Link)` width: 80px; } } + &--title { + &-text { + ${font24} + } + } } &.full { width: 100% !important; diff --git a/components/common/interface.ts b/components/common/interface.ts deleted file mode 100644 index 26e97fa76..000000000 --- a/components/common/interface.ts +++ /dev/null @@ -1,13 +0,0 @@ -// user login info -export interface IHeaderUserLoginInfoApi { - data: [ - { - id:number, - email:string, - name?:string, - image_source?:string, - created_at?:string, - auth_id:string - } - ] -} \ No newline at end of file diff --git a/components/folder/ContantList.tsx b/components/folder/ContantList.tsx deleted file mode 100644 index 1c1e67f33..000000000 --- a/components/folder/ContantList.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { IFolderContent } from '@/pages/folder/interface'; -import PostCard from './PostCard'; -import { EmptyBox, PostCardWrap } from '@/pages/folder/folderStyle'; - - -interface IFolderList { - contant: IFolderContent[] | [] | undefined | null; -} - -function ContantList({ contant }: IFolderList) { - return ( -
- { - contant ? ( - - {contant?.map((data) => ( - - ))} - - ) : ( - 저장된 링크가 없습니다. - ) - } -
- ); -} -export default ContantList; diff --git a/components/folder/FolderButtonList.tsx b/components/folder/FolderButtonList.tsx index e6f961449..e5da8cff0 100644 --- a/components/folder/FolderButtonList.tsx +++ b/components/folder/FolderButtonList.tsx @@ -5,21 +5,21 @@ import { IFolderMenuButtonApi } from '@/pages/folder/interface'; interface IButtonList { $menu: IFolderMenuButtonApi | null; - $btnActive: number | string; + $activeBtnId: number; onClick: (id:number) => void; } function FolderButtonList({ $menu, - $btnActive, + $activeBtnId, onClick, }: IButtonList) { return ( {cardMenuShow && (
- + */}
)} diff --git a/components/folder/PostCardList.tsx b/components/folder/PostCardList.tsx new file mode 100644 index 000000000..fbf1da7e4 --- /dev/null +++ b/components/folder/PostCardList.tsx @@ -0,0 +1,21 @@ +import { memo } from 'react'; +import { IFolderContent } from '@/pages/folder/interface'; +import PostCard from './PostCard'; +import { EmptyBox, PostCardWrap } from '@/pages/folder/folderStyle'; + + +interface IFolderList { + $content: IFolderContent[] | undefined | null; +} + +function PostCardList({ $content }: IFolderList) { + if(!$content) return 저장된 링크가 없습니다. + return ( + + {$content?.map((data) => ( + + ))} + + ); +} +export default memo(PostCardList); diff --git a/components/share/ShareModal.tsx b/components/share/ShareModal.tsx index 4be74d641..70b288f21 100644 --- a/components/share/ShareModal.tsx +++ b/components/share/ShareModal.tsx @@ -1,4 +1,4 @@ -import { snsShare } from '../../constant/share'; +import { snsShare } from '@/src/constant/share'; import Button from '../common/atoms/Button'; import { ShareBox } from './shareStyle'; diff --git a/components/share/shareStyle.ts b/components/share/shareStyle.ts index 2a5cee387..9431cd153 100644 --- a/components/share/shareStyle.ts +++ b/components/share/shareStyle.ts @@ -1,4 +1,18 @@ import { DFlaxAlignCenterBtw } from "@/styles/commonStyle"; +import { theme } from "@/styles/theme"; import styled from "styled-components"; -export const ShareBox = styled(DFlaxAlignCenterBtw)`` \ No newline at end of file +export const ShareBox = styled(DFlaxAlignCenterBtw)` + .tab-title { + margin-bottom: 1.5rem; + font-weight: 700; + } + @media screen and (max-width: ${theme.screenSize.moLarge}) { + flex-direction: column; + align-items: flex-start; + padding-bottom: 1.15rem; + .tab-title { + margin-bottom: 0.75rem; + } + } +` \ No newline at end of file diff --git a/lib/axios.ts b/lib/axios.ts new file mode 100644 index 000000000..28eb6adc2 --- /dev/null +++ b/lib/axios.ts @@ -0,0 +1,7 @@ +import axios from "axios"; + +const instance = axios.create({ + baseURL:'https://bootcamp-api.codeit.kr/api/users/1' +}); + +export default instance; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index e3d06f454..adae6bbb6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,8 @@ "name": "fe-weekly-mission", "version": "0.1.0", "dependencies": { + "axios": "^1.6.8", + "date-fns": "^3.6.0", "next": "13.5.6", "react": "^18", "react-dom": "^18", @@ -1277,6 +1279,11 @@ "has-symbols": "^1.0.3" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/available-typed-arrays": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", @@ -1298,6 +1305,16 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", @@ -1484,6 +1501,17 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1540,6 +1568,15 @@ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true }, + "node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -1594,6 +1631,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -2315,6 +2360,25 @@ "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -2324,6 +2388,19 @@ "is-callable": "^1.1.3" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3200,6 +3277,25 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -3599,6 +3695,11 @@ "react-is": "^16.13.1" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", diff --git a/package.json b/package.json index ba2070910..86310f8f0 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,8 @@ "lint": "next lint" }, "dependencies": { + "axios": "^1.6.8", + "date-fns": "^3.6.0", "next": "13.5.6", "react": "^18", "react-dom": "^18", diff --git a/pages/404.tsx b/pages/404.tsx index e9895ae98..7552ed144 100644 --- a/pages/404.tsx +++ b/pages/404.tsx @@ -7,7 +7,7 @@ export default function NotFound() { 페이지 작업 중 입니다.
- 페이지 이동 + 페이지 이동
); diff --git a/pages/faq/index.tsx b/pages/faq/index.tsx index 82035918d..c7552b6dd 100644 --- a/pages/faq/index.tsx +++ b/pages/faq/index.tsx @@ -1,10 +1,9 @@ - +import { InnerLarge } from "@/styles/commonStyle"; export default function Faq() { return (
- faq - + Faq
); } \ No newline at end of file diff --git a/pages/folder/folderStyle.ts b/pages/folder/folderStyle.ts index 67c5379a2..d8d75b607 100644 --- a/pages/folder/folderStyle.ts +++ b/pages/folder/folderStyle.ts @@ -73,7 +73,7 @@ export const BodyInner = styled(ContainBodyInner)` export const PostCardWrap = styled.div` display: grid; - grid-template-columns: repeat(3, 1fr); + grid-template-columns: repeat(3, minmax(33.3333%, 1fr)); gap: 1.5625rem 1.25rem; @media screen and (max-width: 1124px) { diff --git a/pages/folder/index.tsx b/pages/folder/index.tsx index d0e6140cd..70fc2fa04 100644 --- a/pages/folder/index.tsx +++ b/pages/folder/index.tsx @@ -1,32 +1,58 @@ -import { IModal } from "@/components/modal/interface"; -import { FOLDER_CONTANT_LIST_API, FOLDER_MENU_LIST_API } from "@/src/constant/api"; -import useFetch from "@/src/hook/useFetch"; -import { useState } from "react"; -import { IFolderContentApi, IFolderMenuButtonApi } from "./interface"; +import { useEffect, useState } from "react"; +import { ContainBody } from "@/styles/commonStyle"; import { modalOrder } from "@/src/constant/modal"; -import { BodyInner, BookmarkBox, FolderContainHead } from "./folderStyle"; import LinkAddHeader from "@/components/folder/LinkAddHeader"; -import { ContainBody } from "@/styles/commonStyle"; import SearchInputBox from "@/components/folder/SearchInputBox"; import FolderButtonList from "@/components/folder/FolderButtonList"; import Button from "@/components/common/atoms/Button"; import FolderContentControll from "@/components/folder/FolderContentControll"; -import ContantList from "@/components/folder/ContantList"; -import Loading from "@/components/loading/Loading"; +import PostCardList from "@/components/folder/PostCardList"; import Modal from "@/components/modal/Modal"; +import { IModal } from "@/components/modal/interface"; +import Loading from "@/components/loading/Loading"; +import { BodyInner, BookmarkBox, FolderContainHead } from "./folderStyle"; +import { IFolderContentApi, IFolderMenuButtonApi} from "./interface"; +import axios from "@/lib/axios"; +import { useRouter } from "next/router"; +import { GetServerSidePropsContext } from "next"; const addImage = '/assets/icon/icon_primary_add.svg'; const searchImage = '/assets/icon/icon_search.svg'; const linkImage = '/assets/icon/icon_primaty_link.svg'; -function useFatchDataLoad(api: string) { - return useFetch(api); +export async function getServerSideProps(contaxt:GetServerSidePropsContext) { + let $menu; + let $content; + const { query } = contaxt; + try { + let resContent ; + const resMenu = await axios.get(`/folders`); + if(query.id) { + resContent = await axios.get(`/links?folderId=${query.id}`); + } else { + resContent = await axios.get(`/links`); + } + $menu = resMenu.data; + $content = resContent.data; + + } catch (error) { + return { + notFound: true, + }; + } + + return { + props:{ + $menu, + $content, + } + } } -export default function Folder() { - const [title, setTitle] = useState('전체'); - const [btnActive, setBtnActive] = useState(-1); - const [dynamicAPI, setDynamicAPI] = useState(FOLDER_CONTANT_LIST_API); // 버튼리스트 클릭시 해당 컨텐트 노출 +export default function Folder({$menu, $content}: {$menu:IFolderMenuButtonApi, $content:IFolderContentApi}) { + const router = useRouter(); + const [tabTitle, setTabTitle] = useState('전체'); + const [activeBtn, setActiveBtn] = useState(-1); const [modalShow, setModalShow] = useState(false); const [modalInfo, setModalInfo] = useState({ $title: '', @@ -36,27 +62,21 @@ export default function Folder() { $buttonText: null, $modalData: null, }); - const { value: menu, isLoading: menuLoading } = - useFatchDataLoad(FOLDER_MENU_LIST_API); - const { value: contant, isLoading: contantLoading } = - useFatchDataLoad(dynamicAPI); - const [searchContatn, setSearchContant] = useState(); + const [searchContatn, setSearchContent] = useState(); // 폴더리스트버튼 const handleClick = (id: number) => { if (!id) return; - if (id < 0) { - setDynamicAPI(FOLDER_CONTANT_LIST_API); - setTitle('전체'); - setBtnActive(id) - return; + if(id === -1) { + router.push(``) + setTabTitle('전체'); + } else{ + router.push(`?id=${id}`) + let title = $menu.data.filter(data => `${data.id}` === `${id}`); + setTabTitle(`${title[0].name}`); } - - setBtnActive(id); - setDynamicAPI(`${FOLDER_CONTANT_LIST_API}?folderId=${id}`); - const result = menu?.data.filter((data) => +data.id === +id); - result && setTitle(result[0]?.name as ''); + setActiveBtn(id) }; // modal open @@ -65,7 +85,7 @@ export default function Folder() { if (type === 'folderInAdd') { modalInfo = { ...modalInfo, - $modalData: menu, + $modalData: $menu, }; } setModalInfo(modalInfo); @@ -81,7 +101,7 @@ export default function Folder() { const handelSearch = (value: string) => { let filter; if (value) { - filter = contant?.data.filter((con) => { + filter = $content?.data.filter((con) => { if (!con) return; return ( con.description?.includes(value) || @@ -89,23 +109,37 @@ export default function Folder() { con.url?.includes(value) ); }); - setSearchContant(filter); + setSearchContent(filter); return; } - setSearchContant(contant?.data); + setSearchContent($content?.data); }; - // contant list - const contantSearch = searchContatn ? searchContatn : contant?.data; - const loading = menuLoading === false && contantLoading === false; + // search + const contentSearch = searchContatn ?? $content?.data; + useEffect(()=>{ + let idExists ; + + if(router.query.id){ // folder가 있을때 + idExists = $menu.data.some((item:any) => `${item.id}` === `${router.query.id}`); + } else if (router.query.id === '-1') { // 전체 일때 + router.push(`/folder`) + } + + if (idExists === false) { // folder가 없을때 + alert('페이지가 없습니다.'); + router.push(`/folder`) + } + },[]) + + if(!$menu || !$content) return return ( <> - - {loading ? + {/* 검색창 */} @@ -113,8 +147,8 @@ export default function Folder() { {/* 폴더 리스트 버튼 */}