153 lines
6.0 KiB
TypeScript
153 lines
6.0 KiB
TypeScript
|
|
import { useLanguage } from "../contexts/LanguageContext";
|
||
|
|
import { useListWhatsappLog, useSendWhatsapp, getListWhatsappLogQueryKey, getListRequestsQueryKey } from "@workspace/api-client-react";
|
||
|
|
import { useQueryClient } from "@tanstack/react-query";
|
||
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||
|
|
import { Badge } from "@/components/ui/badge";
|
||
|
|
import { Button } from "@/components/ui/button";
|
||
|
|
import { Skeleton } from "@/components/ui/skeleton";
|
||
|
|
import { Separator } from "@/components/ui/separator";
|
||
|
|
import { MessageSquare, Phone, Send, CheckCircle, Clock, XCircle } from "lucide-react";
|
||
|
|
import { useListRequests } from "@workspace/api-client-react";
|
||
|
|
|
||
|
|
export default function WhatsappLog() {
|
||
|
|
const { t } = useLanguage();
|
||
|
|
const queryClient = useQueryClient();
|
||
|
|
const { data: logs, isLoading } = useListWhatsappLog();
|
||
|
|
const { data: allRequests } = useListRequests();
|
||
|
|
const sendWhatsapp = useSendWhatsapp();
|
||
|
|
|
||
|
|
const getRequestByCase = (caseId: string) =>
|
||
|
|
allRequests?.find((r) => r.caseId === caseId);
|
||
|
|
|
||
|
|
const handleSend = (caseId: string) => {
|
||
|
|
const req = getRequestByCase(caseId);
|
||
|
|
if (!req) return;
|
||
|
|
sendWhatsapp.mutate(
|
||
|
|
{ id: req.id },
|
||
|
|
{
|
||
|
|
onSuccess: () => {
|
||
|
|
queryClient.invalidateQueries({ queryKey: getListWhatsappLogQueryKey() });
|
||
|
|
queryClient.invalidateQueries({ queryKey: getListRequestsQueryKey() });
|
||
|
|
},
|
||
|
|
}
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
const statusConfig = {
|
||
|
|
pending: {
|
||
|
|
icon: Clock,
|
||
|
|
className: "bg-amber-100 text-amber-700 border-amber-200",
|
||
|
|
label: t.whatsapp.pending,
|
||
|
|
},
|
||
|
|
sent: {
|
||
|
|
icon: CheckCircle,
|
||
|
|
className: "bg-green-100 text-green-700 border-green-200",
|
||
|
|
label: t.whatsapp.sent,
|
||
|
|
},
|
||
|
|
failed: {
|
||
|
|
icon: XCircle,
|
||
|
|
className: "bg-red-100 text-red-700 border-red-200",
|
||
|
|
label: t.whatsapp.failed,
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="container mx-auto px-4 py-12">
|
||
|
|
<div className="mb-8 flex items-center gap-3">
|
||
|
|
<MessageSquare className="w-7 h-7 text-primary" />
|
||
|
|
<h1 className="text-3xl font-bold text-foreground">{t.whatsapp.title}</h1>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{isLoading ? (
|
||
|
|
<div className="space-y-4">
|
||
|
|
{[1, 2, 3].map((i) => (
|
||
|
|
<Skeleton key={i} className="h-48 w-full" />
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
) : !logs || logs.length === 0 ? (
|
||
|
|
<Card>
|
||
|
|
<CardContent className="py-16 text-center text-muted-foreground">
|
||
|
|
No WhatsApp log entries yet.
|
||
|
|
</CardContent>
|
||
|
|
</Card>
|
||
|
|
) : (
|
||
|
|
<div className="space-y-4">
|
||
|
|
{logs.map((log) => {
|
||
|
|
const status = statusConfig[log.status as keyof typeof statusConfig] || statusConfig.pending;
|
||
|
|
const StatusIcon = status.icon;
|
||
|
|
|
||
|
|
return (
|
||
|
|
<Card key={log.id} className="overflow-hidden" data-testid={`card-whatsapp-${log.id}`}>
|
||
|
|
<CardHeader className="pb-3 bg-muted/20">
|
||
|
|
<div className="flex items-center justify-between flex-wrap gap-3">
|
||
|
|
<div className="flex items-center gap-3">
|
||
|
|
<CardTitle className="text-base font-mono">{log.caseId}</CardTitle>
|
||
|
|
<Badge variant="outline" className={status.className}>
|
||
|
|
<StatusIcon className="w-3 h-3 me-1" />
|
||
|
|
{status.label}
|
||
|
|
</Badge>
|
||
|
|
</div>
|
||
|
|
{log.status === "pending" && (
|
||
|
|
<Button
|
||
|
|
size="sm"
|
||
|
|
onClick={() => handleSend(log.caseId)}
|
||
|
|
disabled={sendWhatsapp.isPending}
|
||
|
|
className="gap-2"
|
||
|
|
data-testid={`button-sendWhatsapp-${log.id}`}
|
||
|
|
>
|
||
|
|
<Send className="w-3.5 h-3.5" />
|
||
|
|
{t.whatsapp.sendViaOpenClaw}
|
||
|
|
</Button>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
</CardHeader>
|
||
|
|
<CardContent className="pt-4">
|
||
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||
|
|
{/* Donor Info */}
|
||
|
|
<div>
|
||
|
|
<p className="text-xs font-semibold uppercase text-muted-foreground tracking-wide mb-2">
|
||
|
|
{t.whatsapp.donor}
|
||
|
|
</p>
|
||
|
|
<p className="font-medium">{log.donorName}</p>
|
||
|
|
<div className="flex items-center gap-1.5 text-sm text-muted-foreground mt-1">
|
||
|
|
<Phone className="w-3.5 h-3.5" />
|
||
|
|
<span>{log.donorPhone}</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Beneficiary Message */}
|
||
|
|
<div>
|
||
|
|
<p className="text-xs font-semibold uppercase text-muted-foreground tracking-wide mb-2">
|
||
|
|
Beneficiary Message
|
||
|
|
</p>
|
||
|
|
<p className="text-sm text-foreground italic">"{log.beneficiaryMessage}"</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<Separator className="my-4" />
|
||
|
|
|
||
|
|
{/* WhatsApp Message Preview */}
|
||
|
|
<div>
|
||
|
|
<p className="text-xs font-semibold uppercase text-muted-foreground tracking-wide mb-2">
|
||
|
|
{t.whatsapp.message}
|
||
|
|
</p>
|
||
|
|
<div className="bg-[#dcf8c6] dark:bg-green-900/30 rounded-xl rounded-tl-sm p-4 max-w-lg text-sm whitespace-pre-line text-gray-800 dark:text-gray-200 border border-green-200 dark:border-green-700">
|
||
|
|
{log.whatsappMessage}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{log.sentAt && (
|
||
|
|
<p className="text-xs text-muted-foreground mt-3">
|
||
|
|
{t.whatsapp.sentAt}: {new Date(log.sentAt).toLocaleString()}
|
||
|
|
</p>
|
||
|
|
)}
|
||
|
|
</CardContent>
|
||
|
|
</Card>
|
||
|
|
);
|
||
|
|
})}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|