import { CheckBoxOutlineBlank } from "@mui/icons-material"; import ArrowDropUpIcon from '@mui/icons-material/ArrowDropUp'; import CheckBoxIcon from '@mui/icons-material/CheckBox'; import SettingsIcon from '@mui/icons-material/Settings'; import { Button, Divider, IconButton, ListItemIcon, ListItemText, Menu, MenuItem, TextField } from "@mui/material"; import { LoadingProgressIcon } from "@/core/ui/LoadingWidget/LoadingWidget"; import { BaseConfig, OPERATION_REPLACE, REQUIREMENTS_VALUES, RequirementsType, RIR_VALUES_SELECT } from "@/components/Routines/models/BaseConfig"; import { useAddMaxRepsConfigQuery, useAddMaxRestConfigQuery, useAddMaxRiRConfigQuery, useAddMaxWeightConfigQuery, useAddNrOfSetsConfigQuery, useAddRepsConfigQuery, useAddRestConfigQuery, useAddRiRConfigQuery, useAddWeightConfigQuery, useDeleteMaxRepsConfigQuery, useDeleteMaxRestConfigQuery, useDeleteMaxRiRConfigQuery, useDeleteMaxWeightConfigQuery, useDeleteNrOfSetsConfigQuery, useDeleteRepsConfigQuery, useDeleteRestConfigQuery, useDeleteRiRConfigQuery, useDeleteWeightConfigQuery, useEditMaxRepsConfigQuery, useEditMaxRestConfigQuery, useEditMaxRiRConfigQuery, useEditMaxWeightConfigQuery, useEditNrOfSetsConfigQuery, useEditRepsConfigQuery, useEditRestConfigQuery, useEditRiRConfigQuery, useEditWeightConfigQuery } from "@/components/Routines/queries"; import { useAddMaxNrOfSetsConfigQuery, useDeleteMaxNrOfSetsConfigQuery, useEditMaxNrOfSetsConfigQuery } from "@/components/Routines/queries/configs"; import { useFormikContext } from "formik"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import { DEBOUNCE_ROUTINE_FORMS } from "@/core/lib/consts"; import { errorsToString } from "@/core/lib/forms"; // eslint-disable-next-line @typescript-eslint/no-explicit-any export const QUERY_MAP: { [key: string]: any } = { 'weight': { edit: useEditWeightConfigQuery, add: useAddWeightConfigQuery, delete: useDeleteWeightConfigQuery }, 'max-weight': { edit: useEditMaxWeightConfigQuery, add: useAddMaxWeightConfigQuery, delete: useDeleteMaxWeightConfigQuery }, 'reps': { edit: useEditRepsConfigQuery, add: useAddRepsConfigQuery, delete: useDeleteRepsConfigQuery }, 'max-reps': { edit: useEditMaxRepsConfigQuery, add: useAddMaxRepsConfigQuery, delete: useDeleteMaxRepsConfigQuery }, 'sets': { edit: useEditNrOfSetsConfigQuery, add: useAddNrOfSetsConfigQuery, delete: useDeleteNrOfSetsConfigQuery }, 'max-sets': { edit: useEditMaxNrOfSetsConfigQuery, add: useAddMaxNrOfSetsConfigQuery, delete: useDeleteMaxNrOfSetsConfigQuery }, 'rest': { edit: useEditRestConfigQuery, add: useAddRestConfigQuery, delete: useDeleteRestConfigQuery }, 'max-rest': { edit: useEditMaxRestConfigQuery, add: useAddMaxRestConfigQuery, delete: useDeleteMaxRestConfigQuery }, 'rir': { edit: useEditRiRConfigQuery, add: useAddRiRConfigQuery, delete: useDeleteRiRConfigQuery }, 'max-rir': { edit: useEditMaxRiRConfigQuery, add: useAddMaxRiRConfigQuery, delete: useDeleteMaxRiRConfigQuery }, }; export type ConfigType = 'weight' | 'max-weight' | 'reps' | 'max-reps' | 'sets' | 'max-sets' | 'rest' | 'max-rest' | 'rir' | 'max-rir'; export const SlotBaseConfigValueField = (props: { config?: BaseConfig, routineId: number, slotEntryId?: number, type: ConfigType, }) => { const { t } = useTranslation(); const { edit: editQuery, add: addQuery, delete: deleteQuery } = QUERY_MAP[props.type]; const editQueryHook = editQuery(props.routineId); const addQueryHook = addQuery(props.routineId); const deleteQueryHook = deleteQuery(props.routineId); const [value, setValue] = useState(props.config?.value || ''); const [manualValidationError, setManualValidationError] = useState(null); const [timer, setTimer] = useState(null); const isInt = ['sets', 'max-sets', 'rest', 'max-rest'].includes(props.type); const parseFunction = isInt ? parseInt : parseFloat; let title = ''; switch (props.type) { case "weight": title = t('weight'); break; case "max-weight": title = 'max ' + t('weight'); break; case "reps": title = t('server.repetitions'); break; case "max-reps": title = t('server.max_reps'); break; case "sets": title = t('routines.sets'); break; case "max-sets": title = 'max ' + t('routines.sets'); break; case "rest": title = t('routines.restTime'); break; case "max-rest": title = 'max ' + t('routines.restTime'); break; case "rir": title = t('routines.rir'); break; case "max-rir": title = 'max ' + t('routines.rir'); break; } const handleData = (value: string) => { const data = { // eslint-disable-next-line camelcase slot_entry: props.slotEntryId, value: parseFunction(value), }; if (value === '' && props.config) { deleteQueryHook.mutate(props.config.id); } else if (props.config) { editQueryHook.mutate({ id: props.config.id, ...data }); } else { addQueryHook.mutate({ iteration: 1, operation: OPERATION_REPLACE, step: 'abs', requirements: null, ...data }); } }; const onChange = (text: string) => { setValue(text); if (text !== '' && Number.isNaN(parseFunction(text))) { setManualValidationError(t(isInt ? 'forms.enterInteger' : 'forms.enterNumber')); return; } else { setManualValidationError(null); } if (timer) { clearTimeout(timer); } setTimer(setTimeout(() => handleData(text), DEBOUNCE_ROUTINE_FORMS)); }; const isPending = editQueryHook.isPending || addQueryHook.isPending || deleteQueryHook.isPending; const isError = editQueryHook.isError || addQueryHook.isError || deleteQueryHook.isError; const errorMessage = errorsToString(editQueryHook.error?.response?.data) || errorsToString(addQueryHook.error?.response?.data) || errorsToString(deleteQueryHook.error?.response?.data); return ( }, htmlInput: { "data-testid": `${props.type}-field`, inputMode: isInt ? 'numeric' : 'decimal' }, }} label={title} value={value} fullWidth variant="standard" disabled={isPending} onChange={e => onChange(e.target.value)} error={!!manualValidationError || isError} helperText={manualValidationError || errorMessage} /> ); }; export const ConfigDetailsRequirementsField = (props: { fieldName: string, values: RequirementsType[], disabled?: boolean }) => { const { setFieldValue } = useFormikContext(); const { t } = useTranslation(); const disable = props.disabled ?? false; const [selectedElements, setSelectedElements] = useState(props.values); const [anchorEl, setAnchorEl] = React.useState(null); const handleSelection = (value: RequirementsType) => { // if the value is not in selectedElements, add it if (!selectedElements.includes(value)) { setSelectedElements([...selectedElements, value]); } else { setSelectedElements(selectedElements.filter((e) => e !== value)); } }; const handleSubmit = async () => { await setFieldValue(props.fieldName, selectedElements); setAnchorEl(null); }; return <> setAnchorEl(event.currentTarget)} > {anchorEl ? : } setAnchorEl(null)} > {...REQUIREMENTS_VALUES.map((e, index) => handleSelection(e as unknown as RequirementsType)}> {selectedElements.includes(e as unknown as RequirementsType) ? : } {e} )} ; }; export const ConfigDetailsRiRField = (props: { config?: BaseConfig, slotEntryId?: number, routineId: number }) => { const editRiRQuery = useEditRiRConfigQuery(props.routineId); const deleteRiRQuery = useDeleteRiRConfigQuery(props.routineId); const addRiRQuery = useAddRiRConfigQuery(props.routineId); const handleData = (value: string) => { const data = { value: parseFloat(value), }; if (value === '' && props.config) { deleteRiRQuery.mutate(props.config.id); } else if (props.config !== undefined) { editRiRQuery.mutate({ id: props.config.id, ...data }); } else { addRiRQuery.mutate({ // eslint-disable-next-line camelcase slot_entry: props.slotEntryId!, iteration: 1, operation: OPERATION_REPLACE, ...data }); } }; return handleData(e.target.value)} > {RIR_VALUES_SELECT.map((option) => ( {option.label} ))} ; }; /* * ---> These components are not needed anymore but are kept here in case we need * to edit these fields individually in the future */ /* export const AddEntryDetailsButton = (props: { iteration: number, routineId: number, slotEntryId: number, type: ConfigType }) => { const { add: addQuery } = QUERY_MAP[props.type]; const addQueryHook = addQuery(props.routineId); const handleData = () => { addQueryHook.mutate({ slot_entry: props.slotEntryId!, iteration: props.iteration, value: 0, operation: OPERATION_REPLACE, }); }; return (<> ); }; export const DeleteEntryDetailsButton = (props: { configId: number, routineId: number, type: ConfigType, disable?: boolean }) => { const disable = props.disable ?? false; const { delete: deleteQuery } = QUERY_MAP[props.type]; const deleteQueryHook = deleteQuery(props.routineId); const handleData = () => { deleteQueryHook.mutate(props.configId); }; return ( ); }; export const EntryDetailsOperationField = (props: { config: BaseConfig, routineId: number, slotEntryId: number, type: ConfigType, disable?: boolean }) => { const disable = props.disable ?? false; const options = [ { value: '+', label: 'Add', }, { value: '-', label: 'Subtract', }, { value: OPERATION_REPLACE, label: 'Replace', }, ]; const { edit: editQuery } = QUERY_MAP[props.type]; const editQueryHook = editQuery(props.routineId); const handleData = (newValue: string) => { editQueryHook.mutate({ id: props.config.id, operation: newValue, }); }; return (<> handleData(e.target.value)} > {options.map((option) => ( {option.label} ))} ); }; export const EntryDetailsStepField = (props: { config: BaseConfig, routineId: number, slotEntryId: number, type: ConfigType, disable?: boolean }) => { const disable = props.disable ?? false; const options = [ { value: 'abs', label: 'absolute', }, { value: 'percent', label: 'percent', }, ]; if (props.config.iteration === 1) { options.push({ value: 'na', label: 'n/a', }); } const { edit: editQuery } = QUERY_MAP[props.type]; const editQueryHook = editQuery(props.routineId); const handleData = (newValue: string) => { editQueryHook.mutate({ id: props.config.id, step: newValue, }); }; return (<> handleData(e.target.value)} > {options.map((option) => ( {option.label} ))} ); }; */