Created
May 26, 2019 18:43
-
-
Save imjulianeral/b86d9e541f2e90ee531d971789325d9a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Fragment } from 'react'; | |
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; | |
// Components | |
import Header from './components/Layout/Header'; | |
import Clients from './components/Clients/Clients'; | |
import NewClient from './components/Clients/NewClient'; | |
import UpdateClient from './components/Clients/UpdateClient'; | |
import NewProduct from './components/Products/NewProduct'; | |
import Products from './components/Products/Products'; | |
import UpdateProduct from './components/Products/UpdateProduct'; | |
import NewOrder from './components/Orders/NewOrder'; | |
import ClientOrders from './components/Orders/ClientOrders'; | |
import Panel from './components/Panel/Panel'; | |
import Register from './components/Auth/Register'; | |
import Login from './components/Auth/Login'; | |
import Session from './components/Session'; | |
import PrivateRoute from './components/PrivateRoute'; | |
const App = ({ refetch, session }) => { | |
const { getUser } = session; | |
return ( | |
<Router> | |
<Fragment> | |
<Header session={ session }/> | |
<div className="container"> | |
<p className="text-right">{ (getUser) ? `Bienvenido: ${ getUser.name }` : null}</p> | |
<Switch> | |
<PrivateRoute exact path="/clientes" component={ Clients } session={ getUser }/> | |
<PrivateRoute exact path="/cliente/nuevo" component={ NewClient } session={ getUser } /> | |
<PrivateRoute exact path="/cliente/editar/:id" component={ UpdateClient }/> | |
<PrivateRoute exact path="/producto/nuevo" component={ NewProduct }/> | |
<PrivateRoute exact path="/productos" component={ Products }/> | |
<PrivateRoute exact path="/producto/editar/:id" component={ UpdateProduct }/> | |
<PrivateRoute exact path="/pedido/nuevo/:id" component={ NewOrder } session={ getUser }/> | |
<PrivateRoute exact path="/pedidos/:id" component={ ClientOrders }/> | |
<PrivateRoute exact path="/panel" component={ Panel }/> | |
<PrivateRoute exact path="/registro" component={ Register } session={ getUser }/> | |
<Route exact path="/login" render={ () => <Login refetch={ refetch } /> }/> | |
</Switch> | |
</div> | |
</Fragment> | |
</Router> | |
); | |
} | |
const RootSession = Session(App); | |
export { RootSession }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import mongoose from 'mongoose'; | |
mongoose.Promise = global.Promise; | |
mongoose.connect('mongodb://localhost/clients', { useNewUrlParser: true, useFindAndModify: false }); | |
// Define client's schema | |
const clientsSchema = new mongoose.Schema({ | |
name: String, | |
lastname: String, | |
company: String, | |
emails: Array, | |
age: Number, | |
type: String, | |
orders: Array, | |
seller: mongoose.Types.ObjectId | |
}); | |
const Clients = mongoose.model('clients', clientsSchema); | |
const productsSchema = new mongoose.Schema({ | |
name: String, | |
price: Number, | |
stock: Number | |
}); | |
const Products = mongoose.model('products', productsSchema); | |
const ordersSchema = new mongoose.Schema({ | |
order: Array, | |
total: Number, | |
date: Date, | |
client: mongoose.Types.ObjectId, | |
status: String, | |
seller: mongoose.Types.ObjectId | |
}); | |
const Orders = mongoose.model('orders', ordersSchema); | |
const userSchema = new mongoose.Schema({ | |
user: String, | |
password: String, | |
name: String, | |
rol: String | |
}); | |
const Users = mongoose.model('users', userSchema); | |
export { | |
Clients, | |
Products, | |
Orders, | |
Users | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import express from 'express'; | |
import jwt from 'jsonwebtoken'; | |
import dotenv from 'dotenv'; | |
// graphql | |
import { ApolloServer } from 'apollo-server-express'; | |
import { typeDefs } from './data/schema'; | |
import { resolvers } from './data/resolvers'; | |
dotenv.config({ path: 'variables.env' }); | |
const app = express(); | |
const server = new ApolloServer({ | |
typeDefs, | |
resolvers, | |
context: async({ req }) => { | |
const token = req.headers['authorization']; | |
if (token !== 'null') { | |
try { | |
const currentUser = await jwt.verify(token, process.env.SECRET); | |
req.currentUser = currentUser; | |
return{ | |
currentUser | |
}; | |
} catch (error) { | |
throw new Error(error); | |
} | |
} | |
} | |
}); | |
server.applyMiddleware({app}); | |
app.listen({ port: 4000 }, () => console.log(`Server running on: http://localhost:4000${server.graphqlPath}`)); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import ReactDOM from 'react-dom'; | |
import './index.css'; | |
import { RootSession } from './App'; | |
import * as serviceWorker from './serviceWorker'; | |
import { ApolloProvider } from 'react-apollo'; | |
import ApolloClient, { InMemoryCache } from 'apollo-boost'; | |
const client = new ApolloClient({ | |
uri: "http://localhost:4000/graphql", | |
// send token to the server | |
fetchOptions:{ | |
credentials: 'include' | |
}, | |
request: operation => { | |
const token = localStorage.getItem('token'); | |
operation.setContext({ | |
headers: { | |
authorization: token | |
} | |
}); | |
}, | |
cache: new InMemoryCache({ | |
addTypename: false | |
}), | |
onError: ({networkError, graphQLErrors}) => { | |
console.log('graphQLErrors', graphQLErrors); | |
console.log('networkError', networkError); | |
} | |
}); | |
ReactDOM.render( | |
<ApolloProvider client={ client }> | |
<RootSession/> | |
</ApolloProvider>, | |
document.getElementById('root') | |
); | |
// If you want your app to work offline and load faster, you can change | |
// unregister() to register() below. Note this comes with some pitfalls. | |
// Learn more about service workers: https://bit.ly/CRA-PWA | |
serviceWorker.unregister(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Component, Fragment } from 'react'; | |
import { withRouter } from 'react-router-dom'; | |
import Failed from '../Alerts/Failed'; | |
import { Mutation } from 'react-apollo'; | |
import { AUTH_USER_MUTATION } from '../../queries/Users'; | |
const initialState = { | |
user : '', | |
password: '' | |
} | |
class Login extends Component { | |
state = { | |
...initialState | |
} | |
actualizarState = e => { | |
const { name, value } = e.target; | |
this.setState({ | |
[name] : value | |
}) | |
} | |
limpiarState = () => { | |
this.setState({...initialState}); | |
} | |
iniciarSesion = (e, authUser) => { | |
e.preventDefault(); | |
authUser().then(async ({ data }) => { | |
localStorage.setItem('token', data.authUser.token); | |
await this.props.refetch(); | |
this.limpiarState(); | |
this.props.history.push('/panel'); | |
}).catch(err => { | |
console.error(err); | |
}); | |
} | |
validarForm = () => { | |
const { user, password } = this.state; | |
const noValido = !user || !password; | |
return noValido; | |
} | |
render() { | |
const { user, password } = this.state; | |
return ( | |
<Fragment> | |
<h1 className="text-center mb-5">Iniciar Sesión</h1> | |
<div className="row justify-content-center"> | |
<Mutation | |
mutation={ AUTH_USER_MUTATION } | |
variables={{ user, password }} | |
> | |
{( authUser, {loading, error, data}) => { | |
return ( | |
<form | |
onSubmit={ e => this.iniciarSesion(e, authUser) } | |
className="col-md-8" | |
> | |
{ (error) ? <Failed message={ error.message } /> : null } | |
<div className="form-group"> | |
<label>Usuario</label> | |
<input | |
onChange={this.actualizarState} | |
value={user} | |
type="text" | |
name="user" | |
className="form-control" | |
placeholder="Nombre Usuario" | |
/> | |
</div> | |
<div className="form-group"> | |
<label>Password</label> | |
<input | |
onChange={this.actualizarState} | |
value={password} | |
type="password" | |
name="password" | |
className="form-control" | |
placeholder="Password" | |
/> | |
</div> | |
<button | |
disabled={ | |
loading || this.validarForm() | |
} | |
type="submit" | |
className="btn btn-success float-right"> | |
Iniciar Sesión | |
</button> | |
</form> | |
) | |
}} | |
</Mutation> | |
</div> | |
</Fragment> | |
); | |
} | |
} | |
export default withRouter(Login); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import { ApolloConsumer } from 'react-apollo'; | |
import { withRouter } from 'react-router-dom'; | |
const logout = (client, history) => { | |
localStorage.removeItem('token', ''); | |
client.resetStore(); | |
history.push('/login'); | |
} | |
const Logout = ({ history }) => ( | |
<ApolloConsumer> | |
{ client => { | |
return( | |
<button | |
className="btn btn-light ml-md-2 mt-2 mt-md-0" | |
onClick={ () => logout(client, history) } | |
> | |
Cerrar Sesión | |
</button> | |
); | |
} } | |
</ApolloConsumer> | |
); | |
export default withRouter(Logout); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import gql from 'graphql-tag'; | |
export const NEW_USER_MUTATION = gql` | |
mutation createUser($user: String!, $password: String!, $name: String!, $rol: String!) { | |
createUser(user: $user, password: $password, name: $name, rol: $rol) | |
} | |
`; | |
export const AUTH_USER_MUTATION = gql` | |
mutation authUser($user: String!, $password: String!) { | |
authUser(user: $user, password: $password){ | |
token | |
} | |
} | |
`; | |
export const GET_USER_QUERY = gql` | |
query getUser { | |
getUser { | |
id | |
user | |
name | |
rol | |
} | |
} | |
`; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import mongoose from 'mongoose'; | |
import { Clients, Products, Orders, Users } from './db'; | |
import bcrypt from 'bcrypt'; | |
// Generate token | |
import dotenv from 'dotenv'; | |
import jwt from 'jsonwebtoken'; | |
dotenv.config({ path: 'variables.env' }); | |
const createToken = (name, secret, expiresIn) => { | |
const { user } = name; | |
return jwt.sign({ user }, secret, { expiresIn }); | |
} | |
const { ObjectId } = mongoose.Types; | |
export const resolvers = { | |
Query: { | |
getClient: async (root, {id}) => { | |
try { | |
return await Clients.findById(id); | |
} catch (error) { | |
throw new Error(error); | |
} | |
}, | |
getClients: async (root, {limit, offset, seller}) => { | |
try { | |
let filter; | |
if (seller) filter = { seller: new ObjectId(seller) }; | |
return await Clients.find(filter).limit(limit).skip(offset); | |
} catch (error) { | |
throw new Error(error); | |
} | |
}, | |
totalClients: async (root, {seller}) => { | |
try { | |
let filter; | |
if (seller) filter = { seller: new ObjectId(seller) }; | |
return await Clients.countDocuments(filter); | |
} catch (error) { | |
throw new Error(error); | |
} | |
}, | |
getProducts: async (root, {limit, offset, stock}) => { | |
try { | |
let filter; | |
if (stock) { | |
filter = { stock: { $gt: 0 } } | |
} | |
return await Products.find(filter).limit(limit).skip(offset); | |
} catch (error) { | |
throw new Error(error); | |
} | |
}, | |
getProduct: async (root, {id}) => { | |
try { | |
return await Products.findById(id); | |
} catch (error) { | |
throw new Error(error); | |
} | |
}, | |
totalProducts: async (root) => { | |
try { | |
return await Products.countDocuments({}); | |
} catch (error) { | |
throw new Error(error); | |
} | |
}, | |
getOrders: async (root, {client}) => { | |
try { | |
return await Orders.find({ client: client }); | |
} catch (error) { | |
throw new Error(error); | |
} | |
}, | |
topClients: async (root) => { | |
try { | |
return await Orders.aggregate([ | |
{ | |
$match: { status: "COMPLETADO" } | |
}, | |
{ | |
$group: { | |
_id: "$client", | |
total: { $sum: "$total" } | |
} | |
}, | |
{ | |
$lookup: { | |
from: 'clients', | |
localField: '_id', | |
foreignField: '_id', | |
as: 'client' | |
} | |
}, | |
{ | |
$sort: { total: -1 } | |
}, | |
{ | |
$limit: 10 | |
} | |
]); | |
} catch (error) { | |
throw new Error(error); | |
} | |
}, | |
getUser: async (root, args, {currentUser}) => { | |
try { | |
if (!currentUser) return null; | |
return await Users.findOne({ user: currentUser.user }); | |
} catch (error) { | |
throw new Error(error); | |
} | |
}, | |
topSellers: async (root) => { | |
try { | |
return await Orders.aggregate([ | |
{ | |
$match: { status: "COMPLETADO" } | |
}, | |
{ | |
$group: { | |
_id: "$seller", | |
total: { $sum: "$total" } | |
} | |
}, | |
{ | |
$lookup: { | |
from: 'users', | |
localField: '_id', | |
foreignField: '_id', | |
as: 'seller' | |
} | |
}, | |
{ | |
$sort: { total: -1 } | |
}, | |
{ | |
$limit: 10 | |
} | |
]); | |
} catch (error) { | |
throw new Error(error); | |
} | |
} | |
}, | |
Mutation: { | |
createClient: async (root, {input}) => { | |
try { | |
return await Clients.create({ | |
name: input.name, | |
lastname: input.lastname, | |
company: input.company, | |
emails: input.emails, | |
age: input.age, | |
type: input.type, | |
seller: input.seller | |
}); | |
} catch (error) { | |
throw new Error(error); | |
} | |
}, | |
updateClient: async (root, {input}) => { | |
try { | |
return await Clients.findOneAndUpdate({ _id: input.id}, input, {new: true}); | |
} catch (error) { | |
throw new Error(error); | |
} | |
}, | |
deleteClient: async (root, {id}) => { | |
try { | |
await Clients.findOneAndRemove({ _id: id}); | |
return "El cliente se ha eliminado correctamente"; | |
} catch (error) { | |
throw new Error(error); | |
} | |
}, | |
addProduct: async (root, {input}) => { | |
try { | |
return await Products.create({ | |
name: input.name, | |
price: input.price, | |
stock: input.stock | |
}); | |
} catch (error) { | |
throw new Error(error); | |
} | |
}, | |
updateProduct: async (root, {input}) => { | |
try { | |
return await Products.findOneAndUpdate({ _id: input.id}, input, {new: true}); | |
} catch (error) { | |
throw new Error(error); | |
} | |
}, | |
deleteProduct: async (root, {id}) => { | |
try { | |
await Products.findOneAndRemove({ _id: id}); | |
return "El producto ha sido eliminado satisfactoriamente"; | |
} catch (error) { | |
throw new Error(error); | |
} | |
}, | |
addOrder: async (root, {input}) => { | |
try { | |
return await Orders.create({ | |
order: input.order, | |
total: input.total, | |
date: new Date(), | |
client: input.client, | |
status: "PENDIENTE", | |
seller: input.seller | |
}); | |
} catch (error) { | |
throw new Error(error); | |
} | |
}, | |
updateStatus: async (root, {input}) => { | |
try { | |
const { status } = input; | |
let instruction; | |
if (status === 'COMPLETADO') { | |
instruction = '-'; | |
} else if (status === 'CANCELADO') { | |
instruction = '+'; | |
} | |
input.order.forEach(order => { | |
Products.updateOne({ _id: order.id }, { | |
"$inc": { "stock": `${ instruction }${ order.quantity }` } | |
}, function (err) { | |
if(err) return new Error(err); | |
} | |
); | |
}); | |
await Orders.findOneAndUpdate({ _id: input.id}, input, {new: true}); | |
return "El pedido ha sido actualizado satisfactoriamente"; | |
} catch (error) { | |
throw new Error(error); | |
} | |
}, | |
createUser: async (root, {user, password, name, rol}) => { | |
try { | |
const userExists = await Users.findOne({ user }); | |
if(userExists) throw new Error(`El usuario ${ user } ya existe`); | |
await Users.create({ | |
user, | |
password: bcrypt.hashSync(password, 10), | |
name, | |
rol | |
}); | |
return 'El nuevo usuario ha sido creado'; | |
} catch (error) { | |
throw new Error(error); | |
} | |
}, | |
authUser: async (root, {user, password}) => { | |
try { | |
const userExists = await Users.findOne({ user }); | |
if (!userExists) throw new Error(`Usuario NO existe`); | |
const correctPassword = await bcrypt.compare(password, userExists.password); | |
if (!correctPassword) throw new Error(`Password Incorrecto`); | |
return { | |
token: createToken(userExists, process.env.SECRET, '2hr') | |
}; | |
} catch (error) { | |
throw new Error(error); | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type Client{ | |
id: ID | |
name: String | |
lastname: String | |
company: String | |
emails: [Emails] | |
age: Int | |
type: ClientType | |
} | |
type Emails { | |
email: String | |
} | |
type Product { | |
id: ID | |
name: String! | |
price: Float! | |
stock: Int! | |
} | |
type TotalClient { | |
total: Float | |
client: [Client] | |
} | |
type TotalSeller { | |
total: Float | |
seller: [User] | |
} | |
type User { | |
id: ID | |
user: String | |
name: String | |
rol: String | |
} | |
""" Gives a category of the client """ | |
enum ClientType { | |
BASICO | |
PREMIUM | |
} | |
type Query { | |
# Clients | |
""" It gets a single client """ | |
getClient(id: ID): Client | |
""" It gets all the clients """ | |
getClients(limit: Int, offset: Int, seller: String): [Client] | |
""" It counts all the clients """ | |
totalClients(seller: String): String | |
# Products | |
""" It gets all the products """ | |
getProducts(limit: Int, offset: Int, stock: Boolean): [Product] | |
""" It gets a single product """ | |
getProduct(id: ID!): Product | |
""" It counts all the products """ | |
totalProducts: String | |
# Orders | |
""" It shows orders by client """ | |
getOrders(client: String): [Order] | |
# Charts | |
topClients: [TotalClient] | |
topSellers: [TotalSeller] | |
# Users | |
getUser: User | |
} | |
input EmailsInput { | |
email: String! | |
} | |
""" Fields for new clients """ | |
input ClientInput { | |
id: ID | |
name: String! | |
lastname: String! | |
company: String! | |
emails: [EmailsInput] | |
age: Int! | |
type: ClientType! | |
orders: [OrderInput] | |
seller: ID | |
} | |
""" Fields for new products """ | |
input ProductInput { | |
id: ID | |
name: String!, | |
price: Float!, | |
stock: Int! | |
} | |
""" Fields for new orders """ | |
type Order { | |
id: ID | |
order: [OrdersArrayType] | |
total: Float | |
date: String | |
client: ID | |
status: StatusType | |
} | |
type OrdersArrayType { | |
id: ID | |
quantity: Int | |
} | |
type Token { | |
token: String! | |
} | |
input OrderInput { | |
id: ID | |
order: [OrdersArray] | |
total: Float | |
date: String | |
client: ID | |
status: StatusType | |
seller: ID | |
} | |
input OrdersArray { | |
id: ID | |
quantity: Int | |
} | |
""" Gives a status of the order """ | |
enum StatusType { | |
PENDIENTE | |
COMPLETADO | |
CANCELADO | |
} | |
""" Mutations for create clients """ | |
type Mutation { | |
# Resolver's name, input with data and return data | |
# Clients | |
""" Allow you to create new clients """ | |
createClient(input: ClientInput): Client | |
""" Allow you to Update clients """ | |
updateClient(input: ClientInput): Client | |
""" Allow you to Delete clients """ | |
deleteClient(id: ID!): String | |
# Products | |
""" Creates a new product """ | |
addProduct(input: ProductInput): Product | |
""" Updates a Product """ | |
updateProduct(input: ProductInput): Product | |
""" Deletes a Product """ | |
deleteProduct(id: ID!): String | |
# Orders | |
""" Add a new Order """ | |
addOrder(input: OrderInput): Order | |
""" Update the status order """ | |
updateStatus(input: OrderInput): String | |
# Users | |
""" Creates a new user """ | |
createUser(user: String!, password: String!, name: String!, rol: String!): String | |
authUser(user: String!, password: String!): Token | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import { Query } from 'react-apollo'; | |
import { GET_USER_QUERY } from '../queries/Users'; | |
const Session = Component => props => { | |
return ( | |
<Query query={ GET_USER_QUERY }> | |
{({ loading, error, data, refetch }) => { | |
if (loading) return null; | |
return <Component { ...props } refetch={ refetch } session={ data } />; | |
}} | |
</Query> | |
); | |
}; | |
export default Session; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment