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

Update CSV Error Import Dialog Styling #1456

Open
wants to merge 6 commits into
base: dev
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ describe('TelemetryHeaderConfigs', () => {
const result = cellValidator({ cell: 5555 } as CSVParams);
expect(result).to.deep.equal([
{
error: 'Device not found in the survey deployments',
solution: 'Check the serial number and vendor are correct and the device is deployed in the survey'
error: 'Device not found in deployments',
solution: 'Check that the serial number and vendor match a deployment in the Survey'
}
]);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ export const getTelemetrySerialCellValidator = (
if (!deployment) {
return [
{
error: `Device not found in the survey deployments`,
solution: `Check the serial number and vendor are correct and the device is deployed in the survey`
error: `Device not found in deployments`,
solution: `Check that the serial number and vendor match a deployment in the Survey`
}
];
}
Expand Down
6 changes: 3 additions & 3 deletions api/src/utils/csv-utils/csv-config-validation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ describe('csv-config-validation', () => {
errors: [
{
error: 'A required column is missing',
solution: `Add all required columns to the file.`,
solution: `Add the ALIAS column to the file.`,
header: 'ALIAS',
values: ['ALIAS', 'ALIAS_2'],
cell: null,
Expand Down Expand Up @@ -217,7 +217,7 @@ describe('csv-config-validation', () => {
{
row: 1,
error: 'A required column is missing',
solution: `Add all required columns to the file.`,
solution: `Add the ALIAS column to the file.`,
header: 'ALIAS',
values: ['ALIAS'],
cell: null
Expand Down Expand Up @@ -247,7 +247,7 @@ describe('csv-config-validation', () => {
{
row: 1,
error: 'An unknown column is included in the file',
solution: `Remove extra columns from the file.`,
solution: `Remove the UNKNOWN_HEADER column from the file.`,
header: 'UNKNOWN_HEADER',
cell: null,
values: null
Expand Down
4 changes: 2 additions & 2 deletions api/src/utils/csv-utils/csv-config-validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export const validateCSVHeaders = (worksheet: WorkSheet, config: CSVConfig): CSV
if (!headerConfig.optional && !worksheetHasStaticHeader) {
csvErrors.push({
error: 'A required column is missing',
solution: `Add all required columns to the file.`,
solution: `Add the ${staticHeader} column to the file.`,
values: [staticHeader, ...config.staticHeadersConfig[staticHeader].aliases],
header: staticHeader,
cell: null,
Expand All @@ -141,7 +141,7 @@ export const validateCSVHeaders = (worksheet: WorkSheet, config: CSVConfig): CSV
for (const unknownHeader of configUtils.worksheetDynamicHeaders) {
csvErrors.push({
error: 'An unknown column is included in the file',
solution: `Remove extra columns from the file.`,
solution: `Remove the ${unknownHeader} column from the file.`,
values: null,
header: unknownHeader,
cell: null,
Expand Down
13 changes: 10 additions & 3 deletions app/src/components/alert/AlertBar.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import Alert, { AlertProps } from '@mui/material/Alert';
import AlertTitle from '@mui/material/AlertTitle';
import Typography from '@mui/material/Typography';

interface IAlertBarProps extends AlertProps {
severity: 'error' | 'warning' | 'info' | 'success';
variant: 'filled' | 'outlined' | 'standard';
title: string;
text: string | JSX.Element;
ornament?: JSX.Element;
}

/**
Expand All @@ -15,7 +17,7 @@
* @returns
*/
const AlertBar = (props: IAlertBarProps) => {
const { severity, variant, title, text, ...alertProps } = props;
const { severity, variant, title, text, ornament, ...alertProps } = props;

Check warning on line 20 in app/src/components/alert/AlertBar.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/alert/AlertBar.tsx#L20

Added line #L20 was not covered by tests

const defaultProps = {
severity: 'success',
Expand All @@ -30,8 +32,13 @@
{...alertProps}
variant={variant}
severity={severity}
sx={{ flex: '1 1 auto', ...alertProps.sx }}>
<AlertTitle>{title}</AlertTitle>
sx={{ flex: '1 1 auto', '& .MuiAlert-message': { flex: '1 1 auto' }, ...alertProps.sx }}>
<AlertTitle sx={{ justifyContent: 'space-between', display: 'flex', alignItems: 'center', flex: '1 1 auto' }}>
{title}
<Typography component="span" variant="body2">
{ornament}
</Typography>
</AlertTitle>
{text}
</Alert>
);
Expand Down
35 changes: 18 additions & 17 deletions app/src/components/csv/CSVDropzoneSection.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { Box } from '@mui/material';
import { Box, Typography } from '@mui/material';
import Button from '@mui/material/Button';
import { CSVErrorsTableContainer } from 'components/csv/CSVErrorsTableContainer';
import HorizontalSplitFormComponent from 'components/fields/HorizontalSplitFormComponent';
import { PropsWithChildren } from 'react';
import { CSVError } from 'utils/csv-utils';
import { CSVErrorsCardStackContainer } from './CSVErrorsCardStackContainer';

interface CSVDropzoneSectionProps {
title: string;
Expand All @@ -21,20 +20,22 @@ interface CSVDropzoneSectionProps {
*/
export const CSVDropzoneSection = (props: PropsWithChildren<CSVDropzoneSectionProps>) => {
return (
mauberti-bc marked this conversation as resolved.
Show resolved Hide resolved
<HorizontalSplitFormComponent title={props.title} summary={props.summary}>
<Box sx={{ display: 'flex', flexDirection: 'column' }} gap={2}>
<Box sx={{ display: 'flex', ml: 'auto' }}>
<Button
sx={{ textTransform: 'none', fontWeight: 'regular' }}
variant="outlined"
size="small"
onClick={props.onDownloadTemplate}>
Download Template
</Button>
</Box>
{props.children}
{props.errors.length > 0 ? <CSVErrorsTableContainer errors={props.errors} /> : null}
<Box sx={{ display: 'flex', flexDirection: 'column' }} gap={2}>
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
<Typography variant="h3">{props.title}</Typography>
<Button
sx={{ textTransform: 'none', fontWeight: 'regular' }}
variant="outlined"
size="small"
onClick={props.onDownloadTemplate}>
Download Template
</Button>
</Box>
</HorizontalSplitFormComponent>
<Typography color="textSecondary" variant="body2">
{props.summary}
</Typography>
{props.children}
{props.errors.length > 0 ? <CSVErrorsCardStackContainer errors={props.errors} /> : null}
</Box>
);
};
109 changes: 109 additions & 0 deletions app/src/components/csv/CSVErrorsCardStack.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { mdiChevronLeft, mdiChevronRight } from '@mdi/js';
import Icon from '@mdi/react';
import IconButton from '@mui/material/IconButton';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import AlertBar from 'components/alert/AlertBar';
import { useMemo, useState } from 'react';
import { CSVError } from 'utils/csv-utils';
import { v4 } from 'uuid';

const MAX_ERRORS_SHOWN = 10;

Check warning on line 11 in app/src/components/csv/CSVErrorsCardStack.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/csv/CSVErrorsCardStack.tsx#L11

Added line #L11 was not covered by tests

interface CSVErrorsCardStackProps {
errors: CSVError[];
}

/**
* Returns a stack of CSV errors with information about solutions and pagination
*
* @param {CSVErrorsCardStackProps} props
* @returns {*}
*/
export const CSVErrorsCardStack = (props: CSVErrorsCardStackProps) => {
const [currentPage, setCurrentPage] = useState(0);

Check warning on line 24 in app/src/components/csv/CSVErrorsCardStack.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/csv/CSVErrorsCardStack.tsx#L23-L24

Added lines #L23 - L24 were not covered by tests

const pageCount = Math.ceil(props.errors.length / MAX_ERRORS_SHOWN);

Check warning on line 26 in app/src/components/csv/CSVErrorsCardStack.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/csv/CSVErrorsCardStack.tsx#L26

Added line #L26 was not covered by tests

const rows: (CSVError & { id: string })[] = useMemo(() => {
return props.errors.slice(currentPage * MAX_ERRORS_SHOWN, (currentPage + 1) * MAX_ERRORS_SHOWN).map((error) => {
return {

Check warning on line 30 in app/src/components/csv/CSVErrorsCardStack.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/csv/CSVErrorsCardStack.tsx#L28-L30

Added lines #L28 - L30 were not covered by tests
id: v4(),
...error
};
});
}, [props.errors, currentPage]);

const handleNextPage = () => {

Check warning on line 37 in app/src/components/csv/CSVErrorsCardStack.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/csv/CSVErrorsCardStack.tsx#L37

Added line #L37 was not covered by tests
if ((currentPage + 1) * MAX_ERRORS_SHOWN < props.errors.length) {
setCurrentPage(currentPage + 1);

Check warning on line 39 in app/src/components/csv/CSVErrorsCardStack.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/csv/CSVErrorsCardStack.tsx#L39

Added line #L39 was not covered by tests
}
};

const handlePreviousPage = () => {

Check warning on line 43 in app/src/components/csv/CSVErrorsCardStack.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/csv/CSVErrorsCardStack.tsx#L43

Added line #L43 was not covered by tests
if (currentPage > 0) {
setCurrentPage(currentPage - 1);

Check warning on line 45 in app/src/components/csv/CSVErrorsCardStack.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/csv/CSVErrorsCardStack.tsx#L45

Added line #L45 was not covered by tests
}
};

return (

Check warning on line 49 in app/src/components/csv/CSVErrorsCardStack.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/csv/CSVErrorsCardStack.tsx#L49

Added line #L49 was not covered by tests
<Stack gap={1}>
{rows.map((error) => {
return (

Check warning on line 52 in app/src/components/csv/CSVErrorsCardStack.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/csv/CSVErrorsCardStack.tsx#L52

Added line #L52 was not covered by tests
<AlertBar
key={error.id}
severity="error"
variant="standard"
title={error.error}
text={
<Stack gap={1}>
<Typography variant="body2">{error.solution}</Typography>
<Stack gap={3} flexDirection="row">
<Stack>
<Typography variant="body2" fontWeight={700}>
Row
</Typography>
<Typography variant="body2">{error.row ?? 'N/A'}</Typography>
</Stack>
<Stack>
<Typography variant="body2" fontWeight={700}>
Column
</Typography>
<Typography variant="body2">{error.header ?? 'N/A'}</Typography>
</Stack>
<Stack>
<Typography variant="body2" fontWeight={700}>
Copy link
Collaborator

@MacQSL MacQSL Jan 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: The previous version also handled "values", which are the available options the user can enter for the specific cell (if applicable). ie: ['red', 'blue', ...]
CSVErrorsTableOptionsMenu

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

Fixed

Value
</Typography>
<Typography variant="body2">{error.cell ?? 'N/A'}</Typography>
</Stack>
{error.cell && error.values && (
<Stack>
<Typography variant="body2" fontWeight={700}>
Allowed Values
</Typography>
<Typography variant="body2">{error.values.join(', ')}</Typography>
</Stack>
)}
</Stack>
</Stack>
}
/>
);
})}
{props.errors.length > MAX_ERRORS_SHOWN && (
<Stack direction="row" justifyContent="space-between" alignItems="center" mt={1}>
<IconButton onClick={handlePreviousPage} disabled={!currentPage}>
<Icon path={mdiChevronLeft} size={1} />
</IconButton>
<Typography variant="body2">
Page {currentPage + 1} of {pageCount}
</Typography>
<IconButton onClick={handleNextPage} disabled={currentPage + 1 === pageCount}>
<Icon path={mdiChevronRight} size={1} />
</IconButton>
</Stack>
)}
</Stack>
);
};
Original file line number Diff line number Diff line change
@@ -1,47 +1,40 @@
import { Divider, Paper, Toolbar, Typography } from '@mui/material';
import { Toolbar, Typography } from '@mui/material';
import { Box, Stack } from '@mui/system';
import { ReactElement } from 'react';
import { CSVError } from 'utils/csv-utils';
import { CSVErrorsTable } from './CSVErrorsTable';
import { CSVErrorsCardStack } from './CSVErrorsCardStack';

interface CSVErrorsTableContainerProps {
interface CSVErrorsCardStackContainerProps {
errors: CSVError[];
title?: ReactElement;
}

/**
* Renders a CSV errors table with toolbar.
*
* @param {CSVErrorsTableContainerProps} props
* @param {CSVErrorsCardStackContainerProps} props
* @returns {*} {JSX.Element}
*/
export const CSVErrorsTableContainer = (props: CSVErrorsTableContainerProps) => {
export const CSVErrorsCardStackContainer = (props: CSVErrorsCardStackContainerProps) => {

Check warning on line 18 in app/src/components/csv/CSVErrorsCardStackContainer.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/components/csv/CSVErrorsCardStackContainer.tsx#L18

Added line #L18 was not covered by tests
return (
<Paper component={Stack} flexDirection="column" flex="1 1 auto" height="100%">
<Toolbar
disableGutters
sx={{
pl: 2,
pr: 3
}}>
<Stack flexDirection="column" flex="1 1 auto" height="100%">
<Toolbar disableGutters>
{props.title ?? (
<Typography
sx={{
flexGrow: '1',
fontSize: '1.125rem',
fontWeight: 700
}}>
CSV Errors Detected &zwnj;
Errors &zwnj;
<Typography sx={{ fontWeight: '400' }} component="span" variant="inherit" color="textSecondary">
({props.errors.length})
</Typography>
</Typography>
)}
</Toolbar>
<Divider />
<Box width="100%" height="100%">
<CSVErrorsTable errors={props.errors} />
<CSVErrorsCardStack errors={props.errors} />
</Box>
</Paper>
</Stack>
);
};
Loading
Loading