Repository: bradtraversy/codegig
Branch: master
Commit: 8e8f43cfb3f3
Files: 14
Total size: 13.5 KB
Directory structure:
gitextract_0ajwmxgi/
├── .gitignore
├── README.md
├── app.js
├── config/
│ └── database.js
├── models/
│ └── Gig.js
├── package.json
├── public/
│ └── css/
│ └── style.css
├── routes/
│ └── gigs.js
└── views/
├── add.handlebars
├── error.handlebars
├── gigs.handlebars
├── index.handlebars
└── layouts/
├── landing.handlebars
└── main.handlebars
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
node_modules
.env
================================================
FILE: README.md
================================================
# CodeGig
> Simple Job find app for coders. This app uses Node, Express, Sequalize (Postgres) and Handlebars.
## Quick Start
``` bash
# Install dependencies
npm install
# Serve on localhost:5000
npm start
# Dev Server (Nodemon)
npm run dev
```
## App Info
### Author
Brad Traversy
[Traversy Media](http://www.traversymedia.com)
### Version
1.0.0
### License
This project is licensed under the MIT License
================================================
FILE: app.js
================================================
const express = require('express');
const exphbs = require('express-handlebars');
const bodyParser = require('body-parser');
const path = require('path');
// Database
const db = require('./config/database');
// Test DB
db.authenticate()
.then(() => console.log('Database connected...'))
.catch(err => console.log('Error: ' + err))
const app = express();
// Handlebars
app.engine('handlebars', exphbs({ defaultLayout: 'main' }));
app.set('view engine', 'handlebars');
// Body Parser
app.use(express.urlencoded({ extended: false }));
// Set static folder
app.use(express.static(path.join(__dirname, 'public')));
// Index route
app.get('/', (req, res) => res.render('index', { layout: 'landing' }));
// Gig routes
app.use('/gigs', require('./routes/gigs'));
const PORT = process.env.PORT || 5000;
app.listen(PORT, console.log(`Server started on port ${PORT}`));
================================================
FILE: config/database.js
================================================
const Sequelize = require('sequelize');
const dotenv = require('dotenv');
dotenv.config();
module.exports = new Sequelize(process.env.DATABASE_URL, {
host: 'localhost',
dialect: 'postgres',
operatorsAliases: false,
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
},
});
================================================
FILE: models/Gig.js
================================================
const Sequelize = require('sequelize');
const db = require('../config/database');
const Gig = db.define('gig', {
title: {
type: Sequelize.STRING
},
technologies: {
type: Sequelize.STRING
},
description: {
type: Sequelize.STRING
},
budget: {
type: Sequelize.STRING
},
contact_email: {
type: Sequelize.STRING
}
});
Gig.sync().then(() => {
console.log('table created');
});
module.exports = Gig;
================================================
FILE: package.json
================================================
{
"name": "codegig",
"version": "1.0.0",
"description": "Simple job find app for coders",
"main": "app.js",
"scripts": {
"start": "node app.js",
"dev": "nodemon app.js"
},
"author": "Brad Traversy",
"license": "MIT",
"dependencies": {
"dotenv": "^8.1.0",
"express": "^4.16.4",
"express-handlebars": "^3.0.0",
"pg": "^7.7.1",
"pg-hstore": "^2.3.2",
"sequelize": "^4.44.3"
},
"devDependencies": {
"nodemon": "^1.18.8"
}
}
================================================
FILE: public/css/style.css
================================================
@import url('https://fonts.googleapis.com/css?family=Lato');
:root {
--primary-color: #568c9b;
--primary-hover-color: #64a5b7;
--bg-color: #333;
--box-shadow: 3px 4px 12px rgba(0, 0, 0, 0.7);
}
* {
box-sizing: border-box;
}
body {
font-family: 'Lato', sans-serif;
margin: 0;
color: #333;
background: #f4f4f4;
}
ul {
list-style: none;
padding: 0;
}
a {
color: #fff;
text-decoration: none;
}
a:hover {
color: var(--primary-color);
}
.container {
max-width: 960px;
padding: 1rem 4rem;
margin: auto;
overflow: hidden;
}
.error {
padding: 5px;
border: #777 dotted 1px;
margin-bottom: 15px;
}
/* Header */
header {
display: flex;
position: absolute;
top: 0;
left: 0;
width: 100%;
justify-content: space-between;
align-items: center;
padding: 1rem 6rem;
}
header nav ul {
display: flex;
}
header nav li {
margin: 0 1rem;
}
/* Login Button Spacing */
/*header nav li:last-child {
margin-left: -5px;
}*/
header.inner {
background: var(--bg-color);
border-bottom: 4px solid var(--primary-color);
position: relative;
box-shadow: var(--box-shadow);
}
/* Buttons */
.btn {
color: #fff;
padding: 0.6rem;
border: 1px solid #ccc;
transition: all 0.7s;
}
.btn:hover {
background: var(--primary-color);
border: 1px solid var(--primary-color);
color: #fff;
}
.btn-reverse {
background: var(--primary-color);
border: 1px solid var(--primary-color);
}
.btn-reverse:hover {
background: var(--primary-hover-color);
border: 1px solid var(--primary-hover-color);
}
/* Home Search */
.search-wrap {
background: url('../img/showcase.jpg') no-repeat center center fixed;
background-size: cover;
height: 100vh;
width: 100%;
padding: 1.3rem 3rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.search-wrap h1 {
font-size: 3rem;
font-weight: 800;
color: #fff;
margin: 0 0 1.5rem;
text-align: center;
}
.search-form input[type='submit'] {
background: var(--primary-color);
border: 1px solid var(--primary-color);
color: #fff;
padding: 0 2rem;
cursor: pointer;
transition: all 0.8s;
}
.search-form input[type='submit']:hover {
background: var(--primary-hover-color);
border: 1px solid var(--primary-hover-color);
}
.search-form {
display: flex;
width: 600px;
box-shadow: var(--box-shadow);
}
.search-form i {
color: #333;
}
/* Everything in the search form */
.search-form > * {
border: 0;
padding: 0 0 0 10px;
background: #fff;
line-height: 50px;
font-size: 1rem;
border-radius: 0;
outline: 0;
}
input[type='search'] {
flex-basis: 600px;
}
/* Gigs */
.gig {
background: var(--bg-color);
border-bottom: 4px solid var(--primary-color);
color: #fff;
padding: 1rem;
margin-bottom: 1rem;
box-shadow: var(--box-shadow);
}
.gig ul {
list-style: none;
display: flex;
}
.gig li {
margin-right: 0.5rem;
padding: 0.6rem;
}
.gig .tech span {
color: var(--primary-color);
}
/* Form */
.form-wrap {
margin: auto;
background: var(--bg-color);
color: #fff;
padding: 1rem 3rem 3rem;
margin-top: 3rem;
border-bottom: 4px solid var(--primary-color);
box-shadow: var(--box-shadow);
}
.form-wrap.reg-form,
.form-wrap.login-form {
width: 60%;
}
.form-wrap h1,
.form-wrap h2,
.form-wrap p {
text-align: center;
}
.form-wrap .btn {
margin-top: 1rem;
display: block;
width: 100%;
text-align: center;
font-size: 18px;
}
label {
display: block;
margin-bottom: 0.5rem;
}
.input-box {
padding: 0.5rem;
font-size: 18px;
width: 100%;
margin-bottom: 1.2rem;
}
/* Tablets */
@media (max-width: 800px) {
.container {
padding: 1rem 2rem;
}
header {
flex-direction: column;
padding: 0.3rem !important;
}
.search-form {
width: 100%;
}
input[type='search'] {
flex-basis: 100%;
}
.search-wrap h1 {
font-size: 2rem;
}
.search-wrap {
padding: 2.3rem;
}
.gig ul {
flex-direction: column;
}
.gig .btn {
display: block;
margin-top: 1rem;
text-align: center;
}
.form-wrap.reg-form,
.form-wrap.login-form {
width: 80%;
}
}
/* Smartphones */
@media (max-width: 500px) {
.container {
padding: 1rem;
}
header nav li {
margin: 0 10px;
}
.search-form {
display: flex;
flex-direction: column;
}
input[type='search'] {
flex-basis: 0;
}
.search-form i {
display: none;
}
.form-wrap {
padding: 1rem 2rem 2rem;
}
.form-wrap.reg-form,
.form-wrap.login-form {
width: 100%;
}
}
================================================
FILE: routes/gigs.js
================================================
const express = require('express');
const router = express.Router();
const db = require('../config/database');
const Gig = require('../models/Gig');
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
// Get gig list
router.get('/', (req, res) =>
Gig.findAll()
.then(gigs => res.render('gigs', {
gigs
}))
.catch(err => res.render('error', {error: err})));
// Display add gig form
router.get('/add', (req, res) => res.render('add'));
// Add a gig
router.post('/add', (req, res) => {
let { title, technologies, budget, description, contact_email } = req.body;
let errors = [];
// Validate Fields
if(!title) {
errors.push({ text: 'Please add a title' });
}
if(!technologies) {
errors.push({ text: 'Please add some technologies' });
}
if(!description) {
errors.push({ text: 'Please add a description' });
}
if(!contact_email) {
errors.push({ text: 'Please add a contact email' });
}
// Check for errors
if(errors.length > 0) {
res.render('add', {
errors,
title,
technologies,
budget,
description,
contact_email
});
} else {
if(!budget) {
budget = 'Unknown';
} else {
budget = `$${budget}`;
}
// Make lowercase and remove space after comma
technologies = technologies.toLowerCase().replace(/,[ ]+/g, ',');
// Insert into table
Gig.create({
title,
technologies,
description,
budget,
contact_email
})
.then(gig => res.redirect('/gigs'))
.catch(err => res.render('error', {error:err.message}))
}
});
// Search for gigs
router.get('/search', (req, res) => {
let { term } = req.query;
// Make lowercase
term = term.toLowerCase();
Gig.findAll({ where: { technologies: { [Op.like]: '%' + term + '%' } } })
.then(gigs => res.render('gigs', { gigs }))
.catch(err => res.render('error', {error: err}));
});
module.exports = router;
================================================
FILE: views/add.handlebars
================================================
Your contact email will be shared with registered users to apply to your gig {{text}} No gigs availableAdd A Gig
An error occured
{{error}}
All Gigs
{{#each gigs}}
{{else}}
Find A Coding Gig