Skip to content

Commit 32e129f

Browse files
authored
Merge pull request #2296 from airqo-platform/ft-maintenance-banner
2 parents 9e15412 + 5b332d0 commit 32e129f

File tree

5 files changed

+93
-4
lines changed

5 files changed

+93
-4
lines changed

platform/src/common/components/Layout/index.jsx

+7
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ import AuthenticatedSideBar from '@/components/SideBar/AuthenticatedSidebar';
99
import TopBar from '../TopBar';
1010
import SideBarDrawer from '../SideBar/SideBarDrawer';
1111
import Modal from '../Modal/dataDownload';
12+
import MaintenanceBanner from '../MaintenanceBanner';
1213

1314
import { setOpenModal } from '@/lib/store/services/downloadModal';
1415

1516
import useUserPreferences from '@/core/hooks/useUserPreferences';
1617
import useUserChecklists from '@/core/hooks/useUserChecklists';
1718
import useInactivityLogout from '@/core/hooks/useInactivityLogout';
19+
import useMaintenanceStatus from '@/core/hooks/useMaintenanceStatus';
1820

1921
const Layout = ({
2022
pageTitle = 'AirQo Analytics',
@@ -36,6 +38,8 @@ const Layout = ({
3638
const isCollapsed = useSelector((state) => state.sidebar.isCollapsed);
3739
const isOpen = useSelector((state) => state.modal.openModal);
3840

41+
const { maintenance } = useMaintenanceStatus();
42+
3943
// Handler to close the modal
4044
const handleCloseModal = () => {
4145
dispatch(setOpenModal(false));
@@ -66,6 +70,9 @@ const Layout = ({
6670
: 'max-w-[1200px] mx-auto space-y-8 px-4 py-8 sm:px-6 lg:px-8'
6771
} overflow-hidden`}
6872
>
73+
{/* Maintenance Banner */}
74+
<MaintenanceBanner maintenance={maintenance} />
75+
6976
{/* TopBar */}
7077
{noTopNav && (
7178
<TopBar
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import React from 'react';
2+
import { IoWarningOutline } from 'react-icons/io5';
3+
import dayjs from 'dayjs';
4+
5+
const MaintenanceBanner = ({ maintenance }) => {
6+
if (!maintenance?.isActive) return null;
7+
8+
return (
9+
<div className="bg-yellow-50 border-b border-yellow-200 mb-2">
10+
<div className="py-3 px-3 sm:px-6 lg:px-8">
11+
<div className="flex items-center justify-center">
12+
<div className="flex items-center">
13+
<span className="flex">
14+
<IoWarningOutline
15+
className="h-6 w-6 text-yellow-600"
16+
aria-hidden="true"
17+
/>
18+
</span>
19+
<p className="ml-3 font-medium text-yellow-700">
20+
{maintenance.message}
21+
<span className="ml-2 text-yellow-600">
22+
(Estimated downtime:{' '}
23+
{dayjs(maintenance.startDate).format('D MMM, YYYY hh:mm A')} -{' '}
24+
{dayjs(maintenance.endDate).format('D MMM, YYYY hh:mm A')})
25+
</span>
26+
</p>
27+
</div>
28+
</div>
29+
</div>
30+
</div>
31+
);
32+
};
33+
34+
export default MaintenanceBanner;

platform/src/core/apis/Account.js

+11
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
USER_CHECKLISTS_URL,
1313
FORGOT_PWD_URL,
1414
RESET_PWD_URL,
15+
MAINTENANCE_STATUS_URL,
1516
} from '../urls/authentication';
1617
import axios from 'axios';
1718
import createAxiosInstance from './axiosConfig';
@@ -221,3 +222,13 @@ export const updateGroupDetailsApi = async (groupID, data) => {
221222
.put(`${GROUPS_URL}/${groupID}`, data)
222223
.then((response) => response.data);
223224
};
225+
226+
export const getMaintenanceStatus = async () => {
227+
try {
228+
const response = await createAxiosInstance().get(MAINTENANCE_STATUS_URL);
229+
return response.data;
230+
} catch (error) {
231+
console.error('Error fetching maintenance status:', error);
232+
throw error;
233+
}
234+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { useState, useEffect } from 'react';
2+
import { getMaintenanceStatus } from '../apis/Account';
3+
4+
const POLLING_INTERVAL = 5 * 60 * 1000; // 5 minutes
5+
6+
const useMaintenanceStatus = () => {
7+
const [maintenance, setMaintenance] = useState(null);
8+
const [loading, setLoading] = useState(true);
9+
const [error, setError] = useState(null);
10+
11+
const fetchMaintenanceStatus = async () => {
12+
try {
13+
const response = await getMaintenanceStatus();
14+
if (response.success && response.maintenance?.length > 0) {
15+
setMaintenance(response.maintenance[0]);
16+
} else {
17+
setMaintenance(null);
18+
}
19+
setError(null);
20+
} catch (err) {
21+
setError(err);
22+
console.error('Error fetching maintenance status:', err);
23+
} finally {
24+
setLoading(false);
25+
}
26+
};
27+
28+
useEffect(() => {
29+
fetchMaintenanceStatus();
30+
const interval = setInterval(fetchMaintenanceStatus, POLLING_INTERVAL);
31+
32+
return () => clearInterval(interval);
33+
}, []);
34+
35+
return { maintenance, loading, error };
36+
};
37+
38+
export default useMaintenanceStatus;

platform/src/core/urls/authentication.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
import {
2-
NEXT_PUBLIC_API_BASE_URL,
3-
NEXT_PUBLIC_API_TOKEN,
4-
} from '../../lib/envConstants';
1+
import { NEXT_PUBLIC_API_BASE_URL } from '../../lib/envConstants';
52
import { stripTrailingSlash } from '../utils/strings';
63

74
const BASE_AUTH_URL = stripTrailingSlash(NEXT_PUBLIC_API_BASE_URL);
@@ -41,3 +38,5 @@ export const GENERATE_TOKEN_URI = `${AUTH_URL}/tokens`;
4138
export const ACTIVATE_USER_CLIENT = `${CLIENT_URI}/activate`;
4239

4340
export const ACTIVATION_REQUEST_URI = `${CLIENT_URI}/activate-request`;
41+
42+
export const MAINTENANCE_STATUS_URL = `${AUTH_URL}/maintenances/analytics`;

0 commit comments

Comments
 (0)