Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c1517bf
feat: add new design for sessions preview
andre-code Apr 16, 2025
ba5c9f5
fix imports after rebase
andre-code Apr 16, 2025
ee4bdfe
Apply code suggestions from Sekhar: use SVG images instead of PNGs an…
andre-code Apr 29, 2025
0593538
fix: add missed imports
andre-code Apr 16, 2025
052aa29
feat: add new design for sessions preview
andre-code Apr 16, 2025
c789361
feat: add documentation link to set up a new custom environment.
andre-code Apr 17, 2025
a54433e
apply new proposal for environment icons in environment selector
andre-code Apr 17, 2025
5e23ca7
modify order session launcher options, set copy text for buttons
andre-code Apr 25, 2025
f108290
fix after rebase
andre-code Apr 29, 2025
4c3cb3c
chore: minor UI fixes in session launcher
andre-code Apr 30, 2025
c91124f
add styles in code repository selector
andre-code May 2, 2025
090ba0a
add styles sessions in dashboard
andre-code May 2, 2025
b3e0b2a
add share link button in session show page
andre-code May 2, 2025
799585d
modify styles dashboard, add copy for share modal link, modify to di…
andre-code May 5, 2025
68fd455
fix navigate to show session page after resume a session, and remove …
andre-code May 5, 2025
188ba90
fix after rebase
andre-code May 5, 2025
f401318
fix code styles
andre-code May 5, 2025
b36744e
fix responsive in environment selector
andre-code May 6, 2025
1970e20
apply code suggestions:
andre-code May 7, 2025
ccbb720
Merge branch 'main' into andrea/other-session-improvements
andre-code May 8, 2025
7304bd7
apply suggestions to improve accessibility
andre-code May 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ import repositoriesApi, {
import useProjectPermissions from "../../utils/useProjectPermissions.hook";
import { SshRepositoryUrlWarning } from "./AddCodeRepositoryModal";
import {
getRepositoryName,
validateCodeRepository,
validateNoDuplicatesInCodeRepositories,
} from "./repositories.utils";
Expand Down Expand Up @@ -398,12 +399,8 @@ export function RepositoryItem({
setShowDetails((open) => !open);
}, []);
const canonicalUrlStr = useMemo(() => `${url.replace(/.git$/i, "")}`, [url]);
const canonicalUrl = useMemo(
() => safeNewUrl(canonicalUrlStr),
[canonicalUrlStr]
);

const title = canonicalUrl?.pathname.split("/").pop() || canonicalUrlStr;
const title = getRepositoryName(url);
// ! Product team wants this restored -- keeping the code for the next iteration
// const urlDisplay = (
// <div className={cx("d-flex", "align-items-center", "gap-2")}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
* limitations under the License.
*/

import { safeNewUrl } from "../../../../utils/helpers/safeNewUrl.utils";

/**
* Validates the URL of a code repository. Note that RenkuLab only supports HTTP(S) at the moment.
*/
Expand Down Expand Up @@ -67,3 +69,9 @@ export function detectSSHRepository(repositoryURL: string): boolean {
const gitUrlRegex = /git@(?:.)+:/;
return cleaned.match(gitUrlRegex) != null;
}

export function getRepositoryName(repositoryURL: string): string {
const canonicalUrlStr = `${repositoryURL.replace(/.git$/i, "")}`;
const canonicalUrl = safeNewUrl(canonicalUrlStr);
return canonicalUrl?.pathname.split("/").pop() || canonicalUrlStr;
}
Original file line number Diff line number Diff line change
Expand Up @@ -259,13 +259,6 @@ function ProjectSettingsForm({ project }: ProjectPageSettingsProps) {
const formId = "project-settings-form";
return (
<div>
{error && <RtkErrorAlert error={error} />}
{isSuccess && (
<SuccessAlert dismissible={false} timeout={0}>
<p className="mb-0">The project has been successfully updated.</p>
</SuccessAlert>
)}

<Form
className={cx("d-flex", "flex-column", "gap-3")}
id={formId}
Expand Down Expand Up @@ -416,7 +409,12 @@ function ProjectSettingsForm({ project }: ProjectPageSettingsProps) {
requestedPermission="write"
userPermissions={permissions}
/>

{error && <RtkErrorAlert error={error} />}
{isSuccess && (
<SuccessAlert dismissible={false} timeout={0}>
<p className="mb-0">The project has been successfully updated.</p>
</SuccessAlert>
)}
<PermissionsGuard
disabled={null}
enabled={
Expand Down
37 changes: 29 additions & 8 deletions client/src/features/dashboardV2/DashboardV2Sessions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ import { useGetProjectsByProjectIdQuery } from "../projectsV2/api/projectV2.enha
import { useGetSessionLaunchersByLauncherIdQuery as useGetProjectSessionLauncherQuery } from "../sessionsV2/api/sessionLaunchersV2.api";
import ActiveSessionButton from "../sessionsV2/components/SessionButton/ActiveSessionButton";
import {
SessionStatusV2Badge,
getSessionStatusStyles,
SessionStatusV2Description,
SessionStatusV2Label,
} from "../sessionsV2/components/SessionStatus/SessionStatus";
import { SessionList, SessionV2 } from "../sessionsV2/sessionsV2.types";

Expand Down Expand Up @@ -131,7 +132,6 @@ function DashboardSession({ session }: DashboardSessionProps) {
id: projectId,
})
: ABSOLUTE_ROUTES.v2.root;
const sessionHash = project && launcherId ? `launcher-${launcherId}` : "";
const showSessionUrl = project
? generatePath(ABSOLUTE_ROUTES.v2.projects.show.sessions.show, {
namespace: project.namespace,
Expand All @@ -140,6 +140,9 @@ function DashboardSession({ session }: DashboardSessionProps) {
})
: ABSOLUTE_ROUTES.v2.root;

const sessionStyles = getSessionStatusStyles(session);
const state = session.status.state;

return (
<div
className={cx("list-group-item-action", "list-group-item")}
Expand All @@ -154,7 +157,7 @@ function DashboardSession({ session }: DashboardSessionProps) {
"text-body",
"text-decoration-none"
)}
to={{ pathname: projectUrl, hash: sessionHash }}
to={{ pathname: projectUrl }}
>
<Row className="g-2">
<Col className="order-1" xs={12} md={9} lg={10}>
Expand All @@ -181,9 +184,6 @@ function DashboardSession({ session }: DashboardSessionProps) {
)}
</h6>
</Col>
<Col xs={12} xl="auto" className="mt-1">
<SessionStatusV2Badge session={session} />
</Col>
</Row>
</Col>
<Col className={cx("order-3", "order-md-2")} xs={12} md={3} lg={2}>
Expand All @@ -192,8 +192,29 @@ function DashboardSession({ session }: DashboardSessionProps) {
<span className="bi" />
</div>
</Col>
<Col className={cx("order-2", "order-md-3", "mt-2")} xs={12}>
<SessionStatusV2Description session={session} />
<Col
className={cx(
"order-2",
"order-md-3",
"mt-2",
"d-block",
"d-sm-flex",
"gap-5"
)}
xs={12}
>
<div className={cx("d-flex", "gap-2")}>
<img
src={sessionStyles.sessionIcon}
alt={`Session is ${state}`}
loading="lazy"
/>
<SessionStatusV2Label session={session} variant="list" />
</div>
<SessionStatusV2Description
session={session}
showInfoDetails={false}
/>
</Col>
</Row>
</Link>
Expand Down
4 changes: 2 additions & 2 deletions client/src/features/sessionsV2/SessionList/SessionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,15 @@ export default function SessionCard({ project, session }: SessionCardProps) {
xs="12"
xl="auto"
>
<SessionStatusV2Label session={session} />
<SessionStatusV2Label session={session} variant="card" />
</Col>
<Col
xs="auto"
className={cx("mt-0", "ms-0", "ms-xl-3", "d-flex")}
>
<SessionStatusV2Description
session={session}
showInfoDetails={false}
showInfoDetails={true}
/>
</Col>
</Row>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export default function SessionItem({
</Col>
<Col xs={12} xl="auto">
{session ? (
<SessionStatusV2Label session={session} />
<SessionStatusV2Label session={session} variant="card" />
) : (
<SessionBadge className={cx("border-dark-subtle", "bg-light")}>
<CircleFill
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ function SessionLauncherDropdownActions({
onClick={toggleShareLink}
>
<Link45deg className={cx("bi", "me-1")} />
Session launcher share link
Share session launch link
</DropdownItem>
<DropdownItem divider />
<DropdownItem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,10 @@ export function SessionLauncherDisplay({
/>
<SessionStartLinkModal
isOpen={isShareLinkOpen}
launcher={launcher}
launcherId={launcher.id}
toggle={toggleShareLink}
project={project}
namespace={project.namespace}
slug={project.slug}
/>
</>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
Cloud,
ExclamationTriangle,
FileEarmarkText,
Link45deg,
PauseCircle,
Trash,
} from "react-bootstrap-icons";
Expand Down Expand Up @@ -58,6 +59,7 @@ import { useGetProjectsByProjectIdSessionLaunchersQuery as useGetProjectSessionL
import { useGetSessionsQuery } from "../api/sessionsV2.api";
import { getSessionFavicon } from "../session.utils";
import { SessionV2 } from "../sessionsV2.types";
import SessionStartLinkModal from "../SessionView/SessionStartLinkModal";
import SessionIframe from "./SessionIframe";
import SessionPaused from "./SessionPaused";
import SessionUnavailable from "./SessionUnavailable";
Expand Down Expand Up @@ -214,6 +216,11 @@ export default function ShowSessionPage() {
<LogsBtn toggle={toggleModalLogs} />
<PauseSessionBtn openPauseSession={openPauseSession} />
<DeleteSessionBtn openDeleteSession={openDeleteSession} />
<ShareSessionLinkButton
session={thisSession}
namespace={namespace}
slug={slug}
/>
</div>
<div
className={cx(
Expand Down Expand Up @@ -505,3 +512,57 @@ function SessionDetails({
</>
);
}

function ShareSessionLinkButton({
session,
namespace,
slug,
}: {
session?: SessionV2;
namespace?: string;
slug?: string;
}) {
const launcherId = session?.launcher_id;
const ref = useRef<HTMLButtonElement>(null);
const buttonId = "share-session-button";
const tooltip = "Share session launch link";

const [isShareLinkOpen, setIsShareLinkOpen] = useState(false);
const toggleShareLink = useCallback(() => {
setIsShareLinkOpen((open) => !open);
}, []);

if (launcherId == null || namespace == null || slug == null) return null;

return (
<div>
<Button
className={cx(
"bg-transparent",
"border-0",
"no-focus",
"p-0",
"shadow-none",
"text-dark"
)}
data-cy={buttonId}
id={buttonId}
innerRef={ref}
onClick={toggleShareLink}
>
<Link45deg className="bi" />
<span className="visually-hidden">{tooltip}</span>
</Button>
<UncontrolledTooltip placement="bottom" target={ref}>
{tooltip}
</UncontrolledTooltip>
<SessionStartLinkModal
isOpen={isShareLinkOpen}
launcherId={launcherId}
toggle={toggleShareLink}
slug={slug}
namespace={namespace}
/>
</div>
);
}
48 changes: 42 additions & 6 deletions client/src/features/sessionsV2/SessionStyles.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ import linePaused from "../../styles/assets/linePaused.svg";
import lineStopped from "../../styles/assets/lineStopped.svg";
import lineBlock from "../../styles/assets/lineBlock.svg";

import playingIcon from "../../styles/assets/playing.svg";
import failedIcon from "../../styles/assets/failed.svg";
import pausedIcon from "../../styles/assets/paused.svg";
import stoppedIcon from "../../styles/assets/stopped.svg";
import blockIcon from "../../styles/assets/block.svg";

export const SESSION_STATES = {
RUNNING: "running",
STARTING: "starting",
Expand All @@ -32,45 +38,75 @@ export const SESSION_STATES = {

export const SESSION_STYLES = {
WARNING: {
textColor: "text-warning-emphasis",
textColorCard: "text-warning-emphasis",
textColorList: "text-warning-emphasis",
bgColor: "warning",
bgOpacity: 10,
borderColor: "border-warning",
sessionLine: linePlaying,
sessionIcon: playingIcon,
},
SUCCESS: {
textColor: "text-success-emphasis",
textColorCard: "text-success-emphasis",
textColorList: "text-primary",
bgColor: "success",
bgOpacity: 10,
borderColor: "border-success",
sessionLine: linePlaying,
sessionIcon: playingIcon,
},
HIBERNATED: {
textColor: "text-dark-emphasis",
textColorCard: "text-dark-emphasis",
textColorList: "text-dark-emphasis",
bgColor: "light",
bgOpacity: 100,
borderColor: "border-dark-subtle",
sessionLine: linePaused,
sessionIcon: pausedIcon,
},
FAILED: {
textColor: "text-danger-emphasis",
textColorCard: "text-danger-emphasis",
textColorList: "text-danger-emphasis",
bgColor: "danger",
bgOpacity: 10,
borderColor: "border-danger",
sessionLine: lineFailed,
sessionIcon: failedIcon,
},
STOPPING: {
textColor: "text-warning-emphasis",
textColorCard: "text-warning-emphasis",
textColorList: "text-warning-emphasis",
bgColor: "warning",
bgOpacity: 10,
borderColor: "border-warning",
sessionLine: lineStopped,
sessionIcon: stoppedIcon,
},
DEFAULT: {
textColor: "text-warning",
textColorCard: "text-warning-emphasis",
textColorList: "text-warning-emphasis",
bgColor: "warning",
bgOpacity: 10,
borderColor: "border-warning",
sessionLine: lineBlock,
sessionIcon: blockIcon,
},
} as const;

export const SESSION_TITLE = {
[SESSION_STATES.RUNNING]: "My running session",
[SESSION_STATES.STARTING]: "Launching my session",
[SESSION_STATES.STOPPING]: "Shutting down my session...",
[SESSION_STATES.HIBERNATED]: "My paused session",
[SESSION_STATES.FAILED]: "Error in my session",
default: "Unknown status",
};

export const SESSION_TITLE_DASHBOARD = {
[SESSION_STATES.RUNNING]: "Running session",
[SESSION_STATES.STARTING]: "Launching session",
[SESSION_STATES.STOPPING]: "Shutting down session...",
[SESSION_STATES.HIBERNATED]: "Paused session",
[SESSION_STATES.FAILED]: "Error in session",
default: "Unknown status",
};
Loading
Loading