Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/dashboard/src/org-admin/MaintenanceNotificationCard.tsx
2499 views
1
/**
2
* Copyright (c) 2025 Gitpod GmbH. All rights reserved.
3
* Licensed under the GNU Affero General Public License (AGPL).
4
* See License.AGPL.txt in the project root for license information.
5
*/
6
7
import { FC, useState, useEffect } from "react";
8
import { useToast } from "../components/toasts/Toasts";
9
import { Button } from "@podkit/buttons/Button";
10
import { useMaintenanceNotification } from "../data/maintenance-mode/maintenance-notification-query";
11
import { useSetMaintenanceNotificationMutation } from "../data/maintenance-mode/maintenance-notification-mutation";
12
import Alert from "../components/Alert";
13
import { ConfigurationSettingsField } from "../repositories/detail/ConfigurationSettingsField";
14
import { Heading3 } from "@podkit/typography/Headings";
15
16
export const DEFAULT_MESSAGE =
17
"On XX-YY-ZZZZ from HH:MM to HH:MM UTC. Workspaces will be stopped and cannot be started during this time.";
18
19
export const MaintenanceNotificationCard: FC = () => {
20
const { isNotificationEnabled, notificationMessage, isLoading } = useMaintenanceNotification();
21
const setMaintenanceNotificationMutation = useSetMaintenanceNotificationMutation();
22
const [message, setMessage] = useState(notificationMessage || DEFAULT_MESSAGE);
23
const [isEditing, setIsEditing] = useState(false);
24
const toast = useToast();
25
26
// Update local state when the data from the API changes
27
useEffect(() => {
28
setMessage(notificationMessage || DEFAULT_MESSAGE);
29
}, [notificationMessage]);
30
31
const toggleNotification = async () => {
32
try {
33
const newState = !isNotificationEnabled;
34
const result = await setMaintenanceNotificationMutation.mutateAsync({
35
isEnabled: newState,
36
customMessage: message,
37
});
38
39
toast.toast({
40
message: `Maintenance notification ${result.enabled ? "enabled" : "disabled"}`,
41
type: "success",
42
});
43
44
setIsEditing(false);
45
} catch (error) {
46
console.error("Failed to toggle maintenance notification", error);
47
toast.toast({ message: "Failed to toggle maintenance notification", type: "error" });
48
}
49
};
50
51
const saveMessage = async () => {
52
try {
53
await setMaintenanceNotificationMutation.mutateAsync({
54
isEnabled: isNotificationEnabled,
55
customMessage: message,
56
});
57
58
toast.toast({
59
message: "Maintenance notification message updated",
60
type: "success",
61
});
62
63
setIsEditing(false);
64
} catch (error) {
65
console.error("Failed to update maintenance notification message", error);
66
toast.toast({ message: "Failed to update maintenance notification message", type: "error" });
67
}
68
};
69
70
return (
71
<ConfigurationSettingsField>
72
<div className="flex justify-between items-center mb-4">
73
<div>
74
<Heading3>Scheduled Maintenance Notification</Heading3>
75
<p className="text-pk-content-tertiary">
76
Display a notification banner to inform users about upcoming maintenance.
77
</p>
78
</div>
79
<Button
80
variant={isNotificationEnabled ? "secondary" : "default"}
81
onClick={toggleNotification}
82
disabled={isLoading}
83
>
84
{isLoading ? "Loading..." : isNotificationEnabled ? "Disable" : "Enable"}
85
</Button>
86
</div>
87
88
{/* Message input section */}
89
<div className="mt-4">
90
<label
91
htmlFor="maintenance-message"
92
className="block text-sm font-medium text-pk-content-secondary mb-2"
93
>
94
Notification Message
95
</label>
96
{isEditing ? (
97
<div>
98
<textarea
99
id="maintenance-message"
100
className="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"
101
rows={3}
102
value={message}
103
onChange={(e) => setMessage(e.target.value)}
104
placeholder="Enter a message to display in the notification banner"
105
/>
106
<div className="mt-2 flex justify-end space-x-2">
107
<Button
108
variant="secondary"
109
onClick={() => {
110
setMessage(notificationMessage || DEFAULT_MESSAGE);
111
setIsEditing(false);
112
}}
113
>
114
Cancel
115
</Button>
116
<Button onClick={saveMessage}>Save</Button>
117
</div>
118
</div>
119
) : (
120
<div>
121
<div className="px-3 py-2 border border-pk-border-base rounded-md bg-pk-surface-secondary text-pk-content-secondary min-h-[4rem]">
122
{message}
123
</div>
124
<div className="mt-2 flex justify-end">
125
<Button variant="secondary" onClick={() => setIsEditing(true)}>
126
Edit Message
127
</Button>
128
</div>
129
</div>
130
)}
131
</div>
132
133
<div className="mt-4">
134
<label className="block text-sm font-semibold text-pk-content-secondary mb-2">Preview</label>
135
<Alert type="warning" className="mb-0">
136
<div className="flex items-center flex-wrap gap-2">
137
<span className="font-semibold">Scheduled Maintenance:</span>
138
<span>{message}</span>
139
</div>
140
</Alert>
141
</div>
142
</ConfigurationSettingsField>
143
);
144
};
145
146