Skip to content

Commit 0b4cdea

Browse files
committed
fix: Make the workspace URL pretty
This adds the `@username/workspacename` format to the workspace page!
1 parent 1a39931 commit 0b4cdea

File tree

6 files changed

+39
-34
lines changed

6 files changed

+39
-34
lines changed

site/src/AppRouter.tsx

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -65,25 +65,6 @@ export const AppRouter: FC = () => (
6565
</RequireAuth>
6666
}
6767
/>
68-
69-
<Route path=":workspace">
70-
<Route
71-
index
72-
element={
73-
<AuthAndFrame>
74-
<WorkspacePage />
75-
</AuthAndFrame>
76-
}
77-
/>
78-
<Route
79-
path="schedule"
80-
element={
81-
<RequireAuth>
82-
<WorkspaceSchedulePage />
83-
</RequireAuth>
84-
}
85-
/>
86-
</Route>
8768
</Route>
8869

8970
<Route path="templates">
@@ -142,6 +123,23 @@ export const AppRouter: FC = () => (
142123

143124
<Route path="/@:username">
144125
<Route path=":workspace">
126+
<Route
127+
index
128+
element={
129+
<AuthAndFrame>
130+
<WorkspacePage />
131+
</AuthAndFrame>
132+
}
133+
/>
134+
<Route
135+
path="schedule"
136+
element={
137+
<RequireAuth>
138+
<WorkspaceSchedulePage />
139+
</RequireAuth>
140+
}
141+
/>
142+
145143
<Route
146144
path="terminal"
147145
element={

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

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ import { WorkspacePage } from "./WorkspacePage"
2727

2828
// It renders the workspace page and waits for it be loaded
2929
const renderWorkspacePage = async () => {
30-
renderWithAuth(<WorkspacePage />, { route: `/workspaces/${MockWorkspace.id}`, path: "/workspaces/:workspace" })
30+
renderWithAuth(<WorkspacePage />, {
31+
route: `/@${MockWorkspace.owner_name}/${MockWorkspace.name}`,
32+
path: "/@:username/:workspace",
33+
})
3134
await screen.findByText(MockWorkspace.name)
3235
}
3336

@@ -47,7 +50,7 @@ const testButton = async (label: string, actionMock: jest.SpyInstance) => {
4750

4851
const testStatus = async (mock: Workspace, label: string) => {
4952
server.use(
50-
rest.get(`/api/v2/workspaces/${MockWorkspace.id}`, (req, res, ctx) => {
53+
rest.get(`/api/v2/users/:username/workspace/:workspaceName`, (req, res, ctx) => {
5154
return res(ctx.status(200), ctx.json(mock))
5255
}),
5356
)
@@ -87,7 +90,7 @@ describe("Workspace Page", () => {
8790
})
8891
it("requests a start job when the user presses Start", async () => {
8992
server.use(
90-
rest.get(`/api/v2/workspaces/${MockWorkspace.id}`, (req, res, ctx) => {
93+
rest.get(`/api/v2/users/:userId/workspace/:workspaceName`, (req, res, ctx) => {
9194
return res(ctx.status(200), ctx.json(MockStoppedWorkspace))
9295
}),
9396
)
@@ -98,7 +101,7 @@ describe("Workspace Page", () => {
98101
})
99102
it("requests cancellation when the user presses Cancel", async () => {
100103
server.use(
101-
rest.get(`/api/v2/workspaces/${MockWorkspace.id}`, (req, res, ctx) => {
104+
rest.get(`/api/v2/users/:userId/workspace/:workspaceName`, (req, res, ctx) => {
102105
return res(ctx.status(200), ctx.json(MockStartingWorkspace))
103106
}),
104107
)
@@ -110,7 +113,7 @@ describe("Workspace Page", () => {
110113
it("requests a template when the user presses Update", async () => {
111114
const getTemplateMock = jest.spyOn(api, "getTemplate").mockResolvedValueOnce(MockTemplate)
112115
server.use(
113-
rest.get(`/api/v2/workspaces/${MockWorkspace.id}`, (req, res, ctx) => {
116+
rest.get(`/api/v2/users/:userId/workspace/:workspaceName`, (req, res, ctx) => {
114117
return res(ctx.status(200), ctx.json(MockOutdatedWorkspace))
115118
}),
116119
)
@@ -159,7 +162,10 @@ describe("Workspace Page", () => {
159162

160163
describe("Resources", () => {
161164
it("shows the status of each agent in each resource", async () => {
162-
renderWithAuth(<WorkspacePage />, { route: `/workspaces/${MockWorkspace.id}`, path: "/workspaces/:workspace" })
165+
renderWithAuth(<WorkspacePage />, {
166+
route: `/@${MockWorkspace.owner_name}/${MockWorkspace.name}`,
167+
path: "/@:username/:workspace",
168+
})
163169
const agent1Names = await screen.findAllByText(MockWorkspaceAgent.name)
164170
expect(agent1Names.length).toEqual(2)
165171
const agent2Names = await screen.findAllByText(MockWorkspaceAgentDisconnected.name)

site/src/pages/WorkspacePage/WorkspacePage.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ import { workspaceMachine } from "../../xServices/workspace/workspaceXService"
1414
import { workspaceScheduleBannerMachine } from "../../xServices/workspaceSchedule/workspaceScheduleBannerXService"
1515

1616
export const WorkspacePage: React.FC = () => {
17-
const { workspace: workspaceQueryParam } = useParams()
17+
const { username: usernameQueryParam, workspace: workspaceQueryParam } = useParams()
1818
const navigate = useNavigate()
19-
const workspaceId = firstOrItem(workspaceQueryParam, null)
19+
const username = firstOrItem(usernameQueryParam, null)
20+
const workspaceName = firstOrItem(workspaceQueryParam, null)
2021

2122
const [workspaceState, workspaceSend] = useMachine(workspaceMachine)
2223
const { workspace, resources, getWorkspaceError, getResourcesError, builds } = workspaceState.context
@@ -28,8 +29,8 @@ export const WorkspacePage: React.FC = () => {
2829
* workspaceSend should not change.
2930
*/
3031
useEffect(() => {
31-
workspaceId && workspaceSend({ type: "GET_WORKSPACE", workspaceId })
32-
}, [workspaceId, workspaceSend])
32+
username && workspaceName && workspaceSend({ type: "GET_WORKSPACE", username, workspaceName })
33+
}, [username, workspaceName, workspaceSend])
3334

3435
if (workspaceState.matches("error")) {
3536
return <ErrorSummary error={getWorkspaceError} />

site/src/pages/WorkspacesPage/WorkspacesPageView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export const WorkspacesPageView: FC<WorkspacesPageViewProps> = ({ loading, works
7474
<AvatarData
7575
title={workspace.name}
7676
subtitle={workspace.owner_name}
77-
link={`/workspaces/${workspace.id}`}
77+
link={`/@${workspace.owner_name}/${workspace.name}`}
7878
/>
7979
</TableCell>
8080
<TableCell>{workspace.template_name}</TableCell>

site/src/xServices/workspace/workspaceXService.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export interface WorkspaceContext {
3737
}
3838

3939
export type WorkspaceEvent =
40-
| { type: "GET_WORKSPACE"; workspaceId: string }
40+
| { type: "GET_WORKSPACE"; workspaceName: string; username: string }
4141
| { type: "START" }
4242
| { type: "STOP" }
4343
| { type: "ASK_DELETE" }
@@ -431,7 +431,7 @@ export const workspaceMachine = createMachine(
431431
},
432432
services: {
433433
getWorkspace: async (_, event) => {
434-
return await API.getWorkspace(event.workspaceId)
434+
return await API.getWorkspaceByOwnerAndName(event.username, event.workspaceName)
435435
},
436436
getTemplate: async (context) => {
437437
if (context.workspace) {
@@ -470,7 +470,7 @@ export const workspaceMachine = createMachine(
470470
},
471471
refreshWorkspace: async (context) => {
472472
if (context.workspace) {
473-
return await API.getWorkspace(context.workspace.id)
473+
return await API.getWorkspaceByOwnerAndName(context.workspace.owner_name, context.workspace.name)
474474
} else {
475475
throw Error("Cannot refresh workspace without id")
476476
}

site/webpack.dev.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ const config: Configuration = {
6363
port: process.env.PORT || 8080,
6464
proxy: {
6565
"/api": {
66-
target: "http://localhost:3000",
66+
target: "https://dev.coder.com",
6767
ws: true,
6868
secure: false,
6969
},

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy