-
Notifications
You must be signed in to change notification settings - Fork 875
chore: refactor frontend to use workspace status directly #4361
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
Conversation
Just spotted a bug in the deletion banner, will fix! |
<AlertTitle>{Language.bannerTitle}</AlertTitle> | ||
</Alert> | ||
<Maybe condition={workspace.latest_build.status === "deleted"}> | ||
<Alert |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No change to the Alert
here, just wrapping in a Maybe
and getting rid of the isWorkspaceDeleted
call
import * as TypesGen from "../api/typesGenerated" | ||
|
||
dayjs.extend(duration) | ||
dayjs.extend(utc) | ||
dayjs.extend(minMax) | ||
|
||
// all the possible states returned by the API |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The backend gives us the WorkspaceStatus
type and the actual workspace status now, so we can delete this. The display language is deleted just because it's moved into i18n.
) | ||
expect(screen.getByTestId("secondary-ctas")).toHaveTextContent( | ||
t("actionButton.delete", { ns: "workspacePage" }), | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for doing all these translations!!!
|
||
// A mapping of workspace state to button type | ||
// 'Primary' actions are the main ctas | ||
// 'Secondary' actions are ctas housed within the popover | ||
export const WorkspaceStateActions: StateActionsType = { | ||
[WorkspaceStateEnum.starting]: { | ||
starting: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One of the reasons we made WorkspaceStateEnum
was so that we could have a definitive mapping of the state returned from the BE to button UI state. Although this simplification makes my heart sing, I wonder if we're losing a little bit of that clarity and safety here. It is no longer immediately apparent that the keys in this data structure map to the types in the API's WorkspaceStatus
. And the keys here aren't really typed anymore, right? This is a case where I really love enums and wish the API were returning an enum. It would make indexing much easier. @presleyp or @jsjoeio are there any easy workarounds with string literals I'm missing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's still typesafe because the keys have to be values of WorkspaceStatus
. I could be misremembering where I did this check, but I think it complained if I added a key that didn't belong or left one out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahhhhh, I didn't even see that. You're right - still typesafe! Feel free to ignore the clarify part of my comment - I'm just still figuring out where I land on this whole enum literal debate :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Me too honestly! Just following the generated type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point! I left a comment above related to this.
"starting": "Starting...", | ||
"stopping": "Stopping...", | ||
"deleting": "Deleting..." | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same sort of comment here: I like linking the language to the actual typing that's coming back from the API - that way we get a 1:1 mapping of BE state and UI. In the past, I've managed with constants like:
export const ActionButtonLabels = {
[WorkspaceStatus.starting]: i18n.t('workspacePage:actionButton.starting'),
[WorkspaceStatus.stopping]: i18n.t('workspacePage:actionButton.stopping'),
etc...
} as const;
But that was with enums. @presleyp or @jsjoeio again, is there a way to do this with literals? I feel like I could come over to the dark side if there is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could define this but with "starting" instead of [WorkspaceStatus.starting]
, which is not as clear that it's typesafe, but is typesafe. And then we'd know that every value of WorkspaceStatus
mapped to some text. How's that? (@jsjoeio def interested in your input on all of this when you're available!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would be nice, if you feel it's a good add. Not a dealbreaker if you don't!
Also yeah, so curious to hear if there's a way to directly index.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ended up just changing the way I handle text for the disabled buttons, so that it's not a dynamic lookup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure how i18n
could let us pull in TypeScript types 🤔 Ahhh I see @Kira-Pilot's comment above. I think that's a great idea! Happy to look into this after this PR is merged too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work! 👍
@@ -23,7 +22,7 @@ WithDropdown.args = { | |||
|
|||
export const WithCancel = Template.bind({}) | |||
WithCancel.args = { | |||
primaryAction: <DisabledButton workspaceState={WorkspaceStateEnum.deleting} />, | |||
primaryAction: <DisabledButton workspaceStatus="deleting" />, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Curious why we're no longer using the enum here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The WorkspaceStatus type is defined by the backend now
@@ -18,7 +17,7 @@ import { ButtonMapping, ButtonTypesEnum, WorkspaceStateActions } from "./constan | |||
* so check whether workspace job status has reached completion (whether successful or not). | |||
*/ | |||
const canAcceptJobs = (workspaceStatus: WorkspaceStatus) => | |||
["started", "stopped", "deleted", "error", "canceled"].includes(workspaceStatus) | |||
["running", "stopped", "deleted", "failed", "canceled"].includes(workspaceStatus) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch! This approach makes it easy to fall out of sync with valid WorkspaceStatus
values. Here's one way we could fix this:
const CAN_ACCEPT_JOBS_STATUS: Array<
Extract<
WorkspaceStatus,
"running" | "stopped" | "deleted" | "failed" | "canceled"
>
> = ["running", "stopped", "deleted", "failed", "canceled"];
const canAcceptJobs = (workspaceStatus: WorkspaceStatus) => CAN_ACCEPT_JOBS_STATUS.includes(workspaceStatus)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm actually fixing this in a future refactor, thanks for pointing it out!
import { ReactNode } from "react" | ||
import { WorkspaceStateEnum } from "util/workspace" | ||
|
||
// the button types we have | ||
export enum ButtonTypesEnum { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume these are based on the WorkspaceStatus
? We could leverage those types here as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are similar to WorkspaceStatus
but not identical.
|
||
// A mapping of workspace state to button type | ||
// 'Primary' actions are the main ctas | ||
// 'Secondary' actions are ctas housed within the popover | ||
export const WorkspaceStateActions: StateActionsType = { | ||
[WorkspaceStateEnum.starting]: { | ||
starting: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point! I left a comment above related to this.
"starting": "Starting...", | ||
"stopping": "Stopping...", | ||
"deleting": "Deleting..." | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure how i18n
could let us pull in TypeScript types 🤔 Ahhh I see @Kira-Pilot's comment above. I think that's a great idea! Happy to look into this after this PR is merged too.
} | ||
export const MockStoppingWorkspace: TypesGen.Workspace = { | ||
...MockWorkspace, | ||
latest_build: { | ||
...MockWorkspaceBuildStop, | ||
job: MockRunningProvisionerJob, | ||
status: "stopping", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ideally we're pulling these values from the types that way they stay in sync
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, since the generated type is a union of strings, I don't know how to do that. Do you have a way? Or would I just have to create an enum to layer on top of the generated type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm...we might have to dig in. At first glance, I'm not sure. I mean you do have the TypesGen.Workspace
there so maybe we don't have to worry. We could look later!
Since CI was stuck, I went ahead and added my additional refactor to this PR. It puts |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work!
The frontend used to derive the workspace status from the build transition and job status, but now the backend supplies the status. This allows us to delete code and get rid of some indirection in one of the most complex areas of the frontend.
loading
workspace status or action button because the workspace status is loaded with the workspace, and these things aren't rendered if the workspace is still loading (feel free to double check me!)