diff --git a/apps/spruce/src/components/Spawn/editHostModal/getFormSchema.tsx b/apps/spruce/src/components/Spawn/editHostModal/getFormSchema.tsx index 7f0ded4c0..a1e3ff2bf 100644 --- a/apps/spruce/src/components/Spawn/editHostModal/getFormSchema.tsx +++ b/apps/spruce/src/components/Spawn/editHostModal/getFormSchema.tsx @@ -23,7 +23,7 @@ interface Props { myPublicKeys: MyPublicKeysQuery["myPublicKeys"]; noExpirationCheckboxTooltip: string; permanentlyExempt: boolean; - timeZone?: string; + timeZone: string; volumes: MyVolumesQuery["myVolumes"]; } diff --git a/apps/spruce/src/components/Spawn/editHostModal/transformer.test.ts b/apps/spruce/src/components/Spawn/editHostModal/transformer.test.ts index 31f26d2a1..25c25103e 100644 --- a/apps/spruce/src/components/Spawn/editHostModal/transformer.test.ts +++ b/apps/spruce/src/components/Spawn/editHostModal/transformer.test.ts @@ -8,7 +8,6 @@ describe("edit spawn host modal", () => { hostId: "host_id", myPublicKeys, oldUserTags, - timeZone: "America/New_York", }), ).toStrictEqual({ hostId: "host_id", @@ -67,6 +66,9 @@ const formState = { runContinuously: false, }, }, + details: { + timeZone: "America/New_York", + }, }, }, instanceType: "m4.xlarge", diff --git a/apps/spruce/src/components/Spawn/editHostModal/transformer.ts b/apps/spruce/src/components/Spawn/editHostModal/transformer.ts index 85d4c162e..aaff2de57 100644 --- a/apps/spruce/src/components/Spawn/editHostModal/transformer.ts +++ b/apps/spruce/src/components/Spawn/editHostModal/transformer.ts @@ -13,14 +13,12 @@ interface Props { myPublicKeys: MyPublicKeysQuery["myPublicKeys"]; formData: FormState; oldUserTags: { key: string; value: string }[]; - timeZone: string; } export const formToGql = ({ formData, hostId, myPublicKeys, oldUserTags, - timeZone, }: Props): EditSpawnHostMutationVariables => { const { expirationDetails, @@ -78,8 +76,6 @@ export const formToGql = ({ }, savePublicKey: !useExisting && savePublicKey, sleepSchedule: - noExpiration && hostUptime - ? getSleepSchedule(hostUptime, timeZone) - : null, + noExpiration && hostUptime ? getSleepSchedule(hostUptime) : null, }; }; diff --git a/apps/spruce/src/components/Spawn/getFormSchema.tsx b/apps/spruce/src/components/Spawn/getFormSchema.tsx index 9465cc7b8..c7d0ec6bc 100644 --- a/apps/spruce/src/components/Spawn/getFormSchema.tsx +++ b/apps/spruce/src/components/Spawn/getFormSchema.tsx @@ -6,7 +6,7 @@ import { add } from "date-fns"; import widgets from "components/SpruceForm/Widgets"; import { StyledLink } from "components/styles"; import { hostUptimeDocumentationUrl } from "constants/externalResources"; -import { prettifyTimeZone } from "constants/fieldMaps"; +import { abbreviateTimeZone, timeZones } from "constants/fieldMaps"; import { size } from "constants/tokens"; import { MyPublicKeysQuery } from "gql/generated/types"; import { @@ -24,7 +24,7 @@ type HostUptimeProps = { warnings: string[]; }; isEditModal: boolean; - timeZone?: string; + timeZone: string; }; const getHostUptimeSchema = ({ @@ -38,7 +38,7 @@ const getHostUptimeSchema = ({ properties: { useDefaultUptimeSchedule: { type: "boolean" as "boolean", - title: "Use default host uptime schedule (Mon–Fri, 8am–8pm)", + title: `Use default host uptime schedule (Mon–Fri, 8am–8pm ${abbreviateTimeZone(timeZone)})`, default: true, }, sleepSchedule: { @@ -97,7 +97,24 @@ const getHostUptimeSchema = ({ }, }, details: { - type: "null" as "null", + type: "object" as "object", + title: "", + properties: { + timeZone: { + type: "string", + title: "Time Zone", + default: timeZone, + oneOf: timeZones.map(({ str, value }) => ({ + type: "string" as "string", + title: str, + enum: [value], + })), + }, + + uptimeHours: { + type: "null" as "null", + }, + }, }, isBetaTester: { type: "boolean" as "boolean", @@ -169,16 +186,35 @@ const getHostUptimeSchema = ({ }, }, details: { - "ui:descriptionNode": ( -
- ), - "ui:showLabel": false, - "ui:warnings": hostUptimeWarnings?.warnings, + "ui:elementWrapperCSS": css` + align-items: flex-end; + display: flex; + gap: ${size.xs}; + flex-wrap: wrap; + + > div { + width: 40%; + } + + > [role="alert"] { + margin-top: 0; + width: 100%; + } + `, + timeZone: { + "ui:allowDeselect": false, + "ui:sizeVariant": "xsmall", + }, + uptimeHours: { + "ui:descriptionNode": ( +
+ ), + "ui:showLabel": false, + "ui:warnings": hostUptimeWarnings?.warnings, + }, }, isBetaTester: { "ui:widget": widgets.ToggleWidget, @@ -210,19 +246,18 @@ const getHostUptimeSchema = ({ }, }); -const Details: React.FC<{ timeZone: string; totalUptimeHours: number }> = ({ - timeZone, +const Details: React.FC<{ totalUptimeHours: number }> = ({ totalUptimeHours, }) => ( - All times are displayed in{" "} - {prettifyTimeZone.get(timeZone) ?? timeZone} •{" "} - {totalUptimeHours} host uptime hours per week + {" "} + • {totalUptimeHours} host uptime hours per week ); const DetailsDiv = styled.div` - margin-bottom: ${size.xs}; + margin-bottom: 21px; + white-space: nowrap; `; type ExpirationProps = { @@ -234,7 +269,7 @@ type ExpirationProps = { isEditModal: boolean; noExpirationCheckboxTooltip?: string; permanentlyExempt?: boolean; - timeZone?: string; + timeZone: string; }; export const getExpirationDetailsSchema = ({ diff --git a/apps/spruce/src/components/Spawn/spawnHostModal/getFormSchema.tsx b/apps/spruce/src/components/Spawn/spawnHostModal/getFormSchema.tsx index 022f90695..946f9d5ca 100644 --- a/apps/spruce/src/components/Spawn/spawnHostModal/getFormSchema.tsx +++ b/apps/spruce/src/components/Spawn/spawnHostModal/getFormSchema.tsx @@ -35,7 +35,7 @@ interface Props { noExpirationCheckboxTooltip: string; permanentlyExempt: boolean; spawnTaskData?: SpawnTaskQuery["task"]; - timeZone?: string; + timeZone: string; useSetupScript?: boolean; useProjectSetupScript?: boolean; userAwsRegion?: string; diff --git a/apps/spruce/src/components/Spawn/spawnHostModal/transformer.test.ts b/apps/spruce/src/components/Spawn/spawnHostModal/transformer.test.ts index 95d65cf16..c84f706fb 100644 --- a/apps/spruce/src/components/Spawn/spawnHostModal/transformer.test.ts +++ b/apps/spruce/src/components/Spawn/spawnHostModal/transformer.test.ts @@ -11,7 +11,6 @@ describe("spawn host modal", () => { formData, myPublicKeys, spawnTaskData: null, - timeZone: "America/New_York", }), ).toStrictEqual(mutationInput); }); @@ -26,7 +25,6 @@ describe("spawn host modal", () => { myPublicKeys, spawnTaskData: null, migrateVolumeId, - timeZone: "America/New_York", }), ).toStrictEqual({ ...mutationInput, @@ -120,6 +118,7 @@ const data: Array<{ formData: FormState; mutationInput: SpawnHostInput }> = [ runContinuously: false, }, }, + details: { timeZone: "America/New_York" }, }, }, homeVolumeDetails: { selectExistingVolume: true, volumeSelect: "" }, diff --git a/apps/spruce/src/components/Spawn/spawnHostModal/transformer.ts b/apps/spruce/src/components/Spawn/spawnHostModal/transformer.ts index e14404787..4264614c5 100644 --- a/apps/spruce/src/components/Spawn/spawnHostModal/transformer.ts +++ b/apps/spruce/src/components/Spawn/spawnHostModal/transformer.ts @@ -15,7 +15,6 @@ interface Props { myPublicKeys: MyPublicKeysQuery["myPublicKeys"]; spawnTaskData?: SpawnTaskQuery["task"]; migrateVolumeId?: string; - timeZone?: string; } export const formToGql = ({ formData, @@ -23,7 +22,6 @@ export const formToGql = ({ migrateVolumeId, myPublicKeys, spawnTaskData, - timeZone, }: Props): SpawnHostMutationVariables["spawnHostInput"] => { const { expirationDetails, @@ -50,8 +48,7 @@ export const formToGql = ({ noExpiration: expirationDetails?.noExpiration, sleepSchedule: expirationDetails?.noExpiration && hostUptime - ? // @ts-expect-error: FIXME. This comment was added by an automated script. - getSleepSchedule(hostUptime, timeZone) + ? getSleepSchedule(hostUptime) : null, volumeId: migrateVolumeId || diff --git a/apps/spruce/src/components/Spawn/utils/hostUptime.test.ts b/apps/spruce/src/components/Spawn/utils/hostUptime.test.ts index a025c3bfb..377be276d 100644 --- a/apps/spruce/src/components/Spawn/utils/hostUptime.test.ts +++ b/apps/spruce/src/components/Spawn/utils/hostUptime.test.ts @@ -13,6 +13,7 @@ describe("matchesDefaultUptimeSchedule", () => { const sched = { dailyStartTime: "08:00", dailyStopTime: "20:00", + isBetaTester: true, permanentlyExempt: true, shouldKeepOff: true, timeZone: "America/New_York", @@ -25,6 +26,7 @@ describe("matchesDefaultUptimeSchedule", () => { const sched = { dailyStartTime: "08:30", dailyStopTime: "20:00", + isBetaTester: true, permanentlyExempt: true, shouldKeepOff: true, timeZone: "America/New_York", @@ -37,6 +39,7 @@ describe("matchesDefaultUptimeSchedule", () => { const sched = { dailyStartTime: "08:00", dailyStopTime: "21:00", + isBetaTester: true, permanentlyExempt: true, shouldKeepOff: true, timeZone: "America/New_York", @@ -49,6 +52,7 @@ describe("matchesDefaultUptimeSchedule", () => { const sched = { dailyStartTime: "08:00", dailyStopTime: "20:00", + isBetaTester: false, permanentlyExempt: true, shouldKeepOff: true, timeZone: "America/New_York", @@ -79,12 +83,19 @@ describe("validator", () => { runContinuously: true, }, }, + details: { + timeZone: "America/New_York", + }, }, noExpiration: false, }, }, - // @ts-expect-error - { expirationDetails: { hostUptime: { details: { addError: f } } } }, + { + expirationDetails: { + // @ts-expect-error + hostUptime: { details: { uptimeHours: { addError: f } } }, + }, + }, ); expect(f).toHaveBeenCalledTimes(0); }); @@ -105,12 +116,19 @@ describe("validator", () => { runContinuously: true, }, }, + details: { + timeZone: "America/New_York", + }, }, noExpiration: true, }, }, - // @ts-expect-error - { expirationDetails: { hostUptime: { details: { addError: f } } } }, + { + expirationDetails: { + // @ts-expect-error + hostUptime: { details: { uptimeHours: { addError: f } } }, + }, + }, ); expect(f).toHaveBeenCalledTimes(1); }); @@ -131,12 +149,19 @@ describe("validator", () => { runContinuously: true, }, }, + details: { + timeZone: "America/New_York", + }, }, noExpiration: true, }, }, - // @ts-expect-error - { expirationDetails: { hostUptime: { details: { addError: f } } } }, + { + expirationDetails: { + // @ts-expect-error + hostUptime: { details: { uptimeHours: { addError: f } } }, + }, + }, ); expect(f).toHaveBeenCalledTimes(1); }); @@ -157,6 +182,9 @@ describe("validator", () => { runContinuously: true, }, }, + details: { + timeZone: "America/New_York", + }, }, noExpiration: true, }, @@ -184,6 +212,9 @@ describe("validator", () => { }, }, temporarilyExemptUntil: new Date("2024-01-05").toString(), + details: { + timeZone: "America/New_York", + }, }, noExpiration: true, }, @@ -227,6 +258,9 @@ describe("validator", () => { runContinuously: true, }, }, + details: { + timeZone: "America/New_York", + }, temporarilyExemptUntil: new Date("2001-01-01").toString(), }, noExpiration: true, @@ -258,6 +292,9 @@ describe("validator", () => { runContinuously: true, }, }, + details: { + timeZone: "America/New_York", + }, temporarilyExemptUntil: new Date("2025-01-01").toString(), }, noExpiration: true, @@ -289,6 +326,9 @@ describe("validator", () => { runContinuously: true, }, }, + details: { + timeZone: "America/New_York", + }, temporarilyExemptUntil: new Date("2024-01-05").toString(), }, noExpiration: true, @@ -332,6 +372,9 @@ describe("getHostUptimeFromGql", () => { }, temporarilyExemptUntil: "", isBetaTester: true, + details: { + timeZone: "America/New_York", + }, }); }); @@ -361,6 +404,9 @@ describe("getHostUptimeFromGql", () => { temporarilyExemptUntil: "Mon Jul 01 2024 00:00:00 GMT+0000 (Coordinated Universal Time)", isBetaTester: true, + details: { + timeZone: "America/New_York", + }, }); }); @@ -372,7 +418,7 @@ describe("getHostUptimeFromGql", () => { permanentlyExempt: true, shouldKeepOff: true, temporarilyExemptUntil: null, - timeZone: "America/New_York", + timeZone: "America/Chicago", wholeWeekdaysOff: [0, 6], }; expect(getHostUptimeFromGql(sched)).toStrictEqual({ @@ -389,6 +435,9 @@ describe("getHostUptimeFromGql", () => { }, temporarilyExemptUntil: "", isBetaTester: true, + details: { + timeZone: "America/Chicago", + }, }); }); }); @@ -396,23 +445,23 @@ describe("getHostUptimeFromGql", () => { describe("getSleepSchedule", () => { it("sets the default schedule", () => { expect( - getSleepSchedule( - { - useDefaultUptimeSchedule: true, - isBetaTester: false, - sleepSchedule: { - enabledWeekdays: [false, false, true, true, true, true, false], - timeSelection: { - runContinuously: true, - startTime: - "Sun Dec 31 1899 08:00:00 GMT+0000 (Coordinated Universal Time)", - stopTime: - "Sun Dec 31 1899 20:00:00 GMT+0000 (Coordinated Universal Time)", - }, + getSleepSchedule({ + useDefaultUptimeSchedule: true, + isBetaTester: false, + sleepSchedule: { + enabledWeekdays: [false, false, true, true, true, true, false], + timeSelection: { + runContinuously: true, + startTime: + "Sun Dec 31 1899 08:00:00 GMT+0000 (Coordinated Universal Time)", + stopTime: + "Sun Dec 31 1899 20:00:00 GMT+0000 (Coordinated Universal Time)", }, }, - "America/New_York", - ), + details: { + timeZone: "America/New_York", + }, + }), ).toStrictEqual({ dailyStartTime: "08:00", dailyStopTime: "20:00", @@ -426,23 +475,23 @@ describe("getSleepSchedule", () => { it("sets continuously running days", () => { expect( - getSleepSchedule( - { - useDefaultUptimeSchedule: false, - isBetaTester: false, - sleepSchedule: { - enabledWeekdays: [false, false, true, true, true, true, false], - timeSelection: { - runContinuously: true, - startTime: - "Sun Dec 31 1899 08:00:00 GMT+0000 (Coordinated Universal Time)", - stopTime: - "Sun Dec 31 1899 20:00:00 GMT+0000 (Coordinated Universal Time)", - }, + getSleepSchedule({ + useDefaultUptimeSchedule: false, + isBetaTester: false, + sleepSchedule: { + enabledWeekdays: [false, false, true, true, true, true, false], + timeSelection: { + runContinuously: true, + startTime: + "Sun Dec 31 1899 08:00:00 GMT+0000 (Coordinated Universal Time)", + stopTime: + "Sun Dec 31 1899 20:00:00 GMT+0000 (Coordinated Universal Time)", }, }, - "America/New_York", - ), + details: { + timeZone: "America/New_York", + }, + }), ).toStrictEqual({ dailyStartTime: "", dailyStopTime: "", @@ -456,23 +505,23 @@ describe("getSleepSchedule", () => { it("sets start and stop times", () => { expect( - getSleepSchedule( - { - useDefaultUptimeSchedule: false, - isBetaTester: true, - sleepSchedule: { - enabledWeekdays: [false, false, true, true, true, true, false], - timeSelection: { - runContinuously: false, - startTime: - "Sun Dec 31 1899 08:00:00 GMT+0000 (Coordinated Universal Time)", - stopTime: - "Sun Dec 31 1899 20:00:00 GMT+0000 (Coordinated Universal Time)", - }, + getSleepSchedule({ + useDefaultUptimeSchedule: false, + isBetaTester: true, + sleepSchedule: { + enabledWeekdays: [false, false, true, true, true, true, false], + timeSelection: { + runContinuously: false, + startTime: + "Sun Dec 31 1899 08:00:00 GMT+0000 (Coordinated Universal Time)", + stopTime: + "Sun Dec 31 1899 20:00:00 GMT+0000 (Coordinated Universal Time)", }, }, - "America/New_York", - ), + details: { + timeZone: "America/New_York", + }, + }), ).toStrictEqual({ dailyStartTime: "08:00", dailyStopTime: "20:00", @@ -565,6 +614,9 @@ describe("getEnabledHoursCount", () => { runContinuously: true, }, }, + details: { + timeZone: "America/New_York", + }, }), ).toStrictEqual({ enabledHoursCount: 60, @@ -587,6 +639,9 @@ describe("getEnabledHoursCount", () => { "Mon Jan 01 1900 20:00:00 GMT+0000 (Coordinated Universal Time)", }, }, + details: { + timeZone: "America/New_York", + }, }), ).toStrictEqual({ enabledHoursCount: 65, @@ -607,6 +662,9 @@ describe("getEnabledHoursCount", () => { stopTime: "", }, }, + details: { + timeZone: "America/New_York", + }, }), ).toStrictEqual({ enabledHoursCount: 120, @@ -629,6 +687,9 @@ describe("getEnabledHoursCount", () => { "Mon Jan 01 1900 03:00:00 GMT+0000 (Coordinated Universal Time)", }, }, + details: { + timeZone: "America/New_York", + }, }), ).toStrictEqual({ enabledHoursCount: 35, diff --git a/apps/spruce/src/components/Spawn/utils/hostUptime.ts b/apps/spruce/src/components/Spawn/utils/hostUptime.ts index ffdb96134..bef52e350 100644 --- a/apps/spruce/src/components/Spawn/utils/hostUptime.ts +++ b/apps/spruce/src/components/Spawn/utils/hostUptime.ts @@ -9,8 +9,7 @@ import { } from "date-fns"; import { ValidateProps } from "components/SpruceForm"; import { days } from "constants/fieldMaps"; -import { SleepScheduleInput } from "gql/generated/types"; -import { Optional } from "types/utils"; +import { SleepSchedule, SleepScheduleInput } from "gql/generated/types"; import { FormState as EditFormState } from "../editHostModal"; import { FormState as SpawnFormState } from "../spawnHostModal"; @@ -37,7 +36,10 @@ export type HostUptime = { runContinuously: boolean; }; }; - details?: null; + details: { + timeZone: string; + uptimeHours?: null; + }; isBetaTester: boolean; temporarilyExemptUntil?: string; }; @@ -128,18 +130,17 @@ export const getEnabledHoursCount = ( * @param obj.sleepSchedule - sleep schedule configuration * @param obj.temporarilyExemptUntil - temporary exemption date * @param obj.useDefaultUptimeSchedule - boolean indicating whether user has set custom schedule - * @param timeZone - user-defined time zone, defaults to UTC + * @param obj.details - details form object + * @param obj.details.timeZone - user-selected time zone * @returns - sleep schedule used as input for GraphQL mutations */ -export const getSleepSchedule = ( - { - isBetaTester, - sleepSchedule, - temporarilyExemptUntil, - useDefaultUptimeSchedule, - }: HostUptime, - timeZone: string, -): SleepScheduleInput => { +export const getSleepSchedule = ({ + details: { timeZone }, + isBetaTester, + sleepSchedule, + temporarilyExemptUntil, + useDefaultUptimeSchedule, +}: HostUptime): SleepScheduleInput => { if (useDefaultUptimeSchedule) { return { ...defaultSleepSchedule, @@ -203,24 +204,24 @@ const toTimeString = (date: Date): string => minute: "2-digit", }); -type RequiredSleepSchedule = Optional; - -export const defaultSleepSchedule: RequiredSleepSchedule = { +export const defaultSleepSchedule: Omit = { dailyStartTime: toTimeString(defaultStartDate), dailyStopTime: toTimeString(defaultStopDate), + isBetaTester: false, permanentlyExempt: false, shouldKeepOff: false, wholeWeekdaysOff: [0, 6], }; export const getHostUptimeFromGql = ( - sleepSchedule: RequiredSleepSchedule, + sleepSchedule: SleepSchedule, ): HostUptime => { const { dailyStartTime, dailyStopTime, isBetaTester, temporarilyExemptUntil, + timeZone, wholeWeekdaysOff, } = sleepSchedule; @@ -253,11 +254,12 @@ export const getHostUptimeFromGql = ( runContinuously: true, }, }, + details: { timeZone }, }; }; export const matchesDefaultUptimeSchedule = ( - sleepSchedule: RequiredSleepSchedule, + sleepSchedule: SleepSchedule, ): boolean => { const { dailyStartTime, dailyStopTime, wholeWeekdaysOff } = sleepSchedule; @@ -323,7 +325,7 @@ export const validator = (permanentlyExempt: boolean) => // Return error based on whether runContinously enabled if (timeSelection?.runContinuously) { // @ts-expect-error - errors.expirationDetails?.hostUptime?.details?.addError?.( + errors.expirationDetails?.hostUptime?.details?.uptimeHours?.addError?.( "Please pause your host for at least 1 day per week.", ); return errors; @@ -332,7 +334,7 @@ export const validator = (permanentlyExempt: boolean) => maxUptimeHours / enabledWeekdaysCount, ); // @ts-expect-error - errors.expirationDetails?.hostUptime?.details?.addError?.( + errors.expirationDetails?.hostUptime?.details?.uptimeHours?.addError?.( `Please reduce your host uptime to a max of ${hourlyRequirement} hours per day.`, ); return errors; @@ -348,7 +350,7 @@ export const validator = (permanentlyExempt: boolean) => * @param sleepSchedule - sleepSchedule as returned from Evergreen * @returns boolean indicating whether the sleep schedule is effectively unset. */ -export const isNullSleepSchedule = (sleepSchedule: RequiredSleepSchedule) => { +export const isNullSleepSchedule = (sleepSchedule: SleepSchedule) => { if (!sleepSchedule) return true; const { dailyStartTime, dailyStopTime, wholeWeekdaysOff } = sleepSchedule; diff --git a/apps/spruce/src/constants/fieldMaps.test.ts b/apps/spruce/src/constants/fieldMaps.test.ts new file mode 100644 index 000000000..347ab61f1 --- /dev/null +++ b/apps/spruce/src/constants/fieldMaps.test.ts @@ -0,0 +1,11 @@ +import { abbreviateTimeZone } from "./fieldMaps"; + +describe("abbreviateTimeZone", () => { + it("returns the shortened time zone", () => { + expect(abbreviateTimeZone("America/New_York")).oneOf(["EST", "EDT"]); + }); + + it("catches invalid time zone", () => { + expect(abbreviateTimeZone("foo")).toBe(""); + }); +}); diff --git a/apps/spruce/src/constants/fieldMaps.ts b/apps/spruce/src/constants/fieldMaps.ts index 3fd45e10f..6f6cac8b8 100644 --- a/apps/spruce/src/constants/fieldMaps.ts +++ b/apps/spruce/src/constants/fieldMaps.ts @@ -172,6 +172,21 @@ export const prettifyTimeZone = new Map( timeZones.map(({ str, value }) => [value, str]), ); +/** + * abbreviateTimeZone returns the shortened version of a time zone + * @param tz - JS timeZone option used by toLocaleTimeString + * @returns - shortened string, or empty string if invalid time zone provided + */ +export const abbreviateTimeZone = (tz: string) => { + try { + return new Date() + .toLocaleTimeString("en-us", { timeZone: tz, timeZoneName: "short" }) + .split(" ")[2]; + } catch (e) { + return ""; + } +}; + export const listOfDateFormatStrings = [ "MM-dd-yyyy", "dd-MM-yyyy", diff --git a/apps/spruce/src/pages/spawn/spawnHost/EditSpawnHostModal.test.tsx b/apps/spruce/src/pages/spawn/spawnHost/EditSpawnHostModal.test.tsx index e282927dd..a620b68b8 100644 --- a/apps/spruce/src/pages/spawn/spawnHost/EditSpawnHostModal.test.tsx +++ b/apps/spruce/src/pages/spawn/spawnHost/EditSpawnHostModal.test.tsx @@ -158,6 +158,7 @@ describe("editSpawnHostModal", () => { , ); expect(screen.queryByDataCy("edit-spawn-host-modal")).toBeVisible(); + expect(screen.getByText("Central Time")).toBeVisible(); await user.click( screen.getByText("Use default host uptime schedule", { exact: false, @@ -309,7 +310,7 @@ const baseSpawnHost: MyHost = { ...defaultSleepSchedule, isBetaTester: true, temporarilyExemptUntil: null, - timeZone: "America/New_York", + timeZone: "America/Chicago", }, __typename: "Host", }; diff --git a/apps/spruce/src/pages/spawn/spawnHost/EditSpawnHostModal.tsx b/apps/spruce/src/pages/spawn/spawnHost/EditSpawnHostModal.tsx index fa9de0e5a..3d3a065df 100644 --- a/apps/spruce/src/pages/spawn/spawnHost/EditSpawnHostModal.tsx +++ b/apps/spruce/src/pages/spawn/spawnHost/EditSpawnHostModal.tsx @@ -18,7 +18,6 @@ import { useLoadFormData, } from "components/Spawn/editHostModal"; import { SpruceForm } from "components/SpruceForm"; -import { defaultTimeZone } from "constants/fieldMaps"; import { useToastContext } from "context/toast"; import { EditSpawnHostMutation, @@ -42,7 +41,8 @@ export const EditSpawnHostModal: React.FC = ({ }) => { const dispatchToast = useToastContext(); const { sendEvent } = useSpawnAnalytics(); - const timeZone = useUserTimeZone() || defaultTimeZone; + const timeZone = + useUserTimeZone() || Intl.DateTimeFormat().resolvedOptions().timeZone; const { disableExpirationCheckbox, @@ -79,11 +79,10 @@ export const EditSpawnHostModal: React.FC = ({ expirationDetails: { expiration: host.expiration ? host.expiration.toString() : null, noExpiration: host.noExpiration, - // @ts-expect-error: FIXME. This comment was added by an automated script. - hostUptime: isNullSleepSchedule(host?.sleepSchedule) - ? getHostUptimeFromGql(defaultSleepSchedule) - : // @ts-expect-error: FIXME. This comment was added by an automated script. - getHostUptimeFromGql(host.sleepSchedule), + hostUptime: + host?.sleepSchedule && !isNullSleepSchedule(host?.sleepSchedule) + ? getHostUptimeFromGql(host.sleepSchedule) + : getHostUptimeFromGql({ ...defaultSleepSchedule, timeZone }), }, publicKeySection: { useExisting: true, publicKeyNameDropdown: "" }, }; @@ -122,7 +121,8 @@ export const EditSpawnHostModal: React.FC = ({ // @ts-expect-error: FIXME. This comment was added by an automated script. noExpirationCheckboxTooltip, permanentlyExempt: !!host.sleepSchedule?.permanentlyExempt, - timeZone, + timeZone: + formState?.expirationDetails?.hostUptime?.details?.timeZone || timeZone, volumes, }); @@ -159,7 +159,6 @@ export const EditSpawnHostModal: React.FC = ({ hostId: host.id, myPublicKeys: publicKeys, oldUserTags: userTags, - timeZone, }); const [hasChanges, mutationParams] = computeDiff( diff --git a/apps/spruce/src/pages/spawn/spawnHost/spawnHostButton/SpawnHostModal.tsx b/apps/spruce/src/pages/spawn/spawnHost/spawnHostButton/SpawnHostModal.tsx index ee83bfdbb..2bf5629e0 100644 --- a/apps/spruce/src/pages/spawn/spawnHost/spawnHostButton/SpawnHostModal.tsx +++ b/apps/spruce/src/pages/spawn/spawnHost/spawnHostButton/SpawnHostModal.tsx @@ -16,7 +16,6 @@ import { FormState, } from "components/Spawn/spawnHostModal"; import { SpruceForm } from "components/SpruceForm"; -import { defaultTimeZone } from "constants/fieldMaps"; import { useToastContext } from "context/toast"; import { SpawnHostMutation, @@ -41,7 +40,8 @@ export const SpawnHostModal: React.FC = ({ }) => { const dispatchToast = useToastContext(); const spawnAnalytics = useSpawnAnalytics(); - const timeZone = useUserTimeZone() || defaultTimeZone; + const timeZone = + useUserTimeZone() || Intl.DateTimeFormat().resolvedOptions().timeZone; // Handle distroId, taskId query param const { search } = useLocation(); @@ -118,7 +118,8 @@ export const SpawnHostModal: React.FC = ({ isMigration: false, isVirtualWorkstation: !!selectedDistro?.isVirtualWorkStation, spawnTaskData: spawnTaskData?.task, - timeZone, + timeZone: + formState?.expirationDetails?.hostUptime?.details?.timeZone || timeZone, useSetupScript: !!formState?.setupScriptSection?.defineSetupScriptCheckbox, useProjectSetupScript: !!formState?.loadData?.runProjectSpecificSetupScript, });