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

feat: 아티클 상세 페이지 UI를 구현합니다. #120

Merged
merged 9 commits into from
Sep 16, 2023
13 changes: 13 additions & 0 deletions app/article/[slug]/@comments/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { CommentForm } from "components/article/@comments/comment-form";
import { Comments } from "components/article/@comments/comments";

const CommentsPage = () => {
return (
<div className="flex flex-col gap-3">
<CommentForm />
<Comments />
</div>
);
};

export default CommentsPage;
20 changes: 20 additions & 0 deletions app/article/[slug]/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { PropsWithChildren, ReactNode } from "react";

type Props = {
comments: ReactNode;
};

const ArticleLayout = ({ children, comments }: PropsWithChildren<Props>) => {
return (
<div className="flex justify-center">
<div className="page relative">
<div className="flex flex-col gap-12 py-20">
{children}
{comments}
</div>
</div>
</div>
);
};

export default ArticleLayout;
15 changes: 15 additions & 0 deletions app/article/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Author } from "components/article/author";
import { Content } from "components/article/content";
import { Summary } from "components/article/summary";

const ArticlePage = () => {
return (
<>
<Summary />
<Content />
<Author />
</>
);
};

export default ArticlePage;
2 changes: 1 addition & 1 deletion components/(home)/@feed/article-item/article-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const ArticleItem = ({ article }: Props) => {
<Avatar.Info>
<Avatar.Name>{username}</Avatar.Name>

<Avatar.Description>{relativeTime}</Avatar.Description>
<Avatar.Bio>{relativeTime}</Avatar.Bio>
</Avatar.Info>
</Avatar.Root>
</Link>
Expand Down
4 changes: 2 additions & 2 deletions components/(home)/@feed/articles/articles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ type Props = {

export const Articles = ({ articles }: Props) => {
return (
<div className="flex flex-col">
<ul className="flex flex-col">
{articles.map((article) => (
<ArticleItem key={article.slug} article={article} />
))}
</div>
</ul>
);
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Article } from "models/article";

import { Articles } from "./articles";
import { ArticlesPagination } from "./articles-pagination";
import { Articles } from "../articles";
import { ArticlesPagination } from "../articles-pagination";

type Props = {
articles: Article[];
Expand Down
1 change: 1 addition & 0 deletions components/(home)/@feed/feed/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Feed } from "./feed";
16 changes: 16 additions & 0 deletions components/article/@comments/comment-form/comment-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Button } from "components/shared/ui/button";
import { Form } from "components/shared/ui/form";

export const CommentForm = () => {
return (
<Form.Root>
<Form.Label label="Comment">
<Form.Textarea placeholder="Write a comment ..." />
</Form.Label>

<div className="flex justify-end">
<Button size="lg">Post</Button>
</div>
</Form.Root>
);
};
1 change: 1 addition & 0 deletions components/article/@comments/comment-form/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { CommentForm } from "./comment-form";
29 changes: 29 additions & 0 deletions components/article/@comments/comment-item/comment-item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Link from "next/link";

import { Avatar } from "components/shared/ui/avatar";
import { ListItem } from "components/shared/ui/list-item";

export const CommentItem = () => {
return (
<ListItem.Root>
<div className="mb-4 flex items-center justify-between">
<Link href="/profile/eric-simons">
<Avatar.Root>
<Avatar.Image src="http://i.imgur.com/Qr71crq.jpg" alt="" />

<Avatar.Info>
<Avatar.Name>Eric Simons</Avatar.Name>
<Avatar.Bio>9 months ago</Avatar.Bio>
</Avatar.Info>
</Avatar.Root>
</Link>
</div>

<ListItem.Content>
<ListItem.Description className="text-zinc-900">
With supporting text below as a natural lead-in to additional content.
</ListItem.Description>
</ListItem.Content>
</ListItem.Root>
);
};
1 change: 1 addition & 0 deletions components/article/@comments/comment-item/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { CommentItem } from "./comment-item";
11 changes: 11 additions & 0 deletions components/article/@comments/comments/comments.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { CommentItem } from "../comment-item";

export const Comments = () => {
return (
<ul className="flex flex-col">
<CommentItem />
<CommentItem />
<CommentItem />
</ul>
);
};
1 change: 1 addition & 0 deletions components/article/@comments/comments/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Comments } from "./comments";
33 changes: 33 additions & 0 deletions components/article/author/author.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import Link from "next/link";

import { IoAdd, IoHeartOutline } from "react-icons/io5";

import { Avatar } from "components/shared/ui/avatar";
import { Button } from "components/shared/ui/button";

export const Author = () => {
return (
<div className="my-1 flex items-center justify-center gap-7">
<Link href="/profile/eric-simons">
<Avatar.Root>
<Avatar.Image src="http://i.imgur.com/Qr71crq.jpg" alt="" />

<Avatar.Info>
<Avatar.Name>Eric Simons</Avatar.Name>
<Avatar.Bio>Simons bed is comfortable</Avatar.Bio>
</Avatar.Info>
</Avatar.Root>
</Link>

<div className="flex items-center gap-2">
<Button variant="secondary" icon={<IoAdd />}>
Follow (10)
</Button>

<Button variant="secondary" icon={<IoHeartOutline />}>
Favorite (29)
</Button>
</div>
</div>
);
};
1 change: 1 addition & 0 deletions components/article/author/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Author } from "./author";
17 changes: 17 additions & 0 deletions components/article/content/content.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const Content = () => {
return (
<div>
<p className="whitespace-pre-wrap text-base font-normal text-zinc-950">
{`Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ac dui feugiat, elementum est ut, sodales dolor. Quisque nec consequat ante, ac mattis risus. Vestibulum pretium nec sem vel rutrum. Sed congue ipsum sit amet tincidunt maximus. Phasellus vel nulla arcu. Nullam maximus viverra sollicitudin. Nunc nulla enim, consequat a sem at, lacinia auctor tellus. Quisque mollis hendrerit tortor. Quisque et fermentum lectus. Morbi volutpat eros vel lorem laoreet, id scelerisque quam malesuada.

Phasellus id scelerisque leo. Nam dui risus, vehicula vel euismod vel, laoreet eget metus. Aliquam maximus condimentum enim, lobortis semper tortor volutpat id. Vivamus vehicula tellus nec vestibulum volutpat. Sed non maximus mi. Quisque iaculis urna aliquet interdum convallis. Suspendisse varius, nisl ac pretium accumsan, sem justo efficitur lectus, id blandit enim leo ut ligula. Ut quis neque magna. Cras vel enim nec eros venenatis ultrices sed eget dolor. Aliquam erat volutpat. Pellentesque sagittis felis in lectus vehicula, faucibus semper magna interdum. Phasellus elementum nisl nibh, at efficitur diam rutrum vel.

Pellentesque eleifend iaculis aliquam. Ut consequat tellus nisi, id porta dolor laoreet lacinia. Donec diam quam, lacinia ac purus at, placerat scelerisque massa. Ut dictum quam at elementum convallis. Praesent dapibus risus vel convallis tincidunt. Sed nibh felis, placerat ac metus quis, euismod viverra neque. Nulla et tempor arcu, ut molestie elit. Sed porttitor ut risus vel commodo. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nullam id eros et tellus laoreet porttitor. Vestibulum rutrum vulputate luctus. Nulla malesuada venenatis eros id fermentum.

Cras luctus vehicula mattis. Cras id pulvinar leo, non elementum nulla. Integer at ultricies mi. Morbi tempus nisi porttitor malesuada tincidunt. Pellentesque et gravida justo. Proin erat lorem, feugiat a pulvinar a, euismod et turpis. Ut porttitor auctor quam non porttitor. Phasellus in odio sollicitudin, tristique erat non, bibendum elit. Aenean in nisl auctor lectus posuere pulvinar quis ut tellus. Donec elit odio, consequat eu lorem non, fermentum posuere tortor. Donec blandit dolor sed metus imperdiet, vitae ullamcorper tortor congue. Donec nec suscipit erat. Pellentesque ligula nisl, ultrices sit amet laoreet vel, faucibus vel orci. Aenean vitae dictum arcu. Maecenas convallis mauris vel aliquam rutrum. Nam a libero suscipit, scelerisque magna quis, dignissim risus.

Donec iaculis sit amet velit sed accumsan. Etiam et gravida urna. Cras finibus massa nec turpis fringilla ornare. Nullam placerat commodo quam sit amet blandit. Maecenas vel sollicitudin tortor, at commodo neque. In hac habitasse platea dictumst. Nunc vitae elit non nibh auctor faucibus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla a ligula ut urna iaculis commodo. Nunc eleifend sapien nec mauris vestibulum suscipit. Duis tempor mi quis neque malesuada ullamcorper.`}
</p>
</div>
);
};
1 change: 1 addition & 0 deletions components/article/content/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Content } from "./content";
1 change: 1 addition & 0 deletions components/article/summary/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Summary } from "./summary";
30 changes: 30 additions & 0 deletions components/article/summary/summary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { RiDoubleQuotesL, RiDoubleQuotesR } from "react-icons/ri";

import { Tags } from "components/shared/ui/tags";

export const Summary = () => {
return (
<div>
<h1 className="text-center text-5xl font-extrabold text-zinc-950">How to build webapps that scale</h1>

<p className="mt-4 text-center text-base text-zinc-500">9 months ago</p>

<Tags.Root className="mt-4 justify-center">
<Tags.Item>realworld</Tags.Item>
<Tags.Item>implementations</Tags.Item>
</Tags.Root>

<div className="mt-12 flex w-full gap-1 rounded-lg bg-zinc-900 px-5 py-4 shadow-2xl">
<div>
<RiDoubleQuotesL color="white" size={14} />
</div>

<p className="break-all text-sm font-normal text-white">This is the description for the post.</p>

<div>
<RiDoubleQuotesR color="white" size={14} />
</div>
</div>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { clsx } from "lib/clsx";

type Props = ComponentPropsWithoutRef<"span">;

export const Description = forwardRef<HTMLSpanElement, Props>(({ children, className, ...rest }, ref) => {
export const Bio = forwardRef<HTMLSpanElement, Props>(({ children, className, ...rest }, ref) => {
return (
<span ref={ref} {...rest} className={clsx("text-xs font-normal text-zinc-500", className)}>
{children}
Expand Down
4 changes: 2 additions & 2 deletions components/shared/ui/avatar/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Description } from "./description";
import { Bio } from "./bio";
import { Image } from "./image";
import { Info } from "./info";
import { Name } from "./name";
Expand All @@ -9,5 +9,5 @@ export const Avatar = {
Image,
Info,
Name,
Description,
Bio,
};
2 changes: 1 addition & 1 deletion components/shared/ui/button/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const styles = {
"bg-white text-zinc-950 after:rounded-[4px] after:border-[1px] after:border-zinc-950 after:absolute after:inset-0 after:content-['']",
},
size: {
[Size.SMALL]: "h-7 rounded-[4px] px-3 text-xs gap-[2px]",
[Size.SMALL]: "h-7 rounded-[4px] px-3 text-xs gap-1",
[Size.LARGE]: "text-sm h-9 rounded-[4px] px-6 gap-1",
},
} as const;
9 changes: 9 additions & 0 deletions components/shared/ui/form/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Label } from "./label";
import { Root } from "./root";
import { Textarea } from "./text-area";

export const Form = {
Root,
Label,
Textarea,
};
17 changes: 17 additions & 0 deletions components/shared/ui/form/label.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { ComponentPropsWithoutRef } from "react";
import { forwardRef } from "react";

import { clsx } from "lib/clsx";

type Props = ComponentPropsWithoutRef<"label"> & {
label: string;
};

export const Label = forwardRef<HTMLLabelElement, Props>(({ children, className, label, ...rest }, ref) => {
return (
<label ref={ref} {...rest} className={clsx("flex flex-col gap-3 text-sm font-medium text-zinc-950", className)}>
{label}
{children}
</label>
);
});
14 changes: 14 additions & 0 deletions components/shared/ui/form/root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { ComponentPropsWithoutRef } from "react";
import { forwardRef } from "react";

import { clsx } from "lib/clsx";

type Props = ComponentPropsWithoutRef<"form">;

export const Root = forwardRef<HTMLFormElement, Props>(({ children, className, ...rest }, ref) => {
return (
<form ref={ref} {...rest} className={clsx("flex flex-col gap-5", className)}>
{children}
</form>
);
});
20 changes: 20 additions & 0 deletions components/shared/ui/form/text-area.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { ComponentPropsWithoutRef } from "react";
import { forwardRef } from "react";

import { clsx } from "lib/clsx";

type Props = ComponentPropsWithoutRef<"textarea">;

export const Textarea = forwardRef<HTMLTextAreaElement, Props>(({ className, ...rest }, ref) => {
return (
<textarea
ref={ref}
rows={3}
{...rest}
className={clsx(
"resize-none rounded-[4px] border-[1px] border-zinc-200 px-4 py-3 text-sm font-normal text-zinc-900 outline-none placeholder:text-sm placeholder:font-normal placeholder:text-zinc-400",
className,
)}
/>
);
});
2 changes: 1 addition & 1 deletion components/shared/ui/list-item/description.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ type Props = ComponentPropsWithoutRef<"p">;

export const Description = forwardRef<HTMLParagraphElement, Props>(({ children, className, ...rest }, ref) => {
return (
<p ref={ref} {...rest} className={clsx("mb-1 text-sm font-light text-zinc-600", className)}>
<p ref={ref} {...rest} className={clsx("mb-1 text-sm font-normal text-zinc-600", className)}>
{children}
</p>
);
Expand Down
8 changes: 4 additions & 4 deletions components/shared/ui/list-item/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ import type { ComponentPropsWithoutRef } from "react";

import { clsx } from "lib/clsx";

type Props = ComponentPropsWithoutRef<"div">;
type Props = ComponentPropsWithoutRef<"li">;

export const Root = forwardRef<HTMLDivElement, Props>(({ children, className, ...rest }, ref) => {
export const Root = forwardRef<HTMLLIElement, Props>(({ children, className, ...rest }, ref) => {
return (
<div
<li
ref={ref}
{...rest}
className={clsx("flex w-full flex-col border-b-[1px] border-zinc-200 py-5 last:border-b-0", className)}
>
{children}
</div>
</li>
);
});
10 changes: 5 additions & 5 deletions hooks/use-query-params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ import { useMemo } from "react";

import { serializeQuery } from "lib/query";

type Params = Record<string, string | number>;
type Query = Record<string, string | number>;

export const useQueryParams = <T extends Params = Params>() => {
export const useQueryParams = <T extends Query = Query>() => {
const router = useRouter();
const searchParams = useSearchParams();

const query = useMemo(() => toQuery(searchParams), [searchParams]);

const set = (incoming: Params) => {
const set = (incoming: Query) => {
router.push(serializeQuery(incoming));
};

const append = (incoming: Params) => {
const append = (incoming: Query) => {
router.replace(serializeQuery({ ...query, ...incoming }), { scroll: false });
};

Expand All @@ -29,7 +29,7 @@ export const useQueryParams = <T extends Params = Params>() => {
};

const toQuery = (params: ReadonlyURLSearchParams) => {
const query: Params = {};
const query: Query = {};

params.forEach((value, key) => (query[key] = value));

Expand Down
Loading
Loading