diff --git a/client/src/components/Logs.tsx b/client/src/components/Logs.tsx index 31095c7733..0e75078aeb 100644 --- a/client/src/components/Logs.tsx +++ b/client/src/components/Logs.tsx @@ -375,6 +375,16 @@ const EnvironmentLogsPresent = ({ annotations ) as NotebookAnnotations; + const modalTitle = !cleanAnnotations.renkuVersion && ( +
+ + {cleanAnnotations["namespace"]}/{cleanAnnotations["projectName"]} [ + {cleanAnnotations["branch"]}@ + {cleanAnnotations["commit-sha"].substring(0, 8)}] + +
+ ); + return (
Logs
-
- - {cleanAnnotations["namespace"]}/{cleanAnnotations["projectName"]} [ - {cleanAnnotations["branch"]}@ - {cleanAnnotations["commit-sha"].substring(0, 8)}] - -
+ {modalTitle}
diff --git a/client/src/features/sessionsV2/ShowSessionPage.tsx b/client/src/features/sessionsV2/ShowSessionPage.tsx index 8b0db17673..dc64336f0c 100644 --- a/client/src/features/sessionsV2/ShowSessionPage.tsx +++ b/client/src/features/sessionsV2/ShowSessionPage.tsx @@ -18,33 +18,59 @@ import cx from "classnames"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { ArrowLeft, Journals, PauseCircle, Trash } from "react-bootstrap-icons"; +import { + ArrowLeft, + Box, + Briefcase, + Clock, + Cloud, + ExclamationTriangle, + FileEarmarkText, + PauseCircle, + Trash, +} from "react-bootstrap-icons"; import { Link, generatePath, useNavigate, useParams, } from "react-router-dom-v5-compat"; -import { Button, UncontrolledTooltip } from "reactstrap"; +import { + Button, + Modal, + ModalBody, + ModalHeader, + UncontrolledTooltip, +} from "reactstrap"; +import RenkuFrogIcon from "../../components/icons/RenkuIcon"; import { User } from "../../model/renkuModels.types"; -import { SESSION_TABS } from "../../notebooks/Notebooks.present"; +import { ABSOLUTE_ROUTES } from "../../routing/routes.constants"; import useLegacySelector from "../../utils/customHooks/useLegacySelector.hook"; import useWindowSize from "../../utils/helpers/UseWindowsSize"; -import ResourcesSessionModal from "../session/components/ResourcesSessionModal"; +import { resetFavicon, setFavicon } from "../display"; import SessionHibernated from "../session/components/SessionHibernated"; import SessionJupyter from "../session/components/SessionJupyter"; import SessionUnavailable from "../session/components/SessionUnavailable"; import StartSessionProgressBar from "../session/components/StartSessionProgressBar"; import { useGetSessionsQuery } from "../session/sessions.api"; import PauseOrDeleteSessionModal from "./PauseOrDeleteSessionModal"; +import { getSessionFavicon } from "./session.utils"; -import RenkuFrogIcon from "../../components/icons/RenkuIcon"; -import { ABSOLUTE_ROUTES } from "../../routing/routes.constants"; +import { skipToken } from "@reduxjs/toolkit/query"; +import { Loader } from "../../components/Loader"; +import { EnvironmentLogs } from "../../components/Logs"; +import { TimeCaption } from "../../components/TimeCaption"; +import { CommandCopy } from "../../components/commandCopy/CommandCopy"; +import { NotebooksHelper } from "../../notebooks"; +import { NotebookAnnotations } from "../../notebooks/components/session.types"; import useAppDispatch from "../../utils/customHooks/useAppDispatch.hook"; -import { resetFavicon, setFavicon } from "../display"; -import { getSessionFavicon } from "./session.utils"; +import { displaySlice } from "../display"; +import { useGetProjectsByNamespaceAndSlugQuery } from "../projectsV2/api/projectV2.enhanced-api"; +import { SessionRowResourceRequests } from "../session/components/SessionsList"; import styles from "../session/components/ShowSession.module.scss"; +import { Session } from "../session/sessions.types"; +import { useGetProjectSessionLaunchersQuery } from "./sessionsV2.api"; export default function ShowSessionPage() { const dispatch = useAppDispatch(); @@ -84,22 +110,11 @@ export default function ShowSessionPage() { const [isTheSessionReady, setIsTheSessionReady] = useState(false); - const [showModalResourcesData, setShowModalResourcesData] = useState(false); - const toggleModalResources = useCallback( - () => setShowModalResourcesData((show) => !show), - [] - ); - const [activeResourcesTab, setActiveResourcesTab] = useState( - SESSION_TABS.commands - ); - const toggleToResourcesLogs = useCallback(() => { - setActiveResourcesTab(SESSION_TABS.logs); - toggleModalResources(); - }, [toggleModalResources]); - const toggleResources = useCallback(() => { - setActiveResourcesTab(SESSION_TABS.commands); - toggleModalResources(); - }, [toggleModalResources]); + const toggleModalLogs = useCallback(() => { + dispatch( + displaySlice.actions.toggleSessionLogsModal({ targetServer: sessionName }) + ); + }, [dispatch, sessionName]); const [showModalPauseOrDeleteSession, setShowModalPauseOrDeleteSession] = useState(false); @@ -146,15 +161,6 @@ export default function ShowSessionPage() { }, [backUrl, navigate, thisSession?.status.state]); // Modals - const resourcesModal = ( - - ); const pauseOrDeleteSessionModal = ( ); + const logs = thisSession && ( + + ); const content = !isLoading && thisSession == null ? ( @@ -177,7 +189,7 @@ export default function ShowSessionPage() { )} ); @@ -221,11 +233,12 @@ export default function ShowSessionPage() { "d-flex", "flex-grow-0", "gap-3", - "px-3" + "px-3", + "text-truncate" )} > {backButton} - +
@@ -236,11 +249,17 @@ export default function ShowSessionPage() { "d-flex", "flex-grow-1", "justify-content-between", - "py-2" + "text-truncate" )} > -
{sessionName}
-
+
+ +
+
@@ -248,16 +267,16 @@ export default function ShowSessionPage() {
{content}
{/* modals */} - {resourcesModal} + {logs} {pauseOrDeleteSessionModal} ); } -interface ResourcesProps { - toggleModalResources: () => void; +interface LogsBtnProps { + toggle: () => void; } -function ResourcesBtn({ toggleModalResources }: ResourcesProps) { +function LogsBtn({ toggle }: LogsBtnProps) { const ref = useRef(null); return ( @@ -274,13 +293,12 @@ function ResourcesBtn({ toggleModalResources }: ResourcesProps) { data-cy="resources-button" id="resources-button" innerRef={ref} - onClick={toggleModalResources} + onClick={toggle} > - - Resources + - Resources + Get logs ); @@ -362,3 +380,151 @@ function DeleteSessionBtn({ openDeleteSession }: DeleteSessionBtnProps) { ); } + +function SessionDetails({ + session, + namespace, + slug, +}: { + session?: Session; + namespace?: string; + slug?: string; +}) { + const [isOpen, setIsOpen] = useState(false); + const { projectId, launcherId } = useMemo(() => { + if (session == null) { + return { projectId: undefined, launcherId: undefined }; + } + const annotations = NotebooksHelper.cleanAnnotations( + session.annotations + ) as NotebookAnnotations; + return { + projectId: annotations.projectId, + launcherId: annotations.launcherId, + }; + }, [session]); + + const { + data: launchers, + isLoading: isLoadingLaunchers, + error: launchersError, + } = useGetProjectSessionLaunchersQuery({ projectId: projectId ?? "" }); + const { data: project, isLoading: isLoadingProject } = + useGetProjectsByNamespaceAndSlugQuery( + namespace && slug ? { namespace, slug } : skipToken + ); + + const launcher = useMemo( + () => launchers?.find(({ id }) => id === launcherId), + [launcherId, launchers] + ); + const toggle = useCallback(() => { + setIsOpen((open) => !open); + }, []); + + const projectUrl = + project && + generatePath(ABSOLUTE_ROUTES.v2.projects.show.root, { + namespace: project?.namespace, + slug: project?.slug, + }); + + if (isLoadingLaunchers || isLoadingProject) { + return ( +
+

+ Checking session details... +

+
+ ); + } + if (launchersError || !launcher) + return ( +
+

+ Session not accessible +

+
+ ); + const detailsModal = project && session && projectUrl && ( + + Session details {launcher.name} + +
+
+

+ + Project:{" "} + + {project.name} + +

+
+
+

+ + + + +

+
+
+
+ + Session resources requested: +
+ +
+
+
+ + Container image:{" "} +
+ +
+
+
+
+ ); + return ( + <> + + {detailsModal} + + ); +} diff --git a/client/src/features/sessionsV2/components/SessionButton/ActiveSessionButton.tsx b/client/src/features/sessionsV2/components/SessionButton/ActiveSessionButton.tsx index b7c8bce4d1..2024e15610 100644 --- a/client/src/features/sessionsV2/components/SessionButton/ActiveSessionButton.tsx +++ b/client/src/features/sessionsV2/components/SessionButton/ActiveSessionButton.tsx @@ -372,7 +372,7 @@ export default function ActiveSessionButton({ const logsAction = status !== "hibernated" && ( - + Get logs );