GithubHelp home page GithubHelp logo

thunder-waste's Introduction

Thunder Waste

Thunder Waste is a Web application developed as part of Project #2 at IronHack Web Development Bootcamp.

App URL: https://thunder-waste.herokuapp.com/

The App

Thunder Waste ⚡ was born to deal with something that most of the people do not belifs in: trash.

As an waste removal platform for companies that generate residues, companies can easily schedule waste removal by informing type of residue and its weight.

Technologies

Front-end

  • JavaScript
  • HBS
  • Axios
  • CSS
  • HTML

Back-end

  • Node.js
  • Express
  • MongoDB
  • Passport
  • Google Maps API
  • Multer

Our Team

Thunder Waste was developed with ❤️ by:

thunder-waste's People

Contributors

giuroperto avatar flborrelli avatar

Watchers

James Cloos avatar  avatar

thunder-waste's Issues

AF - 1

FINISH

  • CSS/Bootstrap

FEATURES

  • Email
  • Google Social Login
  • Quantity restriction - Companies
  • Sign Up Approval
  • Cooperatives map

SEED

  • Cooperatives

MODELS

  • Images
  • Add location to USERS/COOPERATIVES
    location: { type: { type: String }, coordinates: [Number] }
    and after the schema
    nameOfYourSchema.index({ location: '2dsphere' });
  • Signup - add quantity in the model
  • Signup - add field ACTIVE -> default = false
  • Cooperatives - name, address, location, phone, more? active?

VIEWS

  • Email - form (login creation)
  • Email - message view (login creation)
  • Signup - add form field quantity / location
  • Employees - login request that will change ACTIVE status to true
  • Cooperatives - add location in the form
  • Cooperatives - map with pins showing where they are located
  • Cooperatives - employees manage list (CRUD)
  • Signup - add photo field
  • Create containers where to show the map + set a height
    <div id="map"></div>

ROUTES

  • Email - /send-email route (login creation)
  • Email get form
  • Email get message
  • Change login status
  • show cooperatives list
  • CRUD cooperatives
  • Edit route saving user model
  • To show location deconstruct the infos from the req.body and first create location, then the object:
    let location = { type: 'Point', coordinates: [longitude, latitude] };
    Restaurant.create({ name: name, description: description, location // <= add the location when creating a new restaurant }); .then (_ => res.redirect('/')) .catch(err => next(err)) });
  • set an API route to render the markers in the map
    router.get('/', (req, res) => { Places.find() .then((response) => res.json(response)) .catch((err) => { throw new Error(err); }); });

JAVASCRIPTS

  • Define if the case of creating a baseURL for axios:
    const nameOfApi = axios.create({ baseURL: ‘http://localhost:3000/api, });
  • Configuring the markers from our database
    const coffeBooksApi = axios.create({ baseURL: 'http://localhost:3000/api', });

window.onload = () => { getPlaces(); };

function getPlaces() { coffeBooksApi.get() .then(response => { placeStores(response.data); }) .catch(error => { console.log(error); }) }

function placeStores(places) { const saoPaulo = { lat: -23.6345838, lng: -46.7227298 };

const markers = [];

const map = new google.maps.Map(document.getElementById('map'), { zoom: 13, center: saoPaulo });

places.forEach((place) => { const center = { lat: place.location.coordinates[1], lng: place.location.coordinates[0] }; const pin = new google.maps.Marker({ position: center, map: map, title: place.name, }); markers.push(pin); }); }

  • To use data from the browser (tell the location)
    function startMap() { // Store Ironhack's coordinates const ironhackBCN = { lat: 41.3977381, lng: 2.190471916 }; // Initialize the map const map = new google.maps.Map(document.getElementById('map'), { zoom: 5, center: ironhackBCN } ); // Add a marker for Ironhack Barcelona const IronhackBCNMarker = new google.maps.Marker({ position: { lat: ironhackBCN.lat, lng: ironhackBCN.lng }, map: map, title: "Barcelona Campus" }); if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(function (position) { const user_location = { lat: position.coords.latitude, lng: position.coords.longitude }; // Center map with user location map.setCenter(user_location); // Add a marker for your user location const ironhackBCNMarker = new google.maps.Marker({ position: { lat: user_location.lat, lng: user_location.lng }, map: map, title: "You are here." }); }, function () { console.log('Error in the geolocation service.'); }); } else { console.log('Browser does not support geolocation.'); } } startMap();
  • Check how to do to have all pins centralised at once:
    const bounds = new google.maps.LatLngBounds(); map.fitBounds(bounds);
  • To draw routes:
    const directionsService = new google.maps.DirectionsService; const directionsDisplay = new google.maps.DirectionsRenderer; const directionRequest = { origin: { lat: 41.3977381, lng: 2.190471916}, destination: 'Madrid, ES', travelMode: 'DRIVING' }; //2 params -> obj with options and a callback directionsService.route( // other options below directionRequest, function(response, status) { if (status === 'OK') { // everything is ok directionsDisplay.setDirections(response); } else { // something went wrong window.alert('Directions request failed due to ' + status); } } ); directionsDisplay.setMap(map);

CONFIGS

  • Install nodemail
  • Google Social Login Access
  • Add Google keys in the .env
  • Map
  • Pins
  • Local API
  • Multer
  • Cloudify

IMAGES

  • Define the model the will receive this image and add the desired fields
  • Add the routes that will receive this info -> listing pictures or creating a new object that contains image
  • Require config in the routes
  • View to load the form -> add enctype=”multipart/form-data” to the form and the input that will receive the file has to be type=”file”
  • Create folder config with cloudinary.js:
    const cloudinary = require('cloudinary'); const cloudinaryStorage = require('multer-storage-cloudinary'); const multer = require('multer'); cloudinary.config({ cloud_name: process.env.CLOUDINARY_NAME, api_key: process.env.CLOUDINARY_KEY, api_secret: process.env.CLOUDINARY_SECRET }); var storage = cloudinaryStorage({ cloudinary: cloudinary, folder: 'folder-name', // The name of the folder in cloudinary allowedFormats: ['jpg', 'png'], filename: function (req, file, cb) { cb(null, file.originalname); // The file on cloudinary would have the same name as the original file name } });
  • Tell where we will save this image (right after code above + module.exports = uploadClouds) and call it in the route
    const uploadCloud = multer({ storage }); module.exports = uploadCloud;
  • Keep in the .env file all the keys and secrets
    CLOUDINARY_NAME=yourCLOUDINARYNameHere CLOUDINARY_KEY=yourCLOUDINARYKeyHere CLOUDINARY_SECRET=youCLOUDINARYSecretHere
  • Example of post route using Cloudinary
    router.post('/movie/add', uploadCloud.single('photo'), (req, res, next) => { const { title, description } = req.body; const imgPath = req.file.url; const imgName = req.file.originalname; Movie.create({title, description, imgPath, imgName}) .then(movie => { res.redirect('/'); }) .catch(error => { console.log(error); }) });

WHATELSE TO DO WITH GOOGLE?

Add validation to schemas (email and cnpj)

validate fields (ex of validation:
const userSchema = new Schema ({ linkedinProfile: { type: String, validate: { validator: (text) => { return text.indexOf(‘https://www.linkedin.com/’) === 0; }, message: “linkedinProfile must start with ‘https://www.linkedin.com/’” } } };)

Project Definition

MVP

  • Define MVP

  • Basic Flowchart

  • Define views

  • Define models

  • Define routes

  • Define main configs app

  • Define Authentication

  • HTML/CSS - Fernando (Bootstrap)


GENERAL

  • Set milestones

  • Set Agenda

DEPLOY

  • Create App Heroku website
  • Check if our PORT and MONGODB_URI is in the .env file
  • Check if you have both NPM START and NPM RUN DEV configured
  • Configure the remote repository:
    heroku git:remote -a (name-of-app)
  • When everything is ready and done:
    git push heroku master
  • Configure Environment Variables -> heroku > settings > Config Vars
  • Configure MongoLab Addon:
    heroku addons:create mongolab:sandbox
  • Deploy database in MongoLab:
    mongoexport -h (host) localhost -d (project database) library-project-06 -c (collection) books -o (output, can be any name) books.json
    mongoimport --uri=MONGODB_URI (you can find it in Heroku’s website > Settings > Config Vars) --file=books.json (the name you gave in the step above)
  • Check App Website
    heroku open

PRESENTATION

  • Create github readme

Structure

  • Slide 1 - Title Slide: your project’s name & your name
  • Slide 2 [+1] - Project Elevator Pitch: What is your project? How does it work? Why did you choose it?
  • Slide 3 [+1] - Technical Challenge: What was the most important technical challenge you faced? How did you overcome that challenge?
  • Slide 4 [+1] - Big Mistake: What was the biggest mistake you made during this project? What did you learn from it?
  • Slide 5 - Demo Slide: with a link to your project so you can open it easily
  • Slide 6 - Closing Slide: your project’s name, your name & a “Thank You”

AF - 2

FEATURES

  • Contact section
  • Cooperatives
  • Truck Drivers

MODELS

  • Cooperatives
  • NGOs (copy of cooperatives?)
  • Truck Drivers
  • Content

ROLE

  • Cooperatives
  • Truck Drivers
  • NGOs?

VIEWS

  • Contact Section Form: subject/topic, message, and data about the client
  • Cooperatives - Home
  • Cooperatives - Contact us
  • Cooperatives - Profile > Edit
  • Management - See all cooperatives
  • Management - See details
  • Management - Edit info cooperatives
  • Management - Delete cooperatives
  • Management - Add cooperatives
  • Truck Drivers - Home
  • Truck Drivers - Contact us
  • Truck Drivers - Profile > Edit
  • Management - See all Truck Drivers
  • Management - See details Truck Drivers
  • Management - Edit info Truck Drivers
  • Management - Delete Truck Drivers
  • Management - Add Truck Drivers
  • EDIT client home view to show in the bookings the truck drivers
  • EDIT mgmt home view to show in the bookings the truck drivers
  • Content - Initial page
  • Content - Add Content Form
  • EDIT booking form to accept images
  • EDIT client and mgmt view to show link of images

ROUTES

  • get /contact-us
  • post /contact-us
  • get /cooperatives
  • get /cooperatives/profile
  • get /cooperatives/profile/edit
  • post /cooperatives/profile/edit
  • get /drivers
  • get /drivers/profile
  • get /drivers/profile/edit
  • post /drivers/profile/edit
  • get /content - everyone can see
  • get /content-post - just managers
  • post /content
  • EDIT post /booking to accept images
  • Check the use of geoCoder so we don't have to write the coordinates

EMAIL

  • Contact us - Message to Mgmt

MVP - Part 2

CONTENT

  • Create info content for materials view and about us

ROUTES

AUTH ROUTES

  • Create folder
  • Create file
  • get /login
  • get /signup
  • post /signup
  • post /login
    router.post("/login", passport.authenticate("local", { successRedirect: "/", failureRedirect: "/login", failureFlash: true, passReqToCallback: true, }));
  • require bcrypt
  • set bcryptSalt = 10
  • SIGNUP check Username and Password
  • findOne(username) -> if null, create new user (const salt = bcrypt.genSaltSync(bcryptSalt); const hashWord = bcrypt.hashSync(password, salt);), else, render error
  • LOGIN check Username and Password
    after checking if the user does exist, check if password matches:
    if (bcrypt.compareSync(thePassword, user.password)) { // Save the login in the session! req.session.currentUser = user;
  • Check if all fields were filled in both LOGIN and SIGNUP
  • Check if the username is unique when creating an account SIGNUP
  • Add error messages -> flash install and require in app.js
  • Create logout route
    router.get("/logout", (req, res) => { req.logout(); res.redirect("/login"); });
  • Require ensurelogin in routes
  • Add the route such as the middleware will go between the route and the callback:
    router.get("/private-page", ensureLogin.ensureLoggedIn(), (req, res) => { res.render("private", { user: req.user }); });
  • Add jQuery check strength password
    https://www.npmjs.com/package/zxcvbn
  • Add re-captcha in the signup
    https://www.npmjs.com/package/express-recaptcha

AUTH - LOGIN

  • Add ensurelogin for all routes that are not HOME

AUTH - ROLES

  • Add types of roles and which route it should see

CSS

  • Do basic CSS/Bootstrap

SIGNUP

  • Create model User
  • Create Form
  • Create route that will handle this infos + validations
    if (username === "" || password === "") { res.render("auth/signup", { message: "Indicate a username and a password to sign up" }); return; }
  • To show error message in the view
    `{{#if message}}
    {{ message }}

{{/if}}`

  • To make sure username is unique
    User.findOne({ "username": username }) .then(user => { if (user !== null) { res.render("auth/signup", { message: "The username already exists!" }); return; } // ADD HERE THE ACTION TO SAVE IN DB }) .catch(error => { next(error); })
  • To save in the DB
    // User model const User = require("../models/user"); // BCrypt to encrypt passwords const bcrypt = require("bcrypt"); const bcryptSalt = 10; ... router.post("/signup", (req, res, next) => { const username = req.body.username; const password = req.body.password; const salt = bcrypt.genSaltSync(bcryptSalt); const hashPass = bcrypt.hashSync(password, salt); User.create({ username, password: hashPass }) .then(() => { res.redirect("/"); }) .catch(error => { console.log(error); }) });

CONFIGS

  • Sessions:
    app.use(session({ //secret - used to sign the session ID cookie (required) secret: "basic-auth-secret", resave: true, saveUninitialized: true, (?)// cookie: obj for the session ID cookie -> set maxAge attribute which configures the expiration date of (?)//the cookie (in milliseconds) (?)cookie: { maxAge: 60000 }, // store: sets the session store instance -> new instance of connect-mongo to store the session info in our DB store: new MongoStore({ mongooseConnection: mongoose.connection, ttl: 24 * 60 * 60 // 1 day }) }));
  • Passport:
    passport.serializeUser((user, cb) => { cb(null, user._id); });

passport.deserializeUser((id, cb) => { User.findById(id, (err, user) => { if (err) { return cb(err); } cb(null, user); }); });

app.use(flash()); passport.use(new LocalStrategy({ passReqToCallback: true }, (req, username, password, next) => { User.findOne({ username }, (err, user) => { if (err) { return next(err); } if (!user) { return next(null, false, { message: "Incorrect username" }); } if (!bcrypt.compareSync(password, user.password)) { return next(null, false, { message: "Incorrect password" }); }

return next(null, user); }); }));

app.use(passport.initialize()); app.use(passport.session());

AF - 3

FEATURES

  • Seal of Quality
  • Create a logo
  • Create a page explaining what it is
  • Send route by email to the drivers

MODELS

  • EDIT users to have a distinction between domestic users and companies
  • EDIT drivers to have a distinction between catadores and trucks
  • Catadores (copy of trucks?)
  • Reverse Logistics (copy of cooperatives?)

VIEW

  • Seal of Quality
  • Create route for drivers -> FORM -> MGMT
  • EDIT client home to show upcoming bookings
  • EDIT signup form to have the field to choose domestic users or companies
  • EDIT bookings to show field of truck to companies and catadores to domestic users
  • MGMT list all catadores
  • MGMT details catadores
  • MGMT add new catador
  • MGMT edit catador
  • MGMT delete catador

ROUTES

  • get /seal-of-quality
  • get /route
  • post /route

EMAIL

  • route to the drivers

CONFIG

  • Maps route from a start point to an end point

MVP - Part 1

Create App

  • Create basic structure (folders/files)
  • Install all dependencies (express, mongoose, body-parser, connect-flash, connect-mongo, hbs, multer, passport, passport-local, express-session, bcrypt, connect-ensure-login, cookie-parser, cloudinary, dotenv, nodemon, multer-storage-cloudinary)
  • also install: nodemailer
  • maybe install packages: (morgan, debug)
  • Create .env with the PORT and MONGODB_URI
    MONGODB_URI='mongodb://localhost/passport-roles'
  • Require dotenv -> require('dotenv').config();
  • npm init
  • create npm start and run dev in package.json ("start": "node app.js", "dev": "nodemon app.js")
  • Require all dependencies in app.js
  • Create the app
    const app = express();
  • Add the port to listen to
    app.listen(3000);
  • add in layout.hbs:
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCp_GfKTh7jDVzfNW5aPuUb1RxlG6oNHls"></script>
    + JS
  • Connect to the database ( mongoose
    .connect(‘mongodb://localhost/NAMEOFDATABASE’, { useNewUrlParser: true }) .then( x => console.log(Connected to Mongo! Database name: “${x.connections[0].name}”)) .catch(err => console.error(‘Error connecting to Mongo’, err))
  • add .gitignore with node_modules and .env

# Dependency directory
node_modules
# Environment Variables should NEVER be published
.env

Create models

  • Users + add required and unique
  • Bookings
  • module.exports = MODELS
  • If using document relationship as a reference, create the field in the model such as (for a single entry of this type, or if more, we should add an array of objects):
    PROPERTY: { type : Schema.Types.ObjectId, ref: 'MODEL' },

Seed the DB

  • require Mongoose
  • require Model
  • connect to the DB
  • Create seed files with -> companies/cooperatives/ngos/truck drivers?
  • Create a simple seed.js to populate the DB -> array of objects + action
  • If we will be using references -> add normally all info in the seeds.js but when doing the actions we have to first create the thing that will be referenced in the main collection and then pass only the id to it:
    const createAuthors = books.map(book => { Author.create(book.author) .then(author => { return author.name; }) .catch(error => { throw new Error(Impossible to add the author. ${error}) }) })

let findAuthors = Promise.all(createAuthors) .then(authors => { return books.map(book => { return Author.findOne({name: book.author.name, lastName: book.author.lastName}) .then(author => { if (!author) { throw new Error(unknown author ${book.author.name} ${book.author.lastName}); } return Object.assign({}, book, {author: author._id}); }) }); }) .catch(error => { throw new Error(error) })

const saveBooks = findAuthors.then(findAuthors => { return Promise.all(findAuthors) .then(books => { return books.map(book => { const newBook = new Book(book); return newBook.save(); }) }) }).then(savedBooks => { Promise.all(savedBooks) .then(books => books.forEach(book => console.log(created ${book.title}))) .then(() => mongoose.connection.close()) .catch(err => console.log("Error while saving the book: ",err)) })

  • close connection with Mongo (mongoose.connection.close();)
  • start DB (node file)
  • Check with Mongo

OTHER CONFIGS

app.js

  • hbs and views
    app.set('view engine', 'hbs'); app.set('views', path.join(__dirname, 'views')); app.use(express.static(path.join(__dirname, 'public')));
    if using partials
    hbs.registerPartials(__dirname + '/views/partials');
  • generating default title
    app.locals.title = 'Celebrity and Movie Generator';
  • Export app
    module.exports = app;
  • cookieParser:
    app.use(cookieParser());
  • bodyParser:
    app.use(bodyParser.urlencoded({ extended: true }));

ROUTES

  • Call all routes in index.js routes -> const index = require(‘./routes/index’); app.use(‘/’, index);
  • Populate referenced data, if the case:
    .populate('MODELATTRIBUTE')

INDEX ROUTES

OPEN

  • get /
  • get /about
  • get /services
  • get /recycling-waste-types

INTERNAL

  • get /staff/
  • get /staff/employees
  • get /staff/employees/:id
  • get /staff/employees/:id/edit
  • post /staff/employees/:id/edit
  • get /staff/users
  • get /staff/users/:id
  • get /staff/users/:id/edit
  • post /staff/users/:id/edit
  • get /staff/employees/add

USERS

  • get /home
  • get /profile
  • get /edit-infos/:id
  • post /edit-infos/:id
  • get /bookings
  • post /bookings

OTHERS

  • get /redirect
  • get /staff/bookings
  • get /staff/bookings/edit
  • post /staff/bookings/edit
  • get /logout
  • post /staff/users/:id/delete
  • get /staff/employees/:id/delete

VIEWS

ALL

  • Home:
    Links: Login, Signup and Materials Info.
    Page will have infos about our service + coverage map
  • Signup form - fields: Company's name, CNPJ, Address, Phone Number, Company's sector, Username, Email and Password + password strength jQuery
  • Login form - fields: username and password
  • Materials Info
    Page will contain infos about all different types of recyclable materials
  • About us
    Infos about the company
  • Services
    Listing all our services
  • Redirect -> choose who are you

USER

  • Home Client
    Where client will land when they log in
    Links: Logout, Profile > Edit, Book service
  • Book service form - fields: Date & Time (+ restrictions), Recyclable material, Quantity, Contact/ Responsible person and Address (only to confirm or reject)
  • User Profile
    Show all infos they've filled when signing up
  • User Edit
    They can edit all infos about them except CNPJ.

INTERNAL

  • Home Internal
    Page will cointain the upcoming bookings and some infos
    Links: Logout, Profile > Edit, Create a new MGMT account, Delete/Edit users
  • Employees List
    All employees list
  • Employees Profile
    All info about a employee profile
  • Edit Employee Profile
    They can update their own profile + all profiles can be edited -> ADMIN
  • Add Employee
    Form to create a new internal -> ADMIN
  • Users list
    All clients in our database
  • Users profile
    We can see all clients' profiles
  • Edit users
    A form to edit users or delete their accounts -> ADMIN
  • Profile logged in
    Info about the person logged in
  • Profile logged in edit
    People can change their own profile
  • Bookings
    will show a list of all the bookings

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.