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

실시간 핫한 후기 UI 구현 및 연동 #41

Merged
merged 11 commits into from
Aug 27, 2024
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
2 changes: 1 addition & 1 deletion .github/auto_assign.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ addAssignees: author

reviewers:
- punchdrunkard
# - Zero-1016
- Zero-1016
# - seokzin
# - saseungmin

Expand Down
13 changes: 5 additions & 8 deletions .github/workflows/_build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ on:
NEXT_PUBLIC_STAGE:
required: true
type: string


jobs:
build:
Expand All @@ -16,13 +15,11 @@ jobs:
- name: Checkout
uses: actions/checkout@v3

- name: Set up environment variables
run: |
echo "NEXT_PUBLIC_BASE_URL=$NEXT_PUBLIC_BASE_URL" >> .env
echo "NEXT_PUBLIC_STAGE=$NEXT_PUBLIC_STAGE" >> .env
env:
NEXT_PUBLIC_BASE_URL: ${{ secrets.NEXT_PUBLIC_BASE_URL }}
NEXT_PUBLIC_STAGE: ${{ inputs.NEXT_PUBLIC_STAGE }}
- name: Set up Environment Variable for build
uses: ./.github/workflows/_set_env.yaml
with:
NODE_VERSION: 20.x
NEXT_PUBLIC_STAGE: development

- name: Install pnpm
uses: pnpm/action-setup@v4
Expand Down
38 changes: 38 additions & 0 deletions .github/workflows/_set_env.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Set Environment Variables

on:
workflow_call:
inputs:
NEXT_PUBLIC_STAGE:
required: true
type: string
NEXT_PUBLIC_BASE_URL:
required: true
type: string
AUTH_SECRET:
required: true
type: string
AUTH_KAKAO_ID:
required: true
type: string
AUTH_KAKAO_SECRET:
required: true
type: string

jobs:
set-env-vars:
runs-on: ubuntu-latest
steps:
- name: Set up environment variables
run: |
echo "NEXT_PUBLIC_BASE_URL=$NEXT_PUBLIC_BASE_URL" >> .env
echo "NEXT_PUBLIC_STAGE=$NEXT_PUBLIC_STAGE" >> .env
echo "AUTH_SECRET=$AUTH_SECRET" >> .env
echo "AUTH_KAKAO_ID=$AUTH_KAKAO_ID" >> .env
echo "AUTH_KAKAO_SECRET=$AUTH_KAKAO_SECRET" >> .env
env:
NEXT_PUBLIC_BASE_URL: ${{ inputs.NEXT_PUBLIC_BASE_URL }}
NEXT_PUBLIC_STAGE: ${{ inputs.NEXT_PUBLIC_STAGE }}
AUTH_SECRET: ${{ inputs.AUTH_SECRET }}
AUTH_KAKAO_ID: ${{ inputs.AUTH_KAKAO_ID }}
AUTH_KAKAO_SECRET: ${{ inputs.AUTH_KAKAO_SECRET }}
13 changes: 6 additions & 7 deletions .github/workflows/chromatic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,12 @@ jobs:
cache: "pnpm"
cache-dependency-path: "**/pnpm-lock.yaml"

- name: Set up environment variables
run: |
echo "NEXT_PUBLIC_BASE_URL=$NEXT_PUBLIC_BASE_URL" >> .env
echo "NEXT_PUBLIC_STAGE=$NEXT_PUBLIC_STAGE" >> .env
env:
NEXT_PUBLIC_BASE_URL: ${{ secrets.NEXT_PUBLIC_BASE_URL }}
NEXT_PUBLIC_STAGE: ${{ inputs.NEXT_PUBLIC_STAGE }}

- name: Set up Environment Variable for build
uses: ./.github/workflows/_set_env.yaml
with:
NODE_VERSION: 20.x
NEXT_PUBLIC_STAGE: development

- name: Install dependencies
run: pnpm install
Expand Down
8 changes: 4 additions & 4 deletions src/apis/instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export interface FiestaResponse<T> {
data: T;
}

export async function getUserClientSession() {
export async function getClientSideSession() {
Copy link
Member

Choose a reason for hiding this comment

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

P5

해당 함수를 따로 분리를 했어야하나? 라는 의문이 있습니다.

import { getSession as getClientSideSession } from "next-auth/react";

로 분리해도 될 것 같기도 하고 그대로 넣어도 상관 없을 것 같습니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

반영토록 하겠습니다.

const session = await getSession();
return session;
}
Expand Down Expand Up @@ -111,14 +111,14 @@ export class CreateFiestaFetch {

const userSession = isServer
? await getServerSideSession()
: await getUserClientSession();
: await getClientSideSession();

return userSession;
}

public get = async <T>(
url: string,
options: FiestaFetchOptions,
options: FiestaFetchOptions = {},
): Promise<FiestaResponse<T>> => this.fetch(url, "GET", options);

public post = async <T>(
Expand All @@ -138,7 +138,7 @@ export class CreateFiestaFetch {
public delete = async <T>(
url: string,
options: FiestaFetchOptions = {},
): Promise<FiestaResponse<T>> => this.fetch(url, "DELETE", { ...options });
): Promise<FiestaResponse<T>> => this.fetch(url, "DELETE", options);
}

const instance = new CreateFiestaFetch(env.NEXT_PUBLIC_BASE_URL, true);
Expand Down
17 changes: 17 additions & 0 deletions src/apis/review/topReviews/topReviews.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"use server";

import instance from "@/apis/instance";
import { FIESTA_ENDPOINTS } from "@/config";

import { TopReview } from "./topReviewsType";

const ENDPOINT = FIESTA_ENDPOINTS.reviews;

export async function getTopReviews() {
const endpoint = ENDPOINT.mostlike;
const { data } = await instance.get<Array<TopReview>>(endpoint, {
cache: "no-store",
});

return data;
}
15 changes: 15 additions & 0 deletions src/apis/review/topReviews/topReviewsType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export interface TopReview {
reviewId: number;
festivalId: number;
festivalName: string;
content: string;
rating: number;
images?: {
imageId: number;
imageUrl: string;
};
keywords: {
keywordId: number;
keyword: string;
}[];
}
23 changes: 23 additions & 0 deletions src/app/(home)/_components/TopReviews.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { getTopReviews } from "@/apis/review/topReviews/topReviews";
import { ReviewTile } from "@/components/core/List";

const TopReviews = async () => {
const topReviews = await getTopReviews();
return (
<section className="flex w-full flex-col gap-[12px]">
<div className="flex w-full justify-between">
<div className="flex gap-[4px] text-title-bold text-gray-scale-900">
실시간 핫한 후기
</div>
</div>

<div className="flex w-full flex-col gap-[12px]">
{topReviews.map((review) => (
<ReviewTile key={review.reviewId} review={review} />
))}
</div>
</section>
);
};

export default TopReviews;
3 changes: 3 additions & 0 deletions src/app/(home)/_components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as FestivalHot } from "./FestivalHot";
export { default as FestivalThisWeek } from "./FestivalThisWeek";
export { default as TopReviews } from "./TopReviews";
7 changes: 3 additions & 4 deletions src/app/(home)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { HomeHeader } from "@/layout/Mobile/MobileHeader";
import NavigationBar from "@/layout/Mobile/NavigationBar";

import FestivalHot from "./_components/FestivalHot";
import FestivalThisWeek from "./_components/FestivalThisWeek";

import { FestivalHot, FestivalThisWeek, TopReviews } from "./_components";
export default async function Home() {
return (
<div className="mb-[60px] mt-[44px]">
<HomeHeader />
<main className="flex flex-wrap gap-4 px-[16px]">
<main className="flex flex-col gap-[40px] bg-gray-scale-100 px-[16px] pb-[36px] pt-[16px]">
<FestivalHot />
<FestivalThisWeek />
<TopReviews />
</main>
<NavigationBar />
</div>
Expand Down
48 changes: 35 additions & 13 deletions src/components/core/List/ReviewTile/ReviewTile.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,54 @@ export const Default: Story = {
</div>
),
args: {
festivalReview: {
src: "/images/festivalReview.png",
title: "서울 치맥 페스티벌",
content:
"완전 더워요... 선크림 필수.. 빨간 트럭에서파는 치킨 추천합니당 바삭하고 맛있어요. 바삭하고 맛있어요. 바삭하고 맛있어요.",
duration: "8월 1일 ~ 8월 4일",
review: {
reviewId: 6532,
festivalId: 123,
festivalName: "2024 팔레트 뮤직 페스티벌",
content: "음악 듣기 정말 좋은 페스티벌인 것 같아요. 진짜 힐링 그 자체~",
rating: 4,
keywords: ["✨쾌적해요", "✨쾌적해요"],
images: {
imageId: 145156,
imageUrl: "/images/festivalReview.png",
},
Comment on lines +28 to +31
Copy link
Member

@punchdrunkard punchdrunkard Aug 27, 2024

Choose a reason for hiding this comment

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

여기 응답 변경있습니다............. 죄송해요 (images 만 thumbnailImage로 string 하나 내려와요)

https://breakyourlimit.notion.site/1159d5ddd66a4aa7a8dc615106f70341?pvs=4

명세 참고해주시고, 그리고 후기 이미지 없는 경우가 있어서 없으면 해당 필드 생략되서 내려올거에요.
(이 부분은 아래에 있군요!)

keywords: [
{
keywordId: 12,
keyword: "✨쾌적해요",
},
{
keywordId: 12,
keyword: "✨쾌적해요",
},
],
},
},
};

export const WithOutPhoto: Story = {
export const NoPhoto: Story = {
render: (args) => (
<div className="w-[360px]">
<ReviewTile {...args} />
</div>
),
args: {
festivalReview: {
title: "서울 치맥 페스티벌",
review: {
reviewId: 6532,
festivalId: 123,
festivalName: "2024 팔레트 뮤직 페스티벌",
content:
"완전 더워요... 선크림 필수.. 빨간 트럭에서파는 치킨 추천합니당 바삭하고 맛있어요. 바삭하고 맛있어요. 바삭하고 맛있어요.",
duration: "8월 1일 ~ 8월 4일",
"완전 더워요... 그냥 더워 죽어요... ㅜㅜ 시원하게 입고가세요... 물이랑 손풍기 필수",
rating: 4,
keywords: ["✨쾌적해요", "✨쾌적해요"],
keywords: [
{
keywordId: 12,
keyword: "✨쾌적해요",
},
{
keywordId: 12,
keyword: "✨쾌적해요",
},
],
},
},
};
49 changes: 22 additions & 27 deletions src/components/core/List/ReviewTile/ReviewTile.tsx
Original file line number Diff line number Diff line change
@@ -1,58 +1,53 @@
import isEmpty from "lodash/isEmpty";
import Image from "next/image";
import { FC, HTMLAttributes } from "react";
import { ComponentPropsWithoutRef, FC } from "react";

import { TopReview } from "@/apis/review/topReviews/topReviewsType";
import Ratings from "@/components/rating/Ratings";
import { cn } from "@/utils/cn";

import ReviewTag from "../../Tag/ReviewTag/ReviewTag";
import { ReviewTag } from "../../Tag";

interface FestivalReview {
src?: string;
title: string;
content: string;
duration: string;
keywords: string[];
rating: number;
interface Props extends Omit<ComponentPropsWithoutRef<"div">, "children"> {
review: TopReview;
}

interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
festivalReview: FestivalReview;
}

const ReviewTile: FC<Props> = ({ festivalReview, ...props }) => {
const firstTwoKeywords = festivalReview.keywords.slice(0, 2);

const ReviewTile: FC<Props> = ({ review, className }) => {
return (
<div
className={cn(
"w-full h-full duration-300 rounded-[8px] p-[16px]",
"flex items-start justify-between gap-[12px]",
"bg-gray-scale-0",
props.className,
className,
)}
{...props}
>
{!!festivalReview.src && (
{!isEmpty(review.images) && (
<div className="relative h-[104px] min-w-[80px]">
<Image src={festivalReview.src} alt={festivalReview.title} fill />
<Image
src={review.images.imageUrl}
alt={review.festivalName}
fill
className=" rounded-[8px]"
/>
</div>
)}

<div className="flex h-full w-full flex-col items-start justify-between gap-[8px] py-[4px]">
<div className="flex h-full w-full flex-col items-start gap-[6px]">
<div className="flex h-full w-full items-center justify-between">
<div className="flex h-full w-full flex-col items-start justify-between gap-[8px]">
<div className="flex h-auto w-full items-center justify-between">
<span className="line-clamp-1 text-body2-bold text-gray-scale-700">
{festivalReview.title}
{review.festivalName}
</span>
<Ratings rating={festivalReview.rating} />
<Ratings rating={review.rating} />
</div>
<span className="line-clamp-2 text-body1-regular text-gray-scale-600">
{festivalReview.content}
{review.content}
</span>
</div>
<div className="flex gap-[8px]">
{firstTwoKeywords.map((keyword) => (
<ReviewTag key={keyword} label={keyword} />
{review.keywords.map(({ keyword, keywordId }) => (
<ReviewTag key={keywordId} label={keyword} />
))}
</div>
</div>
Expand Down
12 changes: 5 additions & 7 deletions src/components/core/Tag/ReviewTag/ReviewTag.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
import { ButtonHTMLAttributes, FC } from "react";
import { ComponentPropsWithoutRef, FC } from "react";

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

interface Props
extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
interface Props extends ComponentPropsWithoutRef<"button"> {
label: string;
}

const ReviewTag: FC<Props> = ({ label, ...props }) => {
const ReviewTag: FC<Props> = ({ label, className }) => {
return (
<button
type="button"
className={cn(
"w-auto h-[30px] duration-300 rounded-[17px] py-[10px] px-[16px] border-[1px]",
"flex flex-col items-center justify-center",
"flex flex-col items-center justify-center whitespace-nowrap",
"bg-gray-scale-0 border-gray-scale-100 text-gray-scale-500 text-caption1-medium",
props.className,
className,
)}
{...props}
>
{label}
</button>
Expand Down
1 change: 1 addition & 0 deletions src/config/apiEndpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const FIESTA_ENDPOINTS = {
base: "/reviews",
detail: (reviewId: string) => `/reviews/${reviewId}`,
like: (reviewId: string) => `/reviews/${reviewId}/like`,
mostlike: "/reviews/mostlike",
},
logs: {
userLogs: (userId: string) => `/users/${userId}/logs`,
Expand Down
Loading