mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2025-11-18 11:36:17 +00:00
Update AppImage
This commit is contained in:
@@ -7,7 +7,6 @@ import { Input } from "./ui/input"
|
|||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select"
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select"
|
||||||
import { ScrollArea } from "./ui/scroll-area"
|
import { ScrollArea } from "./ui/scroll-area"
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./ui/tabs"
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./ui/tabs"
|
||||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "./ui/dialog"
|
|
||||||
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from "./ui/sheet"
|
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from "./ui/sheet"
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"
|
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"
|
||||||
import { Calendar } from "./ui/calendar"
|
import { Calendar } from "./ui/calendar"
|
||||||
@@ -21,7 +20,6 @@ import {
|
|||||||
XCircle,
|
XCircle,
|
||||||
Database,
|
Database,
|
||||||
Activity,
|
Activity,
|
||||||
HardDrive,
|
|
||||||
CalendarIcon,
|
CalendarIcon,
|
||||||
RefreshCw,
|
RefreshCw,
|
||||||
Bell,
|
Bell,
|
||||||
@@ -126,6 +124,13 @@ export function SystemLogs() {
|
|||||||
fetchAllData()
|
fetchAllData()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (dateFilter !== "custom") {
|
||||||
|
// Reload logs when non-custom filter changes
|
||||||
|
fetchSystemLogs().then(setLogs)
|
||||||
|
}
|
||||||
|
}, [dateFilter, customDays])
|
||||||
|
|
||||||
const fetchAllData = async () => {
|
const fetchAllData = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
@@ -164,7 +169,23 @@ export function SystemLogs() {
|
|||||||
|
|
||||||
const fetchSystemLogs = async (): Promise<SystemLog[]> => {
|
const fetchSystemLogs = async (): Promise<SystemLog[]> => {
|
||||||
try {
|
try {
|
||||||
const apiUrl = getApiUrl("/api/logs")
|
let apiUrl = getApiUrl("/api/logs")
|
||||||
|
|
||||||
|
const params = new URLSearchParams()
|
||||||
|
|
||||||
|
if (dateFilter === "custom" && dateRange?.from && dateRange?.to) {
|
||||||
|
params.append("from_date", format(dateRange.from, "yyyy-MM-dd"))
|
||||||
|
params.append("to_date", format(dateRange.to, "yyyy-MM-dd"))
|
||||||
|
} else if (dateFilter !== "now") {
|
||||||
|
const daysAgo = dateFilter === "custom" ? Number.parseInt(customDays) : Number.parseInt(dateFilter)
|
||||||
|
params.append("since_days", daysAgo.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.toString()) {
|
||||||
|
apiUrl += `?${params.toString()}`
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("[v0] Fetching logs from:", apiUrl)
|
||||||
|
|
||||||
const response = await fetch(apiUrl, {
|
const response = await fetch(apiUrl, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
@@ -455,9 +476,18 @@ export function SystemLogs() {
|
|||||||
|
|
||||||
const handleApplyDateRange = async () => {
|
const handleApplyDateRange = async () => {
|
||||||
if (dateRange?.from && dateRange?.to) {
|
if (dateRange?.from && dateRange?.to) {
|
||||||
|
console.log("[v0] Applying date range filter:", dateRange)
|
||||||
setIsDatePickerOpen(false)
|
setIsDatePickerOpen(false)
|
||||||
// Fetch logs with the selected date range
|
setLoading(true)
|
||||||
await fetchSystemLogs()
|
try {
|
||||||
|
const newLogs = await fetchSystemLogs()
|
||||||
|
setLogs(newLogs)
|
||||||
|
console.log("[v0] Loaded", newLogs.length, "logs for date range")
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[v0] Error loading logs for date range:", error)
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -787,12 +817,14 @@ export function SystemLogs() {
|
|||||||
{event.type}
|
{event.type}
|
||||||
{event.vmid && ` (VM/CT ${event.vmid})`}
|
{event.vmid && ` (VM/CT ${event.vmid})`}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-muted-foreground whitespace-nowrap">{event.duration}</div>
|
<div className="text-xs text-muted-foreground font-mono whitespace-nowrap">
|
||||||
|
{event.starttime}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="text-sm text-foreground mb-1 line-clamp-2">{event.message}</div>
|
||||||
<div className="text-xs text-muted-foreground truncate">
|
<div className="text-xs text-muted-foreground truncate">
|
||||||
Node: {event.node} • User: {event.user}
|
Node: {event.node} • User: {event.user}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-muted-foreground mt-1">{event.starttime}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -800,7 +832,7 @@ export function SystemLogs() {
|
|||||||
{events.length === 0 && (
|
{events.length === 0 && (
|
||||||
<div className="text-center py-8 text-muted-foreground">
|
<div className="text-center py-8 text-muted-foreground">
|
||||||
<Activity className="h-12 w-12 mx-auto mb-4 opacity-50" />
|
<Activity className="h-12 w-12 mx-auto mb-4 opacity-50" />
|
||||||
<p>No recent events found</p>
|
<p>No events found</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -809,64 +841,32 @@ export function SystemLogs() {
|
|||||||
|
|
||||||
{/* Backups Tab */}
|
{/* Backups Tab */}
|
||||||
<TabsContent value="backups" className="space-y-4">
|
<TabsContent value="backups" className="space-y-4">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
|
<ScrollArea className="h-[600px] w-full rounded-md border border-border">
|
||||||
<Card className="bg-card/50 border-border">
|
|
||||||
<CardContent className="pt-6">
|
|
||||||
<div className="text-2xl font-bold text-cyan-500">{backupStats.qemu}</div>
|
|
||||||
<p className="text-xs text-muted-foreground mt-1">QEMU Backups</p>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
<Card className="bg-card/50 border-border">
|
|
||||||
<CardContent className="pt-6">
|
|
||||||
<div className="text-2xl font-bold text-orange-500">{backupStats.lxc}</div>
|
|
||||||
<p className="text-xs text-muted-foreground mt-1">LXC Backups</p>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
<Card className="bg-card/50 border-border">
|
|
||||||
<CardContent className="pt-6">
|
|
||||||
<div className="text-2xl font-bold text-foreground">{formatBytes(backupStats.totalSize)}</div>
|
|
||||||
<p className="text-xs text-muted-foreground mt-1">Total Size</p>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ScrollArea className="h-[500px] w-full rounded-md border border-border">
|
|
||||||
<div className="space-y-2 p-4">
|
<div className="space-y-2 p-4">
|
||||||
{backups.map((backup, index) => (
|
{backups.map((backup, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className="flex items-start space-x-4 p-3 rounded-lg bg-card/50 border border-border/50 hover:bg-card/80 transition-colors cursor-pointer"
|
className="flex flex-col md:flex-row md:items-start space-y-2 md:space-y-0 md:space-x-4 p-3 rounded-lg bg-card/50 border border-border/50 hover:bg-card/80 transition-colors cursor-pointer"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedBackup(backup)
|
setSelectedBackup(backup)
|
||||||
setIsBackupModalOpen(true)
|
setIsBackupModalOpen(true)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
<HardDrive className="h-5 w-5 text-blue-500" />
|
<Badge variant="outline" className={getBackupTypeColor(backup.volid)}>
|
||||||
|
{getBackupTypeLabel(backup.volid)}
|
||||||
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center justify-between mb-1 gap-2 flex-wrap">
|
<div className="flex items-center justify-between mb-1">
|
||||||
<div className="flex items-center gap-2 flex-wrap">
|
<div className="text-sm font-medium text-foreground truncate">{backup.storage}</div>
|
||||||
<Badge variant="outline" className={getBackupTypeColor(backup.volid)}>
|
<div className="text-xs text-muted-foreground font-mono whitespace-nowrap">
|
||||||
{getBackupTypeLabel(backup.volid)}
|
{backup.created}
|
||||||
</Badge>
|
|
||||||
<Badge variant="outline" className={getBackupStorageColor(backup.volid)}>
|
|
||||||
{getBackupStorageLabel(backup.volid)}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
</div>
|
||||||
<Badge
|
|
||||||
variant="outline"
|
|
||||||
className="bg-green-500/10 text-green-500 border-green-500/20 whitespace-nowrap"
|
|
||||||
>
|
|
||||||
{backup.size_human}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
<div className="text-xs text-muted-foreground mb-1 truncate">Storage: {backup.storage}</div>
|
|
||||||
<div className="text-xs text-muted-foreground flex items-center">
|
|
||||||
<Calendar className="h-3 w-3 mr-1 flex-shrink-0" />
|
|
||||||
<span className="truncate">{backup.created}</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="text-sm text-foreground mb-1 line-clamp-2">{backup.volid}</div>
|
||||||
|
<div className="text-xs text-muted-foreground truncate">Size: {backup.size_human}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -894,23 +894,22 @@ export function SystemLogs() {
|
|||||||
setIsNotificationModalOpen(true)
|
setIsNotificationModalOpen(true)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex-shrink-0 flex items-center gap-2">
|
<div className="flex-shrink-0">
|
||||||
{getNotificationIcon(notification.type)}
|
|
||||||
<Badge variant="outline" className={getNotificationTypeColor(notification.type)}>
|
<Badge variant="outline" className={getNotificationTypeColor(notification.type)}>
|
||||||
|
{getNotificationIcon(notification.type)}
|
||||||
{notification.type.toUpperCase()}
|
{notification.type.toUpperCase()}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center justify-between mb-1">
|
<div className="flex items-center justify-between mb-1">
|
||||||
<div className="text-xs text-muted-foreground font-mono whitespace-nowrap ml-2">
|
<div className="text-sm font-medium text-foreground truncate">{notification.service}</div>
|
||||||
|
<div className="text-xs text-muted-foreground font-mono whitespace-nowrap">
|
||||||
{notification.timestamp}
|
{notification.timestamp}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-foreground mb-1 line-clamp-2">{notification.message}</div>
|
<div className="text-sm text-foreground mb-1 line-clamp-2">{notification.message}</div>
|
||||||
<div className="text-xs text-muted-foreground truncate">
|
<div className="text-xs text-muted-foreground truncate">Source: {notification.source}</div>
|
||||||
Service: {notification.service} • Source: {notification.source}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -927,236 +926,6 @@ export function SystemLogs() {
|
|||||||
</Tabs>
|
</Tabs>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Dialog open={isLogModalOpen} onOpenChange={setIsLogModalOpen}>
|
|
||||||
<DialogContent className="max-w-3xl max-h-[80vh] overflow-y-auto">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle className="flex items-center gap-2">
|
|
||||||
<FileText className="h-5 w-5" />
|
|
||||||
Log Details
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogDescription>Complete information about this log entry</DialogDescription>
|
|
||||||
</DialogHeader>
|
|
||||||
{selectedLog && (
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">Level</div>
|
|
||||||
<Badge variant="outline" className={getLevelColor(selectedLog.level)}>
|
|
||||||
{getLevelIcon(selectedLog.level)}
|
|
||||||
{selectedLog.level.toUpperCase()}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">Service</div>
|
|
||||||
<div className="text-sm text-foreground break-words">{selectedLog.service}</div>
|
|
||||||
</div>
|
|
||||||
<div className="sm:col-span-2">
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">Timestamp</div>
|
|
||||||
<div className="text-sm text-foreground font-mono break-words">{selectedLog.timestamp}</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">Source</div>
|
|
||||||
<div className="text-sm text-foreground break-words">{selectedLog.source}</div>
|
|
||||||
</div>
|
|
||||||
{selectedLog.pid && (
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">Process ID</div>
|
|
||||||
<div className="text-sm text-foreground font-mono">{selectedLog.pid}</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{selectedLog.hostname && (
|
|
||||||
<div className="sm:col-span-2">
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">Hostname</div>
|
|
||||||
<div className="text-sm text-foreground break-words">{selectedLog.hostname}</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-2">Message</div>
|
|
||||||
<div className="p-4 rounded-lg bg-muted/50 border border-border">
|
|
||||||
<pre className="text-sm text-foreground whitespace-pre-wrap break-words">{selectedLog.message}</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
<Dialog open={isEventModalOpen} onOpenChange={setIsEventModalOpen}>
|
|
||||||
<DialogContent className="max-w-3xl max-h-[80vh] overflow-y-auto">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle className="flex items-center gap-2">
|
|
||||||
<Activity className="h-5 w-5" />
|
|
||||||
Event Details
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogDescription>Complete information about this event</DialogDescription>
|
|
||||||
</DialogHeader>
|
|
||||||
{selectedEvent && (
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">Status</div>
|
|
||||||
<Badge variant="outline" className={getLevelColor(selectedEvent.level)}>
|
|
||||||
{getLevelIcon(selectedEvent.level)}
|
|
||||||
{selectedEvent.status}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">Type</div>
|
|
||||||
<div className="text-sm text-foreground break-words">{selectedEvent.type}</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">Node</div>
|
|
||||||
<div className="text-sm text-foreground">{selectedEvent.node}</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">User</div>
|
|
||||||
<div className="text-sm text-foreground break-words">{selectedEvent.user}</div>
|
|
||||||
</div>
|
|
||||||
{selectedEvent.vmid && (
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">VM/CT ID</div>
|
|
||||||
<div className="text-sm text-foreground font-mono">{selectedEvent.vmid}</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">Duration</div>
|
|
||||||
<div className="text-sm text-foreground">{selectedEvent.duration}</div>
|
|
||||||
</div>
|
|
||||||
<div className="sm:col-span-2">
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">Start Time</div>
|
|
||||||
<div className="text-sm text-foreground break-words">{selectedEvent.starttime}</div>
|
|
||||||
</div>
|
|
||||||
<div className="sm:col-span-2">
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">End Time</div>
|
|
||||||
<div className="text-sm text-foreground break-words">{selectedEvent.endtime}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-2">UPID</div>
|
|
||||||
<div className="p-4 rounded-lg bg-muted/50 border border-border">
|
|
||||||
<pre className="text-sm text-foreground font-mono whitespace-pre-wrap break-all">
|
|
||||||
{selectedEvent.upid}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
<Dialog open={isBackupModalOpen} onOpenChange={setIsBackupModalOpen}>
|
|
||||||
<DialogContent className="max-w-3xl max-h-[80vh] overflow-y-auto">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle className="flex items-center gap-2">
|
|
||||||
<Database className="h-5 w-5" />
|
|
||||||
Backup Details
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogDescription>Complete information about this backup</DialogDescription>
|
|
||||||
</DialogHeader>
|
|
||||||
{selectedBackup && (
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">Type</div>
|
|
||||||
<Badge variant="outline" className={getBackupTypeColor(selectedBackup.volid)}>
|
|
||||||
{getBackupTypeLabel(selectedBackup.volid)}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">Storage Type</div>
|
|
||||||
<Badge variant="outline" className={getBackupStorageColor(selectedBackup.volid)}>
|
|
||||||
{getBackupStorageLabel(selectedBackup.volid)}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">Storage</div>
|
|
||||||
<div className="text-sm text-foreground break-words">{selectedBackup.storage}</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">Size</div>
|
|
||||||
<Badge variant="outline" className="bg-green-500/10 text-green-500 border-green-500/20">
|
|
||||||
{selectedBackup.size_human}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
{selectedBackup.vmid && (
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">VM/CT ID</div>
|
|
||||||
<div className="text-sm text-foreground font-mono">{selectedBackup.vmid}</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="sm:col-span-2">
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">Created</div>
|
|
||||||
<div className="text-sm text-foreground break-words">{selectedBackup.created}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-2">Volume ID</div>
|
|
||||||
<div className="p-4 rounded-lg bg-muted/50 border border-border">
|
|
||||||
<pre className="text-sm text-foreground font-mono whitespace-pre-wrap break-all">
|
|
||||||
{selectedBackup.volid}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
<Dialog open={isNotificationModalOpen} onOpenChange={setIsNotificationModalOpen}>
|
|
||||||
<DialogContent className="max-w-3xl max-h-[80vh] overflow-y-auto">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle className="flex items-center gap-2">
|
|
||||||
<Bell className="h-5 w-5" />
|
|
||||||
Notification Details
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogDescription>Complete information about this notification</DialogDescription>
|
|
||||||
</DialogHeader>
|
|
||||||
{selectedNotification && (
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">Type</div>
|
|
||||||
<Badge variant="outline" className={getNotificationTypeColor(selectedNotification.type)}>
|
|
||||||
{selectedNotification.type.toUpperCase()}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
<div className="sm:col-span-2">
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">Timestamp</div>
|
|
||||||
<div className="text-sm text-foreground font-mono break-words">{selectedNotification.timestamp}</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">Service</div>
|
|
||||||
<div className="text-sm text-foreground break-words">{selectedNotification.service}</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-1">Source</div>
|
|
||||||
<div className="text-sm text-foreground break-words">{selectedNotification.source}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="text-sm font-medium text-muted-foreground mb-2">Message</div>
|
|
||||||
<div className="p-4 rounded-lg bg-muted/50 border border-border max-h-[300px] overflow-y-auto">
|
|
||||||
<pre className="text-sm text-foreground whitespace-pre-wrap break-words">
|
|
||||||
{selectedNotification.message}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-end">
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => handleDownloadNotificationLog(selectedNotification)}
|
|
||||||
className="border-border w-full sm:w-auto"
|
|
||||||
>
|
|
||||||
<Download className="h-4 w-4 mr-2" />
|
|
||||||
Download Complete Message
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ function Calendar({ className, classNames, showOutsideDays = true, ...props }: C
|
|||||||
nav: "space-x-1 flex items-center",
|
nav: "space-x-1 flex items-center",
|
||||||
nav_button: cn(
|
nav_button: cn(
|
||||||
buttonVariants({ variant: "outline" }),
|
buttonVariants({ variant: "outline" }),
|
||||||
"h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100",
|
"h-8 w-8 bg-background p-0 hover:bg-accent hover:text-accent-foreground border-border",
|
||||||
),
|
),
|
||||||
nav_button_previous: "absolute left-1",
|
nav_button_previous: "absolute left-1",
|
||||||
nav_button_next: "absolute right-1",
|
nav_button_next: "absolute right-1",
|
||||||
@@ -31,11 +31,10 @@ function Calendar({ className, classNames, showOutsideDays = true, ...props }: C
|
|||||||
head_cell: "text-muted-foreground rounded-md w-9 font-normal text-[0.8rem] flex items-center justify-center",
|
head_cell: "text-muted-foreground rounded-md w-9 font-normal text-[0.8rem] flex items-center justify-center",
|
||||||
row: "grid grid-cols-7 gap-1 mt-2",
|
row: "grid grid-cols-7 gap-1 mt-2",
|
||||||
cell: cn(
|
cell: cn(
|
||||||
"h-9 w-9 text-center text-sm p-0 relative",
|
"h-9 w-9 text-center text-sm p-0 relative flex items-center justify-center",
|
||||||
"[&:has([aria-selected].day-range-end)]:rounded-r-md",
|
"[&:has([aria-selected].day-range-end)]:rounded-r-md",
|
||||||
"[&:has([aria-selected].day-range-start)]:rounded-l-md",
|
"[&:has([aria-selected].day-range-start)]:rounded-l-md",
|
||||||
"[&:has([aria-selected].day-range-middle)]:bg-accent/50",
|
"[&:has([aria-selected].day-range-middle)]:bg-primary/20",
|
||||||
"[&:has([aria-selected])]:bg-accent",
|
|
||||||
"focus-within:relative focus-within:z-20",
|
"focus-within:relative focus-within:z-20",
|
||||||
),
|
),
|
||||||
day: cn(buttonVariants({ variant: "ghost" }), "h-9 w-9 p-0 font-normal aria-selected:opacity-100"),
|
day: cn(buttonVariants({ variant: "ghost" }), "h-9 w-9 p-0 font-normal aria-selected:opacity-100"),
|
||||||
@@ -47,7 +46,7 @@ function Calendar({ className, classNames, showOutsideDays = true, ...props }: C
|
|||||||
day_outside:
|
day_outside:
|
||||||
"day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30",
|
"day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30",
|
||||||
day_disabled: "text-muted-foreground opacity-50",
|
day_disabled: "text-muted-foreground opacity-50",
|
||||||
day_range_middle: "aria-selected:bg-accent/50 aria-selected:text-accent-foreground rounded-none",
|
day_range_middle: "aria-selected:bg-primary/20 aria-selected:text-foreground rounded-none",
|
||||||
day_hidden: "invisible",
|
day_hidden: "invisible",
|
||||||
...classNames,
|
...classNames,
|
||||||
}}
|
}}
|
||||||
|
|||||||
Reference in New Issue
Block a user