144 lines
4.9 KiB
TypeScript
144 lines
4.9 KiB
TypeScript
"use client";
|
||
|
||
import { Link } from "@/i18n/navigation";
|
||
import { mediaUrl } from "@/app/lib/media";
|
||
import type { DatingProfile } from "../types";
|
||
|
||
interface SwipeCardProps {
|
||
profile: DatingProfile;
|
||
badgeText: string;
|
||
dragX: number;
|
||
isDragging: boolean;
|
||
isAnimatingOut: "left" | "right" | null;
|
||
onLike: () => void;
|
||
onDislike: () => void;
|
||
onBeginDrag: (clientX: number) => void;
|
||
onMoveDrag: (clientX: number) => void;
|
||
onEndDrag: () => void;
|
||
t: (key: string) => string;
|
||
}
|
||
|
||
export default function SwipeCard({
|
||
profile,
|
||
badgeText,
|
||
dragX,
|
||
isDragging,
|
||
isAnimatingOut,
|
||
onLike,
|
||
onDislike,
|
||
onBeginDrag,
|
||
onMoveDrag,
|
||
onEndDrag,
|
||
t,
|
||
}: SwipeCardProps) {
|
||
return (
|
||
<div
|
||
className={`relative touch-pan-y select-none overflow-hidden rounded-2xl border border-white/20 bg-gradient-to-br ${profile.gradient} p-8 pb-6 shadow-xl ${
|
||
isDragging ? "cursor-grabbing" : "cursor-grab"
|
||
} transition-transform`}
|
||
style={{
|
||
transform: `translateX(${isAnimatingOut === "right" ? 420 : isAnimatingOut === "left" ? -420 : dragX}px) rotate(${dragX / 18}deg)`,
|
||
opacity: isAnimatingOut ? 0.25 : 1,
|
||
transitionDuration: isDragging ? "0ms" : "180ms",
|
||
}}
|
||
tabIndex={0}
|
||
onKeyDown={(e) => {
|
||
if (e.key === "ArrowRight") onLike();
|
||
if (e.key === "ArrowLeft") onDislike();
|
||
}}
|
||
onPointerDown={(e) => {
|
||
e.currentTarget.setPointerCapture(e.pointerId);
|
||
onBeginDrag(e.clientX);
|
||
}}
|
||
onPointerMove={(e) => onMoveDrag(e.clientX)}
|
||
onPointerUp={onEndDrag}
|
||
onPointerCancel={onEndDrag}
|
||
>
|
||
<div
|
||
className={`pointer-events-none absolute left-5 top-5 rounded-full border-2 border-white/80 px-4 py-2 text-sm font-bold text-white transition-opacity ${
|
||
dragX > 45 ? "opacity-100" : "opacity-0"
|
||
}`}
|
||
>
|
||
{t("likeOverlay")}
|
||
</div>
|
||
<div
|
||
className={`pointer-events-none absolute right-5 top-5 rounded-full border-2 border-white/80 px-4 py-2 text-sm font-bold text-white transition-opacity ${
|
||
dragX < -45 ? "opacity-100" : "opacity-0"
|
||
}`}
|
||
>
|
||
{t("passOverlay")}
|
||
</div>
|
||
|
||
<div className="absolute right-4 top-4 flex flex-col items-end gap-1">
|
||
<span className="rounded-full bg-white/90 px-3 py-1.5 text-xs font-medium text-gray-800 backdrop-blur-sm">
|
||
{badgeText}
|
||
</span>
|
||
<Link
|
||
href={`/feedback?type=report&target=${encodeURIComponent(profile.id)}`}
|
||
className="text-sm font-medium text-[#ff4d4f] hover:text-red-600"
|
||
>
|
||
{t("report")}
|
||
</Link>
|
||
</div>
|
||
|
||
<div className="mb-6 mt-12 flex justify-center">
|
||
<img
|
||
src={mediaUrl(profile.photo)}
|
||
alt={profile.name}
|
||
className="h-28 w-28 rounded-full border-4 border-white/50 object-cover shadow-lg"
|
||
/>
|
||
</div>
|
||
|
||
<div className="mb-4 text-center">
|
||
<h3 className="text-xl font-bold text-white drop-shadow-sm">
|
||
{profile.name},{profile.age}
|
||
</h3>
|
||
<p className="mt-1 text-sm text-white/90">📍 {profile.location}</p>
|
||
{(profile.gender || profile.single) && (
|
||
<p className="mt-1 text-xs text-white/80">
|
||
{[profile.gender, profile.single].filter(Boolean).join(" · ")}
|
||
</p>
|
||
)}
|
||
</div>
|
||
|
||
<div className="mb-4">
|
||
<p className="mb-2 text-xs font-medium text-white/90">{t("matchTags")}:</p>
|
||
<div className="flex flex-wrap justify-center gap-2">
|
||
{profile.tags.map((tag) => (
|
||
<span key={tag} className="rounded-full bg-white/80 px-3 py-1 text-xs font-medium text-gray-800">
|
||
{tag}
|
||
</span>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
<p className="mb-8 px-2 text-center text-sm leading-relaxed text-white/95">{profile.bio}</p>
|
||
|
||
<div className="flex items-center justify-center gap-8">
|
||
<div className="flex flex-col items-center gap-2">
|
||
<button
|
||
type="button"
|
||
onClick={onDislike}
|
||
className="flex h-16 w-16 items-center justify-center rounded-full bg-white text-[#ff4d4f] shadow-lg transition-all hover:scale-105 hover:bg-red-50 active:scale-95"
|
||
aria-label={t("dislike")}
|
||
>
|
||
<span className="text-2xl">✕</span>
|
||
</button>
|
||
<span className="text-xs font-medium text-white/80">{t("dislike")}</span>
|
||
</div>
|
||
<div className="flex flex-col items-center gap-2">
|
||
<button
|
||
type="button"
|
||
onClick={onLike}
|
||
className="flex h-16 w-16 items-center justify-center rounded-full bg-[#22c55e] text-white shadow-lg transition-all hover:scale-105 hover:bg-[#16a34a] active:scale-95"
|
||
aria-label={t("like")}
|
||
>
|
||
<span className="text-2xl">💚</span>
|
||
</button>
|
||
<span className="text-xs font-medium text-white/80">{t("like")}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|