Files
DynamoDNS/client/src/components/activity/recent-activity.tsx
T
alphaeusmote cba930e4d3 Implement initial application structure and UI.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 9111ef36-26c8-4085-84ca-a35dc1fec1b5
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7083d608-d6d3-4a6a-9a27-6286c5109627/54918520-d42c-44cd-b4f4-9e5a42eb5be1.jpg
2025-04-09 21:34:26 +00:00

118 lines
3.5 KiB
TypeScript

import { useQuery } from "@tanstack/react-query";
import { DnsHistory } from "@shared/schema";
import { formatDistanceToNow } from "date-fns";
import { Loader2 } from "lucide-react";
import { Card, CardContent, CardTitle } from "@/components/ui/card";
interface RecentActivityProps {
domainId?: number;
}
export function RecentActivity({ domainId }: RecentActivityProps) {
// Fetch history data for the specified domain or all domains if not specified
const queryKey = domainId
? ["/api/dns-history/domain", domainId]
: ["/api/dns-history"];
const { data: historyEntries = [], isLoading } = useQuery<DnsHistory[]>({
queryKey,
enabled: domainId !== undefined,
});
// Limit to last 5 entries
const recentEntries = historyEntries.slice(0, 5);
const getActivityIcon = (action: string) => {
switch (action) {
case "create":
return "border-success";
case "update":
return "border-primary";
case "delete":
return "border-destructive";
default:
return "border-muted-foreground";
}
};
const getActivityTitle = (entry: DnsHistory) => {
if (!entry.previousValue || !entry.newValue) return "Unknown action";
try {
const prev = JSON.parse(entry.previousValue);
const curr = JSON.parse(entry.newValue);
switch (entry.action) {
case "create":
return `${curr.name} ${curr.type} record created`;
case "update":
return `${curr.name} ${curr.type} record updated`;
case "delete":
return `${prev.name} ${prev.type} record deleted`;
default:
return "Unknown action";
}
} catch (e) {
return "Error parsing history";
}
};
const getActivityDetails = (entry: DnsHistory) => {
if (!entry.newValue) return "";
try {
const data = JSON.parse(entry.newValue);
switch (entry.action) {
case "create":
case "update":
return `Value: ${data.content}`;
default:
return "";
}
} catch (e) {
return "";
}
};
if (isLoading) {
return (
<Card>
<CardContent className="pt-6">
<div className="flex justify-center py-8">
<Loader2 className="h-8 w-8 animate-spin text-primary" />
</div>
</CardContent>
</Card>
);
}
return (
<Card>
<CardContent className="pt-6">
<CardTitle className="text-lg font-semibold mb-4">Recent Activity</CardTitle>
{recentEntries.length === 0 ? (
<div className="text-center py-8 text-muted-foreground">
No recent activity found
</div>
) : (
<div className="space-y-4">
{recentEntries.map((entry) => (
<div
key={entry.id}
className={`border-l-2 ${getActivityIcon(entry.action)} pl-4 pb-5 relative`}
>
<div className={`w-2 h-2 rounded-full ${entry.action === 'create' ? 'bg-success' : entry.action === 'update' ? 'bg-primary' : 'bg-destructive'} absolute -left-[5px] top-1.5`}></div>
<p className="text-sm font-medium">{getActivityTitle(entry)}</p>
<p className="text-xs text-muted-foreground">{getActivityDetails(entry)}</p>
<p className="text-xs text-muted-foreground">
{formatDistanceToNow(new Date(entry.timestamp), { addSuffix: true })}
</p>
</div>
))}
</div>
)}
</CardContent>
</Card>
);
}