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
24 changes: 18 additions & 6 deletions web/src/app/(main)/map/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
useLocation,
useMapCoordinate,
useMapDrag,
useMapFilters,
useMapInitialize,
useStoreListQuery,
} from "@/features/map/lib";
Expand All @@ -19,6 +18,7 @@ import {
StoreBottomSheet,
} from "@/features/map/ui";
import { useToast } from "@/shared/lib/hooks";
import { useFilterStore } from "@/shared/store";
import { TransitionLayout } from "@/shared/ui";

export default function MapPage() {
Expand All @@ -27,14 +27,19 @@ export default function MapPage() {
const { requestLocation } = useLocation(map);
const { isDragging, resetDragging } = useMapDrag(map);

const { isFilterOpen, filters, openFilter, closeFilter, applyFilters } = useMapFilters();
const { storeList, isFetching } = useStoreListQuery(filters, {
bounds,
const { isFilterOpen, filters, openFilter, closeFilter, setFilters } = useFilterStore();
const { storeList, isFetching } = useStoreListQuery({
filters,
center,
bounds,
limit: 30,
});

const { showToast } = useToast();

const levelDisplayValue =
filters.honbobLevel.length > 1 ? "μ»€μŠ€ν…€" : `레벨${filters.honbobLevel[0] || 1}`;

const handleStoreListFetch = () => {
updateCoordinate(map);
resetDragging();
Expand All @@ -51,7 +56,14 @@ export default function MapPage() {
<MapView mapRef={mapContainerRef} initializeMap={initializeMap} />
<MapMarkers map={map} storeList={storeList} />
<FetchStoreListButton onClick={handleStoreListFetch} isFetching={isFetching} />
<LevelFilterButton honbabLevel={filters.honbobLevel} onClick={openFilter} />
<div
className="fixed right-5 z-40"
style={{
top: "calc(84px + env(safe-area-inset-top))",
}}
>
<LevelFilterButton honbobLevel={levelDisplayValue} onClick={openFilter} />
</div>
Comment on lines +59 to +66
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

πŸ‘πŸ‘πŸ‘

<CurrentLocationButton onClick={requestLocation} />
<StoreBottomSheet
storeList={storeList}
Expand All @@ -62,7 +74,7 @@ export default function MapPage() {
isOpen={isFilterOpen}
onClose={closeFilter}
initialFilters={filters}
onApply={applyFilters}
onApply={setFilters}
/>
</TransitionLayout>
);
Expand Down
11 changes: 10 additions & 1 deletion web/src/app/_providers/userStateProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@

import { useSession } from "next-auth/react";
import type React from "react";
import { useMemo } from "react";
import { useEffect, useMemo } from "react";
import { UserStateContext } from "@/entities/user/model/context";
import { deriveUserState } from "@/shared/lib/utils/deriveUserState";
import { useFilterStore } from "@/shared/store";

interface UserStateProviderProps {
children: React.ReactNode;
}

export function UserStateProvider({ children }: UserStateProviderProps) {
const { data: session, status } = useSession();
const { initializeFilters, hasInitialized } = useFilterStore();

const userState = useMemo(() => {
// statusκ°€ loading일 λ•ŒλŠ” 이전 μƒνƒœλ₯Ό μœ μ§€ν•˜κ³ , unauthenticated일 λ•ŒλŠ” null둜 처리
Expand All @@ -26,6 +28,13 @@ export function UserStateProvider({ children }: UserStateProviderProps) {

const isLoading = status === "loading";

// λ‘œκ·ΈμΈν•œ μœ μ €μ˜ honbobLevel둜 ν•„ν„° μ΄ˆκΈ°ν™”
useEffect(() => {
if (!isLoading && userState.isLoggedIn && !hasInitialized) {
initializeFilters(userState.honbobLevel);
}
}, [isLoading, userState.isLoggedIn, userState.honbobLevel, hasInitialized, initializeFilters]);

return (
<UserStateContext.Provider value={{ userState, isLoading }}>
{children}
Expand Down
2 changes: 2 additions & 0 deletions web/src/entities/honbob/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
export * from "./lib/hooks/useCarouselButton";

export * from "./model/constant";
export * from "./model/type";

export * from "./ui/HonbobCarouselIndicator";
export * from "./ui/HonbobCarouselSlide";
export * from "./ui/HonbobFirstVisitModal";
export * from "./ui/HonbobLevelCard";
export * from "./ui/HonbobLevelCarousel";
export * from "./ui/HonbobLevelFilterCard";
export * from "./ui/HonbobLevelInfoCard";
export * from "./ui/HonbobLevelTestGuideModal";
3 changes: 2 additions & 1 deletion web/src/entities/honbob/model/constant.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Lv1Character, Lv2Character, Lv3Character, Lv4Character } from "@/shared/lib/assets";
import type { HonbobLevelCardInfo } from "./type";

export const SAMPLE_LEVEL_INFO = {
1: {
Expand Down Expand Up @@ -27,7 +28,7 @@ export const SAMPLE_LEVEL_INFO = {
},
};

export const honbobLevelCardList = [
export const honbobLevelCardList: HonbobLevelCardInfo[] = [
{
honbobLevel: "Lv.1",
honbobLevelTitle: "혼λ°₯ μž…λ¬Έμž",
Expand Down
11 changes: 11 additions & 0 deletions web/src/entities/honbob/model/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { StaticImageData } from "next/image";

export interface HonbobLevelCardInfo {
honbobLevel: string;
honbobLevelTitle: string;
honbobLevelDescription: string;
honbobLevelIcon: StaticImageData;
recommendedMenu: string;
recommendedStore: string;
characteristics: string;
}
51 changes: 51 additions & 0 deletions web/src/entities/honbob/ui/HonbobLevelFilterCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import Image from "next/image";
import { getHonbobLevelImageSet } from "@/shared/lib";

interface HonbobLevelFilterCardProps {
honbobLevel: string;
honbobLevelTitle: string;
honbobLevelDescription: string;
recommendedMenu: string;
recommendedStore: string;
}

export function HonbobLevelFilterCard({
honbobLevel,
honbobLevelTitle,
honbobLevelDescription,
recommendedMenu,
recommendedStore,
}: HonbobLevelFilterCardProps) {
const { infoCharacterImage } = getHonbobLevelImageSet(honbobLevel);

return (
<div className="flex flex-col bg-gray50 rounded-[12px] p-[16px] gap-y-[16px]">
<div className="flex items-center gap-x-[12px]">
<Image
src={infoCharacterImage}
alt={`${honbobLevel}LevelImage`}
className=" w-[48px] h-[48px] rounded-full object-contain"
/>
<div>
<div className="flex flex-col gap-y-[8px]">
<div className="w-fit px-[4px] py-1px rounded-[4px] bg-primary100 text-caption2-medium text-primary500">
{honbobLevel}
</div>
<p className="text-body2-regular text-gray900">{honbobLevelTitle}</p>
</div>
<div className="text-body1-semibold text-gray700">{honbobLevelDescription}</div>
</div>
</div>
<div className="self-stretch p-4 bg-white rounded-lg inline-flex flex-col justify-start items-start gap-3">
<div className="flex flex-col gap-y-[4px]">
<span className="text-body3-semibold text-gray500">μΆ”μ²œ 메뉴</span>
<span className="text-body3-regular text-gray800">{recommendedMenu}</span>
</div>
<div className="flex flex-col gap-y-[4px]">
<span className="text-body3-semibold text-gray500">μΆ”μ²œ μ’Œμ„</span>
<span className="text-body3-regular text-gray800">{recommendedStore}</span>
</div>
</div>
</div>
);
}
6 changes: 1 addition & 5 deletions web/src/entities/map/ui/MapPin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,7 @@ interface MapPinProps {
export function MapPin({ selected, name, honbobLevel }: MapPinProps) {
return (
<article className="flex w-fit flex-col items-center justify-center gap-1">
{selected ? (
<SelectedPin honbobLevel={honbobLevel} />
) : (
<UnselectedPin />
)}
{selected ? <SelectedPin honbobLevel={honbobLevel} /> : <UnselectedPin />}
<StoreName name={name} />
</article>
);
Expand Down
3 changes: 2 additions & 1 deletion web/src/entities/storeList/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,11 @@ export interface StoreListRequestBody {
min: number;
max: number;
};
honbobLevel: number;
honbobLevel?: number[];
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

λ³΅μˆ˜ν˜• honbobLevels λŠ” μ–΄λ–€κ°€μš”??

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

이 뢀뢄은 dto에 sκ°€λΉ μ Έμžˆμ–΄μ„œ κ³ λ―Όνžˆλ‹€κ°€ λ‹€ λ‹¨μˆ˜λ‘œ λ°”κΏ¨μŠ΅λ‹ˆλ‹€.. BEλž‘ μ΄μ•ΌκΈ°ν•΄μ„œ μˆ˜μ •ν•΄μ•Όν•  것 κ°™μ•„μš”.

seatTypes?: ("FOR_ONE" | "FOR_TWO" | "FOR_FOUR" | "BAR_TABLE" | "CUBICLE")[];
paymentMethods?: string[];
categories?: string[];
sortBy?: "DISTANCE" | "RECOMMENDED";
};
paging: {
limit: number;
Expand Down
1 change: 1 addition & 0 deletions web/src/entities/storeList/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./utils/getDefaultStationCenter";
export * from "./utils/getLevelFilterDisplayValue";
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const getLevelFilterDisplayValue = (honbobLevel: number[]) => {
if (honbobLevel.length > 1) {
return "μ»€μŠ€ν…€";
}
return `레벨${honbobLevel[0] || 1}`;
};
81 changes: 42 additions & 39 deletions web/src/entities/storeList/ui/StoreList.tsx
Original file line number Diff line number Diff line change
@@ -1,53 +1,40 @@
"use client";

import { useInfiniteQuery } from "@tanstack/react-query";
import Link from "next/link";
import { useState } from "react";
import { storeListApi } from "@/entities/storeList/api";
import { getDefaultStationCenter } from "@/entities/storeList/lib";
import { useUserState } from "@/entities/user";
import { queryKeys } from "@/shared/api";
import { getDefaultStationCenter, getLevelFilterDisplayValue } from "@/entities/storeList/lib";
import { useStoreListQuery } from "@/features/map/lib";
import { FilterBottomSheet, LevelFilterButton } from "@/features/map/ui";
import { useInfiniteScroll } from "@/shared/lib";
import { useLocationStore } from "@/shared/store";
import { useFilterStore, useLocationStore } from "@/shared/store";
import { RoundedSelectBox } from "@/shared/ui";
import { StoreCard } from "./StoreCard";
import { StoreLevelList } from "./StoreLevelList";

const sortOptions = [
{ value: "RECOMMENDED", label: "μΆ”μ²œμˆœ" },
{ value: "DISTANCE", label: "거리순" },
];

export function StoreList() {
const { userState } = useUserState();
const { selectedStation } = useLocationStore();
const { isFilterOpen, filters, openFilter, closeFilter, setFilters } = useFilterStore();

const [selectedLevel, setSelectedLevel] = useState(userState.honbobLevel);
const handleLevelChange = (level: number) => {
setSelectedLevel(level);
const handleApplyFilters = (newFilters: typeof filters) => {
setFilters(newFilters);
closeFilter();
};

const centerLatLonInfo = getDefaultStationCenter(selectedStation);
const levelFilterDisplayValue = getLevelFilterDisplayValue(filters.honbobLevel);

const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, error } =
useInfiniteQuery({
queryKey: queryKeys.STORE_LIST(selectedStation, selectedLevel),
queryFn: ({ pageParam }) =>
storeListApi.getStoreByPost({
requestBody: {
center: {
lat: centerLatLonInfo.lat,
lon: centerLatLonInfo.lon,
},
filters: {
honbobLevel: selectedLevel,
},
paging: {
limit: 10,
lastKnown: pageParam as string | undefined,
},
},
}),
initialPageParam: null as string | null,
getNextPageParam: (lastPage) => {
return lastPage.response?.nextCursor && lastPage.response?.hasNext
? lastPage.response.nextCursor
: undefined;
const { storeList, isLoading, error, fetchNextPage, hasNextPage, isFetchingNextPage } =
useStoreListQuery({
filters,
center: {
lat: centerLatLonInfo.lat,
lon: centerLatLonInfo.lon,
},
limit: 30,
station: selectedStation,
});

const loadMoreTriggerRef = useInfiniteScroll({
Expand All @@ -57,11 +44,27 @@ export function StoreList() {
enabled: !isLoading && !error,
});

const storeList = data?.pages.flatMap((page) => page.response?.data || []) || [];

return (
<div className="flex flex-col bg-gray0 pb-[20px]">
<StoreLevelList userLevel={selectedLevel} onLevelChange={handleLevelChange} />
<FilterBottomSheet
isOpen={isFilterOpen}
onClose={closeFilter}
initialFilters={filters}
onApply={handleApplyFilters}
defaultTab="levelFilter"
/>

<div className="flex gap-2 p-[20px] justify-between">
<LevelFilterButton honbobLevel={levelFilterDisplayValue} onClick={openFilter} />
<RoundedSelectBox
value={filters.sortBy}
onChange={(value) =>
setFilters({ ...filters, sortBy: value as "DISTANCE" | "RECOMMENDED" })
}
options={sortOptions}
placeholder="μ •λ ¬"
/>
</div>
{isLoading && (
<div className="flex h-full min-h-[calc(100vh-380px)] flex-col items-center justify-center bg-gray0 px-[20px]">
<span className="text-body2 text-gray500">λ‘œλ”© 쀑...</span>
Expand Down
3 changes: 2 additions & 1 deletion web/src/features/filter/model/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import type { Dispatch, SetStateAction } from "react";

export interface FilterData {
price: { min: number; max: number };
honbobLevel: number;
honbobLevel: number[];
seatTypes: ("FOR_ONE" | "FOR_TWO" | "FOR_FOUR" | "CUBICLE" | "BAR_TABLE")[];
categories: string[];
sortBy: "DISTANCE" | "RECOMMENDED";
}

export interface SectionConfig {
Expand Down
Loading