Updated with intial commit

- MaintenanceAlert.tsx now handles upcoming and active maintenance alerts.
- OverallStatus.tsx updated to include collapsible sections for ongoing and upcoming maintenance alerts.
pull/142/head
QuackieMackie 2025-09-18 01:12:46 +01:00
parent 5769a52f0c
commit ed04b8fead
2 changed files with 95 additions and 25 deletions

View File

@ -5,15 +5,21 @@ import { MaintenanceConfig, MonitorTarget } from '@/types/config'
export default function MaintenanceAlert({
maintenance,
style,
upcoming = false,
}: {
maintenance: Omit<MaintenanceConfig, 'monitors'> & { monitors?: MonitorTarget[] }
style?: React.CSSProperties
upcoming?: boolean
}) {
return (
<Alert
icon={<IconAlertTriangle />}
title={maintenance.title || 'Scheduled Maintenance'}
color={maintenance.color || 'yellow'}
title={
upcoming
? `Upcoming Maintenance: ${maintenance.title || 'Scheduled Maintenance'}`
: maintenance.title || 'Scheduled Maintenance'
}
color={upcoming ? 'gray' : maintenance.color || '#f29030'}
withCloseButton={false}
style={{ position: 'relative', margin: '16px auto 0 auto', ...style }}
>
@ -29,13 +35,27 @@ export default function MaintenanceAlert({
borderRadius: 6,
}}
>
<b>From:</b> {new Date(maintenance.start).toLocaleString()}
<br />
<b>To:</b>{' '}
{maintenance.end ? new Date(maintenance.end).toLocaleString() : 'Until further notice'}
{upcoming ? (
<>
<b>Scheduled for:</b> {new Date(maintenance.start).toLocaleString()}
<br />
<b>Expected end:</b>{' '}
{maintenance.end ? new Date(maintenance.end).toLocaleString() : 'Until further notice'}
</>
) : (
<>
<b>From:</b> {new Date(maintenance.start).toLocaleString()}
<br />
<b>To:</b>{' '}
{maintenance.end ? new Date(maintenance.end).toLocaleString() : 'Until further notice'}
</>
)}
</div>
<Text>{maintenance.body}</Text>
{/* Maintenance description */}
<Text style={{ whiteSpace: 'pre-line' }}>{maintenance.body}</Text>
{/* Affected components */}
{maintenance.monitors && maintenance.monitors.length > 0 && (
<>
<Text mt="xs">

View File

@ -1,6 +1,6 @@
import { MaintenanceConfig, MonitorTarget } from '@/types/config'
import { Center, Container, Title } from '@mantine/core'
import { IconCircleCheck, IconAlertCircle } from '@tabler/icons-react'
import { Center, Container, Title, Collapse, Button, Box } from '@mantine/core'
import { IconCircleCheck, IconAlertCircle, IconPlus, IconMinus } from '@tabler/icons-react'
import { useEffect, useState } from 'react'
import MaintenanceAlert from './MaintenanceAlert'
import { pageConfig } from '@/uptime.config'
@ -58,15 +58,26 @@ export default function OverallStatus({
})
const now = new Date()
let filteredMaintenances: (Omit<MaintenanceConfig, 'monitors'> & { monitors?: MonitorTarget[] })[] =
maintenances
.filter((m) => now >= new Date(m.start) && (!m.end || now <= new Date(m.end)))
.map((maintenance) => ({
...maintenance,
monitors: maintenance.monitors?.map(
(monitorId) => monitors.find((mon) => monitorId === mon.id)!
),
}))
const activeMaintenances = maintenances
.filter((m) => now >= new Date(m.start) && (!m.end || now <= new Date(m.end)))
.map((maintenance) => ({
...maintenance,
monitors: maintenance.monitors?.map(
(monitorId) => monitors.find((mon) => monitorId === mon.id)!
),
}))
const upcomingMaintenances = maintenances
.filter((m) => now < new Date(m.start))
.map((maintenance) => ({
...maintenance,
monitors: maintenance.monitors?.map(
(monitorId) => monitors.find((mon) => monitorId === mon.id)!
),
}))
const [activeOpen, setActiveOpen] = useState(true)
const [upcomingOpen, setUpcomingOpen] = useState(false)
return (
<Container size="md" mt="xl">
@ -81,13 +92,52 @@ export default function OverallStatus({
} sec ago)`}
</Title>
{filteredMaintenances.map((maintenance, idx) => (
<MaintenanceAlert
key={idx}
maintenance={maintenance}
style={{ maxWidth: groupedMonitor ? '897px' : '865px' }}
/>
))}
{/* Active Maintenance */}
{activeMaintenances.length > 0 && (
<>
<Box style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', gap: 8, marginTop: '1rem' }}>
<Title order={3} style={{ color: '#f29030' }}>
Ongoing Maintenance
</Title>
<Button variant="subtle" size="xs" onClick={() => setActiveOpen((o) => !o)}>
{activeOpen ? <IconMinus size={16} /> : <IconPlus size={16} />}
</Button>
</Box>
<Collapse in={activeOpen}>
{activeMaintenances.map((maintenance, idx) => (
<MaintenanceAlert
key={`active-${idx}`}
maintenance={maintenance}
style={{ maxWidth: groupedMonitor ? '897px' : '865px' }}
/>
))}
</Collapse>
</>
)}
{/* Upcoming Maintenance */}
{upcomingMaintenances.length > 0 && (
<>
<Box style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', gap: 8, marginTop: '1rem' }}>
<Title order={3} style={{ color: 'gray' }}>
Upcoming Maintenance
</Title>
<Button variant="subtle" size="xs" onClick={() => setUpcomingOpen((o) => !o)}>
{upcomingOpen ? <IconMinus size={16} /> : <IconPlus size={16} />}
</Button>
</Box>
<Collapse in={upcomingOpen}>
{upcomingMaintenances.map((maintenance, idx) => (
<MaintenanceAlert
key={`upcoming-${idx}`}
maintenance={maintenance}
style={{ maxWidth: groupedMonitor ? '897px' : '865px' }}
upcoming
/>
))}
</Collapse>
</>
)}
</Container>
)
}