-
Notifications
You must be signed in to change notification settings - Fork 887
feat: add extra workspace actions in the workspaces table #17775
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
@@ -221,8 +221,9 @@ const DownloadingItem: FC<DownloadingItemProps> = ({ file, giveUpTimeMs }) => { | |||
function humanBlobSize(size: number) { | |||
const BLOB_SIZE_UNITS = ["B", "KB", "MB", "GB", "TB"] as const; | |||
let i = 0; | |||
while (size > 1024 && i < BLOB_SIZE_UNITS.length) { | |||
size /= 1024; | |||
let sizeIterator = size; |
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
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 the biggest fan of changing parameters either, but in this case, this would never be a bug, since numbers are primitive values
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 have to say it was a Biome catch haha.
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.
Nothing major, but I do think there are some bugs and edge cases we need to address. I also left some comments about things that I think we really should figure out pretty soon (even if they don't happen in this PR)
@@ -482,10 +482,10 @@ class ApiMethods { | |||
return response.data; | |||
}; | |||
|
|||
checkAuthorization = async ( | |||
checkAuthorization = async <TResponse extends TypesGen.AuthorizationResponse>( |
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 concerned about why we have a generic for a function that checks anything auth-based. Does the server really give back multiple return types based on what parameters we give it?
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.
Also, I'm a little concerned that with how the function is set up right now, we force anyone calling this function to pass in the correct type parameter. To some degree that's okay, since we have such a specific type constraint on here, I have to wonder if this is asking for trouble, not just as far as complexity, but also in terms of some other employee passing the wrong type in a few months from now
Two things come to mind:
- I'm wondering if there's a way to redefine
params
so that we can infer the return type automatically. With less need to pass in types manually, there's less room for things to go wrong - I'm also wondering if it's worth updating the generic definition to
TResponse extends TypesGen.AuthorizationResponse = TypesGen.AuthorizationResponse
. That way, most users get back the baseAuthorizationResponse
type by default (which I'd imagine would be okay 99% of the time, since we haven't needed this constraint until now), and then if someone needs something more specific, they have the ability to pass in the type, and override the default return value
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 is not possible right now since the response is dynamic and depends on the request parameters. However, I do agree it would be better to have something more predictable to avoid these kinda of errors.
@@ -6,6 +6,7 @@ export const deploymentConfig = () => { | |||
return { | |||
queryKey: deploymentConfigQueryKey, | |||
queryFn: API.getDeploymentConfig, | |||
staleTime: Number.POSITIVE_INFINITY, |
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.
Just making sure: are we guaranteed that the config won't ever change after the initial request from the server? I know we already disable a lot of the RQ background refetch functionality, so I'm wondering if there's any harm in letting the data be marked as stale, since I'd think the data would still be refetched relatively infrequently
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.
Yeap! To change these deployment configs even coderd would need to be restarted 😄
@@ -390,3 +395,14 @@ export const workspaceUsage = (options: WorkspaceUsageOptions) => { | |||
refetchIntervalInBackground: true, | |||
}; | |||
}; | |||
|
|||
export const workspacePermissions = (workspace?: Workspace) => { |
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.
Could we add an explicit return type here? There's enough going on that I think it'd help communicate what this is supposed to be
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 don't see too much value on that since it is dynamic and other parts of the application would throw an error. But, if it is a must for you, I can do it for sure.
defaultTemplateVersion, | ||
...dialogProps | ||
}) => { | ||
export const ChangeWorkspaceVersionDialog: FC< |
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 don't think it's worth blocking on this, but I think it's worth thinking about for the future: do we want to limit the number of versions we show by default? Right now, we're loading everything, which makes the UI chug for a second, and I think that realistically, most users aren't going to be switching to anything that's more than 10 versions older than the most recent one
We have a lot of versions from the past two years, and I could see some customers having more than us, just because their companies are so much bigger
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 totally agree with you, but I have to say it is not on the scope for this PR. Feel free to open an issue tho
export const ShortMessage: Story = { | ||
args: { | ||
workspace: { | ||
...MockWorkspace, | ||
template_active_version_id: MockTemplateVersion.id, | ||
}, | ||
}, | ||
}; | ||
|
||
export const LongMessage: Story = { | ||
args: { | ||
workspace: { | ||
...MockWorkspace, | ||
template_active_version_id: MockTemplateVersionWithMarkdownMessage.id, | ||
}, | ||
}, | ||
}; |
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.
Could we rename these stories? When I'm looking at them in the list of stories, it's not clear why they're different, especially when the only input that changed is a template ID
Maybe something like PlaintextMessage
and MarkdownMessage
?
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.
Sure, just be aware it is not in the scope of this PR. It was just a renaming.
disabled={disabled} | ||
> | ||
<EllipsisVertical aria-hidden="true" /> | ||
<span className="sr-only">Workspace actions</span> |
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.
Appreciate the screenreader support text!
}; | ||
|
||
const isMissingBuildParameters = (e: unknown): e is MissingBuildParameters => { | ||
return Boolean(e && e instanceof MissingBuildParameters); |
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 don't think we need this function. We can get rid of the Boolean(e &&)
part, because:
instanceof
works with null and undefined values no problem – they'd just always evaluate tofalse
instanceof
always evaluates to a boolean
So this can be cleaned up to
const isMissingBuildParameters = (e: unknown): e is MissingBuildParameters => {
return e instanceof MissingBuildParameters;
};
And that point, I think we can get away with just using the instanceof
operator directly. It still has support for type-narrowing, but unlike a custom predicate function, there's no risk of getting the predicate logic wrong, and accidentally lying to the type system
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 is true 👍
{ canDebug, isOwner }, | ||
{ | ||
canDebug: !!deployment?.config.enable_terraform_debug_mode, | ||
isOwner: !!user.roles.find((role) => role.name === "owner"), |
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.
This can be changed to user.roles.some((role) => role.name === "owner")
, which is a little more clear, and will have less memory usage
@@ -50,14 +50,13 @@ const WorkspaceParametersPage: FC = () => { | |||
const template = templateQuery.data; | |||
|
|||
// Permissions | |||
const checks = | |||
workspace && template ? workspaceChecks(workspace, template) : {}; | |||
const checks = workspace && template ? workspaceChecks(workspace) : {}; |
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.
If we're only using the workspace to define the checks, do we still need template
to be part of the ternary?
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.
Not at all!
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 less query 👏
<TableCell | ||
onClick={(e) => { | ||
// Prevent the click in the actions to trigger the row click | ||
e.stopPropagation(); | ||
}} | ||
> |
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.
This is something that I was going to bring up in the comment for submitting the review, but think this is a better place. I really think that we need to go back to the drawing board on the UI, because I don't think the way it works is obvious anymore:
For a new user, I don't think the row is very clear with these changes. The little arrow at least gave the user a place to click (even though the whole card was valid), but now, they have to just know that clicking the background will "magically" do something
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.
Copying the comments from the image, just in case the image ever gets deleted down the line:
- Red areas are interactive [referring to the checkbox on the left, as well as all the agent actions + MoreActions button on the right]
- The logos for the template and user look exactly the same as the interactive buttons on the right, but have zero interactivity
- You have to know to click the "background" of a card (which has no visual indicators that it's interactive, especially since this PR removes the arrow) to go to the Workspace Details page
As a bandaid, since it seems like we're pushing users to do more operations from the workspaces table row rather than the whole details page, I'm wondering if we could remove the link on the row entirely, and add a new option to the MoreActions menu
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.
And to be clear, I'm okay with addressing that in a separate PR, but I think this PR is degrading the UX in some deceptively big ways. Even though it's a minor aesthetic change, it's affecting one of the biggest user flows in the entire product
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 this PR is degrading the UX in some deceptively big ways.
I kinda disagree with that, even agreeing with your points. Until we have data, it is just an opinion (I think Christin put some thoughts on that, so I'm kinda comfortable to move forward), so we only can verify that when ppl start using it.
IMO, the link should be in the workspace name only as GitHub does with issues table, and many other products. I'm against clickable rows in general because things like this.
@Parkreiner I think I solved the most critical issues you pointed out, so I'm merging the PR, but please, let me know if I forgot anything or if you want to keep the discussions, I'm totally open for it. |
Demo:
