From 448070bcc25252edf53acb8d67efe95be72e469a Mon Sep 17 00:00:00 2001 From: Presley Date: Wed, 18 May 2022 02:10:53 +0000 Subject: [PATCH 01/13] Set up table --- site/src/components/Resources/Resources.tsx | 60 +++++++++++++++++++ .../Workspace/Workspace.stories.tsx | 3 +- site/src/components/Workspace/Workspace.tsx | 7 ++- site/src/testHelpers/entities.ts | 2 +- 4 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 site/src/components/Resources/Resources.tsx diff --git a/site/src/components/Resources/Resources.tsx b/site/src/components/Resources/Resources.tsx new file mode 100644 index 0000000000000..f56ad46f7eb96 --- /dev/null +++ b/site/src/components/Resources/Resources.tsx @@ -0,0 +1,60 @@ +import Table from "@material-ui/core/Table" +import TableBody from "@material-ui/core/TableBody" +import TableCell from "@material-ui/core/TableCell" +import TableHead from "@material-ui/core/TableHead" +import TableRow from "@material-ui/core/TableRow" +import React from "react" +import { WorkspaceResource } from "../../api/typesGenerated" +import { TableHeaderRow } from "../TableHeaders/TableHeaders" +import { WorkspaceSection } from "../WorkspaceSection/WorkspaceSection" + +const Language = { + resources: "Resources", + resourceLabel: "Resource", + agentsLabel: "Agents", + agentLabel: "Agent", + statusLabel: "Status", +} + +interface ResourcesProps { + resources?: WorkspaceResource[] +} + +export const Resources: React.FC = ({ resources }) => { + return + + + + {Language.resourceLabel} + {Language.agentsLabel} + + + + {resources?.map((resource) => ( + + {resource.name} + +
+ + + {Language.agentLabel} + {Language.statusLabel} + + + + {resource.agents?.map((agent) => ( + + {agent.name} + {agent.status} + + ))} + +
+ + + ))} + + + +
+} diff --git a/site/src/components/Workspace/Workspace.stories.tsx b/site/src/components/Workspace/Workspace.stories.tsx index f6a8ca00cd097..237c8f77c965c 100644 --- a/site/src/components/Workspace/Workspace.stories.tsx +++ b/site/src/components/Workspace/Workspace.stories.tsx @@ -1,7 +1,7 @@ import { action } from "@storybook/addon-actions" import { Story } from "@storybook/react" import React from "react" -import { MockOrganization, MockOutdatedWorkspace, MockTemplate, MockWorkspace } from "../../testHelpers/renderHelpers" +import { MockOrganization, MockOutdatedWorkspace, MockTemplate, MockWorkspace, MockWorkspaceResource } from "../../testHelpers/renderHelpers" import { Workspace, WorkspaceProps } from "./Workspace" export default { @@ -21,6 +21,7 @@ Started.args = { handleStop: action("stop"), handleRetry: action("retry"), workspaceStatus: "started", + resources: [MockWorkspaceResource, MockWorkspaceResource] } export const Starting = Template.bind({}) diff --git a/site/src/components/Workspace/Workspace.tsx b/site/src/components/Workspace/Workspace.tsx index 7e2a9f2ab0c2b..a52c7653d368a 100644 --- a/site/src/components/Workspace/Workspace.tsx +++ b/site/src/components/Workspace/Workspace.tsx @@ -3,6 +3,7 @@ import Typography from "@material-ui/core/Typography" import React from "react" import * as TypesGen from "../../api/typesGenerated" import { WorkspaceStatus } from "../../pages/WorkspacePage/WorkspacePage" +import { Resources } from "../Resources/Resources" import { WorkspaceSchedule } from "../WorkspaceSchedule/WorkspaceSchedule" import { WorkspaceSection } from "../WorkspaceSection/WorkspaceSection" import { WorkspaceStatusBar } from "../WorkspaceStatusBar/WorkspaceStatusBar" @@ -16,6 +17,7 @@ export interface WorkspaceProps { handleRetry: () => void handleUpdate: () => void workspaceStatus: WorkspaceStatus + resources?: TypesGen.WorkspaceResource[] } /** @@ -30,6 +32,7 @@ export const Workspace: React.FC = ({ handleRetry, handleUpdate, workspaceStatus, + resources }) => { const styles = useStyles() @@ -46,6 +49,7 @@ export const Workspace: React.FC = ({ handleUpdate={handleUpdate} workspaceStatus={workspaceStatus} /> +
@@ -55,9 +59,6 @@ export const Workspace: React.FC = ({ - - -
diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 28c1120cba6a4..b028b49ab57e0 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -185,7 +185,7 @@ export const MockWorkspaceAgent: TypesGen.WorkspaceAgent = { } export const MockWorkspaceResource: TypesGen.WorkspaceResource = { - agents: [MockWorkspaceAgent], + agents: [MockWorkspaceAgent, MockWorkspaceAgent], created_at: "", id: "test-workspace-resource", job_id: "", From 99e63e5f1e19b45273f661b7d9a02e6ad114fce6 Mon Sep 17 00:00:00 2001 From: Presley Date: Wed, 18 May 2022 03:24:39 +0000 Subject: [PATCH 02/13] Format --- site/src/components/Resources/Resources.tsx | 57 +++++++++++---------- site/src/components/Workspace/Workspace.tsx | 2 +- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/site/src/components/Resources/Resources.tsx b/site/src/components/Resources/Resources.tsx index f56ad46f7eb96..5adb88f883dfb 100644 --- a/site/src/components/Resources/Resources.tsx +++ b/site/src/components/Resources/Resources.tsx @@ -21,26 +21,27 @@ interface ResourcesProps { } export const Resources: React.FC = ({ resources }) => { - return - - - - {Language.resourceLabel} - {Language.agentsLabel} - - - - {resources?.map((resource) => ( - - {resource.name} - -
- - - {Language.agentLabel} - {Language.statusLabel} - - + return ( + +
+ + + {Language.resourceLabel} + {Language.agentsLabel} + + + + {resources?.map((resource) => ( + + {resource.name} + +
+ + + {Language.agentLabel} + {Language.statusLabel} + + {resource.agents?.map((agent) => ( @@ -49,12 +50,12 @@ export const Resources: React.FC = ({ resources }) => { ))} -
- - - ))} - - - -
+ + + + ))} + + +
+ ) } diff --git a/site/src/components/Workspace/Workspace.tsx b/site/src/components/Workspace/Workspace.tsx index a52c7653d368a..a9bc1907b6298 100644 --- a/site/src/components/Workspace/Workspace.tsx +++ b/site/src/components/Workspace/Workspace.tsx @@ -32,7 +32,7 @@ export const Workspace: React.FC = ({ handleRetry, handleUpdate, workspaceStatus, - resources + resources, }) => { const styles = useStyles() From 7b318eeb63b4415bf55e8fac4ecf5fa5fc372530 Mon Sep 17 00:00:00 2001 From: Presley Date: Wed, 18 May 2022 04:28:57 +0000 Subject: [PATCH 03/13] Hook up api and test - bug assigning resources --- site/src/components/Resources/Resources.tsx | 73 +++++----- .../Workspace/Workspace.stories.tsx | 11 +- site/src/components/Workspace/Workspace.tsx | 4 +- .../WorkspacePage/WorkspacePage.test.tsx | 135 ++++++++++-------- .../src/pages/WorkspacePage/WorkspacePage.tsx | 7 +- site/src/testHelpers/entities.ts | 14 +- site/src/testHelpers/handlers.ts | 2 +- .../xServices/workspace/workspaceXService.ts | 45 ++++++ 8 files changed, 190 insertions(+), 101 deletions(-) diff --git a/site/src/components/Resources/Resources.tsx b/site/src/components/Resources/Resources.tsx index 5adb88f883dfb..3f5d1aba298f8 100644 --- a/site/src/components/Resources/Resources.tsx +++ b/site/src/components/Resources/Resources.tsx @@ -18,44 +18,49 @@ const Language = { interface ResourcesProps { resources?: WorkspaceResource[] + getResourcesError?: Error } -export const Resources: React.FC = ({ resources }) => { +export const Resources: React.FC = ({ resources, getResourcesError }) => { return ( - - - - {Language.resourceLabel} - {Language.agentsLabel} - - - - {resources?.map((resource) => ( - - {resource.name} - -
- - - {Language.agentLabel} - {Language.statusLabel} - - - - {resource.agents?.map((agent) => ( - - {agent.name} - {agent.status} - - ))} - -
- - - ))} - - + {getResourcesError ? ( + { getResourcesError } + ) : ( + + + + {Language.resourceLabel} + {Language.agentsLabel} + + + + {resources?.map((resource) => ( + + {resource.name} + +
+ + + {Language.agentLabel} + {Language.statusLabel} + + + + {resource.agents?.map((agent) => ( + + {agent.name} + {agent.status} + + ))} + +
+ + + ))} + + + )}
) } diff --git a/site/src/components/Workspace/Workspace.stories.tsx b/site/src/components/Workspace/Workspace.stories.tsx index 237c8f77c965c..c4be9a159a813 100644 --- a/site/src/components/Workspace/Workspace.stories.tsx +++ b/site/src/components/Workspace/Workspace.stories.tsx @@ -1,7 +1,14 @@ import { action } from "@storybook/addon-actions" import { Story } from "@storybook/react" import React from "react" -import { MockOrganization, MockOutdatedWorkspace, MockTemplate, MockWorkspace, MockWorkspaceResource } from "../../testHelpers/renderHelpers" +import { + MockOrganization, + MockOutdatedWorkspace, + MockTemplate, + MockWorkspace, + MockWorkspaceResource, + MockWorkspaceResource2, +} from "../../testHelpers/renderHelpers" import { Workspace, WorkspaceProps } from "./Workspace" export default { @@ -21,7 +28,7 @@ Started.args = { handleStop: action("stop"), handleRetry: action("retry"), workspaceStatus: "started", - resources: [MockWorkspaceResource, MockWorkspaceResource] + resources: [MockWorkspaceResource, MockWorkspaceResource2], } export const Starting = Template.bind({}) diff --git a/site/src/components/Workspace/Workspace.tsx b/site/src/components/Workspace/Workspace.tsx index a9bc1907b6298..6b67af0fc2c29 100644 --- a/site/src/components/Workspace/Workspace.tsx +++ b/site/src/components/Workspace/Workspace.tsx @@ -18,6 +18,7 @@ export interface WorkspaceProps { handleUpdate: () => void workspaceStatus: WorkspaceStatus resources?: TypesGen.WorkspaceResource[] + getResourcesError?: Error } /** @@ -33,6 +34,7 @@ export const Workspace: React.FC = ({ handleUpdate, workspaceStatus, resources, + getResourcesError, }) => { const styles = useStyles() @@ -49,7 +51,7 @@ export const Workspace: React.FC = ({ handleUpdate={handleUpdate} workspaceStatus={workspaceStatus} /> - +
diff --git a/site/src/pages/WorkspacePage/WorkspacePage.test.tsx b/site/src/pages/WorkspacePage/WorkspacePage.test.tsx index f2e02a888d429..5dd7c81ceb3cb 100644 --- a/site/src/pages/WorkspacePage/WorkspacePage.test.tsx +++ b/site/src/pages/WorkspacePage/WorkspacePage.test.tsx @@ -16,6 +16,8 @@ import { MockStoppingWorkspace, MockTemplate, MockWorkspace, + MockWorkspaceAgent, + MockWorkspaceAgentDisconnected, MockWorkspaceBuild, renderWithAuth, } from "../../testHelpers/renderHelpers" @@ -60,71 +62,86 @@ describe("Workspace Page", () => { expect(workspaceName).toBeDefined() expect(templateName).toBeDefined() }) - it("shows the status of the workspace", async () => { - renderWithAuth(, { route: `/workspaces/${MockWorkspace.id}`, path: "/workspaces/:workspace" }) - const status = await screen.findByRole("status") - expect(status).toHaveTextContent("Running") - }) - it("requests a stop job when the user presses Stop", async () => { - const stopWorkspaceMock = jest - .spyOn(api, "stopWorkspace") - .mockImplementation(() => Promise.resolve(MockWorkspaceBuild)) - testButton(Language.start, stopWorkspaceMock) - }), - it("requests a start job when the user presses Start", async () => { - const startWorkspaceMock = jest - .spyOn(api, "startWorkspace") - .mockImplementation(() => Promise.resolve(MockWorkspaceBuild)) - testButton(Language.start, startWorkspaceMock) - }), - it("requests a start job when the user presses Retry after trying to start", async () => { - const startWorkspaceMock = jest - .spyOn(api, "startWorkspace") - .mockImplementation(() => Promise.resolve(MockWorkspaceBuild)) - testButton(Language.retry, startWorkspaceMock) - }), - it("requests a stop job when the user presses Retry after trying to stop", async () => { + describe("WorkspaceStatusBar", () => { + it("shows the status of the workspace", async () => { + renderWithAuth(, { route: `/workspaces/${MockWorkspace.id}`, path: "/workspaces/:workspace" }) + const status = await screen.findByRole("status") + expect(status).toHaveTextContent("Running") + }) + it("requests a stop job when the user presses Stop", async () => { const stopWorkspaceMock = jest .spyOn(api, "stopWorkspace") .mockImplementation(() => Promise.resolve(MockWorkspaceBuild)) - server.use( - rest.get(`/api/v2/workspaces/${MockWorkspace.id}`, (req, res, ctx) => { - return res(ctx.status(200), ctx.json(MockStoppedWorkspace)) - }), - ) testButton(Language.start, stopWorkspaceMock) }), - it("requests a template when the user presses Update", async () => { - const getTemplateMock = jest.spyOn(api, "getTemplate").mockImplementation(() => Promise.resolve(MockTemplate)) - server.use( - rest.get(`/api/v2/workspaces/${MockWorkspace.id}`, (req, res, ctx) => { - return res(ctx.status(200), ctx.json(MockOutdatedWorkspace)) - }), - ) - testButton(Language.update, getTemplateMock) - }), - it("shows the Stopping status when the workspace is stopping", async () => { - testStatus(MockStoppingWorkspace, Language.stopping) + it("requests a start job when the user presses Start", async () => { + const startWorkspaceMock = jest + .spyOn(api, "startWorkspace") + .mockImplementation(() => Promise.resolve(MockWorkspaceBuild)) + testButton(Language.start, startWorkspaceMock) + }), + it("requests a start job when the user presses Retry after trying to start", async () => { + const startWorkspaceMock = jest + .spyOn(api, "startWorkspace") + .mockImplementation(() => Promise.resolve(MockWorkspaceBuild)) + testButton(Language.retry, startWorkspaceMock) + }), + it("requests a stop job when the user presses Retry after trying to stop", async () => { + const stopWorkspaceMock = jest + .spyOn(api, "stopWorkspace") + .mockImplementation(() => Promise.resolve(MockWorkspaceBuild)) + server.use( + rest.get(`/api/v2/workspaces/${MockWorkspace.id}`, (req, res, ctx) => { + return res(ctx.status(200), ctx.json(MockStoppedWorkspace)) + }), + ) + testButton(Language.start, stopWorkspaceMock) + }), + it("requests a template when the user presses Update", async () => { + const getTemplateMock = jest.spyOn(api, "getTemplate").mockImplementation(() => Promise.resolve(MockTemplate)) + server.use( + rest.get(`/api/v2/workspaces/${MockWorkspace.id}`, (req, res, ctx) => { + return res(ctx.status(200), ctx.json(MockOutdatedWorkspace)) + }), + ) + testButton(Language.update, getTemplateMock) + }), + it("shows the Stopping status when the workspace is stopping", async () => { + testStatus(MockStoppingWorkspace, Language.stopping) + }) + it("shows the Stopped status when the workspace is stopped", async () => { + testStatus(MockStoppedWorkspace, Language.stopped) + }) + it("shows the Building status when the workspace is starting", async () => { + testStatus(MockStartingWorkspace, Language.starting) + }) + it("shows the Running status when the workspace is started", async () => { + testStatus(MockWorkspace, Language.started) + }) + it("shows the Error status when the workspace is failed or canceled", async () => { + testStatus(MockFailedWorkspace, Language.error) + }) + it("shows the Loading status when the workspace is canceling", async () => { + testStatus(MockCancelingWorkspace, Language.canceling) + }) + it("shows the Deleting status when the workspace is deleting", async () => { + testStatus(MockDeletingWorkspace, Language.canceling) + }) + it("shows the Deleted status when the workspace is deleted", async () => { + testStatus(MockDeletedWorkspace, Language.canceling) }) - it("shows the Stopped status when the workspace is stopped", async () => { - testStatus(MockStoppedWorkspace, Language.stopped) - }) - it("shows the Building status when the workspace is starting", async () => { - testStatus(MockStartingWorkspace, Language.starting) - }) - it("shows the Running status when the workspace is started", async () => { - testStatus(MockWorkspace, Language.started) - }) - it("shows the Error status when the workspace is failed or canceled", async () => { - testStatus(MockFailedWorkspace, Language.error) - }) - it("shows the Loading status when the workspace is canceling", async () => { - testStatus(MockCancelingWorkspace, Language.canceling) - }) - it("shows the Deleting status when the workspace is deleting", async () => { - testStatus(MockDeletingWorkspace, Language.canceling) }) - it("shows the Deleted status when the workspace is deleted", async () => { - testStatus(MockDeletedWorkspace, Language.canceling) + describe("Resources", () => { + it("shows the status of each agent in each resource", async () => { + renderWithAuth(, { route: `/workspaces/${MockWorkspace.id}`, path: "/workspaces/:workspace" }) + const agent1Names = await screen.findAllByText(MockWorkspaceAgent.name) + expect(agent1Names.length).toEqual(2) + const agent2Names = await screen.findAllByText(MockWorkspaceAgentDisconnected.name) + expect(agent2Names.length).toEqual(2) + const agent1Status = await screen.findAllByText(MockWorkspaceAgent.status) + expect(agent1Status.length).toEqual(2) + const agent2Status = await screen.findAllByText(MockWorkspaceAgentDisconnected.status) + expect(agent2Status.length).toEqual(2) + }) }) }) diff --git a/site/src/pages/WorkspacePage/WorkspacePage.tsx b/site/src/pages/WorkspacePage/WorkspacePage.tsx index 4dc6ba6ea3122..12ee5c8a98288 100644 --- a/site/src/pages/WorkspacePage/WorkspacePage.tsx +++ b/site/src/pages/WorkspacePage/WorkspacePage.tsx @@ -27,8 +27,7 @@ export const WorkspacePage: React.FC = () => { const xServices = useContext(XServiceContext) const [workspaceState, workspaceSend] = useActor(xServices.workspaceXService) - const { workspace, template, organization, getWorkspaceError, getTemplateError, getOrganizationError } = - workspaceState.context + const { workspace, template, organization, resources, getWorkspaceError, getResourcesError } = workspaceState.context const workspaceStatus = useSelector(xServices.workspaceXService, selectWorkspaceStatus) /** @@ -40,7 +39,7 @@ export const WorkspacePage: React.FC = () => { }, [workspaceId, workspaceSend]) if (workspaceState.matches("error")) { - return + return } else if (!workspace) { return } else { @@ -56,6 +55,8 @@ export const WorkspacePage: React.FC = () => { handleRetry={() => workspaceSend("RETRY")} handleUpdate={() => workspaceSend("UPDATE")} workspaceStatus={workspaceStatus} + resources={resources} + getResourcesError={getResourcesError instanceof Error ? getResourcesError : undefined} /> diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index b028b49ab57e0..9ace6e990ab1a 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -184,8 +184,14 @@ export const MockWorkspaceAgent: TypesGen.WorkspaceAgent = { updated_at: "", } +export const MockWorkspaceAgentDisconnected: TypesGen.WorkspaceAgent = { + ...MockWorkspaceAgent, + name: "another-workspace-agent", + status: "disconnected", +} + export const MockWorkspaceResource: TypesGen.WorkspaceResource = { - agents: [MockWorkspaceAgent, MockWorkspaceAgent], + agents: [MockWorkspaceAgent, MockWorkspaceAgentDisconnected], created_at: "", id: "test-workspace-resource", job_id: "", @@ -194,6 +200,12 @@ export const MockWorkspaceResource: TypesGen.WorkspaceResource = { workspace_transition: "start", } +export const MockWorkspaceResource2 = { + ...MockWorkspaceResource, + id: "test-workspace-resource-2", + name: "another-workspace-resource", +} + export const MockUserAgent: Types.UserAgent = { browser: "Chrome 99.0.4844", device: "Other", diff --git a/site/src/testHelpers/handlers.ts b/site/src/testHelpers/handlers.ts index b7faf6895c176..ab53481986eec 100644 --- a/site/src/testHelpers/handlers.ts +++ b/site/src/testHelpers/handlers.ts @@ -95,6 +95,6 @@ export const handlers = [ // workspace builds rest.get("/api/v2/workspacebuilds/:workspaceBuildId/resources", (req, res, ctx) => { - return res(ctx.status(200), ctx.json([M.MockWorkspaceResource])) + return res(ctx.status(200), ctx.json([M.MockWorkspaceResource, M.MockWorkspaceResource2])) }), ] diff --git a/site/src/xServices/workspace/workspaceXService.ts b/site/src/xServices/workspace/workspaceXService.ts index a64633595466c..8028e910896e8 100644 --- a/site/src/xServices/workspace/workspaceXService.ts +++ b/site/src/xServices/workspace/workspaceXService.ts @@ -13,6 +13,7 @@ export interface WorkspaceContext { template?: TypesGen.Template organization?: TypesGen.Organization build?: TypesGen.WorkspaceBuild + resources?: TypesGen.WorkspaceResource[] getWorkspaceError?: Error | unknown getTemplateError?: Error | unknown getOrganizationError?: Error | unknown @@ -21,6 +22,7 @@ export interface WorkspaceContext { // these are separate from getX errors because they don't make the page unusable refreshWorkspaceError: Error | unknown refreshTemplateError: Error | unknown + getResourcesError: Error | unknown } export type WorkspaceEvent = @@ -55,6 +57,9 @@ export const workspaceMachine = createMachine( refreshWorkspace: { data: TypesGen.Workspace | undefined } + getResources: { + data: TypesGen.WorkspaceResource[] + } }, }, id: "workspaceState", @@ -200,6 +205,25 @@ export const workspaceMachine = createMachine( }, }, }, + pollingResources: { + initial: "gettingResources", + states: { + gettingResources: { + entry: "clearGetResourcesError", + invoke: { + id: "getResources", + src: "getResources", + onDone: { target: "waiting", actions: "assignResources" }, + onError: { target: "waiting", actions: "assignGetResourcesError" }, + }, + }, + waiting: { + after: { + 5000: "gettingResources", + }, + }, + }, + }, }, }, error: { @@ -274,6 +298,20 @@ export const workspaceMachine = createMachine( assign({ refreshTemplateError: undefined, }), + assignResources: (_, event) => { + console.log("here", event.data) + return assign({ + resources: event.data, + }) + }, + assignGetResourcesError: (_, event) => + assign({ + getResourcesError: event.data, + }), + clearGetResourcesError: (_) => + assign({ + getResourcesError: undefined, + }), }, guards: { triedToStart: (context) => context.workspace?.latest_build.transition === "start", @@ -317,6 +355,13 @@ export const workspaceMachine = createMachine( throw Error("Cannot refresh workspace without id") } }, + getResources: async (context) => { + if (context.workspace) { + return await API.getWorkspaceResources(context.workspace.latest_build.id) + } else { + throw Error("Cannot fetch workspace resources without workspace") + } + }, }, }, ) From 8cfb56ae78be431548ccfe08969f5b288ac7c48e Mon Sep 17 00:00:00 2001 From: Presley Date: Wed, 18 May 2022 04:30:06 +0000 Subject: [PATCH 04/13] Remove debugging code --- site/src/xServices/workspace/workspaceXService.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/site/src/xServices/workspace/workspaceXService.ts b/site/src/xServices/workspace/workspaceXService.ts index 8028e910896e8..bfa19f6e709c1 100644 --- a/site/src/xServices/workspace/workspaceXService.ts +++ b/site/src/xServices/workspace/workspaceXService.ts @@ -298,12 +298,10 @@ export const workspaceMachine = createMachine( assign({ refreshTemplateError: undefined, }), - assignResources: (_, event) => { - console.log("here", event.data) - return assign({ + assignResources: (_, event) => + assign({ resources: event.data, - }) - }, + }), assignGetResourcesError: (_, event) => assign({ getResourcesError: event.data, From c5b60dddfa5ba0a697f521495decd6824dedda0d Mon Sep 17 00:00:00 2001 From: Presley Date: Wed, 18 May 2022 17:36:24 +0000 Subject: [PATCH 05/13] Format --- site/src/components/Workspace/Workspace.tsx | 2 +- site/src/pages/WorkspacePage/WorkspacePage.test.tsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/site/src/components/Workspace/Workspace.tsx b/site/src/components/Workspace/Workspace.tsx index 789bd07c5783d..5302b6910e2ec 100644 --- a/site/src/components/Workspace/Workspace.tsx +++ b/site/src/components/Workspace/Workspace.tsx @@ -2,9 +2,9 @@ import { makeStyles } from "@material-ui/core/styles" import Typography from "@material-ui/core/Typography" import React from "react" import * as TypesGen from "../../api/typesGenerated" -import { Resources } from "../Resources/Resources" import { WorkspaceStatus } from "../../util/workspace" import { BuildsTable } from "../BuildsTable/BuildsTable" +import { Resources } from "../Resources/Resources" import { WorkspaceSchedule } from "../WorkspaceSchedule/WorkspaceSchedule" import { WorkspaceSection } from "../WorkspaceSection/WorkspaceSection" import { WorkspaceStatusBar } from "../WorkspaceStatusBar/WorkspaceStatusBar" diff --git a/site/src/pages/WorkspacePage/WorkspacePage.test.tsx b/site/src/pages/WorkspacePage/WorkspacePage.test.tsx index df2d3d58043c5..31b7fa894d59f 100644 --- a/site/src/pages/WorkspacePage/WorkspacePage.test.tsx +++ b/site/src/pages/WorkspacePage/WorkspacePage.test.tsx @@ -183,5 +183,4 @@ describe("Workspace Page", () => { expect(rows).toHaveLength(MockBuilds.length) }) }) - }) From c4257a26192a9c525cf3575ea96047e9b21f7ef4 Mon Sep 17 00:00:00 2001 From: Presley Date: Wed, 18 May 2022 19:21:47 +0000 Subject: [PATCH 06/13] Remove unnecessary cards --- site/src/components/Workspace/Workspace.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/site/src/components/Workspace/Workspace.tsx b/site/src/components/Workspace/Workspace.tsx index 5302b6910e2ec..718ad11f2147b 100644 --- a/site/src/components/Workspace/Workspace.tsx +++ b/site/src/components/Workspace/Workspace.tsx @@ -53,13 +53,7 @@ export const Workspace: React.FC = ({
- - - - - -
From be67df8c7ffd37e2164a85351d77435b5214e613 Mon Sep 17 00:00:00 2001 From: Presley Date: Fri, 20 May 2022 15:03:31 +0000 Subject: [PATCH 07/13] Fix test --- site/src/components/BuildsTable/BuildsTable.tsx | 2 +- site/src/pages/WorkspacePage/WorkspacePage.test.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/site/src/components/BuildsTable/BuildsTable.tsx b/site/src/components/BuildsTable/BuildsTable.tsx index 3e26910894e75..f831b53d01de5 100644 --- a/site/src/components/BuildsTable/BuildsTable.tsx +++ b/site/src/components/BuildsTable/BuildsTable.tsx @@ -50,7 +50,7 @@ export const BuildsTable: React.FC = ({ builds, className }) = const theme: Theme = useTheme() return ( - +
{Language.actionLabel} diff --git a/site/src/pages/WorkspacePage/WorkspacePage.test.tsx b/site/src/pages/WorkspacePage/WorkspacePage.test.tsx index 31b7fa894d59f..fa173edad6148 100644 --- a/site/src/pages/WorkspacePage/WorkspacePage.test.tsx +++ b/site/src/pages/WorkspacePage/WorkspacePage.test.tsx @@ -178,7 +178,7 @@ describe("Workspace Page", () => { describe("Timeline", () => { it("shows the timeline build", async () => { await renderWorkspacePage() - const table = await screen.findByRole("table") + const table = await screen.findByTestId("builds-table") const rows = table.querySelectorAll("tbody > tr") expect(rows).toHaveLength(MockBuilds.length) }) From 85a869d2659d3f2d59599cb38b3132c1aa56c570 Mon Sep 17 00:00:00 2001 From: Presley Date: Fri, 20 May 2022 15:28:12 +0000 Subject: [PATCH 08/13] Fix assignment --- site/src/xServices/workspace/workspaceXService.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/site/src/xServices/workspace/workspaceXService.ts b/site/src/xServices/workspace/workspaceXService.ts index efdaf1f1c4741..1dce0d340f135 100644 --- a/site/src/xServices/workspace/workspaceXService.ts +++ b/site/src/xServices/workspace/workspaceXService.ts @@ -367,9 +367,8 @@ export const workspaceMachine = createMachine( assign({ refreshTemplateError: undefined, }), - assignResources: (_, event) => - assign({ - resources: event.data, + assignResources: assign({ + resources: (_, event) => event.data, }), assignGetResourcesError: (_, event) => assign({ @@ -469,7 +468,8 @@ export const workspaceMachine = createMachine( }, getResources: async (context) => { if (context.workspace) { - return await API.getWorkspaceResources(context.workspace.latest_build.id) + const resources = await API.getWorkspaceResources(context.workspace.latest_build.id) + return resources } else { throw Error("Cannot fetch workspace resources without workspace") } From 1289ad29b301d8e67c423b9575a0d35a0974ae8f Mon Sep 17 00:00:00 2001 From: Presley Date: Fri, 20 May 2022 15:33:56 +0000 Subject: [PATCH 09/13] Fix tests --- site/src/components/Resources/Resources.tsx | 2 +- site/src/testHelpers/entities.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/site/src/components/Resources/Resources.tsx b/site/src/components/Resources/Resources.tsx index 3f5d1aba298f8..8ecad6d3e545c 100644 --- a/site/src/components/Resources/Resources.tsx +++ b/site/src/components/Resources/Resources.tsx @@ -48,7 +48,7 @@ export const Resources: React.FC = ({ resources, getResourcesErr {resource.agents?.map((agent) => ( - + {agent.name} {agent.status} diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 948f435e4383c..a98fec89cbd21 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -190,6 +190,7 @@ export const MockWorkspaceAgent: TypesGen.WorkspaceAgent = { export const MockWorkspaceAgentDisconnected: TypesGen.WorkspaceAgent = { ...MockWorkspaceAgent, + id: "test-workspace-agent-2", name: "another-workspace-agent", status: "disconnected", } From abc770339e913eca69044cb32290ac6c20fcaec8 Mon Sep 17 00:00:00 2001 From: Presley Date: Fri, 20 May 2022 15:39:14 +0000 Subject: [PATCH 10/13] Lint --- site/src/components/Workspace/Workspace.tsx | 12 ------------ site/src/xServices/workspace/workspaceXService.ts | 4 ++-- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/site/src/components/Workspace/Workspace.tsx b/site/src/components/Workspace/Workspace.tsx index 718ad11f2147b..1a2fe95d9fab4 100644 --- a/site/src/components/Workspace/Workspace.tsx +++ b/site/src/components/Workspace/Workspace.tsx @@ -66,18 +66,6 @@ export const Workspace: React.FC = ({ ) } -/** - * Temporary placeholder component until we have the sections implemented - * Can be removed once the Workspace page has all the necessary sections - */ -const Placeholder: React.FC = () => { - return ( -
- Not yet implemented -
- ) -} - export const useStyles = makeStyles(() => { return { root: { diff --git a/site/src/xServices/workspace/workspaceXService.ts b/site/src/xServices/workspace/workspaceXService.ts index 1dce0d340f135..c8b2fea620b41 100644 --- a/site/src/xServices/workspace/workspaceXService.ts +++ b/site/src/xServices/workspace/workspaceXService.ts @@ -368,8 +368,8 @@ export const workspaceMachine = createMachine( refreshTemplateError: undefined, }), assignResources: assign({ - resources: (_, event) => event.data, - }), + resources: (_, event) => event.data, + }), assignGetResourcesError: (_, event) => assign({ getResourcesError: event.data, From 85c2ac88ba2fca1064708b3a404d25e410ff5d7a Mon Sep 17 00:00:00 2001 From: Bruno Date: Fri, 20 May 2022 17:50:46 +0000 Subject: [PATCH 11/13] Refactor design --- site/src/components/Resources/Resources.tsx | 85 +++++++++++++++------ site/src/util/workspace.ts | 33 +++++++- 2 files changed, 95 insertions(+), 23 deletions(-) diff --git a/site/src/components/Resources/Resources.tsx b/site/src/components/Resources/Resources.tsx index 8ecad6d3e545c..df232309c25c3 100644 --- a/site/src/components/Resources/Resources.tsx +++ b/site/src/components/Resources/Resources.tsx @@ -1,10 +1,13 @@ +import { makeStyles, Theme } from "@material-ui/core/styles" import Table from "@material-ui/core/Table" import TableBody from "@material-ui/core/TableBody" import TableCell from "@material-ui/core/TableCell" import TableHead from "@material-ui/core/TableHead" import TableRow from "@material-ui/core/TableRow" +import useTheme from "@material-ui/styles/useTheme" import React from "react" import { WorkspaceResource } from "../../api/typesGenerated" +import { getDisplayAgentStatus } from "../../util/workspace" import { TableHeaderRow } from "../TableHeaders/TableHeaders" import { WorkspaceSection } from "../WorkspaceSection/WorkspaceSection" @@ -22,39 +25,50 @@ interface ResourcesProps { } export const Resources: React.FC = ({ resources, getResourcesError }) => { + const styles = useStyles() + const theme: Theme = useTheme() + return ( - + {getResourcesError ? ( { getResourcesError } ) : ( -
+
- {Language.resourceLabel} - {Language.agentsLabel} + {Language.resourceLabel} + {resources?.map((resource) => ( - {resource.name} - -
- - - {Language.agentLabel} - {Language.statusLabel} - - - - {resource.agents?.map((agent) => ( - - {agent.name} - {agent.status} - - ))} - -
+ {resource.name} + + {resource.agents && ( + + + + {Language.agentLabel} + {Language.statusLabel} + + + + {resource.agents.map((agent) => ( + + + {agent.name} + + + + {getDisplayAgentStatus(theme, agent).status} + + + + ))} + +
+ )}
))} @@ -64,3 +78,30 @@ export const Resources: React.FC = ({ resources, getResourcesErr
) } + +const useStyles = makeStyles(() => ({ + sectionContents: { + margin: 0, + }, + + table: { + border: 0, + }, + + cellWithTable: { + padding: 0, + + "&:last-child": { + padding: 0, + }, + + "& table": { + borderTop: 0, + borderBottom: 0, + + "& tr:last-child td": { + borderBottom: 0, + }, + }, + }, +})) diff --git a/site/src/util/workspace.ts b/site/src/util/workspace.ts index 6c72c72677aea..978d704571467 100644 --- a/site/src/util/workspace.ts +++ b/site/src/util/workspace.ts @@ -1,7 +1,7 @@ import { Theme } from "@material-ui/core/styles" import dayjs from "dayjs" import { WorkspaceBuildTransition } from "../api/types" -import { WorkspaceBuild } from "../api/typesGenerated" +import { WorkspaceAgent, WorkspaceBuild } from "../api/typesGenerated" export type WorkspaceStatus = | "queued" @@ -148,3 +148,34 @@ export const displayWorkspaceBuildDuration = (build: WorkspaceBuild, inProgressL const duration = getWorkspaceBuildDurationInSeconds(build) return duration ? `${duration} seconds` : inProgressLabel } + +export const getDisplayAgentStatus = ( + theme: Theme, + agent: WorkspaceAgent, +): { + color: string + status: string +} => { + switch (agent.status) { + case undefined: + return { + color: theme.palette.text.secondary, + status: DisplayStatusLanguage.loading, + } + case "connected": + return { + color: theme.palette.success.main, + status: `⦿ Connected`, + } + case "connecting": + return { + color: theme.palette.success.main, + status: `⦿ Connecting`, + } + case "disconnected": + return { + color: theme.palette.text.secondary, + status: `◍ Disconnected`, + } + } +} From e7b11103f049f37d46107d853ed6feb33e4fe399 Mon Sep 17 00:00:00 2001 From: Bruno Date: Fri, 20 May 2022 18:03:14 +0000 Subject: [PATCH 12/13] Refactor resources table --- site/src/components/Resources/Resources.tsx | 96 +++++++++++---------- 1 file changed, 49 insertions(+), 47 deletions(-) diff --git a/site/src/components/Resources/Resources.tsx b/site/src/components/Resources/Resources.tsx index df232309c25c3..0ea91855307bc 100644 --- a/site/src/components/Resources/Resources.tsx +++ b/site/src/components/Resources/Resources.tsx @@ -37,41 +37,51 @@ export const Resources: React.FC = ({ resources, getResourcesErr {Language.resourceLabel} - + {Language.agentLabel} + {Language.statusLabel} - {resources?.map((resource) => ( - - {resource.name} - - {resource.agents && ( - - - - {Language.agentLabel} - {Language.statusLabel} - - - - {resource.agents.map((agent) => ( - - - {agent.name} - - - - {getDisplayAgentStatus(theme, agent).status} - - - - ))} - -
- )} -
-
- ))} + {resources?.map((resource) => { + { + /* We need to initialize the agents to display the resource */ + } + const agents = resource.agents ?? [null] + return agents.map((agent, agentIndex) => { + { + /* If there is no agent, just display the resource name */ + } + if (!agent) { + return ( + + {resource.name} + + + ) + } + + return ( + + {/* We only want to display the name in the first row because we are using rowSpan */} + {/* The rowspan should be the same than the number of agents */} + {agentIndex === 0 && ( + + {resource.name} + + )} + + + {agent.name} + + + + {getDisplayAgentStatus(theme, agent).status} + + + + ) + }) + })}
)} @@ -79,7 +89,7 @@ export const Resources: React.FC = ({ resources, getResourcesErr ) } -const useStyles = makeStyles(() => ({ +const useStyles = makeStyles((theme) => ({ sectionContents: { margin: 0, }, @@ -88,20 +98,12 @@ const useStyles = makeStyles(() => ({ border: 0, }, - cellWithTable: { - padding: 0, - - "&:last-child": { - padding: 0, - }, - - "& table": { - borderTop: 0, - borderBottom: 0, + resourceNameCell: { + borderRight: `1px solid ${theme.palette.divider}`, + }, - "& tr:last-child td": { - borderBottom: 0, - }, - }, + // Adds some left spacing + agentColumn: { + paddingLeft: `${theme.spacing(2)}px !important`, }, })) From f645de3c1071ade8107f3a9625027db6107c69de Mon Sep 17 00:00:00 2001 From: Bruno Date: Fri, 20 May 2022 18:21:06 +0000 Subject: [PATCH 13/13] Fix tests --- site/src/components/Resources/Resources.tsx | 2 +- site/src/pages/WorkspacePage/WorkspacePage.test.tsx | 6 +++--- site/src/util/workspace.ts | 12 +++++++++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/site/src/components/Resources/Resources.tsx b/site/src/components/Resources/Resources.tsx index 0ea91855307bc..25448c678bc81 100644 --- a/site/src/components/Resources/Resources.tsx +++ b/site/src/components/Resources/Resources.tsx @@ -61,7 +61,7 @@ export const Resources: React.FC = ({ resources, getResourcesErr } return ( - + {/* We only want to display the name in the first row because we are using rowSpan */} {/* The rowspan should be the same than the number of agents */} {agentIndex === 0 && ( diff --git a/site/src/pages/WorkspacePage/WorkspacePage.test.tsx b/site/src/pages/WorkspacePage/WorkspacePage.test.tsx index d0d7ef975c099..c0f3bab812104 100644 --- a/site/src/pages/WorkspacePage/WorkspacePage.test.tsx +++ b/site/src/pages/WorkspacePage/WorkspacePage.test.tsx @@ -22,7 +22,7 @@ import { renderWithAuth, } from "../../testHelpers/renderHelpers" import { server } from "../../testHelpers/server" -import { DisplayStatusLanguage } from "../../util/workspace" +import { DisplayAgentStatusLanguage, DisplayStatusLanguage } from "../../util/workspace" import { WorkspacePage } from "./WorkspacePage" // It renders the workspace page and waits for it be loaded @@ -176,9 +176,9 @@ describe("Workspace Page", () => { expect(agent1Names.length).toEqual(2) const agent2Names = await screen.findAllByText(MockWorkspaceAgentDisconnected.name) expect(agent2Names.length).toEqual(2) - const agent1Status = await screen.findAllByText(MockWorkspaceAgent.status) + const agent1Status = await screen.findAllByText(DisplayAgentStatusLanguage[MockWorkspaceAgent.status]) expect(agent1Status.length).toEqual(2) - const agent2Status = await screen.findAllByText(MockWorkspaceAgentDisconnected.status) + const agent2Status = await screen.findAllByText(DisplayAgentStatusLanguage[MockWorkspaceAgentDisconnected.status]) expect(agent2Status.length).toEqual(2) }) }) diff --git a/site/src/util/workspace.ts b/site/src/util/workspace.ts index 978d704571467..b78b4b716b7c3 100644 --- a/site/src/util/workspace.ts +++ b/site/src/util/workspace.ts @@ -149,6 +149,12 @@ export const displayWorkspaceBuildDuration = (build: WorkspaceBuild, inProgressL return duration ? `${duration} seconds` : inProgressLabel } +export const DisplayAgentStatusLanguage = { + connected: "⦿ Connected", + connecting: "⦿ Connecting", + disconnected: "◍ Disconnected", +} + export const getDisplayAgentStatus = ( theme: Theme, agent: WorkspaceAgent, @@ -165,17 +171,17 @@ export const getDisplayAgentStatus = ( case "connected": return { color: theme.palette.success.main, - status: `⦿ Connected`, + status: DisplayAgentStatusLanguage["connected"], } case "connecting": return { color: theme.palette.success.main, - status: `⦿ Connecting`, + status: DisplayAgentStatusLanguage["connecting"], } case "disconnected": return { color: theme.palette.text.secondary, - status: `◍ Disconnected`, + status: DisplayAgentStatusLanguage["disconnected"], } } }