Skip to content

Instantly share code, notes, and snippets.

@imjulianeral
Created May 26, 2019 18:43
Show Gist options
  • Save imjulianeral/b86d9e541f2e90ee531d971789325d9a to your computer and use it in GitHub Desktop.
Save imjulianeral/b86d9e541f2e90ee531d971789325d9a to your computer and use it in GitHub Desktop.
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 };
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
};
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}`));
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();
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);
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);
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
}
}
`;
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);
}
}
}
}
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
}
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