diff --git a/src/@types/emotion.d.ts b/src/@types/emotion.d.ts index e798997..5f3387d 100644 --- a/src/@types/emotion.d.ts +++ b/src/@types/emotion.d.ts @@ -76,6 +76,7 @@ declare module "@emotion/react" { Head4: ThemeTypography; Head5: ThemeTypography; Head6: ThemeTypography; + Head7: ThemeTypography; SubTitle1: ThemeTypography; SubTitle2: ThemeTypography; SubTitle3: ThemeTypography; diff --git a/src/components/Design/Pagination/Pagination.tsx b/src/components/Design/Pagination/Pagination.tsx index dde9ec7..185c1c2 100644 --- a/src/components/Design/Pagination/Pagination.tsx +++ b/src/components/Design/Pagination/Pagination.tsx @@ -6,9 +6,10 @@ import React, { useMemo, useState } from "react"; -import { Icon, Row, WhatIF } from "@/components"; +import { Icon, Row, Typography, WhatIF } from "@/components"; import * as S from "./emotion"; import { lightThemeColor } from "@/styles/theme"; +import { Converter } from "@/utils"; interface IPaginationProps { count: number; @@ -52,8 +53,11 @@ export default function Pagination({ return ( @@ -64,7 +68,7 @@ export default function Pagination({ props.onChange?.(ev, Number(prevPage)); }} > - + {totalList.map((page) => { return ( @@ -83,7 +87,6 @@ export default function Pagination({ `${page}` === `${currentPage}` ? "white" : `${lightThemeColor.neutral_90}`, - fontSize: 34, }} disabled={`${currentPage}` === `${page}`} onClick={(ev) => { @@ -91,7 +94,7 @@ export default function Pagination({ props.onChange?.(ev, Number(page)); }} > - {page} + {page} @@ -105,7 +108,7 @@ export default function Pagination({ props.onChange?.(ev, Number(nextPage)); }} > - + ); diff --git a/src/components/Design/Pagination/emotion.ts b/src/components/Design/Pagination/emotion.ts index 14d091b..6c9588a 100644 --- a/src/components/Design/Pagination/emotion.ts +++ b/src/components/Design/Pagination/emotion.ts @@ -7,11 +7,12 @@ import styled from "@emotion/styled"; import { Button, Row } from "@/components"; import { lightThemeColor } from "@/styles/theme"; +import { Converter } from "@/utils"; export const PaginationButton = styled.button` border-radius: 10px; - width: 58px; - height: 58px; + width: ${Converter.pxToRem(58)}; + height: ${Converter.pxToRem(58)}; align-items: center; justify-content: center; border: 1px solid ${lightThemeColor.neutral_40}; @@ -19,8 +20,9 @@ export const PaginationButton = styled.button` export const ArrowButton = styled.button` border-radius: 10px; - padding: 6px; border: 0px; + width: ${Converter.pxToRem(58)}; + height: ${Converter.pxToRem(58)}; align-items: center; justify-content: center; `; diff --git a/src/components/Provider/DonguramiProvider.tsx b/src/components/Provider/DonguramiProvider.tsx index 65a7b92..33e7352 100644 --- a/src/components/Provider/DonguramiProvider.tsx +++ b/src/components/Provider/DonguramiProvider.tsx @@ -59,7 +59,7 @@ const DonguramiThemeProvider = (props: HTMLAttributes) => { break; } case "light": { - document.body.style.backgroundColor = "#f7f7f7"; + document.body.style.backgroundColor = lightThemeColor.white; break; } } diff --git a/src/components/Svg/Icons/check-square-35.tsx b/src/components/Svg/Icons/check-square-35.tsx new file mode 100644 index 0000000..9a22f7d --- /dev/null +++ b/src/components/Svg/Icons/check-square-35.tsx @@ -0,0 +1,48 @@ +import React, { forwardRef, Ref } from "react"; +import { IconProps } from "../Icon"; +import Svg from "../Svg"; +const SvgCheckSquare35 = forwardRef( + ( + { size, title, desc, titleId, descId, ...props }: IconProps, + ref: Ref + ) => { + let ariaLabelledBy: string | undefined = titleId ? titleId : ""; + ariaLabelledBy += desc && descId ? ` ${descId}` : ""; + ariaLabelledBy = ariaLabelledBy ? ariaLabelledBy : undefined; + props["aria-labelledby"] = ariaLabelledBy; + return ( + + {!!title && {title}} + {!!desc && {desc}} + + + ); + } +); +SvgCheckSquare35.defaultProps = { + size: 24, + focusable: false, + "aria-hidden": true, + role: "img", + fill: "currentcolor", +}; +export default SvgCheckSquare35; diff --git a/src/components/Svg/Icons/index.ts b/src/components/Svg/Icons/index.ts index 6868eb5..aa8ed8b 100644 --- a/src/components/Svg/Icons/index.ts +++ b/src/components/Svg/Icons/index.ts @@ -6,3 +6,4 @@ export { default as LeftArrow24 } from "./left-arrow-24"; export { default as RightArrow24 } from "./right-arrow-24"; export { default as Search30 } from "./search-30"; export { default as Pan35 } from "./pan-35"; +export { default as CheckSquare35 } from "./check-square-35"; diff --git a/src/components/Svg/Icons/pan-35.tsx b/src/components/Svg/Icons/pan-35.tsx index 4e99f6f..1b7cee4 100644 --- a/src/components/Svg/Icons/pan-35.tsx +++ b/src/components/Svg/Icons/pan-35.tsx @@ -1,7 +1,7 @@ import React, { forwardRef, Ref } from "react"; import { IconProps } from "../Icon"; import Svg from "../Svg"; -const SvgSearch30 = forwardRef( +const SvgPan35 = forwardRef( ( { size, title, desc, titleId, descId, ...props }: IconProps, ref: Ref @@ -43,11 +43,11 @@ const SvgSearch30 = forwardRef( ); } ); -SvgSearch30.defaultProps = { +SvgPan35.defaultProps = { size: 30, focusable: false, "aria-hidden": true, role: "img", fill: "currentcolor", }; -export default SvgSearch30; +export default SvgPan35; diff --git a/src/components/UI/Board/Table/Table.tsx b/src/components/UI/Board/Table/Table.tsx new file mode 100644 index 0000000..8fa9865 --- /dev/null +++ b/src/components/UI/Board/Table/Table.tsx @@ -0,0 +1,139 @@ +/* + * Created on Fri Jan 19 2024 + * + * Copyright (c) 2024 Your Company + */ + +import React from "react"; + +import * as S from "./emotion"; +import { Button, Typography } from "@/components"; +import { lightThemeColor } from "@/styles/theme"; +import { Converter } from "@/utils"; + +interface PostData { + id: number; + type: string; +} + +interface TableData { + data: + | Swagger.Api.FreePostFindAllAndCount.ResponseBody + | Swagger.Api.NoticePostFindAllAndCount.ResponseBody; + type: string; + handleClickPostDetail: ({ id, type }: PostData) => void; +} + +export default function Table({ + data, + type, + handleClickPostDetail, +}: TableData) { + const slicedData = + type === "free" ? data.contents : data.contents.slice(0, 5); + + const getAuthorType = ( + post: Swagger.FreePostsItemDto | Swagger.NoticePostsItemDto + ) => { + if (type === "free" && "isAnonymous" in post && post.isAnonymous) { + return ( + + + 익명 + + + ); + } else if (type === "free" && "isAnonymous" in post && !post.isAnonymous) { + return ( + + + {post.userId} + + + ); + } else if (type === "notice") { + return ( + + + 운영자 + + + ); + } + }; + + return ( + + {slicedData.map((post) => { + return ( + handleClickPostDetail({ id: post.id, type })} + type={type} + > + + {type === "free" ? ( + + {post.id} + + ) : ( + + )} + + + + {post.title} + + + + {getAuthorType(post)} + + + {new Date(post.createdAt).toLocaleDateString()} + + + + + {post.hit} + + + + ); + })} + + ); +} diff --git a/src/components/UI/Board/Table/TableHeader.tsx b/src/components/UI/Board/Table/TableHeader.tsx new file mode 100644 index 0000000..fed3f9e --- /dev/null +++ b/src/components/UI/Board/Table/TableHeader.tsx @@ -0,0 +1,59 @@ +/* + * Created on Fri Jan 19 2024 + * + * Copyright (c) 2024 Your Company + */ + +import React from "react"; + +import * as S from "./emotion"; +import { Typography } from "@/components"; +import { Converter } from "@/utils"; + +interface PostData { + id: number; +} + +export default function TableHeader() { + return ( + + + + 순서 + + + + + 제목 + + + + + 작성자 + + + + + 작성일 + + + + + 조회 + + + + ); +} diff --git a/src/components/UI/Table/emotion.ts b/src/components/UI/Board/Table/emotion.ts similarity index 66% rename from src/components/UI/Table/emotion.ts rename to src/components/UI/Board/Table/emotion.ts index 7e53671..302df76 100644 --- a/src/components/UI/Table/emotion.ts +++ b/src/components/UI/Board/Table/emotion.ts @@ -1,16 +1,11 @@ import styled from "@emotion/styled"; import { lightThemeColor } from "@/styles/theme"; +import { Converter } from "@/utils"; -export const TableContainer = styled.table` - width: calc(100% - 512px); - min-width: 1408px; -`; -export const Table = styled.table` - width: 100%; - height: 100%; - border-collapse: collapse; -`; +interface Props { + type?: string; +} export const Thead = styled.thead` background-color: ${lightThemeColor.secondary_10}; @@ -22,19 +17,21 @@ export const Tbody = styled.tbody` vertical-align: top; `; -export const Tr = styled.tr` +export const Tr = styled.tr` border-bottom: 1px solid ${lightThemeColor.neutral_40}; + background-color: ${(props) => + props.type === "free" ? "#fff" : `${lightThemeColor.primary_10}`}; `; export const ThTitle = styled.th` border: none; padding: 20px 0px; - width: 50%; + width: 62.5%; + vertical-align: middle; `; export const Th = styled.th` border: none; padding: 20px 0px; - width: 12.5%; - text-align: center; + vertical-align: middle; `; diff --git a/src/components/UI/Table/index.ts b/src/components/UI/Board/Table/index.ts similarity index 68% rename from src/components/UI/Table/index.ts rename to src/components/UI/Board/Table/index.ts index 1e80554..73c4850 100644 --- a/src/components/UI/Table/index.ts +++ b/src/components/UI/Board/Table/index.ts @@ -5,3 +5,4 @@ */ export { default as Table } from "./Table"; +export { default as TableHeader } from "./TableHeader"; diff --git a/src/components/UI/Header/Header.tsx b/src/components/UI/Header/Header.tsx index d84555b..1bbc610 100644 --- a/src/components/UI/Header/Header.tsx +++ b/src/components/UI/Header/Header.tsx @@ -31,16 +31,7 @@ export default function Header({}: {}) { case "free-board": { router.push({ - pathname: "/board/free", - query: { - page: 1, - }, - }); - break; - } - case "notice-board": { - router.push({ - pathname: "/board/notice", + pathname: "/board", query: { page: 1, }, @@ -99,18 +90,7 @@ export default function Header({}: {}) { } hoverTypoColor="neutral_90" > - 수다 게시판 - - - 공지 게시판 + 동아리 게시판 diff --git a/src/components/UI/Quill/Quill.tsx b/src/components/UI/Quill/Quill.tsx index 0a3ac1f..5aad661 100644 --- a/src/components/UI/Quill/Quill.tsx +++ b/src/components/UI/Quill/Quill.tsx @@ -22,13 +22,19 @@ const modules = { const formats = [ "header", - "font", - "size", "bold", "italic", "underline", "strike", "blockquote", + "list", + "bullet", + "indent", + "link", + "image", + "align", + "color", + "background", ]; interface QuillProps { @@ -79,6 +85,7 @@ export default function Quill({ }} > + + + -
); diff --git a/src/components/UI/Table/Table.tsx b/src/components/UI/Table/Table.tsx deleted file mode 100644 index 27b2bbd..0000000 --- a/src/components/UI/Table/Table.tsx +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Created on Fri Jan 19 2024 - * - * Copyright (c) 2024 Your Company - */ - -import React from "react"; - -import * as S from "./emotion"; -import { Typography } from "@/components"; - -interface PostData { - id: number; -} - -interface TableData { - data: - | Swagger.Api.FreePostFindAllAndCount.ResponseBody - | Swagger.Api.NoticePostFindAllAndCount.ResponseBody; - type: string; - handleClickPostDetail: (el: PostData) => void; -} - -export default function Table({ - data, - type, - handleClickPostDetail, -}: TableData) { - const getAuthorType = ( - post: Swagger.FreePostsItemDto | Swagger.NoticePostsItemDto - ) => { - if (type === "free" && "isAnonymous" in post && post.isAnonymous) { - return ( - - - 익명 - - - ); - } else if (type === "free" && "isAnonymous" in post && !post.isAnonymous) { - return ( - - - {post.userId} - - - ); - } else if (type === "notice") { - return ( - - - 운영자 ? - - - ); - } - }; - - return ( - - - - - - - 순서 - - - - - 제목 - - - - - 작성자 - - - - - 작성일 - - - - - 조회 - - - - - - {data.contents.map((post) => { - return ( - handleClickPostDetail(post)}> - - - {post.id} - - - - - {post.title} - - - - {getAuthorType(post)} - - - {new Date(post.createdAt).toLocaleDateString()} - - - - - {post.hit} - - - - ); - })} - - - - ); -} diff --git a/src/containers/Board/DetailBoard/DetailBoard.tsx b/src/containers/Board/DetailBoard/DetailBoard.tsx new file mode 100644 index 0000000..459806f --- /dev/null +++ b/src/containers/Board/DetailBoard/DetailBoard.tsx @@ -0,0 +1,101 @@ +import { useRouter } from "next/router"; +import { useQuery, useMutation } from "@tanstack/react-query"; +import { useEffect } from "react"; + +import * as S from "./emotion"; +import { freePostsAPI, noticePostsAPI } from "@/apis"; +import { + FreePostDetailResponseDto, + NoticePostDetailResponseDto, +} from "@/apis/data-contracts"; + +export default function DetailBoard() { + const router = useRouter(); + const { postId, type } = router.query; + + const { data } = useQuery({ + queryFn: async () => { + let response; + if (type === "free") { + response = await freePostsAPI.freePostFindOneOrNotFound(Number(postId)); + } else { + response = await noticePostsAPI.noticePostFindOneOrNotFound( + Number(postId) + ); + } + + return response.data; + }, + + queryKey: ["post", postId, type], + enabled: postId !== undefined, + }); + + const { mutate } = useMutation({ + mutationKey: ["post", postId, type], + mutationFn: async () => { + let response; + if (type === "free") { + response = await freePostsAPI.freePostIncrementHit(Number(postId)); + } else { + response = await noticePostsAPI.noticePostIncreaseHit(Number(postId)); + } + }, + onSuccess() { + console.log(data); + }, + onError(error) { + console.log(error); + }, + }); + + useEffect(() => { + if (data) { + mutate(); + } + }, [data, mutate]); + + const handleClickDelete = () => { + freePostsAPI.freePostRemove(Number(postId)).then(() => { + router.back(); + }); + }; + + const handleClickUpdate = async () => { + router.push({ + pathname: `/board/free/write/`, + query: { + Id: postId, + }, + }); + }; + + return ( + + + { + (type === "free" + ? (data as FreePostDetailResponseDto)?.freePost + : (data as NoticePostDetailResponseDto)?.noticePost + )?.title + } + + + 수정 + 삭제 + + + + + { + (type === "free" + ? (data as FreePostDetailResponseDto)?.freePost + : (data as NoticePostDetailResponseDto)?.noticePost + )?.description + } + + + + + ); +} diff --git a/src/containers/Board/DetailFreeBoard/emotion.ts b/src/containers/Board/DetailBoard/emotion.ts similarity index 100% rename from src/containers/Board/DetailFreeBoard/emotion.ts rename to src/containers/Board/DetailBoard/emotion.ts diff --git a/src/containers/Board/DetailBoard/index.ts b/src/containers/Board/DetailBoard/index.ts new file mode 100644 index 0000000..ba954e2 --- /dev/null +++ b/src/containers/Board/DetailBoard/index.ts @@ -0,0 +1 @@ +export { default as DetailBoard } from "./DetailBoard"; diff --git a/src/containers/Board/DetailFreeBoard/DetailFreeBoard.tsx b/src/containers/Board/DetailFreeBoard/DetailFreeBoard.tsx deleted file mode 100644 index 615960b..0000000 --- a/src/containers/Board/DetailFreeBoard/DetailFreeBoard.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { useRouter } from "next/router"; -import { useQuery } from "@tanstack/react-query"; -import { useEffect } from "react"; - -import * as S from "./emotion"; -import { freePostsAPI } from "@/apis"; - -export default function DetailFreeBoard() { - const router = useRouter(); - const { postId } = router.query; - - const { data, isFetched } = useQuery({ - queryFn: async () => { - const response = await freePostsAPI.freePostFindOneOrNotFound( - Number(postId) - ); - - return response.data; - }, - - queryKey: ["post", postId], - enabled: postId !== undefined, - }); - - useEffect(() => { - if (isFetched) { - incrementPostViews(); - } - }, [isFetched]); - - const incrementPostViews = async () => { - await freePostsAPI.freePostIncrementHit(Number(postId)); - }; - - const handleClickDelete = () => { - freePostsAPI.freePostRemove(Number(postId)).then(() => { - router.back(); - }); - }; - - const handleClickUpdate = async () => { - router.push({ - pathname: `/board/free/write/`, - query: { - Id: postId, - }, - }); - }; - - return ( - - {data?.freePost.title} - - 수정 - 삭제 - - - - {data?.freePost.description} - - - - ); -} diff --git a/src/containers/Board/DetailFreeBoard/index.ts b/src/containers/Board/DetailFreeBoard/index.ts deleted file mode 100644 index f1006b1..0000000 --- a/src/containers/Board/DetailFreeBoard/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as DetailFreeBoard } from "./DetailFreeBoard"; diff --git a/src/containers/Board/DetailNoticeBoard/DetailNoticeBoard.tsx b/src/containers/Board/DetailNoticeBoard/DetailNoticeBoard.tsx deleted file mode 100644 index eb6257f..0000000 --- a/src/containers/Board/DetailNoticeBoard/DetailNoticeBoard.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { useRouter } from "next/router"; -import { useQuery } from "@tanstack/react-query"; -import { useEffect } from "react"; - -import * as S from "./emotion"; -import { noticePostsAPI } from "@/apis"; - -export default function DetailNoticeBoard() { - const router = useRouter(); - const { postId } = router.query; - - const { data, isFetched } = useQuery({ - queryFn: async () => { - const response = await noticePostsAPI.noticePostFindOneOrNotFound( - Number(postId) - ); - - return response.data; - }, - queryKey: ["post", postId], - enabled: postId !== undefined, - }); - - useEffect(() => { - if (isFetched) { - incrementPostViews(); - } - }, [isFetched]); - - const incrementPostViews = async () => { - await noticePostsAPI.noticePostIncreaseHit(Number(postId)); - }; - - const handleClickDelete = () => { - noticePostsAPI.noticePostRemove(Number(postId)).then(() => { - router.back(); - }); - }; - - const handleClickUpdate = async () => { - router.push({ - pathname: `/board/notice/write/`, - query: { - Id: postId, - }, - }); - }; - - return ( - - {data?.noticePost.title} - - 수정 - 삭제 - - - - {data?.noticePost.description} - - - - ); -} diff --git a/src/containers/Board/DetailNoticeBoard/emotion.ts b/src/containers/Board/DetailNoticeBoard/emotion.ts deleted file mode 100644 index 8ac8633..0000000 --- a/src/containers/Board/DetailNoticeBoard/emotion.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Button, Row } from "@/components"; -import styled from "@emotion/styled"; - -export const Container = styled.div` - width: 100vw; - height: 100vh; - flex-direction: column; - background-color: #fff; -`; - -export const Title = styled.h2` - padding: 12px 20px; -`; - -export const WrapBar = styled(Row)` - padding: 12px 20px; - justify-content: flex-end; -`; - -export const Btn = styled(Button)` - padding: 6px; - margin-right: 12px; -`; - -export const WrapDesc = styled.div` - padding: 12px 20px; - margin: 12px 20px; - border: 1px solid #222; -`; - -export const Desc = styled.span``; diff --git a/src/containers/Board/DetailNoticeBoard/index.ts b/src/containers/Board/DetailNoticeBoard/index.ts deleted file mode 100644 index 886cbb8..0000000 --- a/src/containers/Board/DetailNoticeBoard/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as DetailNoticeBoard } from "./DetailNoticeBoard"; diff --git a/src/containers/Board/SearchWriter/SearchWriter.tsx b/src/containers/Board/SearchWriter/SearchWriter.tsx index 0131a32..b5968ab 100644 --- a/src/containers/Board/SearchWriter/SearchWriter.tsx +++ b/src/containers/Board/SearchWriter/SearchWriter.tsx @@ -21,7 +21,8 @@ export default function SearchWriter({ type }: BoardType) { function handleClickPostWrite() { router.push({ - pathname: `free/write/`, + pathname: `board/write/`, + query: type, }); } diff --git a/src/containers/Board/SearchWriter/emotion.ts b/src/containers/Board/SearchWriter/emotion.ts index 7e544c1..34cf72c 100644 --- a/src/containers/Board/SearchWriter/emotion.ts +++ b/src/containers/Board/SearchWriter/emotion.ts @@ -1,11 +1,14 @@ import { Button, Row } from "@/components"; import { lightThemeColor } from "@/styles/theme"; import styled from "@emotion/styled"; +import { Converter } from "@/utils"; export const Container = styled(Row.div)` - margin-top: 80px; - width: calc(100% - 512px); - min-width: 1408px; + margin-top: ${Converter.pxToRem(80)}; + + width: calc(100% - ${Converter.pxToRem(527)}); + min-width: ${Converter.pxToRem(753)}; + justify-content: space-between; `; export const WrapSearch = styled(Row.li)` @@ -42,7 +45,7 @@ export const Input = styled.input` border-top-right-radius: 10px; border-bottom-right-radius: 10px; width: 60%; - min-width: 844px; + min-width: ${Converter.pxToRem(231)}; &::placeholder { font-size: 28px; /* Remove the quotes */ line-height: normal; /* Remove the quotes */ diff --git a/src/containers/Board/WriteBoard/WriteBoard.tsx b/src/containers/Board/WriteBoard/WriteBoard.tsx new file mode 100644 index 0000000..7c43790 --- /dev/null +++ b/src/containers/Board/WriteBoard/WriteBoard.tsx @@ -0,0 +1,107 @@ +import { useRouter } from "next/router"; +import { useQuery, useQueryClient } from "@tanstack/react-query"; +import { useEffect, useState } from "react"; + +import * as S from "./emotion"; +import { Quill } from "@/components"; +import { freePostsAPI, noticePostsAPI } from "@/apis"; + +export default function WriteBoard() { + const router = useRouter(); + const { id, type } = router.query; + const queryClient = useQueryClient(); + + const { data } = useQuery({ + queryFn: async () => { + let response; + if (type === "free") { + response = await freePostsAPI.freePostFindOneOrNotFound(Number(id)); + } else { + response = await noticePostsAPI.noticePostFindOneOrNotFound(Number(id)); + } + + return response.data; + }, + + queryKey: ["post", id, type], + enabled: id !== undefined, + }); + + const [value, setValue] = useState<{ + title: string; + description: string; + isAnonymous: boolean; + isAllowComment: boolean; + }>({ + title: "", + description: "", + isAnonymous: false, + isAllowComment: false, + }); + + const handleClickUpdate = async () => { + if (!value) return; + const contentWithoutPTag = value.description.replace(/^

|<\/p>$/g, ""); + const params = { + title: value.title, + description: contentWithoutPTag, + }; + + let postData; + let postApi; + let isCreate; + + if (type === "free") { + postData = "freePost"; + postApi = freePostsAPI; + isCreate = !id; + } else { + postData = "noticePost"; + postApi = noticePostsAPI; + isCreate = !id; + } + + if (type === "free") { + if (id) { + await freePostsAPI.freePostPatchUpdate(Number(id), params); + router.back(); + } else { + const { data } = await freePostsAPI.freePostCreate({ + ...params, + isAnonymous: value.isAnonymous, + }); + router.replace({ + pathname: `/board/free/detail/${data.freePost.id}`, + }); + } + } else { + if (id) { + await noticePostsAPI.noticePostPatchUpdate(Number(id), params); + router.back(); + } else { + const { data } = await noticePostsAPI.noticePostCreate({ + ...params, + isAllowComment: value.isAllowComment, + }); + router.replace({ + pathname: `/board/notice/detail/${data.noticePost.id}`, + }); + } + } + + queryClient.removeQueries(); + }; + + return ( + + + {value && ( + + )} + + ); +} diff --git a/src/containers/Board/WriteFreeBoard/emotion.ts b/src/containers/Board/WriteBoard/emotion.ts similarity index 100% rename from src/containers/Board/WriteFreeBoard/emotion.ts rename to src/containers/Board/WriteBoard/emotion.ts diff --git a/src/containers/Board/WriteBoard/index.ts b/src/containers/Board/WriteBoard/index.ts new file mode 100644 index 0000000..a04854f --- /dev/null +++ b/src/containers/Board/WriteBoard/index.ts @@ -0,0 +1 @@ +export { default as WriteBoard } from "./WriteBoard"; diff --git a/src/containers/Board/WriteFreeBoard/WriteFreeBoard.tsx b/src/containers/Board/WriteFreeBoard/WriteFreeBoard.tsx deleted file mode 100644 index 4d8499e..0000000 --- a/src/containers/Board/WriteFreeBoard/WriteFreeBoard.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { useRouter } from "next/router"; -import { useQuery, useQueryClient } from "@tanstack/react-query"; -import { useState } from "react"; - -import * as S from "./emotion"; -import { Quill } from "@/components"; -import { freePostsAPI } from "@/apis"; - -export default function WriteFreeBoard() { - const router = useRouter(); - const { id } = router.query; - const queryClient = useQueryClient(); - - const { data } = useQuery({ - queryFn: async () => { - const response = await freePostsAPI.freePostFindOneOrNotFound(Number(id)); - return response.data; - }, - queryKey: ["post", id], - enabled: id !== undefined, - }); - - const [value, setValue] = useState<{ - title: string; - description: string; - isAnonymous: boolean; - isAllowComment: boolean; - }>({ - title: data?.freePost.title || "", - description: data?.freePost.description || "", - isAnonymous: data?.freePost.isAnonymous || false, - isAllowComment: false, - }); - - const handleClickUpdate = async () => { - const contentWithoutPTag = value.description.replace(/^

|<\/p>$/g, ""); - const params = { - title: value.title, - description: contentWithoutPTag, - isAnonymous: value.isAnonymous, - }; - - if (id) { - await freePostsAPI.freePostPatchUpdate(Number(id), params); - router.back(); - } else { - const { data } = await freePostsAPI.freePostCreate({ - ...params, - }); - router.replace({ - pathname: `/board/free/detail/${data.freePost.id}`, - }); - } - queryClient.removeQueries(); - }; - - return ( - - - - - ); -} diff --git a/src/containers/Board/WriteFreeBoard/index.ts b/src/containers/Board/WriteFreeBoard/index.ts deleted file mode 100644 index 0098f33..0000000 --- a/src/containers/Board/WriteFreeBoard/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as WriteFreeBoard } from "./WriteFreeBoard"; diff --git a/src/containers/Board/WriteNoticeBoard/WriteNoticeBoard.tsx b/src/containers/Board/WriteNoticeBoard/WriteNoticeBoard.tsx deleted file mode 100644 index b9d7387..0000000 --- a/src/containers/Board/WriteNoticeBoard/WriteNoticeBoard.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { useRouter } from "next/router"; -import { useQuery, useQueryClient } from "@tanstack/react-query"; -import { useState } from "react"; - -import * as S from "./emotion"; -import { Quill } from "@/components"; -import { noticePostsAPI } from "@/apis"; - -export default function WriteNoticeBoard() { - const router = useRouter(); - const { id } = router.query; - const queryClient = useQueryClient(); - - const { data } = useQuery({ - queryFn: async () => { - const response = await noticePostsAPI.noticePostFindOneOrNotFound( - Number(id) - ); - - return response.data; - }, - queryKey: ["post", id], - - enabled: id !== undefined, - }); - - const [value, setValue] = useState<{ - title: string; - description: string; - isAnonymous: boolean; - isAllowComment: boolean; - }>({ - title: data?.noticePost.title || "", - description: data?.noticePost.description || "", - isAnonymous: false, - isAllowComment: data?.noticePost.isAllowComment || false, - }); - - const handleClickUpdate = async () => { - const contentWithoutPTag = value.description.replace(/^

|<\/p>$/g, ""); - const params = { - title: value.title, - description: contentWithoutPTag, - isAllowComment: value.isAllowComment, - }; - - if (id) { - await noticePostsAPI.noticePostPutUpdate(Number(id), params); - router.back(); - } else { - const { data } = await noticePostsAPI.noticePostCreate(params); - router.replace({ - pathname: `/board/notice/detail/${data.noticePost.id}`, - }); - } - - queryClient.removeQueries(); - }; - - return ( - - - - ); -} diff --git a/src/containers/Board/WriteNoticeBoard/emotion.ts b/src/containers/Board/WriteNoticeBoard/emotion.ts deleted file mode 100644 index abcb70b..0000000 --- a/src/containers/Board/WriteNoticeBoard/emotion.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Button, Row } from "@/components"; -import styled from "@emotion/styled"; - -export const Container = styled.div` - width: 100vw; - height: 100vh; - flex-direction: column; - background-color: #fff; -`; - -export const Title = styled.h2` - padding: 12px 20px; -`; - -export const WrapBar = styled(Row)` - padding: 12px 20px; - flex-direction: row; - justify-content: flex-end; -`; - -export const Btn = styled(Button)` - padding: 6px; -`; - -export const WrapDesc = styled.div` - padding: 12px 20px; - margin: 12px 20px; - border: 1px solid #222; -`; - -export const Desc = styled.span``; diff --git a/src/containers/Board/WriteNoticeBoard/index.ts b/src/containers/Board/WriteNoticeBoard/index.ts deleted file mode 100644 index a07d80d..0000000 --- a/src/containers/Board/WriteNoticeBoard/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as WriteNoticeBoard } from "./WriteNoticeBoard"; diff --git a/src/containers/Board/index.tsx b/src/containers/Board/index.tsx index 9443848..67260c2 100644 --- a/src/containers/Board/index.tsx +++ b/src/containers/Board/index.tsx @@ -6,7 +6,5 @@ export * from "./InfinityScrollBoard"; export * from "./PaginationBoard"; -export * from "./DetailFreeBoard"; -export * from "./DetailNoticeBoard"; -export * from "./WriteFreeBoard"; -export * from "./WriteNoticeBoard"; +export * from "./DetailBoard"; +export * from "./WriteBoard"; diff --git a/src/pages/board/free/detail/[postId].tsx b/src/pages/board/detail/[postId].tsx similarity index 76% rename from src/pages/board/free/detail/[postId].tsx rename to src/pages/board/detail/[postId].tsx index c69acad..a4e2bb7 100644 --- a/src/pages/board/free/detail/[postId].tsx +++ b/src/pages/board/detail/[postId].tsx @@ -6,7 +6,7 @@ import Head from "next/head"; -import { DetailFreeBoard } from "@/containers/Board"; +import { DetailBoard } from "@/containers/Board"; export default function PostDetailPage() { return ( @@ -14,7 +14,7 @@ export default function PostDetailPage() { 동그라미 - 자유 게시글 - + ); } diff --git a/src/pages/board/free/index.tsx b/src/pages/board/free/index.tsx deleted file mode 100644 index 8881914..0000000 --- a/src/pages/board/free/index.tsx +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Created on Fri Dec 15 2023 - * - * Copyright (c) 2023 Your Company - */ - -import { GetStaticProps } from "next"; -import { useRouter } from "next/router"; -import { useQuery } from "@tanstack/react-query"; -import Head from "next/head"; - -import { Column, Loader, Pagination, Typography, WhatIF } from "@/components"; -import { freePostsAPI } from "@/apis"; -import { Table } from "@/components/UI/Table"; -import { SearchWriter } from "@/containers/Board/SearchWriter"; - -interface PostData { - id: number; -} - -export default function FreeBoard(props: { boardName: string }) { - const router = useRouter(); - - const { data, isLoading, isError } = useQuery({ - queryKey: ["board", "free", router.query.page], - queryFn: async () => { - const page = router.query.page as string; - return ( - await freePostsAPI.freePostFindAllAndCount({ - page: Number(page), - pageSize: 20, - }) - ).data; - }, - }); - - function handleClickPostDetail(el: PostData) { - router.push({ - pathname: `free/detail/${el.id}`, - }); - } - - if (isError) throw new Error("게시판을 불러올 수 없습니다."); - return ( - <> - - 동그라미 - 수다 게시판 - - - - - {props.boardName} - - - }> - {data && ( - - )} - - - - { - router.push({ - pathname: "/board/free", - query: { - page, - }, - }); - }} - /> - - - - ); -} - -export const getStaticProps: GetStaticProps = async (ctx) => { - return { - props: { - boardName: "수다 게시판", - }, - }; -}; diff --git a/src/pages/board/index.tsx b/src/pages/board/index.tsx new file mode 100644 index 0000000..6811e0a --- /dev/null +++ b/src/pages/board/index.tsx @@ -0,0 +1,201 @@ +/* + * Created on Fri Dec 15 2023 + * + * Copyright (c) 2023 Your Company + */ + +import { GetStaticProps } from "next"; +import { useRouter } from "next/router"; +import { useQuery } from "@tanstack/react-query"; +import Head from "next/head"; + +import { + Button, + Column, + Icon, + Loader, + Pagination, + Typography, + WhatIF, +} from "@/components"; +import { freePostsAPI, noticePostsAPI } from "@/apis"; +import { Table, TableHeader } from "@/components/UI/Board/Table"; +import { SearchWriter } from "@/containers/Board/SearchWriter"; +import { Converter } from "@/utils"; +import { lightThemeColor } from "@/styles/theme"; +import { useState } from "react"; + +interface PostData { + id: number; + type: string; +} + +export default function FreeBoard(props: { boardName: string }) { + const router = useRouter(); + const [isNoticeVisible, setIsNoticeVisible] = useState(true); + + const { + data: freeData, + isLoading: isLoadingFree, + isError: isErrorFree, + } = useQuery({ + queryKey: ["board", "free", router.query.page], + queryFn: async () => { + const page = router.query.page as string; + return ( + await freePostsAPI.freePostFindAllAndCount({ + page: Number(page), + pageSize: 20, + }) + ).data; + }, + }); + + const { + data: noticeData, + isLoading: isLoadingNotice, + isError: isErrorNotice, + } = useQuery({ + queryKey: ["board", "notice", router.query.page], + queryFn: async () => { + const page = router.query.page as string; + return ( + await noticePostsAPI.noticePostFindAllAndCount({ + page: Number(page), + pageSize: 5, + }) + ).data; + }, + }); + + function handleClickPostDetail({ id, type }: PostData) { + router.push({ + pathname: `board/detail/${id}/`, + query: { type }, + }); + } + + function toggleNoticeVisibility() { + setIsNoticeVisible(!isNoticeVisible); + } + + if (isErrorFree || isErrorNotice) + throw new Error("게시판을 불러올 수 없습니다."); + return ( + <> + + 동그라미 - 게시판 + + + + + {props.boardName} + + + + + {isNoticeVisible ? ( + + )} + + + 공지 숨기기 + + + +
+ + {isNoticeVisible && noticeData && ( +
+ )} + + {freeData && ( +
+ )} +
+ + + + { + router.push({ + pathname: "/board/free", + query: { + page, + }, + }); + }} + /> +
+ + ); +} + +export const getStaticProps: GetStaticProps = async (ctx) => { + return { + props: { + boardName: "게시판", + }, + }; +}; diff --git a/src/pages/board/notice/detail/[postId].tsx b/src/pages/board/notice/detail/[postId].tsx deleted file mode 100644 index 8297409..0000000 --- a/src/pages/board/notice/detail/[postId].tsx +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Created on Wed Nov 15 2023 - * - * Copyright (c) 2023 Your Company - */ - -import Head from "next/head"; - -import { DetailNoticeBoard } from "@/containers/Board"; - -export default function PostDetailPage() { - return ( - <> - - 동그라미 - 공지 게시글 - - - - ); -} diff --git a/src/pages/board/notice/index.tsx b/src/pages/board/notice/index.tsx deleted file mode 100644 index 9d3e3fc..0000000 --- a/src/pages/board/notice/index.tsx +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Created on Fri Dec 15 2023 - * - * Copyright (c) 2023 Your Company - */ - -import { GetStaticProps } from "next"; -import { useRouter } from "next/router"; -import { useQuery } from "@tanstack/react-query"; -import Head from "next/head"; - -import { Column, Loader, Pagination, Typography, WhatIF } from "@/components"; -import { noticePostsAPI } from "@/apis"; -import { Table } from "@/components/UI/Table"; -import { SearchWriter } from "@/containers/Board/SearchWriter"; - -interface PostData { - id: number; -} - -export default function NoticeBoard(props: { boardName: string }) { - const router = useRouter(); - - const { data, isLoading, isError } = useQuery({ - queryKey: ["board", "notice", router.query.page], - queryFn: async () => { - const page = router.query.page as string; - return ( - await noticePostsAPI.noticePostFindAllAndCount({ - page: Number(page), - pageSize: 20, - }) - ).data; - }, - }); - - function handleClickPostDetail(el: PostData) { - router.push({ - pathname: `notice/detail/${el.id}`, - }); - } - - if (isError) throw new Error("게시판을 불러올 수 없습니다."); - return ( - <> - - 동그라미 - 공지 게시판 - - - - - {props.boardName} - - - }> - {data && ( - - )} - - - - { - router.push({ - pathname: "/board/notice", - query: { - page, - }, - }); - }} - /> - - - - ); -} - -export const getStaticProps: GetStaticProps = async (ctx) => { - return { - props: { - boardName: "공지 게시판", - }, - }; -}; diff --git a/src/pages/board/notice/write/index.tsx b/src/pages/board/notice/write/index.tsx deleted file mode 100644 index e6b4927..0000000 --- a/src/pages/board/notice/write/index.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import Head from "next/head"; - -import { WriteNoticeBoard } from "@/containers/Board"; - -export default function NoticeWritePage() { - return ( - <> - - 동그라미 - 공지 게시글 작성 - - - - ); -} diff --git a/src/pages/board/free/write/index.tsx b/src/pages/board/write/index.tsx similarity index 56% rename from src/pages/board/free/write/index.tsx rename to src/pages/board/write/index.tsx index f36148c..2379ebf 100644 --- a/src/pages/board/free/write/index.tsx +++ b/src/pages/board/write/index.tsx @@ -1,14 +1,14 @@ import Head from "next/head"; -import { WriteFreeBoard } from "@/containers/Board"; +import { WriteBoard } from "@/containers/Board"; -export default function FreeWritePage() { +export default function WritePage() { return ( <> 동그라미 - 자유 게시글 작성 - + ); } diff --git a/src/styles/theme/typography.ts b/src/styles/theme/typography.ts index 37c454f..638d594 100644 --- a/src/styles/theme/typography.ts +++ b/src/styles/theme/typography.ts @@ -37,6 +37,11 @@ const typography: Theme["typography"] = { lineHeight: "normal", fontWeight: 500, }, + Head7: { + fontSize: "1.3125rem", + lineHeight: "normal", + fontWeight: 700, + }, SubTitle1: { fontSize: "1.75rem", lineHeight: "normal",