feat: enhance purchase intentions with chat integration and offer tracking

- Added MongoDB chat/message integration for seller responses
- Improved seller response validation and default messaging
- Integrated automatic chat creation/update when responding to intentions
- Added highest custom offer fields to listing queries and models
- Enhanced listing data with top offer information for buyers
This commit is contained in:
Evandro Fonseca Junior committed 2025-11-08 11:51:21 -03:00
1 parent d27526e026
commit d8e7d5d7e6
19 files changed
+806 -58

No files matched your search

@@ -133,6 +133,23 @@ function formatPrice(price: number | null, method: string): string {
return price.toLocaleString("pt-BR");
}
function formatCustomOffer(amount: number | null | undefined, currency: string | null | undefined): string | null {
if (amount === null || amount === undefined || !currency) {
return null;
}
switch (currency) {
case "pix":
return `R$ ${amount.toFixed(2)}`;
case "tibia_coins":
return `${Math.round(amount).toLocaleString("pt-BR")} TC`;
case "gold_coins":
return `${Math.round(amount).toLocaleString("pt-BR")} GP`;
default:
return amount.toLocaleString("pt-BR");
}
}
interface IntentionCardProps {
intention: PurchaseIntention;
locale?: string;
@@ -213,6 +230,10 @@ function IntentionCard({ intention, locale = "pt" }: IntentionCardProps) {
: listing.price_gold_coins && intention.payment_method === "gold_coins"
? listing.price_gold_coins
: null;
const customOfferLabel = formatCustomOffer(
typeof intention.custom_offer_amount === "number" ? intention.custom_offer_amount : null,
intention.custom_offer_currency ?? null
);
return (
<>
@@ -256,6 +277,13 @@ function IntentionCard({ intention, locale = "pt" }: IntentionCardProps) {
</span>
)}
</div>
{customOfferLabel && (
<div className="flex items-center gap-1 text-foreground">
<Badge variant="outline" className="border-primary text-primary">
Sua oferta: {customOfferLabel}
</Badge>
</div>
)}
</div>
</div>
</div>
@@ -496,10 +524,7 @@ interface MyPurchaseIntentionsPanelProps {
export function MyPurchaseIntentionsPanel({ locale = "pt" }: MyPurchaseIntentionsPanelProps) {
const { user } = useSession();
const [filter, setFilter] = useState<PurchaseIntentionStatus | "all">("all");
const { intentions, isLoading, refetch } = useMyPurchaseIntentions(
filter === "all" ? undefined : filter
);
const { intentions, isLoading } = useMyPurchaseIntentions();
// Contar por status
const pendingCount = intentions.filter((i) => i.status === "pending").length;
@@ -507,6 +532,10 @@ export function MyPurchaseIntentionsPanel({ locale = "pt" }: MyPurchaseIntention
const rejectedCount = intentions.filter((i) => i.status === "rejected").length;
const expiredCount = intentions.filter((i) => i.status === "expired").length;
const completedCount = intentions.filter((i) => i.status === "completed").length;
const filteredIntentions =
filter === "all"
? intentions
: intentions.filter((intention) => intention.status === filter);
if (!user) {
return (
@@ -582,7 +611,7 @@ export function MyPurchaseIntentionsPanel({ locale = "pt" }: MyPurchaseIntention
<Skeleton key={i} className="h-48 w-full" />
))}
</div>
) : intentions.length === 0 ? (
) : filteredIntentions.length === 0 ? (
<div className="text-center py-12 border rounded-lg">
<ShoppingCart className="h-12 w-12 mx-auto mb-4 text-muted-foreground opacity-50" />
<p className="text-lg font-medium mb-2">Nenhuma intenção de compra encontrada</p>
@@ -597,7 +626,7 @@ export function MyPurchaseIntentionsPanel({ locale = "pt" }: MyPurchaseIntention
</div>
) : (
<div className="space-y-4">
{intentions.map((intention) => (
{filteredIntentions.map((intention) => (
<IntentionCard key={intention.intention_id} intention={intention} locale={locale} />
))}
</div>