Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commite40c683

Browse files
authored
feat: resources card (#1627)
* Set up table* Format* Hook up api and test - bug assigning resources* Remove debugging code* Format* Remove unnecessary cards* Fix test* Fix assignment* Fix tests* Lint
1 parentc189fc5 commite40c683

File tree

10 files changed

+250
-17
lines changed

10 files changed

+250
-17
lines changed

‎site/src/components/BuildsTable/BuildsTable.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export const BuildsTable: React.FC<BuildsTableProps> = ({ builds, className }) =
3434
conststyles=useStyles()
3535

3636
return(
37-
<TableclassName={className}>
37+
<TableclassName={className}data-testid="builds-table">
3838
<TableHead>
3939
<TableRow>
4040
<TableCellwidth="20%">{Language.actionLabel}</TableCell>
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import{makeStyles,Theme}from"@material-ui/core/styles"
2+
importTablefrom"@material-ui/core/Table"
3+
importTableBodyfrom"@material-ui/core/TableBody"
4+
importTableCellfrom"@material-ui/core/TableCell"
5+
importTableHeadfrom"@material-ui/core/TableHead"
6+
importTableRowfrom"@material-ui/core/TableRow"
7+
importuseThemefrom"@material-ui/styles/useTheme"
8+
importReactfrom"react"
9+
import{WorkspaceResource}from"../../api/typesGenerated"
10+
import{getDisplayAgentStatus}from"../../util/workspace"
11+
import{TableHeaderRow}from"../TableHeaders/TableHeaders"
12+
import{WorkspaceSection}from"../WorkspaceSection/WorkspaceSection"
13+
14+
constLanguage={
15+
resources:"Resources",
16+
resourceLabel:"Resource",
17+
agentsLabel:"Agents",
18+
agentLabel:"Agent",
19+
statusLabel:"Status",
20+
}
21+
22+
interfaceResourcesProps{
23+
resources?:WorkspaceResource[]
24+
getResourcesError?:Error
25+
}
26+
27+
exportconstResources:React.FC<ResourcesProps>=({ resources, getResourcesError})=>{
28+
conststyles=useStyles()
29+
consttheme:Theme=useTheme()
30+
31+
return(
32+
<WorkspaceSectiontitle={Language.resources}contentsProps={{className:styles.sectionContents}}>
33+
{getResourcesError ?(
34+
{ getResourcesError}
35+
) :(
36+
<TableclassName={styles.table}>
37+
<TableHead>
38+
<TableHeaderRow>
39+
<TableCell>{Language.resourceLabel}</TableCell>
40+
<TableCellclassName={styles.agentColumn}>{Language.agentLabel}</TableCell>
41+
<TableCell>{Language.statusLabel}</TableCell>
42+
</TableHeaderRow>
43+
</TableHead>
44+
<TableBody>
45+
{resources?.map((resource)=>{
46+
{
47+
/* We need to initialize the agents to display the resource */
48+
}
49+
constagents=resource.agents??[null]
50+
returnagents.map((agent,agentIndex)=>{
51+
{
52+
/* If there is no agent, just display the resource name */
53+
}
54+
if(!agent){
55+
return(
56+
<TableRow>
57+
<TableCellclassName={styles.resourceNameCell}>{resource.name}</TableCell>
58+
<TableCellcolSpan={2}></TableCell>
59+
</TableRow>
60+
)
61+
}
62+
63+
return(
64+
<TableRowkey={`${resource.id}-${agent.id}`}>
65+
{/* We only want to display the name in the first row because we are using rowSpan */}
66+
{/* The rowspan should be the same than the number of agents */}
67+
{agentIndex===0&&(
68+
<TableCellclassName={styles.resourceNameCell}rowSpan={agents.length}>
69+
{resource.name}
70+
</TableCell>
71+
)}
72+
73+
<TableCellclassName={styles.agentColumn}>
74+
<spanstyle={{color:theme.palette.text.secondary}}>{agent.name}</span>
75+
</TableCell>
76+
<TableCell>
77+
<spanstyle={{color:getDisplayAgentStatus(theme,agent).color}}>
78+
{getDisplayAgentStatus(theme,agent).status}
79+
</span>
80+
</TableCell>
81+
</TableRow>
82+
)
83+
})
84+
})}
85+
</TableBody>
86+
</Table>
87+
)}
88+
</WorkspaceSection>
89+
)
90+
}
91+
92+
constuseStyles=makeStyles((theme)=>({
93+
sectionContents:{
94+
margin:0,
95+
},
96+
97+
table:{
98+
border:0,
99+
},
100+
101+
resourceNameCell:{
102+
borderRight:`1px solid${theme.palette.divider}`,
103+
},
104+
105+
// Adds some left spacing
106+
agentColumn:{
107+
paddingLeft:`${theme.spacing(2)}px !important`,
108+
},
109+
}))

‎site/src/components/Workspace/Workspace.stories.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import{action}from"@storybook/addon-actions"
22
import{Story}from"@storybook/react"
33
importReactfrom"react"
4-
import{MockOutdatedWorkspace,MockWorkspace}from"../../testHelpers/renderHelpers"
4+
import{
5+
MockOutdatedWorkspace,
6+
MockWorkspace,
7+
MockWorkspaceResource,
8+
MockWorkspaceResource2,
9+
}from"../../testHelpers/renderHelpers"
510
import{Workspace,WorkspaceProps}from"./Workspace"
611

712
exportdefault{
@@ -19,6 +24,7 @@ Started.args = {
1924
handleStop:action("stop"),
2025
handleRetry:action("retry"),
2126
workspaceStatus:"started",
27+
resources:[MockWorkspaceResource,MockWorkspaceResource2],
2228
}
2329

2430
exportconstStarting=Template.bind({})

‎site/src/components/Workspace/Workspace.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as TypesGen from "../../api/typesGenerated"
55
import{MONOSPACE_FONT_FAMILY}from"../../theme/constants"
66
import{WorkspaceStatus}from"../../util/workspace"
77
import{BuildsTable}from"../BuildsTable/BuildsTable"
8+
import{Resources}from"../Resources/Resources"
89
import{Stack}from"../Stack/Stack"
910
import{WorkspaceActions}from"../WorkspaceActions/WorkspaceActions"
1011
import{WorkspaceSection}from"../WorkspaceSection/WorkspaceSection"
@@ -17,6 +18,8 @@ export interface WorkspaceProps {
1718
handleUpdate:()=>void
1819
workspace:TypesGen.Workspace
1920
workspaceStatus:WorkspaceStatus
21+
resources?:TypesGen.WorkspaceResource[]
22+
getResourcesError?:Error
2023
builds?:TypesGen.WorkspaceBuild[]
2124
}
2225

@@ -30,6 +33,8 @@ export const Workspace: React.FC<WorkspaceProps> = ({
3033
handleUpdate,
3134
workspace,
3235
workspaceStatus,
36+
resources,
37+
getResourcesError,
3338
builds,
3439
})=>{
3540
conststyles=useStyles()
@@ -61,6 +66,7 @@ export const Workspace: React.FC<WorkspaceProps> = ({
6166

6267
<Stackspacing={3}>
6368
<WorkspaceStatsworkspace={workspace}/>
69+
<Resourcesresources={resources}getResourcesError={getResourcesError}/>
6470
<WorkspaceSectiontitle="Timeline"contentsProps={{className:styles.timelineContents}}>
6571
<BuildsTablebuilds={builds}className={styles.timelineTable}/>
6672
</WorkspaceSection>

‎site/src/pages/WorkspacePage/WorkspacePage.test.tsx

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@ import {
1616
MockStoppingWorkspace,
1717
MockTemplate,
1818
MockWorkspace,
19+
MockWorkspaceAgent,
20+
MockWorkspaceAgentDisconnected,
1921
MockWorkspaceBuild,
2022
renderWithAuth,
2123
}from"../../testHelpers/renderHelpers"
2224
import{server}from"../../testHelpers/server"
23-
import{DisplayStatusLanguage}from"../../util/workspace"
25+
import{DisplayAgentStatusLanguage,DisplayStatusLanguage}from"../../util/workspace"
2426
import{WorkspacePage}from"./WorkspacePage"
2527

2628
// It renders the workspace page and waits for it be loaded
@@ -157,10 +159,27 @@ describe("Workspace Page", () => {
157159
it("shows the Deleted status when the workspace is deleted",async()=>{
158160
awaittestStatus(MockDeletedWorkspace,DisplayStatusLanguage.deleted)
159161
})
160-
it("shows the timeline build",async()=>{
161-
awaitrenderWorkspacePage()
162-
consttable=awaitscreen.findByRole("table")
163-
constrows=table.querySelectorAll("tbody > tr")
164-
expect(rows).toHaveLength(MockBuilds.length)
162+
163+
describe("Timeline",()=>{
164+
it("shows the timeline build",async()=>{
165+
awaitrenderWorkspacePage()
166+
consttable=awaitscreen.findByTestId("builds-table")
167+
constrows=table.querySelectorAll("tbody > tr")
168+
expect(rows).toHaveLength(MockBuilds.length)
169+
})
170+
})
171+
172+
describe("Resources",()=>{
173+
it("shows the status of each agent in each resource",async()=>{
174+
renderWithAuth(<WorkspacePage/>,{route:`/workspaces/${MockWorkspace.id}`,path:"/workspaces/:workspace"})
175+
constagent1Names=awaitscreen.findAllByText(MockWorkspaceAgent.name)
176+
expect(agent1Names.length).toEqual(2)
177+
constagent2Names=awaitscreen.findAllByText(MockWorkspaceAgentDisconnected.name)
178+
expect(agent2Names.length).toEqual(2)
179+
constagent1Status=awaitscreen.findAllByText(DisplayAgentStatusLanguage[MockWorkspaceAgent.status])
180+
expect(agent1Status.length).toEqual(2)
181+
constagent2Status=awaitscreen.findAllByText(DisplayAgentStatusLanguage[MockWorkspaceAgentDisconnected.status])
182+
expect(agent2Status.length).toEqual(2)
183+
})
165184
})
166185
})

‎site/src/pages/WorkspacePage/WorkspacePage.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import{useActor,useSelector}from"@xstate/react"
1+
import{useActor}from"@xstate/react"
22
importReact,{useContext,useEffect}from"react"
33
import{useParams}from"react-router-dom"
44
import{ErrorSummary}from"../../components/ErrorSummary/ErrorSummary"
@@ -16,10 +16,8 @@ export const WorkspacePage: React.FC = () => {
1616

1717
constxServices=useContext(XServiceContext)
1818
const[workspaceState,workspaceSend]=useActor(xServices.workspaceXService)
19-
const{ workspace, getWorkspaceError, getTemplateError, getOrganizationError, builds}=workspaceState.context
20-
constworkspaceStatus=useSelector(xServices.workspaceXService,(state)=>{
21-
returngetWorkspaceStatus(state.context.workspace?.latest_build)
22-
})
19+
const{ workspace, resources, getWorkspaceError, getResourcesError, builds}=workspaceState.context
20+
constworkspaceStatus=getWorkspaceStatus(workspace?.latest_build)
2321

2422
/**
2523
* Get workspace, template, and organization on mount and whenever workspaceId changes.
@@ -30,7 +28,7 @@ export const WorkspacePage: React.FC = () => {
3028
},[workspaceId,workspaceSend])
3129

3230
if(workspaceState.matches("error")){
33-
return<ErrorSummaryerror={getWorkspaceError||getTemplateError||getOrganizationError}/>
31+
return<ErrorSummaryerror={getWorkspaceError}/>
3432
}elseif(!workspace){
3533
return<FullScreenLoader/>
3634
}else{
@@ -44,6 +42,8 @@ export const WorkspacePage: React.FC = () => {
4442
handleRetry={()=>workspaceSend("RETRY")}
4543
handleUpdate={()=>workspaceSend("UPDATE")}
4644
workspaceStatus={workspaceStatus}
45+
resources={resources}
46+
getResourcesError={getResourcesErrorinstanceofError ?getResourcesError :undefined}
4747
builds={builds}
4848
/>
4949
</Stack>

‎site/src/testHelpers/entities.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,15 @@ export const MockWorkspaceAgent: TypesGen.WorkspaceAgent = {
188188
updated_at:"",
189189
}
190190

191+
exportconstMockWorkspaceAgentDisconnected:TypesGen.WorkspaceAgent={
192+
...MockWorkspaceAgent,
193+
id:"test-workspace-agent-2",
194+
name:"another-workspace-agent",
195+
status:"disconnected",
196+
}
197+
191198
exportconstMockWorkspaceResource:TypesGen.WorkspaceResource={
192-
agents:[MockWorkspaceAgent],
199+
agents:[MockWorkspaceAgent,MockWorkspaceAgentDisconnected],
193200
created_at:"",
194201
id:"test-workspace-resource",
195202
job_id:"",
@@ -198,6 +205,12 @@ export const MockWorkspaceResource: TypesGen.WorkspaceResource = {
198205
workspace_transition:"start",
199206
}
200207

208+
exportconstMockWorkspaceResource2={
209+
...MockWorkspaceResource,
210+
id:"test-workspace-resource-2",
211+
name:"another-workspace-resource",
212+
}
213+
201214
exportconstMockUserAgent:Types.UserAgent={
202215
browser:"Chrome 99.0.4844",
203216
device:"Other",

‎site/src/testHelpers/handlers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ export const handlers = [
125125
returnres(ctx.status(200),ctx.json(M.MockWorkspaceBuild))
126126
}),
127127
rest.get("/api/v2/workspacebuilds/:workspaceBuildId/resources",(req,res,ctx)=>{
128-
returnres(ctx.status(200),ctx.json([M.MockWorkspaceResource]))
128+
returnres(ctx.status(200),ctx.json([M.MockWorkspaceResource,M.MockWorkspaceResource2]))
129129
}),
130130
rest.get("/api/v2/workspacebuilds/:workspaceBuildId/logs",(req,res,ctx)=>{
131131
returnres(ctx.status(200),ctx.json(M.MockWorkspaceBuildLogs))

‎site/src/util/workspace.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import{Theme}from"@material-ui/core/styles"
22
importdayjsfrom"dayjs"
33
import{WorkspaceBuildTransition}from"../api/types"
4-
import{WorkspaceBuild}from"../api/typesGenerated"
4+
import{WorkspaceAgent,WorkspaceBuild}from"../api/typesGenerated"
55

66
exporttypeWorkspaceStatus=
77
|"queued"
@@ -148,3 +148,40 @@ export const displayWorkspaceBuildDuration = (build: WorkspaceBuild, inProgressL
148148
constduration=getWorkspaceBuildDurationInSeconds(build)
149149
returnduration ?`${duration} seconds` :inProgressLabel
150150
}
151+
152+
exportconstDisplayAgentStatusLanguage={
153+
connected:"⦿ Connected",
154+
connecting:"⦿ Connecting",
155+
disconnected:"◍ Disconnected",
156+
}
157+
158+
exportconstgetDisplayAgentStatus=(
159+
theme:Theme,
160+
agent:WorkspaceAgent,
161+
):{
162+
color:string
163+
status:string
164+
}=>{
165+
switch(agent.status){
166+
caseundefined:
167+
return{
168+
color:theme.palette.text.secondary,
169+
status:DisplayStatusLanguage.loading,
170+
}
171+
case"connected":
172+
return{
173+
color:theme.palette.success.main,
174+
status:DisplayAgentStatusLanguage["connected"],
175+
}
176+
case"connecting":
177+
return{
178+
color:theme.palette.success.main,
179+
status:DisplayAgentStatusLanguage["connecting"],
180+
}
181+
case"disconnected":
182+
return{
183+
color:theme.palette.text.secondary,
184+
status:DisplayAgentStatusLanguage["disconnected"],
185+
}
186+
}
187+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp