• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wger-project / react / 15066523227

16 May 2025 10:36AM UTC coverage: 76.412%. First build
15066523227

Pull #1072

github

web-flow
Merge d99167029 into 02a87f6fe
Pull Request #1072: Better handling of dynamic translations

1345 of 1963 branches covered (68.52%)

Branch coverage included in aggregate %.

30 of 32 new or added lines in 12 files covered. (93.75%)

5377 of 6834 relevant lines covered (78.68%)

28.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

60.0
/src/components/Exercises/Add/Step6Overview.tsx
1
import NavigateNextIcon from '@mui/icons-material/NavigateNext';
2✔
2
import {
2✔
3
    Alert,
4
    AlertTitle,
5
    Box,
6
    Button,
7
    ImageListItem,
8
    Table,
9
    TableBody,
10
    TableCell,
11
    TableContainer,
12
    TableRow,
13
    Typography
14
} from "@mui/material";
15
import Grid from '@mui/material/Grid';
2✔
16
import ImageList from "@mui/material/ImageList";
2✔
17
import { LoadingPlaceholder } from "components/Core/LoadingWidget/LoadingWidget";
2✔
18
import { StepProps } from "components/Exercises/Add/AddExerciseStepper";
19
import { Note } from "components/Exercises/models/note";
2✔
20
import { useCategoriesQuery, useEquipmentQuery, useLanguageQuery, useMusclesQuery } from "components/Exercises/queries";
2✔
21
import { useProfileQuery } from "components/User/queries/profile";
2✔
22
import React, { useState } from "react";
2✔
23
import { useTranslation } from "react-i18next";
2✔
24
import { useNavigate } from "react-router-dom";
2✔
25
import { addExercise, addTranslation, postAlias, postExerciseImage } from "services";
2✔
26
import { addNote } from "services/note";
2✔
27
import { addVariation } from "services/variation";
2✔
28
import { useExerciseSubmissionStateValue } from "state";
2✔
29
import { ENGLISH_LANGUAGE_ID } from "utils/consts";
2✔
30
import { makeLink, WgerLink } from "utils/url";
2✔
31

32
export const Step6Overview = ({ onBack }: StepProps) => {
2✔
33
    const [t, i18n] = useTranslation();
2✔
34
    const [state] = useExerciseSubmissionStateValue();
2✔
35

36
    const navigate = useNavigate();
2✔
37
    const categoryQuery = useCategoriesQuery();
2✔
38
    const languageQuery = useLanguageQuery();
2✔
39
    const musclesQuery = useMusclesQuery();
2✔
40
    const equipmentQuery = useEquipmentQuery();
2✔
41
    const profileQuery = useProfileQuery();
2✔
42

43
    // This could be handled better and more cleanly...
44
    type submissionStatus = 'initial' | 'loading' | 'done';
45
    const [submissionState, setSubmissionState] = useState<submissionStatus>('initial',);
2✔
46

47
    const submitExercise = async () => {
2✔
48

49
        setSubmissionState('loading');
×
50

51
        // Create a new variation object if needed
52
        // TODO: PATCH the other exercise base (newVariationExerciseId) with the new variation id
53
        let variationId;
54
        if (state.newVariationExerciseId !== null) {
×
55
            variationId = await addVariation();
×
56
        } else {
57
            variationId = state.variationId;
×
58
        }
59

60
        // Create the exercise
61
        const exerciseId = await addExercise(
×
62
            state.category as number,
63
            state.equipment,
64
            state.muscles,
65
            state.musclesSecondary,
66
            variationId,
67
            profileQuery.data!.username
68
        );
69

70
        // Create the English translation
71
        const translation = await addTranslation({
×
72
            exerciseId: exerciseId,
73
            languageId: ENGLISH_LANGUAGE_ID,
74
            name: state.nameEn,
75
            description: state.descriptionEn,
76
            author: profileQuery.data!.username
77
        });
78

79
        // For each entry in alternative names, create a new alias
80
        for (const alias of state.alternativeNamesEn) {
×
81
            await postAlias(translation.id!, alias);
×
82
        }
83

84
        // Post the images
85
        for (const image of state.images) {
×
86
            await postExerciseImage({
×
87
                exerciseId: exerciseId,
88
                image: image.file,
89
                imageData: image,
90
            });
91
        }
92

93
        // Post the notes
94
        for (const note of state.notesEn) {
×
95
            await addNote(new Note(null, translation.id!, note));
×
96
        }
97

98

99
        // Create the translation if needed
100
        if (state.languageId !== null) {
×
101
            const exerciseI18n = await addTranslation({
×
102
                exerciseId: exerciseId,
103
                languageId: state.languageId,
104
                name: state.nameI18n,
105
                description: state.descriptionI18n,
106
                author: profileQuery.data!.username
107
            });
108

109
            for (const alias of state.alternativeNamesI18n) {
×
110
                await postAlias(exerciseI18n.id!, alias);
×
111
            }
112

113
            for (const note of state.notesI18n) {
×
114
                await addNote(new Note(null, exerciseI18n.id!, note));
×
115
            }
116
        }
117

118
        console.log("Exercise created");
×
119
        setSubmissionState('done');
×
120
    };
121

122
    const navigateToOverview = () => {
2✔
123
        navigate(makeLink(WgerLink.EXERCISE_OVERVIEW, i18n.language));
×
124
    };
125

126
    return equipmentQuery.isLoading || languageQuery.isLoading || musclesQuery.isLoading || categoryQuery.isLoading
2!
127
        ? <LoadingPlaceholder />
128
        : <>
129
            <Typography variant={"h6"}>
130
                {t('exercises.step1HeaderBasics')}
131
            </Typography>
132
            <TableContainer>
133
                <Table>
134
                    <TableBody>
135
                        <TableRow>
136
                            <TableCell>{t('name')}</TableCell>
137
                            <TableCell>{state.nameEn}</TableCell>
138
                        </TableRow>
139
                        <TableRow>
140
                            <TableCell>{t('exercises.alternativeNames')}</TableCell>
141
                            <TableCell>{state.alternativeNamesEn.join(", ")}</TableCell>
142
                        </TableRow>
143
                        <TableRow>
144
                            <TableCell>{t('description')}</TableCell>
145
                            <TableCell>{state.descriptionEn}</TableCell>
146
                        </TableRow>
147
                        <TableRow>
148
                            <TableCell>{t('exercises.notes')}</TableCell>
149
                            <TableCell>{state.notesEn.map(note => <>{note}<br /></>)}</TableCell>
×
150
                        </TableRow>
151
                        <TableRow>
152
                            <TableCell>{t('category')}</TableCell>
153
                            <TableCell>{categoryQuery.data!.find(c => c.id === state.category)!.translatedName}</TableCell>
2✔
154
                        </TableRow>
155
                        <TableRow>
156
                            <TableCell>{t('exercises.equipment')}</TableCell>
157
                            <TableCell>{state.equipment.map(e => equipmentQuery.data!.find(value => value.id === e)!.translatedName).join(', ')}</TableCell>
4✔
158
                        </TableRow>
159
                        <TableRow>
160
                            <TableCell>{t('exercises.muscles')}</TableCell>
161
                            <TableCell>{state.muscles.map(m => musclesQuery.data!.find(value => value.id === m)!.getName()).join(', ')}</TableCell>
4✔
162
                        </TableRow>
163
                        <TableRow>
164
                            <TableCell>{t('exercises.secondaryMuscles')}</TableCell>
NEW
165
                            <TableCell>{state.musclesSecondary.map(m => musclesQuery.data!.find(value => value.id === m)!.getName()).join(', ')}</TableCell>
×
166
                        </TableRow>
167
                        <TableRow>
168
                            <TableCell>{t('exercises.variations')}</TableCell>
169
                            <TableCell>{state.variationId} / {state.newVariationExerciseId}</TableCell>
170
                        </TableRow>
171
                    </TableBody>
172
                </Table>
173
            </TableContainer>
174
            {state.images.length > 0 && (
2!
175
                <ImageList
176
                    cols={3}
177
                    style={{ maxHeight: "200px", }}>
178
                    {state.images.map(imageEntry => (
179
                        <ImageListItem key={imageEntry.url}>
×
180
                            <img
181
                                style={{ maxHeight: "200px", maxWidth: "200px" }}
182
                                src={imageEntry.url}
183
                                alt=""
184
                                loading="lazy"
185
                            />
186
                        </ImageListItem>
187
                    ))}
188
                </ImageList>
189
            )}
190

191

192
            {state.languageId !== null && (
4✔
193
                <>
194
                    <Typography variant={"h6"} sx={{ mt: 3 }}>
195
                        {languageQuery.data!.find(l => l.id === state.languageId)!.nameLong}
6✔
196
                    </Typography>
197
                    <TableContainer>
198
                        <Table>
199
                            <TableBody>
200
                                <TableRow>
201
                                    <TableCell>{t('name')}</TableCell>
202
                                    <TableCell>
203
                                        {state.nameI18n}
204
                                    </TableCell>
205
                                </TableRow>
206

207
                                <TableRow>
208
                                    <TableCell>{t('exercises.alternativeNames')}</TableCell>
209
                                    <TableCell>{state.alternativeNamesI18n.join(", ")}</TableCell>
210
                                </TableRow>
211

212

213
                                <TableRow>
214
                                    <TableCell>{t('description')}</TableCell>
215
                                    <TableCell>{state.descriptionI18n}</TableCell>
216
                                </TableRow>
217

218

219
                                <TableRow>
220
                                    <TableCell>{t('exercises.notes')}</TableCell>
221
                                    <TableCell>{state.notesI18n.map(note => <>{note}<br /></>)}</TableCell>
×
222
                                </TableRow>
223
                            </TableBody>
224
                        </Table>
225
                    </TableContainer>
226
                </>
227
            )}
228

229
            {!(submissionState === 'done')
2!
230
                ? <Alert severity="info" sx={{ mt: 2 }}>
231
                    {t('exercises.checkInformationBeforeSubmitting')}
232
                </Alert>
233
                : <Alert severity="success" sx={{ mt: 2 }}>
234
                    <AlertTitle>{t('success')}</AlertTitle>
235
                    {t('exercises.cacheWarning')}
236
                </Alert>
237
            }
238

239
            <Grid container>
240
                <Grid display="flex" justifyContent={"end"} size={12}>
241
                    <Box sx={{ mb: 2 }}>
242
                        <div>
243
                            {submissionState !== 'done' &&
4✔
244
                                <Button
245
                                    onClick={onBack}
246
                                    sx={{ mt: 1, mr: 1 }}
247
                                >
248
                                    {t('goBack')}
249
                                </Button>
250
                            }
251
                            {submissionState !== 'done'
4✔
252
                                && <Button
253
                                    variant="contained"
254
                                    disabled={submissionState !== 'initial'}
255
                                    onClick={submitExercise}
256
                                    sx={{ mt: 1, mr: 1 }}
257
                                    color="info"
258
                                >
259
                                    {t('exercises.submitExercise')}
260
                                </Button>
261
                            }
262
                            {submissionState === 'done'
2!
263
                                && <Button
264
                                    variant="contained"
265
                                    onClick={navigateToOverview}
266
                                    sx={{ mt: 1, mr: 1 }}
267
                                    color="success"
268
                                >
269
                                    {t('overview')}
270
                                    <NavigateNextIcon />
271
                                </Button>
272
                            }
273
                        </div>
274
                    </Box>
275
                </Grid>
276
            </Grid>
277
        </>;
278
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc