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

[Resolve Errors] [Review Warnings] Exceed error warning limit alert & counts read off the response body #699

Merged
merged 5 commits into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
21 changes: 18 additions & 3 deletions src/api/requests/downloadValidationReport.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,46 @@
import { FILING_URL } from 'api/common';
import type { SblAuthProperties } from 'api/useSblAuth';
import type { AxiosProgressEvent } from 'axios';
import axios from 'axios';
import type { FilingPeriodType } from 'types/filingTypes';
import { Hundred } from 'utils/constants';

export interface DownloadValidationReportProperties {
auth: SblAuthProperties;
lei: string;
submissionId: number;
filingPeriod: FilingPeriodType;
afterDownloadCallback?: () => void;
}

export const downloadValidationReport = async ({
auth,
lei,
filingPeriod,
submissionId,
afterDownloadCallback,
}: DownloadValidationReportProperties): Promise<void> => {
try {
await axios({
baseURL: FILING_URL,
headers: {
Authorization: `Bearer ${auth.user?.access_token}`,
'Cache-Control': 'no-cache',
Pragma: 'no-cache',
Expires: '0',
},
url: `/v1/filing/institutions/${lei}/filings/${filingPeriod}/submissions/${submissionId}/report`,
method: 'GET',
responseType: 'blob',
onDownloadProgress: (progressEvent: AxiosProgressEvent): void => {
if (
typeof progressEvent.total === 'number' &&
typeof progressEvent.loaded === 'number'
) {
// Keep incase we decide to use a progress bar
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const percentCompleted = Math.round(
(progressEvent.loaded * Hundred) / progressEvent.total,
);
}
},
}).then(response => {
const url = window.URL.createObjectURL(
new Blob([response.data], { type: 'text/csv;charset=utf-8' }),
Expand All @@ -47,6 +60,8 @@ export const downloadValidationReport = async ({
});
} catch (error) {
console.log(error);
} finally {
if (afterDownloadCallback) afterDownloadCallback();
}
};

Expand Down
3 changes: 2 additions & 1 deletion src/api/requests/uploadCsvAxios.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { filingApiClient, request } from 'api/axiosService';
import type { SblAuthProperties } from 'api/useSblAuth';
import type { AxiosProgressEvent } from 'axios';
import type { FilingPeriodType, SubmissionResponse } from 'types/filingTypes';
import type { InstitutionDetailsApiType } from 'types/formTypes';
import { Hundred } from 'utils/constants';
Expand All @@ -24,7 +25,7 @@ const uploadCsvAxios = async (
'Content-Type': 'multipart/form-data',
},
options: {
onUploadProgress: progressEvent => {
onUploadProgress: (progressEvent: AxiosProgressEvent): void => {
if (
typeof progressEvent.total === 'number' &&
typeof progressEvent.loaded === 'number'
Expand Down
25 changes: 23 additions & 2 deletions src/pages/Filing/FilingApp/FieldEntry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { Link } from 'components/Link';
import { Heading, Pagination, Table } from 'design-system-react';
import { useState } from 'react';
import Markdown from 'react-markdown';
import type { Detail, Field } from 'types/filingTypes';
import type { Detail, Field, FilingPeriodType } from 'types/filingTypes';
import { ITEMS_PER_PAGE, One } from 'utils/constants';
import useIsOverflowing from 'utils/useIsOverflowing';
import FilingErrorsWarningsLimit from './FilingErrors/FilingErrorsWarningsLimit';

// NOTE: To be removed after table styling finalized
// const maxUidTestRows = [...Array.from({ length: Hundred }).keys()].map(
Expand All @@ -27,13 +28,24 @@ import useIsOverflowing from 'utils/useIsOverflowing';

interface FieldEntryProperties {
fieldObject: Detail;
lei: string;
submissionId: number;
filingPeriod: FilingPeriodType;
isWarning?: boolean;
}

function FieldEntry({ fieldObject }: FieldEntryProperties): JSX.Element {
function FieldEntry({
fieldObject,
lei,
submissionId,
filingPeriod,
isWarning,
}: FieldEntryProperties): JSX.Element {
const validationId = fieldObject.validation.id;
const validationLink = fieldObject.validation.fig_link;
const validationName = fieldObject.validation.name;
const validationDescription = fieldObject.validation.description;
const validationIsTruncated = fieldObject.validation.is_truncated;
// eslint-disable-next-line unicorn/no-array-reduce
const additionalColumnHeaders = fieldObject.records[0].fields.reduce(
(accumulator: Field['name'][], fieldsObject) => [
Expand Down Expand Up @@ -119,6 +131,11 @@ function FieldEntry({ fieldObject }: FieldEntryProperties): JSX.Element {
</Link>
<Heading type='4'>{validationName}</Heading>
<Markdown>{validationDescription}</Markdown>
{validationIsTruncated ? (
<FilingErrorsWarningsLimit
{...{ isWarning, lei, submissionId, filingPeriod }}
/>
) : null}
</div>
<div className='mb-[0.9375rem]'>
<Table
Expand Down Expand Up @@ -160,4 +177,8 @@ function FieldEntry({ fieldObject }: FieldEntryProperties): JSX.Element {
);
}

FieldEntry.defaultProps = {
isWarning: false,
};

export default FieldEntry;
17 changes: 15 additions & 2 deletions src/pages/Filing/FilingApp/FieldSummary.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import SectionIntro from 'components/SectionIntro';
import FieldEntry from 'pages/Filing/FilingApp/FieldEntry';
import type { ReactNode } from 'react';
import type { Detail } from 'types/filingTypes';
import type { Detail, FilingPeriodType } from 'types/filingTypes';

interface FieldProperties {
fieldArray: Detail[];
heading: string;
bottomMargin?: boolean;
children: ReactNode;
id: string;
lei: string;
submissionId: number;
filingPeriod: FilingPeriodType;
isWarning?: boolean;
}

function FieldSummary({
Expand All @@ -18,6 +22,10 @@ function FieldSummary({
children,
id,
className = '',
lei,
submissionId,
filingPeriod,
isWarning,
}: FieldProperties & JSX.IntrinsicElements['div']): JSX.Element {
return (
<div
Expand All @@ -28,14 +36,19 @@ function FieldSummary({
{children}
</SectionIntro>
{fieldArray.map(fieldObject => (
<FieldEntry key={fieldObject.validation.id} fieldObject={fieldObject} />
<FieldEntry
key={fieldObject.validation.id}
fieldObject={fieldObject}
{...{ isWarning, lei, submissionId, filingPeriod }}
/>
))}
</div>
);
}

FieldSummary.defaultProps = {
bottomMargin: false,
isWarning: false,
};

export default FieldSummary;
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export const getErrorsWarningsSummary = (
};
};

// Deprecated: Count should now come from the data response object
export const getRecordsAffected = (
validationDetails: Detail[],
): Set<number> => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
import downloadValidationReport from 'api/requests/downloadValidationReport';
import useSblAuth from 'api/useSblAuth';
import { AlertFieldLevel, Button } from 'design-system-react';
import type { FilingPeriodType } from 'types/filingTypes';
import { Thousand } from 'utils/constants';

interface DownloadValidationReportButtonLimitProperties {
lei: string;
submissionId: number;
filingPeriod: FilingPeriodType;
}
const onHandleDownloadClick = (): void => {
const element = document.querySelector(
`#download-validation-report-button`,
) as HTMLElement | undefined;
if (element) {
element.scrollIntoView({
behavior: 'smooth',
block: 'end',
inline: 'nearest',
});
setTimeout(() => element.focus(), Thousand);
}
};

function DownloadValidationReportButton({
lei,
filingPeriod,
submissionId,
}: DownloadValidationReportButtonLimitProperties): JSX.Element {
const auth = useSblAuth();
const onHandleDownloadClick = async (): Promise<void> => {
await downloadValidationReport({ auth, lei, filingPeriod, submissionId });
};
function DownloadValidationReportButton(): JSX.Element {
return (
// eslint-disable-next-line @typescript-eslint/no-misused-promises
<Button onClick={onHandleDownloadClick} label='Download report' asLink />
);
}
Expand All @@ -30,32 +27,22 @@ interface FilingErrorsWarningsLimitProperties {

function FilingErrorsWarningsLimit({
isWarning,
lei,
filingPeriod,
submissionId,
}: DownloadValidationReportButtonLimitProperties &
FilingErrorsWarningsLimitProperties): JSX.Element {
}: FilingErrorsWarningsLimitProperties): JSX.Element {
return (
<div className='mt-[0.9375rem] max-w-[41.875rem]'>
<div className='my-[1.875rem] max-w-[41.875rem]'>
<AlertFieldLevel
message={
isWarning ? (
<>
The number of warnings for this validation exceeds the display
limit.{' '}
<DownloadValidationReportButton
{...{ lei, filingPeriod, submissionId }}
/>{' '}
to see the full list of warnings.
limit. <DownloadValidationReportButton /> to see the full list of
warnings.
</>
) : (
<>
The number of errors for this validation exceeds the display
limit.{' '}
<DownloadValidationReportButton
{...{ lei, filingPeriod, submissionId }}
/>{' '}
to see the full list of logic errors and warnings.
limit. <DownloadValidationReportButton /> to see the full list of
logic errors and warnings.
</>
)
}
Expand Down
33 changes: 22 additions & 11 deletions src/pages/Filing/FilingApp/FilingErrors/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ import FormWrapper from 'components/FormWrapper';
import { LoadingContent } from 'components/Loading';
import { Paragraph, TextIntroduction } from 'design-system-react';
import FieldSummary from 'pages/Filing/FilingApp/FieldSummary';
import {
getErrorsWarningsSummary,
getRecordsAffected,
} from 'pages/Filing/FilingApp/FilingErrors/FilingErrors.helpers';
import { getErrorsWarningsSummary } from 'pages/Filing/FilingApp/FilingErrors/FilingErrors.helpers';
import FilingErrorsAlerts from 'pages/Filing/FilingApp/FilingErrors/FilingErrorsAlerts';
import { FilingSteps } from 'pages/Filing/FilingApp/FilingSteps';
import InstitutionHeading from 'pages/Filing/FilingApp/InstitutionHeading';
Expand Down Expand Up @@ -64,11 +61,16 @@ function FilingErrors(): JSX.Element {
: syntaxErrorsSingle;

// Count rows with errors per type (not total errors)
const singleFieldRowErrorsCount = getRecordsAffected(
singleFieldErrorsUsed,
).size;
const multiFieldRowErrorsCount = getRecordsAffected(logicErrorsMulti).size;
const registerLevelRowErrorsCount = getRecordsAffected(registerErrors).size;
const singleFieldCategory = isStep2 ? 'logic_errors' : 'syntax_errors';
const singleFieldRowErrorsCount =
actualDataGetSubmissionLatest?.validation_results?.[singleFieldCategory]
.single_field_count ?? 0;
const multiFieldRowErrorsCount =
actualDataGetSubmissionLatest?.validation_results?.[singleFieldCategory]
.multi_field_count ?? 0;
const registerLevelRowErrorsCount =
actualDataGetSubmissionLatest?.validation_results?.[singleFieldCategory]
.register_count ?? 0;

if (isFetchingGetSubmissionLatest || isLoadingInstitution)
return <LoadingContent />;
Expand Down Expand Up @@ -168,25 +170,31 @@ function FilingErrors(): JSX.Element {
{!errorGetSubmissionLatest && (
<>
{/* SINGLE-FIELD ERRORS */}
{errorState ? (
{errorState && actualDataGetSubmissionLatest?.id ? (
<FieldSummary
id='single-field-errors'
heading={`Single-field errors: ${singleFieldRowErrorsCount.toLocaleString()} found`}
fieldArray={singleFieldErrorsUsed}
lei={lei}
filingPeriod={year}
submissionId={actualDataGetSubmissionLatest.id}
bottomMargin={Boolean(isStep2)}
>
Each single-field validation pertains to only one specific field
in each record. These validations check that the data held in an
individual field match the values that are expected.
</FieldSummary>
) : null}
{isStep2 && errorState ? (
{isStep2 && errorState && actualDataGetSubmissionLatest?.id ? (
<>
{/* MULTI-FIELD ERRORS */}
<FieldSummary
id='multi-field-errors'
heading={`Multi-field errors: ${multiFieldRowErrorsCount.toLocaleString()} found`}
fieldArray={logicErrorsMulti}
lei={lei}
filingPeriod={year}
submissionId={actualDataGetSubmissionLatest.id}
bottomMargin
>
Multi-field validations check that the values of certain
Expand All @@ -198,6 +206,9 @@ function FilingErrors(): JSX.Element {
id='register-level-errors'
heading={`Register-level errors: ${registerLevelRowErrorsCount.toLocaleString()} found`}
fieldArray={registerErrors}
lei={lei}
filingPeriod={year}
submissionId={actualDataGetSubmissionLatest.id}
>
This validation checks that the register does not contain
duplicate IDs.
Expand Down
20 changes: 18 additions & 2 deletions src/pages/Filing/FilingApp/FilingFieldLinks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import downloadValidationReport from 'api/requests/downloadValidationReport';
import useSblAuth from 'api/useSblAuth';
import { Link } from 'components/Link';
import { Button, Paragraph } from 'design-system-react';
import { useCallback, useState } from 'react';
import type { FilingPeriodType } from 'types/filingTypes';

interface FilingFieldLinksProperties {
Expand All @@ -19,19 +20,34 @@ function FilingFieldLinks({
className,
...others
}: FilingFieldLinksProperties & JSX.IntrinsicElements['div']): JSX.Element {
// download in-progress state
const [downloadInProgress, setDownloadInProgress] = useState<boolean>(false);
const auth = useSblAuth();

const afterDownloadCallback = useCallback(
() => setDownloadInProgress(false),
[],
);

const onHandleDownloadClick = async (): Promise<void> => {
await downloadValidationReport({ auth, lei, filingPeriod, submissionId });
setDownloadInProgress(true);
await downloadValidationReport({
auth,
lei,
filingPeriod,
submissionId,
afterDownloadCallback,
});
};
return (
<div id={id} className={`mt-[1.875rem] ${className}`} {...others}>
<div className='flex items-center gap-[0.9375rem]'>
<Button
label='Download report'
id='download-validation-report-button'
// eslint-disable-next-line @typescript-eslint/no-misused-promises
onClick={onHandleDownloadClick}
iconRight='download'
iconRight={downloadInProgress ? 'updating' : 'download'}
/>
<Paragraph>
<Link href={`/filing/${filingPeriod}/${lei}/upload`}>
Expand Down
Loading