diff --git a/site/src/modules/apps/AppStatusIcon.tsx b/site/src/modules/apps/AppStatusStateIcon.tsx similarity index 64% rename from site/src/modules/apps/AppStatusIcon.tsx rename to site/src/modules/apps/AppStatusStateIcon.tsx index 3de4ef4194..829a828823 100644 --- a/site/src/modules/apps/AppStatusIcon.tsx +++ b/site/src/modules/apps/AppStatusStateIcon.tsx @@ -1,6 +1,7 @@ -import type { WorkspaceAppStatus } from "api/typesGenerated"; +import type { WorkspaceAppStatusState } from "api/typesGenerated"; import { Spinner } from "components/Spinner/Spinner"; import { + BanIcon, CircleAlertIcon, CircleCheckIcon, HourglassIcon, @@ -9,20 +10,22 @@ import { import type { FC } from "react"; import { cn } from "utils/cn"; -type AppStatusIconProps = { - status: WorkspaceAppStatus; +type AppStatusStateIconProps = { + state: WorkspaceAppStatusState; latest: boolean; + disabled?: boolean; className?: string; }; -export const AppStatusIcon: FC = ({ - status, +export const AppStatusStateIcon: FC = ({ + state, + disabled, latest, className: customClassName, }) => { const className = cn(["size-4 shrink-0", customClassName]); - switch (status.state) { + switch (state) { case "complete": return ( @@ -32,10 +35,12 @@ export const AppStatusIcon: FC = ({ ); case "working": - return latest ? ( + return disabled ? ( + + ) : latest ? ( ) : ( - + ); default: return ( diff --git a/site/src/modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus.tsx b/site/src/modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus.tsx index aba002b2cd..0b999f5440 100644 --- a/site/src/modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus.tsx +++ b/site/src/modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus.tsx @@ -5,7 +5,7 @@ import { TooltipProvider, TooltipTrigger, } from "components/Tooltip/Tooltip"; -import { AppStatusIcon } from "modules/apps/AppStatusIcon"; +import { AppStatusStateIcon } from "modules/apps/AppStatusStateIcon"; import { cn } from "utils/cn"; type WorkspaceAppStatusProps = { @@ -31,9 +31,10 @@ export const WorkspaceAppStatus = ({
- { const { workspace: workspaceName, username } = useParams() as { @@ -115,10 +120,10 @@ const TaskPage = () => {

- Building your task + Building the workspace

- Your task is being built and will be ready soon + Your task will run as soon as the workspace is ready
@@ -146,21 +151,21 @@ const TaskPage = () => { } else if (terminatedStatuses.includes(task.workspace.latest_build.status)) { content = ( -
- {task.workspace.latest_app_status && ( -
- -
- )} -
-
-

- Task build terminated -

- - So apps and previous statuses are not available - -
+
+
+

+ Workspace is not running +

+ + Apps and previous statuses are not available + +
@@ -180,53 +185,7 @@ const TaskPage = () => {
); } else { - const statuses = task.workspace.latest_build.resources - .flatMap((r) => r.agents) - .flatMap((a) => a?.apps) - .flatMap((a) => a?.statuses) - .filter((s) => !!s) - .sort( - (a, b) => - new Date(b.created_at).getTime() - new Date(a.created_at).getTime(), - ); - - content = ( -
- - - -
- ); + content = ; } return ( @@ -235,50 +194,151 @@ const TaskPage = () => { {pageTitle(task.prompt)} -
-
-
- - - - - - Back to tasks - - - -
-

{task.prompt}

- - Created by{" "} - {task.workspace.owner_name ?? task.workspace.owner_name}{" "} - {timeFrom(new Date(task.workspace.created_at))} - -
-
- - -
- +
+ {content} -
+
); }; export default TaskPage; +type TaskSidebarProps = { + task: Task; +}; + +const TaskSidebar: FC = ({ task }) => { + let statuses = task.workspace.latest_build.resources + .flatMap((r) => r.agents) + .flatMap((a) => a?.apps) + .flatMap((a) => a?.statuses) + .filter((s) => !!s) + .sort( + (a, b) => + new Date(b.created_at).getTime() - new Date(a.created_at).getTime(), + ); + + // This happens when the workspace is not running so it has no resources to + // get the statuses so we can fallback to the latest status received from the + // workspace. + if (statuses.length === 0 && task.workspace.latest_app_status) { + statuses = [task.workspace.latest_app_status]; + } + + return ( + + ); +}; + type TaskAppsProps = { task: Task; }; @@ -340,7 +400,7 @@ const TaskApps: FC = ({ task }) => { @@ -465,6 +525,43 @@ const TaskAppIFrame: FC = ({ task, app, active }) => { ); }; +type TaskStatusLinkProps = { + uri: string; +}; + +const TaskStatusLink: FC = ({ uri }) => { + let icon = ; + let label = truncateURI(uri); + + if (uri.startsWith("https://github.com")) { + const issueNumber = uri.split("/").pop(); + const [org, repo] = uri.split("/").slice(3, 5); + const prefix = `${org}/${repo}`; + + if (uri.includes("pull/")) { + icon = ; + label = issueNumber + ? `${prefix}#${issueNumber}` + : `${prefix} Pull Request`; + } else if (uri.includes("issues/")) { + icon = ; + label = issueNumber ? `${prefix}#${issueNumber}` : `${prefix} Issue`; + } else { + icon = ; + label = `${org}/${repo}`; + } + } + + return ( + + ); +}; + export const data = { fetchTask: async (workspaceOwnerUsername: string, workspaceName: string) => { const workspace = await API.getWorkspaceByOwnerAndName( diff --git a/site/src/pages/TasksPage/TasksPage.tsx b/site/src/pages/TasksPage/TasksPage.tsx index 31d5e284b2..fcaee2aa59 100644 --- a/site/src/pages/TasksPage/TasksPage.tsx +++ b/site/src/pages/TasksPage/TasksPage.tsx @@ -363,7 +363,10 @@ const TasksTable: FC = ({ templates, filter }) => { /> - + { - if (uri.startsWith("file://")) { - const path = uri.slice(7); - // Slightly shorter truncation for this context if needed - if (path.length > 35) { - const start = path.slice(0, 15); - const end = path.slice(-15); - return `${start}...${end}`; - } - return path; - } - - try { - const url = new URL(uri); - const fullUrl = url.toString(); - // Slightly shorter truncation - if (fullUrl.length > 40) { - const start = fullUrl.slice(0, 20); - const end = fullUrl.slice(-20); - return `${start}...${end}`; - } - return fullUrl; - } catch { - // Slightly shorter truncation - if (uri.length > 35) { - const start = uri.slice(0, 15); - const end = uri.slice(-15); - return `${start}...${end}`; - } - return uri; - } -}; - -// --- Component Implementation --- +import { truncateURI } from "utils/uri"; interface AppStatusesProps { workspace: Workspace; @@ -109,7 +75,7 @@ export const AppStatuses: FC = ({ >
- + {latestStatus.message} @@ -135,7 +101,7 @@ export const AppStatuses: FC = ({ - {formatURI(latestStatus.uri)} + {truncateURI(latestStatus.uri)} @@ -147,7 +113,7 @@ export const AppStatuses: FC = ({ ))} @@ -189,8 +155,8 @@ export const AppStatuses: FC = ({ >
- diff --git a/site/src/utils/uri.ts b/site/src/utils/uri.ts new file mode 100644 index 0000000000..696f428785 --- /dev/null +++ b/site/src/utils/uri.ts @@ -0,0 +1,32 @@ +export const truncateURI = (uri: string) => { + if (uri.startsWith("file://")) { + const path = uri.slice(7); + // Slightly shorter truncation for this context if needed + if (path.length > 35) { + const start = path.slice(0, 15); + const end = path.slice(-15); + return `${start}...${end}`; + } + return path; + } + + try { + const url = new URL(uri); + const fullUrl = url.toString(); + // Slightly shorter truncation + if (fullUrl.length > 30) { + const start = fullUrl.slice(0, 15); + const end = fullUrl.slice(-15); + return `${start}...${end}`; + } + return fullUrl; + } catch { + // Slightly shorter truncation + if (uri.length > 20) { + const start = uri.slice(0, 10); + const end = uri.slice(-10); + return `${start}...${end}`; + } + return uri; + } +};