Skip to content

Commit ac35373

Browse files
committed
🚧 wip: 리뷰 탭 1차 ui 작업 중...
1 parent 85d0088 commit ac35373

File tree

7 files changed

+190
-40
lines changed

7 files changed

+190
-40
lines changed

next.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const withFlowbiteReact = require("flowbite-react/plugin/nextjs");
55
const nextConfig = {
66
outputFileTracingRoot: path.join(__dirname),
77
images: {
8-
domains: ["localhost", "images.unsplash.com"],
8+
domains: ["localhost", "images.unsplash.com, example.com"],
99
},
1010
webpack: (config) => {
1111
config.resolve.fallback = {

src/app/(main)/_components/RecipeCard/RecipeCard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"use client";
22

33
import { Button, Card } from "flowbite-react";
4-
import Stars from "./_components/Stars";
4+
import Stars from "../../../../components/Stars";
55
import CardSkeleton from "../../../../components/Cards/CardSkeleton";
66
import { UserRecipe } from "@/types/recipe.types";
77
import Link from "next/link";

src/app/(main)/_components/RecipeCard/_components/Stars.tsx

Lines changed: 0 additions & 36 deletions
This file was deleted.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { Review } from "./ReviewCard";
2+
3+
export const mockReviews: Review[] = [
4+
{
5+
id: 1,
6+
userName: "김바텐",
7+
profileImageUrl: "https://example.com/avatars/kim.png",
8+
visitDate: "2025-08-15",
9+
rating: 5,
10+
comment: "칵테일 맛도 훌륭하고 바텐더가 정말 친절했어요!",
11+
createdAt: "2025-09-20T12:34:56Z",
12+
likeCount: 18,
13+
imageUrl: "https://example.com/photos/review-1.jpg",
14+
tags: ["친절해요", "분위기좋아요", "재방문의사있음"]
15+
},
16+
{
17+
id: 2,
18+
userName: "이밤",
19+
profileImageUrl: null,
20+
visitDate: "2025-09-01",
21+
rating: 4,
22+
comment: "조용하고 좋았는데 좌석이 약간 불편했어요.",
23+
createdAt: "2025-09-22T09:11:03Z",
24+
likeCount: 7,
25+
imageUrl: null,
26+
tags: ["조용한분위기", "가성비굿"]
27+
},
28+
{
29+
id: 3,
30+
userName: "Park",
31+
profileImageUrl: "https://example.com/avatars/park.jpg",
32+
visitDate: "2025-09-10",
33+
rating: 3,
34+
comment: "전반적으로 무난. 하이볼은 괜찮았어요.",
35+
createdAt: "2025-09-23T19:45:12Z",
36+
likeCount: 3,
37+
imageUrl: "https://example.com/photos/review-3.png",
38+
tags: ["무난해요"]
39+
},
40+
{
41+
id: 4,
42+
userName: "Jin",
43+
profileImageUrl: "https://example.com/avatars/jin.webp",
44+
visitDate: "2025-09-18",
45+
rating: 5,
46+
comment: "생일 이벤트로 갔는데 음악과 조명이 최고!",
47+
createdAt: "2025-09-24T21:02:40Z",
48+
likeCount: 25,
49+
imageUrl: null,
50+
tags: ["생일추천", "라이브좋아요", "인테리어예쁨"]
51+
},
52+
{
53+
id: 5,
54+
userName: "Sora",
55+
profileImageUrl: null,
56+
visitDate: "2025-09-20",
57+
rating: 2,
58+
comment: "기대보다 시끄러웠고 예약 확인이 늦었어요.",
59+
createdAt: "2025-09-25T08:27:19Z",
60+
likeCount: 1,
61+
imageUrl: "https://example.com/photos/review-5.jpg",
62+
tags: ["시끄러움", "대기있음"]
63+
}
64+
];
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"use client";
2+
3+
import Stars from "@/components/Stars";
4+
import Tags from "@/components/Tags";
5+
import { Avatar, Card } from "flowbite-react";
6+
import Image from "next/image";
7+
import { BiLike } from "react-icons/bi";
8+
9+
export type Review = {
10+
id: number;
11+
userName: string;
12+
profileImageUrl: string | null;
13+
visitDate: string;
14+
rating: number;
15+
comment: string;
16+
createdAt: string;
17+
likeCount: number;
18+
imageUrl: string | null;
19+
tags: string[];
20+
};
21+
22+
export default function ReviewCard({ review }: { review: Review }) {
23+
return (
24+
<Card className="border-neutral-600 bg-neutral-800 py-4">
25+
<Avatar img={review.profileImageUrl || ""} rounded>
26+
<div className="space-y-1 font-medium dark:text-white">
27+
<div>{review.userName}</div>
28+
<div className="text-sm text-gray-500 dark:text-gray-400">
29+
<Stars rating={review.rating} />
30+
<span>{review.visitDate} 방문</span>
31+
</div>
32+
</div>
33+
</Avatar>
34+
<p>{review.comment}</p>
35+
{review.imageUrl && (
36+
<Image
37+
src={review.imageUrl}
38+
alt={review.userName}
39+
width={100}
40+
height={100}
41+
/>
42+
)}
43+
<Tags tags={review.tags} />
44+
<div>
45+
<span>
46+
<BiLike size={16} />
47+
{review.likeCount}
48+
</span>
49+
<span>{review.createdAt}</span>
50+
span
51+
</div>
52+
</Card>
53+
);
54+
}
Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,40 @@
11
"use client";
22

3-
import { Card } from "flowbite-react";
3+
import FormOption from "@/components/Forms/FormOption";
4+
import Stars from "@/components/Stars";
5+
import { Pagination } from "flowbite-react";
6+
import { useState } from "react";
7+
import ReviewCard from "./ReviewCard";
8+
import { mockReviews } from "./ReviewCard.const";
49

510
export default function ReviewTab() {
6-
return <Card className="border-neutral-600 bg-neutral-800">BarReview</Card>;
11+
const rating = 4.5;
12+
const reviews = [1, 2, 3, 4, 5];
13+
const [currentPage, setCurrentPage] = useState<number>(1);
14+
const onPageChange = (page: number) => setCurrentPage(page);
15+
return (
16+
<div className="px-4">
17+
<section className="mb-4">
18+
<div className="flex flex-col gap-2">
19+
<span className="text-4xl font-bold">{rating}</span>
20+
<Stars rating={rating} size={24} />
21+
</div>
22+
<div className="flex items-center justify-between">
23+
<span>리뷰 {reviews.length}</span>
24+
<FormOption options={["최신순", "평점순", "좋아요순"]} />
25+
</div>
26+
</section>
27+
{mockReviews.map((review) => (
28+
<ReviewCard key={review.id} review={review} />
29+
))}
30+
<div className="flex overflow-x-auto sm:justify-center">
31+
<Pagination
32+
currentPage={currentPage}
33+
totalPages={100}
34+
onPageChange={onPageChange}
35+
showIcons
36+
/>
37+
</div>
38+
</div>
39+
);
740
}

src/components/Stars.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { HiStar } from "react-icons/hi";
2+
3+
const MAX_STARS = 5;
4+
5+
type StarsProps = {
6+
rating: number;
7+
size?: number;
8+
showRatingChip?: boolean;
9+
};
10+
11+
export default function Stars({ rating, size, showRatingChip }: StarsProps) {
12+
const safeRating = Math.max(0, Math.min(MAX_STARS, Math.round(rating)));
13+
return (
14+
<div className="mt-2.5 mb-5 flex items-center">
15+
<div className="flex items-center rtl:space-x-reverse">
16+
{Array.from({ length: MAX_STARS }).map((_, i) => (
17+
<HiStar
18+
key={i}
19+
className={`${
20+
i < safeRating
21+
? "text-yellow-300"
22+
: "text-gray-200 dark:text-gray-600"
23+
}`}
24+
size={size}
25+
></HiStar>
26+
))}
27+
</div>
28+
{rating && showRatingChip ? (
29+
<span className="ms-3 rounded-sm bg-blue-100 px-2.5 py-0.5 text-xs font-semibold text-blue-800 dark:bg-blue-200 dark:text-blue-800">
30+
{rating}
31+
</span>
32+
) : null}
33+
</div>
34+
);
35+
}

0 commit comments

Comments
 (0)