fix: extract notification utility functions for better code organizat… #29
@@ -14,32 +14,12 @@ import {
|
|||||||
type Notification,
|
type Notification,
|
||||||
} from '@/lib/api/notifications'
|
} from '@/lib/api/notifications'
|
||||||
import { getAuthErrorMessage } from '@/lib/utils/authErrors'
|
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'
|
import { toast } from '@ciphera-net/ui'
|
||||||
|
|
||||||
const PAGE_SIZE = 50
|
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 <AlertTriangleIcon className="w-4 h-4 shrink-0 text-amber-500" />
|
|
||||||
}
|
|
||||||
return <CheckCircleIcon className="w-4 h-4 shrink-0 text-emerald-500" />
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function NotificationsPage() {
|
export default function NotificationsPage() {
|
||||||
const { user } = useAuth()
|
const { user } = useAuth()
|
||||||
const [notifications, setNotifications] = useState<Notification[]>([])
|
const [notifications, setNotifications] = useState<Notification[]>([])
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ import { useEffect, useState, useRef } from 'react'
|
|||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { listNotifications, markNotificationRead, markAllNotificationsRead, type Notification } from '@/lib/api/notifications'
|
import { listNotifications, markNotificationRead, markAllNotificationsRead, type Notification } from '@/lib/api/notifications'
|
||||||
import { getAuthErrorMessage } from '@/lib/utils/authErrors'
|
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)
|
// * Bell icon (simple SVG, no extra deps)
|
||||||
function BellIcon({ className }: { className?: string }) {
|
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 <AlertTriangleIcon className="w-4 h-4 shrink-0 text-amber-500" />
|
|
||||||
}
|
|
||||||
return <CheckCircleIcon className="w-4 h-4 shrink-0 text-emerald-500" />
|
|
||||||
}
|
|
||||||
|
|
||||||
const LOADING_DELAY_MS = 250
|
const LOADING_DELAY_MS = 250
|
||||||
const POLL_INTERVAL_MS = 90_000
|
const POLL_INTERVAL_MS = 90_000
|
||||||
|
|
||||||
|
|||||||
@@ -281,17 +281,17 @@ export default function OrganizationSettings() {
|
|||||||
}, [currentOrgId])
|
}, [currentOrgId])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (activeTab === 'notifications' && currentOrgId) {
|
if (activeTab === 'notifications' && currentOrgId && user?.role !== 'member') {
|
||||||
loadNotificationSettings()
|
loadNotificationSettings()
|
||||||
}
|
}
|
||||||
}, [activeTab, currentOrgId, loadNotificationSettings])
|
}, [activeTab, currentOrgId, loadNotificationSettings, user?.role])
|
||||||
|
|
||||||
// * Redirect members away from Notifications tab (owners/admins only)
|
// * Redirect members away from Notifications tab (owners/admins only)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (activeTab === 'notifications' && user?.role === 'member') {
|
if (activeTab === 'notifications' && user?.role === 'member') {
|
||||||
handleTabChange('general')
|
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 no org ID, we are in personal organization context, so don't show org settings
|
||||||
if (!currentOrgId) {
|
if (!currentOrgId) {
|
||||||
|
|||||||
28
lib/utils/notifications.tsx
Normal file
28
lib/utils/notifications.tsx
Normal file
@@ -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 <AlertTriangleIcon className="w-4 h-4 shrink-0 text-amber-500" />
|
||||||
|
}
|
||||||
|
return <CheckCircleIcon className="w-4 h-4 shrink-0 text-emerald-500" />
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user