feat: enhance NotificationCenter with system update notifications and improve ProxyHostForm connection source handling

This commit is contained in:
Wikid82
2025-11-21 15:09:18 -05:00
parent 1a29b7ae76
commit f6bd3ecb59
4 changed files with 133 additions and 85 deletions

View File

@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { Bell, X, Info, AlertTriangle, AlertCircle, CheckCircle } from 'lucide-react';
import { getNotifications, markNotificationRead, markAllNotificationsRead } from '../api/system';
import { Bell, X, Info, AlertTriangle, AlertCircle, CheckCircle, ExternalLink } from 'lucide-react';
import { getNotifications, markNotificationRead, markAllNotificationsRead, checkUpdates } from '../api/system';
const NotificationCenter: React.FC = () => {
const [isOpen, setIsOpen] = useState(false);
@@ -13,6 +13,12 @@ const NotificationCenter: React.FC = () => {
refetchInterval: 30000, // Poll every 30s
});
const { data: updateInfo } = useQuery({
queryKey: ['system-updates'],
queryFn: checkUpdates,
staleTime: 1000 * 60 * 60, // 1 hour
});
const markReadMutation = useMutation({
mutationFn: markNotificationRead,
onSuccess: () => {
@@ -27,7 +33,15 @@ const NotificationCenter: React.FC = () => {
},
});
const unreadCount = notifications.length;
const unreadCount = notifications.length + (updateInfo?.available ? 1 : 0);
const hasCritical = notifications.some(n => n.type === 'error');
const hasWarning = notifications.some(n => n.type === 'warning') || updateInfo?.available;
const getBellColor = () => {
if (hasCritical) return 'text-red-500 hover:text-red-600';
if (hasWarning) return 'text-yellow-500 hover:text-yellow-600';
return 'text-green-500 hover:text-green-600';
};
const getIcon = (type: string) => {
switch (type) {
@@ -42,12 +56,12 @@ const NotificationCenter: React.FC = () => {
<div className="relative">
<button
onClick={() => setIsOpen(!isOpen)}
className="relative p-2 text-gray-400 hover:text-white focus:outline-none"
className={`relative p-2 focus:outline-none transition-colors ${getBellColor()}`}
aria-label="Notifications"
>
<Bell className="w-6 h-6" />
{unreadCount > 0 && (
<span className="absolute top-0 right-0 inline-flex items-center justify-center px-2 py-1 text-xs font-bold leading-none text-red-100 transform translate-x-1/4 -translate-y-1/4 bg-red-600 rounded-full">
<span className="absolute top-0 right-0 inline-flex items-center justify-center px-2 py-1 text-xs font-bold leading-none text-white transform translate-x-1/4 -translate-y-1/4 bg-red-600 rounded-full">
{unreadCount}
</span>
)}
@@ -63,7 +77,7 @@ const NotificationCenter: React.FC = () => {
<div className="absolute right-0 z-20 w-80 mt-2 overflow-hidden bg-white rounded-md shadow-lg dark:bg-gray-800 ring-1 ring-black ring-opacity-5">
<div className="flex items-center justify-between px-4 py-2 border-b dark:border-gray-700">
<h3 className="text-sm font-medium text-gray-900 dark:text-white">Notifications</h3>
{unreadCount > 0 && (
{notifications.length > 0 && (
<button
onClick={() => markAllReadMutation.mutate()}
className="text-xs text-blue-600 hover:text-blue-500 dark:text-blue-400"
@@ -73,7 +87,29 @@ const NotificationCenter: React.FC = () => {
)}
</div>
<div className="max-h-96 overflow-y-auto">
{notifications.length === 0 ? (
{/* Update Notification */}
{updateInfo?.available && (
<div className="flex items-start px-4 py-3 border-b dark:border-gray-700 bg-yellow-50 dark:bg-yellow-900/10 hover:bg-yellow-100 dark:hover:bg-yellow-900/20">
<div className="flex-shrink-0 mt-0.5">
<AlertCircle className="w-5 h-5 text-yellow-500" />
</div>
<div className="ml-3 w-0 flex-1">
<p className="text-sm font-medium text-gray-900 dark:text-white">
Update Available: {updateInfo.latest_version}
</p>
<a
href={updateInfo.changelog_url}
target="_blank"
rel="noopener noreferrer"
className="mt-1 text-sm text-blue-600 hover:text-blue-500 dark:text-blue-400 flex items-center"
>
View Changelog <ExternalLink className="w-3 h-3 ml-1" />
</a>
</div>
</div>
)}
{notifications.length === 0 && !updateInfo?.available ? (
<div className="px-4 py-6 text-center text-sm text-gray-500 dark:text-gray-400">
No new notifications
</div>