diff --git a/apps/api/internal/notification/model.go b/apps/api/internal/notification/model.go index 5994586..85eca49 100644 --- a/apps/api/internal/notification/model.go +++ b/apps/api/internal/notification/model.go @@ -39,6 +39,7 @@ type NotificationList struct { Message string `json:"message"` Type NotificationType `json:"type"` Status NotificationStatus `json:"status"` + Metadata json.RawMessage `json:"metadata,omitempty"` CreatedAt string `json:"created_at"` } diff --git a/apps/api/internal/notification/notification_repository.go b/apps/api/internal/notification/notification_repository.go index 2301c37..e771454 100644 --- a/apps/api/internal/notification/notification_repository.go +++ b/apps/api/internal/notification/notification_repository.go @@ -27,7 +27,7 @@ func (r *NotificationRepository) CreateNotification(n *Notification) error { func (r *NotificationRepository) GetUserNotifications(userID uuid.UUID) ([]*NotificationList, error) { query := ` - SELECT id, title, message, type, status, created_at + SELECT id, title, message, type, status, metadata, created_at FROM notifications WHERE user_id = $1 AND (status = 'unread' OR created_at > datetime('now', '-7 days')) @@ -45,7 +45,7 @@ func (r *NotificationRepository) GetUserNotifications(userID uuid.UUID) ([]*Noti var notifications []*NotificationList for rows.Next() { n := &NotificationList{} - err := rows.Scan(&n.ID, &n.Title, &n.Message, &n.Type, &n.Status, &n.CreatedAt) + err := rows.Scan(&n.ID, &n.Title, &n.Message, &n.Type, &n.Status, &n.Metadata, &n.CreatedAt) if err != nil { return nil, err } diff --git a/apps/web/components/ui/sheet.tsx b/apps/web/components/ui/sheet.tsx new file mode 100644 index 0000000..272cb72 --- /dev/null +++ b/apps/web/components/ui/sheet.tsx @@ -0,0 +1,140 @@ +"use client" + +import * as React from "react" +import * as SheetPrimitive from "@radix-ui/react-dialog" +import { cva, type VariantProps } from "class-variance-authority" +import { X } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Sheet = SheetPrimitive.Root + +const SheetTrigger = SheetPrimitive.Trigger + +const SheetClose = SheetPrimitive.Close + +const SheetPortal = SheetPrimitive.Portal + +const SheetOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetOverlay.displayName = SheetPrimitive.Overlay.displayName + +const sheetVariants = cva( + "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out", + { + variants: { + side: { + top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", + bottom: + "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", + left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", + right: + "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", + }, + }, + defaultVariants: { + side: "right", + }, + } +) + +interface SheetContentProps + extends React.ComponentPropsWithoutRef, + VariantProps {} + +const SheetContent = React.forwardRef< + React.ElementRef, + SheetContentProps +>(({ side = "right", className, children, ...props }, ref) => ( + + + + + + Close + + {children} + + +)) +SheetContent.displayName = SheetPrimitive.Content.displayName + +const SheetHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +SheetHeader.displayName = "SheetHeader" + +const SheetFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +SheetFooter.displayName = "SheetFooter" + +const SheetTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetTitle.displayName = SheetPrimitive.Title.displayName + +const SheetDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetDescription.displayName = SheetPrimitive.Description.displayName + +export { + Sheet, + SheetPortal, + SheetOverlay, + SheetTrigger, + SheetClose, + SheetContent, + SheetHeader, + SheetFooter, + SheetTitle, + SheetDescription, +} diff --git a/apps/web/components/views/history/backup-details-sheet.tsx b/apps/web/components/views/history/backup-details-sheet.tsx new file mode 100644 index 0000000..7db3c69 --- /dev/null +++ b/apps/web/components/views/history/backup-details-sheet.tsx @@ -0,0 +1,192 @@ +'use client'; + +import { Database, Calendar, Clock, AlertCircle } from 'lucide-react'; +import { Badge } from '@/components/ui/badge'; +import { ScrollArea } from '@/components/ui/scroll-area'; +import { Separator } from '@/components/ui/separator'; +import { + Sheet, + SheetContent, + SheetDescription, + SheetHeader, + SheetTitle, +} from '@/components/ui/sheet'; +import type { Backup } from '@/types/backup'; +import { useNotifications } from '@/hooks/use-notifications'; +import { useConnections } from '@/hooks/use-connections'; +import { useEffect, useState } from 'react'; + +interface BackupDetailsSheetProps { + backup: Backup | null; + open: boolean; + onClose: () => void; +} + +export function BackupDetailsSheet({ backup, open, onClose }: BackupDetailsSheetProps) { + const { notifications } = useNotifications(); + const { connections } = useConnections(); + const [relatedNotification, setRelatedNotification] = useState(null); + const [connectionName, setConnectionName] = useState('Unknown Connection'); + + useEffect(() => { + if (backup && connections) { + const connection = connections.find((c: any) => c.id === backup.connection_id); + setConnectionName(connection?.name || 'Unknown Connection'); + } + }, [backup, connections]); + + useEffect(() => { + if (backup && backup.status === 'failed' && notifications) { + // Find notification related to this backup + const notification = notifications.find( + (n: any) => + n.type === 'backup_failed' && + n.metadata?.connection_id === backup.connection_id && + Math.abs(new Date(n.created_at).getTime() - new Date(backup.created_at).getTime()) < 5000 + ); + setRelatedNotification(notification); + } + }, [backup, notifications]); + + if (!backup) return null; + + return ( + { + if (!isOpen) { + onClose(); + } + }}> + + +
+
+ +
+
+ Backup Error Details + {connectionName} +
+
+
+ + +
+ {/* Status */} +
+ +
+ + + Failed + +
+
+ + + + {/* Error Message */} + {relatedNotification && ( + <> +
+ +
+

+ {relatedNotification.message} +

+ {relatedNotification.metadata?.error && ( +
+                        {relatedNotification.metadata.error}
+                      
+ )} +
+
+ + + + )} + + {/* Database Info */} +
+ +
+
+ Connection + + {connectionName} + +
+ {backup.database_name && ( +
+ Database + + {backup.database_name} + +
+ )} +
+
+ + + + {/* Timing Info */} +
+ +
+
+ + + Started + + + {new Date(backup.started_time).toLocaleString()} + +
+ {backup.completed_time && ( +
+ + + Failed At + + + {new Date(backup.completed_time).toLocaleString()} + +
+ )} +
+
+ + {/* Path */} + {backup.path && ( + <> + +
+ +
+ + {backup.path} + +
+
+ + )} +
+
+
+
+ ); +} diff --git a/apps/web/components/views/history/history-list.tsx b/apps/web/components/views/history/history-list.tsx index df97170..6c8564f 100644 --- a/apps/web/components/views/history/history-list.tsx +++ b/apps/web/components/views/history/history-list.tsx @@ -17,6 +17,7 @@ import { useNotifications } from '@/hooks/use-notifications'; import { NotificationSidebar } from "./notification-sidebar"; import { BackupCompareDialog } from "./backup-compare-dialog"; import { RestoreDialog } from "./restore-dialog"; +import { BackupDetailsSheet } from "./backup-details-sheet"; import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "@/components/ui/tooltip"; import { useState, useMemo } from "react"; import { useIsFetching } from "@tanstack/react-query"; @@ -28,6 +29,8 @@ export function HistoryList() { const [selectedBackupForCompare, setSelectedBackupForCompare] = useState(); const [restoreDialogOpen, setRestoreDialogOpen] = useState(false); const [selectedBackupForRestore, setSelectedBackupForRestore] = useState(null); + const [detailsSheetOpen, setDetailsSheetOpen] = useState(false); + const [selectedBackupForDetails, setSelectedBackupForDetails] = useState(null); const isFetchingBackups = useIsFetching({ queryKey: ['backups'] }); const [dateRange, setDateRange] = useState("all"); @@ -302,6 +305,29 @@ export function HistoryList() { notifications={notifications} isLoading={isLoadingNotifications} onMarkAsRead={markNotificationsAsRead} + onNotificationClick={(notification) => { + // For failed backups, create a backup object from notification metadata + // since failed backups don't exist in the database + if (notification.metadata) { + const failedBackup: BackupList = { + id: notification.id, + connection_id: notification.metadata.connection_id || '', + database_name: notification.metadata.database_name || '', + database_type: notification.metadata.database_type || '', + path: '', + size: 0, + status: 'failed', + started_time: notification.created_at, + completed_time: notification.created_at, + created_at: notification.created_at, + scheduled_time: '', + updated_at: notification.created_at, + }; + + setSelectedBackupForDetails(failedBackup); + setDetailsSheetOpen(true); + } + }} />
@@ -321,6 +347,15 @@ export function HistoryList() { open={restoreDialogOpen} onOpenChange={setRestoreDialogOpen} /> + + { + setDetailsSheetOpen(false); + setSelectedBackupForDetails(null); + }} + /> ); } \ No newline at end of file diff --git a/apps/web/components/views/history/notification-sidebar.tsx b/apps/web/components/views/history/notification-sidebar.tsx index 22183a0..39a9669 100644 --- a/apps/web/components/views/history/notification-sidebar.tsx +++ b/apps/web/components/views/history/notification-sidebar.tsx @@ -9,9 +9,10 @@ interface NotificationSidebarProps { notifications: Notification[]; isLoading: boolean; onMarkAsRead: (ids: string) => void; + onNotificationClick?: (notification: Notification) => void; } -export function NotificationSidebar({ notifications, isLoading, onMarkAsRead }: NotificationSidebarProps) { +export function NotificationSidebar({ notifications, isLoading, onMarkAsRead, onNotificationClick }: NotificationSidebarProps) { const unreadNotifications = notifications.filter(n => n.status === 'unread'); const recentNotifications = notifications.slice(0, 5); @@ -48,13 +49,17 @@ export function NotificationSidebar({ notifications, isLoading, onMarkAsRead }: if (notification.status === 'unread') { onMarkAsRead(notification.id); } + + if (onNotificationClick) { + onNotificationClick(notification); + } }} className={cn( - "p-3 rounded-lg border transition-all", - "hover:shadow-sm", + "p-3 rounded-lg border transition-all cursor-pointer", + "hover:shadow-sm hover:border-destructive/30", notification.status === 'unread' - ? "bg-background hover:bg-accent/50 cursor-pointer border-primary/20" - : "bg-muted/30", + ? "bg-background hover:bg-accent/50 border-destructive/20" + : "bg-muted/30 border-muted", )} >
diff --git a/apps/web/components/views/history/restore-dialog.tsx b/apps/web/components/views/history/restore-dialog.tsx index b9c2ef1..768d3fc 100644 --- a/apps/web/components/views/history/restore-dialog.tsx +++ b/apps/web/components/views/history/restore-dialog.tsx @@ -68,7 +68,6 @@ export function RestoreDialog({ backup, open, onOpenChange }: RestoreDialogProps (conn) => conn.type === backup?.database_type ); - console.log(connections) return ( diff --git a/apps/web/package-lock.json b/apps/web/package-lock.json index c5a28b2..c6e6ba2 100644 --- a/apps/web/package-lock.json +++ b/apps/web/package-lock.json @@ -8,7 +8,7 @@ "name": "velld-ui", "version": "0.1.0", "dependencies": { - "@radix-ui/react-dialog": "^1.1.4", + "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-label": "^2.1.1", "@radix-ui/react-scroll-area": "^1.2.2", @@ -1047,25 +1047,184 @@ } }, "node_modules/@radix-ui/react-dialog": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.4.tgz", - "integrity": "sha512-Ur7EV1IwQGCyaAuyDRiOLA5JIUZxELJljF+MbM/2NC0BYwfuRrbpS30BiQBJrVruscgUkieKkqXYDOoByaxIoA==", - "license": "MIT", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", + "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.3", - "@radix-ui/react-focus-guards": "1.1.1", - "@radix-ui/react-focus-scope": "1.1.1", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-portal": "1.1.3", - "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-slot": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.1.0", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "^2.6.1" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==" + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -1082,6 +1241,108 @@ } } }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-direction": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", diff --git a/apps/web/package.json b/apps/web/package.json index f3799bc..2d80e60 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -9,7 +9,7 @@ "lint": "next lint" }, "dependencies": { - "@radix-ui/react-dialog": "^1.1.4", + "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-label": "^2.1.1", "@radix-ui/react-scroll-area": "^1.2.2", diff --git a/apps/web/types/notification.ts b/apps/web/types/notification.ts index c42ca2a..4368ba9 100644 --- a/apps/web/types/notification.ts +++ b/apps/web/types/notification.ts @@ -10,6 +10,11 @@ export interface Notification { type: NotificationType; status: NotificationStatus; created_at: string; + metadata?: { + connection_id?: string; + error?: string; + [key: string]: any; + }; } export type NotificationResponse = Base; \ No newline at end of file