-
Notifications
You must be signed in to change notification settings - Fork 7
Add more status options #96
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
95255a7
5cdc68f
61e76e8
dab31ee
cccba6f
de1aca5
3402159
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,22 @@ | ||
| import React, { useState } from 'react' | ||
| import React, { useEffect, useMemo, useState } from 'react' | ||
|
|
||
| import { useStatus } from '../hooks/useStatus' | ||
| import { useBookings } from '../hooks/useBookings.jsx'; | ||
| 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 ( | ||
| <CCard onClick={onClick} className='border-[0.2px] border-gray-100 rounded-none shadow-md hover:shadow-xl col-span-1 h-[236px]'> | ||
| <CCardTitle className='font-bold text-on-white text-[18px]'> | ||
|
|
@@ -24,14 +31,20 @@ const StatusCard = (props) => { | |
|
|
||
| <div className='flex flex-col gap-0 text-[14px]'> | ||
| <strong>Service status:</strong> | ||
| {isOnline ? ( | ||
| <div className='text-center text-[#204303] bg-[#B9DC9C] border-[0.5px] border-[#204303] rounded-[100px] w-[88px] h-[25px]'> | ||
| <p className='font-bold text-[14px]'>Online</p> | ||
| {(props.device_status && !status_options.includes(props.device_status)) ? ( | ||
| <div className='text-center text-[#ae4000] bg-[#ffb66d] border-[0.5px] border-[#ae4000] rounded-[100px] w-[88px] h-[25px]'> | ||
| <p className='font-bold text-[14px]'>{capitalizeFirstLetter(props.device_status)}</p> | ||
| </div> | ||
| ) : ( | ||
| <div className='text-center text-[#7E0707] bg-[#F8CECE] border-[0.5px] border-[#7E0707] rounded-[100px] w-[88px] h-[25px]'> | ||
| <p className='font-bold text-[14px]'>Offline</p> | ||
| </div> | ||
| isOnline ? ( | ||
| <div className='text-center text-[#204303] bg-[#B9DC9C] border-[0.5px] border-[#204303] rounded-[100px] w-[88px] h-[25px]'> | ||
| <p className='font-bold text-[14px]'>Online</p> | ||
| </div> | ||
| ) : ( | ||
| <div className='text-center text-[#7E0707] bg-[#F8CECE] border-[0.5px] border-[#7E0707] rounded-[100px] w-[88px] h-[25px]'> | ||
| <p className='font-bold text-[14px]'>Offline</p> | ||
| </div> | ||
| ) | ||
| )} | ||
| </div> | ||
| </CCardContent> | ||
|
|
@@ -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('|'), | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what is d in this case? Maybe device or dev instead of d. It's clearer |
||
| [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}`) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why fetch for each. why not fetch everything and the filter based on the id?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fetching separately because the backend currently has no endpoint for fetching all the devices. Would probably be smart to add one tho
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's pretty easy to add since we are fetching the list once and then filtering on the backend. We can just add a route to return all or update the controller to return all if no device id is given, otherwise return info for the given device
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup |
||
| .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; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what is this catch block trying to do?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be redundant now. Lefover from testing and problems with the useEffect running infinitely |
||
| 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); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also maybe dev or device instead of d? dev for example immediately communicates that the variable is a device. e.g using var instead of using v |
||
|
|
||
| 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) => { | |
| <div className='pt-[24px] flex flex-col gap-6 mb-6 justify-start'> | ||
| <h2 className='text-on-white'>Reservations</h2> | ||
| <p> | ||
| 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. | ||
| </p> | ||
| <CButton className='w-32' onClick={() => setBookingModalOpen(true)}>View Reservations</CButton> | ||
| </div> | ||
|
|
||
| <h2 className='text-on-white'>Devices</h2> | ||
| <div className='pb-[60px] grid grid-cols-1 min-[450px]:grid-cols-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 min-[2600px]:grid-cols-4 w-full gap-[24px]'> | ||
| {devicesWithStatus.map((qc, index) => ( | ||
| <StatusCard key={qc.device_id || index} {...qc} onClick={() => handleCardClick(qc)} /> | ||
| <StatusCard | ||
| key={qc.device_id || index} | ||
| {...{ | ||
| ...qc, | ||
| device_status: device_status_list?.[qc.device_id.toLowerCase()] || "unknown" | ||
| }} | ||
| onClick={() => handleCardClick(qc)} | ||
| /> | ||
| ))} | ||
|
|
||
|
|
||
| </div> | ||
| {bookingModalOpen && ( | ||
| <BookingModal bookingData={bookingData} name={"Reservations"} isModalOpen={bookingModalOpen} setIsModalOpen={setBookingModalOpen} /> | ||
|
|
@@ -115,7 +180,7 @@ export const ServiceStatus = (props) => { | |
| {modalOpen && ( | ||
| <StatusModal {...modalProps} isModalOpen={modalOpen} setIsModalOpen={setModalOpen} /> | ||
| )} | ||
|
|
||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 ( | ||
| <> | ||
| <div className="flex flex-col justify-start text-[14px]"> | ||
| <p className="pb-2"><strong>Qubits:</strong> {deviceData.qubits}</p> | ||
| <p className="pb-2"><strong>Basis gates:</strong> {deviceData.basis}</p> | ||
| <p className="pb-2"><strong>Topology:</strong> {deviceData.topology}</p> | ||
| </div> | ||
| const status_options = ["offline", "online", "healthy", "unknown"] | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In what cases would the device be healthy and not online? Or what do these different status represent?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe :["offline", "online", "maintenance", "booked", "unknown"] instead? |
||
|
|
||
| <div className='flex flex-col gap-0 text-[14px] col-span-1 pb-8'> | ||
| <strong>Service status:</strong> | ||
| {devicesWithStatus.find(d => d.device_id === deviceData.device_id)?.health ? ( | ||
| <div className='text-center text-[#204303] bg-[#B9DC9C] border-[0.5px] border-[#204303] rounded-[100px] w-[88px] h-[25px]'> | ||
| <p className='font-bold text-[14px]'>Online</p> | ||
| </div> | ||
| ) : ( | ||
| <div className='text-center text-[#7E0707] bg-[#F8CECE] border-[0.5px] border-[#7E0707] rounded-[100px] w-[88px] h-[25px]'> | ||
| <p className='font-bold text-[14px]'>Offline</p> | ||
| </div> | ||
| )} | ||
| </div> | ||
| </> | ||
| ) | ||
| return ( | ||
| <> | ||
| <div className="flex flex-col justify-start text-[14px]"> | ||
| <p className="pb-2"><strong>Qubits:</strong> {deviceData.qubits}</p> | ||
| <p className="pb-2"><strong>Basis gates:</strong> {deviceData.basis}</p> | ||
| <p className="pb-2"><strong>Topology:</strong> {deviceData.topology}</p> | ||
| </div> | ||
|
|
||
| {(devicesWithStatus.device_status && !status_options.includes(devicesWithStatus.device_status)) ? ( | ||
| <div className='text-center text-[#ae4000] bg-[#ffb66d] border-[0.5px] border-[#ae4000] rounded-[100px] w-[88px] h-[25px]'> | ||
| <p className='font-bold text-[14px]'>{capitalizeFirstLetter(devicesWithStatus.device_status)}</p> | ||
| </div> | ||
| ) : ( | ||
| devicesWithStatus.health ? ( | ||
| <div className='text-center text-[#204303] bg-[#B9DC9C] border-[0.5px] border-[#204303] rounded-[100px] w-[88px] h-[25px]'> | ||
| <p className='font-bold text-[14px]'>Online</p> | ||
| </div> | ||
| ) : ( | ||
| <div className='text-center text-[#7E0707] bg-[#F8CECE] border-[0.5px] border-[#7E0707] rounded-[100px] w-[88px] h-[25px]'> | ||
| <p className='font-bold text-[14px]'>Offline</p> | ||
| </div> | ||
| ) | ||
| )} | ||
| </> | ||
| ) | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export const API_BASE_URL = "https://fiqci-backend.2.rahtiapp.fi"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as the comment above