Skip to content

Commit af3ddea

Browse files
committed
feat: list artifacts and allow them to be donwload
1 parent 69fa10d commit af3ddea

File tree

9 files changed

+3951
-2997
lines changed

9 files changed

+3951
-2997
lines changed

package-lock.json

Lines changed: 3705 additions & 2994 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@
164164
"supertest": "^3.1.0",
165165
"tc-core-library-js": "github:appirio-tech/tc-core-library-js#v2.6.3.1",
166166
"tc-ui": "^1.0.12",
167-
"topcoder-react-lib": "1.2.14",
167+
"topcoder-react-lib": "github:topcoder-platform/topcoder-react-lib#1.2.18",
168168
"topcoder-react-ui-kit": "2.0.1",
169169
"topcoder-react-utils": "github:topcoder-platform/topcoder-react-utils#v0.9.5",
170170
"turndown": "^4.0.2",
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/**
2+
* Modal Component for Image Decorators
3+
*/
4+
import _ from 'lodash';
5+
import PT from 'prop-types';
6+
import React, { useEffect, useState } from 'react';
7+
8+
import { Modal } from 'topcoder-react-ui-kit';
9+
10+
import style from './style.scss';
11+
import LoadingIndicator from 'components/LoadingIndicator';
12+
13+
import DownloadIcon from '../Icons/IconSquareDownload.svg';
14+
15+
const theme = {
16+
container: style.modalContainer,
17+
overlay: style.modalOverlay,
18+
};
19+
20+
export const DownloadArtifactsModal = ({submissionId, onCancel, getSubmissionArtifacts, onDownloadArtifacts}) => {
21+
const [artifacts, setArtifacts] = useState([]);
22+
const [loading, setLoading] = useState(false);
23+
24+
const getArtifacts = async () => {
25+
const { artifacts: resp } = await getSubmissionArtifacts(submissionId);
26+
setArtifacts(resp);
27+
setLoading(false);
28+
};
29+
30+
useEffect(() => {
31+
setLoading(true);
32+
getArtifacts();
33+
}, [submissionId]);
34+
35+
console.log(artifacts, 'artifacts');
36+
return (
37+
<div styleName="container">
38+
<Modal
39+
onCancel={() => onCancel()}
40+
theme={theme}
41+
>
42+
<div styleName="content-wrapper">
43+
<div styleName="artifacts-list">
44+
<div styleName="header">
45+
<div styleName="header-title">Artifact ID</div>
46+
<div>Action</div>
47+
</div>
48+
{
49+
!loading && artifacts.map((item) => {
50+
return (
51+
<div styleName="list-item">
52+
<div styleName="artifact-name">{item}</div>
53+
<button
54+
onClick={() => onDownloadArtifacts(item, submissionId)}
55+
type="button"
56+
styleName="icon-download"
57+
>
58+
<DownloadIcon />
59+
</button>
60+
</div>
61+
)
62+
})
63+
}
64+
65+
{
66+
!loading && artifacts.length === 0 && <div styleName="no-artifacts">No artifacts found</div>
67+
}
68+
</div>
69+
{loading && <LoadingIndicator />}
70+
</div>
71+
</Modal>
72+
</div>
73+
)
74+
}
75+
76+
DownloadArtifactsModal.defaultProps = {
77+
onCancel: _.noop,
78+
submissionId: '',
79+
getSubmissionArtifacts: _.noop,
80+
onDownloadArtifacts: _.noop,
81+
};
82+
83+
DownloadArtifactsModal.propTypes = {
84+
onCancel: PT.func,
85+
submissionId: PT.string,
86+
getSubmissionArtifacts: PT.func,
87+
onDownloadArtifacts: PT.func,
88+
};
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
@import "~styles/mixins";
2+
3+
// Competing against some globals
4+
.container {
5+
@include tc-body-md;
6+
7+
z-index: 1000;
8+
}
9+
10+
.content-wrapper {
11+
min-height: 350px;
12+
}
13+
14+
.artifacts-list {
15+
.header {
16+
border-bottom: 1px solid $tc-gray-60;
17+
padding-bottom: 10px;
18+
display: flex;
19+
padding-left: 40px;
20+
padding-right: 40px;
21+
color: $tc-gray-70;
22+
@include roboto-medium;
23+
font-weight: 500;
24+
25+
.header-title {
26+
flex: 1;
27+
}
28+
}
29+
30+
.no-artifacts {
31+
@include roboto-regular;
32+
margin-top: 40px;
33+
text-align: center;
34+
}
35+
36+
.list-item {
37+
border-bottom: 1px solid $tc-gray-60;
38+
padding-bottom: 10px;
39+
padding-top: 10px;
40+
display: flex;
41+
padding-left: 40px;
42+
padding-right: 40px;
43+
color: $tc-gray-70;
44+
@include roboto-regular;
45+
.artifact-name {
46+
display: flex;
47+
flex: 1;
48+
}
49+
.icon-download {
50+
cursor: pointer;
51+
margin-left: 15px;
52+
margin-top: 3px;
53+
background: none;
54+
border: 0;
55+
font-size: 0;
56+
padding: 0;
57+
line-height: 0;
58+
display: inline-block;
59+
}
60+
}
61+
}
62+
63+
.modalContainer {
64+
top: 30%;
65+
left: 50%;
66+
transform: translate(-50%);
67+
height: auto;
68+
width: 720px;
69+
padding-top: 36px;
70+
padding-bottom: 36px;
71+
padding-left: 0;
72+
padding-right: 0;
73+
}
74+
75+
.modalOverlay {
76+
pointer-events: bounding-box;
77+
}

src/shared/components/SubmissionManagement/Submission/styles.scss

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ $submission-space-20: $base-unit * 4;
44
$submission-space-25: $base-unit * 5;
55
$submission-space-50: $base-unit * 10;
66

7+
@mixin button {
8+
margin: 0 1px;
9+
padding: 0 10px;
10+
}
11+
12+
713
.submission-row {
814
width: 100%;
915
font-size: 15px;
@@ -141,6 +147,13 @@ $submission-space-50: $base-unit * 10;
141147
text-align: center;
142148
min-width: 120px;
143149

150+
.download-button {
151+
@include button;
152+
153+
font-weight: bold;
154+
background-color: #767676;
155+
min-width: 120px;
156+
}
144157
@include xs-to-sm {
145158
position: absolute;
146159
right: 0;

src/shared/components/SubmissionManagement/SubmissionManagement/index.jsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ export default function SubmissionManagement(props) {
3939
challengeUrl,
4040
onlineReviewUrl,
4141
submissionPhaseStartDate,
42+
onDownloadArtifacts,
43+
getSubmissionArtifacts,
4244
} = props;
4345

4446
const { track } = challenge;
@@ -70,6 +72,8 @@ export default function SubmissionManagement(props) {
7072
onDownload: onDownload.bind(0, challengeType),
7173
onlineReviewUrl,
7274
onShowDetails,
75+
onDownloadArtifacts,
76+
getSubmissionArtifacts,
7377
};
7478
return (
7579
<div styleName="submission-management">

src/shared/components/SubmissionManagement/SubmissionsTable/index.jsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,19 @@
1515
*/
1616

1717
import _ from 'lodash';
18-
import React from 'react';
18+
import React, { useState } from 'react';
1919
import PT from 'prop-types';
2020
import shortid from 'shortid';
2121
import moment from 'moment';
2222
import { COMPETITION_TRACKS } from 'utils/tc';
2323
import Submission from '../Submission';
2424
import ScreeningDetails from '../ScreeningDetails';
25-
import './styles.scss';
25+
import { PrimaryButton } from 'topcoder-react-ui-kit';
26+
import style from './styles.scss';
27+
import { DownloadArtifactsModal } from '../DownloadArtifactsModal';
2628

2729
export default function SubmissionsTable(props) {
30+
const [submissionId, setSubmissionId] = useState('');
2831
const {
2932
challenge,
3033
submissionObjects,
@@ -37,6 +40,8 @@ export default function SubmissionsTable(props) {
3740
onShowDetails,
3841
status,
3942
submissionPhaseStartDate,
43+
onDownloadArtifacts,
44+
getSubmissionArtifacts,
4045
} = props;
4146

4247
const submissionsWithDetails = [];
@@ -76,6 +81,7 @@ export default function SubmissionsTable(props) {
7681
{showDetails[subObject.id]
7782
&& (
7883
<td colSpan="6" styleName="dev-details">
84+
<PrimaryButton theme={{button: style['upload-artifact-btn']}} onClick={() => onOpenDownloadArtifactsModal(subObject.id)}>Download Artifacts</PrimaryButton>
7985
<ScreeningDetails
8086
screeningObject={subObject.screening}
8187
helpPageUrl={helpPageUrl}
@@ -90,6 +96,10 @@ export default function SubmissionsTable(props) {
9096
});
9197
}
9298

99+
const onOpenDownloadArtifactsModal = (submissionId) => {
100+
setSubmissionId(submissionId);
101+
};
102+
93103
return (
94104
<div styleName="submissions-table">
95105
<table>
@@ -118,6 +128,7 @@ export default function SubmissionsTable(props) {
118128
{submissionsWithDetails}
119129
</tbody>
120130
</table>
131+
{submissionId && <DownloadArtifactsModal onCancel={() => setSubmissionId('')} getSubmissionArtifacts={getSubmissionArtifacts} submissionId={submissionId} onDownloadArtifacts={onDownloadArtifacts} />}
121132
</div>
122133
);
123134
}

src/shared/components/SubmissionManagement/SubmissionsTable/styles.scss

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,25 @@ $submission-space-50: $base-unit * 10;
9090
padding-right: 60px;
9191
border-top: 0;
9292
padding-top: 0;
93+
.upload-artifact-btn {
94+
@include roboto-medium;
95+
line-height: 24px;
96+
border-radius: 50px;
97+
border-color: #137d60;
98+
background-color: transparent;
99+
color: #137d60;
100+
text-transform: uppercase;
101+
&:hover {
102+
background-color: #137d60;
103+
color: #fff;
104+
background-image: none;
105+
}
106+
107+
@include xs-to-md {
108+
font-size: 14px;
109+
line-height: 20px;
110+
}
111+
}
93112
}
94113
}
95114

src/shared/containers/SubmissionManagement/index.jsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,17 @@ class SubmissionManagementPageContainer extends React.Component {
127127

128128
if (!challenge.isRegistered) return <AccessDenied redirectLink={`${challengesUrl}/${challenge.id}`} cause={ACCESS_DENIED_REASON.HAVE_NOT_SUBMITTED_TO_THE_CHALLENGE} />;
129129

130+
const getExtensionFromMime = (mimeType) => {
131+
const mimeMap = {
132+
'application/zip': 'zip',
133+
'application/pdf': 'pdf',
134+
'image/jpeg': 'jpg',
135+
'image/png': 'png',
136+
'text/plain': 'txt'
137+
};
138+
return mimeMap[mimeType] || 'zip';
139+
};
140+
130141
const isEmpty = _.isEmpty(challenge);
131142
const smConfig = {
132143
onShowDetails,
@@ -144,6 +155,26 @@ class SubmissionManagementPageContainer extends React.Component {
144155
link.parentNode.removeChild(link);
145156
});
146157
},
158+
onDownloadArtifacts: (artifactId, submissionId) => {
159+
const submissionsService = getService(authTokens.tokenV3);
160+
submissionsService.downloadSubmissionArtifact(submissionId, artifactId)
161+
.then((blob) => {
162+
const fileBlob = new Blob([blob]);
163+
const url = window.URL.createObjectURL(fileBlob);
164+
const link = document.createElement('a');
165+
link.href = url;
166+
const extension = getExtensionFromMime(fileBlob);
167+
link.setAttribute('download', `submission-artifact-${submissionId}.${extension}`);
168+
document.body.appendChild(link);
169+
link.click();
170+
link.parentNode.removeChild(link);
171+
});
172+
},
173+
getSubmissionArtifacts: (submissionId) => {
174+
console.log(authTokens, 'authTokens');
175+
const submissionsService = getService(authTokens.tokenV3);
176+
return submissionsService.getSubmissionArtifacts(submissionId);
177+
},
147178
onlineReviewUrl: `${config.URL.ONLINE_REVIEW}/review/actions/ViewProjectDetails?pid=${challengeId}`,
148179
challengeUrl: `${challengesUrl}/${challengeId}`,
149180
addSumissionUrl: `${config.URL.BASE}/challenges/${challengeId}/submit`,

0 commit comments

Comments
 (0)