diff --git a/app/notifications/page.tsx b/app/notifications/page.tsx index c3f1b94..b46a912 100644 --- a/app/notifications/page.tsx +++ b/app/notifications/page.tsx @@ -14,32 +14,12 @@ import { type Notification, } from '@/lib/api/notifications' import { getAuthErrorMessage } from '@/lib/utils/authErrors' -import { AlertTriangleIcon, CheckCircleIcon, Button, ArrowLeftIcon } from '@ciphera-net/ui' +import { formatTimeAgo, getTypeIcon } from '@/lib/utils/notifications' +import { Button, ArrowLeftIcon } from '@ciphera-net/ui' import { toast } from '@ciphera-net/ui' const PAGE_SIZE = 50 -function formatTimeAgo(dateStr: string): string { - const d = new Date(dateStr) - const now = new Date() - const diffMs = now.getTime() - d.getTime() - const diffMins = Math.floor(diffMs / 60000) - const diffHours = Math.floor(diffMs / 3600000) - const diffDays = Math.floor(diffMs / 86400000) - if (diffMins < 1) return 'Just now' - if (diffMins < 60) return `${diffMins}m ago` - if (diffHours < 24) return `${diffHours}h ago` - if (diffDays < 7) return `${diffDays}d ago` - return d.toLocaleDateString() -} - -function getTypeIcon(type: string) { - if (type.includes('down') || type.includes('degraded') || type.startsWith('billing_')) { - return - } - return -} - export default function NotificationsPage() { const { user } = useAuth() const [notifications, setNotifications] = useState([]) diff --git a/components/notifications/NotificationCenter.tsx b/components/notifications/NotificationCenter.tsx index 70b0508..1258e72 100644 --- a/components/notifications/NotificationCenter.tsx +++ b/components/notifications/NotificationCenter.tsx @@ -8,7 +8,8 @@ import { useEffect, useState, useRef } from 'react' import Link from 'next/link' import { listNotifications, markNotificationRead, markAllNotificationsRead, type Notification } from '@/lib/api/notifications' import { getAuthErrorMessage } from '@/lib/utils/authErrors' -import { AlertTriangleIcon, CheckCircleIcon, SettingsIcon } from '@ciphera-net/ui' +import { formatTimeAgo, getTypeIcon } from '@/lib/utils/notifications' +import { SettingsIcon } from '@ciphera-net/ui' // * Bell icon (simple SVG, no extra deps) function BellIcon({ className }: { className?: string }) { @@ -31,27 +32,6 @@ function BellIcon({ className }: { className?: string }) { ) } -function formatTimeAgo(dateStr: string): string { - const d = new Date(dateStr) - const now = new Date() - const diffMs = now.getTime() - d.getTime() - const diffMins = Math.floor(diffMs / 60000) - const diffHours = Math.floor(diffMs / 3600000) - const diffDays = Math.floor(diffMs / 86400000) - if (diffMins < 1) return 'Just now' - if (diffMins < 60) return `${diffMins}m ago` - if (diffHours < 24) return `${diffHours}h ago` - if (diffDays < 7) return `${diffDays}d ago` - return d.toLocaleDateString() -} - -function getTypeIcon(type: string) { - if (type.includes('down') || type.includes('degraded') || type.startsWith('billing_')) { - return - } - return -} - const LOADING_DELAY_MS = 250 const POLL_INTERVAL_MS = 90_000 diff --git a/components/settings/OrganizationSettings.tsx b/components/settings/OrganizationSettings.tsx index 7cd052f..8268940 100644 --- a/components/settings/OrganizationSettings.tsx +++ b/components/settings/OrganizationSettings.tsx @@ -281,17 +281,17 @@ export default function OrganizationSettings() { }, [currentOrgId]) useEffect(() => { - if (activeTab === 'notifications' && currentOrgId) { + if (activeTab === 'notifications' && currentOrgId && user?.role !== 'member') { loadNotificationSettings() } - }, [activeTab, currentOrgId, loadNotificationSettings]) + }, [activeTab, currentOrgId, loadNotificationSettings, user?.role]) // * Redirect members away from Notifications tab (owners/admins only) useEffect(() => { if (activeTab === 'notifications' && user?.role === 'member') { handleTabChange('general') } - }, [activeTab, user?.role]) + }, [activeTab, user?.role, handleTabChange]) // If no org ID, we are in personal organization context, so don't show org settings if (!currentOrgId) { diff --git a/lib/utils/notifications.tsx b/lib/utils/notifications.tsx new file mode 100644 index 0000000..9ed6c7c --- /dev/null +++ b/lib/utils/notifications.tsx @@ -0,0 +1,28 @@ +import { AlertTriangleIcon, CheckCircleIcon } from '@ciphera-net/ui' + +/** + * Formats a date string as a human-readable relative time (e.g. "5m ago", "2h ago"). + */ +export function formatTimeAgo(dateStr: string): string { + const d = new Date(dateStr) + const now = new Date() + const diffMs = now.getTime() - d.getTime() + const diffMins = Math.floor(diffMs / 60000) + const diffHours = Math.floor(diffMs / 3600000) + const diffDays = Math.floor(diffMs / 86400000) + if (diffMins < 1) return 'Just now' + if (diffMins < 60) return `${diffMins}m ago` + if (diffHours < 24) return `${diffHours}h ago` + if (diffDays < 7) return `${diffDays}d ago` + return d.toLocaleDateString() +} + +/** + * Returns the icon for a notification type (alert for down/degraded/billing, check for success). + */ +export function getTypeIcon(type: string) { + if (type.includes('down') || type.includes('degraded') || type.startsWith('billing_')) { + return + } + return +}