Skip to content

feat: Add timeline in the workspace page #1533

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
May 18, 2022
Next Next commit
Add base structure
  • Loading branch information
BrunoQuaresma committed May 16, 2022
commit 339e8fead4b3ee676e448588cfa06353e5340c3c
5 changes: 5 additions & 0 deletions site/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,3 +216,8 @@ export const regenerateUserSSHKey = async (userId = "me"): Promise<TypesGen.GitS
const response = await axios.put<TypesGen.GitSSHKey>(`/api/v2/users/${userId}/gitsshkey`)
return response.data
}

export const getWorkspaceBuilds = async (workspaceId: string): Promise<TypesGen.WorkspaceBuild[]> => {
const response = await axios.get<TypesGen.WorkspaceBuild[]>(`/api/v2/workspaces/${workspaceId}/builds`)
return response.data
}
64 changes: 64 additions & 0 deletions site/src/components/BuildsTable/BuildsTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import Box from "@material-ui/core/Box"
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 * as TypesGen from "../../api/typesGenerated"
import { EmptyState } from "../EmptyState/EmptyState"
import { TableHeaderRow } from "../TableHeaders/TableHeaders"
import { TableLoader } from "../TableLoader/TableLoader"

export const Language = {
pageTitle: "Builds",
usersTitle: "All users",
emptyMessage: "No users found",
usernameLabel: "User",
suspendMenuItem: "Suspend",
resetPasswordMenuItem: "Reset password",
rolesLabel: "Roles",
}

export interface BuildsTableProps {
builds?: TypesGen.WorkspaceBuild[]
}

export const BuildsTable: React.FC<BuildsTableProps> = ({ builds }) => {
const isLoading = !builds

return (
<Table>
<TableHead>
<TableHeaderRow>
<TableCell size="small">Action</TableCell>
<TableCell size="small">Duration</TableCell>
<TableCell size="small">Started at</TableCell>
<TableCell size="small">Status</TableCell>
</TableHeaderRow>
</TableHead>
<TableBody>
{isLoading && <TableLoader />}
{builds &&
builds.map((b) => (
<TableRow key={b.id}>
<TableCell>{b.transition}</TableCell>
<TableCell>{b.created_at}</TableCell>
<TableCell>{b.created_at}</TableCell>
<TableCell>{b.job.status}</TableCell>
</TableRow>
))}

{builds && builds.length === 0 && (
<TableRow>
<TableCell colSpan={999}>
<Box p={4}>
<EmptyState message="No builds for this workspace" />
</Box>
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
)
}
10 changes: 4 additions & 6 deletions site/src/components/Workspace/Workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 { BuildsTable } from "../BuildsTable/BuildsTable"
import { WorkspaceSchedule } from "../WorkspaceSchedule/WorkspaceSchedule"
import { WorkspaceSection } from "../WorkspaceSection/WorkspaceSection"
import { WorkspaceStatusBar } from "../WorkspaceStatusBar/WorkspaceStatusBar"
Expand All @@ -16,6 +17,7 @@ export interface WorkspaceProps {
handleRetry: () => void
handleUpdate: () => void
workspaceStatus: WorkspaceStatus
builds?: TypesGen.WorkspaceBuild[]
}

/**
Expand All @@ -30,6 +32,7 @@ export const Workspace: React.FC<WorkspaceProps> = ({
handleRetry,
handleUpdate,
workspaceStatus,
builds,
}) => {
const styles = useStyles()

Expand Down Expand Up @@ -61,12 +64,7 @@ export const Workspace: React.FC<WorkspaceProps> = ({
</div>
<div className={styles.timelineContainer}>
<WorkspaceSection title="Timeline">
<div
className={styles.vertical}
style={{ justifyContent: "center", alignItems: "center", height: "300px" }}
>
<Placeholder />
</div>
<BuildsTable builds={builds} />
</WorkspaceSection>
</div>
</div>
Expand Down
3 changes: 2 additions & 1 deletion site/src/pages/WorkspacePage/WorkspacePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const WorkspacePage: React.FC = () => {

const xServices = useContext(XServiceContext)
const [workspaceState, workspaceSend] = useActor(xServices.workspaceXService)
const { workspace, template, organization, getWorkspaceError, getTemplateError, getOrganizationError } =
const { workspace, template, organization, getWorkspaceError, getTemplateError, getOrganizationError, builds } =
workspaceState.context
const workspaceStatus = useSelector(xServices.workspaceXService, selectWorkspaceStatus)

Expand Down Expand Up @@ -56,6 +56,7 @@ export const WorkspacePage: React.FC = () => {
handleRetry={() => workspaceSend("RETRY")}
handleUpdate={() => workspaceSend("UPDATE")}
workspaceStatus={workspaceStatus}
builds={builds}
/>
</Stack>
</Margins>
Expand Down
101 changes: 101 additions & 0 deletions site/src/xServices/workspace/workspaceXService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export interface WorkspaceContext {
// these are separate from getX errors because they don't make the page unusable
refreshWorkspaceError: Error | unknown
refreshTemplateError: Error | unknown
// Builds
builds?: TypesGen.WorkspaceBuild[]
getBuildsError?: Error | unknown
loadMoreBuildsError?: Error | unknown
}

export type WorkspaceEvent =
Expand All @@ -29,6 +33,7 @@ export type WorkspaceEvent =
| { type: "STOP" }
| { type: "RETRY" }
| { type: "UPDATE" }
| { type: "LOAD_MORE_BUILDS" }
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will implement this when the BE is done


export const workspaceMachine = createMachine(
{
Expand All @@ -55,6 +60,12 @@ export const workspaceMachine = createMachine(
refreshWorkspace: {
data: TypesGen.Workspace | undefined
}
getBuilds: {
data: TypesGen.WorkspaceBuild[]
}
loadMoreBuilds: {
data: TypesGen.WorkspaceBuild[]
}
},
},
id: "workspaceState",
Expand Down Expand Up @@ -200,6 +211,54 @@ export const workspaceMachine = createMachine(
},
},
},

builds: {
initial: "gettingBuilds",
states: {
idle: {},
gettingBuilds: {
entry: "clearGetBuildsError",
invoke: {
src: "getBuilds",
onDone: {
actions: ["assignBuilds"],
target: "loadedBuilds",
},
onError: {
actions: ["assignGetBuildsError"],
target: "idle",
},
},
},
loadedBuilds: {
initial: "idle",
states: {
idle: {
on: {
LOAD_MORE_BUILDS: {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this for pagination?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeap! To load more/old builds.

target: "loadingMoreBuilds",
cond: "hasMoreBuilds",
},
},
},
loadingMoreBuilds: {
entry: "clearLoadMoreBuildsError",
invoke: {
src: "loadMoreBuilds",
onDone: {
actions: ["assignNewBuilds"],
target: "idle",
},
onError: {
actions: ["assignLoadMoreBuildsError"],
target: "idle",
},
},
},
},
},
},
},
},
},
error: {
Expand Down Expand Up @@ -274,9 +333,37 @@ export const workspaceMachine = createMachine(
assign({
refreshTemplateError: undefined,
}),
// Builds
assignBuilds: assign({
builds: (_, event) => event.data,
}),
assignGetBuildsError: assign({
getBuildsError: (_, event) => event.data,
}),
clearGetBuildsError: assign({
getBuildsError: (_) => undefined,
}),
assignNewBuilds: assign({
builds: (context, event) => {
const oldBuilds = context.builds

if (!oldBuilds) {
throw new Error("Builds not loaded")
}

return [...oldBuilds, ...event.data]
},
}),
assignLoadMoreBuildsError: assign({
loadMoreBuildsError: (_, event) => event.data,
}),
clearLoadMoreBuildsError: assign({
loadMoreBuildsError: (_) => undefined,
}),
},
guards: {
triedToStart: (context) => context.workspace?.latest_build.transition === "start",
hasMoreBuilds: (_) => false,
},
services: {
getWorkspace: async (_, event) => {
Expand Down Expand Up @@ -317,6 +404,20 @@ export const workspaceMachine = createMachine(
throw Error("Cannot refresh workspace without id")
}
},
getBuilds: async (context) => {
if (context.workspace) {
return await API.getWorkspaceBuilds(context.workspace.id)
} else {
throw Error("Cannot refresh workspace without id")
}
},
loadMoreBuilds: async (context) => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will change when the BE is ready.

if (context.workspace) {
return await API.getWorkspaceBuilds(context.workspace.id)
} else {
throw Error("Cannot refresh workspace without id")
}
},
},
},
)