Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions MAINTENANCE_WINDOWS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Maintenance Windows Feature

This document describes the maintenance windows feature added to the WATcloud status page.

## Overview

The maintenance windows feature displays scheduled maintenance, ongoing maintenance, and completed maintenance windows on the status page. This helps users understand when planned outages or service interruptions might occur.

## Components

### MaintenanceWindows Component (`src/maintenance.tsx`)

The main component that displays maintenance window information with:

- **Visual Status Indicators**: Different emoji icons for each status type
- **Status-based Styling**: Color-coded backgrounds and text
- **Responsive Design**: Works on desktop and mobile devices
- **Dark Mode Support**: Adapts to the theme selection
- **Time Calculations**: Shows relative time ("starts in", "ends in")
- **Sorting**: Prioritizes ongoing maintenance, then upcoming, then completed

### Types and Constants (`src/constants.ts`)

Added the following types and enums:

- `MaintenanceStatus`: Enum for upcoming, ongoing, completed
- `MaintenanceWindow`: Interface defining the data structure
- `MAINTENANCE_SYMBOLS`: Emoji symbols for each status

### Utility Functions (`src/utils.ts`)

Added `timeUntil()` function to calculate time until a future date.

## Data Structure

Each maintenance window contains:

```typescript
interface MaintenanceWindow {
id: string; // Unique identifier
title: string; // Display title
description: string; // Detailed description
startTime: Date; // When maintenance starts
endTime: Date; // When maintenance ends
status: MaintenanceStatus; // Current status (calculated)
affectedServices?: string[]; // Optional list of affected services
detailsUrl?: string; // Optional link to more details
}
```

## Features

1. **Automatic Status Detection**: Status is calculated based on current time vs. start/end times
2. **Smart Sorting**: Ongoing maintenance appears first, then upcoming, then completed
3. **Responsive Design**: Adapts to different screen sizes
4. **Theme Support**: Works with light, dark, and auto themes
5. **Empty State**: Shows appropriate message when no maintenance is scheduled
6. **External Links**: Optional "More Details" links to announcements
7. **Time Display**: Shows both absolute times and relative times
8. **Service Information**: Lists affected services when available

## Usage

The component is automatically included in the main status page. To modify maintenance windows:

1. **For Demo/Testing**: Update the `SAMPLE_MAINTENANCE_WINDOWS` array in `src/maintenance.tsx`
2. **For Production**: Replace the sample data with an API call or external data source

## Integration Points

- Integrated into `App.tsx` between the Options and Healthchecks.io sections
- Uses existing utility functions and styling patterns
- Follows the same design language as other status sections

## Future Enhancements

The current implementation uses static sample data. Future enhancements could include:

- Integration with a calendar service (Google Calendar, Outlook, etc.)
- API endpoint for dynamic maintenance window management
- Admin interface for adding/editing maintenance windows
- Email notifications for upcoming maintenance
- Integration with existing monitoring tools
16 changes: 10 additions & 6 deletions src/App.css
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
#root {
margin: 0 auto;
padding: 2rem;
text-align: center;
text-align: left;
min-height: 100vh;
}

.main-logo > svg {
height: 5rem;
height: 6rem;
mask-image: linear-gradient(
60deg,
black 25%,
Expand All @@ -14,6 +15,7 @@
);
mask-size: 400%;
mask-position: 0%;
transition: mask-position 1s ease;
}
.main-logo > svg:hover {
mask-position: 100%;
Expand All @@ -23,8 +25,10 @@
}

.main-logo {
@apply text-inherit;
&:hover {
@apply text-inherit;
}
color: inherit;
display: inline-block;
}

.main-logo:hover {
color: inherit;
}
43 changes: 27 additions & 16 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { HealthchecksioStatus } from './healthchecksio'
import { useState } from 'react'
import { SentryStatus } from './sentry'
import { OptionGroup } from './option-group'
import { MaintenanceWindows } from './maintenance'

function updateQueryParams(key: string, val: string, queryParams: URLSearchParams) {
queryParams.set(key, val);
Expand Down Expand Up @@ -74,42 +75,52 @@ function App() {
</div>
<div className="flex flex-wrap justify-center gap-x-16 gap-y-8 mb-8">
<div>
<h2 className="text-2xl">Quick Links</h2>
<ul>
<h2 className="text-xl">Quick Links</h2>
<ul className="text-sm">
<li><a href="https://cloud.watonomous.ca/docs/compute-cluster/support-resources" target="_blank">Support Resources</a></li>
<li><a href="https://groups.google.com/a/watonomous.ca/g/watcloud-compute-cluster-announcements" target="_blank">Announcements</a></li>
<li><a href="https://cloud.watonomous.ca" target="_blank">Documentation</a></li>
<li><a href={`./legacy.html${theme == "auto" ? "" : `#theme=${theme}`}`}>Legacy Status Page</a></li>
</ul>
</div>
<div>
<h2 className="text-2xl">Options</h2>
<h2 className="text-xl">Options</h2>
<div>
<span className="text-sm text-gray-500 flex items-center justify-center mb-1">Theme:</span>
<span className="text-xs text-gray-500 flex items-center justify-center mb-1">Theme:</span>
<OptionGroup
options={THEMES}
selected={theme}
onChange={setTheme}
className="mb-4"
optionClassName="text-gray-900 bg-white dark:bg-gray-800 dark:border-gray-700 dark:text-white dark:hover:text-white dark:hover:bg-gray-700 dark:focus:ring-blue-500 dark:focus:text-white"
className="mb-2"
optionClassName="text-gray-900 bg-white dark:bg-gray-800 dark:border-gray-700 dark:text-white dark:hover:text-white dark:hover:bg-gray-700 dark:focus:ring-blue-500 dark:focus:text-white text-xs px-2 py-1"
selectedClassName="bg-blue-500 text-white"
/>
</div>
<span className="text-sm text-gray-500 flex items-center justify-center">
<span className="text-xs text-gray-500 flex items-center justify-center">
<input type="checkbox" id="show-internal" checked={showInternal} onChange={() => setShowInternal(!showInternal)} />
<label htmlFor="show-internal" className="ml-1">Show internal checks</label>
</span>
</div>
</div>
<div className="mb-8">
<h2 className="text-2xl">Healthchecks.io</h2>
<h3 className="text-lg text-gray-500">Monitoring data from healthchecks.io</h3>
<HealthchecksioStatus {...healthchecksioParams} />
</div>
<div className="mb-8">
<h2 className="text-2xl">Sentry</h2>
<h3 className="text-lg text-gray-500">Monitoring data from watonomous.sentry.io</h3>
<SentryStatus {...sentryParams} />

<div className="space-y-8">
<div className="text-center">
<h2 className="text-2xl font-bold mb-2 text-gray-900 dark:text-white">Scheduled Maintenance</h2>
<h3 className="text-base text-gray-600 dark:text-gray-400 mb-4">Planned maintenance windows and outages</h3>
<MaintenanceWindows />
</div>

<div className="mb-8">
<h2 className="text-2xl">Healthchecks.io</h2>
<h3 className="text-lg text-gray-500">Monitoring data from healthchecks.io</h3>
<HealthchecksioStatus {...healthchecksioParams} />
</div>

<div className="mb-8">
<h2 className="text-2xl">Sentry</h2>
<h3 className="text-lg text-gray-500">Monitoring data from watonomous.sentry.io</h3>
<SentryStatus {...sentryParams} />
</div>
</div>
</>
)
Expand Down
25 changes: 24 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,27 @@ export const STATUS_SYMBOLS = {
[Status.Good]: '🟒',
[Status.Neutral]: '🟑',
[Status.Bad]: 'πŸ”΄',
} as const;
} as const;

export enum MaintenanceStatus {
Upcoming = 'upcoming',
Ongoing = 'ongoing',
Completed = 'completed',
}

export const MAINTENANCE_SYMBOLS = {
[MaintenanceStatus.Upcoming]: 'πŸ””',
[MaintenanceStatus.Ongoing]: 'πŸ”§',
[MaintenanceStatus.Completed]: 'βœ…',
} as const;

export interface MaintenanceWindow {
id: string;
title: string;
description: string;
startTime: Date;
endTime: Date;
status: MaintenanceStatus;
affectedServices?: string[];
detailsUrl?: string;
}
Loading
Loading