Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit 7688d3a

Browse files
committed
feat(bulk-upload): add table of last uploads
1 parent a894f3d commit 7688d3a

File tree

4 files changed

+190
-23
lines changed

4 files changed

+190
-23
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
* Content of the Table component.
3+
*/
4+
5+
import React from "react";
6+
import PT from "prop-types";
7+
import _ from "lodash";
8+
9+
import style from "./style.module.scss";
10+
11+
export const TABLE_STATES = {
12+
LOADING_LAST_UPLOADS: "LOADING_LAST_UPLOADS",
13+
RESULT: "RESULT",
14+
};
15+
16+
export default function Table({ state, data }) {
17+
const columns = {
18+
created: {
19+
name: "Upload Date",
20+
formatter: (date) =>
21+
new Intl.DateTimeFormat("en", {
22+
year: "numeric",
23+
month: "short",
24+
day: "2-digit",
25+
}).format(new Date(date)),
26+
},
27+
status: { name: "Status" },
28+
info: { name: "Info" },
29+
failedRecordsUrl: {
30+
name: "Assets",
31+
formatter: (url) =>
32+
url ? (
33+
<a href={url} target="_blank" rel="noopener noreferrer">
34+
Download
35+
</a>
36+
) : (
37+
"N/A"
38+
),
39+
},
40+
};
41+
42+
return state === TABLE_STATES.LOADING_LAST_UPLOADS ? (
43+
<p>Loading last uploads...</p>
44+
) : (
45+
data.length > 0 && (
46+
<div className={style.content}>
47+
<h1 className={style.title}>Past 24 Hours Upload Status</h1>
48+
<table className={style.tableContent}>
49+
<tr>
50+
{_.map(_.values(columns), ({ name }) => (
51+
<th>{name}</th>
52+
))}
53+
</tr>
54+
{_.map(data, (item) => (
55+
<tr>
56+
{_.map(_.keys(columns), (colKey) => (
57+
<td>
58+
{columns[colKey].formatter
59+
? columns[colKey].formatter(item[colKey])
60+
: item[colKey] || "N/A"}
61+
</td>
62+
))}
63+
</tr>
64+
))}
65+
</table>
66+
</div>
67+
)
68+
);
69+
}
70+
71+
Table.propTypes = {
72+
state: PT.any,
73+
data: PT.array.isRequired,
74+
};
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
.content {
2+
margin-top: 15px;
3+
margin-bottom: 15px;
4+
}
5+
6+
.title {
7+
text-align: center;
8+
color: #252526;
9+
font: 500 14pt Inter;
10+
}
11+
12+
.tableContent {
13+
margin-top: 15px;
14+
margin-bottom: 15px;
15+
margin-left: auto;
16+
margin-right: auto;
17+
width: 70%;
18+
}
19+
20+
table {
21+
font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
22+
border-collapse: collapse;
23+
}
24+
25+
th {
26+
padding-top: 12px;
27+
padding-bottom: 12px;
28+
text-align: center;
29+
background-color: #3cd656;
30+
color: white;
31+
}
32+
33+
td {
34+
border: 1px solid #ddd;
35+
padding: 8px;
36+
text-align: center;
37+
}
38+
39+
tr:nth-child(even) {
40+
background-color: #f2f2f2;
41+
}
42+
tr:hover {
43+
background-color: #ddd;
44+
}
45+
46+
a {
47+
color: #30a2c7;
48+
text-decoration: none;
49+
&:hover {
50+
color: #56ccf2;
51+
text-decoration: underline;
52+
}
53+
}

client/src/components/Upload/index.jsx

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,18 @@ import FormData from "form-data";
44
import PT from "prop-types";
55

66
import Container from "./Container";
7+
import Table, { TABLE_STATES } from "./Table";
78
import Initial from "./Initial";
89
import Message from "./Message";
910
import Progress from "./Progress";
1011

12+
import style from "./style.module.scss";
13+
1114
import config from "../../config";
1215
import api from "../../services/api";
1316
import { getSingleOrg } from "../../services/user-org";
1417

15-
const STATES = {
18+
const UPLOAD_STATES = {
1619
INITIAL: "INITIAL",
1720
MESSAGE: "MESSAGE",
1821
RESULT: "RESULT",
@@ -21,15 +24,19 @@ const STATES = {
2124

2225
export default function Upload({ templateId }) {
2326
const apiClient = api();
24-
const [state, setState] = React.useState({
25-
type: STATES.INITIAL,
27+
const [uploadState, setUploadState] = React.useState({
28+
type: UPLOAD_STATES.INITIAL,
2629
data: null,
2730
});
31+
const [tableState, setTableState] = React.useState(
32+
TABLE_STATES.LOADING_LAST_UPLOADS
33+
);
34+
const [lastUploads, setLastUploads] = React.useState([]);
2835

2936
const showError = (error) => {
3037
const { message } = error.toJSON ? error.toJSON() : error;
31-
setState({
32-
type: STATES.MESSAGE,
38+
setUploadState({
39+
type: UPLOAD_STATES.MESSAGE,
3340
data: {
3441
title: "Error Occured",
3542
message,
@@ -44,8 +51,8 @@ export default function Upload({ templateId }) {
4451
data.append("upload", file);
4552
data.append("organizationId", getSingleOrg());
4653

47-
setState({
48-
type: STATES.UPLOADING,
54+
setUploadState({
55+
type: UPLOAD_STATES.UPLOADING,
4956
data: { progress: 0 },
5057
});
5158

@@ -55,17 +62,17 @@ export default function Upload({ templateId }) {
5562
"Content-Type": "multipart/form-data",
5663
},
5764
onUploadProgress: ({ loaded, total }) => {
58-
setState({
59-
type: STATES.UPLOADING,
65+
setUploadState({
66+
type: UPLOAD_STATES.UPLOADING,
6067
data: {
6168
progress: loaded / total,
6269
},
6370
});
6471
},
6572
});
6673

67-
setState({
68-
type: STATES.MESSAGE,
74+
setUploadState({
75+
type: UPLOAD_STATES.MESSAGE,
6976
data: {
7077
title: "Profiles uploaded successfully",
7178
message:
@@ -77,33 +84,58 @@ export default function Upload({ templateId }) {
7784
}
7885
};
7986

80-
let content;
81-
switch (state.type) {
82-
case STATES.MESSAGE:
83-
content = (
87+
React.useEffect(() => {
88+
async function fetchUploads() {
89+
const url = `${config.API_PREFIX}/uploads`;
90+
91+
setTableState({ type: TABLE_STATES.LOADING_LAST_UPLOADS });
92+
93+
try {
94+
const { data } = await apiClient.get(url);
95+
96+
setTableState(TABLE_STATES.RESULT);
97+
console.log("response", data);
98+
setLastUploads(data);
99+
} catch (error) {
100+
setTableState(TABLE_STATES.RESULT);
101+
setLastUploads([]);
102+
}
103+
}
104+
fetchUploads();
105+
}, [apiClient]);
106+
107+
let uploadSectionContent;
108+
switch (uploadState.type) {
109+
case UPLOAD_STATES.MESSAGE:
110+
uploadSectionContent = (
84111
<Message
85-
message={state.data.message}
86-
onClose={() => setState({ type: STATES.INITIAL })}
87-
title={state.data.title}
112+
message={uploadState.data.message}
113+
onClose={() => setUploadState({ type: UPLOAD_STATES.INITIAL })}
114+
title={uploadState.data.title}
88115
/>
89116
);
90117
break;
91-
case STATES.INITIAL:
92-
content = (
118+
case UPLOAD_STATES.INITIAL:
119+
uploadSectionContent = (
93120
<Initial
94121
onError={showError}
95122
onUpload={upload}
96123
templateId={templateId}
97124
/>
98125
);
99126
break;
100-
case STATES.UPLOADING:
101-
content = <Progress progress={state.data.progress} />;
127+
case UPLOAD_STATES.UPLOADING:
128+
uploadSectionContent = <Progress progress={uploadState.data.progress} />;
102129
break;
103130
default:
104131
throw Error("Invalid state");
105132
}
106-
return <Container>{content}</Container>;
133+
return (
134+
<div className={style.content}>
135+
<Table state={tableState} data={lastUploads} />
136+
<Container>{uploadSectionContent}</Container>
137+
</div>
138+
);
107139
}
108140

109141
Upload.propTypes = {
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.content {
2+
background: #eee;
3+
border-radius: 10px;
4+
margin: auto;
5+
position: relative;
6+
user-select: none;
7+
width: 80%;
8+
}

0 commit comments

Comments
 (0)