mirror of https://github.com/coder/coder.git
feat: add UI for autostart workspace days (#10263)
* feat: add ui for selecting auto start days
This commit is contained in:
parent
5a90228c60
commit
6ebe9b0402
|
@ -610,6 +610,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
|
|||
req.DefaultTTLMillis == time.Duration(template.DefaultTTL).Milliseconds() &&
|
||||
req.MaxTTLMillis == time.Duration(template.MaxTTL).Milliseconds() &&
|
||||
autostopRequirementDaysOfWeekParsed == scheduleOpts.AutostopRequirement.DaysOfWeek &&
|
||||
autostartRequirementDaysOfWeekParsed == scheduleOpts.AutostartRequirement.DaysOfWeek &&
|
||||
req.AutostopRequirement.Weeks == scheduleOpts.AutostopRequirement.Weeks &&
|
||||
req.FailureTTLMillis == time.Duration(template.FailureTTL).Milliseconds() &&
|
||||
req.TimeTilDormantMillis == time.Duration(template.TimeTilDormant).Milliseconds() &&
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
import { FC } from "react";
|
||||
import { TemplateAutostartRequirementDaysValue } from "utils/schedule";
|
||||
import Button from "@mui/material/Button";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import FormHelperText from "@mui/material/FormHelperText";
|
||||
|
||||
export interface TemplateScheduleAutostartProps {
|
||||
allow_user_autostart?: boolean;
|
||||
autostart_requirement_days_of_week: TemplateAutostartRequirementDaysValue[];
|
||||
isSubmitting: boolean;
|
||||
onChange: (newDaysOfWeek: TemplateAutostartRequirementDaysValue[]) => void;
|
||||
}
|
||||
|
||||
export const TemplateScheduleAutostart: FC<
|
||||
React.PropsWithChildren<TemplateScheduleAutostartProps>
|
||||
> = ({
|
||||
autostart_requirement_days_of_week,
|
||||
isSubmitting,
|
||||
allow_user_autostart,
|
||||
onChange,
|
||||
}) => {
|
||||
return (
|
||||
<Stack
|
||||
direction="column"
|
||||
width="100%"
|
||||
alignItems="center"
|
||||
css={{
|
||||
marginBottom: "20px",
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
css={{
|
||||
width: "100%",
|
||||
}}
|
||||
spacing={0}
|
||||
alignItems="baseline"
|
||||
justifyContent="center"
|
||||
>
|
||||
{(
|
||||
[
|
||||
{ value: "monday", key: "Mon" },
|
||||
{ value: "tuesday", key: "Tue" },
|
||||
{ value: "wednesday", key: "Wed" },
|
||||
{ value: "thursday", key: "Thu" },
|
||||
{ value: "friday", key: "Fri" },
|
||||
{ value: "saturday", key: "Sat" },
|
||||
{ value: "sunday", key: "Sun" },
|
||||
] as {
|
||||
value: TemplateAutostartRequirementDaysValue;
|
||||
key: string;
|
||||
}[]
|
||||
).map((day) => (
|
||||
<Button
|
||||
key={day.key}
|
||||
css={{
|
||||
borderRadius: "0px",
|
||||
}}
|
||||
// TODO: Adding a background color would also help
|
||||
color={
|
||||
autostart_requirement_days_of_week.includes(day.value)
|
||||
? "primary"
|
||||
: "secondary"
|
||||
}
|
||||
disabled={isSubmitting || !allow_user_autostart}
|
||||
onClick={() => {
|
||||
if (!autostart_requirement_days_of_week.includes(day.value)) {
|
||||
onChange(autostart_requirement_days_of_week.concat(day.value));
|
||||
} else {
|
||||
onChange(
|
||||
autostart_requirement_days_of_week.filter(
|
||||
(obj) => obj !== day.value,
|
||||
),
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{day.key}
|
||||
</Button>
|
||||
))}
|
||||
</Stack>
|
||||
<FormHelperText>
|
||||
<AutostartRequirementDaysHelperText
|
||||
allowed={allow_user_autostart}
|
||||
days={autostart_requirement_days_of_week}
|
||||
/>
|
||||
</FormHelperText>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export const sortedDays = [
|
||||
"monday",
|
||||
"tuesday",
|
||||
"wednesday",
|
||||
"thursday",
|
||||
"friday",
|
||||
"saturday",
|
||||
"sunday",
|
||||
] as TemplateAutostartRequirementDaysValue[];
|
||||
|
||||
const AutostartRequirementDaysHelperText: FC<{
|
||||
allowed?: boolean;
|
||||
days: TemplateAutostartRequirementDaysValue[];
|
||||
}> = ({ allowed, days: unsortedDays }) => {
|
||||
if (!allowed) {
|
||||
return <span>Workspaces are not allowed to auto start.</span>;
|
||||
}
|
||||
// Sort the days
|
||||
const days = unsortedDays.sort(
|
||||
(a, b) => sortedDays.indexOf(a) - sortedDays.indexOf(b),
|
||||
);
|
||||
|
||||
let daymsg = `Workspaces can autostart on ${days.join(", ")}.`;
|
||||
if (days.length === 7) {
|
||||
// If every day is allowed, no more explaining is needed.
|
||||
return <span>Workspaces are allowed to auto start on any day.</span>;
|
||||
}
|
||||
if (days.length === 0) {
|
||||
return (
|
||||
<span>
|
||||
Workspaces will never auto start. This is effectively the same as
|
||||
disabling autostart.
|
||||
</span>
|
||||
);
|
||||
}
|
||||
if (
|
||||
days.length === 5 &&
|
||||
!days.includes("saturday") &&
|
||||
!days.includes("sunday")
|
||||
) {
|
||||
daymsg = "Workspaces will never auto start on the weekends.";
|
||||
}
|
||||
return (
|
||||
<span>{daymsg} These days are relative to the user's timezone.</span>
|
||||
);
|
||||
};
|
|
@ -42,7 +42,14 @@ import {
|
|||
AutostopRequirementWeeksHelperText,
|
||||
} from "pages/TemplateSettingsPage/TemplateSchedulePage/AutostopRequirementHelperText";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import { TemplateAutostopRequirementDaysValue } from "utils/schedule";
|
||||
import {
|
||||
TemplateAutostartRequirementDaysValue,
|
||||
TemplateAutostopRequirementDaysValue,
|
||||
} from "utils/schedule";
|
||||
import {
|
||||
TemplateScheduleAutostart,
|
||||
sortedDays,
|
||||
} from "components/TemplateScheduleAutostart/TemplateScheduleAutostart";
|
||||
|
||||
const MAX_DESCRIPTION_CHAR_LIMIT = 128;
|
||||
const MAX_TTL_DAYS = 30;
|
||||
|
@ -54,6 +61,7 @@ export interface CreateTemplateData {
|
|||
icon: string;
|
||||
default_ttl_hours: number;
|
||||
max_ttl_hours: number;
|
||||
autostart_requirement_days_of_week: TemplateAutostartRequirementDaysValue[];
|
||||
autostop_requirement_days_of_week: TemplateAutostopRequirementDaysValue;
|
||||
autostop_requirement_weeks: number;
|
||||
allow_user_autostart: boolean;
|
||||
|
@ -88,6 +96,7 @@ const validationSchema = Yup.object({
|
|||
),
|
||||
autostop_requirement_days_of_week: Yup.string().required(),
|
||||
autostop_requirement_weeks: Yup.number().required().min(1).max(16),
|
||||
autostart_requirement_days_of_week: Yup.array().of(Yup.string()).required(),
|
||||
});
|
||||
|
||||
const defaultInitialValues: CreateTemplateData = {
|
||||
|
@ -110,6 +119,7 @@ const defaultInitialValues: CreateTemplateData = {
|
|||
// user's timezone.
|
||||
autostop_requirement_days_of_week: "sunday",
|
||||
autostop_requirement_weeks: 1,
|
||||
autostart_requirement_days_of_week: sortedDays,
|
||||
allow_user_cancel_workspace_jobs: false,
|
||||
allow_user_autostart: false,
|
||||
allow_user_autostop: false,
|
||||
|
@ -434,6 +444,25 @@ export const CreateTemplateForm: FC<CreateTemplateFormProps> = (props) => {
|
|||
</strong>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
{allowAdvancedScheduling && (
|
||||
<TemplateScheduleAutostart
|
||||
allow_user_autostart={form.values.allow_user_autostart}
|
||||
autostart_requirement_days_of_week={
|
||||
form.values.autostart_requirement_days_of_week
|
||||
}
|
||||
isSubmitting={isSubmitting}
|
||||
onChange={async (
|
||||
newDaysOfWeek: TemplateAutostartRequirementDaysValue[],
|
||||
) => {
|
||||
await form.setFieldValue(
|
||||
"autostart_requirement_days_of_week",
|
||||
newDaysOfWeek,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Stack direction="row" alignItems="center">
|
||||
<Checkbox
|
||||
id="allow-user-autostop"
|
||||
|
|
|
@ -17,6 +17,7 @@ export const newTemplate = (formData: CreateTemplateData) => {
|
|||
max_ttl_hours,
|
||||
parameter_values_by_name,
|
||||
allow_everyone_group_access,
|
||||
autostart_requirement_days_of_week,
|
||||
autostop_requirement_days_of_week,
|
||||
autostop_requirement_weeks,
|
||||
...safeTemplateData
|
||||
|
@ -33,6 +34,9 @@ export const newTemplate = (formData: CreateTemplateData) => {
|
|||
),
|
||||
weeks: formData.autostop_requirement_weeks,
|
||||
},
|
||||
autostart_requirement: {
|
||||
days_of_week: autostart_requirement_days_of_week,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -9,7 +9,10 @@ import { FC, ChangeEvent, useState, useEffect } from "react";
|
|||
import { Template, UpdateTemplateMeta } from "api/typesGenerated";
|
||||
import { getFormHelpers } from "utils/formUtils";
|
||||
import { docs } from "utils/docs";
|
||||
import { calculateAutostopRequirementDaysValue } from "utils/schedule";
|
||||
import {
|
||||
TemplateAutostartRequirementDaysValue,
|
||||
calculateAutostopRequirementDaysValue,
|
||||
} from "utils/schedule";
|
||||
import {
|
||||
FormSection,
|
||||
HorizontalForm,
|
||||
|
@ -36,6 +39,7 @@ import {
|
|||
convertAutostopRequirementDaysValue,
|
||||
} from "./AutostopRequirementHelperText";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { TemplateScheduleAutostart } from "components/TemplateScheduleAutostart/TemplateScheduleAutostart";
|
||||
|
||||
const MS_HOUR_CONVERSION = 3600000;
|
||||
const MS_DAY_CONVERSION = 86400000;
|
||||
|
@ -95,6 +99,8 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
|
|||
? template.autostop_requirement.weeks
|
||||
: 1
|
||||
: 1,
|
||||
autostart_requirement_days_of_week: template.autostart_requirement
|
||||
.days_of_week as TemplateAutostartRequirementDaysValue[],
|
||||
|
||||
allow_user_autostart: template.allow_user_autostart,
|
||||
allow_user_autostop: template.allow_user_autostop,
|
||||
|
@ -215,6 +221,9 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
|
|||
),
|
||||
weeks: autostop_requirement_weeks,
|
||||
},
|
||||
autostart_requirement: {
|
||||
days_of_week: form.values.autostart_requirement_days_of_week,
|
||||
},
|
||||
|
||||
allow_user_autostart: form.values.allow_user_autostart,
|
||||
allow_user_autostop: form.values.allow_user_autostop,
|
||||
|
@ -430,6 +439,24 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
|
|||
</strong>
|
||||
</Stack>
|
||||
</Stack>
|
||||
{allowAdvancedScheduling && (
|
||||
<TemplateScheduleAutostart
|
||||
allow_user_autostart={form.values.allow_user_autostart}
|
||||
autostart_requirement_days_of_week={
|
||||
form.values.autostart_requirement_days_of_week
|
||||
}
|
||||
isSubmitting={isSubmitting}
|
||||
onChange={async (
|
||||
newDaysOfWeek: TemplateAutostartRequirementDaysValue[],
|
||||
) => {
|
||||
await form.setFieldValue(
|
||||
"autostart_requirement_days_of_week",
|
||||
newDaysOfWeek,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Stack direction="row" alignItems="center">
|
||||
<Checkbox
|
||||
id="allow-user-autostop"
|
||||
|
@ -623,4 +650,7 @@ const styles = {
|
|||
ttlFields: {
|
||||
width: "100%",
|
||||
},
|
||||
dayButtons: {
|
||||
borderRadius: "0px",
|
||||
},
|
||||
};
|
||||
|
|
|
@ -26,6 +26,15 @@ const validFormValues: TemplateScheduleFormValues = {
|
|||
failure_cleanup_enabled: false,
|
||||
inactivity_cleanup_enabled: false,
|
||||
dormant_autodeletion_cleanup_enabled: false,
|
||||
autostart_requirement_days_of_week: [
|
||||
"monday",
|
||||
"tuesday",
|
||||
"wednesday",
|
||||
"thursday",
|
||||
"friday",
|
||||
"saturday",
|
||||
"sunday",
|
||||
],
|
||||
};
|
||||
|
||||
const renderTemplateSchedulePage = async () => {
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
import { UpdateTemplateMeta } from "api/typesGenerated";
|
||||
import { TemplateAutostopRequirementDaysValue } from "utils/schedule";
|
||||
import {
|
||||
TemplateAutostartRequirementDaysValue,
|
||||
TemplateAutostopRequirementDaysValue,
|
||||
} from "utils/schedule";
|
||||
import * as Yup from "yup";
|
||||
|
||||
export interface TemplateScheduleFormValues
|
||||
extends Omit<UpdateTemplateMeta, "autostop_requirement"> {
|
||||
extends Omit<
|
||||
UpdateTemplateMeta,
|
||||
"autostop_requirement" | "autostart_requirement"
|
||||
> {
|
||||
autostart_requirement_days_of_week: TemplateAutostartRequirementDaysValue[];
|
||||
autostop_requirement_days_of_week: TemplateAutostopRequirementDaysValue;
|
||||
autostop_requirement_weeks: number;
|
||||
failure_cleanup_enabled: boolean;
|
||||
|
@ -75,5 +82,6 @@ export const getValidationSchema = (): Yup.AnyObjectSchema =>
|
|||
allow_user_autostop: Yup.boolean(),
|
||||
|
||||
autostop_requirement_days_of_week: Yup.string().required(),
|
||||
autostart_requirement_days_of_week: Yup.array().of(Yup.string()).required(),
|
||||
autostop_requirement_weeks: Yup.number().required().min(1).max(16),
|
||||
});
|
||||
|
|
|
@ -211,6 +211,15 @@ export const quietHoursDisplay = (
|
|||
return display;
|
||||
};
|
||||
|
||||
export type TemplateAutostartRequirementDaysValue =
|
||||
| "monday"
|
||||
| "tuesday"
|
||||
| "wednesday"
|
||||
| "thursday"
|
||||
| "friday"
|
||||
| "saturday"
|
||||
| "sunday";
|
||||
|
||||
export type TemplateAutostopRequirementDaysValue =
|
||||
| "off"
|
||||
| "daily"
|
||||
|
|
Loading…
Reference in New Issue