Commit 611c28ab authored by zarina's avatar zarina 🌊

#3, реализовала возможность добавления товаров

parent 50b7e654
import React from 'react'; import React from 'react';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import App from './App'; import App from './containers/App';
test('renders learn react link', () => { test('renders learn react link', () => {
const { getByText } = render(<App />); const { getByText } = render(<App />);
......
...@@ -3,27 +3,28 @@ import {DropdownItem, DropdownMenu, DropdownToggle, NavItem, NavLink, Uncontroll ...@@ -3,27 +3,28 @@ import {DropdownItem, DropdownMenu, DropdownToggle, NavItem, NavLink, Uncontroll
import {NavLink as RouterNavLink} from "react-router-dom"; import {NavLink as RouterNavLink} from "react-router-dom";
const UserMenu = ({user, logout}) => { const UserMenu = ({user, logout}) => {
return( return (<>
<UncontrolledDropdown nav inNavbar>
<DropdownToggle nav caret>
Hello, {user.displayName}!
</DropdownToggle>
<NavItem> <NavItem>
<NavLink tag={RouterNavLink} to="/products" exact> <NavLink tag={RouterNavLink} to="/" exact>
Products Products
</NavLink> </NavLink>
</NavItem> </NavItem>
<DropdownMenu right> <NavItem>
<DropdownItem>
<NavLink tag={RouterNavLink} to="/products/new" exact>Create product</NavLink> <NavLink tag={RouterNavLink} to="/products/new" exact>Create product</NavLink>
</DropdownItem> </NavItem>
<DropdownItem divider /> <UncontrolledDropdown className='ml-5' nav navbar inNavbar>
<DropdownToggle nav caret>
Hello, {user.displayName}!
</DropdownToggle>
<DropdownMenu right>
<DropdownItem divider/>
<DropdownItem <DropdownItem
onClick={logout}> onClick={logout}>
Logout Logout
</DropdownItem> </DropdownItem>
</DropdownMenu> </DropdownMenu>
</UncontrolledDropdown> </UncontrolledDropdown> </>
)}; )
};
export default UserMenu; export default UserMenu;
import React, {Component} from "react";
import {Button, Col, Form, FormGroup, Input, Label} from "reactstrap";
import {connect} from "react-redux";
import {history} from "../../store/configureStore";
import FormElement from "../../components/UI/FormElement/FormElement";
import {createProduct} from "../../store/actions/productsActions";
import {fetchCategories} from "../../store/actions/categoriesActions";
class AddProduct extends Component {
state = {
title: "",
description: "",
image: "",
category: "",
price: '1'
};
componentDidMount() {
if (!this.props.user) {
history.push('/login')
}
this.props.fetchCategories()
}
componentDidUpdate() {
if(this.state.category === '') {
this.setState({category: this.props.categories[0]._id})
}
}
submitFormHandler = (e) => {
const formData = new FormData();
Object.keys(this.state).forEach(key => {
formData.append(key, this.state[key]);
});
e.preventDefault();
this.props.onProductCreated(formData)
};
inputChangeHandler = (e) => {
this.setState({[e.target.name]: e.target.value});
};
fileChangeHandler = (e) => {
this.setState({image: e.target.files[0]});
};
getIdByName = name => {
for(let i = 0; i < this.props.categories.length; i++) {
if(name === this.props.categories[i].title){
return this.props.categories[i]._id;
}
}
return name
};
selectChangeHandler = e => {
this.setState({category: this.getIdByName(e.target.value)})
};
getFieldError = fieldName => {
return this.props.error && this.props.error.errors && this.props.error.errors[fieldName] && this.props.error.errors[fieldName].message;
};
render() {
return (
<Form onSubmit={this.submitFormHandler}>
<FormElement
propertyName="title"
label="Title"
onChange={this.inputChangeHandler}
value={this.state.title}
required={true}
type="text"
placeholder="Enter post title"
error={this.getFieldError("title")}
/>
<FormElement
propertyName="description"
label="Description"
onChange={this.inputChangeHandler}
value={this.state.description}
required={true}
type="text"
placeholder="Enter post description"
error={this.getFieldError("description")}
/>
<FormElement
propertyName="image"
label="Image"
onChange={this.fileChangeHandler}
required={true}
type="file"
error={this.getFieldError("image")}
/>
<FormElement
propertyName="price"
label="Price"
onChange={this.inputChangeHandler}
value={this.state.price}
required={true}
min='1'
type="number"
error={this.getFieldError("price")}
/>
<FormGroup row>
<Label sm={2} for="category">Category</Label>
<Col sm={10}>
<Input onChange={this.selectChangeHandler} type="select" required name="category" id="category">
{this.props.categories.map(category => {
return (<option name={category._id} key={category._id}>{category.title}</option>)
})}
</Input>
</Col>
</FormGroup>
<FormGroup row>
<Col sm={{offset: 2, size: 10}}>
<Button type="submit">Publish Post</Button>
</Col>
</FormGroup>
</Form>
);
}
}
const mapStateToProps = state => {
return {
user: state.users.user,
categories: state.categories.categories,
error: state.products.error
}
};
const mapDispatchToProps = dispatch => {
return {
onProductCreated: (product) => dispatch(createProduct(product)),
fetchCategories: () => dispatch(fetchCategories())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(AddProduct);
import React, {Component, Fragment} from 'react'; import React, {Component, Fragment} from 'react';
import Toolbar from "./components/UI/Toolbar/Toolbar"; import Toolbar from "../components/UI/Toolbar/Toolbar";
import {Container} from "reactstrap"; import {Container} from "reactstrap";
import {Route, Switch, withRouter} from "react-router-dom"; import {Route, Switch, withRouter} from "react-router-dom";
import Register from "./containers/Register/Register"; import Register from "./Register/Register";
import Login from "./containers/Login/Login"; import Login from "./Login/Login";
import {connect} from "react-redux"; import {connect} from "react-redux";
import {NotificationContainer} from "react-notifications"; import {NotificationContainer} from "react-notifications";
import {logoutUser} from "./store/actions/usersActions"; import {logoutUser} from "../store/actions/usersActions";
import AddProductForm from "./AddProductForm/AddProductForm";
class App extends Component { class App extends Component {
render() { render() {
...@@ -24,6 +25,7 @@ class App extends Component { ...@@ -24,6 +25,7 @@ class App extends Component {
<Switch> <Switch>
<Route path="/register" exact component={Register} /> <Route path="/register" exact component={Register} />
<Route path="/login" exact component={Login} /> <Route path="/login" exact component={Login} />
<Route path="/products/new" exact component={AddProductForm} />
</Switch> </Switch>
</Container> </Container>
</main> </main>
......
...@@ -4,7 +4,7 @@ import {Provider} from 'react-redux'; ...@@ -4,7 +4,7 @@ import {Provider} from 'react-redux';
import * as serviceWorker from './serviceWorker'; import * as serviceWorker from './serviceWorker';
import "react-notifications/lib/notifications.css" import "react-notifications/lib/notifications.css"
import 'bootstrap/dist/css/bootstrap.min.css'; import 'bootstrap/dist/css/bootstrap.min.css';
import App from './App'; import App from './containers/App';
import {store, history} from "./store/configureStore"; import {store, history} from "./store/configureStore";
import {ConnectedRouter} from "connected-react-router"; import {ConnectedRouter} from "connected-react-router";
......
export const FETCH_PRODUCTS_SUCCESS = "FETCH_PRODUCTS_SUCCESS";
export const GET_FULL_PRODUCT_SUCCESS = 'GET_FULL_PRODUCT_SUCCESS';
export const FETCH_CATEGORIES_SUCCESS = "FETCH_CATEGORIES_SUCCESS";
export const REGISTER_USER_SUCCESS = "REGISTER_USER_SUCCESS"; export const REGISTER_USER_SUCCESS = "REGISTER_USER_SUCCESS";
export const REGISTER_USER_FAILURE = "REGISTER_USER_FAILURE"; export const REGISTER_USER_FAILURE = "REGISTER_USER_FAILURE";
export const LOGIN_USER_SUCCESS = "LOGIN_USER_SUCCESS"; export const LOGIN_USER_SUCCESS = "LOGIN_USER_SUCCESS";
......
import {FETCH_CATEGORIES_SUCCESS, FETCH_ERROR} from "../actionTypes";
import axios from "../../axios-api";
const fetchCategoriesSuccess = (categories) => {
return {type: FETCH_CATEGORIES_SUCCESS, categories};
};
const fetchError = error => {
return {type: FETCH_ERROR, error};
};
export const fetchCategories = () => {
return dispatch => {
axios.get("/categories").then(response => {
dispatch(fetchCategoriesSuccess(response.data));
}).catch(e => {
dispatch(fetchError(e));
})
};
};
import {
FETCH_ERROR,
FETCH_PRODUCTS_SUCCESS,
GET_FULL_PRODUCT_SUCCESS
} from "../actionTypes";
import axios from "../../axios-api";
import {push} from "connected-react-router";
import {NotificationManager} from "react-notifications";
const fetchProductsSuccess = products => {
return {type: FETCH_PRODUCTS_SUCCESS, products}
};
const getFullProductSuccess = product => {
return {type: GET_FULL_PRODUCT_SUCCESS, product: product}
};
const fetchError = error => {
return {type: FETCH_ERROR, error};
};
export const fetchProducts = (category) => {
return dispatch => {
if (category) {
axios.get('/products?category=' + category)
.then(res => {
dispatch(fetchProductsSuccess(res.data));
})
.catch(e => {
dispatch(fetchError(e));
})
} else {
axios.get('/products')
.then(res => {
dispatch(fetchProductsSuccess(res.data));
})
.catch(e => {
dispatch(fetchError(e));
})
}
};
};
export const getFullProduct = id => {
return dispatch => {
axios.get('/products/' + id)
.then(res => {
dispatch(getFullProductSuccess(res.data));
})
.catch(e => {
dispatch(fetchError(e));
})
};
};
export const createProduct = product => {
return (dispatch, getState) => {
const token = getState().users.user.token;
const headers = {Token: token};
console.log(product)
return axios.post("/products", product, {headers})
.then(() => {
dispatch(push('/'));
NotificationManager.success("Created!");
})
.catch(e => {
dispatch(fetchError(e));
})
};
};
export const deleteProduct = id => {
return (dispatch, getState) => {
const token = getState().users.user.token;
const headers = {Token: token};
console.log(headers)
axios.delete('/products/' + id, {headers})
.then(() => {
dispatch(push('/'));
NotificationManager.success("Deleted!");
})
.catch(e => {
dispatch(fetchError(e));
})
};
};
...@@ -4,15 +4,18 @@ import {connectRouter, routerMiddleware} from "connected-react-router"; ...@@ -4,15 +4,18 @@ import {connectRouter, routerMiddleware} from "connected-react-router";
import {loadFromLocalStorage, saveToLocalStorage} from "./localStorage"; import {loadFromLocalStorage, saveToLocalStorage} from "./localStorage";
import {applyMiddleware, combineReducers, compose, createStore} from "redux"; import {applyMiddleware, combineReducers, compose, createStore} from "redux";
import thunkMiddleware from 'redux-thunk'; import thunkMiddleware from 'redux-thunk';
import categoriesReducer from "./reducers/categoriesReducer";
import usersReducer from "./reducers/usersReducer"; import usersReducer from "./reducers/usersReducer";
import productsReducer from "./reducers/productsReducer";
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
export const history = createBrowserHistory(); export const history = createBrowserHistory();
const rootReducer = combineReducers({ const rootReducer = combineReducers({
products: productsReducer,
users: usersReducer, users: usersReducer,
categories: categoriesReducer,
router: connectRouter(history) router: connectRouter(history)
}); });
...@@ -32,4 +35,3 @@ store.subscribe(() => { ...@@ -32,4 +35,3 @@ store.subscribe(() => {
} }
}) })
}); });
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment