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

MPDX-8205 Style CSV #1063

Merged
merged 5 commits into from
Sep 13, 2024
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
22 changes: 12 additions & 10 deletions src/components/Tool/Import/Csv/CsvHeaders.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,9 @@ describe('CsvHeaders', () => {
></CsvHeadersMockComponent>,
);

expect(queryByText('first_name is required')).toBeInTheDocument();
expect(queryByText('last_name is required')).toBeInTheDocument();
expect(queryByText('First Name is required')).toBeInTheDocument();
expect(queryByText('Last Name is required')).toBeInTheDocument();
expect(queryByText('Full Name is required')).toBeInTheDocument();
});

it('should not show errors if required headers are mapped', async () => {
Expand All @@ -112,13 +113,16 @@ describe('CsvHeaders', () => {
></CsvHeadersMockComponent>,
);

expect(queryByText('first_name is required')).not.toBeInTheDocument();
expect(queryByText('last_name is required')).not.toBeInTheDocument();
expect(queryByText('full_name is required')).not.toBeInTheDocument();
expect(queryByText('First Name is required')).not.toBeInTheDocument();
expect(queryByText('Last Name is required')).not.toBeInTheDocument();
expect(queryByText('Full Name is required')).not.toBeInTheDocument();
});

it('should allow the user to map every header', async () => {
uploadData.fileHeaders = { first_name: 'Test1', custom_header: 'Wee' };
uploadData.fileHeaders = {
first_name: 'First Name',
custom_header: 'Wee',
};

const { findByRole } = render(
<CsvHeadersMockComponent
Expand All @@ -128,11 +132,9 @@ describe('CsvHeaders', () => {
);

expect(
await findByRole('cell', { name: 'first_name' }),
).toBeInTheDocument();
expect(
await findByRole('cell', { name: 'custom_header' }),
await findByRole('cell', { name: 'First Name' }),
).toBeInTheDocument();
expect(await findByRole('cell', { name: 'Wee' })).toBeInTheDocument();
});
});

Expand Down
84 changes: 52 additions & 32 deletions src/components/Tool/Import/Csv/CsvHeaders.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,30 @@ import {
Alert,
Box,
Button,
Card,
CardHeader,
List,
ListItem,
MenuItem,
Select,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
Typography,
} from '@mui/material';
import { cloneDeep } from 'lodash/fp';
import { useTranslation } from 'react-i18next';
import { useApiConstants } from 'src/components/Constants/UseApiConstants';
import { Confirmation } from 'src/components/common/Modal/Confirmation/Confirmation';
import useGetAppSettings from 'src/hooks/useGetAppSettings';
import theme from 'src/theme';
import {
CsvImportContext,
CsvImportType,
CsvImportValue,
CsvImportViewStepEnum,
} from './CsvImportContext';
import { HeaderBox } from './HeaderBox';
import { get, save } from './csvImportService';
import { useRequiredHeaders, useSupportedHeaders } from './uploadCsvFile';

Expand Down Expand Up @@ -56,8 +59,21 @@ const updateUnmappedHeaders = (
fileHeadersMappings: object,
setUnmappedHeaders: React.Dispatch<React.SetStateAction<string[]>>,
) => {
const unmapped = requiredHeaders.filter((header) => {
return !Object.values(fileHeadersMappings).includes(header);
const fileHeaderMappingsValues = Object.values(fileHeadersMappings);

const containsName =
(fileHeaderMappingsValues.includes('first_name') &&
fileHeaderMappingsValues.includes('last_name')) ||
fileHeaderMappingsValues.includes('full_name');

// If the file's headers contain a name, then remove names from required headers.
const newRequiredHeaders = containsName
? requiredHeaders.filter(
(header) => !['first_name', 'last_name', 'full_name'].includes(header),
)
: requiredHeaders;
const unmapped = newRequiredHeaders.filter((header) => {
return !fileHeaderMappingsValues.includes(header);
});
setUnmappedHeaders(unmapped);
};
Expand Down Expand Up @@ -200,23 +216,34 @@ const CsvHeaders: React.FC<CsvHeadersProps> = ({
{unmappedHeaders.map((header) => {
return (
<p key={header}>
{header}
{supportedHeaders[header]}
{t(' is required')}
</p>
);
})}
{unmappedHeaders.includes('full_name') && (
<p>
{t('* You need to include both First & Last Name OR Full Name')}
</p>
)}
</Alert>
)}

<Alert severity="info" sx={{ marginBottom: '12px', minWidth: '340px' }}>
<ul>
<li>
<Alert
severity="info"
icon={false}
sx={{ marginBottom: '12px', minWidth: '340px' }}
>
<List sx={{ listStyleType: 'disc', pl: 2 }}>
<ListItem sx={{ display: 'list-item', p: 0 }}>
{t(
'Columns with duplicate or empty headers are ignored. Please ensure your CSV does not have any such headers!',
)}
</li>
<li>{t('Street is required to import any address information.')}</li>
</ul>
</ListItem>
<ListItem sx={{ display: 'list-item', p: 0 }}>
{t('Street is required to import any address information.')}
</ListItem>
</List>
</Alert>

{showBackWarningModal && (
Expand All @@ -233,10 +260,13 @@ const CsvHeaders: React.FC<CsvHeadersProps> = ({
></Confirmation>
)}

<Box sx={{ border: '1px solid', minWidth: '340px' }}>
<HeaderBox>
<Typography variant="body1">{t('Map your headers')}</Typography>
</HeaderBox>
<Card sx={{ minWidth: '340px' }}>
<CardHeader
sx={{
backgroundColor: theme.palette.cruGrayLight.main,
}}
title={t('Map your headers')}
/>
<Table>
<TableHead>
<TableRow>
Expand All @@ -248,15 +278,16 @@ const CsvHeaders: React.FC<CsvHeadersProps> = ({
</TableHead>

<TableBody>
{Object.keys(importHeaders).map((header) => {
{Object.entries(importHeaders).map(([header, headerName]) => {
return (
<TableRow key={header}>
<TableCell>{header}</TableCell>
<TableCell>
<TableCell>{headerName}</TableCell>
<TableCell sx={{ paddingY: 1 }}>
<Select
onChange={(e) => handleUpdateHeaders(e, header)}
value={fileHeadersMappings[header] || -1}
sx={{ minWidth: '210px' }}
size="small"
>
<MenuItem value={-1} selected={true}>
{t('Do Not Import')}
Expand Down Expand Up @@ -289,29 +320,18 @@ const CsvHeaders: React.FC<CsvHeadersProps> = ({
justifyContent: 'space-between',
}}
>
<Button
sx={{
bgcolor: 'cruGrayDark.main',
color: 'white',
height: '34px',
}}
onClick={handleBack}
>
<Button variant="contained" onClick={handleBack}>
{t('Back')}
</Button>
<Button
sx={{
bgcolor: 'mpdxBlue.main',
color: 'white',
height: '34px',
}}
variant="contained"
onClick={handleSave}
disabled={!uploadData || unmappedHeaders.length !== 0}
>
{t('Next')}
</Button>
</Box>
</Box>
</Card>
</>
);
};
Expand Down
120 changes: 57 additions & 63 deletions src/components/Tool/Import/Csv/CsvImportWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Box, Grid, Typography } from '@mui/material';
import { Box, Card, Grid, Typography } from '@mui/material';
import { styled } from '@mui/material/styles';
import { TFunction } from 'react-i18next';
import { makeStyles } from 'tss-react/mui';
Expand All @@ -9,7 +9,6 @@ import { DynamicCsvHeaders } from './DynamicCsvHeaders';
import { DynamicCsvPreview } from './DynamicCsvPreview';
import { DynamicCsvUpload } from './DynamicCsvUpload';
import { DynamicCsvValues } from './DynamicCsvValues';
import { HeaderBox } from './HeaderBox';

const useStyles = makeStyles()(() => ({
panelSuccess: {
Expand All @@ -19,20 +18,10 @@ const useStyles = makeStyles()(() => ({
},
}));

const StepBox = styled(HeaderBox)(({ theme }) => ({
border: '1px solid transparent',
const StepCard = styled(Card)(({ theme }) => ({
textAlign: 'center',
width: '22%',
marginLeft: '10px',
marginRight: '10px',
[theme.breakpoints.down('lg')]: {
width: '22%',
},
[theme.breakpoints.down('md')]: {
width: '100%',
marginLeft: 0,
minWidth: '340px',
},
backgroundColor: theme.palette.cruGrayLight.main,
padding: '10px',
}));

export interface CsvImportWrapperProps {
Expand Down Expand Up @@ -95,58 +84,63 @@ export const CsvImportWrapper: React.FC<CsvImportWrapperProps> = ({
flexDirection: 'row',
justifyContent: 'center',
width: '100%',
minWidth: '340px',
}}
>
<ToolsGridContainer container spacing={3}>
<Grid item xs={12}>
<Box>
<StepBox
m={2}
className={
currentTab === CsvImportViewStepEnum.Upload
? classes.panelSuccess
: ''
}
>
<Typography variant="h5">{t('Step 1')}</Typography>
<Typography variant="body1">
{t('Upload your CSV File')}
</Typography>
</StepBox>
<StepBox
m={2}
className={
currentTab === CsvImportViewStepEnum.Headers
? classes.panelSuccess
: ''
}
>
<Typography variant="h5">{t('Step 2')}</Typography>
<Typography variant="body1">{t('Map your headers')}</Typography>
</StepBox>
<StepBox
m={2}
className={
currentTab === CsvImportViewStepEnum.Values
? classes.panelSuccess
: ''
}
>
<Typography variant="h5">{t('Step 3')}</Typography>
<Typography variant="body1">{t('Map your values')}</Typography>
</StepBox>
<StepBox
m={2}
className={
currentTab === CsvImportViewStepEnum.Preview
? classes.panelSuccess
: ''
}
>
<Typography variant="h5">{t('Step 4')}</Typography>
<Typography variant="body1">{t('Preview')}</Typography>
</StepBox>
</Box>
<Grid container spacing={2}>
<Grid item xs={12} sm={6} md={3}>
<StepCard
className={
currentTab === CsvImportViewStepEnum.Upload
? classes.panelSuccess
: ''
}
>
<Typography variant="h5">{t('Step 1')}</Typography>
<Typography variant="body1">
{t('Upload your CSV File')}
</Typography>
</StepCard>
</Grid>
<Grid item xs={12} sm={6} md={3}>
<StepCard
className={
currentTab === CsvImportViewStepEnum.Headers
? classes.panelSuccess
: ''
}
>
<Typography variant="h5">{t('Step 2')}</Typography>
<Typography variant="body1">{t('Map your headers')}</Typography>
</StepCard>
</Grid>
<Grid item xs={12} sm={6} md={3}>
<StepCard
className={
currentTab === CsvImportViewStepEnum.Values
? classes.panelSuccess
: ''
}
>
<Typography variant="h5">{t('Step 3')}</Typography>
<Typography variant="body1">{t('Map your values')}</Typography>
</StepCard>
</Grid>
<Grid item xs={12} sm={6} md={3}>
<StepCard
className={
currentTab === CsvImportViewStepEnum.Preview
? classes.panelSuccess
: ''
}
>
<Typography variant="h5">{t('Step 4')}</Typography>
<Typography variant="body1">{t('Preview')}</Typography>
</StepCard>
</Grid>
</Grid>
<br />
{renderTab(accountListId)}
</Grid>
Expand Down
Loading
Loading