├── .gitignore ├── README.md ├── client ├── .babelrc ├── .editorconfig ├── .eslintrc.js ├── .gitignore ├── README.md ├── build │ ├── build.js │ ├── css-loaders.js │ ├── dev-client.js │ ├── dev-server.js │ ├── webpack.base.conf.js │ ├── webpack.dev.conf.js │ └── webpack.prod.conf.js ├── config.js ├── index.html ├── package.json ├── src │ ├── App.vue │ ├── assets │ │ └── logo.png │ ├── components │ │ ├── Home.vue │ │ ├── LogTime.vue │ │ ├── Sidebar.vue │ │ └── TimeEntries.vue │ └── main.js ├── static │ └── .gitkeep └── test │ ├── e2e │ ├── custom-assertions │ │ └── elementCount.js │ ├── nightwatch.conf.js │ ├── runner.js │ └── specs │ │ └── test.js │ └── unit │ ├── .eslintrc │ ├── index.js │ ├── karma.conf.js │ └── specs │ └── Hello.spec.js └── server └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | npm-debug.log 5 | selenium-debug.log 6 | test/unit/coverage 7 | test/e2e/reports 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vue.js + Node Time Tracker 2 | 3 | This repo goes along with the Vue.js + Node time tracker tutorial available at [Scotch.io](https://scotch.io). 4 | 5 |  6 | 7 | ## Install and Run 8 | 9 | ```bash 10 | cd client 11 | npm install 12 | npm run dev 13 | ``` 14 | 15 | **Note:** The server code isn't ready yet :) 16 | 17 | The app will be served at `localhost:8080`. 18 | 19 | ## License 20 | 21 | MIT 22 | -------------------------------------------------------------------------------- /client/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2"], 3 | "plugins": ["transform-runtime"], 4 | "comments": false 5 | } 6 | -------------------------------------------------------------------------------- /client/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /client/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style 4 | extends: 'standard', 5 | // required to lint *.vue files 6 | plugins: [ 7 | 'html' 8 | ], 9 | // add your custom rules here 10 | 'rules': { 11 | // allow paren-less arrow functions 12 | 'arrow-parens': 0, 13 | // allow debugger during development 14 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | npm-debug.log 5 | selenium-debug.log 6 | test/unit/coverage 7 | test/e2e/reports 8 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # Vue.js + Node Time Tracker 2 | 3 | This repo goes along with the Vue.js + Node time tracker tutorial available at [Scotch.io](https://scotch.io). 4 | 5 |  6 | 7 | ## Install and Run 8 | 9 | ```bash 10 | npm install 11 | npm run dev 12 | ``` 13 | 14 | **Note:** The server code isn't ready yet :) 15 | 16 | The app will be served at `localhost:8080`. 17 | 18 | ## License 19 | 20 | MIT 21 | -------------------------------------------------------------------------------- /client/build/build.js: -------------------------------------------------------------------------------- 1 | // https://github.com/shelljs/shelljs 2 | require('shelljs/global') 3 | env.NODE_ENV = 'production' 4 | 5 | var path = require('path') 6 | var config = require('../config') 7 | var ora = require('ora') 8 | var webpack = require('webpack') 9 | var webpackConfig = require('./webpack.prod.conf') 10 | 11 | console.log( 12 | ' Tip:\n' + 13 | ' Built files are meant to be served over an HTTP server.\n' + 14 | ' Opening index.html over file:// won\'t work.\n' 15 | ) 16 | 17 | var spinner = ora('building for production...') 18 | spinner.start() 19 | 20 | var assetsPath = path.join(config.build.assetsRoot, config.build.assetsSubDirectory) 21 | rm('-rf', assetsPath) 22 | mkdir('-p', assetsPath) 23 | cp('-R', 'static/', assetsPath) 24 | 25 | webpack(webpackConfig, function (err, stats) { 26 | spinner.stop() 27 | if (err) throw err 28 | process.stdout.write(stats.toString({ 29 | colors: true, 30 | modules: false, 31 | children: false, 32 | chunks: false, 33 | chunkModules: false 34 | }) + '\n') 35 | }) 36 | -------------------------------------------------------------------------------- /client/build/css-loaders.js: -------------------------------------------------------------------------------- 1 | var ExtractTextPlugin = require('extract-text-webpack-plugin') 2 | 3 | module.exports = function (options) { 4 | options = options || {} 5 | // generate loader string to be used with extract text plugin 6 | function generateLoaders (loaders) { 7 | var sourceLoader = loaders.map(function (loader) { 8 | var extraParamChar 9 | if (/\?/.test(loader)) { 10 | loader = loader.replace(/\?/, '-loader?') 11 | extraParamChar = '&' 12 | } else { 13 | loader = loader + '-loader' 14 | extraParamChar = '?' 15 | } 16 | return loader + (options.sourceMap ? extraParamChar + 'sourceMap' : '') 17 | }).join('!') 18 | 19 | if (options.extract) { 20 | return ExtractTextPlugin.extract('vue-style-loader', sourceLoader) 21 | } else { 22 | return ['vue-style-loader', sourceLoader].join('!') 23 | } 24 | } 25 | 26 | // http://vuejs.github.io/vue-loader/configurations/extract-css.html 27 | return { 28 | css: generateLoaders(['css']), 29 | postcss: generateLoaders(['css']), 30 | less: generateLoaders(['css', 'less']), 31 | sass: generateLoaders(['css', 'sass?indentedSyntax']), 32 | scss: generateLoaders(['css', 'sass']), 33 | stylus: generateLoaders(['css', 'stylus']), 34 | styl: generateLoaders(['css', 'stylus']) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /client/build/dev-client.js: -------------------------------------------------------------------------------- 1 | require('eventsource-polyfill') 2 | var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true') 3 | 4 | hotClient.subscribe(function (event) { 5 | if (event.action === 'reload') { 6 | window.location.reload() 7 | } 8 | }) 9 | -------------------------------------------------------------------------------- /client/build/dev-server.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var express = require('express') 3 | var webpack = require('webpack') 4 | var config = require('../config') 5 | var proxyMiddleware = require('http-proxy-middleware') 6 | var webpackConfig = process.env.NODE_ENV === 'testing' 7 | ? require('./webpack.prod.conf') 8 | : require('./webpack.dev.conf') 9 | 10 | // default port where dev server listens for incoming traffic 11 | var port = process.env.PORT || config.dev.port 12 | // Define HTTP proxies to your custom API backend 13 | // https://github.com/chimurai/http-proxy-middleware 14 | var proxyTable = config.dev.proxyTable 15 | 16 | var app = express() 17 | var compiler = webpack(webpackConfig) 18 | 19 | var devMiddleware = require('webpack-dev-middleware')(compiler, { 20 | publicPath: webpackConfig.output.publicPath, 21 | stats: { 22 | colors: true, 23 | chunks: false 24 | } 25 | }) 26 | 27 | var hotMiddleware = require('webpack-hot-middleware')(compiler) 28 | // force page reload when html-webpack-plugin template changes 29 | compiler.plugin('compilation', function (compilation) { 30 | compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { 31 | hotMiddleware.publish({ action: 'reload' }) 32 | cb() 33 | }) 34 | }) 35 | 36 | // proxy api requests 37 | Object.keys(proxyTable).forEach(function (context) { 38 | var options = proxyTable[context] 39 | if (typeof options === 'string') { 40 | options = { target: options } 41 | } 42 | app.use(proxyMiddleware(context, options)) 43 | }) 44 | 45 | // handle fallback for HTML5 history API 46 | app.use(require('connect-history-api-fallback')()) 47 | 48 | // serve webpack bundle output 49 | app.use(devMiddleware) 50 | 51 | // enable hot-reload and state-preserving 52 | // compilation error display 53 | app.use(hotMiddleware) 54 | 55 | // serve pure static assets 56 | var staticPath = path.join(config.build.assetsPublicPath, config.build.assetsSubDirectory) 57 | app.use(staticPath, express.static('./static')) 58 | 59 | module.exports = app.listen(port, function (err) { 60 | if (err) { 61 | console.log(err) 62 | return 63 | } 64 | console.log('Listening at http://localhost:' + port + '\n') 65 | }) 66 | -------------------------------------------------------------------------------- /client/build/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var config = require('../config') 3 | var cssLoaders = require('./css-loaders') 4 | var projectRoot = path.resolve(__dirname, '../') 5 | 6 | module.exports = { 7 | entry: { 8 | app: './src/main.js' 9 | }, 10 | output: { 11 | path: config.build.assetsRoot, 12 | publicPath: config.build.assetsPublicPath, 13 | filename: '[name].js' 14 | }, 15 | resolve: { 16 | extensions: ['', '.js', '.vue'], 17 | fallback: [path.join(__dirname, '../node_modules')], 18 | alias: { 19 | 'src': path.resolve(__dirname, '../src') 20 | } 21 | }, 22 | resolveLoader: { 23 | fallback: [path.join(__dirname, '../node_modules')] 24 | }, 25 | module: { 26 | preLoaders: [ 27 | { 28 | test: /\.vue$/, 29 | loader: 'eslint', 30 | include: projectRoot, 31 | exclude: /node_modules/ 32 | }, 33 | { 34 | test: /\.js$/, 35 | loader: 'eslint', 36 | include: projectRoot, 37 | exclude: /node_modules/ 38 | } 39 | ], 40 | loaders: [ 41 | { 42 | test: /\.vue$/, 43 | loader: 'vue' 44 | }, 45 | { 46 | test: /\.js$/, 47 | loader: 'babel', 48 | include: projectRoot, 49 | exclude: /node_modules/ 50 | }, 51 | { 52 | test: /\.json$/, 53 | loader: 'json' 54 | }, 55 | { 56 | test: /\.html$/, 57 | loader: 'vue-html' 58 | }, 59 | { 60 | test: /\.(png|jpe?g|gif|svg|woff2?|eot|ttf|otf)(\?.*)?$/, 61 | loader: 'url', 62 | query: { 63 | limit: 10000, 64 | name: path.join(config.build.assetsSubDirectory, '[name].[ext]?[hash:7]') 65 | } 66 | } 67 | ] 68 | }, 69 | vue: { 70 | loaders: cssLoaders() 71 | }, 72 | eslint: { 73 | formatter: require('eslint-friendly-formatter') 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /client/build/webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack') 2 | var merge = require('webpack-merge') 3 | var baseWebpackConfig = require('./webpack.base.conf') 4 | var HtmlWebpackPlugin = require('html-webpack-plugin') 5 | 6 | // add hot-reload related code to entry chunks 7 | Object.keys(baseWebpackConfig.entry).forEach(function (name) { 8 | baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name]) 9 | }) 10 | 11 | module.exports = merge(baseWebpackConfig, { 12 | // eval-source-map is faster for development 13 | devtool: '#eval-source-map', 14 | plugins: [ 15 | // https://github.com/glenjamin/webpack-hot-middleware#installation--usage 16 | new webpack.optimize.OccurenceOrderPlugin(), 17 | new webpack.HotModuleReplacementPlugin(), 18 | new webpack.NoErrorsPlugin(), 19 | // https://github.com/ampedandwired/html-webpack-plugin 20 | new HtmlWebpackPlugin({ 21 | filename: 'index.html', 22 | template: 'index.html', 23 | inject: true 24 | }) 25 | ] 26 | }) 27 | -------------------------------------------------------------------------------- /client/build/webpack.prod.conf.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var config = require('../config') 3 | var webpack = require('webpack') 4 | var merge = require('webpack-merge') 5 | var baseWebpackConfig = require('./webpack.base.conf') 6 | var cssLoaders = require('./css-loaders') 7 | var ExtractTextPlugin = require('extract-text-webpack-plugin') 8 | var HtmlWebpackPlugin = require('html-webpack-plugin') 9 | 10 | module.exports = merge(baseWebpackConfig, { 11 | devtool: config.build.productionSourceMap ? '#source-map' : false, 12 | output: { 13 | path: config.build.assetsRoot, 14 | filename: path.join(config.build.assetsSubDirectory, '[name].[chunkhash].js'), 15 | chunkFilename: path.join(config.build.assetsSubDirectory, '[id].[chunkhash].js') 16 | }, 17 | vue: { 18 | loaders: cssLoaders({ 19 | sourceMap: config.build.productionSourceMap, 20 | extract: true 21 | }) 22 | }, 23 | plugins: [ 24 | // http://vuejs.github.io/vue-loader/workflow/production.html 25 | new webpack.DefinePlugin({ 26 | 'process.env': { 27 | NODE_ENV: '"production"' 28 | } 29 | }), 30 | new webpack.optimize.UglifyJsPlugin({ 31 | compress: { 32 | warnings: false 33 | } 34 | }), 35 | new webpack.optimize.OccurenceOrderPlugin(), 36 | // extract css into its own file 37 | new ExtractTextPlugin(path.join(config.build.assetsSubDirectory, '[name].[contenthash].css')), 38 | // generate dist index.html with correct asset hash for caching. 39 | // you can customize output by editing /index.html 40 | // see https://github.com/ampedandwired/html-webpack-plugin 41 | new HtmlWebpackPlugin({ 42 | filename: process.env.NODE_ENV === 'testing' 43 | ? 'index.html' 44 | : config.build.index, 45 | template: 'index.html', 46 | inject: true, 47 | minify: { 48 | removeComments: true, 49 | collapseWhitespace: true, 50 | removeAttributeQuotes: true 51 | // more options: 52 | // https://github.com/kangax/html-minifier#options-quick-reference 53 | } 54 | }) 55 | ] 56 | }) 57 | -------------------------------------------------------------------------------- /client/config.js: -------------------------------------------------------------------------------- 1 | // see http://vuejs-templates.github.io/webpack for documentation. 2 | var path = require('path') 3 | 4 | module.exports = { 5 | build: { 6 | index: path.resolve(__dirname, 'dist/index.html'), 7 | assetsRoot: path.resolve(__dirname, 'dist'), 8 | assetsSubDirectory: 'static', 9 | assetsPublicPath: '/', 10 | productionSourceMap: true 11 | }, 12 | dev: { 13 | port: 8080, 14 | proxyTable: {} 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |5 | 6 | Get started by creating a time entry. 7 | 8 |
9 |No time entries yet
20 | 21 | 63 |
{{ timeEntry.comment }}
49 |