Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat: add UI for autostart workspace days #10263

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

Merged
merged 8 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions coderd/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() &&
Expand Down
Original file line number Diff line number Diff line change
@@ -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&apos;s timezone.</span>
);
};
31 changes: 30 additions & 1 deletion site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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 = {
Expand All @@ -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,
Expand Down Expand Up @@ -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"
Expand Down
4 changes: 4 additions & 0 deletions site/src/pages/CreateTemplatePage/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -33,6 +34,9 @@ export const newTemplate = (formData: CreateTemplateData) => {
),
weeks: formData.autostop_requirement_weeks,
},
autostart_requirement: {
days_of_week: autostart_requirement_days_of_week,
},
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -623,4 +650,7 @@ const styles = {
ttlFields: {
width: "100%",
},
dayButtons: {
borderRadius: "0px",
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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),
});
9 changes: 9 additions & 0 deletions site/src/utils/schedule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,15 @@ export const quietHoursDisplay = (
return display;
};

export type TemplateAutostartRequirementDaysValue =
| "monday"
| "tuesday"
| "wednesday"
| "thursday"
| "friday"
| "saturday"
| "sunday";

export type TemplateAutostopRequirementDaysValue =
| "off"
| "daily"
Expand Down