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

CSCfi / metadata-submitter-frontend / 15155733929

21 May 2025 07:05AM UTC coverage: 49.155% (+1.3%) from 47.832%
15155733929

push

github

mradavi
Remove template-related functionality (merge commit)

Merge branch 'feature/remove-templates' into 'main'
* Remove template-related functionality

Closes #1034
See merge request https://gitlab.ci.csc.fi/sds-dev/sd-submit/metadata-submitter-frontend/-/merge_requests/1113

Approved-by: Hang Le <lhang@csc.fi>
Merged by Monika Radaviciute <mradavic@csc.fi>

647 of 948 branches covered (68.25%)

Branch coverage included in aggregate %.

6 of 8 new or added lines in 4 files covered. (75.0%)

24 existing lines in 4 files now uncovered.

6160 of 12900 relevant lines covered (47.75%)

4.45 hits per line

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

76.98
/src/components/SubmissionWizard/WizardComponents/WizardAlert.tsx
1
import React, { useState, useEffect } from "react"
1✔
2

3
import WarningIcon from "@mui/icons-material/Warning"
1✔
4
import Alert from "@mui/material/Alert"
1✔
5
import Box from "@mui/material/Box"
1✔
6
import Button from "@mui/material/Button"
1✔
7
import Dialog from "@mui/material/Dialog"
1✔
8
import DialogActions from "@mui/material/DialogActions"
1✔
9
import DialogContent from "@mui/material/DialogContent"
1✔
10
import DialogContentText from "@mui/material/DialogContentText"
1✔
11
import { styled } from "@mui/material/styles"
1✔
12
import Typography from "@mui/material/Typography"
1✔
13
import { useTranslation } from "react-i18next"
1✔
14

15
import saveDraftHook from "../WizardHooks/WizardSaveDraftHook"
1✔
16

17
import { ResponseStatus } from "constants/responseStatus"
1✔
18
import { ObjectStatus, ObjectTypes } from "constants/wizardObject"
1✔
19
import { resetDraftStatus } from "features/draftStatusSlice"
1✔
20
import { updateStatus } from "features/statusMessageSlice"
1✔
21
import { setAlert, resetAlert } from "features/wizardAlertSlice"
1✔
22
import { resetCurrentObject } from "features/wizardCurrentObjectSlice"
1✔
23
import { useAppSelector, useAppDispatch } from "hooks"
1✔
24
import objectAPIService from "services/objectAPI"
1✔
25
import type { ObjectInsideSubmissionWithTags } from "types"
26
import { checkObjectStatus } from "utils"
1✔
27

28
const CustomDialog = styled(Dialog)(({ theme }) => ({
1✔
29
  "& .MuiDialog-paper": {
3✔
30
    backgroundColor: theme.palette.background.paper,
3✔
31
    borderLeft: `1.25rem solid ${theme.palette.error.light}`,
3✔
32
    borderTop: `0.25rem solid ${theme.palette.error.light}`,
3✔
33
    borderRight: `0.25rem solid ${theme.palette.error.light}`,
3✔
34
    borderBottom: `0.25rem solid ${theme.palette.error.light}`,
3✔
35
    color: theme.palette.secondary.main,
3✔
36
    lineHeight: "1",
3✔
37
    boxShadow: "0 0.25rem 0.625rem rgba(0, 0, 0, 0.2)",
3✔
38
    padding: "0.5rem",
3✔
39
    display: "flex",
3✔
40
    flexDirection: "column",
3✔
41
    justifyContent: "flex-start",
3✔
42
    alignItems: "center",
3✔
43
    minWidth: "65rem",
3✔
44
  },
3✔
45
}))
1✔
46

47
const CustomBox = styled(Box)(() => ({
1✔
48
  width: "100%",
3✔
49
  padding: "1rem",
3✔
50
  paddingTop: "0",
3✔
51
  display: "flex",
3✔
52
  flexDirection: "row",
3✔
53
  alignItems: "flex-start",
3✔
54
}))
1✔
55

56
const IconBox = styled(Box)(() => ({
1✔
57
  width: "10%",
3✔
58
  display: "flex",
3✔
59
  justifyContent: "center",
3✔
60
  alignItems: "center",
3✔
61
}))
1✔
62

63
const ContentBox = styled(Box)(() => ({
1✔
64
  width: "90%",
3✔
65
  display: "flex",
3✔
66
  flexDirection: "column",
3✔
67
  padding: "1rem",
3✔
68
  paddingTop: 0,
3✔
69
}))
1✔
70

71
const CustomDialogTitle = styled(Typography)(({ theme }) => ({
1✔
72
  color: theme.palette.secondary.main,
3✔
73
  fontSize: "1.5rem",
3✔
74
  fontWeight: "bold",
3✔
75
}))
1✔
76

77
const StyledWarningIcon = styled(WarningIcon)(({ theme }) => ({
1✔
78
  color: theme.palette.error.light,
3✔
79
  fontSize: "3rem",
3✔
80
}))
1✔
81

82
const CustomDialogContentText = styled(DialogContentText)(({ theme }) => ({
1✔
83
  color: theme.palette.secondary.main,
3✔
84
  padding: "1rem",
3✔
85
  paddingTop: "0.5rem",
3✔
86
  paddingLeft: 0,
3✔
87
}))
1✔
88

89
// Simple template for error messages
90
const ErrorMessage = (props: { message: string }) => {
1✔
91
  return <Alert severity="error">{props.message}</Alert>
×
92
}
×
93

94
/*
95
 * Dialog contents are rendered based on parent component location and alert type
96
 */
97
const CancelFormDialog = ({
1✔
98
  handleDialog,
4✔
99
  alertType,
4✔
100
  parentLocation,
4✔
101
}: {
102
  handleDialog: (status: boolean, formData?: Array<ObjectInsideSubmissionWithTags>) => void
103
  alertType?: string
104
  parentLocation: string
105
  currentSubmissionType: string
106
}) => {
4✔
107
  const submission = useAppSelector(state => state.submission)
4✔
108
  const currentObject = useAppSelector(state => state.currentObject)
4✔
109
  const objectType = useAppSelector(state => state.objectType)
4✔
110
  const [error, setError] = useState(false)
4✔
111
  const [errorMessage, setErrorMessage] = useState("")
4✔
112
  const dispatch = useAppDispatch()
4✔
113

114
  const { hasSubmittedObject } = checkObjectStatus(submission, ObjectTypes.study)
4✔
115

116
  // Draft save logic
117
  const saveDraft = async () => {
4✔
118
    setError(false)
×
119

120
    const handleSave = await saveDraftHook({
×
121
      accessionId: currentObject.accessionId || currentObject.objectId,
×
122
      objectType: objectType,
×
123
      objectStatus: currentObject.status,
×
124
      submission: submission,
×
125
      values: currentObject.cleanedValues || currentObject,
×
126
      dispatch: dispatch,
×
127
    })
×
128

129
    if (handleSave.ok) {
×
130
      handleDialog(true)
×
131
    } else {
×
132
      setError(true)
×
133
      setErrorMessage(t("errors.connection.saveDraft"))
×
134
    }
×
135
  }
×
136

137
  const updateForm = async () => {
4✔
138
    const response = await objectAPIService.patchFromJSON(
×
139
      objectType,
×
140
      currentObject.accessionId,
×
141
      currentObject.cleanedValues
×
142
    )
×
143
    if (response.ok) {
×
144
      dispatch(resetDraftStatus())
×
145
      dispatch(
×
146
        updateStatus({
×
147
          status: ResponseStatus.success,
×
148
          response: response,
×
149
          helperText: "",
×
150
        })
×
151
      )
×
152
      dispatch(resetCurrentObject())
×
153
      handleDialog(true)
×
154
    } else {
×
155
      setError(true)
×
156
      setErrorMessage(t("errors.connection.updateObject"))
×
157
    }
×
158
  }
×
159

160
  const { t } = useTranslation()
4✔
161
  let [dialogTitle, dialogContent] = ["", ""]
4✔
162
  let dialogActions
4✔
163

164
  switch (parentLocation) {
4✔
165
    case "submission": {
4✔
166
      // Text depends on ObjectStatus or ObjectSubmissionType
167
      const textType =
2✔
168
        currentObject?.status === ObjectStatus.submitted ? "submitted" : alertType?.toLowerCase()
2!
169
      dialogTitle = t(`${"alerts." + textType + ".title"}`)
2✔
170
      dialogContent = t(`${"alerts." + textType + ".content"}`)
2✔
171
      dialogActions = (
2✔
172
        <DialogActions>
2✔
173
          <Button variant="outlined" onClick={() => handleDialog(false)} color="primary">
2✔
174
            {t("alerts.actions.cancel")}
2✔
175
          </Button>
2✔
176
          <Button variant="contained" onClick={() => handleDialog(true)} color="primary">
2✔
177
            {t("alerts.actions.noSave")}
2✔
178
          </Button>
2✔
179
          <Button
2✔
180
            variant="contained"
2✔
181
            onClick={() => {
2✔
182
              saveDraft()
×
183
            }}
×
184
            color="primary"
2✔
185
            disabled={hasSubmittedObject && objectType === ObjectTypes.study}
2!
186
          >
187
            {t("alerts.actions.saveDraft")}
2✔
188
          </Button>
2✔
189
          {textType === "submitted" && (
2!
190
            <Button
×
191
              variant="contained"
×
192
              onClick={() => {
×
193
                updateForm()
×
194
              }}
×
195
              color="primary"
×
196
            >
197
              {t("alerts.actions.update")}
×
198
            </Button>
×
199
          )}
200
        </DialogActions>
2✔
201
      )
202
      break
2✔
203
    }
2✔
204
    case "header": {
4✔
205
      switch (alertType) {
1✔
206
        case "save": {
1✔
207
          dialogTitle = t("alerts.save.title")
1✔
208
          dialogContent = t("alerts.save.content")
1✔
209
          dialogActions = (
1✔
210
            <DialogActions style={{ justifyContent: "flex-end" }}>
1✔
211
              <Button
1✔
212
                variant="outlined"
1✔
213
                onClick={() => handleDialog(false)}
1✔
214
                color="primary"
1✔
215
                autoFocus
1✔
216
              >
217
                {t("alerts.actions.continueSubmission")}
1✔
218
              </Button>
1✔
219
              <Button
1✔
220
                variant="contained"
1✔
221
                aria-label="Save a new submission and move to frontpage"
1✔
222
                onClick={() => handleDialog(true)}
1✔
223
                color="primary"
1✔
224
              >
225
                {t("alerts.actions.saveExit")}
1✔
226
              </Button>
1✔
227
            </DialogActions>
1✔
228
          )
229
          break
1✔
230
        }
1✔
231
        default: {
1!
UNCOV
232
          dialogTitle = "default"
×
233
          dialogContent = "default content"
×
234
        }
×
235
      }
1✔
236
      break
1✔
237
    }
1✔
238
    default: {
4!
239
      dialogTitle = "default"
×
240
      dialogContent = "default content"
×
241
    }
×
242
  }
4✔
243

244
  return (
3✔
245
    <CustomDialog
3✔
246
      open={true}
3✔
247
      onClose={() => handleDialog(false)}
3✔
248
      aria-labelledby="alert-dialog-title"
3✔
249
      aria-describedby="alert-dialog-description"
3✔
250
    >
251
      <DialogContent style={{ paddingLeft: 0 }}>
3✔
252
        <CustomBox>
3✔
253
          <IconBox>
3✔
254
            <StyledWarningIcon />
3✔
255
          </IconBox>
3✔
256
          <ContentBox>
3✔
257
            <CustomDialogTitle id="alert-dialog-title">{dialogTitle}</CustomDialogTitle>
3✔
258
            <CustomDialogContentText
3✔
259
              id="alert-dialog-description"
3✔
260
              data-testid="alert-dialog-content"
3✔
261
            >
262
              {dialogContent}
3✔
263
            </CustomDialogContentText>
3✔
264
            {error && <ErrorMessage message={errorMessage} />}
4!
265
            <DialogActions style={{ width: "100%", justifyContent: "flex-end", padding: "1rem" }}>
4✔
266
              {dialogActions}
4✔
267
            </DialogActions>
4✔
268
          </ContentBox>
4✔
269
        </CustomBox>
4✔
270
      </DialogContent>
4✔
271
    </CustomDialog>
4✔
272
  )
273
}
4✔
274

275
/*
276
 * Render alert form based on location and type
277
 */
278
const WizardAlert = ({
1✔
279
  onAlert,
4✔
280
  parentLocation,
4✔
281
  alertType,
4✔
282
}: {
283
  onAlert: (status: boolean, formData?: Array<ObjectInsideSubmissionWithTags>) => void
284
  parentLocation: string
285
  alertType?: string
286
}) => {
4✔
287
  const currentSubmissionType = useAppSelector(state => state.submissionType)
4✔
288

289
  const dispatch = useAppDispatch()
4✔
290

291
  useEffect(() => {
4✔
292
    dispatch(setAlert())
3✔
293
  }, [dispatch])
4✔
294

295
  const handleDialog = (action: boolean, formData?: Array<ObjectInsideSubmissionWithTags>) => {
4✔
296
    dispatch(resetAlert())
×
297
    onAlert(action, formData)
×
298
  }
×
299

300
  return (
4✔
301
    <div>
4✔
302
      <CancelFormDialog
4✔
303
        handleDialog={handleDialog}
4✔
304
        alertType={alertType}
4✔
305
        parentLocation={parentLocation}
4✔
306
        currentSubmissionType={currentSubmissionType}
4✔
307
      />
308
    </div>
4✔
309
  )
310
}
4✔
311

312
export default WizardAlert
1✔
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