diff --git a/components/_styled/addboardStyled.tsx b/components/_styled/addboardStyled.tsx new file mode 100644 index 00000000..ec773449 --- /dev/null +++ b/components/_styled/addboardStyled.tsx @@ -0,0 +1,24 @@ +import styled from "styled-components"; +export const AddBoardPage = styled.div` + width:100%; + display:flex; + flex-direction:column; + align-items:center; + justify-content:center; +`; +export const AddBoardPageContainer = styled.div` + max-width:1200px; + width:100%; + height:100%; + margin-top:24px; + display:flex; + gap:40px; + flex-direction:column; + + @media(max-width:1199px){ + max-width:696px; + } + @media(max-width:767px){ + max-width:343px; + } +`; \ No newline at end of file diff --git a/components/_styled/boardsStyled.tsx b/components/_styled/boardsStyled.tsx new file mode 100644 index 00000000..9dbbc45f --- /dev/null +++ b/components/_styled/boardsStyled.tsx @@ -0,0 +1,24 @@ +import styled from "styled-components"; + +export const BoardPage = styled.div` + width:100%; + display:flex; + flex-direction:column; + align-items:center; + justify-content:center; +`; +export const BoardPageContainer = styled.div` + max-width:1200px; + width:100%; + margin-top:24px; + display:flex; + gap:40px; + flex-direction:column; + + @media(max-width:1199px){ + max-width:696px; + } + @media(max-width:767px){ + max-width:343px; + } +`; \ No newline at end of file diff --git a/components/addboard/AddBoard.tsx b/components/addboard/AddBoard.tsx new file mode 100644 index 00000000..907ea344 --- /dev/null +++ b/components/addboard/AddBoard.tsx @@ -0,0 +1,148 @@ +import React, { useState, useRef, useEffect } from 'react'; +import * as AS from './Styled'; +import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons"; + +export interface AddItemValues { + image: File | null; + description: string; + title: string; +} + +export default function AddBoard() { + const [values, setValues] = useState({ + image: null, + title: '', + description: '', + }); + const [isValid, setIsValid] = useState(false); + const [preview, setPreview] = useState(null); + const [errorMessage, setErrorMessage] = useState(""); + const inputRef = useRef(null); + + const onChange = (name: keyof AddItemValues, value: File | null) => { + setValues((prevValues) => ({ + ...prevValues, + [name]: value, + })); + }; + + const handleTitleChange = (event: React.ChangeEvent) => { + const { value } = event.target; + setValues((prevValues) => ({ + ...prevValues, + title: value, + })); + }; + + const handleTextareaChange = (event: React.ChangeEvent) => { + const { value } = event.target; + setValues((prevValues) => ({ + ...prevValues, + description: value, + })); + }; + + const handleImageChange = (e: React.ChangeEvent) => { + const file: File | null = e.target.files?.[0] || null; + + if (file) { + if (values.image) { + setErrorMessage("이미지 파일이 이미 선택되었습니다."); + return; + } + onChange("image", file); + const objectUrl = URL.createObjectURL(file); + setPreview(objectUrl); + + } else { + onChange("image", null); + setPreview(null); + } + }; + + const handleClearClick = () => { + if (inputRef.current) { + inputRef.current.value = ''; + } + onChange("image", null); + setPreview(null); + setErrorMessage(""); + }; + + const isFormValid = (values: AddItemValues) => { + return ( + values.title.trim() !== '' && + values.description.trim() !== '' && + values.image !== null + ); + }; + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + if (!isFormValid(values)) { + return; + } + console.log(values); + + }; + + useEffect(() => { + setIsValid(isFormValid(values)); + }, [values]); + + return ( + + + 게시물 쓰기 + 등록 + + + + *제목 + + + + *내용 + + + + 이미지 + + {inputRef.current?.click();}}> + + + 이미지 등록 + + + {preview && ( + + )} + {values.image && ( + + )} + + + + {errorMessage && {errorMessage}} + + + + ); +} \ No newline at end of file diff --git a/components/addboard/Styled.tsx b/components/addboard/Styled.tsx new file mode 100644 index 00000000..d6de0208 --- /dev/null +++ b/components/addboard/Styled.tsx @@ -0,0 +1,189 @@ +import styled from "styled-components"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +export const AddBoardForm = styled.form` + width:100%; + height:100%; + display:flex; + flex-direction:column; + gap:32px; +`; + +export const TitleContainer = styled.div` + width:100%; + display:flex; + justify-content:space-between; +`; + +export const Title = styled.div` + color: #1F2937; + font-size: 20px; + font-weight: 700; + line-height: 32px; +`; + +export const AddButton = styled.button` + width:74px; + height: 42px; + display: flex; + justify-content: center; + align-items: center; + border-radius: 8px; + border:none; + background:#3692FF; + color: #F3F4F6; + font-size: 16px; + font-weight: 600; + line-height: 26px; + cursor:pointer; + &:disabled { + background:#9CA3AF; + } +`; + +export const InputFormContainer = styled.div` + width:100%; + display:flex; + gap:24px; + flex-direction:column; +`; +export const InputContainer = styled.div` + width:100%; + display:flex; + gap:12px; + flex-direction:column; +`; + +export const InputLabel = styled.label` + color: #1F2937; + font-size: 18px; + font-weight: 700; + line-height: 26px; +`; +export const ContentInput = styled.input` + display: flex; + height: 56px; + padding: 16px 24px; + border-radius: 12px; + background: #F3F4F6; + border:none; + z-index:0 + &::placeholder { + color: #9CA3AF; + font-size: 16px; + font-weight: 400; + line-height: 26px; + } +`; +export const TextareaInput = styled.textarea` + height: 250px; + padding: 16px 24px; + border:none; + border-radius: 12px; + background: var(--Cool-Gray-100, #F3F4F6); + resize:none; + &::placeholder { + color: #9CA3AF; + font-size: 16px; + font-weight: 400; + line-height: 26px; + } +`; + + +export const ImgBox = styled.div` + width: 588px; + display: flex; + gap: 24px; + + @media (max-width: 1199px) { + width: 346px; + gap: 10px; + } +`; + +export const CustomButton = styled.button` + width: 282px; + height: 282px; + display: flex; + background: var(--Secondary-200, #F3F4F6); + border-radius: 12px; + justify-content: center; + align-items: center; + flex-direction: column; + border: none; + @media (max-width: 1199px) { + width: 168px; + height: 168px; + } +`; + + +export const PlusIcon = styled(FontAwesomeIcon)` + width: 48px; + height: 48px; + background: none; + + @media (max-width: 1199px) { + width: 24px; + height: 24px; + } +`; + +export const ButtonText = styled.span` + color: #9CA3AF; + font-size: 16px; + font-weight: 400; + line-height: 26px; + background: none; +`; + +export const Upload = styled.div` + width: 282px; + height: 282px; + + @media (max-width: 1199px) { + width: 168px; + height: 168px; + } +`; + +export const Preview = styled.img` + width: 100%; + height: 100%; + background-color: blue; + border-radius: 12px; + object-fit: cover; +`; + +export const UploadButton = styled.button` + background: none; + border: none; + position: relative; + left: 248px; + bottom: 276px; + cursor: pointer; + color: #9CA3AF; + @media (max-width: 1199px) { + left: 130px; + bottom: 160px; + } +`; + +export const ErrorMessage = styled.span` + color: #F74747; + font-size: 16px; + font-weight: 400; + line-height: 26px; +`; +export const DeleteButton = styled(FontAwesomeIcon)` + width:24px; + height:24px; + position:relative; + bottom:270px; + left:240px; + @media(max-width:1199px) { + bottom:160px; + left:130px; + } +`; \ No newline at end of file diff --git a/components/api/api.tsx b/components/api/api.tsx new file mode 100644 index 00000000..9c4eca88 --- /dev/null +++ b/components/api/api.tsx @@ -0,0 +1,50 @@ +import axios from 'axios'; +import { ItemList } from '@/components/boards/PostList'; +import { Article } from '@/components/board/Article'; +import { CommentList } from '@/components/board/Comment'; +const BASE_URL = 'https://panda-market-api.vercel.app'; + +export async function getArticles(params = {}) { + const allowedParams = ["orderBy", "pageSize", "page", "keyword"]; + const invalidParams = Object.keys(params) + .filter(key => !allowedParams.includes(key)); + + if (invalidParams.length > 0) { + console.error(`Invalid parameters detected: ${invalidParams.join(", ")}`); + } + + try { + const response = await axios.get(`${BASE_URL}/articles`, { + params + }); + return response.data; + } catch (error) { + console.error("Failed to fetch articles:", error); + throw error; + } +} + +export async function getArticleDetail(articleId: number) { + try { + const response = await axios.get
(`${BASE_URL}/articles/${articleId}`); + return response.data; + } catch (error) { + console.error("Failed to fetch article details:", error); + throw error; + } +} + +export async function getArticleComment(articleId: number, params = { limit: 10, cursor: 0 }) { + try { + const response = await axios.get(`${BASE_URL}/articles/${articleId}/comments`, { + params: { + limit: params.limit, + cursor: params.cursor + } + }); + return response.data; + } catch (error) { + console.error("Failed to fetch comment details:", error); + throw error; + } +} diff --git a/components/board/Article.tsx b/components/board/Article.tsx new file mode 100644 index 00000000..e0233948 --- /dev/null +++ b/components/board/Article.tsx @@ -0,0 +1,65 @@ +import React, { useEffect, useCallback, useState } from 'react'; +import { useRouter } from 'next/router'; +import * as BS from '@/components/board/Styled'; +import LikeIcon from '@/components/common/images/ic_heart.png'; +import ProfileImg from '@/components/common/images/profile.png'; +import { getArticleDetail } from '../api/api'; +import formatDate from '../common/function/formatDate'; +export interface Article { + id: number; + title: string; + content: string; + images: string; + writer: Writer; + likeCount: number; + createdAt: string; +} + +export interface Writer { + nickname: string; + id: number; +} + +export default function Article() { + const router = useRouter(); + const id = router.query['id']; + const [item, setItem] = useState
(undefined); + + const fetchArticleData = useCallback(async () => { + try { + if (id) { + const product = await getArticleDetail(Number(id)); + setItem(product); + } + } catch (err) { + console.error(err); + } + }, [id]); + + useEffect(() => { + if (id) { + fetchArticleData(); + } + }, [fetchArticleData, id]); + + if (!item) { + return ; + } + + return ( + + {item.title} + + + {item.writer.nickname} + {formatDate(item.createdAt)} + + + {item.likeCount} + + + + {item.content} + + ); +} \ No newline at end of file diff --git a/components/board/Comment.tsx b/components/board/Comment.tsx new file mode 100644 index 00000000..afb943e0 --- /dev/null +++ b/components/board/Comment.tsx @@ -0,0 +1,75 @@ +import React, { useState, useCallback, useEffect } from 'react'; +import { useRouter } from 'next/router'; +import * as CS from './Styled'; +import { getArticleComment } from '../api/api'; +import CommentItem from './CommentItem'; + +export interface Writer { + image: string; + nickname: string; + id: number; +} + +export interface Comment { + writer: Writer; + createdAt: string; + content: string; + id: number; +} + +export interface CommentList{ + nextCursor: number; + list: Comment[]; +} + +export default function Comment() { + const [value, setValue] = useState(""); + const [isValid, setIsValid] = useState(false); + const router = useRouter(); + const id = router.query['id']; + const [itemList, setItemList] = useState({ nextCursor: 0, list: [] }); + const LIMIT = 6; + + const handleTextareaChange = (event: React.ChangeEvent) => { + setValue(event.target.value); + }; + + const handleLoad = useCallback(async () => { + try { + const comments = await getArticleComment(Number(id),{ limit: LIMIT, cursor: 0 }); + setItemList({ nextCursor: comments.nextCursor, list: comments.list }); + } catch (error) { + console.error("Failed to load comments:", error); + } + }, [id]); + + useEffect(() => { + setIsValid(value.trim() !== ''); + }, [value]); + + useEffect(() => { + handleLoad(); + }, [handleLoad]); + + return ( + + + 댓글달기 + + + 등록 + + + + {itemList.list.map((item) => ( + + ))} + + + ); +} \ No newline at end of file diff --git a/components/board/CommentItem.tsx b/components/board/CommentItem.tsx new file mode 100644 index 00000000..71d3e485 --- /dev/null +++ b/components/board/CommentItem.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import * as CS from "./Styled"; +import { Comment } from './Comment'; +import elapsedTime from '../common/function/elapsedTime'; +import defaultImg from '@/components/common/images/profile.png'; + +export interface CommentItemProps { + item:Comment; +} + +const CommentItem: React.FC = ({ item }) => { + return ( + + {item.content} + + + + {item.writer.nickname} + {elapsedTime(item.createdAt)} + + + + + ); +}; + +export default CommentItem; \ No newline at end of file diff --git a/components/board/Styled.tsx b/components/board/Styled.tsx new file mode 100644 index 00000000..a3e75e04 --- /dev/null +++ b/components/board/Styled.tsx @@ -0,0 +1,174 @@ +import styled from "styled-components"; +import Image from 'next/image'; + + +//BoardContent +export const ContentContainer = styled.div` + width:100%; + display:flex; + flex-direction:column; + gap:16px; +`; + +export const Title = styled.div` + color: #1F2937; + font-size: 20px; + font-weight: 700; + line-height: 32px; +`; +export const ContentInfoContainer = styled.div` + display: flex; + align-items: flex-start; + gap: 24px; + align-self: stretch; + align-items: center; +`; +export const WriterProfile = styled(Image)` + width:16px; + height:16px; +`; +export const WriterName = styled.div` + color: #4B5563; + font-size: 14px; + font-weight: 500; + line-height: 24px; +`; +export const WriteDate = styled.div` + color: #9CA3AF; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 24px; +`; +export const LikeContainer = styled.div` + display: flex; + height: 40px; + padding: 4px 12px; + align-items: flex-start; + gap: 10px; + border-radius: 35px; + border: 1px solid #E5E7EB; + background: #FFF; + align-items: center; +`; +export const LikeIcon = styled(Image)` + width:24px; + height:24px; +`; +export const LikeCount = styled.div` + color: var(--Cool-Gray-500, #6B7280); + font-size: 16px; + font-weight: 500; + line-height: 26px; +`; +export const Line = styled.hr` + size:1px; + width:100%; + color:#E5E7EB; +`; + +export const ArticleText = styled.div` + width:100%; + color: #1F2937; + font-size: 18px; + font-weight: 400; + line-height: 26px; +`; + +//Comment +export const CommentContainer = styled.div` + display:flex; + flex-direction: column; + gap:40px; +`; + +export const WriteCommentContainer = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + gap: 9px; +`; +export const CommentTitle = styled.div` + color: #111827; + font-size: 16px; + font-weight: 600; + line-height: 26px; +`; +export const CommentTextarea = styled.textarea` + height: 96px; + padding: 16px 24px; + border:none; + border-radius: 12px; + background: var(--Cool-Gray-100, #F3F4F6); + resize:none; + &::placeholder { + color: #9CA3AF; + font-size: 16px; + font-weight: 400; + line-height: 26px; + } +`; +export const CommentList = styled.div` + display:flex; + flex-direction:column; + gap:24px; +`; +export const CommentWrapper = styled.div` + display:flex; + flex-direction:column; + gap:24px; +`; +export const CommentText = styled.div` + color: #1F2937; + font-family: Pretendard; + font-size: 14px; + font-weight: 400; + line-height: 24px; +`; +export const CommentInfoContainer = styled.div` + display:flex; + gap:8px; +`; +export const CommentWriterImg = styled(Image)` + width:32px; + height:32px; +`; +export const CommentInfoWrapper = styled.div` + display:flex; + flex-direction:column; + gap:4px; +`; +export const CommentUser = styled.div` + color: #4B5563; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 18px; +`; +export const CommentDate = styled.div` + color: #9CA3AF; + font-size: 12px; + font-weight: 400; + line-height: 18px; +`; +export const CommentSubmitButton = styled.button` + display: flex; + height: 42px; + padding: 12px 23px; + justify-content: center; + align-items: center; + gap: 10px; + border-radius: 8px; + background: #3692FF; + color:#F3F4F6; + border:none; + + &:disabled { + background: #9CA3AF; + } +`; +export const CommentSubmitButtonWrapper = styled.div` + display:flex; + width:100%; + justify-content:right; +`; diff --git a/components/boards/BestPost.tsx b/components/boards/BestPost.tsx index 535b4a53..b63a6df3 100644 --- a/components/boards/BestPost.tsx +++ b/components/boards/BestPost.tsx @@ -2,15 +2,7 @@ import React, { useState, useEffect, useCallback} from 'react'; import * as BS from './Styled'; import BestPostCard from './BestPostCard'; import { Item, ItemList } from './PostList'; -import { getArticles } from '@/pages/api/api'; - -interface ItemCardProps { - item: Item; -} - -interface ItemListProps { - itemList : ItemList; -} +import { getArticles } from '@/components/api/api'; export default function BestPost() { const [itemList, setItemList] = useState({ totalCount: 0, list: [] }); @@ -20,7 +12,6 @@ export default function BestPost() { try { const articles = await getArticles({ orderBy: order, pageSize }); setItemList(articles); - console.log(articles); } catch (error) { console.error('Error fetching articles:', error); } diff --git a/components/boards/BestPostCard.tsx b/components/boards/BestPostCard.tsx index 152e5827..3e98cfe8 100644 --- a/components/boards/BestPostCard.tsx +++ b/components/boards/BestPostCard.tsx @@ -1,19 +1,24 @@ import React from 'react'; import * as BS from './Styled'; +import { useRouter } from 'next/router'; import MedalIcon from '../common/images/ic_medal.png'; import LikeIcon from '../common/images/ic_heart.png'; import DefaultImg from '../common/images/default.png'; import { Item } from './PostList'; import formatDate from '../common/function/formatDate'; - interface ItemCardProps { item:Item; } const BestPostCard:React.FC = ({ item }) => { + const router = useRouter(); + const handleCardClick = () => { + router.push(`/board/${item.id}`); + }; return ( - + + Best diff --git a/components/boards/PostCard.tsx b/components/boards/PostCard.tsx index 201c83f3..402f5720 100644 --- a/components/boards/PostCard.tsx +++ b/components/boards/PostCard.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { useRouter } from 'next/router'; import * as PS from './Styled'; import DefaultImg from '../common/images/default.png'; import LikeIcon from '../common/images/ic_heart.png'; @@ -11,8 +12,14 @@ interface ItemCardProps { } const PostCard: React.FC = ({ item }) => { + const router = useRouter(); + + const handleCardClick = () => { + router.push(`/board/${item.id}`); + }; + return ( - + {item.title} = ({ item }) => { {item.writer.nickname} diff --git a/components/boards/PostList.tsx b/components/boards/PostList.tsx index 95226531..02e202b2 100644 --- a/components/boards/PostList.tsx +++ b/components/boards/PostList.tsx @@ -2,7 +2,8 @@ import React, { useState, useEffect, useCallback } from 'react'; import { useRouter } from 'next/router'; import * as BS from './Styled'; import PostCard from './PostCard'; -import { getArticles } from '@/pages/api/api'; +import { getArticles } from '@/components/api/api'; + export interface Item { id: number; @@ -27,8 +28,7 @@ export interface ItemList { export default function PostList() { const [itemList, setItemList] = useState({ totalCount: 0, list: [] }); - const [pageSize, setPageSize] = useState(4); - const [order, setOrder] = useState('recent'); + const [order, setOrder] = useState<'recent' | 'like'>('recent'); const [isDropdownOpen, setIsDropdownOpen] = useState(false); const [search, setSearch] = useState(''); @@ -56,7 +56,6 @@ export default function PostList() { const totalCount = articles.totalCount; const allArticles = await getArticles({ orderBy: order, pageSize: totalCount, keyword:search}); setItemList(allArticles); - console.log(allArticles); } catch (error) { console.error('Error fetching articles:', error); } diff --git a/next.config.js b/next.config.js index 033b672c..7a79441e 100644 --- a/next.config.js +++ b/next.config.js @@ -21,6 +21,13 @@ const nextConfig = { port: '', pathname: '/flexible/**', }, + { + protocol: 'https', + hostname: 'example.com', + port: '', + pathname: '/...', + }, + ], }, }; diff --git a/package-lock.json b/package-lock.json index 89bb358b..54f01d52 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,11 @@ "name": "fe-weekly-mission", "version": "0.1.0", "dependencies": { + "@fortawesome/fontawesome-svg-core": "^6.6.0", + "@fortawesome/free-brands-svg-icons": "^6.6.0", + "@fortawesome/free-regular-svg-icons": "^6.6.0", + "@fortawesome/free-solid-svg-icons": "^6.6.0", + "@fortawesome/react-fontawesome": "^0.2.2", "axios": "^1.7.7", "babel-plugin-styled-components": "^2.1.4", "framer-motion": "^11.11.9", @@ -553,6 +558,76 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.6.0.tgz", + "integrity": "sha512-xyX0X9mc0kyz9plIyryrRbl7ngsA9jz77mCZJsUkLl+ZKs0KWObgaEBoSgQiYWAsSmjz/yjl0F++Got0Mdp4Rw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.6.0.tgz", + "integrity": "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg==", + "license": "MIT", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-brands-svg-icons": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.6.0.tgz", + "integrity": "sha512-1MPD8lMNW/earme4OQi1IFHtmHUwAKgghXlNwWi9GO7QkTfD+IIaYpIai4m2YJEzqfEji3jFHX1DZI5pbY/biQ==", + "license": "(CC-BY-4.0 AND MIT)", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-regular-svg-icons": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.6.0.tgz", + "integrity": "sha512-Yv9hDzL4aI73BEwSEh20clrY8q/uLxawaQ98lekBx6t9dQKDHcDzzV1p2YtBGTtolYtNqcWdniOnhzB+JPnQEQ==", + "license": "(CC-BY-4.0 AND MIT)", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.6.0.tgz", + "integrity": "sha512-IYv/2skhEDFc2WGUcqvFJkeK39Q+HyPf5GHUrT/l2pKbtgEIv1al1TKd6qStR5OIwQdN1GZP54ci3y4mroJWjA==", + "license": "(CC-BY-4.0 AND MIT)", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/react-fontawesome": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.2.tgz", + "integrity": "sha512-EnkrprPNqI6SXJl//m29hpaNzOp1bruISWaOiRtkMi/xSvHJlzc2j2JAYS7egxt/EbjSNV/k6Xy0AQI6vB2+1g==", + "license": "MIT", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6", + "react": ">=16.3" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.13", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", @@ -3372,7 +3447,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3659,7 +3733,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -3727,8 +3800,39 @@ "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-router": { + "version": "6.27.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.27.0.tgz", + "integrity": "sha512-YA+HGZXz4jaAkVoYBE98VQl+nVzI+cVI2Oj/06F5ZM+0u3TgedN9Y9kmMRo2mnkSK2nCpNQn0DVob4HCsY/WLw==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.20.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.27.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.27.0.tgz", + "integrity": "sha512-+bvtFWMC0DgAFrfKXKG9Fc+BcXWRUO1aJIihbB79xaeq0v5UzfvnM5houGUm1Y461WVRcgAQ+Clh5rdb1eCx4g==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.20.0", + "react-router": "6.27.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } }, "node_modules/react-router": { "version": "6.27.0", diff --git a/package.json b/package.json index faf61e32..1bf7185d 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,11 @@ "lint": "next lint" }, "dependencies": { + "@fortawesome/fontawesome-svg-core": "^6.6.0", + "@fortawesome/free-brands-svg-icons": "^6.6.0", + "@fortawesome/free-regular-svg-icons": "^6.6.0", + "@fortawesome/free-solid-svg-icons": "^6.6.0", + "@fortawesome/react-fontawesome": "^0.2.2", "axios": "^1.7.7", "babel-plugin-styled-components": "^2.1.4", "framer-motion": "^11.11.9", diff --git a/pages/addboard/index.tsx b/pages/addboard/index.tsx new file mode 100644 index 00000000..621c4c45 --- /dev/null +++ b/pages/addboard/index.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import * as AS from '@/components/_styled/addboardStyled'; +import Header from "@/components/layout/Header"; +import AddBoard from '@/components/addboard/AddBoard'; + +export default function Boards() { + return ( + +
+ + + + + ); +} diff --git a/pages/board/[id].tsx b/pages/board/[id].tsx new file mode 100644 index 00000000..83fc3699 --- /dev/null +++ b/pages/board/[id].tsx @@ -0,0 +1,17 @@ +import { useRouter } from 'next/router'; +import React from 'react'; +import Header from '@/components/layout/Header'; +import * as BS from '@/components/_styled/boardStyled'; +import Article from '@/components/board/Article'; +import Comment from '@/components/board/Comment'; +export default function Board() { + return ( + +
+ +
+ + + + ); +} diff --git a/pages/boards/index.tsx b/pages/boards/index.tsx index e16a83be..ab737cd3 100644 --- a/pages/boards/index.tsx +++ b/pages/boards/index.tsx @@ -1,5 +1,7 @@ import React from 'react'; -import * as BS from "../../components/_styled/boardStyled"; +import { useRouter } from 'next/router'; +import * as BS from "../../components/_styled/boardsStyled"; + import Header from "@/components/layout/Header"; import BestPost from '@/components/boards/BestPost'; import PostList from '@/components/boards/PostList';