Skip to content

Commit

Permalink
Quality of life improvements to the forms
Browse files Browse the repository at this point in the history
  • Loading branch information
rolandgeider committed Aug 1, 2023
1 parent 67b2e5d commit de906cb
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 40 deletions.
4 changes: 4 additions & 0 deletions public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"delete": "Delete",
"deleteConfirmation": "Are you sure you want to delete \"{{name}}\"?",
"add": "Add",
"close": "Close",
"difference": "Difference",
"days": "Days",
"loading": "Loading...",
Expand Down Expand Up @@ -61,8 +62,11 @@
"nutrition": {
"plans": "Nutritional plans",
"addNutritionalDiary": "Add nutrition diary entry",
"addMeal": "Add meal",
"addMealItem": "Add ingredient to meal",
"nutritionalDiary": "Nutrition diary",
"valueUnitG": "{{value}}g",
"gramShort": "g",
"valueUnitPercent": "{{value}}%",
"valueEnergyKcalKj": "{{kcal}} kcal / {{kj}} kJ",
"searchIngredientName": "Search by ingredient name",
Expand Down
2 changes: 1 addition & 1 deletion src/components/Nutrition/components/PlanDetail.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe("Test the PlanDetail component", () => {
// Act
render(
<QueryClientProvider client={queryClient}>
<MemoryRouter initialEntries={['/nutrition/42/view']}>
<MemoryRouter initialEntries={['/nutrition/101/view']}>
<Routes>
<Route path="nutrition/:planId/view" element={<PlanDetail />} />
</Routes>
Expand Down
77 changes: 55 additions & 22 deletions src/components/Nutrition/components/PlanDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,24 +50,44 @@ import { useTranslation } from "react-i18next";
import { useParams } from "react-router-dom";


const MealItemListItem = (props: { mealItem: MealItem }) => {
return <ListItem>
<ListItemAvatar>
<Avatar>
{/*{props.mealItem.ingredient?.image ?*/}
{/* <Avatar alt="" src={`${SERVER_URL}${option.data.image}`} variant="rounded" />*/}
{/* : <PhotoIcon fontSize="large" />}*/}
{/*<ImageIcon />*/}
<PhotoIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary={`${props.mealItem.amountString} ${props.mealItem.ingredient?.name}`} />
</ListItem>;
const MealItemListItem = (props: { mealItem: MealItem, planId: number, mealId: number }) => {
const [expandForm, setExpandForm] = useState(false);
const handleToggleForm = () => setExpandForm(!expandForm);


return <>
<ListItem>
<ListItemAvatar onClick={handleToggleForm}>
<Avatar>
{/*{props.mealItem.ingredient?.image ?*/}
{/* <Avatar alt="" src={`${SERVER_URL}${option.data.image}`} variant="rounded" />*/}
{/* : <PhotoIcon fontSize="large" />}*/}
{/*<ImageIcon />*/}
<PhotoIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary={`${props.mealItem.amountString} ${props.mealItem.ingredient?.name}`} />

</ListItem>
<ListItem>
<Collapse in={expandForm} timeout="auto" unmountOnExit sx={{ width: '100%' }}>
<ListItemText>
<MealItemForm
planId={props.planId}
mealId={props.mealId}
item={props.mealItem}
closeFn={handleToggleForm}
/>
</ListItemText>
</Collapse>
</ListItem>
</>;
};

const IngredientTableRow = (props: { item: MealItem | DiaryEntry }) => {
const [t] = useTranslation();


return <TableRow key={props.item.id}>
<TableCell sx={{ pr: 0 }}>
<Avatar>
Expand Down Expand Up @@ -101,10 +121,16 @@ const MealDetail = (props: { meal: Meal, planId: number }) => {
const handleToggleExpandStats = () => setExpandViewStats(!expandViewStats);

const [expandItemForm, setExpandItemForm] = useState(false);
const handleToggleExpandItemForm = () => setExpandItemForm(!expandItemForm);
const handleToggleExpandItemForm = () => {
setExpandItemForm(!expandItemForm);
setExpandDiaryForm(false);
};

const [expandDiaryForm, setExpandDiaryForm] = useState(false);
const handleToggleExpandDiaryForm = () => setExpandDiaryForm(!expandDiaryForm);
const handleToggleExpandDiaryForm = () => {
setExpandDiaryForm(!expandDiaryForm);
setExpandItemForm(false);
};

return <Card>
<CardHeader
Expand Down Expand Up @@ -176,6 +202,8 @@ const MealDetail = (props: { meal: Meal, planId: number }) => {
{props.meal.items.map((item) => (
<MealItemListItem
mealItem={item}
planId={props.planId}
mealId={props.meal.id}
key={item.id}
/>
))}
Expand All @@ -192,7 +220,7 @@ const MealDetail = (props: { meal: Meal, planId: number }) => {
</IconButton>
</Tooltip>

{props.meal.id !== PSEUDO_MEAL_ID && <Tooltip title={t('add')}>
{props.meal.id !== PSEUDO_MEAL_ID && <Tooltip title={t('nutrition.addMeal')}>
<IconButton onClick={handleToggleExpandItemForm}>
<Add />
</IconButton>
Expand All @@ -206,16 +234,20 @@ const MealDetail = (props: { meal: Meal, planId: number }) => {
</CardActions>
<CardContent>
<Collapse in={expandItemForm} timeout="auto" unmountOnExit>
<MealItemForm planId={props.planId} mealId={props.meal.id} closeFn={handleToggleExpandItemForm} />
<p><b>{t('nutrition.addMealItem')}</b></p>
<MealItemForm
planId={props.planId}
mealId={props.meal.id}
closeFn={handleToggleExpandItemForm} />
</Collapse>
<Collapse in={expandDiaryForm} timeout="auto" unmountOnExit>
<p><b>{t('nutrition.addNutritionalDiary')}</b></p>
<NutritionDiaryEntryForm
closeFn={handleToggleExpandDiaryForm}
planId={props.planId}
mealId={props.meal.id !== PSEUDO_MEAL_ID ? props.meal.id : null} />
</Collapse>
</CardContent>

</Card>;
};

Expand All @@ -239,15 +271,16 @@ export const PlanDetail = () => {
<MealDetail meal={meal} planId={planQuery.data!.id} key={meal.id} />
)}
<MealDetail meal={planQuery.data!.pseudoMealOthers} planId={planQuery.data!.id} key={-1} />
<Tooltip title={t('add')}>

<Tooltip title={t('nutrition.addMeal')}>
<IconButton onClick={handleToggleExpandedForm}>
<Add />
</IconButton>
</Tooltip>

<Collapse in={expandedForm} timeout="auto" unmountOnExit>
<CardContent>
<MealForm planId={planQuery.data!.id} closeFn={handleToggleExpandedForm} />
</CardContent>
<p><b>{t('nutrition.addMeal')}</b></p>
<MealForm planId={planQuery.data!.id} closeFn={handleToggleExpandedForm} />
</Collapse>

<Typography gutterBottom variant="h5">
Expand Down
23 changes: 18 additions & 5 deletions src/components/Nutrition/widgets/IngredientAutcompleter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,25 @@ import { SERVER_URL } from "utils/url";

type IngredientAutocompleterProps = {
callback: Function;
initialIngredient?: string | null;
}

export function IngredientAutocompleter({ callback }: IngredientAutocompleterProps) {
const [value, setValue] = useState<IngredientSearchResponse | null>(null);
export function IngredientAutocompleter({ callback, initialIngredient }: IngredientAutocompleterProps) {
const initialData = initialIngredient
? {
value: initialIngredient,
data: {
id: -1,
name: initialIngredient,
image: null,
// eslint-disable-next-line camelcase
image_thumbnail: null,
}
}
: null;


const [value, setValue] = useState<IngredientSearchResponse | null>(initialData);
const [inputValue, setInputValue] = useState('');
const [options, setOptions] = useState<readonly IngredientSearchResponse[]>([]);
const [t] = useTranslation();
Expand Down Expand Up @@ -46,9 +61,7 @@ export function IngredientAutocompleter({ callback }: IngredientAutocompleterPro
return (
<Autocomplete
id="ingredient-autocomplete"
getOptionLabel={(option) =>
option.value
}
getOptionLabel={(option) => option.value}
data-testid="autocomplete"
filterOptions={(x) => x}
options={options}
Expand Down
7 changes: 5 additions & 2 deletions src/components/Nutrition/widgets/forms/MealForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,16 @@ export const MealForm = ({ meal, planId, closeFn }: MealFormProps) => {
/>}
/>
</LocalizationProvider>
<Stack direction="row" justifyContent="end" sx={{ mt: 2 }}>
<Stack direction="row" justifyContent="end" spacing={2}>
{closeFn !== undefined
&& <Button color="primary" variant="outlined" onClick={() => closeFn()}>
{t('close')}
</Button>}
<Button
disabled={addMealQuery.isLoading || editMealQuery.isLoading}
color="primary"
variant="contained"
type="submit"
sx={{ mt: 2 }}
>
{t('submit')}
</Button>
Expand Down
44 changes: 36 additions & 8 deletions src/components/Nutrition/widgets/forms/MealItemForm.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Button, Stack, TextField } from "@mui/material";
import { Button, InputAdornment, Stack, TextField } from "@mui/material";
import { MealItem } from "components/Nutrition/models/mealItem";
import { useAddMealItemQuery, useEditMealItemQuery } from "components/Nutrition/queries";
import { useAddMealItemQuery, useDeleteMealItemQuery, useEditMealItemQuery } from "components/Nutrition/queries";
import { IngredientAutocompleter } from "components/Nutrition/widgets/IngredientAutcompleter";
import { Form, Formik } from "formik";
import { useTranslation } from "react-i18next";
Expand All @@ -11,14 +11,26 @@ type MealItemFormProps = {
planId: number,
mealId: number,
item?: MealItem,
closeFn?: Function,
closeFn?: () => void,
}

export const MealItemForm = ({ planId, item, mealId, closeFn }: MealItemFormProps) => {

const [t] = useTranslation();
const addMealItemQuery = useAddMealItemQuery(planId);
const editMealItemQuery = useEditMealItemQuery(planId);
const deleteMealItemQuery = useDeleteMealItemQuery(planId);

const handleDelete = () => {
if (item) {
deleteMealItemQuery.mutate(item.id);
}

if (closeFn) {
closeFn();
}
};

const validationSchema = yup.object({
amount: yup
.number()
Expand All @@ -34,8 +46,8 @@ export const MealItemForm = ({ planId, item, mealId, closeFn }: MealItemFormProp
return (
<Formik
initialValues={{
amount: 0,
ingredient: 0,
amount: item ? item.amount : 0,
ingredient: item ? item.ingredientId : 0,
}}
validationSchema={validationSchema}
onSubmit={async (values) => {
Expand All @@ -62,18 +74,34 @@ export const MealItemForm = ({ planId, item, mealId, closeFn }: MealItemFormProp
<Form>
<Stack spacing={2}>
<IngredientAutocompleter
callback={(value: IngredientSearchResponse) => formik.setFieldValue('ingredient', value.data.id)} />
callback={(value: IngredientSearchResponse | null) => formik.setFieldValue('ingredient', value ? value.data.id : null)}
initialIngredient={item ? item.ingredient?.name : null}
/>
<TextField
fullWidth
id="amount"
label={'amount'}
InputProps={{
endAdornment: <InputAdornment position="end">{t('nutrition.gramShort')}</InputAdornment>
}}

error={formik.touched.amount && Boolean(formik.errors.amount)}
helperText={formik.touched.amount && formik.errors.amount}
{...formik.getFieldProps('amount')}
/>

<Stack direction="row" justifyContent="end" sx={{ mt: 2 }}>
<Button color="primary" variant="contained" type="submit" sx={{ mt: 2 }}>
<Stack direction="row" justifyContent="end" spacing={2}>
{closeFn !== undefined
&& <Button color="error" variant="outlined" onClick={handleDelete}>
{t('delete')}
</Button>}

{closeFn !== undefined
&& <Button color="primary" variant="outlined" onClick={() => closeFn()}>
{t('close')}
</Button>}

<Button color="primary" variant="contained" type="submit">
{t('submit')}
</Button>
</Stack>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,12 @@ export const NutritionDiaryEntryForm = ({ planId, entry, mealId, closeFn }: Nutr
}}
/>
</LocalizationProvider>
<Stack direction="row" justifyContent="end" sx={{ mt: 2 }}>
<Button color="primary" variant="contained" type="submit" sx={{ mt: 2 }}>
<Stack direction="row" justifyContent="end" spacing={2}>
{closeFn !== undefined
&& <Button color="primary" variant="outlined" onClick={() => closeFn()}>
{t('close')}
</Button>}
<Button color="primary" variant="contained" type="submit">
{t('submit')}
</Button>
</Stack>
Expand Down

0 comments on commit de906cb

Please sign in to comment.