Commit 7c2d1de1 authored by Valeriy Panov's avatar Valeriy Panov Committed by Matija Obreza
Browse files

datasets basic info page + steps

go to next step when click button

removed material libs from dev dependencies

- create new development branch
- fix version lib for material-ui

file and csv text

Material Design Dataset Creator

autosubmit onBlur

changed files location

step 2 in dataset structure

router for locality

traits with mock

timing and location step

autocomplete check if input value doesn't match suggestion then clear input

added third step

- test

- small fix for build

button next

basic info functionality

create new creator

basic info functionality

check file types functionality

update delete creators

timing and location functionality

google map init fix

button next step

button next step

button next step

button next step

dataset version change on file upload or delete, fix date and react prop-types

descriptor list form added new fields

'timing and locality' fields set max length = 255

step 8

typescript hold 2.4.2 version because of latest version bug.

added google map to the location section (step 8)

data in preparation

data publisher with styles

libs

Grid for the dataset creator steps components

git ignore

rebase + fixed files
parent 482cd9b6
/target/
.awesome-typescript-loader-cache/
node_modules
.idea
*.iml
\ No newline at end of file
import {push} from 'react-router-redux';
import {SubmissionError} from 'redux-form';
import {Creator} from '../model/creator.model';
import {CreatorService} from '../service/CreatorService';
import {RECEIVE_CREATOR, ADD_CREATOR_ID_TO_DATASET, REMOVE_CREATOR_FROM_DATASET, UPDATE_DATASET_CREATOR} from '../constants/datasets';
function createCreatorRequest(uuid: string) {
return (dispatch, getState) => {
const token = getState().login.access_token;
return CreatorService.createCreator(token, uuid)
.then((obj) => {
dispatch(receiveCreator(obj));
dispatch(addCreatorToDataset(obj, uuid));
}).catch((error) => {
console.log('Create creator error', error);
});
};
}
function updateCreatorRequest(uuid: string, creator: Creator) {
return (dispatch, getState) => {
const token = getState().login.access_token;
return CreatorService.updateCreator(token, uuid, creator)
.then((obj) => {
dispatch(updateCreator(obj));
}).catch((error) => {
console.log('Create creator error', error);
});
};
}
function deleteCreatorRequest(uuid: string, creator: Creator) {
return (dispatch, getState) => {
const token = getState().login.access_token;
return CreatorService.deleteCreator(token, uuid, creator)
.then((obj) => {
dispatch(removeCreator(obj, uuid));
}).catch((error) => {
console.log('Create creator error', error);
});
};
}
function receiveCreator(creator: Creator) {
return {
type: RECEIVE_CREATOR,
payload : {
creator,
},
};
}
function addCreatorToDataset(creator: Creator, uuid: string) {
return {
type: ADD_CREATOR_ID_TO_DATASET,
payload : {
creator,
datasetUUID: uuid,
},
};
}
function removeCreator(creator: Creator, uuid: string) {
return {
type: REMOVE_CREATOR_FROM_DATASET,
payload : {
creator,
datasetUUID: uuid,
},
};
}
function updateCreator(creator: Creator) {
return {
type: UPDATE_DATASET_CREATOR,
payload : {
creator,
},
};
}
export { createCreatorRequest, deleteCreatorRequest, receiveCreator, updateCreatorRequest };
import {push} from 'react-router-redux';
import {SubmissionError} from 'redux-form';
import {Dataset} from '../model/dataset.model';
import {Creator} from '../model/creator.model';
import {DatasetService} from '../service/DatasetService';
import {RECEIVE_DATASET, RECEIVE_CREATOR} from '../constants/datasets';
import {receiveRepositoryFile} from './repositoryFile';
import {receiveCreator} from './creators';
import {receiveLocation} from './location';
import {RepositoryFile} from '../model/repositoryFile.model';
import {Location} from '../model/location.model';
function handleDataset(dispatch, dataset: Dataset) {
const repositoryFiles: RepositoryFile[] = [ ...dataset.repositoryFiles ];
const locations: Location[] = [ ...dataset.locations ];
const creators: Creator[] = [...dataset.creators];
const normalized = new Dataset({
...dataset,
repositoryFiles: repositoryFiles.map((file) => file.uuid),
locations: locations.map((location) => location.uuid),
creators: creators.map((creator) => creator.uuid),
});
dispatch(receiveDataset(normalized));
repositoryFiles.forEach((file) => dispatch(receiveRepositoryFile(file)));
locations.forEach((location) => dispatch(receiveLocation(location)));
creators.forEach((creator) => dispatch(receiveCreator(creator)));
}
function listDatasetsRequest() {
return (dispatch, getState) => {
const token = getState().login.access_token;
return DatasetService.listDatasets(token)
.then((paged) => {
paged.content.forEach((d) => dispatch(receiveDataset(d)));
})
.catch((error) => {
console.log('Error', error);
});
};
}
function getDatasetRequest(uuid: string) {
return (dispatch, getState) => {
const token = getState().login.access_token;
return DatasetService.getDataset(token, uuid)
.then((dataset) => {
handleDataset(dispatch, dataset);
})
.catch((error) => {
console.log('Error', error);
});
};
}
function createDatasetRequest() {
const dataset = new Dataset({title: 'New Dataset', description: 'Description here'});
return (dispatch, getState) => {
const token = getState().login.access_token;
return DatasetService.saveDataset(token, dataset)
.then((saved) => {
dispatch(receiveDataset(saved));
dispatch(showDataset(saved.uuid));
}).catch((error) => {
console.log('Save error', error);
});
};
}
function saveDatasetRequest(dataset: Dataset) {
// remove normalized data here
const data = new Dataset({
...dataset,
repositoryFiles: [],
creators: [],
locations: [],
});
return (dispatch, getState) => {
const token = getState().login.access_token;
return DatasetService.saveDataset(token, data)
.then((saved) => {
handleDataset(dispatch, saved);
}).catch((error) => {
console.log('Save error', error);
});
};
}
function receiveDataset(dataset: Dataset) {
return {
type: RECEIVE_DATASET,
payload: {
dataset,
},
};
}
function showDataset(uuid: string) {
return (dispatch) => {
dispatch(push(`/datasets/${uuid}`));
};
}
export {listDatasetsRequest, getDatasetRequest, createDatasetRequest, saveDatasetRequest, receiveDataset, showDataset, handleDataset};
import {Location} from '../model/location.model';
import {LocationService} from '../service/LocationService';
import {ADD_LOCATION, RECEIVE_LOCATION, REMOVE_LOCATION} from '../constants/locations';
function createLocationRequest(datasetUUID: string) {
const location = new Location({});
return (dispatch, getState) => {
const token = getState().login.access_token;
return LocationService.saveLocation(token, datasetUUID, location)
.then((saved) => {
dispatch(addLocation(datasetUUID, saved));
}).catch((error) => {
console.log('Save error', error);
});
};
}
function saveLocationRequest(datasetUUID: string, location: Location) {
return (dispatch, getState) => {
const token = getState().login.access_token;
return LocationService.saveLocation(token, datasetUUID, location)
.then((saved) => {
dispatch(addLocation(datasetUUID, saved));
}).catch((error) => {
console.log('Save error', error);
});
};
}
function deleteLocationRequest(datasetUUID: string, location: Location) {
return (dispatch, getState) => {
const token = getState().login.access_token;
return LocationService.deleteLocation(token, datasetUUID, location)
.then((saved) => {
dispatch(deleteLocation(datasetUUID, saved));
}).catch((error) => {
console.log('Delete error', error);
});
};
}
function receiveLocation(location: Location) {
return {
type: RECEIVE_LOCATION,
payload: {
location,
},
};
}
function addLocation(datasetUUID: string, location: Location) {
return {
type: ADD_LOCATION,
payload: {
datasetUUID,
location,
},
};
}
function deleteLocation(datasetUUID: string, location: Location) {
return {
type: REMOVE_LOCATION,
payload: {
datasetUUID,
location,
},
};
}
export {createLocationRequest, saveLocationRequest, deleteLocationRequest, receiveLocation};
import {RepositoryFile} from '../model/repositoryFile.model';
import {RepositoryFileService} from '../service/RepositoryFileService';
import {RECEIVE_REPOSITORY_FILE, REMOVE_REPOSITORY_FILE, ADD_REPOSITORY_FILE} from '../constants/repositoryFile';
import {handleDataset} from './dataset';
function uploadRepositoryFileRequest(datasetUUID: string, file: File) {
return (dispatch, getState) => {
const token = getState().login.access_token;
return RepositoryFileService.saveRepositoryFile(token, datasetUUID, file)
.then((dataset) => {
handleDataset(dispatch, dataset);
// dispatch(addRepositoryFile(datasetUUID, saved));
}).catch((error) => {
console.log('Save error', error);
});
};
}
function deleteRepositoryFileRequest(datasetUUID: string, uuid: string) {
return (dispatch, getState) => {
const token = getState().login.access_token;
return RepositoryFileService.deleteRepositoryFile(token, datasetUUID, uuid)
.then((dataset) => {
handleDataset(dispatch, dataset);
// dispatch(deleteRepositoryFile(datasetUUID, saved));
}).catch((error) => {
console.log('Delete error', error);
});
};
}
function receiveRepositoryFile(repositoryFile: RepositoryFile) {
return {
type: RECEIVE_REPOSITORY_FILE,
payload: {
repositoryFile,
},
};
}
function addRepositoryFile(datasetUUID: string, repositoryFile: RepositoryFile) {
return {
type: ADD_REPOSITORY_FILE,
payload: {
datasetUUID,
repositoryFile,
},
};
}
function deleteRepositoryFile(datasetUUID: string, repositoryFile: RepositoryFile) {
return {
type: REMOVE_REPOSITORY_FILE,
payload: {
datasetUUID,
repositoryFile,
},
};
}
export {uploadRepositoryFileRequest, receiveRepositoryFile, deleteRepositoryFileRequest, deleteRepositoryFile};
import * as React from 'react';
import {withGoogleMap, GoogleMap, Marker} from 'react-google-maps';
const Map = ({onMapLoad, onMapClick, onMarkerRightClick, onMouseOut, position, marker}) => (
<GoogleMap
ref={ onMapLoad }
onClick={ onMapClick }
onMouseOut={ onMouseOut }
center={ position }
zoom={ 5 }
>
{ marker && <Marker { ...marker } onRightClick={ onMarkerRightClick }/> }
</GoogleMap>
);
export default withGoogleMap(Map);
import * as React from 'react';
import * as PropTypes from 'prop-types';
import {Field} from 'redux-form';
import * as Autosuggest from 'react-autosuggest';
import * as match from 'autosuggest-highlight/match';
import * as parse from 'autosuggest-highlight/parse';
import TextField from 'material-ui/TextField';
import Paper from 'material-ui/Paper';
import {MenuItem} from 'material-ui/Menu';
import {withStyles, createStyleSheet} from 'material-ui/styles';
const styleSheet = createStyleSheet('MaterialAutosuggest', (theme) => ({
container: {
flexGrow: 1,
position: 'relative',
},
suggestionsContainerOpen: {
position: 'absolute',
marginTop: theme.spacing.unit,
marginBottom: theme.spacing.unit * 3,
left: 0,
right: 0,
zIndex: 1,
},
suggestion: {
display: 'block',
},
suggestionsList: {
margin: 0,
padding: 0,
listStyleType: 'none',
},
textField: {
width: '100%',
},
}));
interface IMaterialAutosuggestProps extends React.ClassAttributes<any> {
// array of suggestion objects
suggestions: [any];
// name of the field from suggestion object for searching
suggestionLabel: string;
// styles
classes: any;
// comes from redux form
input: any;
meta: any;
}
class MaterialAutosuggest extends React.Component<IMaterialAutosuggestProps, any> {
public constructor(props: any) {
super(props);
this.state = {
suggestions: [],
};
}
private static propTypes = {
suggestionLabel: PropTypes.string.isRequired,
suggestions: PropTypes.array.isRequired,
};
protected handleSuggestionsFetchRequested = ({value}) => {
this.setState({
suggestions: this.getSuggestions(value),
});
}
protected handleSuggestionsClearRequested = () => {
this.setState({
suggestions: [],
});
}
protected getSuggestions = (value) => {
const inputValue = value.trim().toLowerCase();
const inputLength = inputValue.length;
const label = this.props.suggestionLabel;
return inputLength === 0 ? [] : this.props.suggestions.filter((suggestion) =>
suggestion[label].toLowerCase().slice(0, inputLength) === inputValue,
);
}
protected renderInput = (inputProps) => {
const {classes, ref, value, label, autoComplete, ...other} = inputProps;
return (
<TextField
className={ classes.textField }
value={ value }
label={ label }
inputRef={ ref }
InputProps={ {
classes: {
input: classes.input,
},
...other,
} }
/>
);
}
protected renderSuggestion = (suggestion, {query, isHighlighted}) => {
const {suggestionLabel} = this.props;
const matches = match(suggestion[suggestionLabel], query);
const parts = parse(suggestion[suggestionLabel], matches);
return (
<MenuItem selected={ isHighlighted } component="div">
<div>
{ parts.map((part, index) => {
return part.highlight
? (
<span key={ index } style={ {fontWeight: 300} }>
{ part.text }
</span>
)
:
(
<strong key={ index } style={ {fontWeight: 500} }>
{ part.text }
</strong>
);
}) }
</div>
</MenuItem>
);
}
protected renderSuggestionsContainer = (options) => {
const {containerProps, children} = options;
return (
<Paper { ...containerProps } square>
{ children }
</Paper>
);
}
public render() {
const {suggestions, suggestionLabel, input, classes, meta: {touched, error}, ...other} = this.props;
const getSuggestionValue = (suggestion) => {
input.onChange.call(this, suggestion[suggestionLabel]);
return suggestion[suggestionLabel];
};
const checkIfMatch = (e) => {
if (suggestions.find((el) => el[suggestionLabel] === e.target.value) === undefined) {
input.onChange.call(this, '');
} else {
input.onBlur.call(this, e);
}
};
return (
<Autosuggest
theme={ {
container: classes.container,
suggestionsContainerOpen: classes.suggestionsContainerOpen,
suggestionsList: classes.suggestionsList,
suggestion: classes.suggestion,
} }
renderInputComponent={ this.renderInput }
suggestions={ this.state.suggestions }
onSuggestionsFetchRequested={ this.handleSuggestionsFetchRequested }
onSuggestionsClearRequested={ this.handleSuggestionsClearRequested }
renderSuggestionsContainer={ this.renderSuggestionsContainer }
getSuggestionValue={ getSuggestionValue }
renderSuggestion={ this.renderSuggestion }
inputProps={ {
classes,
...input,
...other,
onBlur: checkIfMatch,
} }
/>
);
}
}
export default withStyles(styleSheet)(MaterialAutosuggest);
import * as React from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import Grid from 'material-ui/Grid';
import Container from '../../common/container/index';
import TopDataPublisherSection from './top-section';
import MainDataPublisherSection from './main-section';
import {withStyles, createStyleSheet} from 'material-ui/styles';
import {createDatasetRequest} from '../../../actions/dataset';
interface IDatasetsProps extends React.ClassAttributes<any> {
classes: any;
router: any;
createDatasetRequest: () => void;
}
const styleSheet = createStyleSheet('DatasetsProps', (theme) => ({
root: {
margin: '0px',
},
}));
class DataPublisher extends React.Component<IDatasetsProps, any> {
public render() {
const {classes} = this.props;
return (
<Grid container size={ 12 } gutter= { 0 } className={ classes.root }>
<TopDataPublisherSection/>
<MainDataPublisherSection/>
</Grid>
);
}
}
const mapDispatchToProps = (dispatch) => bindActionCreators({
createDatasetReques