Files
pagodera/components/marketplace/MyAdsIntentionList.tsx
T
Evandro Fonseca Junior ab37b477df feat: implement market page with QueryProvider and dynamic listings
- wrap root layout with QueryProvider for React Query
- replace market UnderConstruction placeholder with MarketClient and real data fetching
- implement search, pagination, and sort via search params using getActiveListings
- add necessary client components and queries
- update dependencies (e.g., prisma, pkg-types)
2025-11-05 01:58:03 -03:00

228 lines
7.0 KiB
TypeScript

"use client";
import { useState, useEffect } from "react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { ShoppingCart, Clock, CheckCircle2, XCircle, MessageSquare } from "lucide-react";
import { IntentionDetailModal } from "./IntentionDetailModal";
interface PurchaseIntention {
intention_id: number;
buyer_id: number;
payment_method: string;
status: "pending" | "accepted" | "rejected" | "expired" | "completed";
buyer_message: string | null;
seller_response: string | null;
created_at: string;
buyer?: {
user_id: number;
username: string;
email: string;
};
}
interface MyAdsIntentionListProps {
listingId: number;
sellerId: number;
}
function formatTimeAgo(date: Date): string {
const seconds = Math.floor((new Date().getTime() - date.getTime()) / 1000);
if (seconds < 60) return "há alguns segundos";
const minutes = Math.floor(seconds / 60);
if (minutes < 60) return `há ${minutes} ${minutes === 1 ? "minuto" : "minutos"}`;
const hours = Math.floor(minutes / 60);
if (hours < 24) return `há ${hours} ${hours === 1 ? "hora" : "horas"}`;
const days = Math.floor(hours / 24);
return `há ${days} ${days === 1 ? "dia" : "dias"}`;
}
function getStatusBadge(status: string) {
switch (status) {
case "pending":
return (
<Badge variant="secondary" className="bg-yellow-500/20 text-yellow-700 dark:text-yellow-400">
<Clock className="h-3 w-3 mr-1" />
Pendente
</Badge>
);
case "accepted":
return (
<Badge variant="secondary" className="bg-green-500/20 text-green-700 dark:text-green-400">
<CheckCircle2 className="h-3 w-3 mr-1" />
Aceita
</Badge>
);
case "rejected":
return (
<Badge variant="secondary" className="bg-red-500/20 text-red-700 dark:text-red-400">
<XCircle className="h-3 w-3 mr-1" />
Rejeitada
</Badge>
);
default:
return <Badge variant="secondary">{status}</Badge>;
}
}
function getPaymentMethodLabel(method: string): string {
switch (method) {
case "pix":
return "PIX";
case "tibia_coins":
return "Tibia Coins";
case "gold_coins":
return "Gold Coins";
case "trade":
return "Troca";
default:
return method.toUpperCase();
}
}
export function MyAdsIntentionList({ listingId, sellerId }: MyAdsIntentionListProps) {
const [intentions, setIntentions] = useState<PurchaseIntention[]>([]);
const [loading, setLoading] = useState(true);
const [selectedIntention, setSelectedIntention] = useState<PurchaseIntention | null>(null);
const [filter, setFilter] = useState<"all" | "pending" | "accepted" | "rejected">("all");
useEffect(() => {
loadIntentions();
}, [listingId, filter]);
const loadIntentions = async () => {
setLoading(true);
try {
const params = new URLSearchParams();
if (filter !== "all") {
params.set("status", filter);
}
const res = await fetch(`/api/listings/${listingId}/intentions?${params.toString()}`);
if (res.ok) {
const data = await res.json();
if (data.success) {
setIntentions(data.intentions || []);
}
}
} catch (error) {
console.error("Erro ao carregar intenções:", error);
} finally {
setLoading(false);
}
};
const pendingCount = intentions.filter((i) => i.status === "pending").length;
if (loading) {
return (
<div className="space-y-2">
{Array.from({ length: 3 }).map((_, i) => (
<Skeleton key={i} className="h-20 w-full" />
))}
</div>
);
}
if (intentions.length === 0) {
return (
<div className="text-center py-8 text-sm text-muted-foreground">
Nenhuma intenção de compra encontrada
</div>
);
}
return (
<div className="space-y-4">
{/* Filtros */}
<div className="flex items-center gap-2 flex-wrap">
<Button
variant={filter === "all" ? "default" : "outline"}
size="sm"
onClick={() => setFilter("all")}
>
Todas ({intentions.length})
</Button>
<Button
variant={filter === "pending" ? "default" : "outline"}
size="sm"
onClick={() => setFilter("pending")}
>
Pendentes ({pendingCount})
</Button>
<Button
variant={filter === "accepted" ? "default" : "outline"}
size="sm"
onClick={() => setFilter("accepted")}
>
Aceitas
</Button>
<Button
variant={filter === "rejected" ? "default" : "outline"}
size="sm"
onClick={() => setFilter("rejected")}
>
Rejeitadas
</Button>
</div>
{/* Lista de intenções */}
<div className="space-y-2">
{intentions.map((intention) => (
<div
key={intention.intention_id}
className="border rounded-lg p-4 hover:bg-muted/50 transition-colors cursor-pointer"
onClick={() => setSelectedIntention(intention)}
>
<div className="flex items-start justify-between gap-4">
<div className="flex items-start gap-3 flex-1">
<Avatar className="h-10 w-10">
<AvatarFallback>
{intention.buyer?.username.charAt(0).toUpperCase() || "?"}
</AvatarFallback>
</Avatar>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1">
<span className="font-medium text-sm">
{intention.buyer?.username || "Usuário"}
</span>
{getStatusBadge(intention.status)}
</div>
<div className="flex items-center gap-2 text-xs text-muted-foreground mb-2">
<ShoppingCart className="h-3 w-3" />
<span>{getPaymentMethodLabel(intention.payment_method)}</span>
<span>•</span>
<Clock className="h-3 w-3" />
<span>{formatTimeAgo(new Date(intention.created_at))}</span>
</div>
{intention.buyer_message && (
<p className="text-xs text-muted-foreground line-clamp-1">
<MessageSquare className="h-3 w-3 inline mr-1" />
{intention.buyer_message}
</p>
)}
</div>
</div>
</div>
</div>
))}
</div>
{/* Modal de detalhes */}
{selectedIntention && (
<IntentionDetailModal
open={!!selectedIntention}
onOpenChange={(open) => {
if (!open) setSelectedIntention(null);
}}
intention={selectedIntention}
listingId={listingId}
onUpdate={loadIntentions}
/>
)}
</div>
);
}