Added Vegetables <-> Properties

This commit is contained in:
dbroqua 2018-10-06 17:22:20 +02:00
parent d8fac4b708
commit a7aa0cb01a
8 changed files with 357 additions and 40 deletions

37
package-lock.json generated
View file

@ -1710,6 +1710,11 @@
"lazy-cache": "^1.0.3" "lazy-cache": "^1.0.3"
} }
}, },
"chain-function": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/chain-function/-/chain-function-1.0.1.tgz",
"integrity": "sha512-SxltgMwL9uCko5/ZCLiyG2B7R9fY4pDZUw7hJ4MhirdjBLosoDqkWABi3XMucddHdLiFJMb7PD2MZifZriuMTg=="
},
"chalk": { "chalk": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
@ -8755,6 +8760,38 @@
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
}, },
"react-notifications": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/react-notifications/-/react-notifications-1.4.3.tgz",
"integrity": "sha1-cGDTOYlvElpbGD680EUmmApDMiI=",
"requires": {
"classnames": "^2.1.1",
"prop-types": "^15.5.10",
"react-transition-group": "^1.2.0"
},
"dependencies": {
"react-transition-group": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-1.2.1.tgz",
"integrity": "sha512-CWaL3laCmgAFdxdKbhhps+c0HRGF4c+hdM4H23+FI1QBNUyx/AMeIJGWorehPNSaKnQNOAxL7PQmqMu78CDj3Q==",
"requires": {
"chain-function": "^1.0.0",
"dom-helpers": "^3.2.0",
"loose-envify": "^1.3.1",
"prop-types": "^15.5.6",
"warning": "^3.0.0"
}
},
"warning": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
"integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
"requires": {
"loose-envify": "^1.0.0"
}
}
}
},
"react-popper": { "react-popper": {
"version": "0.10.4", "version": "0.10.4",
"resolved": "https://registry.npmjs.org/react-popper/-/react-popper-0.10.4.tgz", "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-0.10.4.tgz",

View file

@ -10,6 +10,7 @@
"react-cookie": "^3.0.4", "react-cookie": "^3.0.4",
"react-dom": "^16.5.0", "react-dom": "^16.5.0",
"react-icons": "^3.0.5", "react-icons": "^3.0.5",
"react-notifications": "^1.4.3",
"react-router-dom": "^4.3.1", "react-router-dom": "^4.3.1",
"react-scripts": "1.1.5", "react-scripts": "1.1.5",
"reactstrap": "^6.4.0" "reactstrap": "^6.4.0"

View file

@ -1,4 +1,6 @@
import React, { Component } from 'react'; import React, {
Component
} from 'react';
import { import {
Container, Container,
Table, Table,
@ -7,12 +9,23 @@ import {
Button, Button,
Input, Input,
} from 'reactstrap'; } from 'reactstrap';
import { FaTrashAlt, FaPlus } from 'react-icons/fa'; import {
import { Link } from 'react-router-dom'; FaTrashAlt,
FaPlus
} from 'react-icons/fa';
import {
Link
} from 'react-router-dom';
import {
NotificationContainer,
NotificationManager
} from 'react-notifications';
import Navigation from './Navigation'; import Navigation from './Navigation';
import Header from './Header'; import Header from './Header';
import API from './Api'; import API from './Api';
import 'react-notifications/lib/notifications.css';
class Categories extends Component { class Categories extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
@ -44,13 +57,17 @@ class Categories extends Component {
API.get('types') API.get('types')
.then((res) => { .then((res) => {
if (res.status === 200) { if (res.status === 200) {
this.setState({ Categories: res.data.rows }); this.setState({
Categories: res.data.rows
});
} else { } else {
this.setState({ Categories: [] }); this.setState({
Categories: []
});
} }
}) })
.catch((e) => { .catch((e) => {
alert('Erreur lors de la récupération des catégories'); NotificationManager.error('Erreur lors de la récupération des catégories');
}); });
} }
@ -58,13 +75,16 @@ class Categories extends Component {
event.preventDefault(); // Let's stop this event. event.preventDefault(); // Let's stop this event.
event.stopPropagation(); // Really this time. event.stopPropagation(); // Really this time.
API.post('types', { API.post('types', {
name: this.state.name, name: this.state.name,
}) })
.then((res) => { .then((res) => {
this.setState(prevState => ({ Categories: [...prevState.Categories, res.data], name: '' })); this.setState(prevState => ({
Categories: [...prevState.Categories, res.data],
name: ''
}));
}) })
.catch(() => { .catch(() => {
alert('Impossile de créer cette catégorie'); NotificationManager.error('Impossile de créer cette catégorie');
}); });
} }
@ -76,13 +96,14 @@ class Categories extends Component {
}); });
}) })
.catch((e) => { .catch((e) => {
alert('Erreur lors de la suppression de cette catégorie'); NotificationManager.error('Erreur lors de la suppression de cette catégorie');
}); });
} }
render() { render() {
return ( return (
<div> <div>
<NotificationContainer/>
<Header /> <Header />
<Container> <Container>
<Navigation /> <Navigation />

View file

@ -1,4 +1,6 @@
import React, { Component } from 'react'; import React, {
Component
} from 'react';
import { import {
Container, Container,
Table, Table,
@ -7,12 +9,24 @@ import {
Button, Button,
Input, Input,
} from 'reactstrap'; } from 'reactstrap';
import { FaTrashAlt, FaPlus, FaEdit } from 'react-icons/fa'; import {
import { Link } from 'react-router-dom'; FaTrashAlt,
FaPlus,
FaEdit
} from 'react-icons/fa';
import {
Link
} from 'react-router-dom';
import {
NotificationContainer,
NotificationManager
} from 'react-notifications';
import Navigation from './Navigation'; import Navigation from './Navigation';
import Header from './Header'; import Header from './Header';
import API from './Api'; import API from './Api';
import 'react-notifications/lib/notifications.css';
class Category extends Component { class Category extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
@ -48,13 +62,15 @@ class Category extends Component {
event.preventDefault(); // Let's stop this event. event.preventDefault(); // Let's stop this event.
event.stopPropagation(); // Really this time. event.stopPropagation(); // Really this time.
API.patch(`types/${this.state.Category.id}`, { API.patch(`types/${this.state.Category.id}`, {
name: this.state.categoryName, name: this.state.categoryName,
}) })
.then((res) => { .then((res) => {
this.setState({ Category: res.data }); this.setState({
Category: res.data
});
}) })
.catch(() => { .catch(() => {
alert('Impossile de mettre à jour cette catégorie'); NotificationManager.error('Impossile de mettre à jour cette catégorie');
}); });
} }
@ -66,13 +82,18 @@ class Category extends Component {
res.data.Vegetables = []; res.data.Vegetables = [];
} }
this.setState({ Category: res.data, categoryName: res.data.name }); this.setState({
Category: res.data,
categoryName: res.data.name
});
} else { } else {
this.setState({ Category: {} }); this.setState({
Category: {}
});
} }
}) })
.catch((e) => { .catch((e) => {
alert('Erreur lors de la récupération des catégories'); NotificationManager.error('Erreur lors de la récupération des catégories');
}); });
} }
@ -80,19 +101,21 @@ class Category extends Component {
event.preventDefault(); // Let's stop this event. event.preventDefault(); // Let's stop this event.
event.stopPropagation(); // Really this time. event.stopPropagation(); // Really this time.
API.post(`types/${this.state.Category.id}/vegetables/`, { API.post(`types/${this.state.Category.id}/vegetables/`, {
name: this.state.name, name: this.state.name,
lat: 0, lat: 0,
lng: 0, lng: 0,
description: '', description: '',
}) })
.then((res) => { .then((res) => {
const Category = this.state.Category; const Category = this.state.Category;
Category.Vegetables.push(res.data); Category.Vegetables.push(res.data);
this.setState({ Category }); this.setState({
Category
});
}) })
.catch(() => { .catch(() => {
alert('Impossile de créer cette catégorie'); NotificationManager.error('Impossile de créer cette catégorie');
}); });
} }
@ -103,16 +126,19 @@ class Category extends Component {
Category.Vegetables.splice(key, 1); Category.Vegetables.splice(key, 1);
this.setState({ Category }); this.setState({
Category
});
}) })
.catch((e) => { .catch((e) => {
alert('Erreur lors de la suppression de ce végétal'); NotificationManager.error('Erreur lors de la suppression de ce végétal');
}); });
} }
render() { render() {
return ( return (
<div> <div>
<NotificationContainer/>
<Header /> <Header />
<Container> <Container>
<Navigation categoryId={this.state.categoryId} categoryName={this.state.Category.name} /> <Navigation categoryId={this.state.categoryId} categoryName={this.state.Category.name} />

View file

@ -13,10 +13,16 @@ import {
FaTrashAlt, FaTrashAlt,
FaPlus FaPlus
} from 'react-icons/fa'; } from 'react-icons/fa';
import {
NotificationContainer,
NotificationManager
} from 'react-notifications';
import Navigation from './Navigation'; import Navigation from './Navigation';
import Header from './Header'; import Header from './Header';
import API from './Api'; import API from './Api';
import 'react-notifications/lib/notifications.css';
class Properties extends Component { class Properties extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
@ -58,7 +64,7 @@ class Properties extends Component {
} }
}) })
.catch((e) => { .catch((e) => {
alert('Erreur lors de la récupération des types de propriétés'); NotificationManager.error('Erreur lors de la récupération des types de propriétés');
}); });
} }
@ -75,7 +81,7 @@ class Properties extends Component {
})); }));
}) })
.catch(() => { .catch(() => {
alert('Impossile de créer ce type de propriété'); NotificationManager.error('Impossile de créer ce type de propriété');
}); });
} }
@ -87,13 +93,14 @@ class Properties extends Component {
}); });
}) })
.catch((e) => { .catch((e) => {
alert('Erreur lors de la suppression de ce type de propriété'); NotificationManager.error('Erreur lors de la suppression de ce type de propriété');
}); });
} }
render() { render() {
return ( return (
<div> <div>
<NotificationContainer/>
<Header /> <Header />
<Container> <Container>
<Navigation root="Property" /> <Navigation root="Property" />

View file

@ -15,6 +15,10 @@ import classnames from 'classnames';
import { import {
FaEdit, FaEdit,
} from 'react-icons/fa'; } from 'react-icons/fa';
import {
NotificationContainer,
NotificationManager
} from 'react-notifications';
import Navigation from './Navigation'; import Navigation from './Navigation';
import Header from './Header'; import Header from './Header';
import API from './Api'; import API from './Api';
@ -23,6 +27,7 @@ import VegetableCarousel from './Vegetable/Carousel'
import VegetableMap from './Vegetable/Map' import VegetableMap from './Vegetable/Map'
import './Vegetable.css'; import './Vegetable.css';
import 'react-notifications/lib/notifications.css';
class Vegetable extends Component { class Vegetable extends Component {
constructor(props) { constructor(props) {
@ -100,27 +105,66 @@ class Vegetable extends Component {
} }
updateVegetable(event) { updateVegetable(event) {
NotificationManager.info('Sauvegarde en cours');
event.preventDefault(); // Let's stop this event. event.preventDefault(); // Let's stop this event.
event.stopPropagation(); // Really this time. event.stopPropagation(); // Really this time.
const requests = 1 + this.state.Vegetable.Properties.length;
let requestsDone = 0;
let isDone = () => {
if (requests === requestsDone) {
NotificationManager.success('Sauvegarde terminée');
this.getVegetable(this.props.match.params.categoryId, this.props.match.params.vegetableId);
}
}
// Patch Vegetable
const fd = new FormData(); const fd = new FormData();
if (this.state.imagePreviewUrl) { if (this.state.imagePreviewUrl) {
fd.append('mainPicture', this.state.Vegetable.mainPictureImported); fd.append('mainPicture', this.state.Vegetable.mainPictureImported);
} }
Object.keys(this.state.Vegetable).map((objectKey) => { Object.keys(this.state.Vegetable).map((objectKey) => {
if (objectKey !== 'mainPicture' && objectKey !== 'mainPictureImported') { // if (objectKey === 'Properties') {
// fd.append(objectKey, JSON.stringify(this.state.Vegetable[objectKey]));
// } else
if (objectKey !== 'mainPicture' && objectKey !== 'mainPictureImported' && objectKey !== 'Properties') {
fd.append(objectKey, this.state.Vegetable[objectKey]); fd.append(objectKey, this.state.Vegetable[objectKey]);
} }
return true; return true;
}); });
API.patch(`types/${this.state.Vegetable.Type.id}/vegetables/${this.state.Vegetable.id}`, fd) API.patch(`types/${this.state.Vegetable.Type.id}/vegetables/${this.state.Vegetable.id}`, fd)
.then((res) => { .then((res) => {
// this.setState({ Category: res.data }); requestsDone++;
isDone();
}) })
.catch(() => { .catch(() => {
alert('Impossile de mettre à jour ce végétal'); NotificationManager.error('Impossile de mettre à jour ce végétal');
}); });
// Patch or create Properties
this.state.Vegetable.Properties.forEach(propertyItem => {
if (propertyItem.id) {
API.patch(`types/${this.state.Vegetable.Type.id}/vegetables/${this.state.Vegetable.id}/properties/${propertyItem.id}`, propertyItem)
.then((res) => {
requestsDone++;
isDone();
})
.catch(() => {
NotificationManager.error(`Impossile de mettre à jour la propriété ${propertyItem.Property.name}`);
});
} else {
API.post(`types/${this.state.Vegetable.Type.id}/vegetables/${this.state.Vegetable.id}/properties`, propertyItem)
.then((res) => {
requestsDone++;
isDone();
})
.catch(() => {
NotificationManager.error(`Impossile de sauvegarder la propriété ayant pour valeur ${propertyItem.value}`);
});
}
})
} }
postPicture(picture) { postPicture(picture) {
@ -133,7 +177,7 @@ class Vegetable extends Component {
// this.setState({ Category: res.data }); // this.setState({ Category: res.data });
}) })
.catch(() => { .catch(() => {
alert('Impossile d\'ajouter cette image'); NotificationManager.error(`Impossible d'ajouter cette image`);
}); });
} }
@ -148,7 +192,7 @@ class Vegetable extends Component {
} }
}) })
.catch(e => { .catch(e => {
alert('Erreur lors de la récupération des types de propriétés'); NotificationManager.error('Erreur lors de la récupération des types de propriétés');
}); });
API.get(`types/${categoryId}/vegetables/${vegetableId}`) API.get(`types/${categoryId}/vegetables/${vegetableId}`)
@ -175,13 +219,14 @@ class Vegetable extends Component {
} }
}) })
.catch((e) => { .catch((e) => {
alert('Erreur lors de la récupération de ce végétal'); NotificationManager.error('Erreur lors de la récupération de ce végétal');
}); });
} }
render() { render() {
return ( return (
<div> <div>
<NotificationContainer/>
<Header /> <Header />
<Container className="Vegetable"> <Container className="Vegetable">
<Navigation categoryId={this.state.Vegetable.Type ? this.state.Vegetable.Type.id : 1} categoryName={this.state.Vegetable.Type ? this.state.Vegetable.Type.name : null} vegetableId={this.state.Vegetable.id || 1} vegetableName={this.state.Vegetable.name} /> <Navigation categoryId={this.state.Vegetable.Type ? this.state.Vegetable.Type.id : 1} categoryName={this.state.Vegetable.Type ? this.state.Vegetable.Type.name : null} vegetableId={this.state.Vegetable.id || 1} vegetableName={this.state.Vegetable.name} />
@ -219,6 +264,7 @@ class Vegetable extends Component {
imagePreviewUrl={this.state.imagePreviewUrl} imagePreviewUrl={this.state.imagePreviewUrl}
updateValue={this.updateValueFromChild} updateValue={this.updateValueFromChild}
changeMainPicture={this.changeMainPicture} changeMainPicture={this.changeMainPicture}
availableProperties={this.state.availableProperties}
/> />
</TabPane> </TabPane>
<TabPane tabId="2"> <TabPane tabId="2">

View file

@ -11,6 +11,7 @@ import {
import { import {
FaFileImage, FaFileImage,
} from 'react-icons/fa'; } from 'react-icons/fa';
import VegetablesProperties from './Properties'
class VegetableMain extends Component { class VegetableMain extends Component {
constructor(props) { constructor(props) {
@ -25,13 +26,13 @@ class VegetableMain extends Component {
Pictures: [], Pictures: [],
}, },
imagePreviewUrl: '', imagePreviewUrl: '',
availableProperties: []
}; };
this.handleChange = this.handleChange.bind(this); this.handleChange = this.handleChange.bind(this);
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
console.log(nextProps)
if (nextProps.Vegetable) { if (nextProps.Vegetable) {
this.setState({ this.setState({
Vegetable: nextProps.Vegetable Vegetable: nextProps.Vegetable
@ -42,6 +43,11 @@ class VegetableMain extends Component {
imagePreviewUrl: nextProps.imagePreviewUrl imagePreviewUrl: nextProps.imagePreviewUrl
}); });
} }
if (nextProps.availableProperties) {
this.setState({
availableProperties: nextProps.availableProperties
});
}
} }
handleChange(event) { handleChange(event) {
@ -128,6 +134,12 @@ class VegetableMain extends Component {
/> />
</Col> </Col>
</FormGroup> </FormGroup>
<VegetablesProperties
selectedProperties={this.state.Vegetable.Properties}
availableProperties={this.state.availableProperties}
setProperties={this.handleChange}
/>
</Col> </Col>
<Col xs={12} sm={8}> <Col xs={12} sm={8}>
<div className="imgPreview"> <div className="imgPreview">

167
src/Vegetable/Properties.js Normal file
View file

@ -0,0 +1,167 @@
import React, {
Component,
} from 'react';
import {
Button,
FormGroup,
Label,
Input,
InputGroup,
InputGroupAddon,
Col
} from 'reactstrap';
import {
FaPlus,
} from 'react-icons/fa';
class VegetablesProperties extends Component {
constructor(props) {
super(props);
this.state = {
selectedProperties: [],
availableProperties: [],
selectedPropety: null
};
this.handleChange = this.handleChange.bind(this);
this.handleArrayChange = this.handleArrayChange.bind(this);
this.updateParent = this.updateParent.bind(this);
this.addProperty = this.addProperty.bind(this);
this.notSet = this.notSet.bind(this);
}
componentWillReceiveProps(nextProps) {
if (nextProps.selectedProperties) {
this.setState({
selectedProperties: nextProps.selectedProperties
});
}
if (nextProps.availableProperties) {
this.setState({
availableProperties: nextProps.availableProperties,
// selectedPropety: (nextProps.availableProperties.length > 0 ? nextProps.availableProperties[0].id : null)
});
}
}
handleChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value,
});
}
handleArrayChange(e) {
const {
id,
value
} = e.target;
let {
selectedProperties
} = this.state;
const targetIndex = selectedProperties.findIndex(datum => {
return Number(datum.propertyId) === Number(id);
});
if (targetIndex !== -1) {
selectedProperties[targetIndex].value = value;
this.setState({
selectedProperties
});
}
}
addProperty() {
for (let i = 0; i < this.state.availableProperties.length; i += 1) {
if (this.state.availableProperties[i].id === Number(this.state.selectedPropety)) {
this.setState(prevState => ({
selectedProperties: [
...prevState.selectedProperties,
{
propertyId: this.state.selectedPropety,
value: '',
label: this.state.availableProperties[i].name
}
]
}));
break;
}
}
}
notSet(propertyId) {
let isSet = false;
for (let i = 0; i < this.state.selectedProperties.length; i += 1) {
if (this.state.selectedProperties[i].propertyId === propertyId) {
isSet = true;
break;
}
}
return !isSet;
}
updateParent() {
this.props.setProperties({
target: {
type: 'text',
name: 'Properties',
value: this.state.selectedProperties
}
});
}
render() {
return (
<div>
{this.state.selectedProperties.map( (item , key) => (
<FormGroup row key={key}>
<Label for="name" sm={4}>{item.label ? item.label : (item.Property ? item.Property.name : '')}</Label>
<Col sm={8}>
<Input
type="text"
value={item.value}
id={item.propertyId}
placeholder={item.label}
onChange={this.handleArrayChange}
onBlur={this.updateParent}
/>
</Col>
</FormGroup>
))}
<hr />
<FormGroup row>
<Label for="newProperty" sm={4}>Propriétés</Label>
<Col sm={8}>
<InputGroup>
<Input value={this.state.selectedPropety} type="select" name="selectedPropety" id="newProperty" onChange={this.handleChange}>
{this.state.availableProperties.map( (item, key) => {
if ( this.notSet( item.id ) ) {
return (<option key={key} value={item.id}>{item.name}</option>)
}
return (null)
})}
</Input>
<InputGroupAddon addonType="append">
<Button color="primary" onClick={this.addProperty}>
<FaPlus />
</Button>
</InputGroupAddon>
</InputGroup>
</Col>
</FormGroup>
</div>
)
}
}
export default VegetablesProperties;