Skip to content
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
5 changes: 4 additions & 1 deletion src/app/(home)/_api/cheer/cheer.types.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { type ImageResponse } from "@/types";

export type Cheer = {
storeId: number;
imageUrl: string;
images: ImageResponse[];
storeName: string;
storeDistrict: string;
storeNeighborhood: string;
storeCategory: string;
cheerId: number;
cheerDescription: string;
tags: string[];
};

export type CheersResponse = {
Expand Down
9 changes: 9 additions & 0 deletions src/app/(home)/_api/shop/shop.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,24 @@ import { type StoresResponse } from "./shop.types";
export const getStores = async ({
size,
category,
tag,
location,
}: {
size: number;
category?: string;
tag?: string[];
location?: string[];
}): Promise<StoresResponse> => {
const toCSV = (v?: string | string[]) =>
Array.isArray(v) ? v.filter(Boolean).join(",") : v || undefined;

return await http
.get("api/shops", {
searchParams: {
size,
...(category ? { category } : {}),
...(toCSV(tag) ? { tag: toCSV(tag) } : {}),
...(toCSV(location) ? { location: toCSV(location) } : {}),
},
})
.json<StoresResponse>();
Expand Down
16 changes: 12 additions & 4 deletions src/app/(home)/_api/shop/shop.queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,26 @@ import { getStores } from "./shop.api";

export const storesQueryKeys = {
all: ["stores"] as const,
size: (size: number, category?: string) =>
[...storesQueryKeys.all, size, category] as const,
size: (
size: number,
category?: string,
tag?: string[],
location?: string[]
) => [...storesQueryKeys.all, size, category, tag, location] as const,
};

export const storesQueryOptions = ({
size,
category,
tag,
location,
}: {
size: number;
category?: string;
tag?: string[];
location?: string[];
}) =>
queryOptions({
queryKey: storesQueryKeys.size(size, category),
queryFn: () => getStores({ size, category }),
queryKey: storesQueryKeys.size(size, category, tag, location),
queryFn: () => getStores({ size, category, tag, location }),
});
5 changes: 4 additions & 1 deletion src/app/(home)/_components/RecentCheers/RecentCheers.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ export const recentSupportCard = style({
borderRadius: "2.8rem",
padding: "2rem",
border: "2px solid #fff",
height: "13.6rem",
minHeight: "13.6rem",
maxHeight: "17.4rem",
overflow: "hidden",
});

export const cheersContent = style({
Expand Down Expand Up @@ -58,6 +60,7 @@ export const storeImage = style({
width: "4rem",
height: "4rem",
borderRadius: radius[100],
objectFit: "cover",
});

export const storeImageFallback = style([
Expand Down
31 changes: 26 additions & 5 deletions src/app/(home)/_components/RecentCheers/RecentCheers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import { Suspense } from "@suspensive/react";
import { useSuspenseQuery } from "@tanstack/react-query";
import { chunk } from "es-toolkit";
import { at, chunk, compact } from "es-toolkit";
import { isEmpty } from "es-toolkit/compat";
Comment on lines +5 to +6
Copy link
Contributor

Choose a reason for hiding this comment

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

신기하고 편리한 것들이 정말 많군유 @@

import Image from "next/image";
import Link from "next/link";
import { type HTMLAttributes, useState } from "react";
Expand All @@ -13,8 +14,10 @@ import ResetIcon from "@/assets/reset-20.svg";
import { Button } from "@/components/ui/Button";
import { Skeleton } from "@/components/ui/Skeleton";
import { HStack, VStack } from "@/components/ui/Stack";
import { Tag } from "@/components/ui/Tag";
import { Text } from "@/components/ui/Text";
import { TextButton } from "@/components/ui/TextButton";
import { ALL_TAGS } from "@/constants/tag.constants";
import { colors } from "@/styles";

import { cheerQueryOptions } from "../../_api/cheer";
Expand Down Expand Up @@ -66,23 +69,24 @@ const RecentSupportCardContent = () => {
}}
store={{
name: cheer.storeName,
imageUrl: cheer.imageUrl,
imageUrl: cheer.images[0]?.url ?? "",
location: `${cheer.storeDistrict} ${cheer.storeNeighborhood}`,
category: cheer.storeCategory,
}}
content={cheer.cheerDescription}
tags={cheer.tags}
/>
</Link>
))}
</VStack>
<Link href='/stores'>
<Link href='/cheer'>
<Button
variant='custom'
size='large'
className={styles.showAllButton}
fullWidth
>
가게 전체보기
응원 전체보기
</Button>
</Link>
</VStack>
Expand All @@ -98,13 +102,19 @@ type RecentSupportCardProps = {
category: string;
};
content: string;
tags: string[];
} & HTMLAttributes<HTMLDivElement>;

const RecentSupportCard = ({
store,
content,
tags,
...restProps
}: RecentSupportCardProps) => {
const selectedTags = ALL_TAGS.filter(tag => tags?.includes(tag.name));

const visibleTags = compact(at(selectedTags, [0, 1]));

return (
<VStack gap={8} className={styles.recentSupportCard} {...restProps}>
<HStack gap={12}>
Expand All @@ -114,7 +124,6 @@ const RecentSupportCard = ({
height={40}
alt={`${store.name} 가게 이미지`}
className={styles.storeImage}
objectFit='cover'
src={store.imageUrl}
// TODO: 추후 제거
unoptimized
Expand Down Expand Up @@ -152,6 +161,18 @@ const RecentSupportCard = ({
>
{content}
</Text>

{isEmpty(selectedTags) ? null : (
<HStack gap={8}>
{visibleTags.map(tag => (
<Tag key={tag.name}>
<Image src={tag.iconUrl} alt={tag.label} width={14} height={14} />
{tag.label}
</Tag>
))}
{selectedTags.length > 2 && <Tag>+{selectedTags.length - 2}</Tag>}
</HStack>
)}
</VStack>
);
};
Expand Down
34 changes: 34 additions & 0 deletions src/app/(home)/_components/StoreList/StoreList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Suspense } from "react";
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Client 전용 훅 사용 파일에 'use client' 누락 — 빌드 에러 가능

이 컴포넌트는 useFoodCategory를 통해 useRouter/useSearchParams/usePathname(Client 전용)를 사용합니다. 파일 최상단에 "use client" 지시어가 없으면 Server Component로 취급되어 Next.js 빌드 시 에러가 납니다.

아래 패치를 적용해 주세요(Line 1 상단에 추가):

+ "use client";
 import { Suspense } from "react";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { Suspense } from "react";
"use client";
import { Suspense } from "react";
🤖 Prompt for AI Agents
In src/app/(home)/_components/StoreList/StoreList.tsx around line 1, this
component uses client-only hooks (useRouter/useSearchParams/usePathname via
useFoodCategory) but lacks the "use client" directive; add the "use client"
directive as the very first line of the file so Next.js treats the file as a
Client Component and prevents build-time errors.


import { ChipFilter } from "@/app/_shared/ChipFilter";
import { StoreList as StoreListComponent } from "@/app/(store)/stores/(list)/_components";
import { Bleed } from "@/components/ui/Bleed";
import { FoodCategories } from "@/components/ui/FoodCategory";
import { Spacer } from "@/components/ui/Spacer";
import { VStack } from "@/components/ui/Stack";
import { useFoodCategory } from "@/hooks/useFoodCategory";

export const StoreList = () => {
const { categories, handleSelectCategory, selectedCategory } =
useFoodCategory("/");

return (
<VStack>
<Bleed inline={20}>
<FoodCategories
categories={categories}
selectedCategory={selectedCategory}
onSelectCategory={handleSelectCategory}
/>
</Bleed>

<Spacer size={12} />

<ChipFilter />

<Suspense>
<StoreListComponent category={selectedCategory.name} />
</Suspense>
</VStack>
);
};
1 change: 1 addition & 0 deletions src/app/(home)/_components/StoreList/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./StoreList";
1 change: 1 addition & 0 deletions src/app/(home)/_components/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { RecentCheers } from "./RecentCheers";
export { RecentlySupportedStores } from "./RecentlySupportStories";
export { RegisterPopup } from "./RegisterPopup";
export { StoreList } from "./StoreList";
export { StoreStory } from "./StoreStory";
export { Story } from "./Story";
15 changes: 12 additions & 3 deletions src/app/(home)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import { Bleed } from "@/components/ui/Bleed";
import { Spacer } from "@/components/ui/Spacer";
import { VStack } from "@/components/ui/Stack";
import { colors } from "@/styles";

import { RecentCheers, RegisterPopup, Story } from "./_components";
import { RecentCheers, RegisterPopup, StoreList, Story } from "./_components";
import { ServiceIntroBottomSheet } from "./_components/ServiceIntroBottomSheet";
import { RegisterFloatingButton } from "./_shared/RegisterFloatingButton";

Expand All @@ -15,10 +16,18 @@ export default function HomePage() {
<Spacer size={12} />
<Story />
</Bleed>
<Spacer size={12} />
<Spacer size={32} />
<VStack gap={40}>
<VStack gap={32}>
<RecentCheers />

<Bleed inline={20}>
<Spacer
size={12}
style={{ backgroundColor: colors.coolNeutral[98] }}
/>
</Bleed>

<StoreList />
</VStack>
<RegisterFloatingButton />
<ServiceIntroBottomSheet />
Expand Down
39 changes: 0 additions & 39 deletions src/app/(store)/constants/storeCategory.constants.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const storeCard = style({
});

export const storeImagesContainer = style({
position: "relative",
paddingInline: "2rem",
overflowX: "auto",

Expand All @@ -31,29 +32,18 @@ export const storeImagesContainer = style({
},
});

export const storeImages = style({
selectors: {
"&::-webkit-scrollbar": {
display: "none",
},
},
export const storeImageWrapper = style({
position: "relative",
width: "100%",
height: "168px",
});

export const storeImage = style({
objectFit: "cover",
overflow: "hidden",
flexShrink: 0,

selectors: {
"&[data-first=true]": {
borderTopLeftRadius: radius[160],
borderBottomLeftRadius: radius[160],
},
"&[data-last=true]": {
borderTopRightRadius: radius[160],
borderBottomRightRadius: radius[160],
},
},
borderRadius: radius[160],
});

export const emptyImage = style({
Expand All @@ -68,16 +58,7 @@ export const emptyImage = style({

backgroundColor: semantic.background.grayLight,

selectors: {
"&[data-first=true]": {
borderTopLeftRadius: radius[160],
borderBottomLeftRadius: radius[160],
},
"&[data-last=true]": {
borderTopRightRadius: radius[160],
borderBottomRightRadius: radius[160],
},
},
borderRadius: radius[160],
});

export const moreButtonText = style({
Expand Down
Loading