저희는 Home, community, chats를 리팩토링 하였습니다. 이제 items, live, profile만 리팩토링하면 끝입니다.
바로 live를 리팩토링 해보도록 하겠습니다.
pages/live/index.tsx
import type { NextPage } from "next";
import Link from "next/link";
import Layout from "../../components/layout";
import FloatingButton from "../../components/FloatingButton";
const Live: NextPage = () => {
return (
<Layout title="라이브" hasTabBar>
<div className="space-y-4 divide-y-2">
{[...Array(5)].map((_i, i) => (
<Link key={i} href={`/live/${i}`}>
<a className="px-4 pt-4">
<div className="aspect-video w-full rounded-md bg-slate-300" />
<h3 className="mt-2 text-lg font-medium text-gray-700">
Let's try potatos
</h3>
</a>
</Link>
))}
<FloatingButton href="/live/create">
<svg
className="h-6 w-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"
></path>
</svg>
</FloatingButton>
</div>
</Layout>
);
};
export default Live;
pages/live/create.tsx
import type { NextPage } from "next";
import Layout from "../../components/layout";
import TextArea from "../../components/textArea";
import Button from "../../components/button";
import Input from "../../components/Input";
const Create: NextPage = () => {
return (
<Layout canGoBack>
<div className=" space-y-5 py-2 px-4">
<Input name="name" label="Name" required />
<Input name="price" label="Price" kind="price" required />
<TextArea name="description" label="Description" />
<Button text="Go live" />
</div>
</Layout>
);
};
export default Create;
여기서 저는 Input에 kind="price"를 추가해 주기 위해 Input 컴포넌트를 수정해 주었습니다.
components/Input.tsx
interface InputProps {
label: string;
name: string;
kind?: "text" | "phone" | "price";
[key: string]: any;
}
/* defualt is kind="text" */
const Input = ({ name, label, kind = "text", rest }: InputProps) => {
return (
<div>
<label htmlFor={name} className="text-sm font-medium text-gray-700">
{label}
</label>
{kind === "text" ? (
<div className="mt-2 flex items-center shadow-sm">
<input
id={name}
{...rest}
className="w-full appearance-none rounded-md border border-gray-300
px-3 py-2 placeholder-gray-400 shadow-sm focus:border-orange-500
focus:outline-none focus:ring-1 focus:ring-orange-500"
/>
</div>
) : null}
{kind === "phone" ? (
<div className="mt-2 flex rounded-md shadow-sm">
<span className="flex select-none items-center justify-center rounded-l-md border border-r-0 border-gray-300 bg-gray-50 px-3 text-sm text-gray-500">
+82
</span>
<input
id={name}
{...rest}
className="w-full appearance-none rounded-r-md border border-gray-300
px-3 py-2 placeholder-gray-400 shadow-sm focus:border-orange-500 focus:outline-none focus:ring-1 focus:ring-orange-500"
/>
</div>
) : null}
{kind === "price" ? (
<div className="relative flex items-center rounded-md shadow-sm">
<div className="pointer-events-none absolute left-0 flex items-center justify-center pl-3">
<span className="text-sm text-gray-500">$</span>
</div>
<input
id={name}
{...rest}
className="w-full appearance-none rounded-md border border-gray-300 px-3 py-2 pl-7 placeholder-gray-400 shadow-sm focus:border-orange-500 focus:outline-none focus:ring-1 focus:ring-orange-500"
/>
<div className="pointer-events-none absolute right-0 flex items-center pr-3">
<span className="text-gray-500">USD</span>
</div>
</div>
) : null}
</div>
);
};
export default Input;
다음과 같이 Input에는 text, phone, price 종류가 있게 됩니다.
또한 Text Area도 바꿔야 하는데, label을 추가해 주기 위함입니다. 옵션으로 label을 주게되면 label까지 렌더링되기 textArea컴포넌트를 바꾸어 보겠습니다.
components/textArea.tsx
interface TextAreaProps {
name?: string;
label?: string;
[key: string]: any;
}
const TextArea = ({ name, label, ...rest }: TextAreaProps) => {
return (
<div>
{label ? (
<label
htmlFor={name}
className="mb-1 block text-sm font-medium text-gray-700"
>
{label}
</label>
) : null}
<textarea
id={name}
{...rest}
rows={4}
className="focus:border-1 mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-orange-500 focus:ring-1 focus:ring-orange-500"
/>
</div>
);
};
export default TextArea;
pages/live/[id].tsx
import type { NextPage } from "next";
import Layout from "../../components/layout";
import Message from "../../components/message";
const StreamDetail: NextPage = () => {
return (
<Layout canGoBack>
<div className="space-y-4 px-4 py-2 scrollbar-hide">
<div className="aspect-video w-full rounded-md bg-slate-300" />
<h3 className="mt-2 text-2xl font-semibold text-gray-800">
Let's try potatos
</h3>
<div className=" h-[50vh] space-y-4 overflow-y-scroll py-10 px-4 pb-16">
<Message message="Hi how much are you selling them for?" />
<Message message="I want ₩20,000" reversed />
<Message message="미쳤어" />
<Message message="Hi how much are you selling them for?" />
<Message message="I want ₩20,000" reversed />
<Message message="미쳤어" />
</div>
<div className="fixed inset-x-0 bottom-3 m-0 mx-auto w-full max-w-md">
<div className="relative flex items-center">
<input
type="text"
className="w-full rounded-full border-gray-300 shadow-sm
focus:border-orange-500 focus:outline-none focus:ring-orange-500
"
/>
<div className="absolute inset-y-0 right-0 py-1.5 pr-1.5">
<button className="flex h-full items-center rounded-full bg-orange-500 px-3 text-sm text-white hover:bg-orange-600 focus:ring-2 focus:ring-orange-500 focus:ring-offset-2">
→
</button>
</div>
</div>
</div>
</div>
</Layout>
);
};
export default StreamDetail;
과 같이 작성해 줄 수 있습니다.
다음으로는 profile 페이지를 리펙토링 하도록 하겠습니다.
pages/profile/index.tsx
import type { NextPage } from "next";
import Layout from "../../components/layout";
import Link from "next/link";
const Profile: NextPage = () => {
return (
<Layout title="나의 계정" hasTabBar>
<div className="py-3 px-4">
<div className="flex items-center space-x-3">
<div className="h-16 w-16 rounded-full bg-slate-500" />
<div className="flex flex-col">
<span className="font-medium text-gray-800">Steve Jebs</span>
<Link href="/profile/edit">
<a className="font-sm text-gray-700">Edit profile →</a>
</Link>
</div>
</div>
<div className="mt-10 flex justify-around">
<Link href="/profile/sold">
<a className="flex flex-col items-center">
<div className="flex h-14 w-14 items-center justify-center rounded-full bg-orange-500 text-white">
<svg
className="h-6 w-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"
></path>
</svg>
</div>
<span className="mt-2 text-sm font-medium text-gray-700">
판매내역
</span>
</a>
</Link>
<Link href="/profile/bought">
<a className="flex flex-col items-center">
<div className="flex h-14 w-14 items-center justify-center rounded-full bg-orange-500 text-white">
<svg
className="h-6 w-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z"
></path>
</svg>
</div>
<span className="mt-2 text-sm font-medium text-gray-700">
구매내역
</span>
</a>
</Link>
<Link href="/profile/loved">
<a className="flex flex-col items-center">
<div className="aligned-center flex h-14 w-14 items-center justify-center rounded-full bg-orange-500 text-white">
<svg
className="h-6 w-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"
></path>
</svg>
</div>
<span className="mt-2 text-sm font-medium text-gray-700">
관심목록
</span>
</a>
</Link>
</div>
<div className="mt-12">
<div className="flex items-center space-x-4">
<div className="h-12 w-12 rounded-full bg-slate-400" />
<div>
<h4 className="text-sm font-bold text-gray-800">니꼬</h4>
<div className="flex items-center">
<svg
className="h-5 w-5 text-yellow-400"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
</svg>
<svg
className="h-5 w-5 text-yellow-400"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
</svg>
<svg
className="h-5 w-5 text-yellow-400"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
</svg>
<svg
className="h-5 w-5 text-yellow-400"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
</svg>
<svg
className="h-5 w-5 text-gray-400"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
</svg>
</div>
</div>
</div>
<div className="mt-4 text-sm text-gray-600">
<p>
Normally, both your asses would be dead as fucking fried chicken, but
you happen to pull this shit while I'm in a transitional period so
I don't wanna kill you, I wanna help you. But I can't give
you this case, it don't belong to me. Besides, I've already
been through too much shit this morning over this case to hand it over
to your dumb ass.
</p>
</div>
</div>
</div>
</Layout>
);
};
export default Profile;
pages/profile/edit.tsx
import type { NextPage } from "next";
import Layout from "../../components/layout";
import Button from "../../components/button";
import Input from "../../components/Input";
const EditProfile: NextPage = () => {
return (
<Layout canGoBack>
<div className="space-y-4 py-3 px-4">
<div className="flex items-center space-x-3">
<div className="h-14 w-14 rounded-full bg-slate-400" />
<label
htmlFor="picture"
className="cursor-pointer rounded-md border border-gray-300 py-2 px-3 text-sm font-medium text-gray-700 shadow-sm focus:ring-2 focus:ring-orange-500 focus:ring-offset-2"
>
Change photo
<input
id="picture"
type="file"
className="hidden"
accept="image/*"
></input>
</label>
</div>
<Input required name="email" label="Email Address" type="email" />
<Input
required
name="phone"
label="Phone number"
kind="phone"
type="number"
/>
<Button text="Update profile" />
</div>
</Layout>
);
};
export default EditProfile;
pages/profile/sold.tsx
import type { NextPage } from "next";
import Layout from "../../components/layout";
import Item from "../../components/item";
const Sold: NextPage = () => (
<Layout canGoBack>
<div className="p flex flex-col space-y-5 py-3">
{[...Array(5)].map((_, i) => (
<Item key={i} title="iPhone 14" price={99} id={i} comments={1} hearts={1} />
))}
</div>
</Layout>
);
export default Sold;
loved, bought는 위와 동일한 형식이므로 생략합니다.
마지막으로 items페이지 리팩토링 진행하도록 하겠습니다.
pages/items/upload.tsx
import type { NextPage } from "next";
import Layout from "../../components/layout";
import Button from "../../components/button";
import Input from "../../components/Input";
import TextArea from "../../components/textArea";
const Upload: NextPage = () => {
return (
<Layout canGoBack>
<div className="space-y-5 px-4 py-3">
<div>
<label className="flex h-48 w-full cursor-pointer items-center justify-center rounded-md border-2 border-dashed border-gray-300 py-6 text-gray-600 hover:border-orange-500 hover:text-orange-500">
<svg
className="h-12 w-12"
stroke="currentColor"
fill="none"
viewBox="0 0 48 48"
aria-hidden="true"
>
<path
d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
<input className="hidden" type="file" />
</label>
</div>
<Input required label="Name" name="name" />
<Input required label="Price" name="price" placeholder="0.00" kind="price" />
<TextArea name="description" label="Description" />
<Button text="Upload item" />
</div>
</Layout>
);
};
export default Upload;
pages/items/[id].tsx
import type { NextPage } from "next";
import Layout from "../../components/layout";
import Button from "../../components/button";
const ItemDetail: NextPage = () => {
return (
<Layout canGoBack>
<div className="px-4 py-3">
<div className="mb-8">
<div className="h-96 bg-slate-300" />
<div className="mt-1 flex items-center space-x-3 border-t border-b py-3">
<div className="h-12 w-12 rounded-full bg-slate-300" />
<div>
<p className="text-sm font-medium text-gray-700">Steve Jebs</p>
<p className="cursor-pointer text-xs font-medium text-gray-500">
View profile →
</p>
</div>
</div>
<div className="mt-5">
<h1 className="text-3xl font-bold text-gray-900">Galaxy S50</h1>
<span className="mt-3 block text-3xl text-gray-900">$140</span>
<p className="my-6 text-base text-gray-700">
My money's in that office, right? If she start giving me some
bullshit about it ain't there, and we got to go someplace else and
get it, I'm gonna shoot you in the head then and there. Then
I'm gonna shoot that bitch in the kneecaps, find out where my
goddamn money is. She gonna tell me too. Hey, look at me when I'm
talking to you, motherfucker. You listen: we go in there, and that
ni**a Winston or anybody else is in there, you the first motherfucker
to get shot. You understand?
</p>
<div className="flex items-center justify-between space-x-2">
<Button text="Talk to seller" large />
<button className="flex items-center justify-center rounded-md p-3 text-gray-400 hover:bg-gray-100 hover:text-gray-500">
<svg
className="h-6 w-6 "
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"
/>
</svg>
</button>
</div>
</div>
</div>
<div>
<h2 className="text-2xl font-bold text-gray-900">Similar items</h2>
<div className="mt-6 grid grid-cols-2 gap-4">
{[1, 2, 3, 4, 5, 6].map((_, i) => (
<div key={i}>
<div className="mb-4 h-56 w-full bg-slate-300" />
<h3 className=" -mb-1 text-gray-700">Galaxy S60</h3>
<span className="text-sm font-medium text-gray-900">$6</span>
</div>
))}
</div>
</div>
</div>
</Layout>
);
};
export default ItemDetail;
이로서 오지게 노가다 많은 리팩토링을 끝내겠습니당~
'Web > CloneCoding' 카테고리의 다른 글
[Carrot Market] #7 React Hook Form (0) | 2022.05.03 |
---|---|
[Carrot Market] #6 Prisma - PlanetScale (0) | 2022.05.03 |
[Carrot Market] #5 TAILWIND ReFactoring UI - 1 (0) | 2022.05.02 |
[Carrot Market] #5 TAILWIND CLONING UI - 3 (0) | 2022.05.01 |
[Carrot Market] #5 TAILWIND CLONING UI - 2 (0) | 2022.05.01 |