Files
tasteby/frontend/src/components/MyReviewsList.tsx
joungmin 3134994817 비공개 메모 기능 추가 + 아이콘 개선
- 식당별 1:1 비공개 메모 CRUD (user_memos 테이블)
- 내 기록에 리뷰/메모 탭 분리
- 백오피스 유저 관리에 메모 수/상세 표시
- 리뷰/메모 작성 시 현재 날짜 기본값
- 지도우선/목록우선 버튼 Material Symbols 아이콘 적용

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 14:10:06 +09:00

144 lines
4.9 KiB
TypeScript

"use client";
import { useState } from "react";
import type { Review, Memo } from "@/lib/api";
import Icon from "@/components/Icon";
interface MyReview extends Review {
restaurant_id: string;
restaurant_name: string | null;
}
interface MyMemo extends Memo {
restaurant_name: string | null;
}
interface MyReviewsListProps {
reviews: MyReview[];
memos: MyMemo[];
onClose: () => void;
onSelectRestaurant: (restaurantId: string) => void;
}
export default function MyReviewsList({
reviews,
memos,
onClose,
onSelectRestaurant,
}: MyReviewsListProps) {
const [tab, setTab] = useState<"reviews" | "memos">("reviews");
return (
<div className="p-4 space-y-3">
<div className="flex justify-between items-center">
<h2 className="font-bold text-lg"> </h2>
<button
onClick={onClose}
className="text-gray-400 hover:text-gray-600"
>
<Icon name="close" size={18} />
</button>
</div>
<div className="flex gap-1 border-b">
<button
onClick={() => setTab("reviews")}
className={`px-3 py-1.5 text-sm font-medium border-b-2 transition-colors ${
tab === "reviews"
? "border-brand-500 text-brand-600"
: "border-transparent text-gray-500 hover:text-gray-700"
}`}
>
<Icon name="rate_review" size={14} className="mr-1" />
({reviews.length})
</button>
<button
onClick={() => setTab("memos")}
className={`px-3 py-1.5 text-sm font-medium border-b-2 transition-colors ${
tab === "memos"
? "border-brand-500 text-brand-600"
: "border-transparent text-gray-500 hover:text-gray-700"
}`}
>
<Icon name="edit_note" size={14} className="mr-1" />
({memos.length})
</button>
</div>
{tab === "reviews" ? (
reviews.length === 0 ? (
<p className="text-sm text-gray-500 py-8 text-center">
.
</p>
) : (
<div className="space-y-2">
{reviews.map((r) => (
<button
key={r.id}
onClick={() => onSelectRestaurant(r.restaurant_id)}
className="w-full text-left border rounded-lg p-3 hover:bg-gray-50 transition-colors"
>
<div className="flex items-center justify-between mb-1">
<span className="font-semibold text-sm truncate">
{r.restaurant_name || "알 수 없는 식당"}
</span>
<span className="text-yellow-500 text-sm shrink-0 ml-2">
{"★".repeat(Math.round(r.rating))}
<span className="text-gray-500 ml-1">{r.rating}</span>
</span>
</div>
{r.review_text && (
<p className="text-xs text-gray-600 line-clamp-2">
{r.review_text}
</p>
)}
<div className="flex items-center gap-2 mt-1.5 text-[10px] text-gray-400">
{r.visited_at && <span>: {r.visited_at}</span>}
{r.created_at && <span>{r.created_at.slice(0, 10)}</span>}
</div>
</button>
))}
</div>
)
) : (
memos.length === 0 ? (
<p className="text-sm text-gray-500 py-8 text-center">
.
</p>
) : (
<div className="space-y-2">
{memos.map((m) => (
<button
key={m.id}
onClick={() => onSelectRestaurant(m.restaurant_id)}
className="w-full text-left border border-brand-200 rounded-lg p-3 bg-brand-50/30 hover:bg-brand-50 transition-colors"
>
<div className="flex items-center justify-between mb-1">
<span className="font-semibold text-sm truncate">
{m.restaurant_name || "알 수 없는 식당"}
</span>
{m.rating && (
<span className="text-yellow-500 text-sm shrink-0 ml-2">
{"★".repeat(Math.round(m.rating))}
<span className="text-gray-500 ml-1">{m.rating}</span>
</span>
)}
</div>
{m.memo_text && (
<p className="text-xs text-gray-600 line-clamp-2">
{m.memo_text}
</p>
)}
<div className="flex items-center gap-2 mt-1.5 text-[10px] text-gray-400">
{m.visited_at && <span>: {m.visited_at}</span>}
<span className="text-brand-400"></span>
</div>
</button>
))}
</div>
)
)}
</div>
);
}