diff --git a/src/components/Measurements/widgets/EntryForm.tsx b/src/components/Measurements/widgets/EntryForm.tsx index 936d3092..7a78bf5a 100644 --- a/src/components/Measurements/widgets/EntryForm.tsx +++ b/src/components/Measurements/widgets/EntryForm.tsx @@ -92,6 +92,7 @@ export const EntryForm = ({ entry, closeFn, categoryId }: EntryFormProps) => { label={t('date')} value={dateValue} disableFuture={true} + onChange={(newValue) => { if (newValue) { formik.setFieldValue('date', newValue.toJSDate()); diff --git a/src/components/WorkoutRoutines/widgets/forms/RoutineForm.tsx b/src/components/WorkoutRoutines/widgets/forms/RoutineForm.tsx index c44b9f1a..5924b6e7 100644 --- a/src/components/WorkoutRoutines/widgets/forms/RoutineForm.tsx +++ b/src/components/WorkoutRoutines/widgets/forms/RoutineForm.tsx @@ -1,12 +1,15 @@ import { Button } from "@mui/material"; import Grid from '@mui/material/Grid2'; +import { DatePicker, LocalizationProvider } from "@mui/x-date-pickers"; +import { AdapterLuxon } from "@mui/x-date-pickers/AdapterLuxon"; import { WgerTextField } from "components/Common/forms/WgerTextField"; import { Routine } from "components/WorkoutRoutines/models/Routine"; import { useAddRoutineQuery, useEditRoutineQuery } from "components/WorkoutRoutines/queries/routines"; import { Form, Formik } from "formik"; -import React from 'react'; +import { DateTime } from "luxon"; +import React, { useState } from 'react'; import { useTranslation } from "react-i18next"; -import { dateToYYYYMMDD } from "utils/date"; +import { DEFAULT_WORKOUT_DURATION, MIN_WORKOUT_DURATION } from "utils/consts"; import * as yup from 'yup'; interface RoutineFormProps { @@ -16,9 +19,18 @@ interface RoutineFormProps { export const RoutineForm = ({ routine, closeFn }: RoutineFormProps) => { - const [t] = useTranslation(); + const [t, i18n] = useTranslation(); const addRoutineQuery = useAddRoutineQuery(); const editRoutineQuery = useEditRoutineQuery(routine?.id!); + + /* + * Note: Controlling the state of the dates manually, otherwise some undebuggable errors + * about missing properties occur deep within formik. + */ + const [startValue, setStartValue] = useState(routine ? DateTime.fromJSDate(routine.start) : DateTime.now()); + const [endValue, setEndValue] = useState(routine ? DateTime.fromJSDate(routine.end) : DateTime.now().plus({ weeks: DEFAULT_WORKOUT_DURATION })); + + const validationSchema = yup.object({ name: yup .string() @@ -34,6 +46,24 @@ export const RoutineForm = ({ routine, closeFn }: RoutineFormProps) => { end: yup .date() .required() + .min( + yup.ref('start'), + "end date must be after start date" + ) + .test( + 'hasMinimumDuration', + "the workout needs to be at least 2 weeks long", + function (value) { + const startDate = this.parent.start; + if (startDate && value) { + const startDateTime = DateTime.fromJSDate(startDate); + const endDateTime = DateTime.fromJSDate(value); + + return endDateTime.diff(startDateTime, 'weeks').weeks >= MIN_WORKOUT_DURATION; + } + return true; + } + ), }); @@ -42,18 +72,28 @@ export const RoutineForm = ({ routine, closeFn }: RoutineFormProps) => { initialValues={{ name: routine ? routine.name : '', description: routine ? routine.description : '', - start: routine ? routine.start : dateToYYYYMMDD(new Date()), - end: routine ? routine.end : dateToYYYYMMDD(new Date()), - // eslint-disable-next-line camelcase + start: startValue, + end: endValue, }} validationSchema={validationSchema} onSubmit={async (values) => { + console.log(values); + if (routine) { - editRoutineQuery.mutate({ ...values, id: routine.id }); + editRoutineQuery.mutate({ + ...values, + start: values.start?.toISODate()!, + end: values.end?.toISODate()!, + id: routine.id + }); } else { - addRoutineQuery.mutate(values); + addRoutineQuery.mutate({ + ...values, + start: values.start?.toISODate()!, + end: values.end?.toISODate()!, + }); } // if closeFn is defined, close the modal (this form does not have to be displayed in one) @@ -64,15 +104,55 @@ export const RoutineForm = ({ routine, closeFn }: RoutineFormProps) => { > {formik => (
- + - + + { + if (newValue) { + formik.setFieldValue('start', newValue); + } + setStartValue(newValue); + }} + slotProps={{ + textField: { + variant: "standard", + fullWidth: true, + error: formik.touched.start && Boolean(formik.errors.start), + helperText: formik.touched.start && formik.errors.start + } + }} + /> + - + + { + if (newValue) { + formik.setFieldValue('end', newValue); + } + setEndValue(newValue); + }} + slotProps={{ + textField: { + variant: "standard", + fullWidth: true, + error: formik.touched.end && Boolean(formik.errors.end), + helperText: formik.touched.end && formik.errors.end + } + }} + /> + diff --git a/src/utils/consts.ts b/src/utils/consts.ts index f368411f..c607147c 100644 --- a/src/utils/consts.ts +++ b/src/utils/consts.ts @@ -5,6 +5,13 @@ export const ENGLISH_LANGUAGE_CODE = 'en'; export const MIN_ACCOUNT_AGE = MIN_ACCOUNT_AGE_TO_TRUST || 21; +// Duration in weeks +export const MIN_WORKOUT_DURATION = 2; + +// Duration in weeks +export const DEFAULT_WORKOUT_DURATION = 12; + + export const REP_UNIT_REPETITIONS = 1; export const REP_UNIT_TILL_FAILURE = 2;