diff --git a/client/src/components/Upload/Table/index.jsx b/client/src/components/Upload/Table/index.jsx new file mode 100644 index 0000000..7b37f87 --- /dev/null +++ b/client/src/components/Upload/Table/index.jsx @@ -0,0 +1,76 @@ +/** + * Content of the Table component. + */ + +import React from "react"; +import PT from "prop-types"; +import _ from "lodash"; + +import style from "./style.module.scss"; + +export const TABLE_STATES = { + LOADING_LAST_UPLOADS: "LOADING_LAST_UPLOADS", + RESULT: "RESULT", +}; + +export default function Table({ state, data }) { + const columns = { + created: { + name: "Upload Date", + formatter: (date) => + new Intl.DateTimeFormat("en", { + year: "numeric", + month: "short", + day: "2-digit", + }).format(new Date(date)), + }, + status: { name: "Status" }, + info: { name: "Info" }, + failedRecordsUrl: { + name: "Assets", + formatter: (url) => + url ? ( + + Download + + ) : ( + "N/A" + ), + }, + }; + + return state === TABLE_STATES.LOADING_LAST_UPLOADS ? ( +
+

Loading last uploads...

+
+ ) : ( + data.length > 0 && ( +
+

Past 24 Hours Upload Status

+ + + {_.map(_.values(columns), ({ name }) => ( + + ))} + + {_.map(data, (item) => ( + + {_.map(_.keys(columns), (colKey) => ( + + ))} + + ))} +
{name}
+ {columns[colKey].formatter + ? columns[colKey].formatter(item[colKey]) + : item[colKey] || "N/A"} +
+
+ ) + ); +} + +Table.propTypes = { + state: PT.any, + data: PT.array.isRequired, +}; diff --git a/client/src/components/Upload/Table/style.module.scss b/client/src/components/Upload/Table/style.module.scss new file mode 100644 index 0000000..e7fd9e1 --- /dev/null +++ b/client/src/components/Upload/Table/style.module.scss @@ -0,0 +1,53 @@ +.content { + margin-top: 15px; + margin-bottom: 15px; +} + +.title { + text-align: center; + color: #252526; + font: 500 14pt Inter; +} + +.tableContent { + margin-top: 15px; + margin-bottom: 15px; + margin-left: auto; + margin-right: auto; + width: 70%; +} + +table { + font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; + border-collapse: collapse; +} + +th { + padding-top: 12px; + padding-bottom: 12px; + text-align: center; + background-color: #3cd656; + color: white; +} + +td { + border: 1px solid #ddd; + padding: 8px; + text-align: center; +} + +tr:nth-child(even) { + background-color: #f2f2f2; +} +tr:hover { + background-color: #ddd; +} + +a { + color: #30a2c7; + text-decoration: none; + &:hover { + color: #56ccf2; + text-decoration: underline; + } +} diff --git a/client/src/components/Upload/index.jsx b/client/src/components/Upload/index.jsx index e364533..708296d 100644 --- a/client/src/components/Upload/index.jsx +++ b/client/src/components/Upload/index.jsx @@ -4,15 +4,18 @@ import FormData from "form-data"; import PT from "prop-types"; import Container from "./Container"; +import Table, { TABLE_STATES } from "./Table"; import Initial from "./Initial"; import Message from "./Message"; import Progress from "./Progress"; +import style from "./style.module.scss"; + import config from "../../config"; import api from "../../services/api"; import { getSingleOrg } from "../../services/user-org"; -const STATES = { +const UPLOAD_STATES = { INITIAL: "INITIAL", MESSAGE: "MESSAGE", RESULT: "RESULT", @@ -21,15 +24,19 @@ const STATES = { export default function Upload({ templateId }) { const apiClient = api(); - const [state, setState] = React.useState({ - type: STATES.INITIAL, + const [uploadState, setUploadState] = React.useState({ + type: UPLOAD_STATES.INITIAL, data: null, }); + const [tableState, setTableState] = React.useState( + TABLE_STATES.LOADING_LAST_UPLOADS + ); + const [lastUploads, setLastUploads] = React.useState([]); const showError = (error) => { const { message } = error.toJSON ? error.toJSON() : error; - setState({ - type: STATES.MESSAGE, + setUploadState({ + type: UPLOAD_STATES.MESSAGE, data: { title: "Error Occured", message, @@ -44,8 +51,8 @@ export default function Upload({ templateId }) { data.append("upload", file); data.append("organizationId", getSingleOrg()); - setState({ - type: STATES.UPLOADING, + setUploadState({ + type: UPLOAD_STATES.UPLOADING, data: { progress: 0 }, }); @@ -55,8 +62,8 @@ export default function Upload({ templateId }) { "Content-Type": "multipart/form-data", }, onUploadProgress: ({ loaded, total }) => { - setState({ - type: STATES.UPLOADING, + setUploadState({ + type: UPLOAD_STATES.UPLOADING, data: { progress: loaded / total, }, @@ -64,8 +71,8 @@ export default function Upload({ templateId }) { }, }); - setState({ - type: STATES.MESSAGE, + setUploadState({ + type: UPLOAD_STATES.MESSAGE, data: { title: "Profiles uploaded successfully", message: @@ -77,19 +84,39 @@ export default function Upload({ templateId }) { } }; - let content; - switch (state.type) { - case STATES.MESSAGE: - content = ( + React.useEffect(() => { + async function fetchUploads() { + const url = `${config.API_PREFIX}/uploads`; + + setTableState(TABLE_STATES.LOADING_LAST_UPLOADS); + + try { + const { data } = await apiClient.get(url); + + setTableState(TABLE_STATES.RESULT); + console.log("response", data); + setLastUploads(data); + } catch (error) { + setTableState(TABLE_STATES.RESULT); + setLastUploads([]); + } + } + fetchUploads(); + }, [apiClient]); + + let uploadSectionContent; + switch (uploadState.type) { + case UPLOAD_STATES.MESSAGE: + uploadSectionContent = ( setState({ type: STATES.INITIAL })} - title={state.data.title} + message={uploadState.data.message} + onClose={() => setUploadState({ type: UPLOAD_STATES.INITIAL })} + title={uploadState.data.title} /> ); break; - case STATES.INITIAL: - content = ( + case UPLOAD_STATES.INITIAL: + uploadSectionContent = ( ); break; - case STATES.UPLOADING: - content = ; + case UPLOAD_STATES.UPLOADING: + uploadSectionContent = ; break; default: throw Error("Invalid state"); } - return {content}; + return ( +
+ + {uploadSectionContent} + + ); } Upload.propTypes = { diff --git a/client/src/components/Upload/style.module.scss b/client/src/components/Upload/style.module.scss new file mode 100644 index 0000000..da79ddf --- /dev/null +++ b/client/src/components/Upload/style.module.scss @@ -0,0 +1,8 @@ +.content { + background: #eee; + border-radius: 10px; + margin: auto; + position: relative; + user-select: none; + width: 80%; +}