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 #643

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ yarn-error.log*

# local env files
.env*.local
.env

# vercel
.vercel
Expand Down
3 changes: 3 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"plugins": ["prettier-plugin-tailwindcss"]
}
30 changes: 19 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
## Sprint 9
## Sprint 10

![alt text](image.png)
![alt text](image-1.png)
![alt text](image-2.png)

![Sprint 9](</스크린샷 2024-05-30 201712.png>)
****
### 기본 사항
- 자유 게시판 페이지 주소는 “/boards” 입니다.
- 전체 게시글에서 드롭 다운으로 “최신 순” 또는 “좋아요 순”을 선택해서 정렬을 할 수 있습니다.
- 게시글 목록 조회 api를 사용하여 베스트 게시글, 게시글을 구현합니다.
- 게시글 title에 검색어가 일부 포함되면 검색이 됩니다
- 베스트 상품 기준
- 정렬 : like
- like가 높은 순
- 상품 등록 페이지 주소는 “/addboard” 입니다.
- 게시판 이미지는 최대 한개 업로드가 가능합니다.
- 각 input의 placeholder 값을 정확히 입력해주세요.
- 이미지를 제외하고 input 에 모든 값을 입력하면 ‘등록' 버튼이 활성화 됩니다.
- 회원가입, 로그인 api를 사용하여 받은accessToken을 사용하여 게시물 등록을 합니다.
- ‘등록’ 버튼을 누르면 게시물 상세 페이지로 이동합니다.
- 상품 상세 페이지 주소는 “/addboard/{id}” 입니다.
- 댓글 input 값을 입력하면 ‘등록' 버튼이 활성화 됩니다.
- 활성화된 ‘등록' 버튼을 누르면 댓글이 등록됩니다

### 심화

Expand All @@ -22,5 +27,8 @@ https://panda-market-api.vercel.app/docs/#/

### 개인적으로 도전할 것

1. tailwind 적용
2. 기본 페이지 SSG, 검색 결과 페이지 SSR로 구현
1. 기본 페이지 SSR로 수정
2. 상품 상세 페이지 SSR로 구현
3. react-hook-form 써보기
4. shadcn 적용
5. 댓글, 포스트에 인피니티 스크롤 적용
17 changes: 17 additions & 0 deletions components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "styles/globals.css",
"baseColor": "slate",
"cssVariables": false,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}
198 changes: 198 additions & 0 deletions components/ui/dropdown-menu.tsx
Copy link
Collaborator

Choose a reason for hiding this comment

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

오호 shadcn/ui 인가욤? 후기가 궁금하네요 ☺️

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

러닝커브 하나도 없이 적용하면 됐었습니다. shadcn자체에서 무언가를 하기 보다는 시니어 개발자분이 좋은 라이브러리를 활용해서 만든 더 쉬운 라이브러리를 활용하는 기분이었어요! 잘할려면 더 봐야되긴 하겠지만 사용하기 편한것 같습니다 😊

Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import * as React from "react"
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
import { Check, ChevronRight, Circle } from "lucide-react"

import { cn } from "@/lib/utils"

const DropdownMenu = DropdownMenuPrimitive.Root

const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger

const DropdownMenuGroup = DropdownMenuPrimitive.Group

const DropdownMenuPortal = DropdownMenuPrimitive.Portal

const DropdownMenuSub = DropdownMenuPrimitive.Sub

const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup

const DropdownMenuSubTrigger = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
inset?: boolean
}
>(({ className, inset, children, ...props }, ref) => (
<DropdownMenuPrimitive.SubTrigger
ref={ref}
className={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-slate-100 data-[state=open]:bg-slate-100 dark:focus:bg-slate-800 dark:data-[state=open]:bg-slate-800",
inset && "pl-8",
className
)}
{...props}
>
{children}
<ChevronRight className="ml-auto h-4 w-4" />
</DropdownMenuPrimitive.SubTrigger>
))
DropdownMenuSubTrigger.displayName =
DropdownMenuPrimitive.SubTrigger.displayName

const DropdownMenuSubContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
>(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.SubContent
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-200 bg-white p-1 text-slate-950 shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-slate-800 dark:bg-slate-950 dark:text-slate-50",
className
)}
{...props}
/>
))
DropdownMenuSubContent.displayName =
DropdownMenuPrimitive.SubContent.displayName

const DropdownMenuContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-200 bg-white p-1 text-slate-950 shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-slate-800 dark:bg-slate-950 dark:text-slate-50",
className
)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
))
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName

const DropdownMenuItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean
}
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Item
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-slate-100 focus:text-slate-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-slate-800 dark:focus:text-slate-50",
inset && "pl-8",
className
)}
{...props}
/>
))
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName

const DropdownMenuCheckboxItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
>(({ className, children, checked, ...props }, ref) => (
<DropdownMenuPrimitive.CheckboxItem
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-slate-100 focus:text-slate-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-slate-800 dark:focus:text-slate-50",
className
)}
checked={checked}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.CheckboxItem>
))
DropdownMenuCheckboxItem.displayName =
DropdownMenuPrimitive.CheckboxItem.displayName

const DropdownMenuRadioItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
>(({ className, children, ...props }, ref) => (
<DropdownMenuPrimitive.RadioItem
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-slate-100 focus:text-slate-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-slate-800 dark:focus:text-slate-50",
className
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Circle className="h-2 w-2 fill-current" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.RadioItem>
))
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName

const DropdownMenuLabel = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
inset?: boolean
}
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Label
ref={ref}
className={cn(
"px-2 py-1.5 text-sm font-semibold",
inset && "pl-8",
className
)}
{...props}
/>
))
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName

const DropdownMenuSeparator = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
>(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.Separator
ref={ref}
className={cn("-mx-1 my-1 h-px bg-slate-100 dark:bg-slate-800", className)}
{...props}
/>
))
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName

const DropdownMenuShortcut = ({
className,
...props
}: React.HTMLAttributes<HTMLSpanElement>) => {
return (
<span
className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
{...props}
/>
)
}
DropdownMenuShortcut.displayName = "DropdownMenuShortcut"

export {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuCheckboxItem,
DropdownMenuRadioItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuGroup,
DropdownMenuPortal,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuRadioGroup,
}
58 changes: 33 additions & 25 deletions entities/bestPostCard/ui/bestPostCard.tsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,60 @@
import { formatDate } from "@/shared/lib/formatDate";
import { Article } from "@/shared/model";
import { ItemImage } from "@/shared/ui/itemImage";
import { LikeCount } from "@/shared/ui/LikeCount";
import Image from "next/image";

interface Props {
article: Article;
title: string;
image: string | null;
nickname: string;
likeCount: number;
createdAt: string;
id: number;
}

export function BestPostCard({ article }: Props) {
export function BestPostCard({
title,
image,
nickname,
likeCount,
createdAt,
id,
}: Props) {
Comment on lines +15 to +22
Copy link
Collaborator

Choose a reason for hiding this comment

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

오호 해당 컴포넌트가 사용하는 Props� 들로만 이루어졌군요 !

return (
<section className="relative bg-[#f9fafb] h-full rounded-lg px-6 pb-4">
<header className="w-[103px] h-[30px] relative">
<section className="relative h-full rounded-lg bg-[#f9fafb] px-6 pb-4">
<header className="relative h-[30px] w-[103px]">
<Image
fill
src="/images/bestPostLabel.png"
alt="best Post Label"
className="z-0"
/>
<div className="z-10 absolute left-5 top-2 flex gap-2 h-4 items-center">
<div className="absolute left-5 top-2 z-10 flex h-4 items-center gap-2">
<Image width={16} height={16} src="/icons/medal.png" alt="medal" />
<p className="text-base font-semibold text-white tracking-wide">
<p className="text-base font-semibold tracking-wide text-white">
Best
</p>
</div>
</header>
<main className="overflow-hidden">
<header className="text-lg scrollbar-hide font-semibold flex gap-2 mt-4 h-[72px] overflow-auto">
{article.title}
<ItemImage imageUrl={article.image} />
<header className="mt-4 flex h-[72px] gap-2 overflow-auto text-lg font-semibold scrollbar-hide">
{title}
<ItemImage imageUrl={image} />
</header>
</main>
<footer className="mt-4 text-sm flex justify-between">
<header className="flex gap-2 text-[#4b5563] font-normal items-center">
{article.writer.nickname}
<figure className="flex items-center gap-1">
<button type="button">
<Image
src="/icons/heart.png"
alt="heart icon"
width={16}
height={16}
/>
</button>
<div className="text-[#9ca3af]">{article.likeCount}</div>
</figure>
<footer className="mt-4 flex justify-between text-sm">
<header className="flex items-center gap-2 font-normal text-[#4b5563]">
{nickname}
<LikeCount
className="flex items-center gap-1"
height={16}
id={id}
likeCount={likeCount}
width={16}
/>
</header>
<footer className="text-[#9ca3af]">
{formatDate(new Date(article.createdAt))}
{formatDate(new Date(createdAt))}
</footer>
</footer>
</section>
Expand Down
Empty file added entities/commentsCard/index.tsx
Empty file.
Loading
Loading