Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0764df3

Browse files
committed
Add basic template page
1 parent 5492ab7 commit 0764df3

File tree

12 files changed

+981
-23
lines changed

12 files changed

+981
-23
lines changed

site/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"history": "5.3.0",
4242
"react": "17.0.2",
4343
"react-dom": "17.0.2",
44+
"react-markdown": "^8.0.3",
4445
"react-router-dom": "6.3.0",
4546
"sourcemapped-stacktrace": "1.1.11",
4647
"swr": "1.2.2",

site/src/AppRouter.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { OrgsPage } from "./pages/OrgsPage/OrgsPage"
1212
import { SettingsPage } from "./pages/SettingsPage/SettingsPage"
1313
import { AccountPage } from "./pages/SettingsPages/AccountPage/AccountPage"
1414
import { SSHKeysPage } from "./pages/SettingsPages/SSHKeysPage/SSHKeysPage"
15+
import { TemplatePage } from "./pages/TemplatePage/TemplatePage"
1516
import TemplatesPage from "./pages/TemplatesPage/TemplatesPage"
1617
import { CreateUserPage } from "./pages/UsersPage/CreateUserPage/CreateUserPage"
1718
import { UsersPage } from "./pages/UsersPage/UsersPage"
@@ -95,6 +96,15 @@ export const AppRouter: React.FC = () => (
9596
</AuthAndFrame>
9697
}
9798
/>
99+
100+
<Route
101+
path=":template"
102+
element={
103+
<AuthAndFrame>
104+
<TemplatePage />
105+
</AuthAndFrame>
106+
}
107+
/>
98108
</Route>
99109

100110
<Route path="users">

site/src/api/api.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ export const getTemplateVersionSchema = async (versionId: string): Promise<Types
101101
return response.data
102102
}
103103

104+
export const getTemplateVersionResources = async (versionId: string): Promise<TypesGen.WorkspaceResource[]> => {
105+
const response = await axios.get<TypesGen.WorkspaceResource[]>(`/api/v2/templateversions/${versionId}/resources`)
106+
return response.data
107+
}
108+
104109
export const getWorkspace = async (workspaceId: string): Promise<TypesGen.Workspace> => {
105110
const response = await axios.get<TypesGen.Workspace>(`/api/v2/workspaces/${workspaceId}`)
106111
return response.data

site/src/components/Resources/Resources.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ export const Resources: React.FC<ResourcesProps> = ({ resources, getResourcesErr
7979
)}
8080

8181
<TableCell className={styles.agentColumn}>
82-
<span style={{ color: theme.palette.text.secondary }}>{agent.name}</span>
82+
{agent.name}
83+
<span className={styles.operatingSystem}>{agent.operating_system}</span>
8384
</TableCell>
8485
<TableCell>
8586
<span style={{ color: getDisplayAgentStatus(theme, agent).color }}>
@@ -143,4 +144,12 @@ const useStyles = makeStyles((theme) => ({
143144
marginRight: theme.spacing(1.5),
144145
},
145146
},
147+
148+
operatingSystem: {
149+
fontSize: 14,
150+
color: theme.palette.text.secondary,
151+
marginTop: theme.spacing(0.5),
152+
display: "block",
153+
textTransform: "capitalize",
154+
},
146155
}))
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { makeStyles } from "@material-ui/core/styles"
2+
import Table from "@material-ui/core/Table"
3+
import TableBody from "@material-ui/core/TableBody"
4+
import TableCell from "@material-ui/core/TableCell"
5+
import TableHead from "@material-ui/core/TableHead"
6+
import TableRow from "@material-ui/core/TableRow"
7+
import React from "react"
8+
import { WorkspaceResource } from "../../api/typesGenerated"
9+
import { TableHeaderRow } from "../TableHeaders/TableHeaders"
10+
11+
const Language = {
12+
resourceLabel: "Resource",
13+
agentLabel: "Agent",
14+
}
15+
16+
interface TemplateResourcesProps {
17+
resources: WorkspaceResource[]
18+
}
19+
20+
export const TemplateResourcesTable: React.FC<TemplateResourcesProps> = ({ resources }) => {
21+
const styles = useStyles()
22+
23+
return (
24+
<Table className={styles.table}>
25+
<TableHead>
26+
<TableHeaderRow>
27+
<TableCell>{Language.resourceLabel}</TableCell>
28+
<TableCell className={styles.agentColumn}>{Language.agentLabel}</TableCell>
29+
</TableHeaderRow>
30+
</TableHead>
31+
<TableBody>
32+
{resources.map((resource) => {
33+
{
34+
/* We need to initialize the agents to display the resource */
35+
}
36+
const agents = resource.agents ?? [null]
37+
return agents.map((agent, agentIndex) => {
38+
{
39+
/* If there is no agent, just display the resource name */
40+
}
41+
if (!agent) {
42+
return (
43+
<TableRow>
44+
<TableCell className={styles.resourceNameCell}>
45+
{resource.name}
46+
<span className={styles.resourceType}>{resource.type}</span>
47+
</TableCell>
48+
<TableCell colSpan={3}></TableCell>
49+
</TableRow>
50+
)
51+
}
52+
53+
return (
54+
<TableRow key={`${resource.id}-${agent.id}`}>
55+
{/* We only want to display the name in the first row because we are using rowSpan */}
56+
{/* The rowspan should be the same than the number of agents */}
57+
{agentIndex === 0 && (
58+
<TableCell className={styles.resourceNameCell} rowSpan={agents.length}>
59+
{resource.name}
60+
<span className={styles.resourceType}>{resource.type}</span>
61+
</TableCell>
62+
)}
63+
64+
<TableCell className={styles.agentColumn}>
65+
{agent.name}
66+
<span className={styles.operatingSystem}>{agent.operating_system}</span>
67+
</TableCell>
68+
</TableRow>
69+
)
70+
})
71+
})}
72+
</TableBody>
73+
</Table>
74+
)
75+
}
76+
77+
const useStyles = makeStyles((theme) => ({
78+
sectionContents: {
79+
margin: 0,
80+
},
81+
82+
table: {
83+
border: 0,
84+
},
85+
86+
resourceNameCell: {
87+
borderRight: `1px solid ${theme.palette.divider}`,
88+
},
89+
90+
resourceType: {
91+
fontSize: 14,
92+
color: theme.palette.text.secondary,
93+
marginTop: theme.spacing(0.5),
94+
display: "block",
95+
},
96+
97+
// Adds some left spacing
98+
agentColumn: {
99+
paddingLeft: `${theme.spacing(2)}px !important`,
100+
},
101+
102+
operatingSystem: {
103+
fontSize: 14,
104+
color: theme.palette.text.secondary,
105+
marginTop: theme.spacing(0.5),
106+
display: "block",
107+
textTransform: "capitalize",
108+
},
109+
}))
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { makeStyles } from "@material-ui/core/styles"
2+
import dayjs from "dayjs"
3+
import React from "react"
4+
import { Template, TemplateVersion } from "../../api/typesGenerated"
5+
import { CardRadius, MONOSPACE_FONT_FAMILY } from "../../theme/constants"
6+
7+
const Language = {
8+
usedByLabel: "Used by",
9+
activeVersionLabel: "Active version",
10+
lastUpdateLabel: "Last updated",
11+
userPlural: "users",
12+
userSingular: "user",
13+
}
14+
15+
export interface TemplateStatsProps {
16+
template: Template
17+
activeVersion: TemplateVersion
18+
}
19+
20+
export const TemplateStats: React.FC<TemplateStatsProps> = ({ template, activeVersion }) => {
21+
const styles = useStyles()
22+
23+
return (
24+
<div className={styles.stats}>
25+
<div className={styles.statItem}>
26+
<span className={styles.statsLabel}>{Language.usedByLabel}</span>
27+
28+
<span className={styles.statsValue}>
29+
{template.workspace_owner_count}{" "}
30+
{template.workspace_owner_count === 1 ? Language.userSingular : Language.userPlural}
31+
</span>
32+
</div>
33+
<div className={styles.statsDivider} />
34+
<div className={styles.statItem}>
35+
<span className={styles.statsLabel}>{Language.activeVersionLabel}</span>
36+
<span className={styles.statsValue}>{activeVersion.name}</span>
37+
</div>
38+
<div className={styles.statsDivider} />
39+
<div className={styles.statItem}>
40+
<span className={styles.statsLabel}>{Language.lastUpdateLabel}</span>
41+
<span className={styles.statsValue} data-chromatic="ignore">
42+
{dayjs().to(dayjs(template.updated_at))}
43+
</span>
44+
</div>
45+
</div>
46+
)
47+
}
48+
49+
const useStyles = makeStyles((theme) => ({
50+
stats: {
51+
paddingLeft: theme.spacing(2),
52+
paddingRight: theme.spacing(2),
53+
backgroundColor: theme.palette.background.paper,
54+
borderRadius: CardRadius,
55+
display: "flex",
56+
alignItems: "center",
57+
color: theme.palette.text.secondary,
58+
fontFamily: MONOSPACE_FONT_FAMILY,
59+
border: `1px solid ${theme.palette.divider}`,
60+
},
61+
62+
statItem: {
63+
minWidth: theme.spacing(20),
64+
padding: theme.spacing(2),
65+
paddingTop: theme.spacing(1.75),
66+
},
67+
68+
statsLabel: {
69+
fontSize: 12,
70+
textTransform: "uppercase",
71+
display: "block",
72+
fontWeight: 600,
73+
},
74+
75+
statsValue: {
76+
fontSize: 16,
77+
marginTop: theme.spacing(0.25),
78+
display: "inline-block",
79+
},
80+
81+
statsDivider: {
82+
width: 1,
83+
height: theme.spacing(5),
84+
backgroundColor: theme.palette.divider,
85+
marginRight: theme.spacing(2),
86+
},
87+
}))

site/src/hooks/useOrganizationID.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { useActor } from "@xstate/react"
2+
import { useContext } from "react"
3+
import { XServiceContext } from "../xServices/StateContext"
4+
5+
export const useOrganizationID = (): string => {
6+
const xServices = useContext(XServiceContext)
7+
const [authState] = useActor(xServices.authXService)
8+
const organizationId = authState.context.me?.organization_ids[0]
9+
10+
if (!organizationId) {
11+
throw new Error("No organization ID found")
12+
}
13+
14+
return organizationId
15+
}

site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,13 @@
1-
import { useActor, useMachine } from "@xstate/react"
2-
import React, { useContext } from "react"
1+
import { useMachine } from "@xstate/react"
2+
import React from "react"
33
import { useNavigate, useSearchParams } from "react-router-dom"
44
import { Template } from "../../api/typesGenerated"
5+
import { useOrganizationID } from "../../hooks/useOrganizationID"
56
import { createWorkspaceMachine } from "../../xServices/createWorkspace/createWorkspaceXService"
6-
import { XServiceContext } from "../../xServices/StateContext"
77
import { CreateWorkspacePageView } from "./CreateWorkspacePageView"
88

9-
const useOrganizationId = () => {
10-
const xServices = useContext(XServiceContext)
11-
const [authState] = useActor(xServices.authXService)
12-
const organizationId = authState.context.me?.organization_ids[0]
13-
14-
if (!organizationId) {
15-
throw new Error("No organization ID found")
16-
}
17-
18-
return organizationId
19-
}
20-
219
const CreateWorkspacePage: React.FC = () => {
22-
const organizationId = useOrganizationId()
10+
const organizationId = useOrganizationID()
2311
const [searchParams] = useSearchParams()
2412
const preSelectedTemplateName = searchParams.get("template")
2513
const navigate = useNavigate()

0 commit comments

Comments
 (0)