This commit is contained in:
bipproduction
2025-10-21 16:35:17 +08:00
parent b16f31524b
commit 68c35e1257

View File

@@ -1,66 +1,185 @@
import apiFetch from "@/lib/apiFetch"; import apiFetch from "@/lib/apiFetch";
import { Button, Card, Group, Pagination, Skeleton, Stack, Text, Title } from "@mantine/core"; import {
Button,
Card,
Container,
Group,
Pagination,
Skeleton,
Stack,
Text,
Title,
Badge,
ScrollArea,
Tooltip,
} from "@mantine/core";
import { useLocalStorage, useShallowEffect } from "@mantine/hooks"; import { useLocalStorage, useShallowEffect } from "@mantine/hooks";
import { showNotification } from "@mantine/notifications";
import { IconRefresh, IconMessageCircle, IconUser, IconCalendar, IconHash, IconCode } from "@tabler/icons-react";
import dayjs from "dayjs"; import dayjs from "dayjs";
import useSWR from "swr"; import useSWR from "swr";
import { showNotification } from "@mantine/notifications";
export default function WaHookHome() { export default function WaHookHome() {
const [page, setPage] = useLocalStorage({ const [page, setPage] = useLocalStorage({ key: "wa-hook-page", defaultValue: 1 });
key: "wa-hook-page", const { data, error, isLoading, mutate } = useSWR(
defaultValue: 1, "/wa-hook",
}) () => apiFetch["wa-hook"].list.get({ query: { page, limit: 10 } }),
const { data, error, isLoading, mutate } = useSWR("/wa-hook", () => apiFetch["wa-hook"].list.get({ query: { page, limit: 10 } }), { {
refreshInterval: 3000, refreshInterval: 4000,
revalidateOnFocus: true, revalidateOnFocus: true,
revalidateOnReconnect: true, revalidateOnReconnect: true,
revalidateIfStale: true, dedupingInterval: 3000,
refreshWhenHidden: true, }
refreshWhenOffline: true, );
dedupingInterval: 3000,
})
useShallowEffect(() => { useShallowEffect(() => {
mutate() mutate();
setPage(data?.data?.list?.length || 1) setPage(data?.data?.list?.length || 1);
}, []) }, []);
async function reset() { async function handleReset() {
await apiFetch["wa-hook"].reset.post() await apiFetch["wa-hook"].reset.post();
mutate() mutate();
showNotification({ title: "Success", message: "WhatsApp Hook reset", color: "green" }) showNotification({
title: "Reset Completed",
message: "WhatsApp Hook data has been successfully reset.",
color: "teal",
});
} }
if (isLoading) return <Skeleton height={500} /> if (isLoading) return <Skeleton height={500} radius="lg" />;
if (error) return <div>Error: {error.message}</div> if (error)
return (
<Container p="xl">
<Text c="red.5" ta="center" fz="lg" fw={500}>
Failed to load data: {error.message}
</Text>
</Container>
);
return ( return (
<Stack> <Container size="lg" p="lg" style={{ backgroundColor: "#191919", borderRadius: 20 }}>
<Title order={2}>WaHookHome</Title> <Stack gap="xl">
<Group justify="flex-end"> <Group justify="space-between" align="center">
<Button onClick={reset}>Reset</Button> <Title order={2} c="#EAEAEA" fw={700} style={{ letterSpacing: 0.5 }}>
</Group> WhatsApp Hook Monitor
{data?.data?.list.map((item) => ( </Title>
<Card key={item.id}> <Tooltip label="Reset all webhook data" withArrow color="teal">
<Stack> <Button
<Text>Name: {item.data?.entry?.[0]?.changes?.[0]?.value?.contacts?.[0]?.profile?.name}</Text> onClick={handleReset}
<Text>From: {item.data?.entry?.[0]?.changes?.[0]?.value?.messages?.[0]?.from}</Text> leftSection={<IconRefresh size={18} />}
<Text>ID: {item.data?.entry?.[0]?.changes?.[0]?.value?.messages?.[0]?.id}</Text> variant="gradient"
<Text>Timestamp: {dayjs(item.data?.entry?.[0]?.changes?.[0]?.value?.messages?.[0]?.timestamp).format("YYYY-MM-DD HH:mm:ss")}</Text> gradient={{ from: "#00FFC8", to: "#00FFFF", deg: 45 }}
<Text>Type: {item.data?.entry?.[0]?.changes?.[0]?.value?.messages?.[0]?.type}</Text> radius="xl"
<Text>Body: {item.data?.entry?.[0]?.changes?.[0]?.value?.messages?.[0]?.text?.body}</Text> >
{JSON.stringify((item.data as any)?.answer)} Reset
</Button>
</Tooltip>
</Group>
<ScrollArea style={{ height: 600 }}>
<Stack gap="md">
{data?.data?.list?.length ? (
data.data.list.map((item) => {
const msg = item.data?.entry?.[0]?.changes?.[0]?.value?.messages?.[0];
const contact = item.data?.entry?.[0]?.changes?.[0]?.value?.contacts?.[0];
const answer = (item.data as any)?.answer;
return (
<Card
key={item.id}
radius="lg"
style={{
background:
"linear-gradient(145deg, rgba(45,45,45,0.8) 0%, rgba(25,25,25,0.95) 100%)",
backdropFilter: "blur(12px)",
border: "1px solid rgba(0,255,200,0.2)",
boxShadow: "0 0 10px rgba(0,255,200,0.15)",
}}
>
<Stack gap={6}>
<Group gap="xs">
<IconUser size={16} color="#00FFC8" />
<Text c="#EAEAEA" fw={500}>
{contact?.profile?.name || "Unknown"}
</Text>
</Group>
<Group gap="xs">
<IconMessageCircle size={16} color="#00FFFF" />
<Text c="#9A9A9A">{msg?.text?.body || "(No message body)"}</Text>
</Group>
<Group gap="xs">
<IconHash size={16} color="#00FFC8" />
<Text c="#9A9A9A" fz="sm">
{msg?.id}
</Text>
</Group>
<Group gap="xs">
<IconCalendar size={16} color="#00FFFF" />
<Text c="#9A9A9A" fz="sm">
{dayjs(Number(msg?.timestamp) * 1000).format("YYYY-MM-DD HH:mm:ss")}
</Text>
</Group>
<Group gap="xs">
<IconCode size={16} color="#B554FF" />
<Badge color="grape" radius="sm" variant="light">
{msg?.type || "Unknown"}
</Badge>
</Group>
{answer && (
<Card
p="sm"
radius="md"
style={{
backgroundColor: "#2D2D2D",
border: "1px solid rgba(0,255,255,0.1)",
}}
>
<Text c="#EAEAEA" fz="sm">
{JSON.stringify(answer, null, 2)}
</Text>
</Card>
)}
</Stack>
</Card>
);
})
) : (
<Card
radius="lg"
style={{
backgroundColor: "#2D2D2D",
border: "1px solid rgba(0,255,255,0.1)",
textAlign: "center",
padding: 40,
}}
>
<Text c="#9A9A9A" fz="lg">
No webhook data available yet.
</Text>
</Card>
)}
</Stack> </Stack>
</Card> </ScrollArea>
))}
<Pagination <Group justify="center" mt="md">
value={page} <Pagination
total={data?.data?.count || 1} value={page}
onChange={(value) => { total={data?.data?.count || 1}
setPage(value) onChange={(value) => {
mutate() setPage(value);
}} mutate();
withEdges }}
/> radius="xl"
</Stack> withEdges
color="teal"
/>
</Group>
</Stack>
</Container>
); );
} }