Path: blob/main/components/dashboard/src/org-admin/MaintenanceNotificationCard.tsx
2499 views
/**1* Copyright (c) 2025 Gitpod GmbH. All rights reserved.2* Licensed under the GNU Affero General Public License (AGPL).3* See License.AGPL.txt in the project root for license information.4*/56import { FC, useState, useEffect } from "react";7import { useToast } from "../components/toasts/Toasts";8import { Button } from "@podkit/buttons/Button";9import { useMaintenanceNotification } from "../data/maintenance-mode/maintenance-notification-query";10import { useSetMaintenanceNotificationMutation } from "../data/maintenance-mode/maintenance-notification-mutation";11import Alert from "../components/Alert";12import { ConfigurationSettingsField } from "../repositories/detail/ConfigurationSettingsField";13import { Heading3 } from "@podkit/typography/Headings";1415export const DEFAULT_MESSAGE =16"On XX-YY-ZZZZ from HH:MM to HH:MM UTC. Workspaces will be stopped and cannot be started during this time.";1718export const MaintenanceNotificationCard: FC = () => {19const { isNotificationEnabled, notificationMessage, isLoading } = useMaintenanceNotification();20const setMaintenanceNotificationMutation = useSetMaintenanceNotificationMutation();21const [message, setMessage] = useState(notificationMessage || DEFAULT_MESSAGE);22const [isEditing, setIsEditing] = useState(false);23const toast = useToast();2425// Update local state when the data from the API changes26useEffect(() => {27setMessage(notificationMessage || DEFAULT_MESSAGE);28}, [notificationMessage]);2930const toggleNotification = async () => {31try {32const newState = !isNotificationEnabled;33const result = await setMaintenanceNotificationMutation.mutateAsync({34isEnabled: newState,35customMessage: message,36});3738toast.toast({39message: `Maintenance notification ${result.enabled ? "enabled" : "disabled"}`,40type: "success",41});4243setIsEditing(false);44} catch (error) {45console.error("Failed to toggle maintenance notification", error);46toast.toast({ message: "Failed to toggle maintenance notification", type: "error" });47}48};4950const saveMessage = async () => {51try {52await setMaintenanceNotificationMutation.mutateAsync({53isEnabled: isNotificationEnabled,54customMessage: message,55});5657toast.toast({58message: "Maintenance notification message updated",59type: "success",60});6162setIsEditing(false);63} catch (error) {64console.error("Failed to update maintenance notification message", error);65toast.toast({ message: "Failed to update maintenance notification message", type: "error" });66}67};6869return (70<ConfigurationSettingsField>71<div className="flex justify-between items-center mb-4">72<div>73<Heading3>Scheduled Maintenance Notification</Heading3>74<p className="text-pk-content-tertiary">75Display a notification banner to inform users about upcoming maintenance.76</p>77</div>78<Button79variant={isNotificationEnabled ? "secondary" : "default"}80onClick={toggleNotification}81disabled={isLoading}82>83{isLoading ? "Loading..." : isNotificationEnabled ? "Disable" : "Enable"}84</Button>85</div>8687{/* Message input section */}88<div className="mt-4">89<label90htmlFor="maintenance-message"91className="block text-sm font-medium text-pk-content-secondary mb-2"92>93Notification Message94</label>95{isEditing ? (96<div>97<textarea98id="maintenance-message"99className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white"100rows={3}101value={message}102onChange={(e) => setMessage(e.target.value)}103placeholder="Enter a message to display in the notification banner"104/>105<div className="mt-2 flex justify-end space-x-2">106<Button107variant="secondary"108onClick={() => {109setMessage(notificationMessage || DEFAULT_MESSAGE);110setIsEditing(false);111}}112>113Cancel114</Button>115<Button onClick={saveMessage}>Save</Button>116</div>117</div>118) : (119<div>120<div className="px-3 py-2 border border-pk-border-base rounded-md bg-pk-surface-secondary text-pk-content-secondary min-h-[4rem]">121{message}122</div>123<div className="mt-2 flex justify-end">124<Button variant="secondary" onClick={() => setIsEditing(true)}>125Edit Message126</Button>127</div>128</div>129)}130</div>131132<div className="mt-4">133<label className="block text-sm font-semibold text-pk-content-secondary mb-2">Preview</label>134<Alert type="warning" className="mb-0">135<div className="flex items-center flex-wrap gap-2">136<span className="font-semibold">Scheduled Maintenance:</span>137<span>{message}</span>138</div>139</Alert>140</div>141</ConfigurationSettingsField>142);143};144145146