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

CBIIT / crdc-datahub-ui / 12563833752

31 Dec 2024 08:49PM UTC coverage: 57.693% (+1.1%) from 56.598%
12563833752

Pull #567

github

web-flow
Merge 29463e128 into d0c758391
Pull Request #567: PBAC Update user roles and permissions

2771 of 5218 branches covered (53.1%)

Branch coverage included in aggregate %.

228 of 279 new or added lines in 28 files covered. (81.72%)

5 existing lines in 5 files now uncovered.

3952 of 6435 relevant lines covered (61.41%)

141.79 hits per line

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

0.0
/src/content/dataSubmissions/DataSubmission.tsx
1
import { FC, useCallback, useEffect, useMemo, useRef } from "react";
2
import { useMutation } from "@apollo/client";
3
import { Box, Card, CardActions, CardContent, Container, Tabs, styled } from "@mui/material";
4
import { useSnackbar, VariantType } from "notistack";
5
import { useNavigate } from "react-router-dom";
6
import bannerPng from "../../assets/banner/submission_banner.png";
7
import summaryBannerPng from "../../assets/banner/summary_banner.png";
8
import LinkTab from "../../components/DataSubmissions/LinkTab";
9
import MetadataUpload from "../../components/DataSubmissions/MetadataUpload";
10
import { SUBMISSION_ACTION, SubmissionActionResp } from "../../graphql";
11
import DataSubmissionSummary from "../../components/DataSubmissions/DataSubmissionSummary";
12
import DataSubmissionActions from "./DataSubmissionActions";
13
import QualityControl from "./QualityControl";
14
import ValidationStatistics from "../../components/DataSubmissions/ValidationStatistics";
15
import ValidationControls from "../../components/DataSubmissions/ValidationControls";
16
import { useAuthContext } from "../../components/Contexts/AuthContext";
17
import {
18
  ReleaseInfo,
19
  shouldDisableRelease,
20
  shouldEnableSubmit,
21
} from "../../utils/dataSubmissionUtils";
22
import usePageTitle from "../../hooks/usePageTitle";
23
import BackButton from "../../components/DataSubmissions/BackButton";
24
import SubmittedData from "./SubmittedData";
25
import { UserGuide } from "../../components/DataSubmissions/UserGuide";
26
import { DataUpload } from "../../components/DataSubmissions/DataUpload";
27
import { useSearchParamsContext } from "../../components/Contexts/SearchParamsContext";
28
import { useSubmissionContext } from "../../components/Contexts/SubmissionContext";
29
import DataActivity, { DataActivityRef } from "./DataActivity";
30
import CrossValidation from "./CrossValidation";
31
import CopyAdornment from "../../components/DataSubmissions/CopyAdornment";
32
import { Logger } from "../../utils";
33
import { hasPermission } from "../../config/AuthPermissions";
34

35
const StyledBanner = styled("div")(({ bannerSrc }: { bannerSrc: string }) => ({
×
36
  background: `url(${bannerSrc})`,
37
  backgroundBlendMode: "luminosity, normal",
38
  backgroundSize: "cover",
39
  backgroundPosition: "center",
40
  width: "100%",
41
  height: "296px",
42
  zIndex: 0,
43
}));
44

45
const StyledBannerContentContainer = styled(Container)(({ padding }: { padding?: string }) => ({
×
46
  "&.MuiContainer-root": {
47
    padding: padding || "58px 73px 75px",
×
48
    marginTop: "-295px",
49
    width: "100%",
50
    height: "100%",
51
    position: "relative",
52
    zIndex: 1,
53
  },
54
}));
55

56
const StyledCard = styled(Card)(() => ({
×
57
  borderRadius: "8px",
58
  backgroundColor: "#FFFFFF",
59
  padding: 0,
60
  // boxShadow: "0px -5px 35px 0px rgba(53, 96, 160, 0.30)",
61
  "& .MuiCardContent-root": {
62
    padding: 0,
63
  },
64
  "& .MuiCardActions-root": {
65
    justifyContent: "center",
66
    alignItems: "center",
67
    paddingTop: 0,
68
    paddingBottom: 0,
69
    position: "relative",
70
  },
71
  "&.MuiPaper-root": {
72
    border: "1px solid #6CACDA",
73
    borderTopRightRadius: 0,
74
    borderTopLeftRadius: 0,
75
    overflow: "visible",
76
  },
77
  "&::after": {
78
    content: '""',
79
    position: "absolute",
80
    zIndex: 1,
81
    bottom: "50px",
82
    left: 0,
83
    pointerEvents: "none",
84
    backgroundImage: "linear-gradient(to bottom, rgba(255,255,255,0), rgba(251,253,255, 1) 20%)",
85
    width: "100%",
86
    height: "260px",
87
  },
88
}));
89

90
const StyledMainContentArea = styled("div")(() => ({
×
91
  position: "relative",
92
  zIndex: 2,
93
  borderRadius: 0,
94
  padding: "21px 40px 0",
95
}));
96

97
const StyledCardActions = styled(CardActions)(() => ({
×
98
  "&.MuiCardActions-root": {
99
    paddingTop: "32px",
100
  },
101
}));
102

103
const StyledTabs = styled(Tabs)(() => ({
×
104
  position: "relative",
105
  display: "flex",
106
  alignItems: "flex-end",
107
  zIndex: 3,
108
  "& .MuiTabs-flexContainer": {
109
    justifyContent: "center",
110
  },
111
  "& .MuiTabs-indicator": {
112
    display: "none !important",
113
  },
114
  "&::before": {
115
    content: '""',
116
    position: "absolute",
117
    bottom: 0,
118
    left: 0,
119
    right: 0,
120
    borderBottom: "1.25px solid #6CACDA",
121
    zIndex: 1,
122
  },
123
}));
124

125
const StyledWrapper = styled("div")({
×
126
  background: "#FBFDFF",
127
});
128

129
const StyledCardContent = styled(CardContent)({
×
130
  background: `url(${summaryBannerPng})`,
131
  backgroundSize: "auto",
132
  backgroundRepeat: "no-repeat",
133
  backgroundPosition: "top",
134
});
135

136
const StyledFlowContainer = styled(Box)({
×
137
  padding: "27px 59px 59px 60px",
138
});
139

140
const URLTabs = {
×
141
  UPLOAD_ACTIVITY: "upload-activity",
142
  VALIDATION_RESULTS: "validation-results",
143
  SUBMITTED_DATA: "data-view",
144
  CROSS_VALIDATION_RESULTS: "cross-validation-results",
145
};
146

147
const submissionLockedStatuses: SubmissionStatus[] = [
×
148
  "Submitted",
149
  "Released",
150
  "Completed",
151
  "Canceled",
152
];
153

154
type Props = {
155
  submissionId: string;
156
  tab: string;
157
};
158

159
const DataSubmission: FC<Props> = ({ submissionId, tab = URLTabs.UPLOAD_ACTIVITY }) => {
×
160
  usePageTitle(`Data Submission ${submissionId || ""}`);
×
161

162
  const navigate = useNavigate();
×
163
  const { user } = useAuthContext();
×
164
  const { enqueueSnackbar } = useSnackbar();
×
165
  const { lastSearchParams } = useSearchParamsContext();
×
166
  const { data, error, refetch: getSubmission } = useSubmissionContext();
×
167

168
  const dataSubmissionListPageUrl = `/data-submissions${
×
169
    lastSearchParams?.["/data-submissions"] ?? ""
×
170
  }`;
171
  const activityRef = useRef<DataActivityRef>(null);
×
172
  const hasUploadingBatches = useMemo<boolean>(
×
173
    () => data?.batchStatusList?.batches?.some((b) => b.status === "Uploading"),
×
174
    [data?.batchStatusList?.batches]
175
  );
176
  const crossValidationVisible: boolean = useMemo<boolean>(
×
177
    () =>
NEW
178
      hasPermission(user, "data_submission", "review", data?.getSubmission) &&
×
179
      data?.getSubmission?.crossSubmissionStatus !== null,
180
    [user, data?.getSubmission]
181
  );
182
  const isValidTab =
NEW
183
    tab &&
×
184
    Object.values(URLTabs).includes(tab) &&
185
    (tab !== URLTabs.CROSS_VALIDATION_RESULTS || crossValidationVisible);
186

187
  const submitInfo: SubmitButtonResult = useMemo(() => {
×
NEW
188
    if (!data?.getSubmission?._id || hasUploadingBatches) {
×
189
      return { enabled: false };
×
190
    }
191

NEW
192
    return shouldEnableSubmit(data.getSubmission, user);
×
193
  }, [data?.getSubmission, user, hasUploadingBatches]);
194
  const releaseInfo: ReleaseInfo = useMemo(
×
195
    () => shouldDisableRelease(data?.getSubmission),
×
196
    [data?.getSubmission?.crossSubmissionStatus, data?.getSubmission?.otherSubmissions]
197
  );
198

199
  const [submissionAction] = useMutation<SubmissionActionResp>(SUBMISSION_ACTION, {
×
200
    context: { clientName: "backend" },
201
    fetchPolicy: "no-cache",
202
  });
203

204
  const updateSubmissionAction = async (action: SubmissionAction, reviewComment?: string) => {
×
205
    if (!submissionId) {
×
206
      return;
×
207
    }
208

209
    try {
×
210
      const { data: d, errors } = await submissionAction({
×
211
        variables: {
212
          submissionID: submissionId,
213
          action,
214
          comment: reviewComment,
215
        },
216
      });
217
      if (errors || !d?.submissionAction?._id) {
×
218
        Logger.error("Submission Action Error", errors);
×
219
        throw new Error(`Error occurred while performing '${action}' submission action.`);
×
220
      }
221
      await getSubmission();
×
222
    } catch (err) {
223
      enqueueSnackbar(err?.message, { variant: "error" });
×
224
    }
225
  };
226

227
  const handleBatchRefresh = useCallback(() => {
×
228
    // Force refresh the batch table to fetch the latest batch
229
    activityRef?.current?.tableRef?.refresh?.();
×
230
  }, [activityRef?.current?.tableRef]);
231

232
  const handleOnUpload = useCallback(
×
233
    async (message: string, variant: VariantType) => {
234
      enqueueSnackbar(message, { variant });
×
235

236
      // If the Data Activity tab is active, refresh the table
237
      handleBatchRefresh();
×
238

239
      // Refresh the submission data to start polling if needed
240
      await getSubmission();
×
241
    },
242
    [enqueueSnackbar, handleBatchRefresh, getSubmission]
243
  );
244

245
  useEffect(() => {
×
246
    if (!submissionId) {
×
247
      navigate(dataSubmissionListPageUrl, {
×
248
        state: { error: "Oops! An invalid Data Submission ID was provided." },
249
      });
250
    } else if (error) {
×
251
      navigate(dataSubmissionListPageUrl, {
×
252
        state: { error: "Oops! An error occurred while retrieving that Data Submission." },
253
      });
254
    }
255
  }, [error]);
256

NEW
257
  useEffect(() => {
×
NEW
258
    if (!isValidTab) {
×
NEW
259
      navigate(`/data-submission/${submissionId}/${URLTabs.UPLOAD_ACTIVITY}`, { replace: true });
×
260
    }
261
  }, [isValidTab]);
262

UNCOV
263
  return (
×
264
    <StyledWrapper>
265
      <StyledBanner bannerSrc={bannerPng} />
266
      <StyledBannerContentContainer maxWidth="xl">
267
        <CopyAdornment _id={submissionId} />
268
        <StyledCard>
269
          <StyledCardContent>
270
            <DataSubmissionSummary dataSubmission={data?.getSubmission} />
271
            <ValidationStatistics
272
              dataSubmission={data?.getSubmission}
273
              statistics={data?.submissionStats?.stats}
274
            />
275
            <StyledFlowContainer>
276
              <UserGuide />
277
              <MetadataUpload
278
                submission={data?.getSubmission}
279
                readOnly={submissionLockedStatuses.includes(data?.getSubmission?.status)}
280
                onCreateBatch={handleBatchRefresh}
281
                onUpload={handleOnUpload}
282
              />
283
              <DataUpload submission={data?.getSubmission} />
284
              <ValidationControls />
285
            </StyledFlowContainer>
286
            <StyledTabs value={isValidTab ? tab : URLTabs.UPLOAD_ACTIVITY}>
×
287
              <LinkTab
288
                value={URLTabs.UPLOAD_ACTIVITY}
289
                label="Upload Activities"
290
                to={`/data-submission/${submissionId}/${URLTabs.UPLOAD_ACTIVITY}`}
291
                selected={tab === URLTabs.UPLOAD_ACTIVITY}
292
              />
293
              <LinkTab
294
                value={URLTabs.VALIDATION_RESULTS}
295
                label="Validation Results"
296
                to={`/data-submission/${submissionId}/${URLTabs.VALIDATION_RESULTS}`}
297
                selected={tab === URLTabs.VALIDATION_RESULTS}
298
              />
299
              {crossValidationVisible && (
×
300
                <LinkTab
301
                  value={URLTabs.CROSS_VALIDATION_RESULTS}
302
                  label="Cross Validation Results"
303
                  to={`/data-submission/${submissionId}/${URLTabs.CROSS_VALIDATION_RESULTS}`}
304
                  selected={tab === URLTabs.CROSS_VALIDATION_RESULTS}
305
                />
306
              )}
307
              <LinkTab
308
                value={URLTabs.SUBMITTED_DATA}
309
                label="Data View"
310
                to={`/data-submission/${submissionId}/${URLTabs.SUBMITTED_DATA}`}
311
                selected={tab === URLTabs.SUBMITTED_DATA}
312
              />
313
            </StyledTabs>
314

315
            <StyledMainContentArea>
316
              {/* Primary Tab Content */}
317
              {tab === URLTabs.UPLOAD_ACTIVITY && <DataActivity ref={activityRef} />}
×
318
              {tab === URLTabs.VALIDATION_RESULTS && <QualityControl />}
×
319
              {tab === URLTabs.CROSS_VALIDATION_RESULTS && crossValidationVisible && (
×
320
                <CrossValidation />
321
              )}
322
              {tab === URLTabs.SUBMITTED_DATA && <SubmittedData />}
×
323

324
              {/* Return to Data Submission List Button */}
325
              <BackButton
326
                navigateTo={dataSubmissionListPageUrl}
327
                text="Back to Data Submissions List"
328
              />
329
            </StyledMainContentArea>
330
          </StyledCardContent>
331
          <StyledCardActions>
332
            <DataSubmissionActions
333
              submission={data?.getSubmission}
334
              onAction={updateSubmissionAction}
335
              submitActionButton={submitInfo}
336
              releaseActionButton={releaseInfo}
337
              onError={(message: string) => enqueueSnackbar(message, { variant: "error" })}
×
338
            />
339
          </StyledCardActions>
340
        </StyledCard>
341
      </StyledBannerContentContainer>
342
    </StyledWrapper>
343
  );
344
};
345

346
export default DataSubmission;
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