Skip to content
Open
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
6 changes: 0 additions & 6 deletions src/components/restaurant/RestaurantCard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { categoryLabel, type RestaurantSummary } from "@/types/store";
import { Star } from "lucide-react";

type Props = {
restaurant: RestaurantSummary;
Expand All @@ -22,11 +21,6 @@ export default function RestaurantCard({ restaurant, onClick }: Props) {
{categoryLabel[restaurant.category]} • {restaurant.address}
</p>
</div>

<span className="flex items-center gap-2 text-sm text-gray-500 shrink-0 self-center">
<Star className="size-5 text-yellow-500 fill-yellow-500" />
{restaurant.rating.toFixed(1)}
</span>
</div>
</button>
);
Expand Down
12 changes: 12 additions & 0 deletions src/components/restaurant/RestaurantCardSkeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export default function RestaurantCardSkeleton() {
return (
<div className="w-full px-5 py-4" aria-hidden="true">
<div className="flex items-center justify-between gap-3">
<div className="min-w-0 flex-1">
<div className="h-5 w-2/3 rounded bg-gray-200 animate-pulse" />
<div className="mt-2 h-4 w-5/6 rounded bg-gray-200 animate-pulse" />
</div>
</div>
</div>
);
}
12 changes: 2 additions & 10 deletions src/components/restaurant/RestaurantDetailModal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Day, RestaurantDetail } from "@/types/store";
import { Clock, Star, X } from "lucide-react";
import { Clock, X } from "lucide-react";
import { Button } from "../ui/button";
import { useNavigate } from "react-router-dom";
import { useAuthStore } from "@/stores/useAuthStore";
Expand Down Expand Up @@ -124,6 +124,7 @@ export default function RestaurantDetailModal({
<button
type="button"
className="absolute inset-0 bg-black/40"
aria-label="모달 닫기"
onClick={() => onOpenChange(false)}
/>
<div className="relative z-10 w-[92vw] max-w-3xl rounded-2xl bg-white shadow-xl p-6">
Expand Down Expand Up @@ -165,9 +166,6 @@ export default function RestaurantDetailModal({
if (status !== "success") return null;
if (!restaurant) return null;

const rating = typeof restaurant.rating === "number" ? restaurant.rating : 0;
const reviewCount =
typeof restaurant.reviewCount === "number" ? restaurant.reviewCount : 0;
const tableImageUrls = Array.isArray(restaurant.tableImageUrls)
? restaurant.tableImageUrls
: [];
Expand Down Expand Up @@ -213,12 +211,6 @@ export default function RestaurantDetailModal({
</div>
) : null}
<div className="p-6">
<div className="flex items-center gap-2 mb-4">
<Star className="size-5 text-yellow-500 fill-yellow-500" />
<span>{rating.toFixed(1)}</span>
<span className="text-gray-500">({reviewCount}개 리뷰)</span>
</div>

<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
<div className="flex items-center gap-5">
<Clock className="size-5 text-gray-600 mt-1" />
Expand Down
8 changes: 0 additions & 8 deletions src/components/restaurant/RestaurantList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,6 @@ type Props = {
};

export default function RestaurantList({ restaurants, onSelect }: Props) {
if (restaurants.length === 0) {
return (
<div className="rounded p-6 text-center text-md text-muted-foreground">
검색 결과가 없어요.
</div>
);
}

return (
<div className="rounded-xl border bg-white overflow-hidden">
{restaurants.map((r, idx) => (
Expand Down
22 changes: 22 additions & 0 deletions src/components/restaurant/RestaurantListSkeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import RestaurantCardSkeleton from "./RestaurantCardSkeleton";

export default function RestaurantListSkeleton({
count = 8,
}: {
count?: number;
}) {
return (
<div
className="border rounded-xl bg-white overflow-hidden"
role="status"
aria-label="검색 결과 로딩 중"
>
{Array.from({ length: count }).map((_, idx) => (
<div key={idx}>
<RestaurantCardSkeleton />
{idx !== count - 1 ? <div className="h-px bg-gray-100" /> : null}
</div>
))}
</div>
);
}
40 changes: 36 additions & 4 deletions src/pages/SearchPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { useRestaurantDetail } from "@/hooks/store/useRestaurantDetail";
import { useSearchStores } from "@/hooks/store/useSearchStores";
import type { CreateBookingResult } from "@/api/endpoints/reservations";
import { toHHmm } from "@/utils/time";
import RestaurantListSkeleton from "@/components/restaurant/RestaurantListSkeleton";

export default function SearchPage() {
const [query, setQuery] = useState("");
Expand All @@ -34,6 +35,8 @@ export default function SearchPage() {

const detailQuery = useRestaurantDetail(selectedStoreId);

const [isSearchingUI, setIsSearchingUI] = useState(false);

const [searchParams, setSearchParams] = useState<{
keyword: string;
lat: number;
Expand Down Expand Up @@ -225,15 +228,26 @@ export default function SearchPage() {

if (!keyword) {
setSearchParams(null);
setIsSearchingUI(false);
return;
}
setIsSearchingUI(true);

const c = coords ?? (await getCoords());
setCoords(c);
setMapCenter({ lat: c.lat, lng: c.lng });
setSearchParams({ keyword, lat: c.lat, lng: c.lng });
};

useEffect(() => {
if (!hasSearched) return;
if (!isSearchingUI) return;

if (searchQuery.isSuccess || searchQuery.isError) {
setIsSearchingUI(false);
}
}, [hasSearched, isSearchingUI, searchQuery.isSuccess, searchQuery.isError]);

return (
<>
<div className="w-full max-w-2xl mx-auto mb-6">
Expand Down Expand Up @@ -269,11 +283,29 @@ export default function SearchPage() {
/>

<div className="mt-6 w-full max-w-2xl mx-auto">
{searchError ? (
<p className="mt-2 text-sm text-red-500">{searchError}</p>
) : null}
{hasSearched ? (
<RestaurantList restaurants={results} onSelect={handleSelectStore} />
<>
{searchError ? (
<p className="mt-2 text-sm text-red-500">{searchError}</p>
) : isSearchingUI || searchQuery.isFetching ? (
<>
<div className="mb-3 inline-flex items-center gap-2 border rounded-full px-3 py-1 text-xs text-gray-600">
<span className="h-3 w-3 animate-spin border-2 border-gray-300 border-t-transparent rounded-full" />
검색 중...
</div>
<RestaurantListSkeleton count={8} />
</>
) : results.length === 0 ? (
<div className="rounded p-6 text-center text-md text-muted-foreground">
검색 결과가 없어요.
</div>
) : (
<RestaurantList
restaurants={results}
onSelect={handleSelectStore}
/>
)}
</>
) : null}
</div>

Expand Down