Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitd65eea8

Browse files
stirbyaslilacangrycub
authored
fix: fix workspace actions options (#13572) (#14071)
* fix: fix workspace actions options (#13572)(cherry picked from commit07cd9ac)* fix: change time format string from 15:40 to 15:04 (#14033)* Change string format to constant value(cherry picked from commiteacdfb9)---------Co-authored-by: Kayla Washburn-Love <mckayla@hey.com>Co-authored-by: Charlie Voiselle <464492+angrycub@users.noreply.github.com>
1 parent2f0c2d7 commitd65eea8

File tree

7 files changed

+153
-109
lines changed

7 files changed

+153
-109
lines changed

‎enterprise/coderd/users.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import (
1414
"github.com/coder/coder/v2/codersdk"
1515
)
1616

17+
constTimeFormatHHMM="15:04"
18+
1719
func (api*API)autostopRequirementEnabledMW(next http.Handler) http.Handler {
1820
returnhttp.HandlerFunc(func(rw http.ResponseWriter,r*http.Request) {
1921
// Entitlement must be enabled.
@@ -66,7 +68,7 @@ func (api *API) userQuietHoursSchedule(rw http.ResponseWriter, r *http.Request)
6668
RawSchedule:opts.Schedule.String(),
6769
UserSet:opts.UserSet,
6870
UserCanSet:opts.UserCanSet,
69-
Time:opts.Schedule.TimeParsed().Format("15:40"),
71+
Time:opts.Schedule.TimeParsed().Format(TimeFormatHHMM),
7072
Timezone:opts.Schedule.Location().String(),
7173
Next:opts.Schedule.Next(time.Now().In(opts.Schedule.Location())),
7274
})
@@ -118,7 +120,7 @@ func (api *API) putUserQuietHoursSchedule(rw http.ResponseWriter, r *http.Reques
118120
RawSchedule:opts.Schedule.String(),
119121
UserSet:opts.UserSet,
120122
UserCanSet:opts.UserCanSet,
121-
Time:opts.Schedule.TimeParsed().Format("15:40"),
123+
Time:opts.Schedule.TimeParsed().Format(TimeFormatHHMM),
122124
Timezone:opts.Schedule.Location().String(),
123125
Next:opts.Schedule.Next(time.Now().In(opts.Schedule.Location())),
124126
})

‎enterprise/coderd/users_test.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@ import (
1111
"github.com/coder/coder/v2/coderd/coderdtest"
1212
"github.com/coder/coder/v2/coderd/schedule/cron"
1313
"github.com/coder/coder/v2/codersdk"
14+
"github.com/coder/coder/v2/enterprise/coderd"
1415
"github.com/coder/coder/v2/enterprise/coderd/coderdenttest"
1516
"github.com/coder/coder/v2/enterprise/coderd/license"
1617
"github.com/coder/coder/v2/testutil"
1718
)
1819

20+
constTimeFormatHHMM=coderd.TimeFormatHHMM
21+
1922
funcTestUserQuietHours(t*testing.T) {
2023
t.Parallel()
2124

@@ -41,15 +44,17 @@ func TestUserQuietHours(t *testing.T) {
4144

4245
t.Run("OK",func(t*testing.T) {
4346
t.Parallel()
44-
45-
defaultQuietHoursSchedule:="CRON_TZ=America/Chicago 0 1 * * *"
47+
// Using 10 for minutes lets us test a format bug in which values greater
48+
// than 5 were causing the API to explode because the time was returned
49+
// incorrectly
50+
defaultQuietHoursSchedule:="CRON_TZ=America/Chicago 10 1 * * *"
4651
defaultScheduleParsed,err:=cron.Daily(defaultQuietHoursSchedule)
4752
require.NoError(t,err)
4853
nextTime:=defaultScheduleParsed.Next(time.Now().In(defaultScheduleParsed.Location()))
4954
iftime.Until(nextTime)<time.Hour {
5055
// Use a different default schedule instead, because we want to avoid
5156
// the schedule "ticking over" during this test run.
52-
defaultQuietHoursSchedule="CRON_TZ=America/Chicago0 13 * * *"
57+
defaultQuietHoursSchedule="CRON_TZ=America/Chicago10 13 * * *"
5358
defaultScheduleParsed,err=cron.Daily(defaultQuietHoursSchedule)
5459
require.NoError(t,err)
5560
}
@@ -78,7 +83,7 @@ func TestUserQuietHours(t *testing.T) {
7883
require.NoError(t,err)
7984
require.Equal(t,defaultScheduleParsed.String(),sched1.RawSchedule)
8085
require.False(t,sched1.UserSet)
81-
require.Equal(t,defaultScheduleParsed.TimeParsed().Format("15:40"),sched1.Time)
86+
require.Equal(t,defaultScheduleParsed.TimeParsed().Format(TimeFormatHHMM),sched1.Time)
8287
require.Equal(t,defaultScheduleParsed.Location().String(),sched1.Timezone)
8388
require.WithinDuration(t,defaultScheduleParsed.Next(time.Now()),sched1.Next,15*time.Second)
8489

@@ -101,7 +106,7 @@ func TestUserQuietHours(t *testing.T) {
101106
require.NoError(t,err)
102107
require.Equal(t,customScheduleParsed.String(),sched2.RawSchedule)
103108
require.True(t,sched2.UserSet)
104-
require.Equal(t,customScheduleParsed.TimeParsed().Format("15:40"),sched2.Time)
109+
require.Equal(t,customScheduleParsed.TimeParsed().Format(TimeFormatHHMM),sched2.Time)
105110
require.Equal(t,customScheduleParsed.Location().String(),sched2.Timezone)
106111
require.WithinDuration(t,customScheduleParsed.Next(time.Now()),sched2.Next,15*time.Second)
107112

@@ -110,7 +115,7 @@ func TestUserQuietHours(t *testing.T) {
110115
require.NoError(t,err)
111116
require.Equal(t,customScheduleParsed.String(),sched3.RawSchedule)
112117
require.True(t,sched3.UserSet)
113-
require.Equal(t,customScheduleParsed.TimeParsed().Format("15:40"),sched3.Time)
118+
require.Equal(t,customScheduleParsed.TimeParsed().Format(TimeFormatHHMM),sched3.Time)
114119
require.Equal(t,customScheduleParsed.Location().String(),sched3.Timezone)
115120
require.WithinDuration(t,customScheduleParsed.Next(time.Now()),sched3.Next,15*time.Second)
116121

‎site/src/pages/WorkspacePage/WorkspaceActions/Buttons.tsx

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,21 @@ export const StartButton: FC<ActionButtonPropsWithWorkspace> = ({
9797
);
9898
};
9999

100+
exportconstUpdateAndStartButton:FC<ActionButtonProps>=({
101+
handleAction,
102+
})=>{
103+
return(
104+
<Tooltiptitle="This template requires automatic updates on workspace startup. Contact your administrator if you want to preserve the template version.">
105+
<TopbarButton
106+
startIcon={<PlayCircleOutlineIcon/>}
107+
onClick={()=>handleAction()}
108+
>
109+
Update and start&hellip;
110+
</TopbarButton>
111+
</Tooltip>
112+
);
113+
};
114+
100115
exportconstStopButton:FC<ActionButtonProps>=({
101116
handleAction,
102117
loading,
@@ -146,16 +161,13 @@ export const RestartButton: FC<ActionButtonPropsWithWorkspace> = ({
146161
);
147162
};
148163

149-
exportconstUpdateAndStartButton:FC<ActionButtonProps>=({
164+
exportconstUpdateAndRestartButton:FC<ActionButtonProps>=({
150165
handleAction,
151166
})=>{
152167
return(
153168
<Tooltiptitle="This template requires automatic updates on workspace startup. Contact your administrator if you want to preserve the template version.">
154-
<TopbarButton
155-
startIcon={<PlayCircleOutlineIcon/>}
156-
onClick={()=>handleAction()}
157-
>
158-
Update and start&hellip;
169+
<TopbarButtonstartIcon={<ReplayIcon/>}onClick={()=>handleAction()}>
170+
Update and restart&hellip;
159171
</TopbarButton>
160172
</Tooltip>
161173
);

‎site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.stories.tsx

Lines changed: 69 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -26,93 +26,128 @@ export const Running: Story = {
2626
},
2727
};
2828

29-
exportconstStopping:Story={
29+
exportconstRunningUpdateAvailable:Story={
30+
name:"Running (Update available)",
3031
args:{
31-
workspace:Mocks.MockStoppingWorkspace,
32+
workspace:{
33+
...Mocks.MockWorkspace,
34+
outdated:true,
35+
},
3236
},
3337
};
3438

35-
exportconstStopped:Story={
39+
exportconstRunningRequireActiveVersion:Story={
40+
name:"Running (No required update)",
3641
args:{
37-
workspace:Mocks.MockStoppedWorkspace,
42+
workspace:{
43+
...Mocks.MockWorkspace,
44+
template_require_active_version:true,
45+
},
3846
},
3947
};
4048

41-
exportconstCanceling:Story={
49+
exportconstRunningUpdateRequired:Story={
50+
name:"Running (Update Required)",
4251
args:{
43-
workspace:Mocks.MockCancelingWorkspace,
52+
workspace:{
53+
...Mocks.MockWorkspace,
54+
template_require_active_version:true,
55+
outdated:true,
56+
},
4457
},
4558
};
4659

47-
exportconstCanceled:Story={
60+
exportconstStopping:Story={
4861
args:{
49-
workspace:Mocks.MockCanceledWorkspace,
62+
workspace:Mocks.MockStoppingWorkspace,
5063
},
5164
};
5265

53-
exportconstDeleting:Story={
66+
exportconstStopped:Story={
5467
args:{
55-
workspace:Mocks.MockDeletingWorkspace,
68+
workspace:Mocks.MockStoppedWorkspace,
5669
},
5770
};
5871

59-
exportconstDeleted:Story={
72+
exportconstStoppedUpdateAvailable:Story={
73+
name:"Stopped (Update available)",
6074
args:{
61-
workspace:Mocks.MockDeletedWorkspace,
75+
workspace:{
76+
...Mocks.MockStoppedWorkspace,
77+
outdated:true,
78+
},
6279
},
6380
};
6481

65-
exportconstOutdated:Story={
82+
exportconstStoppedRequireActiveVersion:Story={
83+
name:"Stopped (No required update)",
84+
args:{
85+
workspace:{
86+
...Mocks.MockStoppedWorkspace,
87+
template_require_active_version:true,
88+
},
89+
},
90+
};
91+
92+
exportconstStoppedUpdateRequired:Story={
93+
name:"Stopped (Update Required)",
94+
args:{
95+
workspace:{
96+
...Mocks.MockStoppedWorkspace,
97+
template_require_active_version:true,
98+
outdated:true,
99+
},
100+
},
101+
};
102+
103+
exportconstUpdating:Story={
66104
args:{
67105
workspace:Mocks.MockOutdatedWorkspace,
106+
isUpdating:true,
68107
},
69108
};
70109

71-
exportconstFailed:Story={
110+
exportconstRestarting:Story={
72111
args:{
73-
workspace:Mocks.MockFailedWorkspace,
112+
workspace:Mocks.MockStoppingWorkspace,
113+
isRestarting:true,
74114
},
75115
};
76116

77-
exportconstFailedWithDebug:Story={
117+
exportconstCanceling:Story={
78118
args:{
79-
workspace:Mocks.MockFailedWorkspace,
80-
canDebug:true,
119+
workspace:Mocks.MockCancelingWorkspace,
81120
},
82121
};
83122

84-
exportconstUpdating:Story={
123+
exportconstDeleting:Story={
85124
args:{
86-
isUpdating:true,
87-
workspace:Mocks.MockOutdatedWorkspace,
125+
workspace:Mocks.MockDeletingWorkspace,
88126
},
89127
};
90128

91-
exportconstRequireActiveVersionStarted:Story={
129+
exportconstDeleted:Story={
92130
args:{
93-
workspace:Mocks.MockOutdatedRunningWorkspaceRequireActiveVersion,
94-
canChangeVersions:false,
131+
workspace:Mocks.MockDeletedWorkspace,
95132
},
96133
};
97134

98-
exportconstRequireActiveVersionStopped:Story={
135+
exportconstOutdated:Story={
99136
args:{
100-
workspace:Mocks.MockOutdatedStoppedWorkspaceRequireActiveVersion,
101-
canChangeVersions:false,
137+
workspace:Mocks.MockOutdatedWorkspace,
102138
},
103139
};
104140

105-
exportconstAlwaysUpdateStarted:Story={
141+
exportconstFailed:Story={
106142
args:{
107-
workspace:Mocks.MockOutdatedRunningWorkspaceAlwaysUpdate,
108-
canChangeVersions:true,
143+
workspace:Mocks.MockFailedWorkspace,
109144
},
110145
};
111146

112-
exportconstAlwaysUpdateStopped:Story={
147+
exportconstFailedWithDebug:Story={
113148
args:{
114-
workspace:Mocks.MockOutdatedStoppedWorkspaceAlwaysUpdate,
115-
canChangeVersions:true,
149+
workspace:Mocks.MockFailedWorkspace,
150+
canDebug:true,
116151
},
117152
};
118153

@@ -125,6 +160,7 @@ export const CancelShownForOwner: Story = {
125160
isOwner:true,
126161
},
127162
};
163+
128164
exportconstCancelShownForUser:Story={
129165
args:{
130166
workspace:Mocks.MockStartingWorkspace,

‎site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.tsx

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
ActivateButton,
2626
FavoriteButton,
2727
UpdateAndStartButton,
28+
UpdateAndRestartButton,
2829
}from"./Buttons";
2930
import{typeActionType,abilitiesByWorkspaceStatus}from"./constants";
3031
import{DebugButton}from"./DebugButton";
@@ -85,12 +86,12 @@ export const WorkspaceActions: FC<WorkspaceActionsProps> = ({
8586

8687
constmustUpdate=mustUpdateWorkspace(workspace,canChangeVersions);
8788
consttooltipText=getTooltipText(workspace,mustUpdate,canChangeVersions);
88-
constcanBeUpdated=workspace.outdated&&canAcceptJobs;
8989

9090
// A mapping of button type to the corresponding React component
9191
constbuttonMapping:Record<ActionType,ReactNode>={
9292
update:<UpdateButtonhandleAction={handleUpdate}/>,
9393
updateAndStart:<UpdateAndStartButtonhandleAction={handleUpdate}/>,
94+
updateAndRestart:<UpdateAndRestartButtonhandleAction={handleUpdate}/>,
9495
updating:<UpdateButtonloadinghandleAction={handleUpdate}/>,
9596
start:(
9697
<StartButton
@@ -148,43 +149,29 @@ export const WorkspaceActions: FC<WorkspaceActionsProps> = ({
148149
enableBuildParameters={workspace.latest_build.transition==="start"}
149150
/>
150151
),
151-
toggleFavorite:(
152-
<FavoriteButton
153-
workspaceID={workspace.id}
154-
isFavorite={workspace.favorite}
155-
onToggle={handleToggleFavorite}
156-
/>
157-
),
158152
};
159153

160154
return(
161155
<div
162156
css={{display:"flex",alignItems:"center",gap:8}}
163157
data-testid="workspace-actions"
164158
>
165-
{canBeUpdated&&(
166-
<>
167-
{isUpdating
168-
?buttonMapping.updating
169-
:workspace.template_require_active_version
170-
?buttonMapping.updateAndStart
171-
:buttonMapping.update}
172-
</>
173-
)}
174-
175-
{!canBeUpdated&&
176-
workspace.template_require_active_version&&
177-
buttonMapping.start}
178-
179-
{isRestarting
180-
?buttonMapping.restarting
181-
:actions.map((action)=>(
182-
<Fragmentkey={action}>{buttonMapping[action]}</Fragment>
183-
))}
159+
{/* Restarting must be handled separately, because it otherwise would appear as stopping */}
160+
{isUpdating
161+
?buttonMapping.updating
162+
:isRestarting
163+
?buttonMapping.restarting
164+
:actions.map((action)=>(
165+
<Fragmentkey={action}>{buttonMapping[action]}</Fragment>
166+
))}
184167

185168
{showCancel&&<CancelButtonhandleAction={handleCancel}/>}
186169

187-
{buttonMapping.toggleFavorite}
170+
<FavoriteButton
171+
workspaceID={workspace.id}
172+
isFavorite={workspace.favorite}
173+
onToggle={handleToggleFavorite}
174+
/>
188175

189176
<MoreMenu>
190177
<MoreMenuTrigger>

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp