diff --git a/config/config.json b/config/config.json index 37e61d87..4c46361c 100644 --- a/config/config.json +++ b/config/config.json @@ -1,17 +1,13 @@ { "development": { + "username": "sequelize", + "password": "sequelize", "database": "project_organizer_development", "host": "127.0.0.1", "dialect": "postgres" }, - "test": { - "database": "project_organizer_test", - "host": "127.0.0.1", - "dialect": "postgres" - }, "production": { - "database": "project_organizer_production", - "host": "127.0.0.1", + "use_env_variable": "DATABASE_URL", "dialect": "postgres" } } diff --git a/controllers/categories.js b/controllers/categories.js new file mode 100644 index 00000000..85041a7a --- /dev/null +++ b/controllers/categories.js @@ -0,0 +1,32 @@ +let express = require("express"); +let db = require("../models"); +let router = express.Router(); + +router.get("/", (req, res) => { + db.category + .findAll() + .then((categories) => { + res.render("categories/index", { categories: categories }); + }) + .catch((error) => { + console.log("Error in GET /", error); + res.status(400).render("main/404"); + }); +}); + +// GET /categories/:id - display a specific category +router.get("/:id", async (req, res) => { + try { + const category = await db.category.findOne({ + where: { id: req.params.id }, + }); + if (!category) throw Error(); + const projects = await category.getProjects(); + res.render("categories/show", { category: category, projects: projects }); + } catch (error) { + console.log(error); + res.status(400).render("main/404"); + } +}); + +module.exports = router; diff --git a/controllers/projects.js b/controllers/projects.js index 3d878a8a..b012227d 100644 --- a/controllers/projects.js +++ b/controllers/projects.js @@ -1,40 +1,49 @@ -let express = require('express') -let db = require('../models') -let router = express.Router() +let express = require("express"); +let db = require("../models"); +let router = express.Router(); -// POST /projects - create a new project -router.post('/', (req, res) => { - db.project.create({ - name: req.body.name, - githubLink: req.body.githubLink, - deployLink: req.body.deployedLink, - description: req.body.description - }) - .then((project) => { - res.redirect('/') - }) - .catch((error) => { - res.status(400).render('main/404') - }) -}) +router.post("/", async (req, res) => { + try { + const newProject = await db.project.create({ + name: req.body.name, + githubLink: req.body.githubLink, + deployLink: req.body.deployedLink, + description: req.body.description, + }); + const categoryInputs = await req.body.category.split(", "); + console.log(categoryInputs); + categoryInputs.forEach(async (categoryInput) => { + const [category] = await db.category.findOrCreate({ + where: { + name: categoryInput, + }, + }); + newProject.addCategory(category); + }); + res.redirect("../"); + } catch (error) { + res.status(400).render("main/404"); + } +}); // GET /projects/new - display form for creating a new project -router.get('/new', (req, res) => { - res.render('projects/new') -}) +router.get("/new", (req, res) => { + res.render("projects/new"); +}); // GET /projects/:id - display a specific project -router.get('/:id', (req, res) => { - db.project.findOne({ - where: { id: req.params.id } - }) - .then((project) => { - if (!project) throw Error() - res.render('projects/show', { project: project }) - }) - .catch((error) => { - res.status(400).render('main/404') - }) -}) +router.get("/:id", (req, res) => { + db.project + .findOne({ + where: { id: req.params.id }, + }) + .then((project) => { + if (!project) throw Error(); + res.render("projects/show", { project: project }); + }) + .catch((error) => { + res.status(400).render("main/404"); + }); +}); -module.exports = router +module.exports = router; diff --git a/dbTest.js b/dbTest.js new file mode 100644 index 00000000..4f746be9 --- /dev/null +++ b/dbTest.js @@ -0,0 +1,16 @@ +const db = require("./models"); + +db.project + .findOne({ + where: { id: 1 }, + include: [db.category], + }) + .then((project) => { + // by using eager loading, the project model should have a categories key + console.log(project.categories); + + // createCategory function should be available to this model - it will create the category then add it to the project + project.createCategory({ name: "express" }).then((category) => { + console.log(category.id); + }); + }); diff --git a/index.js b/index.js index f404f83d..baf4b471 100644 --- a/index.js +++ b/index.js @@ -1,35 +1,37 @@ -const express = require('express') -const ejsLayouts = require('express-ejs-layouts') -const db = require('./models') -const rowdy = require('rowdy-logger') +const express = require("express"); +const ejsLayouts = require("express-ejs-layouts"); +const db = require("./models"); +const rowdy = require("rowdy-logger"); -const app = express() -const PORT = process.env.PORT || 3000 -rowdy.begin(app) +const app = express(); +const PORT = process.env.PORT || 3000; +rowdy.begin(app); -app.set('view engine', 'ejs') -app.use(require('morgan')('dev')) -app.use(express.urlencoded({ extended: false })) -app.use(ejsLayouts) +app.set("view engine", "ejs"); +app.use(require("morgan")("dev")); +app.use(express.urlencoded({ extended: false })); +app.use(ejsLayouts); -app.get('/', (req, res) => { - db.project.findAll() - .then((projects) => { - res.render('main/index', { projects: projects }) - }) - .catch((error) => { - console.log('Error in GET /', error) - res.status(400).render('main/404') - }) -}) +app.get("/", (req, res) => { + db.project + .findAll() + .then((projects) => { + res.render("main/index", { projects: projects }); + }) + .catch((error) => { + console.log("Error in GET /", error); + res.status(400).render("main/404"); + }); +}); -app.use('/projects', require('./controllers/projects')) +app.use("/projects", require("./controllers/projects")); +app.use("/categories", require("./controllers/categories")); -app.get('*', (req, res) => { - res.render('main/404') -}) +app.get("*", (req, res) => { + res.render("main/404"); +}); -app.listen(PORT, function() { - rowdy.print() - console.log(`listening on port: ${PORT}`) -}) +app.listen(PORT, function () { + rowdy.print(); + console.log(`listening on port: ${PORT}`); +}); diff --git a/migrations/20230405222904-create-category.js b/migrations/20230405222904-create-category.js new file mode 100644 index 00000000..8e52fd79 --- /dev/null +++ b/migrations/20230405222904-create-category.js @@ -0,0 +1,28 @@ +'use strict'; +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.createTable('categories', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER + }, + name: { + type: Sequelize.STRING + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE + } + }); + }, + async down(queryInterface, Sequelize) { + await queryInterface.dropTable('categories'); + } +}; \ No newline at end of file diff --git a/migrations/20230405223808-create-categories-projects.js b/migrations/20230405223808-create-categories-projects.js new file mode 100644 index 00000000..4e407a24 --- /dev/null +++ b/migrations/20230405223808-create-categories-projects.js @@ -0,0 +1,31 @@ +'use strict'; +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.createTable('categoriesProjects', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER + }, + categoryId: { + type: Sequelize.INTEGER + }, + projectId: { + type: Sequelize.INTEGER + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE + } + }); + }, + async down(queryInterface, Sequelize) { + await queryInterface.dropTable('categoriesProjects'); + } +}; \ No newline at end of file diff --git a/models/categoriesprojects.js b/models/categoriesprojects.js new file mode 100644 index 00000000..ddb47da2 --- /dev/null +++ b/models/categoriesprojects.js @@ -0,0 +1,24 @@ +'use strict'; +const { + Model +} = require('sequelize'); +module.exports = (sequelize, DataTypes) => { + class categoriesProjects extends Model { + /** + * Helper method for defining associations. + * This method is not a part of Sequelize lifecycle. + * The `models/index` file will call this method automatically. + */ + static associate(models) { + // define association here + } + } + categoriesProjects.init({ + categoryId: DataTypes.INTEGER, + projectId: DataTypes.INTEGER + }, { + sequelize, + modelName: 'categoriesProjects', + }); + return categoriesProjects; +}; \ No newline at end of file diff --git a/models/category.js b/models/category.js new file mode 100644 index 00000000..1221e7a2 --- /dev/null +++ b/models/category.js @@ -0,0 +1,27 @@ +"use strict"; +const { Model } = require("sequelize"); +module.exports = (sequelize, DataTypes) => { + class category extends Model { + /** + * Helper method for defining associations. + * This method is not a part of Sequelize lifecycle. + * The `models/index` file will call this method automatically. + */ + static associate(models) { + // define association here + models.category.belongsToMany(models.project, { + through: "categoriesProjects", + }); + } + } + category.init( + { + name: DataTypes.STRING, + }, + { + sequelize, + modelName: "category", + } + ); + return category; +}; diff --git a/models/project.js b/models/project.js index 735a0a47..e0396ae0 100644 --- a/models/project.js +++ b/models/project.js @@ -1,7 +1,5 @@ -'use strict'; -const { - Model -} = require('sequelize'); +"use strict"; +const { Model } = require("sequelize"); module.exports = (sequelize, DataTypes) => { class project extends Model { /** @@ -11,24 +9,30 @@ module.exports = (sequelize, DataTypes) => { */ static associate(models) { // define association here + models.project.belongsToMany(models.category, { + through: "categoriesProjects", + }); } } - project.init({ - githubLink: { - type: DataTypes.TEXT, - validate: { isUrl: true } + project.init( + { + githubLink: { + type: DataTypes.TEXT, + validate: { isUrl: true }, + }, + name: DataTypes.STRING, + deployLink: { + type: DataTypes.TEXT, + validate: { isUrl: true }, + }, + description: { + type: DataTypes.TEXT, + }, }, - name: DataTypes.STRING, - deployLink: { - type: DataTypes.TEXT, - validate: { isUrl: true } - }, - description: { - type: DataTypes.TEXT - }, - }, { - sequelize, - modelName: 'project', - }); + { + sequelize, + modelName: "project", + } + ); return project; -}; \ No newline at end of file +}; diff --git a/package.json b/package.json index cbce809b..9dd406b4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,9 @@ { "name": "express-project-organizer", "version": "1.0.0", + "engines": { + "node": "18.15.0" + }, "main": "index.js", "scripts": { "start": "node index.js" diff --git a/views/categories/index.ejs b/views/categories/index.ejs new file mode 100644 index 00000000..60bd5607 --- /dev/null +++ b/views/categories/index.ejs @@ -0,0 +1,11 @@ +

Categories

+ +<% if (categories.length < 1) { %> +

Better get coding, I've no categories!

+<% } %> <% categories.forEach(function(category) { %> +
+

+ <%= category.name %> +

+
+<% }); %> diff --git a/views/categories/show.ejs b/views/categories/show.ejs new file mode 100644 index 00000000..c829eb53 --- /dev/null +++ b/views/categories/show.ejs @@ -0,0 +1,16 @@ +

Category: <%= category.name %>

+ +

Projects that fall under this category

+ +<% projects.forEach(function(project) { %> +
+

+ <%= project.name %> +

+
+<% }); %> + + +
+ +← Back Home diff --git a/views/partials/navbar.ejs b/views/partials/navbar.ejs index 081dee3b..64246a51 100644 --- a/views/partials/navbar.ejs +++ b/views/partials/navbar.ejs @@ -2,7 +2,12 @@
+
diff --git a/views/projects/new.ejs b/views/projects/new.ejs index 3342d3ee..9ac769d8 100644 --- a/views/projects/new.ejs +++ b/views/projects/new.ejs @@ -3,23 +3,52 @@
- +
- +
- + +
+ +
+ +
- +
- +