diff --git a/workspaces/frontend/src/app/actions/WorkspaceKindsActions.tsx b/workspaces/frontend/src/app/actions/WorkspaceKindsActions.tsx index 5bc1abc..49bb9ec 100644 --- a/workspaces/frontend/src/app/actions/WorkspaceKindsActions.tsx +++ b/workspaces/frontend/src/app/actions/WorkspaceKindsActions.tsx @@ -19,3 +19,34 @@ export function buildKindLogoDictionary(workspaceKinds: WorkspaceKind[] | []): K } return kindLogoDict; } + +type WorkspaceRedirectStatus = Record< + string, + { to: string; message: string; level: string } | null +>; + +/** + * Builds a dictionary of workspace kinds to redirect statuses. + * @param {WorkspaceKind[]} workspaceKinds - The list of workspace kinds. + * @returns {WorkspaceRedirectStatus} A dictionary with kind names as keys and redirect status objects as values. + */ +export function buildWorkspaceRedirectStatus( + workspaceKinds: WorkspaceKind[] | [], +): WorkspaceRedirectStatus { + const workspaceRedirectStatus: WorkspaceRedirectStatus = {}; + for (const workspaceKind of workspaceKinds) { + // Loop through the `values` array inside `imageConfig` + const redirect = workspaceKind.podTemplate.options.imageConfig.values.find( + (value) => value.redirect, + )?.redirect; + // If redirect exists, extract the necessary properties + workspaceRedirectStatus[workspaceKind.name] = redirect + ? { + to: redirect.to, + message: redirect.message.text, + level: redirect.message.level, + } + : null; + } + return workspaceRedirectStatus; +} diff --git a/workspaces/frontend/src/app/pages/Workspaces/Workspaces.tsx b/workspaces/frontend/src/app/pages/Workspaces/Workspaces.tsx index b3ca85e..d19dd6a 100644 --- a/workspaces/frontend/src/app/pages/Workspaces/Workspaces.tsx +++ b/workspaces/frontend/src/app/pages/Workspaces/Workspaces.tsx @@ -25,13 +25,22 @@ import { ActionsColumn, IActions, } from '@patternfly/react-table'; +import { + InfoCircleIcon, + ExclamationTriangleIcon, + TimesCircleIcon, + QuestionCircleIcon, + CodeIcon, +} from '@patternfly/react-icons'; import { useState } from 'react'; -import { CodeIcon } from '@patternfly/react-icons'; import { Workspace, WorkspacesColumnNames, WorkspaceState } from '~/shared/types'; import { WorkspaceDetails } from '~/app/pages/Workspaces/Details/WorkspaceDetails'; import { ExpandedWorkspaceRow } from '~/app/pages/Workspaces/ExpandedWorkspaceRow'; import DeleteModal from '~/shared/components/DeleteModal'; -import { buildKindLogoDictionary } from '~/app/actions/WorkspaceKindsActions'; +import { + buildKindLogoDictionary, + buildWorkspaceRedirectStatus, +} from '~/app/actions/WorkspaceKindsActions'; import useWorkspaceKinds from '~/app/hooks/useWorkspaceKinds'; import { WorkspaceConnectAction } from '~/app/pages/Workspaces/WorkspaceConnectAction'; import { WorkspaceStartActionModal } from '~/app/pages/Workspaces/workspaceActions/WorkspaceStartActionModal'; @@ -107,6 +116,10 @@ export const Workspaces: React.FunctionComponent = () => { state: WorkspaceState.Paused, stateMessage: 'It is paused.', }, + redirectStatus: { + level: 'Info', + text: 'This is informational', // Tooltip text + }, }, { name: 'My Other Jupyter Notebook', @@ -162,6 +175,10 @@ export const Workspaces: React.FunctionComponent = () => { state: WorkspaceState.Running, stateMessage: 'It is running.', }, + redirectStatus: { + level: 'Danger', + text: 'This is dangerous', + }, }, ]; @@ -169,8 +186,15 @@ export const Workspaces: React.FunctionComponent = () => { let kindLogoDict: Record = {}; kindLogoDict = buildKindLogoDictionary(workspaceKinds); + let workspaceRedirectStatus: Record< + string, + { to: string; message: string; level: string } | null + > = {}; // Initialize the redirect status dictionary + workspaceRedirectStatus = buildWorkspaceRedirectStatus(workspaceKinds); // Populate the dictionary + // Table columns const columnNames: WorkspacesColumnNames = { + redirectStatus: 'Redirect Status', name: 'Name', kind: 'Kind', image: 'Image', @@ -266,18 +290,20 @@ export const Workspaces: React.FunctionComponent = () => { const [activeSortDirection, setActiveSortDirection] = React.useState<'asc' | 'desc' | null>(null); const getSortableRowValues = (workspace: Workspace): (string | number)[] => { - const { name, kind, image, podConfig, state, homeVol, cpu, ram, lastActivity } = { - name: workspace.name, - kind: workspace.kind, - image: workspace.options.imageConfig, - podConfig: workspace.options.podConfig, - state: WorkspaceState[workspace.status.state], - homeVol: workspace.podTemplate.volumes.home, - cpu: workspace.cpu, - ram: workspace.ram, - lastActivity: workspace.status.activity.lastActivity, - }; - return [name, kind, image, podConfig, state, homeVol, cpu, ram, lastActivity]; + const { redirectStatus, name, kind, image, podConfig, state, homeVol, cpu, ram, lastActivity } = + { + redirectStatus: '', + name: workspace.name, + kind: workspace.kind, + image: workspace.options.imageConfig, + podConfig: workspace.options.podConfig, + state: WorkspaceState[workspace.status.state], + homeVol: workspace.podTemplate.volumes.home, + cpu: workspace.cpu, + ram: workspace.ram, + lastActivity: workspace.status.activity.lastActivity, + }; + return [redirectStatus, name, kind, image, podConfig, state, homeVol, cpu, ram, lastActivity]; }; let sortedWorkspaces = workspaces; @@ -436,6 +462,43 @@ export const Workspaces: React.FunctionComponent = () => { | 'yellow' )[] = ['green', 'orange', 'yellow', 'blue', 'red', 'purple']; + // Redirect Status Icons + + const getRedirectStatusIcon = (level: string | undefined, message: string) => { + switch (level) { + case 'Info': + return ( + + + ); + case 'Warning': + return ( + + + ); + case 'Danger': + return ( + + + ); + case undefined: + return ( + + + ); + default: + return ( + + + ); + } + }; + // Pagination const [page, setPage] = React.useState(1); @@ -494,7 +557,10 @@ export const Workspaces: React.FunctionComponent = () => { {Object.values(columnNames).map((columnName, index) => ( - + {columnName} ))} @@ -517,6 +583,15 @@ export const Workspaces: React.FunctionComponent = () => { setWorkspaceExpanded(workspace, !isWorkspaceExpanded(workspace)), }} /> + + {workspaceRedirectStatus[workspace.kind] + ? getRedirectStatusIcon( + workspaceRedirectStatus[workspace.kind]?.level, + workspaceRedirectStatus[workspace.kind]?.message || + 'No API response available', + ) + : getRedirectStatusIcon(undefined, 'No API response available')} + {workspace.name} {kindLogoDict[workspace.kind] ? ( diff --git a/workspaces/frontend/src/shared/types.ts b/workspaces/frontend/src/shared/types.ts index af2f812..8826c52 100644 --- a/workspaces/frontend/src/shared/types.ts +++ b/workspaces/frontend/src/shared/types.ts @@ -38,7 +38,7 @@ export interface WorkspaceKind { pythonVersion: string; }; hidden: true; - redirect: { + redirect?: { to: string; message: { text: string; @@ -126,6 +126,10 @@ export interface Workspace { podConfig: string; }; status: WorkspaceStatus; + redirectStatus: { + level: 'Info' | 'Warning' | 'Danger'; + text: string; + }; } export type WorkspacesColumnNames = { @@ -138,4 +142,5 @@ export type WorkspacesColumnNames = { cpu: string; ram: string; lastActivity: string; + redirectStatus: string; };