diff --git a/.gitignore b/.gitignore index 123ae94..3bf57d2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,27 +1,5 @@ -# Logs -logs -*.log - -# Runtime data -pids -*.pid -*.seed - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (http://nodejs.org/api/addons.html) -build/Release - -# Dependency directory -# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git -node_modules +node_modules/ +.DS_Store +/public/js/* +/public/styles/app.css +/public/app.css diff --git a/CRUDGulpfile.js b/CRUDGulpfile.js new file mode 100644 index 0000000..ef84a0e --- /dev/null +++ b/CRUDGulpfile.js @@ -0,0 +1,29 @@ +var gulp = require("gulp"); +var server = require("gulp-express"); +var sass = require("gulp-sass"); +var webpack = require('webpack-stream'); + +gulp.task("build:css", function() { + gulp.src("./src/sass/**/*.scss") + .pipe(sass().on("error", sass.logError)) + .pipe(gulp.dest("./public/css")); +}); + +gulp.task("webpack", function(callback) { + return gulp.src("src/js/index.js") + .pipe(webpack({ + output: { + filename: "packed.js" + } + })) + .pipe(gulp.dest('public/js/')); +}); + +gulp.task("serve", function() { + server.run([server.js]); + gulp.watch("src/sass/**/*.scss", ["build:css"]); + gulp.watch("src/js/**/*.js", ["webpack"]); + gulp.watch("server.js", [server.run]); +}); + +gulp.task("default", ["build:css", "serve"]); diff --git a/Gulpfile.js b/Gulpfile.js new file mode 100644 index 0000000..69b26e3 --- /dev/null +++ b/Gulpfile.js @@ -0,0 +1,2 @@ +var requireDir = require('require-dir'); +requireDir('./gulp/tasks', { recurse: true }); diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..489b270 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: node server.js diff --git a/README.md b/README.md new file mode 100644 index 0000000..5b193f1 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +#C.R.U.D. Blog +### Molly Kent & Zach Gordon + +This assignment was to create a simple CRUD application that will allow a user to create, read, update, and delete blog posts from the application. There are two different views - one a list of available blog posts, the other a detail view of the blog post. We did not have to worry about authentication or user management for this assignment. + +The requirements were: +1. Semantically correct HTML +2. Images will be cropped and compressed correctly +3. Flexible CSS measurements +4. Clean, maintainable JavaScript +5. After checking out the repository, a user must be able to run npm install and gulp serve in order to access the application locally +6. Plugins are allowed, but must be described and justified +7. No jQuery + +We modeled our code after Mike's ng-soccer app, refactored to handle blog posts. diff --git a/gulp/config.js b/gulp/config.js new file mode 100644 index 0000000..0c7ec0b --- /dev/null +++ b/gulp/config.js @@ -0,0 +1,30 @@ +var dest = './public'; +var src = './src'; + +module.exports = { + javascript: { + src: src + '/app/**/*.js', + dest: dest + '/js/', + entryPoint: src + '/webpack-entry.js', + packedFile: 'packed.js' + }, + sass: { + src: src + '/styles/**/*.{sass,scss}', + dest: dest + '/styles/', + settings: { + indentedSyntax: true, + } + }, + html: { + src: src + "/app/**/*.html", + dest: dest + "/views/", + }, + server: { + serverFile: './server.js' + }, + production: { + cssSrc: dest + '/styles/*.css', + jsSrc: dest + '/*.js', + dest: dest + } +}; diff --git a/gulp/tasks/html.js b/gulp/tasks/html.js new file mode 100644 index 0000000..be9c3bc --- /dev/null +++ b/gulp/tasks/html.js @@ -0,0 +1,7 @@ +var gulp = require("gulp"); +var config = require('../config').html; + +gulp.task('html', function() { + return gulp.src(config.src) + .pipe(gulp.dest(config.dest)); +}); \ No newline at end of file diff --git a/gulp/tasks/minifyCSS.js b/gulp/tasks/minifyCSS.js new file mode 100644 index 0000000..a0ac874 --- /dev/null +++ b/gulp/tasks/minifyCSS.js @@ -0,0 +1,11 @@ +var gulp = require('gulp'); +var config = require('../config').production; +var minifyCSS = require('gulp-minify-css'); +var size = require('gulp-filesize'); + +gulp.task('minifyCSS', ['sass'], function() { + return gulp.src(config.cssSrc) + .pipe(minifyCSS({ keepBreaks: true })) + .pipe(gulp.dest(config.dest)) + .pipe(size()); +}) diff --git a/gulp/tasks/production.js b/gulp/tasks/production.js new file mode 100644 index 0000000..dcd72c6 --- /dev/null +++ b/gulp/tasks/production.js @@ -0,0 +1,4 @@ +var gulp = require('gulp'); +gulp.task('production', function() { + gulp.start(['minifyCSS', 'uglifyJs']) +}); diff --git a/gulp/tasks/sass.js b/gulp/tasks/sass.js new file mode 100644 index 0000000..f088463 --- /dev/null +++ b/gulp/tasks/sass.js @@ -0,0 +1,16 @@ +var gulp = require('gulp'); +var sass = require('gulp-sass'); +var sourcemaps = require('gulp-sourcemaps'); +var handleErrors = require('../util/handleErrors'); +var autoprefixer = require('gulp-autoprefixer'); +var config = require('../config').sass; + +gulp.task('sass', function() { + return gulp.src(config.src) + .pipe(sourcemaps.init()) + .pipe(sass(config.settings)) + .on('error', handleErrors) + .pipe(sourcemaps.write()) + .pipe(autoprefixer({ browsers: ['last 2 version'] })) + .pipe(gulp.dest(config.dest)); +}); diff --git a/gulp/tasks/serve.js b/gulp/tasks/serve.js new file mode 100644 index 0000000..2957248 --- /dev/null +++ b/gulp/tasks/serve.js @@ -0,0 +1,2 @@ +var gulp = require('gulp'); +gulp.task('serve', ['sass', 'html', 'webpack', 'watch', 'server']); diff --git a/gulp/tasks/server.js b/gulp/tasks/server.js new file mode 100644 index 0000000..9c85c4c --- /dev/null +++ b/gulp/tasks/server.js @@ -0,0 +1,8 @@ +var gulp = require('gulp'); +var config = require('../config').server; +var server = require("gulp-express"); + +gulp.task('server', ['sass', 'webpack'], function() { + server.run([config.serverFile]); + gulp.watch([config.serverFile], [server.run]); +}); diff --git a/gulp/tasks/uglifyJs.js b/gulp/tasks/uglifyJs.js new file mode 100644 index 0000000..c147129 --- /dev/null +++ b/gulp/tasks/uglifyJs.js @@ -0,0 +1,11 @@ +var gulp = require('gulp'); +var config = require('../config').production; +var size = require('gulp-filesize'); +var uglify = require('gulp-uglify'); + +gulp.task('uglifyJs', ['webpack'], function() { + return gulp.src(config.jsSrc) + .pipe(uglify()) + .pipe(gulp.dest(config.dest)) + .pipe(size()); +}); diff --git a/gulp/tasks/watch.js b/gulp/tasks/watch.js new file mode 100644 index 0000000..928c751 --- /dev/null +++ b/gulp/tasks/watch.js @@ -0,0 +1,7 @@ +var gulp = require('gulp'); +var config = require('../config'); + +gulp.task('watch', function() { + gulp.watch(config.javascript.src, ['webpack']); + gulp.watch(config.sass.src, ['sass']); +}); diff --git a/gulp/tasks/webpack.js b/gulp/tasks/webpack.js new file mode 100644 index 0000000..7dded07 --- /dev/null +++ b/gulp/tasks/webpack.js @@ -0,0 +1,13 @@ +var gulp = require('gulp'); +var config = require('../config').javascript; +var webpack = require('webpack-stream'); + +gulp.task('webpack', function(callback) { + return gulp.src(config.entryPoint) + .pipe(webpack({ + output: { + filename: config.packedFile + } + })) + .pipe(gulp.dest(config.dest)); +}); diff --git a/gulp/util/handleErrors.js b/gulp/util/handleErrors.js new file mode 100644 index 0000000..a6e481d --- /dev/null +++ b/gulp/util/handleErrors.js @@ -0,0 +1,15 @@ +var notify = require("gulp-notify"); + +module.exports = function() { + + var args = Array.prototype.slice.call(arguments); + + // Send error to notification center with gulp-notify + notify.onError({ + title: "Compile Error", + message: "<%= error %>" + }).apply(this, args); + + // Keep gulp from hanging on this task + this.emit('end'); +}; diff --git a/readme.md b/instructions.md similarity index 100% rename from readme.md rename to instructions.md diff --git a/models/post.js b/models/post.js new file mode 100644 index 0000000..cb70bc6 --- /dev/null +++ b/models/post.js @@ -0,0 +1,14 @@ +// Load required packages +var mongoose = require("mongoose"); + +// Define our teams schema +var PostSchema = new mongoose.Schema({ + title: String, + content: String, + author: String, + date: Date, + comments: Number +}); + +// Export the Mongoose model +module.exports = mongoose.model("Post", PostSchema); diff --git a/package.json b/package.json new file mode 100644 index 0000000..8d70859 --- /dev/null +++ b/package.json @@ -0,0 +1,52 @@ +{ + "name": "mean-stack-1", + "version": "0.1.0", + "description": "Simple CRUD app to demonstrate the MEAN stack", + "main": "Gulpfile.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/mollyfish/mean-stack-1.git" + }, + "author": [ + "Molly Kent ", + "Zach Gordon " + ], + "license": "MIT", + "bugs": { + "url": "https://github.com/mollyfish/mean-stack-1/issues" + }, + "homepage": "https://github.com/mollyfish/mean-stack-1#readme", + "dependencies": { + "angular": "^1.4.7", + "angular-route": "^1.4.7", + "body-parser": "^1.14.1", + "connect-livereload": "^0.5.3", + "express": "^4.13.3", + "gulp": "^3.9.0", + "gulp-autoprefixer": "^2.1.0", + "gulp-changed": "^1.1.1", + "gulp-express": "^0.3.5", + "gulp-filesize": "0.0.6", + "gulp-jshint": "^1.11.2", + "gulp-livereload": "^3.8.1", + "gulp-minify-css": "^1.2.1", + "gulp-notify": "^2.2.0", + "gulp-rename": "^1.2.2", + "gulp-sass": "^2.0.4", + "gulp-sourcemaps": "^1.6.0", + "gulp-uglify": "^1.4.1", + "gulp-util": "^3.0.4", + "jshint-stylish": "^2.0.1", + "lodash": "^3.3.1", + "merge-stream": "^0.1.7", + "mongoose": "^4.1.10", + "pretty-hrtime": "~1.0.0", + "require-dir": "^0.3.0", + "vinyl-source-stream": "~1.0.0", + "webpack": "^1.12.2", + "webpack-stream": "^2.1.1" + } +} diff --git a/public/404.html b/public/404.html new file mode 100644 index 0000000..85a5a9c --- /dev/null +++ b/public/404.html @@ -0,0 +1,14 @@ + + + + + + 404 - Page Not Found + + + + + +

404: Page Not Found

+ + \ No newline at end of file diff --git a/public/buttons.html b/public/buttons.html new file mode 100644 index 0000000..153e1c2 --- /dev/null +++ b/public/buttons.html @@ -0,0 +1,19 @@ + + + + + + Buttons + + + + + + Read More + Prev + Contact Now + Learn More + 1 + + + \ No newline at end of file diff --git a/public/connection-links.html b/public/connection-links.html new file mode 100644 index 0000000..073acc1 --- /dev/null +++ b/public/connection-links.html @@ -0,0 +1,24 @@ + + + + + + Connection Links + + + + + + + Follow Us + + + Like Us + + + Connect + + + + + diff --git a/public/contact.html b/public/contact.html new file mode 100644 index 0000000..ec17e92 --- /dev/null +++ b/public/contact.html @@ -0,0 +1,33 @@ + + + + + + Contact + + + + + + + + + + diff --git a/public/feature-box.html b/public/feature-box.html new file mode 100644 index 0000000..2e8fa48 --- /dev/null +++ b/public/feature-box.html @@ -0,0 +1,21 @@ + + + + + + Feature Box + + + + + +
+

Serene in their assurance of their empire over matter

+

No one would have believed in the last years of the nineteenth century that this world was being watched keenly and closely by intelligences greater than man’s and yet as more

+ Contact Now + Learn More +
+ + + + diff --git a/public/footer.html b/public/footer.html new file mode 100644 index 0000000..257a7cf --- /dev/null +++ b/public/footer.html @@ -0,0 +1,23 @@ + + + + + + Footer + + + + + +
+ + +
+ + + diff --git a/public/header.html b/public/header.html new file mode 100644 index 0000000..db63692 --- /dev/null +++ b/public/header.html @@ -0,0 +1,40 @@ + + + + + + Header + + + + + +
+
+

intelly

+ +
+
+ + + diff --git a/public/hero-box.html b/public/hero-box.html new file mode 100644 index 0000000..961f925 --- /dev/null +++ b/public/hero-box.html @@ -0,0 +1,19 @@ + + + + + + Hero Box + + + + + +
+

Blog

+

The secular cooling that must someday overtake our planet has already gone far indeed with our neighbour. Its physical condition is still largely a mystery, but we know now that even in its equatorial

+
+ + + + diff --git a/public/icons.html b/public/icons.html new file mode 100644 index 0000000..2044825 --- /dev/null +++ b/public/icons.html @@ -0,0 +1,20 @@ + + + + + + Icon Components + + + + + + + + + + + + + + diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..b87ba4f --- /dev/null +++ b/public/index.html @@ -0,0 +1,94 @@ + + + + + + Crud Blog + + + + + +
+
+

intelly

+ +
+
+
+

Blog

+

The secular cooling that must someday overtake our planet has already gone far indeed with our neighbour. Its physical condition is still largely a mystery, but we know now that even in its equatorial

+
+ +
+ + + + + + + + + + +
+

Serene in their assurance of their empire over matter

+

No one would have believed in the last years of the nineteenth century that this world was being watched keenly and closely by intelligences greater than man’s and yet as more

+ Contact Now + Learn More +
+ +
+

© Intelly LLC

+
    +
  • Terms of use
  • +
  • Privacy Policy
  • +
  • Contact
  • +
  • Support
  • +
+
+ + diff --git a/public/previews.html b/public/previews.html new file mode 100644 index 0000000..d30e18a --- /dev/null +++ b/public/previews.html @@ -0,0 +1,29 @@ + + + + + + Previews + + + + + +
+
+

{{item.title}}

+

{{item.body}}

+
+
    +
  • By {{item.author}}
  • +
  • {{item.date}}
  • +
  • {{item.comments}} comments
  • +
+ Read More +
+
+
+ + + + diff --git a/public/sec-nav.html b/public/sec-nav.html new file mode 100644 index 0000000..f9f9f95 --- /dev/null +++ b/public/sec-nav.html @@ -0,0 +1,29 @@ + + + + + + Secondary Nav + + + + + + + + + + diff --git a/public/styles/fonts/icons.eot b/public/styles/fonts/icons.eot new file mode 100755 index 0000000..9a0fa1c Binary files /dev/null and b/public/styles/fonts/icons.eot differ diff --git a/public/styles/fonts/icons.svg b/public/styles/fonts/icons.svg new file mode 100755 index 0000000..c199b2b --- /dev/null +++ b/public/styles/fonts/icons.svg @@ -0,0 +1,20 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/styles/fonts/icons.ttf b/public/styles/fonts/icons.ttf new file mode 100755 index 0000000..51b47e4 Binary files /dev/null and b/public/styles/fonts/icons.ttf differ diff --git a/public/styles/fonts/icons.woff b/public/styles/fonts/icons.woff new file mode 100755 index 0000000..1eaa25b Binary files /dev/null and b/public/styles/fonts/icons.woff differ diff --git a/public/views/post/post_detail.html b/public/views/post/post_detail.html new file mode 100644 index 0000000..26d577a --- /dev/null +++ b/public/views/post/post_detail.html @@ -0,0 +1,10 @@ +
+

{{vm.post.title}}

+
    +
  • By {{vm.post.author}}
  • +
  • {{vm.post.date | date: 'MMM dd, yyyy'}}
  • +
  • {{vm.post.comments}} comments
  • +
+

{{vm.post.content}}

+ edit this post +
diff --git a/public/views/post/post_details.html b/public/views/post/post_details.html new file mode 100644 index 0000000..26d577a --- /dev/null +++ b/public/views/post/post_details.html @@ -0,0 +1,10 @@ +
+

{{vm.post.title}}

+
    +
  • By {{vm.post.author}}
  • +
  • {{vm.post.date | date: 'MMM dd, yyyy'}}
  • +
  • {{vm.post.comments}} comments
  • +
+

{{vm.post.content}}

+ edit this post +
diff --git a/public/views/post/post_form.html b/public/views/post/post_form.html new file mode 100644 index 0000000..5d89bfa --- /dev/null +++ b/public/views/post/post_form.html @@ -0,0 +1,8 @@ +
+ + + + + + +
diff --git a/public/views/posts/posts_list.html b/public/views/posts/posts_list.html new file mode 100644 index 0000000..e45657a --- /dev/null +++ b/public/views/posts/posts_list.html @@ -0,0 +1,16 @@ +
+
+

{{post.title}}

+

{{post.content | limitTo: 370}}

+
+
    +
  • By {{post.author}}
  • +
  • {{post.date | date: 'MMM dd, yyyy'}}
  • +
  • {{post.comments}} comments
  • +
+ Read More +
+ +
+ Add a new post +
diff --git a/server.js b/server.js new file mode 100644 index 0000000..59493af --- /dev/null +++ b/server.js @@ -0,0 +1,125 @@ +var express = require('express'); +var mongoose = require("mongoose"); +var bodyParser = require("body-parser"); +var Post = require("./models/post"); + +mongoose.connect("mongodb://localhost/blog") + +var app = express(); +var port = process.env.PORT || 3000; +var router = express.Router(); + +app.use(bodyParser.json()); + +// Root route +router.get("/", function (req, res) { + res.sendFile(__dirname + "/src/app/index.html"); +}); + +// All assets +app.use(express.static(__dirname + "/public")); + +// Register the router with the application +app.use("/", router); + +// Create a new route with prefix /posts +var postsRoute = router.route("/api/posts"); + +// READ + +// Create endpoint /api/posts for POST +postsRoute.post(function (req, res) { + // Create a new instance of the Post model + var post = new Post(); + + // Set the post properties that came from the POST data + post.title = req.body.title; + post.content = req.body.content; + post.author = req.body.author; + post.date = req.body.date; + post.comments = req.body.comments; + + // Save the post and check for errors + post.save(function (err) { + if (err) { + res.send(err); + } + + res.json(post); + }); +}); + +// Create endpoint /api/posts for GET +postsRoute.get(function(req, res) { + // Use the Post model to find all posts + Post.find(function (err, posts) { + if (err) { + res.send(err); + } + + res.json(posts); + }); +}); + +// CREATE + +// Create a new route for /posts/:post_id +var postRoute = router.route("/api/posts/:post_id"); + + +// Create endpoint for /api/posts/:postID +postRoute.get(function(req, res) { + // Use the post model to find a specific post + Post.findById(req.params.post_id, function (err, post) { + if (err) { + res.send(err); + } + + res.json(post); + }); +}); + +// UPDATE + +// Change the post's stuff +postRoute.put(function(req, res) { + // Use the Post model to find a specific post + Post.findById(req.params.post_id, function (err, post) { + if (err) { + res.send(err); + } + + // Update the post's title + post.title = req.body.title; + post.content = req.body.content; + post.author = req.body.author; + post.date = req.body.date; + post.comments = req.body.comments; + + // Save the post and check for errors + post.save(function (err) { + if (err) { + res.send(err); + } + + res.json(post); + }); + }); +}); + +// DELETE + +// Create endpoint /api/posts/:post_id for DELETE +postRoute.delete(function (req, res) { + // Use the post model to find a specific post and remove it + Post.findByIdAndRemove(req.params.post_id, function(err) { + if (err) { + res.send(err); + } + + res.json({ message: "Successfully removed post." }); + }); +}); + +app.listen(port); +console.log('server is running on localhost:' + port); diff --git a/src/app/app.js b/src/app/app.js new file mode 100644 index 0000000..db66df5 --- /dev/null +++ b/src/app/app.js @@ -0,0 +1,38 @@ +require('angular'); +require('angular-route'); + +(function () { + "use strict"; + + var app = angular.module("blog", ["ngRoute"]); + + app.config(["$routeProvider", function ($routeProvider) { + $routeProvider.when("/posts", { + templateUrl: "views/posts/posts_list.html", + controller: "PostsCtrl as vm", + }) + .when("/posts/new", { + templateUrl: "views/post/post_form.html", + controller: "PostFormCtrl as vm", + }) + .when("/posts/:post_id/edit", { + templateUrl: "views/post/post_form.html", + controller: "PostFormCtrl as vm", + }) + .when("/posts/:post_id", { + templateUrl: "views/post/post_detail.html", + controller: "PostCtrl as vm", + }) + .otherwise({ + redirectTo: "/posts", + }); + }]); + + app.controller('DateCtrl', ['$scope', function($scope) { + $scope.date = new Date(); + } + ]); +}()); + + +require('./index.js'); diff --git a/src/app/index.js b/src/app/index.js new file mode 100644 index 0000000..8c727a8 --- /dev/null +++ b/src/app/index.js @@ -0,0 +1,5 @@ +require('./posts/posts.service'); +require('./posts/posts.ctrl'); +require('./post/post.ctrl'); +require('./post/post_form.ctrl.js'); +require('./post/post_details.directive.js'); diff --git a/src/app/post/post.ctrl.js b/src/app/post/post.ctrl.js new file mode 100644 index 0000000..e77c98e --- /dev/null +++ b/src/app/post/post.ctrl.js @@ -0,0 +1,15 @@ +require('../app'); + +angular.module("blog").controller("PostCtrl", ["PostsService", "$routeParams", function (PostsService, $routeParams) { + var vm = this; + + initialize(); + + function initialize() { + PostsService + .get($routeParams.post_id) + .then(function (resp) { + vm.post = resp.data; + }); + } +}]); diff --git a/src/app/post/post_detail.html b/src/app/post/post_detail.html new file mode 100644 index 0000000..26d577a --- /dev/null +++ b/src/app/post/post_detail.html @@ -0,0 +1,10 @@ +
+

{{vm.post.title}}

+
    +
  • By {{vm.post.author}}
  • +
  • {{vm.post.date | date: 'MMM dd, yyyy'}}
  • +
  • {{vm.post.comments}} comments
  • +
+

{{vm.post.content}}

+ edit this post +
diff --git a/src/app/post/post_details.directive.js b/src/app/post/post_details.directive.js new file mode 100644 index 0000000..99da595 --- /dev/null +++ b/src/app/post/post_details.directive.js @@ -0,0 +1,14 @@ +require('../app'); + +(function() { + "use strict"; + + angular.module("blog").directive("postDetails", function () { + return { + scope: { + post: "=post", + }, + templateUrl: 'views/post/post_details.html', + }; + }); +}()); diff --git a/src/app/post/post_form.ctrl.js b/src/app/post/post_form.ctrl.js new file mode 100644 index 0000000..9e6ec9f --- /dev/null +++ b/src/app/post/post_form.ctrl.js @@ -0,0 +1,34 @@ +require('../app'); + +(function () { + "use strict"; + angular.module("blog").controller("PostFormCtrl", ["PostsService", "$routeParams", "$location", function (PostsService, $routeParams, $location) { + var vm = this; + + vm.save = saveForm; + + vm.post = {}; + + initialize(); + + function initialize () { + vm.post.date = new Date(); + if ($routeParams.post_id) { + PostsService.get($routeParams.post_id).then(function (resp) { + vm.post = resp.data; + vm.post.date = vm.post.date || new Date(); + }); + } + + } + + function saveForm () { + var method; + + method = $routeParams.post_id ? "update" : "create"; + PostsService[method](vm.post).then(function (resp) { + $location.path("/posts/" + resp.data._id); + }); + } + }]); +}()); diff --git a/src/app/post/post_form.html b/src/app/post/post_form.html new file mode 100644 index 0000000..5d89bfa --- /dev/null +++ b/src/app/post/post_form.html @@ -0,0 +1,8 @@ +
+ + + + + + +
diff --git a/src/app/posts/posts.ctrl.js b/src/app/posts/posts.ctrl.js new file mode 100644 index 0000000..8372267 --- /dev/null +++ b/src/app/posts/posts.ctrl.js @@ -0,0 +1,32 @@ +require('../app'); + +(function () { + "use strict"; + + angular.module("blog").controller("PostsCtrl", ["PostsService", function (PostsService) { + var vm = this; + + vm.posts = []; + vm.delete = deletePost; + + initialize(); + + ///// + + function initialize () { + getPosts(); + } + + function getPosts () { + PostsService.get().then(function (resp) { + vm.posts = resp.data; + }); + } + + function deletePost (post) { + PostsService.delete(post).then(function () { + getPosts(); + }); + } + }]); +}()); diff --git a/src/app/posts/posts.service.js b/src/app/posts/posts.service.js new file mode 100644 index 0000000..30ecab4 --- /dev/null +++ b/src/app/posts/posts.service.js @@ -0,0 +1,29 @@ +require('../app'); + +(function () { + "use strict"; + + angular.module("blog").service("PostsService", ["$http", function ($http) { + var urlRoot = "/api/posts"; + + var Post = { + get: function (id) { + if (angular.isDefined(id)) { + return $http.get(urlRoot + "/" + id); + } else { + return $http.get(urlRoot); + } + }, + update: function (model) { + return $http.put(urlRoot + "/" + model._id, model); + }, + create: function (model) { + return $http.post(urlRoot, model); // ideal, but doesn't work + }, + delete: function (model) { + return $http.delete(urlRoot + "/" + model._id); + } + }; + return Post; + }]); +}()); diff --git a/src/app/posts/posts_list.html b/src/app/posts/posts_list.html new file mode 100644 index 0000000..e45657a --- /dev/null +++ b/src/app/posts/posts_list.html @@ -0,0 +1,16 @@ +
+
+

{{post.title}}

+

{{post.content | limitTo: 370}}

+
+
    +
  • By {{post.author}}
  • +
  • {{post.date | date: 'MMM dd, yyyy'}}
  • +
  • {{post.comments}} comments
  • +
+ Read More +
+ +
+ Add a new post +
diff --git a/src/styles/app.scss b/src/styles/app.scss new file mode 100644 index 0000000..3200bcc --- /dev/null +++ b/src/styles/app.scss @@ -0,0 +1,21 @@ +@import "modules/reset.scss"; +@import "modules/typography.scss"; +@import "modules/base.scss"; +@import "modules/flexbox.scss"; +@import "modules/breaks"; +@import "modules/colors.scss"; +@import "modules/icon-fonts"; + +@import "partials/connection-links.scss"; +@import "partials/icons.scss"; +@import "partials/buttons.scss"; +@import "partials/contact.scss"; +@import "partials/footer.scss"; +@import "partials/header.scss"; +@import "partials/sec-nav.scss"; +@import "partials/hero-box.scss"; +@import "partials/header.scss"; +@import "partials/feature-box.scss"; +@import "partials/previews.scss"; +@import "partials/post-detail.scss"; +@import "partials/post-form.scss"; diff --git a/src/styles/modules/_base.scss b/src/styles/modules/_base.scss new file mode 100644 index 0000000..1bf8621 --- /dev/null +++ b/src/styles/modules/_base.scss @@ -0,0 +1,3 @@ +body { + font-family: $main-font-regular; +} \ No newline at end of file diff --git a/src/styles/modules/_breaks.scss b/src/styles/modules/_breaks.scss new file mode 100644 index 0000000..8caaaf4 --- /dev/null +++ b/src/styles/modules/_breaks.scss @@ -0,0 +1,5 @@ +@mixin breaks($point) { + @if $point == desktop { + @media (min-width: 600px) { @content; } + } +} \ No newline at end of file diff --git a/src/styles/modules/_colors.scss b/src/styles/modules/_colors.scss new file mode 100644 index 0000000..d34fb34 --- /dev/null +++ b/src/styles/modules/_colors.scss @@ -0,0 +1,19 @@ +$color1: #000; +$color2: lighten($color1, 60%); +$color3: #fff; +$color4: #97f1e7; + +$text-color-dark: $color1; +$text-color-med: $color2; +$text-color-light: $color3; +$text-color-theme: $color4; + +$background-color-dark: $color1; +$background-color-med: $color2; +$background-color-light: $color3; +$background-color-theme: $color4; + +$border-color-dark: $color1; +$border-color-med: $color2; +$border-color-light: $color3; +$border-color-theme: $color4; diff --git a/src/styles/modules/_flexbox.scss b/src/styles/modules/_flexbox.scss new file mode 100644 index 0000000..cdcf2f5 --- /dev/null +++ b/src/styles/modules/_flexbox.scss @@ -0,0 +1,8 @@ +@mixin flex-container($type,$direction,$wrap,$justify,$content,$items) { + display: $type; + flex-direction: $direction; + flex-wrap: $wrap; + justify-content: $justify; + align-content: $content; + align-items: $items; +} diff --git a/src/styles/modules/_icon-fonts.scss b/src/styles/modules/_icon-fonts.scss new file mode 100644 index 0000000..7454b0c --- /dev/null +++ b/src/styles/modules/_icon-fonts.scss @@ -0,0 +1,40 @@ +@font-face { + font-family: 'icons'; + src:url('fonts/icons.eot?j42c1j'); + src:url('fonts/icons.eot?j42c1j#iefix') format('embedded-opentype'), + url('fonts/icons.ttf?j42c1j') format('truetype'), + url('fonts/icons.woff?j42c1j') format('woff'), + url('fonts/icons.svg?j42c1j#icomoon') format('svg'); + font-weight: normal; + font-style: normal; +} + +[class^="icon-"], [class*=" icon-"] { + font-family: 'icons'; + speak: none; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + // line-height: 2; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +$icons: +(menu3 '\e603') +(menu '\20') +(google-plus '\e800') +(facebook '\e801') +(twitter '\e802') +(linkedin '\e803') +(pinterest '\e804') +; + +@each $icon in $icons { + .icon-#{nth($icon, 1)}:before { + content:nth($icon, 2); + } +} + + diff --git a/src/styles/modules/_reset.scss b/src/styles/modules/_reset.scss new file mode 100644 index 0000000..ef9e538 --- /dev/null +++ b/src/styles/modules/_reset.scss @@ -0,0 +1,96 @@ +/* http://meyerweb.com/eric/tools/css/reset/ + v2.0 | 20110126 + License: none (public domain)*/ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; + box-sizing: border-box; + &:focus { + outline: 0; + } +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + /*display: block;*/ +} +html,body +{ + width: 100%; + overflow-x: hidden; +} +body { + line-height: 1; + width: 100%; +} +ol, ul { + list-style-type: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +/*&&&& Mods &&&&*/ +a { + text-decoration: none; + color: inherit; + &:hover { + cursor: pointer; + } +} +section, img, nav, figure { + max-width: 100%; +} +button, input[type="button"] { + // adapted from Codepen: http://codepen.io/terkel/pen/dvejH + // removes browser button style defaults + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + background: none; + border: 0; + // padding: 1em; + color: inherit; + cursor: pointer; + font: inherit; + overflow: visible; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} +.clearfix { + zoom: 1; +} +.clearfix:before, .clearfix:after { + content: "\0020"; + display: block; + height: 0; + overflow: hidden; +} +.clearfix:after { + clear: both; +} \ No newline at end of file diff --git a/src/styles/modules/_typography.scss b/src/styles/modules/_typography.scss new file mode 100644 index 0000000..952035e --- /dev/null +++ b/src/styles/modules/_typography.scss @@ -0,0 +1,47 @@ +@font-face { + font-family: 'Ubuntu Light'; + font-weight: 300; + src: local('Ubuntu Light'), local('Ubuntu-Light'), url(https://fonts.gstatic.com/s/ubuntu/v8/_aijTyevf54tkVDLy-dlnJBw1xU1rKptJj_0jans920.woff2) format('woff2'); +} +@font-face { + font-family: 'Ubuntu Light Italic'; + font-style: italic; + font-weight: 300; + src: local('Ubuntu Light Italic'), local('Ubuntu-LightItalic'), url(https://fonts.gstatic.com/s/ubuntu/v8/DZ_YjBPqZ88vcZCcIXm6Vogp9Q8gbYrhqGlRav_IXfk.woff2) format('woff2'); +} +@font-face { + font-family: 'Ubuntu'; + font-weight: 400; + src: local('Ubuntu'), url(https://fonts.gstatic.com/s/ubuntu/v8/zvCUQcxqeoKhyOlbifSAaevvDin1pK8aKteLpeZ5c0A.woff2) format('woff2'); +} +@font-face { + font-family: 'Ubuntu Medium'; + font-weight: 500; + src: local('Ubuntu Medium'), local('Ubuntu-Medium'), url(https://fonts.gstatic.com/s/ubuntu/v8/OsJ2DjdpjqFRVUSto6IffJBw1xU1rKptJj_0jans920.woff2) format('woff2'); +} +@font-face { + font-family: 'Ubuntu Bold'; + font-weight: 700; + src: local('Ubuntu Bold'), local('Ubuntu-Bold'), url(https://fonts.gstatic.com/s/ubuntu/v8/0ihfXUL2emPh0ROJezvraJBw1xU1rKptJj_0jans920.woff2) format('woff2'); +} + +$main-font-light: 'Ubuntu Light', sans-serif; +$main-font-light-italic: 'Ubuntu Light Italic', sans-serif; +$main-font-regular: 'Ubuntu', sans-serif; +$main-font-medium: 'Ubuntu Medium', sans-serif; +$main-font-bold: 'Ubuntu Bold', sans-serif; + +@font-face { + font-family: 'Roboto Slab Light'; + font-weight: 300; + src: local('Roboto Slab Light'), local('RobotoSlab-Light'), url(https://fonts.gstatic.com/s/robotoslab/v6/dazS1PrQQuCxC3iOAJFEJdTIkQYohD4BpHvJ3NvbHoA.woff2) format('woff2'); +} + +@font-face { + font-family: 'Roboto Slab'; + font-weight: 400; + src: local('Roboto Slab Regular'), local('RobotoSlab-Regular'), url(https://fonts.gstatic.com/s/robotoslab/v6/y7lebkjgREBJK96VQi37Zogp9Q8gbYrhqGlRav_IXfk.woff2) format('woff2'); +} + +$accent-font-light: 'Roboto Slab Light', serif; +$accent-font-regular: 'Roboto Slab', serif; diff --git a/src/styles/partials/_buttons.scss b/src/styles/partials/_buttons.scss new file mode 100644 index 0000000..4332136 --- /dev/null +++ b/src/styles/partials/_buttons.scss @@ -0,0 +1,44 @@ +.btn { + font-family: $main-font-medium; + display: inline-block; + padding: 0.75em 1em; + border: 1px solid $border-color-med; + border-radius: 0.25em; + text-align: center; + &:hover { + cursor: pointer; + } +} +.btn__text { + text-transform: uppercase; + width: 10em; +} +.btn__number { + padding-left: 1em; + padding-right: 1em; +} +.btn--dark { + background-color: $background-color-dark; + color: $text-color-light; + &:hover { + background-color: $background-color-light; + color: $text-color-dark; + } +} +.btn--light { + background-color: $background-color-light; + color: $text-color-dark; + &:hover { + background-color: $background-color-dark; + color: $text-color-light; + } +} +.btn--theme { + background-color: $background-color-light; + color: $text-color-theme; + border: 1px solid $border-color-light; + &:hover { + background-color: $background-color-theme; + color: $text-color-light; + } +} \ No newline at end of file diff --git a/src/styles/partials/_connection-links.scss b/src/styles/partials/_connection-links.scss new file mode 100644 index 0000000..0687895 --- /dev/null +++ b/src/styles/partials/_connection-links.scss @@ -0,0 +1,36 @@ +.connection-links { + @include flex-container(inline-flex,row,nowrap,center,center,center); + position: relative; + width: 8em; + height: 8em; + border: 1px solid $border-color-med; + border-radius: 50%; + &:hover { + background-color: $background-color-theme; + color: $text-color-light; + .connection-links__icon:before { + background-color: $background-color-light; + color: $text-color-dark; + } + } +} +.connection-links__text { + font-family: $main-font-bold; + text-transform: uppercase; +} +.connection-links__icon { + color: red; +} +.connection-links__icon:before { + @include flex-container(flex,row,nowrap,center,center,center); + position: absolute; + top: -0.4em; + left: -0.4em; + border: 1px solid $border-color-dark; + border-radius: 50%; + width: 35%; + height: 35%; + font-size: 120%; + color: $text-color-light; + background-color: $background-color-dark; +} \ No newline at end of file diff --git a/src/styles/partials/_contact.scss b/src/styles/partials/_contact.scss new file mode 100644 index 0000000..a2d1b29 --- /dev/null +++ b/src/styles/partials/_contact.scss @@ -0,0 +1,32 @@ +.contact { + // padding-bottom: 0.5em; + @include flex-container(flex,row,wrap,space-around,center,center); + text-align: center; + .connection-links { + margin: 1em 2em; + @include breaks(desktop) { + margin: 0 1em; + } + } +} +.contact__method { + margin: 2em 1em; + line-height: 1.5; + @include breaks(desktop) { + text-align: left; + } + a[href="tel:6624076792"], + a[href="mailto:contact@intell.com"] { + display: block; + text-align: center; + &:hover { + color: $text-color-theme; + } + @include breaks(desktop) { + text-align: left; + } + } +} +address a:hover { + color: $text-color-theme; +} diff --git a/src/styles/partials/_feature-box.scss b/src/styles/partials/_feature-box.scss new file mode 100644 index 0000000..7d80327 --- /dev/null +++ b/src/styles/partials/_feature-box.scss @@ -0,0 +1,20 @@ +.feature-box { + padding: 2em 8%; + text-align: center; + color: $text-color-light; + background-color: $background-color-theme; + h1 { + font-family: $main-font-medium; + font-size: 200%; + margin-bottom: 1em; + } + p { + font-family: $main-font-regular; + font-size: 100%; + line-height: 1.7; + margin: 0 10% 2em 10%; + } + .btn { + margin: 1em; + } +} diff --git a/src/styles/partials/_footer.scss b/src/styles/partials/_footer.scss new file mode 100644 index 0000000..486e304 --- /dev/null +++ b/src/styles/partials/_footer.scss @@ -0,0 +1,20 @@ +footer { + p { + padding-bottom: 1em; + } + @include flex-container(flex,row,wrap,center,center,center); + font-family: $main-font-medium; + font-size: .4em; + border-top: 1px solid $border-color-med; + padding-top: 3em; + ul { + padding-bottom: 1em; + text-align: center; + text-transform: uppercase; + } + li { + padding-left: 2em; + padding-right: 2em; + display: inline; + } +} diff --git a/src/styles/partials/_header.scss b/src/styles/partials/_header.scss new file mode 100644 index 0000000..3cd7ab1 --- /dev/null +++ b/src/styles/partials/_header.scss @@ -0,0 +1,71 @@ +header { + background-color: $background-color-dark; + h1 { + color: $text-color-light; + font-size: 1.5em; + padding: 1em; + text-align: center; + } + ul li { + display: none; + padding-left: .5em; + padding-bottom: .3em; + color: $text-color-light; + span { + display: none; + } + } + ul li:before { + content: "."; + display: inline-block; + position: relative; + bottom: 0.25em; + right: 1em; + } + ul li:last-child:before { + content: ""; + } + .icon-menu3:before { + padding-left: .5em; + color: $text-color-light; + } + input[type=checkbox] { + display: none; + } + input[type=checkbox]:checked ~ ul li { + display: block; + } +} + +@media only screen and (min-width: 650px) { + header { + .icon-menu3 { + display: none; + } + h1 { + position: absolute; + padding-top: 1.9em; + padding-left: 1.9em; + padding-bottom: 1.9em; + } + ul { + position: relative; + padding-top: 5em; + padding-right: 5em; + padding-bottom: 5em; + font-size: .7em; + text-transform: uppercase; + li { + padding-left: 1em; + padding-right: 1em; + float: right; + display: inline; + span { + display: inline; + color: $text-color-light; + } + } + } + } + +} diff --git a/src/styles/partials/_hero-box.scss b/src/styles/partials/_hero-box.scss new file mode 100644 index 0000000..1da6a94 --- /dev/null +++ b/src/styles/partials/_hero-box.scss @@ -0,0 +1,18 @@ +.hero-box { + padding: 2em 10% 2em 8%; + @include breaks(desktop) { + padding-right: 40%; + } + background-color: $background-color-theme; + h1 { + font-family: $accent-font-regular; + font-size: 200%; + margin-bottom: 1em; + } + p { + font-family: $accent-font-light; + font-size: 130%; + line-height: 1.7; + } + +} diff --git a/src/styles/partials/_icons.scss b/src/styles/partials/_icons.scss new file mode 100644 index 0000000..0543c17 --- /dev/null +++ b/src/styles/partials/_icons.scss @@ -0,0 +1,14 @@ +.icon-style { + font-size: 100%; + border-radius: 50%; + padding: .3em; + background-color: $background-color-dark; +} +// .icon-pinterest:before { +// font-size: 150%; +// color: $text-color-dark; +// } +.icon-menu:before { + font-size: 150%; + color: $text-color-dark; +} diff --git a/src/styles/partials/_post-detail.scss b/src/styles/partials/_post-detail.scss new file mode 100644 index 0000000..ce41edd --- /dev/null +++ b/src/styles/partials/_post-detail.scss @@ -0,0 +1,57 @@ +.post-detail { + @include flex-container(flex,column,nowrap,flex-start,flex-start,flex-start); + margin: 0 20% 2em 20%; + padding: 2em 0; + text-align: left; + article { + width: 100%; + border-bottom: 1px solid $border-color-med; + padding: 2em 0; + } + h1 { + font-family: $main-font-regular; + font-size: 200%; + margin-bottom: 1em; + } + p { + font-family: $main-font-regular; + font-size: 100%; + line-height: 1.7; + margin: 1em 0 2em 0; + } + .post-data { + @include flex-container(flex,row,wrap,space-between,center,center); + } + .btn { + width: 100%; + margin-top: 1em; + &.btn--light { + width: 15em; + margin-top: 1em; + } + @include breaks(desktop) { + width: 10em; + margin-top: 0; + } + } + ul { + @include flex-container(flex,row,wrap,center,center,center); + } + li { + margin: 0.5em 1em; + display: inline; + } + li:before { + content: ""; + display: inline-block; + position: relative; + bottom: 0.25em; + right: 1em; + @include breaks(desktop) { + content: "."; + } + } + li:first-child:before { + content: ""; + } +} diff --git a/src/styles/partials/_post-form.scss b/src/styles/partials/_post-form.scss new file mode 100644 index 0000000..3347b4b --- /dev/null +++ b/src/styles/partials/_post-form.scss @@ -0,0 +1,17 @@ +.post-form { + @include flex-container(flex,column,nowrap,flex-start,flex-start,flex-start); + margin: 0 20% 2em 20%; + padding: 2em 0; + text-align: left; + .btn { + width: 100%; + margin-top: 1em; + @include breaks(desktop) { + width: 10em; + margin-top: 0; + } + } + textarea { + width: 100%; + } +} diff --git a/src/styles/partials/_previews.scss b/src/styles/partials/_previews.scss new file mode 100644 index 0000000..142b5d5 --- /dev/null +++ b/src/styles/partials/_previews.scss @@ -0,0 +1,59 @@ +.previews { + @include flex-container(flex,column,nowrap,center,center,center); + margin: 0 20% 2em 20%; + padding: 2em 0; + article { + width: 100%; + border-bottom: 1px solid $border-color-med; + padding: 2em 0; + } + h1 { + font-family: $main-font-regular; + font-size: 200%; + margin-bottom: 1em; + } + p { + font-family: $main-font-regular; + font-size: 100%; + line-height: 1.7; + margin: 0 0 2em 0; + } + .post-data { + @include flex-container(flex,row,wrap,space-between,center,center); + } + .btn { + width: 100%; + margin-top: 1em; + &.btn--light { + width: 15em; + margin-top: 1em; + } + @include breaks(desktop) { + width: 10em; + margin-top: 0; + } + } + button[ng-click="vm.delete(post)"] { + font-size: 30%; + } + ul { + @include flex-container(flex,row,wrap,center,center,center); + } + li { + margin: 0.5em 1em; + display: inline; + } + li:before { + content: ""; + display: inline-block; + position: relative; + bottom: 0.25em; + right: 1em; + @include breaks(desktop) { + content: "."; + } + } + li:first-child:before { + content: ""; + } +} diff --git a/src/styles/partials/_sec-nav.scss b/src/styles/partials/_sec-nav.scss new file mode 100644 index 0000000..de31a97 --- /dev/null +++ b/src/styles/partials/_sec-nav.scss @@ -0,0 +1,46 @@ +.secondary-nav { + color: $text-color-med; + padding: 1em 8% 2em 8%; + border-top: 1px solid $border-color-med; + border-bottom: 1px solid $border-color-med; + p { + display: inline-block; + margin-right: 0.5em; + } +} +.secondary-nav__justify-L { + font-family: $main-font-bold; + float: left; + color: $text-color-dark; + a { + margin: 0 0.25em; + } + a:before { + content: "."; + display: inline-block; + position: relative; + bottom: 0.25em; + right: 0.4em; + } + a:first-child:before { + content: ""; + } +} +.secondary-nav__justify-R { + font-family: $main-font-medium; + float: right; +} +.secondary-nav__icon { + margin-left: 0.5em; +} +.secondary-nav__icon:before { + @include flex-container(flex,column,nowrap,center,center,center); + font-size: 60%; + border-radius: 50%; + display: inline-block; + height: 1em; + width: 1em; + color: $text-color-light; + background-color: $background-color-med; + padding: 0.5em; +} \ No newline at end of file diff --git a/src/webpack-entry.js b/src/webpack-entry.js new file mode 100644 index 0000000..85a87a5 --- /dev/null +++ b/src/webpack-entry.js @@ -0,0 +1,2 @@ +require('./app/app.js'); +require('./app/index.js');