From b4c186e1ffd5c8f34ca37c97eafafb8ed00f3b11 Mon Sep 17 00:00:00 2001 From: Torrie Fischer Date: Tue, 11 Sep 2018 23:43:42 -0700 Subject: [PATCH] components: importdialog: load available headers from server --- assets/js/components/ImportDialog.js | 57 ++++++++++++++++++---------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/assets/js/components/ImportDialog.js b/assets/js/components/ImportDialog.js index 19d1040b..293a29fe 100644 --- a/assets/js/components/ImportDialog.js +++ b/assets/js/components/ImportDialog.js @@ -22,15 +22,20 @@ import SheetClip from 'sheetclip' const sheetclip = new SheetClip() -const columnNames = { - name: ['name', 'full name'], - email: ['email', 'e-mail', 'email address'] +function fetchValidFields() { + return fetch('/api/people/', {method: 'OPTIONS'}) + .then(resp => resp.json()) + .then(resp => Object.entries(resp.actions.POST) + .filter(([_fieldName, fieldProps]) => fieldProps.read_only == false) + .map(([fieldName, fieldProps]) => [fieldName, [fieldName, ...fieldProps.aliases]]) + .reduce((prev, next) => ({...prev, [next[0]]: next[1]}), {}) + ) } -function guessHeaderMap(columns) { +function guessHeaderMap(columns, knownColumns) { return _.fromPairs(_.map(columns, c => { const normalized = c.toLowerCase() - const commonName = _.findKey(columnNames, alternatives => alternatives.indexOf(normalized) >= 0) + const commonName = _.findKey(knownColumns, alternatives => alternatives.indexOf(normalized) >= 0) if (commonName) { return [c, commonName] } else { @@ -45,7 +50,7 @@ function mapRows(rows, headerMap, tags) { return _.map(compactRows, row => { - const mappedObject = _.mapKeys(row, (_v, k) => _.get(headerMap, k, k)) + const mappedObject = _.pickBy(_.mapKeys(row, (_v, k) => _.get(headerMap, k, k)), v => v != undefined) return { ...mappedObject, @@ -56,17 +61,17 @@ function mapRows(rows, headerMap, tags) { } -function parsePaste({input}) { +function parsePaste({input}, knownHeaders) { const sheet = sheetclip.parse(input) const headers = _.head(sheet) - const rows = _.tail(sheet) + const rows = _.tail(sheet).map(row => row.map(column => column == '' ? undefined : column)) const tagKeys = _.filter(headers, key => key.startsWith('tag_')) const tags = _.map(tagKeys, key => { return key.substr(4) }) - const headerMap = guessHeaderMap(_.difference(headers, tagKeys)) + const headerMap = guessHeaderMap(_.difference(headers, tagKeys), knownHeaders) const jsonRows = _.map(rows, row => _.zipObject(headers, row)) @@ -83,12 +88,18 @@ class ImportDialog extends React.Component { this.state = { stage: 0, parsed: {headerMap: {}}, - mapped: [] + mapped: [], + validFields: {} } this.next = this.next.bind(this) this.previous = this.previous.bind(this) } + componentDidMount() { + fetchValidFields() + .then(fields => this.setState({validFields: fields})) + } + reset() { this.setState({stage: 0}) } @@ -96,7 +107,7 @@ class ImportDialog extends React.Component { next() { switch(this.state.stage) { case 0: - this.setState({parsed: parsePaste(this.pasteForm.formContext.formState.values), stage: 1}) + this.setState({parsed: parsePaste(this.pasteForm.formContext.formState.values, this.state.validFields), stage: 1}) break case 1: this.setState({stage: 2, mapped: mapRows(this.state.parsed.rows, this.state.parsed.headerMap, this.state.parsed.tags)}) @@ -131,11 +142,13 @@ class ImportDialog extends React.Component { What headers can I use?

All headers are case insensitive. The following lists acceptable values:

- + + + {Object.entries(this.state.validFields).map(([k, v]) => ( + + ))} + +
ColumnAliases
{k}{v.join(', ')}
TagsTo tag people, include a column that starts with "tag:". eg "tag:Came to meeting" tags each person in the spreadsheet with "Came to meeting"
) @@ -154,7 +167,7 @@ class ImportDialog extends React.Component { value={dst} onChange={evt => this.setState({parsed: {...this.state.parsed, headerMap: {...this.state.parsed.headerMap, [src]: evt.target.value}}})}> None - {_.map(_.keys(columnNames), name => {name})} + {_.map(_.keys(this.state.validFields), name => {name})} {_.join(samples[src], ', ')} @@ -184,12 +197,14 @@ class ImportDialog extends React.Component {

The following data will be imported:

- + + {Object.keys(this.state.validFields).map(header => )} + - {_.map(this.state.mapped, (row, idx) => ( - - + {this.state.mapped.map((row, idx) => ( + + {Object.keys(this.state.validFields).map(header => )} ))}
NameE-mail
{header}
{row.name} {_.map(row.tags, t => )}{row.email}
{header == 'tags' ? row[header].map(tag => ) : row[header]}