Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[신승헌] sprint10 #632

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
14ab44a
refactor: api 호출 함수 부분 useEffect 내부로 이동
AdamSeungheonShin Jun 4, 2024
c68a248
refactor: 검색 input 핸들러 함수에 불필요한 함수 호출 제거
AdamSeungheonShin Jun 4, 2024
a9c3fe8
refactor: 드롭다운 클릭 범위 수정, 포인터 옵션 수정
AdamSeungheonShin Jun 4, 2024
287eaaa
refactor: 인기게시물 조건부 렌더링 부분 수정
AdamSeungheonShin Jun 4, 2024
d35d413
refactor: 추상적인 타입명 구체적으로 수정
AdamSeungheonShin Jun 4, 2024
fedc2ae
refactor[pages/boards/index.tsx]: 불필요한 재할당 부분 삭제
AdamSeungheonShin Jun 4, 2024
e65364d
feat: SSG -> SSR 변경
AdamSeungheonShin Jun 5, 2024
096204e
feat: 초기 데이터 로딩 SSR로 변경
AdamSeungheonShin Jun 5, 2024
8ce5d26
feat: 게시글 작성 기본 ui 제작
AdamSeungheonShin Jun 6, 2024
cdd40ad
feat: 제목, 내용 작성 인풋 제작
AdamSeungheonShin Jun 6, 2024
e9e552d
feat: 파일인풋 제작, 새게시물 작성 폼 일부 수정, 타입선언 빠트린 부분 수정
AdamSeungheonShin Jun 6, 2024
1230632
feat: 파일인풋 이미지 미리보기, 이미지 삭제하기 기능 추가
AdamSeungheonShin Jun 6, 2024
ba764dd
feat: 파일인풋, 이미지인풋 css 작성
AdamSeungheonShin Jun 6, 2024
0a764eb
feat: 페이지 라우팅 Link 경로 수정
AdamSeungheonShin Jun 6, 2024
5137cb2
feat: 게시글 상세 페이지 기본 ui 작성
AdamSeungheonShin Jun 7, 2024
9941c08
feat: 게시글 상세 데이터 호출부 작성
AdamSeungheonShin Jun 7, 2024
f818ee2
feat: 게시글 상세 페이지 이미지 박스, 디폴트 이미지 추가
AdamSeungheonShin Jun 7, 2024
7826d77
refactor: 파일명 수정, 임포트한 파일 변수명 수정
AdamSeungheonShin Jun 7, 2024
e1211cc
refactor: 게시물 상세 이미지 잘림 오류 css 수정
AdamSeungheonShin Jun 7, 2024
369410b
feat: 댓글 리스트 컴포넌트 기본 ui 작성
AdamSeungheonShin Jun 7, 2024
1bfe187
feat: 댓글 api 호출 부분 작성, Type추가, props 지정
AdamSeungheonShin Jun 7, 2024
24601a3
feat: 지난 시간 카운트 유틸함수 작성
AdamSeungheonShin Jun 7, 2024
1e00784
feat: 댓글 등록버튼 추가, form태그로 수정 및 이벤트 함수 추가
AdamSeungheonShin Jun 7, 2024
9ebe7c8
feat: 댓글 등록 버튼 유효성 검사 옵션 추가
AdamSeungheonShin Jun 7, 2024
c3e8b44
feat: 게시글 작성 버튼 유효성 검사 옵션 추가
AdamSeungheonShin Jun 7, 2024
12a4711
feat: 게시글 작성 submit 함수 수정, post 함수 작성
AdamSeungheonShin Jun 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 26 additions & 24 deletions components/Boards/AllArticles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ import iconArrow from "@/public/icons/icon_arrow_down.svg";
import formatDate from "@/utils/formatDate";
import heart_active from "@/public/images/heart_active.png";
import heart_inactive from "@/public/images/heart_inactive.png";
import imgProfile from "@/public/images/skeleton_profile.png";
import ProfileDefault from "@/public/images/profile_default.png";
import icon_search from "@/public/icons/icon_search.svg";
import useClickOutside from "@/hooks/useClickOutside";

const PAGE_SIZE_MAX = 10;

export default function AllArticles({
articleData,
initialData,
}: {
articleData: Article[];
initialData: Article[];
}) {
const [articles, setArticles] = useState<Article[]>([]);
const [articles, setArticles] = useState<Article[]>(initialData);
const [pageNum, setPageNum] = useState<number>(1);
const [orderBy, setOrderBy] = useState<string>("recent");
const [keyword, setKeyword] = useState<string>("");
Expand All @@ -30,22 +30,21 @@ export default function AllArticles({
orderBy: orderBy,
})}`;

async function getArticlesByPageNum() {
try {
const { data } = await axios.get(pathName);
setArticles(data.list);
} catch (e) {
console.error("failed to fetch", e);
useEffect(() => {
async function getArticlesByPageNum() {
try {
const { data } = await axios.get(pathName);
setArticles(data.list);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

만약 모종의 이유로 api에서 에러가 발생한다면 data 는 'undefined'가 될거에요

data.list 대신 data?.list 로 바꾸는게 좋을것 같아요 !

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안그러면 런타임에서 해당 api가 에러를 throw할 경우 앱 자체가 뻗어버릴거에요..!

} catch (e) {
console.error("failed to fetch", e);
}
}
}

useEffect(() => {
getArticlesByPageNum();
}, [pageNum, orderBy, keyword]);

const handleSearch = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
getArticlesByPageNum();
};

const handleChangeOrderBy = (orderBy: string) => {
Expand All @@ -58,6 +57,7 @@ export default function AllArticles({
};

const dropDownRef = useRef<HTMLDivElement>(null);

useClickOutside(dropDownRef, () => setShowDropdown(false));

return (
Expand All @@ -66,7 +66,7 @@ export default function AllArticles({
<h2 className=" text-xl text-gray-900 font-bold">게시글</h2>
<Link
className="w-20 h-10 flex justify-center items-center bg-blue-default rounded-button text-white hover:bg-hover-blue"
href="/"
href="/boards/new"
>
글쓰기
</Link>
Expand All @@ -90,8 +90,9 @@ export default function AllArticles({
className="text-white absolute top-auto left-2 translate-y-[45%]"
/>
<div
className="w-32 h-11 px-4 border-solid border-gray-200 border-[1px] rounded-box flex justify-between items-center gap-3 relative"
className="w-32 h-11 px-4 border-solid border-gray-200 border-[1px] rounded-box flex justify-between items-center gap-3 relative cursor-pointer"
ref={dropDownRef}
onClick={toggleDropdown}
>
{orderBy === "recent" ? "최신순" : "좋아요순"}
<Image
Expand All @@ -103,7 +104,6 @@ export default function AllArticles({
alt="게시물정렬"
width={24}
height={24}
onClick={toggleDropdown}
/>
{showDropdown && (
<div className="w-32 absolute top-11 left-0 border-solid border-gray-200 border-[1px] rounded-box flex flex-col">
Expand Down Expand Up @@ -144,24 +144,26 @@ export default function AllArticles({
>
<div className="h-16 flex justify-between">
<Link
className="text-gray-800 font-semibold text-xl"
className="text-gray-800 font-semibold text-xl hover:text-blue-600 active:text-blue-900"
href={`/boards/${article.id}`}
>
{article.title}
</Link>
{article.image && (
<Image
height={72}
width={72}
src={article.image}
alt={article.title}
/>
<Link href={`/boards/${article.id}`}>
<Image
height={72}
width={72}
src={article.image}
alt={article.title}
/>
</Link>
)}
</div>
<div className="flex text-gray-400 justify-between">
<div className="flex items-center gap-2">
<Image
src={imgProfile}
src={ProfileDefault}
alt="testImage"
width={24}
height={24}
Expand Down
79 changes: 39 additions & 40 deletions components/Boards/BestArticles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,47 +38,46 @@ export default function BestArticles() {
<section className="flex flex-col gap-6">
<h2 className=" text-xl text-gray-900 font-bold">베스트 게시글</h2>
<article className="w-[1200px] flex gap-6">
{articles &&
articles.map(function (article) {
return (
<Link
href={`/boards/${article.id}`}
className="w-96 h-44 px-6 pb-4 bg-gray-50 rounded-box flex flex-col gap-4"
key={article.id}
>
<Image
width={102}
height={30}
src={article_badge}
alt="베스트게시글뱃지"
/>
<div className="h-20 flex gap-2">
<p className="font-semibold">{article.title}</p>
{article.image && (
<Image
height={72}
width={72}
src={article.image}
alt={article.title}
/>
)}
{articles?.map(function (article) {
return (
<Link
href={`/boards/${article.id}`}
className="w-96 h-44 px-6 pb-4 bg-gray-50 rounded-box flex flex-col gap-4"
key={article.id}
>
<Image
width={102}
height={30}
src={article_badge}
alt="베스트게시글뱃지"
/>
<div className="h-20 flex gap-2">
<p className="font-semibold">{article.title}</p>
{article.image && (
<Image
height={72}
width={72}
src={article.image}
alt={article.title}
/>
)}
</div>
<div className="flex justify-between text-gray-500 text-sm font-normal">
<div className="flex items-center gap-1 ">
<p className="text-gray-600">{article.writer.nickname}</p>
<Image
width={16}
height={16}
src={heart_inactive}
alt="좋아요"
/>
{article.likeCount}
</div>
<div className="flex justify-between text-gray-500 text-sm font-normal">
<div className="flex items-center gap-1 ">
<p className="text-gray-600">{article.writer.nickname}</p>
<Image
width={16}
height={16}
src={heart_inactive}
alt="좋아요"
/>
{article.likeCount}
</div>
<p>{formatDate(article.createdAt)}</p>
</div>
</Link>
);
})}
<p>{formatDate(article.createdAt)}</p>
</div>
</Link>
);
})}
</article>
</section>
);
Expand Down
36 changes: 36 additions & 0 deletions components/Boards/CommentList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import ProfileDefault from "@/public/images/profile_default.png";
import EmptyComments from "@/public/images/Img_reply_empty.png";
import { Comment } from "@/types";

import Image from "next/image";
import formatTimeAgo from "@/utils/formatTimeAgo";

export default function CommentList({ comments }: { comments: Comment[] }) {
return (
<>
{comments &&
comments.map((comment) => {
return (
<div className="flex flex-col gap-6 mb-6" key={comment.id}>
<div>{comment.content}</div>
<div className="flex items-center gap-2">
<Image
src={ProfileDefault}
width={32}
height={32}
alt="댓글프로필이미지"
/>
<div>
<p className="text-gray-600">{comment.writer.nickname}</p>
<p className="text-gray-400">
{formatTimeAgo(comment.updatedAt)}
</p>
</div>
</div>
<hr className="h-[1px] bg-gray-200" />
</div>
);
})}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  {    comments?.map((comment) => (
            <div className="flex flex-col gap-6 mb-6" key={comment.id}>
              <div>{comment.content}</div>
              <div className="flex items-center gap-2">
                <Image
                  src={ProfileDefault}
                  width={32}
                  height={32}
                  alt="댓글프로필이미지"
                />
                <div>
                  <p className="text-gray-600">{comment.writer.nickname}</p>
                  <p className="text-gray-400">
                    {formatDate(comment.updatedAt)}
                  </p>
                </div>
              </div>
              <hr className="h-[1px] bg-gray-200" />
            </div>
          );
        )}

</>
);
}
71 changes: 71 additions & 0 deletions components/Boards/ImageInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import Image from "next/image";
import { ChangeEvent, useEffect, useRef, useState } from "react";
import IconPlus from "@/public/icons/icon_plus.svg";
import IconDelete from "@/public/icons/icon_delete.svg";

interface ImageInputProps {
name: string;
value: File | undefined;
onChange: (name: string, value: File | undefined) => void;
}

export default function ImageInput({ name, value, onChange }: ImageInputProps) {
const [preview, setPreview] = useState<string>("");

const inputRef = useRef<HTMLInputElement>(null);

const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
const nextValue = e.target.files ? e.target.files[0] : undefined;
onChange(name, nextValue);
};

const handleClearImage = () => {
const inputNode = inputRef.current;
if (!inputNode) return;

inputNode.value = "";
onChange(name, undefined);
};

useEffect(() => {
if (!value) return;

const nextPreview = URL.createObjectURL(value);
setPreview(nextPreview);
}, [value]);

return (
<div>
{value ? (
<div className="w-72 h-72 mb-3 bg-gray-100 rounded-box relative">
<button className="absolute top-2 right-2" onClick={handleClearImage}>
<Image width={24} height={24} src={IconDelete} alt="이미지삭제" />
</button>
<Image
className="object-cover w-72 h-72 rounded-box"
width={288}
height={288}
src={preview}
alt="이미지미리보기"
/>
</div>
) : (
<label
className="w-72 h-72 mb-3 bg-gray-100 rounded-box flex flex-col justify-center items-center cursor-pointer"
htmlFor={name}
>
<Image src={IconPlus} width={48} height={48} alt="이미지 등록" />
<p className="text-base text-gray-400 font-normal">이미지 등록</p>
</label>
)}
<input
className="absolute opacity-0"
type="file"
id={name}
name={name}
onChange={handleChange}
ref={inputRef}
/>
</div>
);
}
Loading
Loading