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

wger-project / react / 25593340855

09 May 2026 05:49AM UTC coverage: 78.152% (-0.04%) from 78.195%
25593340855

push

github

rolandgeider
Show the image form when uploading new images from the exercise detail page

2227 of 3137 branches covered (70.99%)

Branch coverage included in aggregate %.

5 of 14 new or added lines in 1 file covered. (35.71%)

24 existing lines in 4 files now uncovered.

4827 of 5889 relevant lines covered (81.97%)

31.59 hits per line

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

51.35
/src/components/Exercises/forms/ImageCard.tsx
1
import AddCircleIcon from '@mui/icons-material/AddCircle';
2
import { Box, Button, Card, CardActions, CardMedia } from "@mui/material";
3
import { FormQueryErrorsSnackbar } from "@/core/ui/Widgets/FormError";
4
import { ImageFormModal } from "@/components/Exercises/forms/ImageModal";
5
import { ImageFormData } from "@/components/Exercises/models/exercise";
6
import { ExerciseImage, ImageStyle } from "@/components/Exercises/models/image";
7
import { useAddExerciseImageQuery, useDeleteExerciseImageQuery } from "@/components/Exercises/queries";
8
import React, { useState } from "react";
9
import { useTranslation } from "react-i18next";
10

11
type ImageCardProps = {
12
    exerciseId: number;
13
    image: ExerciseImage;
14
    canDelete: boolean
15
    onEdit: (image: ExerciseImage) => void;
16
};
17

18
export const ImageEditCard = ({ exerciseId, image, canDelete, onEdit }: ImageCardProps) => {
21✔
19
    const [t] = useTranslation();
4✔
20
    const deleteImageQuery = useDeleteExerciseImageQuery(exerciseId);
4✔
21

22
    return <Card>
4✔
23
        <CardMedia
24
            component="img"
25
            image={image.url}
26
            sx={{ height: 120 }}
27
            alt=""
28
        />
29
        <CardActions style={{ justifyContent: 'space-between' }}>
30
            {canDelete &&
8✔
31
                <Button
32
                    color="primary"
33
                    onClick={() => deleteImageQuery.mutate(image.id)}
×
34
                >
35
                    {t('delete')}
36
                </Button>}
37
            {canDelete &&
8✔
38
                <Button
39
                    color="primary"
40
                    onClick={() => onEdit(image)}
1✔
41
                    data-testid={`edit-image-${image.id}`}
42
                >
43
                    {t('edit')}
44
                </Button>
45
            }
46
        </CardActions>
47
    </Card>;
48
};
49

50
type AddImageCardProps = {
51
    exerciseId: number;
52
};
53

54
export const AddImageCard = ({ exerciseId }: AddImageCardProps) => {
21✔
55

56
    const [t] = useTranslation();
4✔
57
    const addImageQuery = useAddExerciseImageQuery();
4✔
58

59
    const [selectedImage, setSelectedImage] = useState<ImageFormData | null>(null);
4✔
60
    const [openModal, setOpenModal] = useState(false);
4✔
61

62
    const handleFileInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
4✔
63
        if (!e.target.files?.length) {
×
64
            return;
×
65
        }
66
        const [uploadedFile] = e.target.files;
×
NEW
67
        setSelectedImage({
×
68
            url: URL.createObjectURL(uploadedFile),
69
            file: uploadedFile,
70
            author: '',
71
            authorUrl: '',
72
            title: '',
73
            objectUrl: '',
74
            derivativeSourceUrl: '',
75
            style: ImageStyle.PHOTO,
76
            isAi: false,
77
        });
NEW
78
        setOpenModal(true);
×
79
        // Reset the input so picking the same file twice still triggers onChange
NEW
80
        e.target.value = '';
×
81
    };
82

83
    const handleCloseModal = () => {
4✔
NEW
84
        setOpenModal(false);
×
NEW
85
        setSelectedImage(null);
×
86
    };
87

88
    const handleSubmit = (values: ImageFormData) => {
4✔
NEW
89
        if (!values.file) {
×
NEW
90
            return;
×
91
        }
NEW
92
        addImageQuery.mutate({
×
93
            exerciseId: exerciseId,
94
            image: values.file,
95
            imageData: values,
96
        });
NEW
97
        handleCloseModal();
×
98
    };
99

100
    return <>
4✔
101
        <Card>
102
            <CardMedia>
103
                <Box sx={{
104
                    backgroundColor: "lightgray",
105
                    height: 120,
106
                    display: "flex",
107
                    alignItems: "center",
108
                    justifyContent: "center"
109
                }}>
110
                    <AddCircleIcon sx={{ fontSize: 80, color: "gray" }} />
111
                </Box>
112
            </CardMedia>
113
            <CardActions>
114
                <Button component="label">
115
                    {t('add')}
116
                    <input
117
                        style={{ display: "none" }}
118
                        id="camera-input"
119
                        type="file"
120
                        accept="image/*"
121
                        capture="environment"
122
                        onChange={handleFileInputChange}
123
                    />
124
                </Button>
125
            </CardActions>
126
        </Card>
127
        <ImageFormModal
128
            open={openModal}
129
            onClose={handleCloseModal}
130
            image={selectedImage}
131
            onSubmit={handleSubmit}
132
            submitLabel={t('add')}
133
        />
134
        {addImageQuery.isError &&
4!
135
            <FormQueryErrorsSnackbar mutationQuery={addImageQuery} />
136
        }
137
    </>;
138
};
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

© 2026 Coveralls, Inc