Skip to content

Commit

Permalink
Some commit - needs clean up
Browse files Browse the repository at this point in the history
  • Loading branch information
shivam-tripathi committed Mar 10, 2018
1 parent 876e59a commit 091d0ea
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 21 deletions.
16 changes: 12 additions & 4 deletions src/client/entity-editor/common/name-field.js
Expand Up @@ -25,7 +25,8 @@ import ValidationLabel from '../common/validation-label';

type Props = {
empty?: boolean,
error?: boolean
error?: boolean,
warn?: boolean
};

/**
Expand All @@ -35,6 +36,8 @@ type Props = {
* @param {Object} props - The properties passed to the component.
* @param {boolean} props.error - Passed to the ValidationLabel within the
* component to indicate a validation error.
* @param {boolean} props.warn - Passed to the ValidationLabel within the
* component to indicate a validation warning.
* @param {boolean} props.empty - Passed to the ValidationLabel within the
* component to indicate that the field is empty.
* @param {Function} props.onChange - Function to be called when the value in
Expand All @@ -44,10 +47,14 @@ type Props = {
function NameField({
empty,
error,
warn,
...rest
}: Props) {
const label =
<ValidationLabel empty={empty} error={error}>Name</ValidationLabel>;
const label = (
<ValidationLabel empty={empty} error={error} warn={warn}>
Name
</ValidationLabel>
);

return (
<CustomInput label={label} type="text" {...rest}/>
Expand All @@ -56,7 +63,8 @@ function NameField({
NameField.displayName = 'NameField';
NameField.defaultProps = {
empty: false,
error: false
error: false,
warn: false
};

export default NameField;
32 changes: 23 additions & 9 deletions src/client/entity-editor/common/validation-label.js
Expand Up @@ -22,7 +22,7 @@ import * as React from 'react';
import Icon from 'react-fontawesome';


function icon(empty: ?boolean, error: ?boolean): string | null {
function icon(empty: ?boolean, error: ?boolean, warn: ?boolean): string | null {
if (empty) {
return null;
}
Expand All @@ -31,11 +31,18 @@ function icon(empty: ?boolean, error: ?boolean): string | null {
return 'times';
}

if (warn) {
return 'exclamation-triangle';
}

return 'check';
}


function contextualColor(empty: ?boolean, error: ?boolean): string | null {
function contextualColor(
empty: ?boolean,
error: ?boolean,
warn: ?boolean
): string | null {
if (empty) {
return null;
}
Expand All @@ -44,13 +51,18 @@ function contextualColor(empty: ?boolean, error: ?boolean): string | null {
return 'text-danger';
}

if (warn) {
return 'text-warning';
}

return 'text-success';
}

type Props = {
children?: React.Node,
empty?: boolean,
error?: boolean
error?: boolean,
warn?: boolean
};

/**
Expand All @@ -70,13 +82,14 @@ type Props = {
function ValidationLabel({
children,
empty,
error
error,
warn
}: Props) {
const iconElement = icon(empty, error) &&
<Icon className="margin-left-0-5" name={icon(empty, error)}/>;
const iconElement = icon(empty, error, warn) &&
<Icon className="margin-left-0-5" name={icon(empty, error, warn)}/>;

return (
<span className={contextualColor(empty, error)}>
<span className={contextualColor(empty, error, warn)}>
{children}
{iconElement}
</span>
Expand All @@ -86,7 +99,8 @@ ValidationLabel.displayName = 'ValidationLabel';
ValidationLabel.defaultProps = {
children: null,
empty: false,
error: false
error: false,
warn: false
};

export default ValidationLabel;
46 changes: 46 additions & 0 deletions src/client/entity-editor/name-section/actions.js
Expand Up @@ -18,10 +18,15 @@

// @flow

// import {debounce} from 'lodash';
import request from 'superagent-bluebird-promise';


export const UPDATE_DISAMBIGUATION_FIELD = 'UPDATE_DISAMBIGUATION_FIELD';
export const UPDATE_LANGUAGE_FIELD = 'UPDATE_LANGUAGE_FIELD';
export const UPDATE_NAME_FIELD = 'UPDATE_NAME_FIELD';
export const UPDATE_SORT_NAME_FIELD = 'UPDATE_SORT_NAME_FIELD';
export const UPDATE_WARN_IF_EXISTS = 'UPDATE_WARN_IF_EXISTS';

export type Action = {
type: string,
Expand Down Expand Up @@ -95,3 +100,44 @@ export function debouncedUpdateDisambiguationField(
type: UPDATE_DISAMBIGUATION_FIELD
};
}

/*
* Return a function which dispatches action to update the name field and
* asynchronously checks if it already exists in the database, dispatching
* action to update the warning if the name exists or not.
* @param {string} newName - The new value to be used for the name.
* @param {string} entityType - The entityType of the value.
* @return {(Action) => mixed} Thunk action.
*/
export function handleNameChange(
newName: string,
entityType: string
): ((Action) => mixed) => mixed {
return (dispatch) => {
dispatch({
meta: {debounce: 'keystroke'},
payload: newName,
type: UPDATE_NAME_FIELD
});

request.get('/search/exists')
.query({
collection: entityType,
q: newName
})
.then(res => dispatch({
meta: {debounce: 'keystroke'},
payload: res.text === 'true',
type: UPDATE_WARN_IF_EXISTS
}))
.catch((error: {message: string}) => error);

/*
* Between these two actions, only the one which is dispatched later
* takes effect. Why???
* WIP: Add debounced querying => but that would cause writing of
* another function, which would take dispatch as argument. Not sure if
* it becomes anti-pattern?
*/
};
}
18 changes: 12 additions & 6 deletions src/client/entity-editor/name-section/name-section.js
Expand Up @@ -19,7 +19,7 @@
import {Col, Row} from 'react-bootstrap';
import {
debouncedUpdateDisambiguationField, debouncedUpdateNameField,
debouncedUpdateSortNameField, updateLanguageField
debouncedUpdateSortNameField, handleNameChange, updateLanguageField
} from './actions';
import {
validateNameSectionLanguage, validateNameSectionName,
Expand Down Expand Up @@ -75,13 +75,16 @@ function NameSection({
onLanguageChange,
onNameChange,
onSortNameChange,
onDisambiguationChange
onDisambiguationChange,
warnIfExists
}) {
const languageOptionsForDisplay = languageOptions.map((language) => ({
label: language.name,
value: language.id
}));

// console.log('Namesection', nameValue, warnIfExists);

return (
<div>
<h2>{`What is the ${_.capitalize(entityType)} called?`}</h2>
Expand All @@ -94,6 +97,7 @@ function NameSection({
nameValue, sortNameValue, languageValue
)}
error={!validateNameSectionName(nameValue)}
warn={warnIfExists}
onChange={onNameChange}
/>
</Col>
Expand Down Expand Up @@ -152,7 +156,8 @@ NameSection.propTypes = {
onLanguageChange: PropTypes.func.isRequired,
onNameChange: PropTypes.func.isRequired,
onSortNameChange: PropTypes.func.isRequired,
sortNameValue: PropTypes.string.isRequired
sortNameValue: PropTypes.string.isRequired,
warnIfExists: PropTypes.bool.isRequired
};
NameSection.defaultProps = {
disambiguationDefaultValue: null,
Expand All @@ -168,18 +173,19 @@ function mapStateToProps(rootState) {
rootState.getIn(['buttonBar', 'disambiguationVisible']),
languageValue: state.get('language'),
nameValue: state.get('name'),
sortNameValue: state.get('sortName')
sortNameValue: state.get('sortName'),
warnIfExists: state.get('warnIfExists')
};
}

function mapDispatchToProps(dispatch) {
function mapDispatchToProps(dispatch, {entityType}) {
return {
onDisambiguationChange: (event) =>
dispatch(debouncedUpdateDisambiguationField(event.target.value)),
onLanguageChange: (value) =>
dispatch(updateLanguageField(value && value.value)),
onNameChange: (event) =>
dispatch(debouncedUpdateNameField(event.target.value)),
dispatch(handleNameChange(event.target.value, entityType)),
onSortNameChange: (event) =>
dispatch(debouncedUpdateSortNameField(event.target.value))
};
Expand Down
9 changes: 7 additions & 2 deletions src/client/entity-editor/name-section/reducer.js
Expand Up @@ -18,7 +18,7 @@

import {
UPDATE_DISAMBIGUATION_FIELD, UPDATE_LANGUAGE_FIELD, UPDATE_NAME_FIELD,
UPDATE_SORT_NAME_FIELD
UPDATE_SORT_NAME_FIELD, UPDATE_WARN_IF_EXISTS
} from './actions';
import Immutable from 'immutable';

Expand All @@ -28,11 +28,14 @@ function reducer(
disambiguation: '',
language: null,
name: '',
sortName: ''
sortName: '',
warnIfExists: false
}),
action
) {
// console.log(action);
const {payload, type} = action;
// console.log('reducer', payload, type);
switch (type) {
case UPDATE_NAME_FIELD:
return state.set('name', payload);
Expand All @@ -42,6 +45,8 @@ function reducer(
return state.set('language', payload);
case UPDATE_DISAMBIGUATION_FIELD:
return state.set('disambiguation', payload);
case UPDATE_WARN_IF_EXISTS:
return state.set('warnIfExists', payload);
// no default
}
return state;
Expand Down
25 changes: 25 additions & 0 deletions src/server/helpers/search.js
Expand Up @@ -339,6 +339,31 @@ export async function generateIndex(orm) {
await refreshIndex();
}

export function checkIfExists(orm, name, collection) {
const {bookshelf} = orm;
const rawSql = `
SELECT (
CASE WHEN count > 0 THEN 'true' ELSE 'false' END
) FROM (
SELECT COUNT(*)
AS count
FROM ${collection}
JOIN (
SELECT DISTINCT set_id
FROM alias_set__alias
WHERE alias_id IN (
SELECT id
FROM alias
WHERE name = '${name}'
)
) AS sets
ON sets.set_id = ${collection}.alias_set_id
WHERE master = 'true'
) AS result;`;

return bookshelf.knex.raw(rawSql).then(val => val.rows[0].case);
}

export function searchByName(orm, name, collection) {
const dslQuery = {
body: {
Expand Down
13 changes: 13 additions & 0 deletions src/server/routes/search.js
Expand Up @@ -93,6 +93,19 @@ router.get('/autocomplete', (req, res) => {
handler.sendPromiseResult(res, searchPromise);
});

/**
* Responds with json object containing boolean field which signifies if the
* given user query already exists
*/
router.get('/exists', (req, res) => {
const {orm} = req.app.locals;
const {q, collection} = req.query;

const searchPromise = search.checkIfExists(orm, q, collection);

handler.sendPromiseResult(res, searchPromise);
});

/**
* Regenerates search index. Restricted to administrators.
*
Expand Down

0 comments on commit 091d0ea

Please sign in to comment.