diff --git a/src/components/ServiceStatus.jsx b/src/components/ServiceStatus.jsx index d21af3be8158..b692f355d295 100644 --- a/src/components/ServiceStatus.jsx +++ b/src/components/ServiceStatus.jsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React, { useEffect, useMemo, useState } from 'react' import { useStatus } from '../hooks/useStatus' import { useBookings } from '../hooks/useBookings.jsx'; @@ -6,10 +6,17 @@ import { mdiInformation, mdiClose, mdiAlert } from '@mdi/js'; import { CCard, CCardTitle, CCardContent, CIcon, CButton } from '@cscfi/csc-ui-react'; import { StatusModal } from './StatusModal/StatusModal'; import { BookingModal } from './bookingCalendar.jsx'; +import { set } from 'date-fns'; +import { capitalizeFirstLetter } from '../utils/textUtils.js'; + +import { API_BASE_URL } from '../config/api'; const StatusCard = (props) => { const isOnline = props.health; const { onClick, ...rest } = props + + const status_options = ["offline", "online", "healthy", "unknown"] + return ( @@ -24,14 +31,20 @@ const StatusCard = (props) => {
Service status: - {isOnline ? ( -
-

Online

+ {(props.device_status && !status_options.includes(props.device_status)) ? ( +
+

{capitalizeFirstLetter(props.device_status)}

) : ( -
-

Offline

-
+ isOnline ? ( +
+

Online

+
+ ) : ( +
+

Offline

+
+ ) )}
@@ -40,25 +53,70 @@ const StatusCard = (props) => { } export const ServiceStatus = (props) => { - const { status: statusList } = useStatus("https://fiqci-backend.2.rahtiapp.fi/devices/healthcheck"); - const { bookingData: bookingData } = useBookings("https://fiqci-backend.2.rahtiapp.fi/bookings") - const qcs = props["quantum-computers"] || []; + const { status: statusList } = useStatus(`${API_BASE_URL}/devices/healthcheck`); + const { bookingData: bookingData } = useBookings(`${API_BASE_URL}/bookings`) + const qcs = Array.isArray(props["quantum-computers"]) ? props["quantum-computers"] : []; + const qcsKey = useMemo( + () => qcs.map(d => d?.device_id?.toLowerCase()).filter(Boolean).sort().join('|'), + [qcs] + ); + + const [device_status_list, setDeviceStatusList] = useState([]); const devicesWithStatus = (qcs.length === 0 || !Array.isArray(statusList)) ? qcs : qcs.map(device => { - const deviceStatus = statusList.find(({ name }) => name === device.device_id); - return { - ...device, - health: deviceStatus?.health ?? false, - }; + const deviceStatus = statusList.find(({ name }) => name === device.device_id); + return { + ...device, + health: deviceStatus?.health ?? false, + }; + }); + + useEffect(() => { + let cancelled = false; + + if (!qcs.length) { + setDeviceStatusList({}); + return; + } + + // reset for current device set + setDeviceStatusList({}); + + qcs.forEach((device) => { + const id = device.device_id.toLowerCase(); + fetch(`${API_BASE_URL}/device/${id}`) + .then((resp) => resp.json()) + .then((result) => result?.data?.status) + .then((status) => { + if (cancelled) return; + setDeviceStatusList((prev) => + prev[id] === status ? prev : { ...prev, [id]: status } + ); + }) + .catch(() => { + if (cancelled) return; + setDeviceStatusList((prev) => + prev[id] === 'unknown' ? prev : { ...prev, [id]: 'unknown' } + ); }); - + }); + + return () => { + cancelled = true; + }; +}, [qcsKey]); + + const [bookingModalOpen, setBookingModalOpen] = useState(false) const [modalOpen, setModalOpen] = useState(false); const [modalProps, setModalProps] = useState({}); const handleCardClick = (qc) => { - setModalProps({ ...qc, devicesWithStatus }); + + const add_device_status = devicesWithStatus.map(d => d.device_id.toLowerCase() === qc.device_id.toLowerCase() ? { ...d, device_status: device_status_list?.[qc.device_id.toLowerCase()] || "unknown" } : d); + + setModalProps({ ...qc, devicesWithStatus: add_device_status.find(d => d.device_id.toLowerCase() === qc.device_id.toLowerCase()) }); setModalOpen(true); }; // Determine alert color based on props.alert.type @@ -94,19 +152,26 @@ export const ServiceStatus = (props) => {

Reservations

- VTT devices can at times be reserved. At these times the queue will be paused. + VTT devices can at times be reserved. At these times the queue will be paused. Reservations can be viewed from this calendar. Note that making reservations through FiQCI is not currently possible.

setBookingModalOpen(true)}>View Reservations
- +

Devices

{devicesWithStatus.map((qc, index) => ( - handleCardClick(qc)} /> + handleCardClick(qc)} + /> ))} - - + +
{bookingModalOpen && ( @@ -115,7 +180,7 @@ export const ServiceStatus = (props) => { {modalOpen && ( )} - +
); } diff --git a/src/components/StatusModal/DeviceStatus.jsx b/src/components/StatusModal/DeviceStatus.jsx index d14eefc742fe..45b9c3a182cf 100644 --- a/src/components/StatusModal/DeviceStatus.jsx +++ b/src/components/StatusModal/DeviceStatus.jsx @@ -1,30 +1,36 @@ import React from 'react'; +import { capitalizeFirstLetter } from '../../utils/textUtils.js'; export const DeviceStatus = (props) => { - const { deviceData, devicesWithStatus } = props; + const { deviceData, devicesWithStatus } = props; - return ( - <> -
-

Qubits: {deviceData.qubits}

-

Basis gates: {deviceData.basis}

-

Topology: {deviceData.topology}

-
+ const status_options = ["offline", "online", "healthy", "unknown"] -
- Service status: - {devicesWithStatus.find(d => d.device_id === deviceData.device_id)?.health ? ( -
-

Online

-
- ) : ( -
-

Offline

-
- )} -
- - ) + return ( + <> +
+

Qubits: {deviceData.qubits}

+

Basis gates: {deviceData.basis}

+

Topology: {deviceData.topology}

+
+ + {(devicesWithStatus.device_status && !status_options.includes(devicesWithStatus.device_status)) ? ( +
+

{capitalizeFirstLetter(devicesWithStatus.device_status)}

+
+ ) : ( + devicesWithStatus.health ? ( +
+

Online

+
+ ) : ( +
+

Offline

+
+ ) + )} + + ) } \ No newline at end of file diff --git a/src/components/StatusModal/StatusModalConent.jsx b/src/components/StatusModal/StatusModalConent.jsx index 7683e2f16a6c..d8cd514b429c 100644 --- a/src/components/StatusModal/StatusModalConent.jsx +++ b/src/components/StatusModal/StatusModalConent.jsx @@ -14,10 +14,12 @@ import { import { object } from 'framer-motion/client'; import { getDeviceMetricsConfig } from '../../config/deviceMetrics'; +import { API_BASE_URL } from '../../config/api'; + export const ModalContent = (props) => { - const { calibrationData: calibrationDataAll, calibrationError } = useCalibration(`https://fiqci-backend.2.rahtiapp.fi/device/${props.device_id.toLowerCase()}/calibration`) - const { deviceInfo: deviceInfoData, infoError } = useDeviceInfo(`https://fiqci-backend.2.rahtiapp.fi/device/${props.device_id.toLowerCase()}`) + const { calibrationData: calibrationDataAll, calibrationError } = useCalibration(`${API_BASE_URL}/device/${props.device_id.toLowerCase()}/calibration`) + const { deviceInfo: deviceInfoData, infoError } = useDeviceInfo(`${API_BASE_URL}/device/${props.device_id.toLowerCase()}`) const [activeTab, setActiveTab] = useState('overview'); diff --git a/src/config/api.js b/src/config/api.js new file mode 100644 index 000000000000..eb4263c5bc0c --- /dev/null +++ b/src/config/api.js @@ -0,0 +1 @@ +export const API_BASE_URL = "https://fiqci-backend.2.rahtiapp.fi"; \ No newline at end of file