Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Frontend] Data Upload v1: Playtesting Followups #33

Merged
merged 1 commit into from
Sep 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 59 additions & 15 deletions publisher/src/components/DataUpload/DataUpload.styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ export const ButtonWrapper = styled.div`
margin: 13px 0;
`;

export const UploadErrorButtonWrapper = styled(ButtonWrapper)`
export const ErrorWarningButtonWrapper = styled(ButtonWrapper)`
width: 100%;
justify-content: space-between;

Expand Down Expand Up @@ -258,7 +258,7 @@ export const Button = styled.div<{ type?: ButtonTypes }>`
}
`;

export const DownloadTemplateBox = styled.div`
export const DownloadTemplateBox = styled.a`
${typography.sizeCSS.normal};
display: flex;
align-items: center;
Expand All @@ -270,7 +270,15 @@ export const DownloadTemplateBox = styled.div`
border: 1px solid ${palette.highlight.grey4};
border-radius: 4px;

a {
&:hover {
background: ${palette.highlight.grey1};
}
`;

export const DownloadTemplateSystem = styled.div`
color: ${palette.solid.darkgrey};

span {
${typography.sizeCSS.small};
display: block;
width: fit-content;
Expand Down Expand Up @@ -360,7 +368,7 @@ export const DragDropContainer = styled.div<{ dragging?: boolean }>`
color: ${palette.solid.white};
`;

export const UserPromptContainer = styled.div`
export const Container = styled.div`
width: 100%;
min-height: 100%;
display: flex;
Expand All @@ -370,7 +378,7 @@ export const UserPromptContainer = styled.div`
padding-bottom: 80px;
`;

export const UserPromptWrapper = styled.div`
export const Wrapper = styled.div`
width: 100%;
max-width: 50%;
display: flex;
Expand All @@ -379,11 +387,11 @@ export const UserPromptWrapper = styled.div`
align-items: flex-start;
`;

export const UserPromptTitle = styled.div`
export const Title = styled.div`
${typography.sizeCSS.title};
`;

export const UserPromptDescription = styled.div`
export const ErrorWarningDescription = styled.div`
${typography.sizeCSS.medium};
margin: 8px 0;

Expand All @@ -392,13 +400,20 @@ export const UserPromptDescription = styled.div`
}
`;

export const UserPromptErrorContainer = styled.div`
export const MessagesContainer = styled.div`
width: 100%;
margin-top: 19px;
`;

export const UserPromptError = styled.div`
margin-bottom: 40px;
export const Message = styled.div``;

export const SectionHeader = styled.div`
${typography.sizeCSS.title};
margin: 10px 0;

&:not(:first-child) {
margin: 40px 0 10px 0;
}
`;

export const MetricTitle = styled.div`
Expand All @@ -418,28 +433,28 @@ export const MetricTitle = styled.div`
}
`;

export const ErrorIconWrapper = styled.div`
export const IconWrapper = styled.div`
display: flex;
align-items: center;
gap: 5px;
`;
export const ErrorMessageWrapper = styled.div`
export const MessageBody = styled.div`
${typography.sizeCSS.medium};
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
`;

export const ErrorMessageTitle = styled.div`
export const MessageTitle = styled.div`
display: block;
`;

export const ErrorMessageDescription = styled.div`
export const MessageSubtitle = styled.div`
display: block;
`;

export const ErrorAdditionalInfo = styled.div`
export const MessageDescription = styled.div`
${typography.sizeCSS.normal};
margin: 8px 0 13px 0;
`;
Expand Down Expand Up @@ -530,3 +545,32 @@ export const OrangeText = styled.span`
export const StrikethroughText = styled.span`
text-decoration: line-through;
`;

export const BlueText = styled.span`
color: ${palette.solid.blue};
`;

export const DataUploadLoading = styled.div`
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
`;

export const LoadingHeader = styled.div`
${typography.sizeCSS.large};
display: flex;
margin: 20px 0 5px 0;
gap: 3px;
`;

export const LoadingSubheader = styled.div`
${typography.sizeCSS.normal};
`;

export const CheckIcon = styled.img`
width: 16px;
margin-right: 5px;
`;
97 changes: 67 additions & 30 deletions publisher/src/components/DataUpload/DataUpload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,30 @@
// =============================================================================

import { observer } from "mobx-react-lite";
import React, { useState } from "react";
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";

import { AgencySystems } from "../../shared/types";
import { useStore } from "../../stores";
import logoImg from "../assets/jc-logo-vector.png";
import { Logo, LogoContainer } from "../Header";
import { Loading } from "../Loading";
import { Loader } from "../Loading";
import { showToast } from "../Toast";
import {
Button,
DataUploadContainer,
DataUploadHeader,
DataUploadLoading,
LoadingHeader,
LoadingSubheader,
SystemSelection,
UploadFile,
} from ".";
import {
DataUploadResponseBody,
ErrorsWarningsMetrics,
MetricErrors,
UploadedMetric,
} from "./types";
import { UploadErrorsWarnings } from "./UploadErrorsWarnings";

Expand Down Expand Up @@ -82,6 +86,7 @@ export const systemToTemplateSpreadsheetFileName: { [system: string]: string } =
export const DataUpload: React.FC = observer(() => {
const { userStore, reportStore } = useStore();
const navigate = useNavigate();
// eslint-disable-next-line react-hooks/exhaustive-deps
const userSystems =
userStore.currentAgency?.systems.filter(
(system) => !EXCLUDED_SYSTEMS.includes(system)
Expand All @@ -93,7 +98,7 @@ export const DataUpload: React.FC = observer(() => {
const [selectedFile, setSelectedFile] = useState<File>();
const [selectedSystem, setSelectedSystem] = useState<
AgencySystems | undefined
>(userSystems.length === 1 ? userSystems[0] : undefined);
>();

const handleFileUpload = async (
file: File,
Expand All @@ -118,12 +123,15 @@ export const DataUpload: React.FC = observer(() => {
const data = await response?.json();

const errorsWarningsAndMetrics = processUploadResponseBody(data);
const hasErrorsOrWarnings =
(errorsWarningsAndMetrics.preIngestErrors &&
errorsWarningsAndMetrics.preIngestErrors.length > 0) ||
errorsWarningsAndMetrics.errorSheetsAndSuccessfulMetrics.errorSheets
.length > 0 ||
errorsWarningsAndMetrics.errorSheetsAndSuccessfulMetrics.hasWarnings;
setIsLoading(false);

if (
errorsWarningsAndMetrics.errorCount ||
errorsWarningsAndMetrics.warningCount
) {
if (hasErrorsOrWarnings) {
return setErrorsWarningsMetrics(errorsWarningsAndMetrics);
}

Expand All @@ -137,35 +145,56 @@ export const DataUpload: React.FC = observer(() => {
const processUploadResponseBody = (
data: DataUploadResponseBody
): ErrorsWarningsMetrics => {
const metricErrors = data.metrics.reduce(
(acc, metric) => [...acc, ...metric.sheets],
[] as MetricErrors[]
);
const errorWarningCount = metricErrors.reduce(
(acc, sheet) => {
sheet.messages.forEach((msg) => {
if (msg.type === "ERROR") acc.errorCount += 1;
if (msg.type === "WARNING") acc.warningCount += 1;
const errorSheetsAndSuccessfulMetrics = data.metrics.reduce(
(acc, metric) => {
/**
* Peek into the `messages` array to look for any error messages within
* the sheet and return `true` if no errors are found
*/
const noErrorsInCurrentSheet =
metric.sheets.filter(
(sheet) =>
sheet.messages.filter((msg) => msg.type === "ERROR").length > 0
).length === 0;

if (metric.sheets.length === 0 || noErrorsInCurrentSheet) {
acc.successfulMetrics.push(metric);
}

metric.sheets.forEach((sheet) => {
sheet.messages.forEach((message) => {
if (message.type === "ERROR") {
acc.errorSheets.push(sheet);
}
if (message.type === "WARNING" && acc.hasWarnings === false) {
acc.hasWarnings = true;
}
});
});

return acc;
},
{
errorCount: 0,
warningCount: 0,
successfulMetrics: [] as UploadedMetric[],
errorSheets: [] as MetricErrors[],
hasWarnings: false,
}
);

/**
* Pre-Ingest errors: errors that are not associated with a metric.
mxosman marked this conversation as resolved.
Show resolved Hide resolved
* @example: user uploads an excel file that contains a sheet not associated
* with a metric.
*/
if (data.pre_ingest_errors) {
errorWarningCount.errorCount += data.pre_ingest_errors.length;
return {
metricErrors,
...errorWarningCount,
errorSheetsAndSuccessfulMetrics,
metrics: data.metrics,
preIngestErrors: data.pre_ingest_errors,
};
}

return { metricErrors, ...errorWarningCount, metrics: data.metrics };
return { errorSheetsAndSuccessfulMetrics, metrics: data.metrics };
};

const handleSystemSelection = (file: File, system: AgencySystems) => {
Expand All @@ -181,14 +210,6 @@ export const DataUpload: React.FC = observer(() => {
setSelectedSystem(userSystems.length === 1 ? userSystems[0] : undefined);
};

if (isLoading) {
return (
<DataUploadContainer>
<Loading />
</DataUploadContainer>
);
}

const renderCurrentUploadStep = (): JSX.Element => {
/**
* There are ~4 steps in the upload phase before reaching the metrics confirmation page.
Expand Down Expand Up @@ -241,6 +262,22 @@ export const DataUpload: React.FC = observer(() => {
);
};

useEffect(() => {
setSelectedSystem(userSystems.length === 1 ? userSystems[0] : undefined);
}, [userSystems]);

if (isLoading) {
return (
<DataUploadContainer>
<DataUploadLoading>
<Loader />
<LoadingHeader>We are processing your data...</LoadingHeader>
<LoadingSubheader>This might take a few minutes.</LoadingSubheader>
</DataUploadLoading>
</DataUploadContainer>
);
}

return (
<DataUploadContainer>
<DataUploadHeader transparent={!selectedFile && !errorsWarningsMetrics}>
Expand Down
18 changes: 9 additions & 9 deletions publisher/src/components/DataUpload/InstructionsTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { ReactComponent as SpreadsheetIcon } from "../assets/microsoft-excel-ico
import {
ButtonWrapper,
DownloadTemplateBox,
DownloadTemplateSystem,
systemToTemplateSpreadsheetFileName,
} from ".";

Expand Down Expand Up @@ -202,18 +203,17 @@ export const GeneralInstructions: React.FC<
const systemFileName = systemToTemplateSpreadsheetFileName[system];

return (
<DownloadTemplateBox key={system}>
<DownloadTemplateBox
key={system}
href={`./assets/${systemFileName}`}
download={systemFileName}
>
<SpreadsheetIcon />

<span>
<DownloadTemplateSystem>
{systemName}
<a
href={`./assets/${systemFileName}`}
download={systemFileName}
>
Download
</a>
</span>
<span>Download</span>
</DownloadTemplateSystem>
</DownloadTemplateBox>
);
})}
Expand Down
Loading