-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2401 from k-nut/issue-2348/import-gender
Map gender fields on CSV import
- Loading branch information
Showing
12 changed files
with
388 additions
and
0 deletions.
There are no files selected for viewing
65 changes: 65 additions & 0 deletions
65
src/features/import/components/ImportDialog/Configure/Configuration/GenderConfig.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { FC } from 'react'; | ||
import { Box, Divider, Typography } from '@mui/material'; | ||
|
||
import messageIds from 'features/import/l10n/messageIds'; | ||
import { GenderColumn } from 'features/import/utils/types'; | ||
import { UIDataColumn } from 'features/import/hooks/useUIDataColumn'; | ||
import { Msg, useMessages } from 'core/i18n'; | ||
import GenderConfigRow from './GenderConfigRow'; | ||
import useGenderMapping from 'features/import/hooks/useGenderMapping'; | ||
|
||
interface GenderConfigProps { | ||
uiDataColumn: UIDataColumn<GenderColumn>; | ||
} | ||
|
||
const GenderConfig: FC<GenderConfigProps> = ({ uiDataColumn }) => { | ||
const messages = useMessages(messageIds); | ||
const { selectGender, getSelectedGender, deselectGender } = useGenderMapping( | ||
uiDataColumn.originalColumn, | ||
uiDataColumn.columnIndex | ||
); | ||
|
||
return ( | ||
<Box | ||
display="flex" | ||
flexDirection="column" | ||
overflow="hidden" | ||
padding={2} | ||
sx={{ overflowY: 'auto' }} | ||
> | ||
<Box alignItems="baseline" display="flex" justifyContent="space-between"> | ||
<Typography sx={{ paddingBottom: 2 }} variant="h5"> | ||
<Msg id={messageIds.configuration.configure.tags.header} /> | ||
</Typography> | ||
</Box> | ||
<Box alignItems="center" display="flex" paddingY={2}> | ||
<Box width="50%"> | ||
<Typography variant="body2"> | ||
{uiDataColumn.title.toLocaleUpperCase()} | ||
</Typography> | ||
</Box> | ||
<Box width="50%"> | ||
<Typography variant="body2"> | ||
{messages.configuration.configure.genders | ||
.label() | ||
.toLocaleUpperCase()} | ||
</Typography> | ||
</Box> | ||
</Box> | ||
{uiDataColumn.uniqueValues.map((uniqueValue, index) => ( | ||
<> | ||
{index != 0 && <Divider sx={{ marginY: 1 }} />} | ||
<GenderConfigRow | ||
numRows={uiDataColumn.numRowsByUniqueValue[uniqueValue]} | ||
onDeselectGender={() => deselectGender(uniqueValue)} | ||
onSelectGender={(gender) => selectGender(gender, uniqueValue)} | ||
selectedGender={getSelectedGender(uniqueValue)} | ||
title={uniqueValue.toString()} | ||
/> | ||
</> | ||
))} | ||
</Box> | ||
); | ||
}; | ||
|
||
export default GenderConfig; |
112 changes: 112 additions & 0 deletions
112
src/features/import/components/ImportDialog/Configure/Configuration/GenderConfigRow.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import { ArrowForward, Delete } from '@mui/icons-material'; | ||
import { | ||
Box, | ||
FormControl, | ||
IconButton, | ||
InputLabel, | ||
MenuItem, | ||
Select, | ||
Typography, | ||
} from '@mui/material'; | ||
import { FC } from 'react'; | ||
|
||
import messageIds from 'features/import/l10n/messageIds'; | ||
import { Msg, useMessages } from 'core/i18n'; | ||
import { Gender, genders } from '../../../../hooks/useGenderMapping'; | ||
|
||
interface GenderConfigRowProps { | ||
italic?: boolean; | ||
numRows: number; | ||
onSelectGender: (gender: Gender | null) => void; | ||
onDeselectGender: () => void; | ||
selectedGender: Gender | 'unknown' | null; | ||
title: string; | ||
} | ||
|
||
const GenderConfigRow: FC<GenderConfigRowProps> = ({ | ||
italic, | ||
numRows, | ||
onSelectGender, | ||
onDeselectGender, | ||
selectedGender, | ||
title, | ||
}) => { | ||
const messages = useMessages(messageIds); | ||
return ( | ||
<Box display="flex" flexDirection="column"> | ||
<Box display="flex"> | ||
<Box | ||
alignItems="flex-start" | ||
display="flex" | ||
justifyContent="space-between" | ||
paddingTop={1} | ||
width="50%" | ||
> | ||
<Box display="flex" sx={{ wordBreak: 'break-all' }} width="100%"> | ||
<Typography fontStyle={italic ? 'italic' : ''}>{title}</Typography> | ||
</Box> | ||
<ArrowForward color="secondary" sx={{ marginRight: 1 }} /> | ||
</Box> | ||
<Box | ||
alignItems="flex-start" | ||
display="flex" | ||
paddingRight={1} | ||
width="50%" | ||
> | ||
<FormControl fullWidth size="small"> | ||
<InputLabel> | ||
<Msg id={messageIds.configuration.configure.genders.label} /> | ||
</InputLabel> | ||
<Select | ||
label={messages.configuration.configure.genders.label()} | ||
onChange={(event) => { | ||
const { value } = event.target; | ||
if (value === 'm' || value === 'f' || value === 'o') { | ||
onSelectGender(value); | ||
} else if (value === 'unknown') { | ||
onSelectGender(null); | ||
} | ||
}} | ||
value={selectedGender} | ||
> | ||
{genders.map((key) => ( | ||
<MenuItem key={key} value={key}> | ||
<Msg | ||
id={ | ||
messageIds.configuration.configure.genders.selectLabels[ | ||
key | ||
] | ||
} | ||
/> | ||
</MenuItem> | ||
))} | ||
<MenuItem value="unknown"> | ||
<Msg | ||
id={ | ||
messageIds.configuration.configure.genders.selectLabels | ||
.unknown | ||
} | ||
/> | ||
</MenuItem> | ||
</Select> | ||
</FormControl> | ||
<IconButton | ||
onClick={() => { | ||
onDeselectGender(); | ||
}} | ||
> | ||
<Delete color="secondary" /> | ||
</IconButton> | ||
</Box> | ||
</Box> | ||
<Typography color="secondary"> | ||
<Msg | ||
id={messageIds.configuration.configure.tags.numberOfRows} | ||
values={{ numRows }} | ||
/> | ||
</Typography> | ||
</Box> | ||
); | ||
}; | ||
|
||
export default GenderConfigRow; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
57 changes: 57 additions & 0 deletions
57
src/features/import/components/ImportDialog/Configure/Preview/GenderPreview.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import messageIds from 'features/import/l10n/messageIds'; | ||
import PreviewGrid from './PreviewGrid'; | ||
import { useMessages } from 'core/i18n'; | ||
import { CellData, ColumnKind, Sheet } from 'features/import/utils/types'; | ||
|
||
interface GenderPreviewProps { | ||
currentSheet: Sheet; | ||
fieldKey: string; | ||
fields: Record<string, CellData> | undefined; | ||
} | ||
|
||
const GenderPreview = ({ | ||
currentSheet, | ||
fieldKey, | ||
fields, | ||
}: GenderPreviewProps) => { | ||
const messages = useMessages(messageIds); | ||
|
||
const map = currentSheet.columns.find( | ||
(column) => column.kind === ColumnKind.GENDER && column.mapping.length > 0 | ||
); | ||
|
||
if (!map) { | ||
return ( | ||
<PreviewGrid | ||
columnHeader={messages.configuration.preview.columnHeader.gender()} | ||
rowValue={null} | ||
unmappedRow={true} | ||
/> | ||
); | ||
} | ||
|
||
const value = fields?.[fieldKey]; | ||
|
||
if (value === 'o' || value === 'f' || value === 'm' || value === null) { | ||
const key = value === null ? 'unknown' : value; | ||
return ( | ||
<PreviewGrid | ||
columnHeader={messages.configuration.preview.columnHeader.gender()} | ||
rowValue={messages.configuration.preview.genders[key]()} | ||
unmappedRow={false} | ||
/> | ||
); | ||
} | ||
|
||
// This should never happen | ||
return ( | ||
<PreviewGrid | ||
columnHeader={messages.configuration.preview.columnHeader.gender()} | ||
emptyLabel={messages.configuration.preview.noValue()} | ||
rowValue={null} | ||
unmappedRow={false} | ||
/> | ||
); | ||
}; | ||
|
||
export default GenderPreview; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { columnUpdate } from '../store'; | ||
import { useAppDispatch } from 'core/hooks'; | ||
import { CellData, Column, ColumnKind } from '../utils/types'; | ||
|
||
export const genders = ['f', 'm', 'o'] as const; | ||
export type Gender = typeof genders[keyof typeof genders]; | ||
|
||
export default function useGenderMapping(column: Column, columnIndex: number) { | ||
const dispatch = useAppDispatch(); | ||
|
||
const getSelectedGender = (value: CellData) => { | ||
if (column.kind == ColumnKind.GENDER) { | ||
const map = column.mapping.find((m) => m.value === value); | ||
if (!map) { | ||
return null; | ||
} | ||
return map.gender ?? 'unknown'; | ||
} | ||
return null; | ||
}; | ||
|
||
const selectGender = (gender: Gender | null, value: CellData) => { | ||
if (column.kind !== ColumnKind.GENDER) { | ||
return; | ||
} | ||
|
||
dispatch( | ||
columnUpdate([ | ||
columnIndex, | ||
{ | ||
...column, | ||
mapping: [ | ||
...column.mapping.filter((m) => m.value !== value), | ||
{ gender, value }, | ||
], | ||
}, | ||
]) | ||
); | ||
}; | ||
|
||
const deselectGender = (value: CellData) => { | ||
if (column.kind != ColumnKind.GENDER) { | ||
return; | ||
} | ||
|
||
const map = column.mapping.find((map) => map.value == value); | ||
if (map) { | ||
const filteredMapping = column.mapping.filter((m) => m.value != value); | ||
|
||
dispatch( | ||
columnUpdate([ | ||
columnIndex, | ||
{ | ||
...column, | ||
mapping: filteredMapping, | ||
}, | ||
]) | ||
); | ||
} | ||
}; | ||
|
||
return { | ||
deselectGender, | ||
getSelectedGender, | ||
selectGender, | ||
}; | ||
} |
Oops, something went wrong.