들어가기
나의 Profile칸을 완선
0. schema.prisma
1. 판매내역, 구매내역, 관심목록
2. EditProfile
3. 나에 대한 Review
sales, purchases, fav를 따로따로 구현해도 되고,
enum으로 만들어서(kind)로 분류해도 된다.
아래에서는 두가지 모두 구현해 놓음.
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
previewFeatures = ["referentialIntegrity"]
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
referentialIntegrity = "prisma"
}
model User {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
phone String? @unique
email String? @unique
name String
avatar String?
tokens Token[]
products Product[]
fav Fav[] ///Enum으로 나누지 않고 따로만듬
sales Sale[] ///Enum으로 나누지 않고 따로만듬
purchases Purchase[] ///Enum으로 나누지 않고 따로만듬
posts Post[]
answers Answer[]
wonderings Wondering[]
writtenReviews Review[] @relation("writtenRiviews")
///내가 쓴 Review와 나에대해 쓰여진 Review를 구분지음
receivedReviews Review[] @relation("receivedRiviews")
///내가 쓴 Review와 나에대해 쓰여진 Review를 구분지음
records Record[] ///sales, purchases, favs를 enum으로 만들때,
///records로 사용!!!
}
model Token {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
payload String @unique
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId Int
}
model Product {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId Int
image String
name String
price Int
description String @db.MediumText
favs Fav[] ///Enum으로 나누지 않고 따로만듬
sales Sale[] ///Enum으로 나누지 않고 따로만듬
purchases Purchase[] ///Enum으로 나누지 않고 따로만듬
record Record[] ///sales, purchases, favs를 enum으로 만들때,
///records로 사용!!!
}
model Post {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId Int
question String @db.MediumText
answers Answer[]
wondering Wondering[]
latitude Float?
longitude Float?
}
model Answer {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId Int
answer String @db.MediumText
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
postId Int
}
model Wondering {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId Int
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
postId Int
}
model Review {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
review String @db.MediumText
createdBy User @relation(name: "writtenRiviews", fields: [createdById], references: [id], onDelete: Cascade)
///내가 쓴 리뷰, name을 넣어서 나에대해 쓰여진 리뷰와 구분지어줌.
createdFor User @relation(name: "receivedRiviews", fields: [createdForId], references: [id], onDelete: Cascade)
///나에게 쓰여진 리뷰, name을 넣어서 내가 쓴 리뷰와 구분지어줌.
createdById Int
createdForId Int
score Int @default(1) ///별점 넣기!!
}
model Fav {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
userId Int
productId Int
}
model Sale {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
userId Int
productId Int
}
model Purchase {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
userId Int
productId Int
}
model Record { //favs, purchases, sales로 따로 만들지 않고 하나로 만들때,
///맨 밑에 kind의 type을 Kind로 해 놓음.
/// 그리고 enum Kind를 만들어줌.
/// -->/api/users/me/record?kind=sale (req.query로 확인가능)
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
userId Int
productId Int
kind Kind
}
enum Kind {
Purchase
Sale
Fav
}
import withHandler, { ResponseType } from '@libs/server/withHandler'
import { NextApiRequest, NextApiResponse } from 'next'
import client from '../../../../libs/server/client'
import { withApiSession } from '@libs/server/withSession'
async function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseType>
) {
const { user } = req.session
const favs = await client.fav.findMany({
///favs에 fav DB에서 where에서 나의 id로 찾은 fav를
///favs에 담아서 return해줌, product를 포함하고,
///각각의 product에 favs counting을 넣어줌.
///sales, purchase도 똑같음.
where: {
userId: user?.id,
},
include: {
product: {
include: {
_count: {
select: {
favs: true,
},
},
},
},
},
})
res.json({
ok: true,
favs,
})
}
export default withApiSession(
withHandler({
methods: ['GET'],
handler,
})
)
import withHandler, { ResponseType } from '@libs/server/withHandler'
import { NextApiRequest, NextApiResponse } from 'next'
import client from '../../../../libs/server/client'
import { withApiSession } from '@libs/server/withSession'
async function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseType>
) {
const { user } = req.session
// client.record.findMany({
// where:{
// userId:user?.id,
// kind:"Sale" //enum은 지정한 것만 들어갈수 있다.
// }
// })
const sales = await client.sale.findMany({
where: {
userId: user?.id,
},
include: {
product: {
include: {
_count: {
select: {
favs: true,
},
},
},
},
},
})
res.json({
ok: true,
sales,
})
}
export default withApiSession(
withHandler({
methods: ['GET'],
handler,
})
)
import withHandler, { ResponseType } from '@libs/server/withHandler'
import { NextApiRequest, NextApiResponse } from 'next'
import client from '../../../../libs/server/client'
import { withApiSession } from '@libs/server/withSession'
async function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseType>
) {
const { user } = req.session
const purchases = await client.purchase.findMany({
where: {
userId: user?.id,
},
include: {
product: {
include: {
_count: {
select: {
favs: true,
},
},
},
},
},
})
res.json({
ok: true,
purchases,
})
}
export default withApiSession(
withHandler({
methods: ['GET'],
handler,
})
)
profile index화면에서는 Link만 걸어주면 끝
<Link href="/profile/sold"> ///////////////////Link걸어줌.
<a className="flex flex-col items-center">
<div className="w-14 h-15 text-white bg-orange-500 rounded-full p-3 flex items-center justify-center">
<svg
className="w-6 h-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="text-sm font-medium text-gray-700 mt-2">
판매내역
</span>
</a>
</Link>
sales, favs, purchases 모두 같은 구조이기 때문에,
공통적으로 data를 부르는 부분을 component로 만듬
import { ProductWithCount } from 'pages'
import useSWR from 'swr'
import Item from './item'
interface ProductListProps {
kind: 'favs' | 'sales' | 'purchases'
}
///ProductListProps를 kind로 type을 만듬.
interface Record {
id: number
product: ProductWithCount
}
///count가 포함된 Product type인 ProductWithCount(product 페이지에서 만들어놓음)
///를 만듬.
///Record에 id를 널어줌.
interface ProductListResponse {
[key: string]: Record[]
}
///[ket:string] 는 걍 모든 type다 가능, 위에서 만든 Record배열
///상품을 뿌려주어야 하니깐.
export default function ProductList({ kind }: ProductListProps) {
const { data } = useSWR<ProductListResponse>(`/api/users/me/${kind}`)
///kind를 입력받는데, 위에서 만든 ProductLostProps임.
return data ? (
///return은 받는 data가 있으면, data?.[kind]?.map으로 뿌려진것을 return함.
///반드시 맨 위와 밑에 fragment로 감싸줌. <></>
<>
{data?.[kind]?.map((record) => (
<Item
key={record.id}
id={record.product.id}
title={record.product.name}
price={record.product.price}
comments={7}
hearts={record.product._count.favs}
></Item>
))}
</>
) : null
}
위에서 만든 product-list덕분에 profile/index.tsx에서
관심목록을 클릭했을때, 구현되는 page.
엄청시리 간단함.
import ProductList from '@components/product-list'
import type { NextPage } from 'next'
import Layout from '../../components/layout'
const Loved: NextPage = () => {
return (
<Layout title="관심목록" canGoBack>
<div className="flex flex-col space-y-5 py-10 ">
<ProductList kind="favs" /> ///kind에 favs만 넣어주면 됨.
///sales, purchases만 넣어주면,
///판매내역, 구매내역 구현됨.
</div>
</Layout>
)
}
export default Loved