├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .postcssrc.js
├── .vscode
├── extensions.json
└── settings.json
├── README.md
├── build
├── build.js
├── check-versions.js
├── dev-client.js
├── dev-server.js
├── load-minified.js
├── service-worker-dev.js
├── service-worker-prod.js
├── utils.js
├── vue-loader.conf.js
├── webpack.base.conf.js
├── webpack.dev.conf.js
└── webpack.prod.conf.js
├── config
├── dev.env.js
├── index.js
├── prod.env.js
└── test.env.js
├── index.html
├── package.json
├── src
├── app.vue
├── assets
│ ├── images
│ │ ├── backdrop.jpg
│ │ ├── logo-name.svg
│ │ ├── logo.png
│ │ ├── logo.svg
│ │ ├── oops.svg
│ │ ├── profile.jpg
│ │ └── traffic-cone.svg
│ └── logo.png
├── auth
│ ├── helpers.js
│ ├── index.js
│ └── store.js
├── components
│ ├── Hello.vue
│ ├── app-bar.vue
│ ├── app-dialog.vue
│ ├── app-footer.vue
│ ├── app-sidebar.vue
│ ├── app-snackbar.vue
│ └── loading.vue
├── constants
│ └── index.js
├── features
│ ├── account
│ │ ├── components
│ │ │ ├── address-edit.vue
│ │ │ └── countries.js
│ │ ├── main.vue
│ │ ├── service.js
│ │ └── store.js
│ ├── billing
│ │ └── main.vue
│ ├── dashboard
│ │ ├── components
│ │ │ ├── chart.vue
│ │ │ └── line-chart.vue
│ │ ├── main.vue
│ │ ├── service.js
│ │ ├── store.js
│ │ └── test.css
│ ├── login
│ │ ├── main.vue
│ │ ├── service.js
│ │ └── store.js
│ ├── premium
│ │ └── main.vue
│ ├── tutorial
│ │ └── main.vue
│ └── wip
│ │ └── main.vue
├── http
│ ├── index.js
│ ├── router.js
│ └── routes.js
├── layouts
│ ├── default
│ │ └── main.vue
│ └── public
│ │ └── main.vue
├── main.js
├── store
│ ├── common.js
│ ├── index.js
│ └── plugins.js
└── styles
│ ├── scss
│ ├── _variables.scss
│ ├── _vendor.scss
│ └── main.scss
│ └── stylus
│ ├── 1-settings
│ ├── 1-settings.styl
│ ├── variables.styl
│ └── vendor.styl
│ ├── 2-tools
│ └── 2-tools.styl
│ ├── 3-generic
│ └── 3-generic.styl
│ ├── 4-elements
│ └── 4-elements.styl
│ ├── 5-objects
│ └── 5-objects.styl
│ ├── 6-components
│ ├── 6-components.styl
│ └── vendor.styl
│ ├── 7-utils
│ └── 7-utils.styl
│ └── main.styl
├── static
├── images
│ └── mountains.png
├── img
│ └── icons
│ │ ├── android-chrome-192x192.png
│ │ ├── android-chrome-512x512.png
│ │ ├── apple-touch-icon-120x120.png
│ │ ├── apple-touch-icon-152x152.png
│ │ ├── apple-touch-icon-180x180.png
│ │ ├── apple-touch-icon-60x60.png
│ │ ├── apple-touch-icon-76x76.png
│ │ ├── apple-touch-icon.png
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ ├── favicon.ico
│ │ ├── msapplication-icon-144x144.png
│ │ ├── mstile-150x150.png
│ │ └── safari-pinned-tab.svg
└── manifest.json
└── test
└── e2e
├── custom-assertions
└── elementCount.js
├── nightwatch.conf.js
├── runner.js
└── specs
└── test.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", {
4 | "modules": false,
5 | "targets": {
6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
7 | }
8 | }],
9 | "stage-2"
10 | ],
11 | "plugins": ["transform-runtime"],
12 | "env": {
13 | "test": {
14 | "presets": ["env", "stage-2"],
15 | "plugins": [ "istanbul" ]
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /build/
2 | /config/
3 | /dist/
4 | /*.js
5 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | // http://eslint.org/docs/user-guide/configuring
2 |
3 | module.exports = {
4 | root: true,
5 | parserOptions: {
6 | parser: 'babel-eslint',
7 | sourceType: 'module'
8 | },
9 | env: {
10 | browser: true,
11 | jest: true
12 | },
13 | globals: {
14 | utils: true
15 | },
16 | // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
17 | // https://github.com/vuejs/eslint-plugin-vue
18 | extends: [
19 | 'standard',
20 | 'plugin:vue/recommended'
21 | ],
22 | // required to lint *.vue files
23 | plugins: [
24 | 'vue'
25 | ],
26 | // add your custom rules here
27 | 'rules': {
28 | // allow paren-less arrow functions
29 | 'arrow-parens': 0,
30 | // allow async-await
31 | 'generator-star-spacing': 0,
32 | // allow debugger during development
33 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /node_modules/
3 | /dist/
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | /test/e2e/reports
8 | selenium-debug.log
9 | package-lock.json
10 |
11 | # Editor directories and files
12 | .idea
13 | *.suo
14 | *.ntvs*
15 | *.njsproj
16 | *.sln
17 |
--------------------------------------------------------------------------------
/.postcssrc.js:
--------------------------------------------------------------------------------
1 | // https://github.com/michael-ciniawsky/postcss-load-config
2 |
3 | module.exports = {
4 | "plugins": {
5 | // to edit target browsers: use "browserlist" field in package.json
6 | "autoprefixer": {}
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // See http://go.microsoft.com/fwlink/?LinkId=827846
3 | // for the documentation about the extensions.json format
4 | "recommendations": [
5 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
6 | "dbaeumer.vscode-eslint",
7 | "sysoev.language-stylus",
8 | "octref.vetur"
9 | ]
10 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // Place your settings in this file to override the default and user settings
2 | {
3 | // Settings for .js, .vue, .styl files.
4 | "[javascript]": {
5 | "editor.tabSize": 2,
6 | "editor.insertSpaces": true
7 | },
8 | "[vue]": {
9 | "editor.tabSize": 2,
10 | "editor.insertSpaces": true
11 | },
12 | "[stylus]": {
13 | "editor.tabSize": 2,
14 | "editor.insertSpaces": true
15 | },
16 |
17 | // This is required for ESLint to work in Vue in VS Code.
18 | "eslint.options": {
19 | "extensions": [".html", ".js", ".vue", ".jsx"]
20 | },
21 | "eslint.validate": [
22 | {
23 | "language": "html",
24 | "autoFix": true
25 | },
26 | {
27 | "language": "vue",
28 | "autoFix": true
29 | },
30 | {
31 | "language": "javascript",
32 | "autoFix": true
33 | },
34 | {
35 | "language": "javascriptreact",
36 | "autoFix": true
37 | }
38 | ],
39 |
40 | // When you hit ctrl+e to search, you don't want node_modules to be included.
41 | "search.exclude": {
42 | "**/.git": true,
43 | "**/node_modules": true,
44 | "**/tmp": true
45 | }
46 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # :pizza: Vue Pizza
2 |
3 | > A tasty Vue.js starter project. Clone, remove `vue-pizza` everywhere, and add your own features. Also, read through the wiki.
4 |
5 | ## Demo
6 |
7 | [https://vue.pizza/app](https://vue.pizza/app)
8 |
9 | ## Why?
10 |
11 | A lot of starter projects or examples of Vue.js in the wild were:
12 |
13 | - Outdated.
14 | - No tutorial or wiki.
15 | - Involved unneccesary, complex, server-side-rendering/NUXT setups (this project is for authenticated, no SEO needed, static/cacheable apps that communicate to a backend server API).
16 | - Not comprehensive and didn't cover most of the elements necessary for real world apps.
17 | - This project was started from the latest official Vue-cli PWA template and built up from there.
18 |
19 | ## Wiki
20 |
21 | Visit the Github wiki tab [here](https://github.com/prograhammer/vue-pizza/wiki) to learn everything you need to know about
22 | how this starter project was built (which allows you to learn and rebuild this project from scratch if you want).
23 |
24 | ## Installation
25 |
26 | Very simple. See the Installation section in [wiki](https://github.com/prograhammer/vue-pizza/wiki).
27 |
28 |
--------------------------------------------------------------------------------
/build/build.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | require('./check-versions')()
4 |
5 | process.env.NODE_ENV = 'production'
6 |
7 | const ora = require('ora')
8 | const rm = require('rimraf')
9 | const path = require('path')
10 | const chalk = require('chalk')
11 | const webpack = require('webpack')
12 | const config = require('../config')
13 | const webpackConfig = require('./webpack.prod.conf')
14 |
15 | const spinner = ora('building for production...')
16 | spinner.start()
17 |
18 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
19 | if (err) throw err
20 | webpack(webpackConfig, function (err, stats) {
21 | spinner.stop()
22 | if (err) throw err
23 | process.stdout.write(stats.toString({
24 | colors: true,
25 | modules: false,
26 | children: false,
27 | chunks: false,
28 | chunkModules: false
29 | }) + '\n\n')
30 |
31 | console.log(chalk.cyan(' Build complete.\n'))
32 | console.log(chalk.yellow(
33 | ' Tip: built files are meant to be served over an HTTP server.\n' +
34 | ' Opening index.html over file:// won\'t work.\n'
35 | ))
36 | })
37 | })
38 |
--------------------------------------------------------------------------------
/build/check-versions.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const chalk = require('chalk')
4 | const semver = require('semver')
5 | const packageConfig = require('../package.json')
6 | const shell = require('shelljs')
7 | function exec (cmd) {
8 | return require('child_process').execSync(cmd).toString().trim()
9 | }
10 |
11 | const versionRequirements = [
12 | {
13 | name: 'node',
14 | currentVersion: semver.clean(process.version),
15 | versionRequirement: packageConfig.engines.node
16 | },
17 | ]
18 |
19 | if (shell.which('npm')) {
20 | versionRequirements.push({
21 | name: 'npm',
22 | currentVersion: exec('npm --version'),
23 | versionRequirement: packageConfig.engines.npm
24 | })
25 | }
26 |
27 | module.exports = function () {
28 | const warnings = []
29 | for (let i = 0; i < versionRequirements.length; i++) {
30 | const mod = versionRequirements[i]
31 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
32 | warnings.push(mod.name + ': ' +
33 | chalk.red(mod.currentVersion) + ' should be ' +
34 | chalk.green(mod.versionRequirement)
35 | )
36 | }
37 | }
38 |
39 | if (warnings.length) {
40 | console.log('')
41 | console.log(chalk.yellow('To use this template, you must update following to modules:'))
42 | console.log()
43 | for (let i = 0; i < warnings.length; i++) {
44 | const warning = warnings[i]
45 | console.log(' ' + warning)
46 | }
47 | console.log()
48 | process.exit(1)
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/build/dev-client.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | /* eslint-disable */
4 | require('eventsource-polyfill')
5 | var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
6 |
7 | hotClient.subscribe(function (event) {
8 | if (event.action === 'reload') {
9 | window.location.reload()
10 | }
11 | })
12 |
--------------------------------------------------------------------------------
/build/dev-server.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | require('./check-versions')()
4 |
5 | const config = require('../config')
6 | if (!process.env.NODE_ENV) {
7 | process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
8 | }
9 |
10 | const opn = require('opn')
11 | const path = require('path')
12 | const express = require('express')
13 | const webpack = require('webpack')
14 | const proxyMiddleware = require('http-proxy-middleware')
15 | const webpackConfig = process.env.NODE_ENV === 'testing'
16 | ? require('./webpack.prod.conf')
17 | : require('./webpack.dev.conf')
18 |
19 | // default port where dev server listens for incoming traffic
20 | const port = process.env.PORT || config.dev.port
21 | // automatically open browser, if not set will be false
22 | const autoOpenBrowser = !!config.dev.autoOpenBrowser
23 | // Define HTTP proxies to your custom API backend
24 | // https://github.com/chimurai/http-proxy-middleware
25 | const proxyTable = config.dev.proxyTable
26 |
27 | const app = express()
28 | const compiler = webpack(webpackConfig)
29 |
30 | const devMiddleware = require('webpack-dev-middleware')(compiler, {
31 | publicPath: webpackConfig.output.publicPath,
32 | quiet: true
33 | })
34 |
35 | const hotMiddleware = require('webpack-hot-middleware')(compiler, {
36 | log: false
37 | })
38 | // force page reload when html-webpack-plugin template changes
39 | compiler.plugin('compilation', function (compilation) {
40 | compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
41 | hotMiddleware.publish({ action: 'reload' })
42 | cb()
43 | })
44 | })
45 |
46 | // enable hot-reload and state-preserving
47 | // compilation error display
48 | app.use(hotMiddleware)
49 |
50 | // proxy api requests
51 | Object.keys(proxyTable).forEach(function (context) {
52 | let options = proxyTable[context]
53 | if (typeof options === 'string') {
54 | options = { target: options }
55 | }
56 | app.use(proxyMiddleware(options.filter || context, options))
57 | })
58 |
59 | // handle fallback for HTML5 history API
60 | app.use(require('connect-history-api-fallback')())
61 |
62 | // serve webpack bundle output
63 | app.use(devMiddleware)
64 |
65 | // serve pure static assets
66 | const staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
67 | app.use(staticPath, express.static('./static'))
68 |
69 | const uri = 'http://localhost:' + port
70 |
71 | let _resolve
72 | const readyPromise = new Promise(resolve => {
73 | _resolve = resolve
74 | })
75 |
76 | console.log('> Starting dev server...')
77 | devMiddleware.waitUntilValid(() => {
78 | console.log('> Listening at ' + uri + '\n')
79 | // when env is testing, don't need open it
80 | if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
81 | opn(uri)
82 | }
83 | _resolve()
84 | })
85 |
86 | const server = app.listen(port)
87 |
88 | module.exports = {
89 | ready: readyPromise,
90 | close: () => {
91 | server.close()
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/build/load-minified.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const fs = require('fs')
4 | const UglifyJS = require('uglify-es')
5 |
6 | module.exports = function(filePath) {
7 | const code = fs.readFileSync(filePath, 'utf-8')
8 | const result = UglifyJS.minify(code)
9 | if (result.error) return ''
10 | return result.code
11 | }
12 |
--------------------------------------------------------------------------------
/build/service-worker-dev.js:
--------------------------------------------------------------------------------
1 | // This service worker file is effectively a 'no-op' that will reset any
2 | // previous service worker registered for the same host:port combination.
3 | // In the production build, this file is replaced with an actual service worker
4 | // file that will precache your site's local assets.
5 | // See https://github.com/facebookincubator/create-react-app/issues/2272#issuecomment-302832432
6 |
7 | self.addEventListener('install', () => self.skipWaiting());
8 |
9 | self.addEventListener('activate', () => {
10 | self.clients.matchAll({ type: 'window' }).then(windowClients => {
11 | for (let windowClient of windowClients) {
12 | // Force open pages to refresh, so that they have a chance to load the
13 | // fresh navigation response from the local dev server.
14 | windowClient.navigate(windowClient.url);
15 | }
16 | });
17 | });
--------------------------------------------------------------------------------
/build/service-worker-prod.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 |
4 | // Check to make sure service workers are supported in the current browser,
5 | // and that the current page is accessed from a secure origin. Using a
6 | // service worker from an insecure origin will trigger JS console errors.
7 | var isLocalhost = Boolean(window.location.hostname === 'localhost' ||
8 | // [::1] is the IPv6 localhost address.
9 | window.location.hostname === '[::1]' ||
10 | // 127.0.0.1/8 is considered localhost for IPv4.
11 | window.location.hostname.match(
12 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
13 | )
14 | );
15 |
16 | window.addEventListener('load', function() {
17 | if ('serviceWorker' in navigator &&
18 | (window.location.protocol === 'https:' || isLocalhost)) {
19 | navigator.serviceWorker.register('service-worker.js')
20 | .then(function(registration) {
21 | // updatefound is fired if service-worker.js changes.
22 | registration.onupdatefound = function() {
23 | // updatefound is also fired the very first time the SW is installed,
24 | // and there's no need to prompt for a reload at that point.
25 | // So check here to see if the page is already controlled,
26 | // i.e. whether there's an existing service worker.
27 | if (navigator.serviceWorker.controller) {
28 | // The updatefound event implies that registration.installing is set
29 | var installingWorker = registration.installing;
30 |
31 | installingWorker.onstatechange = function() {
32 | switch (installingWorker.state) {
33 | case 'installed':
34 | // At this point, the old content will have been purged and the
35 | // fresh content will have been added to the cache.
36 | // It's the perfect time to display a "New content is
37 | // available; please refresh." message in the page's interface.
38 | break;
39 |
40 | case 'redundant':
41 | throw new Error('The installing ' +
42 | 'service worker became redundant.');
43 |
44 | default:
45 | // Ignore
46 | }
47 | };
48 | }
49 | };
50 | }).catch(function(e) {
51 | console.error('Error during service worker registration:', e);
52 | });
53 | }
54 | });
55 | })();
56 |
--------------------------------------------------------------------------------
/build/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const path = require('path')
4 | const config = require('../config')
5 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
6 |
7 | exports.assetsPath = function (_path) {
8 | const assetsSubDirectory = process.env.NODE_ENV === 'production'
9 | ? config.build.assetsSubDirectory
10 | : config.dev.assetsSubDirectory
11 | return path.posix.join(assetsSubDirectory, _path)
12 | }
13 |
14 | exports.cssLoaders = function (options) {
15 | options = options || {}
16 |
17 | const cssLoader = {
18 | loader: 'css-loader',
19 | options: {
20 | minimize: process.env.NODE_ENV === 'production',
21 | sourceMap: options.sourceMap
22 | }
23 | }
24 |
25 | // generate loader string to be used with extract text plugin
26 | function generateLoaders (loader, loaderOptions) {
27 | const loaders = [cssLoader]
28 | if (loader) {
29 | loaders.push({
30 | loader: loader + '-loader',
31 | options: Object.assign({}, loaderOptions, {
32 | sourceMap: options.sourceMap
33 | })
34 | })
35 | }
36 |
37 | // Extract CSS when that option is specified
38 | // (which is the case during production build)
39 | if (options.extract) {
40 | return ExtractTextPlugin.extract({
41 | use: loaders,
42 | fallback: 'vue-style-loader'
43 | })
44 | } else {
45 | return ['vue-style-loader'].concat(loaders)
46 | }
47 | }
48 |
49 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html
50 | return {
51 | css: generateLoaders(),
52 | postcss: generateLoaders(),
53 | less: generateLoaders('less'),
54 | sass: generateLoaders('sass', { indentedSyntax: true }),
55 | scss: generateLoaders('sass'),
56 | stylus: generateLoaders('stylus', {
57 | preferPathResolver: 'webpack',
58 | import: [
59 | '~@/styles/stylus/1-settings/1-settings.styl', // <-- Load these files into every stylus file.
60 | '~@/styles/stylus/2-tools/2-tools.styl', // Only variables/functions so output CSS is not increased.
61 | ]
62 | }),
63 | styl: generateLoaders('stylus', {
64 | preferPathResolver: 'webpack',
65 | import: [
66 | '~@/styles/stylus/1-settings/1-settings.styl', // <-- Load these files into every stylus file.
67 | '~@/styles/stylus/2-tools/2-tools.styl', // Only variables/functions so output CSS is not increased.
68 | ]
69 | })
70 | }
71 | }
72 |
73 | // Generate loaders for standalone style files (outside of .vue)
74 | exports.styleLoaders = function (options) {
75 | const output = []
76 | const loaders = exports.cssLoaders(options)
77 | for (const extension in loaders) {
78 | const loader = loaders[extension]
79 | output.push({
80 | test: new RegExp('\\.' + extension + '$'),
81 | use: loader
82 | })
83 | }
84 | return output
85 | }
86 |
--------------------------------------------------------------------------------
/build/vue-loader.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const utils = require('./utils')
4 | const config = require('../config')
5 | const isProduction = process.env.NODE_ENV === 'production'
6 |
7 | module.exports = {
8 | loaders: utils.cssLoaders({
9 | sourceMap: isProduction
10 | ? config.build.productionSourceMap
11 | : config.dev.cssSourceMap,
12 | extract: isProduction
13 | }),
14 | transformToRequire: {
15 | video: ['src', 'poster'],
16 | source: 'src',
17 | img: 'src',
18 | image: 'xlink:href'
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/build/webpack.base.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const path = require('path')
4 | const utils = require('./utils')
5 | const config = require('../config')
6 | const vueLoaderConfig = require('./vue-loader.conf')
7 |
8 | function resolve (dir) {
9 | return path.join(__dirname, '..', dir)
10 | }
11 |
12 | module.exports = {
13 | entry: {
14 | app: './src/main.js'
15 | },
16 | output: {
17 | path: config.build.assetsRoot,
18 | filename: '[name].js',
19 | publicPath: process.env.NODE_ENV === 'production'
20 | ? config.build.assetsPublicPath
21 | : config.dev.assetsPublicPath
22 | },
23 | resolve: {
24 | extensions: ['.js', '.vue', '.json'],
25 | alias: {
26 | '@': resolve('src')
27 | }
28 | },
29 | module: {
30 | rules: [
31 | {
32 | test: /\.(js|vue)$/,
33 | loader: 'eslint-loader',
34 | enforce: 'pre',
35 | include: [resolve('src'), resolve('test')],
36 | options: {
37 | formatter: require('eslint-friendly-formatter')
38 | }
39 | },
40 | {
41 | test: /\.vue$/,
42 | loader: 'vue-loader',
43 | options: vueLoaderConfig
44 | },
45 | {
46 | test: /\.js$/,
47 | loader: 'babel-loader',
48 | include: [resolve('src'), resolve('test')]
49 | },
50 | {
51 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
52 | loader: 'url-loader',
53 | options: {
54 | limit: 10000,
55 | name: utils.assetsPath('img/[name].[hash:7].[ext]')
56 | }
57 | },
58 | {
59 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
60 | loader: 'url-loader',
61 | options: {
62 | limit: 10000,
63 | name: utils.assetsPath('media/[name].[hash:7].[ext]')
64 | }
65 | },
66 | {
67 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
68 | loader: 'url-loader',
69 | options: {
70 | limit: 10000,
71 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
72 | }
73 | }
74 | ]
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/build/webpack.dev.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const fs = require('fs')
4 | const path = require('path')
5 | const utils = require('./utils')
6 | const webpack = require('webpack')
7 | const config = require('../config')
8 | const merge = require('webpack-merge')
9 | const baseWebpackConfig = require('./webpack.base.conf')
10 | const HtmlWebpackPlugin = require('html-webpack-plugin')
11 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
12 |
13 | // add hot-reload related code to entry chunks
14 | Object.keys(baseWebpackConfig.entry).forEach(function (name) {
15 | baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
16 | })
17 |
18 | module.exports = merge(baseWebpackConfig, {
19 | module: {
20 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
21 | },
22 | // cheap-module-eval-source-map is faster for development
23 | devtool: '#cheap-module-eval-source-map',
24 | plugins: [
25 | new webpack.DefinePlugin({
26 | 'process.env': config.dev.env
27 | }),
28 | // https://github.com/glenjamin/webpack-hot-middleware#installation--usage
29 | new webpack.HotModuleReplacementPlugin(),
30 | new webpack.NoEmitOnErrorsPlugin(),
31 | // https://github.com/ampedandwired/html-webpack-plugin
32 | new HtmlWebpackPlugin({
33 | filename: 'index.html',
34 | template: 'index.html',
35 | inject: true,
36 | serviceWorkerLoader: ``
38 | }),
39 | new FriendlyErrorsPlugin()
40 | ]
41 | })
42 |
--------------------------------------------------------------------------------
/build/webpack.prod.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const fs = require('fs')
4 | const path = require('path')
5 | const utils = require('./utils')
6 | const webpack = require('webpack')
7 | const config = require('../config')
8 | const merge = require('webpack-merge')
9 | const baseWebpackConfig = require('./webpack.base.conf')
10 | const CopyWebpackPlugin = require('copy-webpack-plugin')
11 | const HtmlWebpackPlugin = require('html-webpack-plugin')
12 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
13 | const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
14 | const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin')
15 | const loadMinified = require('./load-minified')
16 |
17 | const env = process.env.NODE_ENV === 'testing'
18 | ? require('../config/test.env')
19 | : config.build.env
20 |
21 | const webpackConfig = merge(baseWebpackConfig, {
22 | module: {
23 | rules: utils.styleLoaders({
24 | sourceMap: config.build.productionSourceMap,
25 | extract: true
26 | })
27 | },
28 | devtool: config.build.productionSourceMap ? '#source-map' : false,
29 | output: {
30 | path: config.build.assetsRoot,
31 | filename: utils.assetsPath('js/[name].[chunkhash].js'),
32 | chunkFilename: utils.assetsPath('js/[name].[chunkhash].js')
33 | },
34 | plugins: [
35 | // http://vuejs.github.io/vue-loader/en/workflow/production.html
36 | new webpack.DefinePlugin({
37 | 'process.env': env
38 | }),
39 | new webpack.optimize.UglifyJsPlugin({
40 | compress: {
41 | warnings: false
42 | },
43 | sourceMap: true
44 | }),
45 | // extract css into its own file
46 | new ExtractTextPlugin({
47 | filename: utils.assetsPath('css/[name].[contenthash].css')
48 | }),
49 | // Compress extracted CSS. We are using this plugin so that possible
50 | // duplicated CSS from different components can be deduped.
51 | new OptimizeCSSPlugin({
52 | cssProcessorOptions: {
53 | safe: true
54 | }
55 | }),
56 | // generate dist index.html with correct asset hash for caching.
57 | // you can customize output by editing /index.html
58 | // see https://github.com/ampedandwired/html-webpack-plugin
59 | new HtmlWebpackPlugin({
60 | filename: process.env.NODE_ENV === 'testing'
61 | ? 'index.html'
62 | : config.build.index,
63 | template: 'index.html',
64 | inject: true,
65 | minify: {
66 | removeComments: true,
67 | collapseWhitespace: true,
68 | removeAttributeQuotes: true
69 | // more options:
70 | // https://github.com/kangax/html-minifier#options-quick-reference
71 | },
72 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin
73 | chunksSortMode: 'dependency',
74 | serviceWorkerLoader: ``
76 | }),
77 | // split vendor js into its own file
78 | new webpack.optimize.CommonsChunkPlugin({
79 | name: 'vendor',
80 | minChunks: function (module, count) {
81 | // any required modules inside node_modules are extracted to vendor
82 | return (
83 | module.resource &&
84 | /\.js$/.test(module.resource) &&
85 | module.resource.indexOf(
86 | path.join(__dirname, '../node_modules')
87 | ) === 0
88 | )
89 | }
90 | }),
91 | // extract webpack runtime and module manifest to its own file in order to
92 | // prevent vendor hash from being updated whenever app bundle is updated
93 | new webpack.optimize.CommonsChunkPlugin({
94 | name: 'manifest',
95 | chunks: ['vendor']
96 | }),
97 | // copy custom static assets
98 | new CopyWebpackPlugin([
99 | {
100 | from: path.resolve(__dirname, '../static'),
101 | to: config.build.assetsSubDirectory,
102 | ignore: ['.*']
103 | }
104 | ]),
105 | // service worker caching
106 | new SWPrecacheWebpackPlugin({
107 | cacheId: 'vue-pizza',
108 | filename: 'service-worker.js',
109 | staticFileGlobs: ['dist/**/*.{js,html,css}'],
110 | minify: true,
111 | stripPrefix: 'dist/'
112 | })
113 | ]
114 | })
115 |
116 | if (config.build.productionGzip) {
117 | const CompressionWebpackPlugin = require('compression-webpack-plugin')
118 |
119 | webpackConfig.plugins.push(
120 | new CompressionWebpackPlugin({
121 | asset: '[path].gz[query]',
122 | algorithm: 'gzip',
123 | test: new RegExp(
124 | '\\.(' +
125 | config.build.productionGzipExtensions.join('|') +
126 | ')$'
127 | ),
128 | threshold: 10240,
129 | minRatio: 0.8
130 | })
131 | )
132 | }
133 |
134 | if (config.build.bundleAnalyzerReport) {
135 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
136 | webpackConfig.plugins.push(new BundleAnalyzerPlugin())
137 | }
138 |
139 | module.exports = webpackConfig
140 |
--------------------------------------------------------------------------------
/config/dev.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const merge = require('webpack-merge')
4 | const prodEnv = require('./prod.env')
5 |
6 | module.exports = merge(prodEnv, {
7 | NODE_ENV: '"development"'
8 | })
9 |
--------------------------------------------------------------------------------
/config/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | // see http://vuejs-templates.github.io/webpack for documentation.
4 | const path = require('path')
5 |
6 | module.exports = {
7 | build: {
8 | env: require('./prod.env'),
9 | index: path.resolve(__dirname, '../dist/index.html'),
10 | assetsRoot: path.resolve(__dirname, '../dist'),
11 | assetsSubDirectory: 'static',
12 | assetsPublicPath: '/app/',
13 | productionSourceMap: true,
14 | // Gzip off by default as many popular static hosts such as
15 | // Surge or Netlify already gzip all static assets for you.
16 | // Before setting to `true`, make sure to:
17 | // npm install --save-dev compression-webpack-plugin
18 | productionGzip: false,
19 | productionGzipExtensions: ['js', 'css'],
20 | // Run the build command with an extra argument to
21 | // View the bundle analyzer report after build finishes:
22 | // `npm run build --report`
23 | // Set to `true` or `false` to always turn it on or off
24 | bundleAnalyzerReport: process.env.npm_config_report
25 | },
26 | dev: {
27 | env: require('./dev.env'),
28 | port: 8080,
29 | autoOpenBrowser: true,
30 | assetsSubDirectory: 'static',
31 | assetsPublicPath: '/',
32 | proxyTable: {
33 | '/auth': {
34 | // @TODO: You need to replace this with your own backend API.
35 | // Demo OAuth2 server https://github.com/bshaffer/oauth2-demo-php.
36 | // Username: demouser Password: demopass
37 | //target: 'http://brentertainment.com/oauth2/lockdin/token',
38 | target: 'http://localhost:8081',
39 | changeOrigin: true,
40 | ws: true,
41 | pathRewrite: {
42 | '^/auth': ''
43 | },
44 | router: {
45 | }
46 | },
47 | '/api': {
48 | // target: 'http://brentertainment.com/oauth2', // <-- Api server.
49 | target: 'http://localhost:8081/experience',
50 | changeOrigin: true, // <-- For virtual hosted sites.
51 | ws: true, // <-- Proxy websockets.
52 | pathRewrite: {
53 | // Rewrite path localhost:8080/api to http://brentertainment.com/oauth2/lockdin.
54 | '^/api': ''
55 | },
56 | router: {
57 | // when request.headers.host == 'dev.localhost:3000',
58 | // override target 'http://www.example.org' to 'http://localhost:8000'
59 | // 'dev.localhost:3000': 'http://localhost:8000'
60 | }
61 | }
62 | },
63 | // CSS Sourcemaps off by default because relative paths are "buggy"
64 | // with this option, according to the CSS-Loader README
65 | // (https://github.com/webpack/css-loader#sourcemaps)
66 | // In our experience, they generally work as expected,
67 | // just be aware of this issue when enabling this option.
68 | cssSourceMap: false
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/config/prod.env.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | NODE_ENV: '"production"'
3 | }
4 |
--------------------------------------------------------------------------------
/config/test.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const merge = require('webpack-merge')
4 | const devEnv = require('./dev.env')
5 |
6 | module.exports = merge(devEnv, {
7 | NODE_ENV: '"testing"'
8 | })
9 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | vue-pizza
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | <% for (var chunk of webpack.chunks) {
26 | for (var file of chunk.files) {
27 | if (file.match(/\.(js|css)$/)) { %>
28 | <% }}} %>
29 |
30 |
31 |
34 |
35 |
36 | <%= htmlWebpackPlugin.options.serviceWorkerLoader %>
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-pizza",
3 | "version": "1.0.0",
4 | "description": "A Vue.js project",
5 | "author": "David Graham ",
6 | "private": true,
7 | "scripts": {
8 | "dev": "node build/dev-server.js",
9 | "start": "node build/dev-server.js",
10 | "build": "node build/build.js",
11 | "e2e": "node test/e2e/runner.js",
12 | "test": "npm run e2e",
13 | "lint": "eslint --ext .js,.vue src test/e2e/specs",
14 | "deploy": "gh-pages-deploy"
15 | },
16 | "gh-pages-deploy": {
17 | "staticpath": "dist"
18 | },
19 | "dependencies": {
20 | "vue": "^2.5.2",
21 | "vue-router": "^3.0.1",
22 | "vuex-router-sync": "^5.0.0"
23 | },
24 | "devDependencies": {
25 | "autoprefixer": "^7.1.5",
26 | "axios": "^0.18.0",
27 | "babel-core": "^6.26.0",
28 | "babel-eslint": "^8.0.1",
29 | "babel-loader": "^7.1.2",
30 | "babel-plugin-transform-runtime": "^6.23.0",
31 | "babel-preset-env": "^1.6.0",
32 | "babel-preset-stage-2": "^6.24.1",
33 | "babel-register": "^6.26.0",
34 | "chalk": "^2.1.0",
35 | "chart.js": "^2.7.2",
36 | "chromedriver": "^2.33.1",
37 | "connect-history-api-fallback": "^1.4.0",
38 | "copy-webpack-plugin": "^4.1.1",
39 | "cross-spawn": "^5.1.0",
40 | "css-loader": "^0.28.7",
41 | "cssnano": "^3.10.0",
42 | "eslint": "^4.9.0",
43 | "eslint-config-standard": "^10.2.1",
44 | "eslint-friendly-formatter": "^3.0.0",
45 | "eslint-loader": "^1.9.0",
46 | "eslint-plugin-html": "^3.2.2",
47 | "eslint-plugin-import": "^2.7.0",
48 | "eslint-plugin-node": "^5.2.0",
49 | "eslint-plugin-promise": "^3.6.0",
50 | "eslint-plugin-standard": "^3.0.1",
51 | "eslint-plugin-vue": "^4.5.0",
52 | "eventsource-polyfill": "^0.9.6",
53 | "express": "^4.16.2",
54 | "extract-text-webpack-plugin": "^3.0.0",
55 | "file-loader": "^1.1.5",
56 | "friendly-errors-webpack-plugin": "^1.6.1",
57 | "gh-pages-deploy": "^0.4.2",
58 | "html-webpack-plugin": "^2.30.1",
59 | "http-proxy-middleware": "^0.17.4",
60 | "nightwatch": "^0.9.16",
61 | "node-sass": "^4.9.0",
62 | "opn": "^5.1.0",
63 | "optimize-css-assets-webpack-plugin": "^3.2.0",
64 | "ora": "^1.3.0",
65 | "pug": "^2.0.3",
66 | "pug-loader": "^2.3.0",
67 | "rimraf": "^2.6.2",
68 | "sass-loader": "^7.0.1",
69 | "selenium-server": "^3.6.0",
70 | "semver": "^5.4.1",
71 | "shelljs": "^0.7.8",
72 | "stylus": "^0.54.5",
73 | "stylus-loader": "^3.0.2",
74 | "sw-precache-webpack-plugin": "^0.11.4",
75 | "uglify-es": "^3.1.3",
76 | "url-loader": "^0.6.2",
77 | "url-search-params": "^0.10.0",
78 | "vue-chartjs": "^3.3.1",
79 | "vue-gravatar": "^1.2.1",
80 | "vue-loader": "^13.3.0",
81 | "vue-style-loader": "^3.0.3",
82 | "vue-template-compiler": "^2.5.2",
83 | "vuetify": "^1.0.17",
84 | "vuex": "^3.0.1",
85 | "webpack": "^3.7.1",
86 | "webpack-bundle-analyzer": "^2.9.0",
87 | "webpack-dev-middleware": "^1.12.0",
88 | "webpack-hot-middleware": "^2.19.1",
89 | "webpack-merge": "^4.1.0"
90 | },
91 | "engines": {
92 | "node": ">= 4.0.0",
93 | "npm": ">= 3.0.0"
94 | },
95 | "browserslist": [
96 | "> 1%",
97 | "last 2 versions",
98 | "not ie <= 8"
99 | ]
100 | }
101 |
--------------------------------------------------------------------------------
/src/app.vue:
--------------------------------------------------------------------------------
1 |
2 | #app
3 | component(:is="component")
4 | slot
5 |
6 |
7 |
38 |
--------------------------------------------------------------------------------
/src/assets/images/backdrop.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/src/assets/images/backdrop.jpg
--------------------------------------------------------------------------------
/src/assets/images/logo-name.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
238 |
--------------------------------------------------------------------------------
/src/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/src/assets/images/logo.png
--------------------------------------------------------------------------------
/src/assets/images/oops.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/assets/images/profile.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/src/assets/images/profile.jpg
--------------------------------------------------------------------------------
/src/assets/images/traffic-cone.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/src/assets/logo.png
--------------------------------------------------------------------------------
/src/auth/helpers.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import { router } from '@/http'
3 | import store from '@/store'
4 | import auth from './'
5 |
6 | const LOGIN_URL = '/auth'
7 |
8 | // const CLIENT_SECRET = 'ZGVtb2FwcDpkZW1vcGFzcw==' // Base64(client_id:client_secret) "demoapp:demopass"
9 |
10 | export default {
11 | URLSearchParams (obj) {
12 | var params = new URLSearchParams()
13 |
14 | for (var [key, value] of Object.entries(obj)) params.append(key, value)
15 |
16 | return params
17 | },
18 |
19 | login (creds, redirect, callback) {
20 | return Vue.http({
21 | method: 'post',
22 | url: LOGIN_URL,
23 | // headers: {
24 | // 'Authorization': 'Basic ' + CLIENT_SECRET,
25 | // 'Content-Type': 'application/x-www-form-urlencoded'
26 | // },
27 | data: this.URLSearchParams({
28 | grant_type: 'password',
29 | client_id: 'demoapp',
30 | client_secret: 'demopass',
31 | username: creds.username,
32 | password: creds.password
33 | })
34 | })
35 | .then((response) => {
36 | auth.storeToken(response)
37 |
38 | if (redirect) router.push({ name: redirect })
39 | return response
40 | })
41 | .catch((error) => {
42 | let errorMessage = null
43 |
44 | if (error.response) errorMessage = error.response.status
45 | else if (error.request) errorMessage = 'no response from server'
46 | else errorMessage = error.message
47 |
48 | return errorMessage
49 | })
50 | },
51 |
52 | logout () {
53 | store.dispatch('common/clear')
54 | router.push({ name: 'login' })
55 | },
56 |
57 | fakeLogin (creds, redirect) {
58 | return new Promise((resolve, reject) => {
59 | setTimeout(() => {
60 | auth.storeToken({data: { accessToken: '123456789', refreshToken: '77777777' }})
61 | if (redirect) router.push({ name: redirect })
62 | resolve({})
63 | }, 500)
64 | })
65 | },
66 |
67 | // Standardizes errors. A place to add logging if needed.
68 | get (url, params = {}) {
69 | const config = {
70 | params: {
71 | username: store.state.auth.user.id,
72 | orgId: store.state.auth.user.orgId
73 | }
74 | }
75 |
76 | config.params = Object.assign(config.params, params)
77 |
78 | return Vue.auth.get(url, config)
79 | .then((response) => {
80 | return new Promise((resolve) => {
81 | // @TODO check for no response.data.data?
82 | resolve(response.data.data)
83 | })
84 | })
85 | .catch((error) => {
86 | // Standardize errors.
87 | let errorMessage = null
88 |
89 | if (error.response) {
90 | errorMessage = error.response.statusText || error.response.status
91 | } else if (error.request) {
92 | errorMessage = 'no response from server'
93 | } else {
94 | errorMessage = error.message
95 | }
96 |
97 | return new Promise((resolve, reject) => {
98 | reject(new Error(errorMessage))
99 | })
100 | })
101 | },
102 |
103 | put (url, data = {}) {
104 | const config = {}
105 |
106 | const defaultData = {
107 | username: store.state.auth.user.id,
108 | orgId: store.state.auth.user.orgId
109 | }
110 |
111 | data = Object.assign(defaultData, data)
112 |
113 | // console.log(settings.data)
114 |
115 | return Vue.auth.put(url, data, config)
116 | .then((response) => {
117 | if (response.data.errors) {
118 | return new Promise((resolve, reject) => {
119 | reject(new Error(response.data.errors[0].user_message))
120 | })
121 | }
122 |
123 | return new Promise((resolve) => {
124 | // @TODO check for no response.data.data?
125 | resolve(response.data.data)
126 | })
127 | })
128 | .catch((error) => {
129 | // Standardize errors.
130 | let errorMessage = null
131 |
132 | if (error.response) {
133 | errorMessage = error.response.statusText || error.response.status
134 | } else if (error.request) {
135 | errorMessage = 'no response from server'
136 | } else {
137 | errorMessage = error.message
138 | }
139 |
140 | return new Promise((resolve, reject) => {
141 | reject(new Error(errorMessage))
142 | })
143 | })
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/src/auth/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import store from '@/store'
3 | import axios from 'axios'
4 | import * as constants from '@/constants'
5 |
6 | // const CLIENT_SECRET = 'demopass' // Base64(client_id:client_secret) "demoapp:demopass"
7 |
8 | export default {
9 |
10 | install (Vue, options) {
11 | Vue.prototype.$auth = Vue.auth = axios.create()
12 |
13 | this.setDefaults()
14 | this.addInterceptors()
15 | },
16 |
17 | setDefaults () {
18 | Vue.auth.defaults.baseURL = constants.API_BASE_URL
19 | },
20 |
21 | addInterceptors () {
22 | // Watch for accessToken changes and update our common Auth header.
23 | store.watch((state) => {
24 | return state.auth.accessToken
25 | }, (accessToken) => {
26 | if (!constants.DEBUG) {
27 | Vue.auth.defaults.headers.common['Authorization'] = 'Bearer ' + accessToken
28 | Vue.auth.defaults.transformRequest = [(data, headers) => {
29 | data.access_token = accessToken
30 | return data
31 | }]
32 | }
33 |
34 | if (constants.DEBUG) {
35 | console.log('token set')
36 | }
37 | }, {
38 | deep: true
39 | })
40 |
41 | // Intercept the response and refresh (one retry) if invalid token.
42 | Vue.auth.interceptors.response.use(function (response) {
43 | if (constants.DEBUG) return Promise.resolve(response)
44 |
45 | if (this.isInvalidToken(response)) {
46 | return this.refreshToken(response.request)
47 | }
48 | }, function (error) {
49 | return Promise.reject(error)
50 | })
51 | },
52 |
53 | isInvalidToken (response) {
54 | const status = response.status
55 | const error = response.data.error
56 |
57 | // Customize this to your Oauth server.
58 | return (status === 401 && (error === 'invalid_token' || error === 'expired_token'))
59 | },
60 |
61 | refreshToken (request) {
62 | return axios({
63 | method: 'post',
64 | url: constants.REFRESH_TOKEN_URL,
65 | // headers: {'Authorization': 'Basic ' + CLIENT_SECRET},
66 | data: {
67 | grant_type: 'refresh_token',
68 | refresh_token: store.state.auth.refreshToken
69 | }
70 | })
71 | .then((response) => {
72 | this.storeToken(response)
73 | return this.retry(request)
74 | })
75 | .catch((errorResponse) => {
76 | if (this.isInvalidToken(errorResponse)) { this.logout() }
77 | return errorResponse
78 | })
79 | },
80 |
81 | storeToken (response) {
82 | const auth = store.state.auth
83 |
84 | auth.isLoggedIn = true
85 | auth.accessToken = response.data.accessToken
86 | auth.refreshToken = response.data.refreshToken
87 | // @TODO: get user's name from response from Oauth server.
88 | auth.user.name = 'David Graham'
89 | auth.user.id = 'e3f657cb80354820b657cb8035c8208e'
90 |
91 | store.dispatch('auth/update', auth)
92 | },
93 |
94 | retry (request) {
95 | return Vue.auth(request)
96 | .then((response) => { return response })
97 | .catch((response) => { return response })
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/auth/store.js:
--------------------------------------------------------------------------------
1 | const defaults = {
2 | isLoggedIn: false,
3 | accessToken: null,
4 | refreshToken: null,
5 | user: {
6 | name: '',
7 | id: ''
8 | }
9 | }
10 |
11 | const auth = {
12 | namespaced: true,
13 |
14 | state: Object.assign({}, defaults),
15 |
16 | mutations: {
17 | update (state, data) {
18 | state = Object.assign({}, defaults, data)
19 | },
20 | clear (state) {
21 | state = Object.assign(state, defaults)
22 | }
23 | },
24 |
25 | actions: {
26 | clear ({ state, commit, rootState, dispatch }) {
27 | commit('clear')
28 | },
29 | update ({ state, commit, rootState }, data) {
30 | commit('update', data)
31 | }
32 | }
33 | }
34 |
35 | export default auth
36 |
--------------------------------------------------------------------------------
/src/components/Hello.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ msg }}
4 |
Essential Links
5 |
12 |
Ecosystem
13 |
19 |
20 |
21 |
22 |
32 |
33 |
34 |
53 |
--------------------------------------------------------------------------------
/src/components/app-bar.vue:
--------------------------------------------------------------------------------
1 |
2 | v-toolbar.my-appbar(
3 | :color="$vuetify.breakpoint.smAndDown ? 'primary' : 'default'"
4 | :dark="$vuetify.breakpoint.smAndDown"
5 | flat
6 | fixed
7 | app
8 | dense
9 | )
10 | v-toolbar-side-icon(v-show="!backButton" @click.stop="toggleSidebar()")
11 | v-btn(icon v-show="backButton" @click.stop="$router.back()")
12 | v-icon arrow_back
13 | v-toolbar-title.my_appbar__default-title(v-if="!$slots.title") {{ $store.state.common.title }}
14 | v-toolbar-title.my-appbar__title(v-if="$slots.title && (!$slots.smallTitle || $vuetify.breakpoint.mdAndUp)")
15 | slot(name="title")
16 | v-toolbar-title.my-appbar__small-title(v-if="$slots.smallTitle && $vuetify.breakpoint.smAndDown")
17 | slot(name="smallTitle")
18 | v-spacer
19 | .my-appbar__icons(v-if="!$slots.smallIcons || $vuetify.breakpoint.mdAndUp")
20 | slot(name="icons")
21 | .my-appbar__small-icons(v-if="$slots.smallIcons && $vuetify.breakpoint.smAndDown")
22 | slot(name="smallIcons")
23 |
24 |
25 |
43 |
44 |
66 |
--------------------------------------------------------------------------------
/src/components/app-dialog.vue:
--------------------------------------------------------------------------------
1 |
2 | .my-dialog
3 | v-dialog(v-model='dialogActive' max-width='500px')
4 | v-card
5 | v-card-title
6 | span {{ $store.state.common.dialog.text }}
7 | v-card-actions
8 | v-spacer
9 | v-btn(color='primary' flat='' @click.stop='dialogActive = false') Close
10 |
11 |
12 |
35 |
36 |
39 |
--------------------------------------------------------------------------------
/src/components/app-footer.vue:
--------------------------------------------------------------------------------
1 |
2 | v-footer.my-appfooter(:class="{ 'my-appfooter--sidebar-open': sidebarVisibility }")
3 | .grey--text © 2018 Your Company Here
4 |
5 |
6 |
17 |
18 |
26 |
--------------------------------------------------------------------------------
/src/components/app-sidebar.vue:
--------------------------------------------------------------------------------
1 |
2 | v-navigation-drawer.my-sidebar(
3 | v-model="isActive"
4 | fixed
5 | :mobile-break-point="1904"
6 | app
7 | touchless
8 | dark
9 | )
10 | v-layout(justify-center wrap)
11 | // img.my-sidebar__backdrop(src='~/@/assets/images/backdrop.jpg' alt='Vue Pizza')
12 | .my-sidebar__hero-pattern
13 |
14 | v-divider
15 |
16 | v-avatar.my-sidebar__avatar(:size="70")
17 | img(src='~/@/assets/images/profile.jpg' alt='Avatar')
18 |
19 | v-list(dark)
20 | v-list-group(prepend-icon='person')
21 | v-list-tile(slot="activator" ripple)
22 | v-list-tile-content
23 | v-list-tile-title {{ $store.state.auth.user.name }}
24 | v-list-tile(ripple :to="{ name: 'account' }")
25 | v-list-tile-action
26 | v-icon account_box
27 | v-list-tile-content
28 | v-list-tile-title Account
29 | v-list-tile(ripple :to="{ name: 'settings' }")
30 | v-list-tile-action
31 | v-icon settings
32 | v-list-tile-content
33 | v-list-tile-title Settings
34 |
35 | v-divider
36 | v-list(dark)
37 | v-list-tile(ripple :to="{ name: 'tutorial' }")
38 | v-list-tile-action
39 | v-icon library_books
40 | v-list-tile-content
41 | v-list-tile-title Tutorial
42 |
43 | v-list-tile(ripple :to="{ name: 'dashboard' }")
44 | v-list-tile-action
45 | v-icon dashboard
46 | v-list-tile-content
47 | v-list-tile-title Dashboard
48 |
49 | v-list-group(prepend-icon='build')
50 | v-list-tile(slot="activator" ripple)
51 | v-list-tile-content
52 | v-list-tile-title Tools
53 | v-list-tile(ripple :to="{ name: 'budgetTool' }")
54 | v-list-tile-content
55 | v-list-tile-title Budget Analyzer
56 | v-list-tile(ripple :to="{ name: 'rateTool' }")
57 | v-list-tile-content
58 | v-list-tile-title Rate Adjuster
59 |
60 | v-list-group(prepend-icon='assessment')
61 | v-list-tile(slot="activator" ripple)
62 | v-list-tile-content
63 | v-list-tile-title Reports
64 | v-list-tile(ripple :to="{ name: 'laborReport' }")
65 | v-list-tile-content
66 | v-list-tile-title Labor Report
67 | v-list-tile(ripple :to="{ name: 'earningsReport' }")
68 | v-list-tile-content
69 | v-list-tile-title Earnings
70 |
71 | v-list-group(prepend-icon='web')
72 | v-list-tile(slot="activator" ripple)
73 | v-list-tile-content
74 | v-list-tile-title Examples
75 | v-list-tile(ripple :to="{ name: 'wip' }")
76 | v-list-tile-content
77 | v-list-tile-title Work-in-progress
78 |
79 | v-list-group(prepend-icon='more_horiz')
80 | v-list-tile(slot="activator" ripple)
81 | v-list-tile-content
82 | v-list-tile-title More
83 | v-list-tile(ripple :to="{ name: 'importMyData' }")
84 | v-list-tile-content
85 | v-list-tile-title More Here
86 |
87 | v-list-tile(@click="logout()" ripple)
88 | v-list-tile-action
89 | v-icon exit_to_app
90 | v-list-tile-content
91 | v-list-tile-title Log out
92 |
93 |
94 |
118 |
119 |
140 |
--------------------------------------------------------------------------------
/src/components/app-snackbar.vue:
--------------------------------------------------------------------------------
1 |
2 | .my-snackbar
3 | v-snackbar(
4 | :timeout='$store.state.common.snackbar.timeout'
5 | :color='$store.state.common.snackbar.color'
6 | v-model='snackbarActive')
7 | | {{ $store.state.common.snackbar.text }}
8 | v-btn(dark='' flat='' @click.native='snackbarActive = false') Close
9 |
10 |
11 |
32 |
33 |
36 |
--------------------------------------------------------------------------------
/src/components/loading.vue:
--------------------------------------------------------------------------------
1 |
2 | .my-loading(v-show="loading || oops")
3 | v-progress-circular(
4 | v-show="loading"
5 | ref="progress"
6 | indeterminate
7 | v-bind:size="70"
8 | v-bind:width="7"
9 | color="primary"
10 | :style="style"
11 | )
12 | .my-loading__oops(ref="oops" v-show="!loading" :style="style")
13 | img(src='~/@/assets/images/oops.svg' alt='Page Error')
14 | div
15 | | Oops!
16 | br
17 | | Something went wrong.
18 |
19 |
20 |
169 |
170 |
186 |
--------------------------------------------------------------------------------
/src/constants/index.js:
--------------------------------------------------------------------------------
1 | // Testing
2 | export const DEBUG = true
3 |
4 | // Backend API endpoints
5 | export const API_BASE_URL = '/api'
6 | export const REFRESH_TOKEN_URL = '/auth'
7 |
8 | /**
9 | * Key for local storage.
10 | *
11 | * Set the key to use in local storage to hold persistant data. If logged in,
12 | * you can see this key by going to Chrome > dev tools > application tab,
13 | * then choosing "Local Storage" and "http://localhost:8080".
14 | *
15 | * @type {string}
16 | */
17 | export const STORAGE_KEY = 'vue-pizza'
18 |
--------------------------------------------------------------------------------
/src/features/account/components/address-edit.vue:
--------------------------------------------------------------------------------
1 |
2 | v-card.my-profile-address(tile)
3 | v-toolbar(card dark color="primary")
4 | v-btn(icon @click.native="isActive = false" dark)
5 | v-icon close
6 | v-toolbar-title Address
7 | v-spacer
8 | v-toolbar-items
9 | v-btn(dark flat @click.native="save()") Save
10 | v-menu(bottom right offset-y)
11 | v-btn(slot="activator" dark icon)
12 | v-icon more_vert
13 | v-list
14 |
15 | v-card-text(style="position: relative; max-width: 500px; margin: auto;")
16 | loading(:loading="loading" :oops="oops")
17 |
18 | v-form(v-model="valid")
19 | // v-text-field(label="Name" v-model="name" :rules="nameRules" :counter="10" required="")
20 | // v-text-field(label="E-mail" v-model="email" :rules="emailRules" required="")
21 | v-text-field(label="Address Line 1" v-model="address.addressLine1" :rules="rules.addressLine1" required="")
22 | v-text-field(label="Address Line 2" v-model="address.addressLine2")
23 | v-text-field(label="City" v-model="address.city")
24 | v-text-field(label="State/Region" v-model="address.state" :rules="rules.state" required="")
25 | v-select(
26 | label="Country"
27 | v-bind:items="countries"
28 | v-model="address.country"
29 | required
30 | item-value="name"
31 | item-text="name"
32 | :rules="rules.country"
33 | )
34 | v-text-field(label="Zipcode" v-model="address.zipcode" :rules="rules.zipcode" required="")
35 |
36 |
37 |
38 |
137 |
138 |
142 |
--------------------------------------------------------------------------------
/src/features/account/components/countries.js:
--------------------------------------------------------------------------------
1 | const countries = [
2 | { 'name': 'United States', 'code': 'US' },
3 | { 'name': 'Afghanistan', 'code': 'AF' },
4 | { 'name': 'Åland Islands', 'code': 'AX' },
5 | { 'name': 'Albania', 'code': 'AL' },
6 | { 'name': 'Algeria', 'code': 'DZ' },
7 | { 'name': 'American Samoa', 'code': 'AS' },
8 | { 'name': 'Andorra', 'code': 'AD' },
9 | { 'name': 'Angola', 'code': 'AO' },
10 | { 'name': 'Anguilla', 'code': 'AI' },
11 | { 'name': 'Antarctica', 'code': 'AQ' },
12 | { 'name': 'Antigua and Barbuda', 'code': 'AG' },
13 | { 'name': 'Argentina', 'code': 'AR' },
14 | { 'name': 'Armenia', 'code': 'AM' },
15 | { 'name': 'Aruba', 'code': 'AW' },
16 | { 'name': 'Australia', 'code': 'AU' },
17 | { 'name': 'Austria', 'code': 'AT' },
18 | { 'name': 'Azerbaijan', 'code': 'AZ' },
19 | { 'name': 'Bahamas', 'code': 'BS' },
20 | { 'name': 'Bahrain', 'code': 'BH' },
21 | { 'name': 'Bangladesh', 'code': 'BD' },
22 | { 'name': 'Barbados', 'code': 'BB' },
23 | { 'name': 'Belarus', 'code': 'BY' },
24 | { 'name': 'Belgium', 'code': 'BE' },
25 | { 'name': 'Belize', 'code': 'BZ' },
26 | { 'name': 'Benin', 'code': 'BJ' },
27 | { 'name': 'Bermuda', 'code': 'BM' },
28 | { 'name': 'Bhutan', 'code': 'BT' },
29 | { 'name': 'Bolivia', 'code': 'BO' },
30 | { 'name': 'Bosnia and Herzegovina', 'code': 'BA' },
31 | { 'name': 'Botswana', 'code': 'BW' },
32 | { 'name': 'Bouvet Island', 'code': 'BV' },
33 | { 'name': 'Brazil', 'code': 'BR' },
34 | { 'name': 'British Indian Ocean Territory', 'code': 'IO' },
35 | { 'name': 'Brunei Darussalam', 'code': 'BN' },
36 | { 'name': 'Bulgaria', 'code': 'BG' },
37 | { 'name': 'Burkina Faso', 'code': 'BF' },
38 | { 'name': 'Burundi', 'code': 'BI' },
39 | { 'name': 'Cambodia', 'code': 'KH' },
40 | { 'name': 'Cameroon', 'code': 'CM' },
41 | { 'name': 'Canada', 'code': 'CA' },
42 | { 'name': 'Cape Verde', 'code': 'CV' },
43 | { 'name': 'Cayman Islands', 'code': 'KY' },
44 | { 'name': 'Central African Republic', 'code': 'CF' },
45 | { 'name': 'Chad', 'code': 'TD' },
46 | { 'name': 'Chile', 'code': 'CL' },
47 | { 'name': 'China', 'code': 'CN' },
48 | { 'name': 'Christmas Island', 'code': 'CX' },
49 | { 'name': 'Cocos (Keeling) Islands', 'code': 'CC' },
50 | { 'name': 'Colombia', 'code': 'CO' },
51 | { 'name': 'Comoros', 'code': 'KM' },
52 | { 'name': 'Congo', 'code': 'CG' },
53 | { 'name': 'Congo, The Democratic Republic of the', 'code': 'CD' },
54 | { 'name': 'Cook Islands', 'code': 'CK' },
55 | { 'name': 'Costa Rica', 'code': 'CR' },
56 | { 'name': 'Cote D\'Ivoire', 'code': 'CI' },
57 | { 'name': 'Croatia', 'code': 'HR' },
58 | { 'name': 'Cuba', 'code': 'CU' },
59 | { 'name': 'Cyprus', 'code': 'CY' },
60 | { 'name': 'Czech Republic', 'code': 'CZ' },
61 | { 'name': 'Denmark', 'code': 'DK' },
62 | { 'name': 'Djibouti', 'code': 'DJ' },
63 | { 'name': 'Dominica', 'code': 'DM' },
64 | { 'name': 'Dominican Republic', 'code': 'DO' },
65 | { 'name': 'Ecuador', 'code': 'EC' },
66 | { 'name': 'Egypt', 'code': 'EG' },
67 | { 'name': 'El Salvador', 'code': 'SV' },
68 | { 'name': 'Equatorial Guinea', 'code': 'GQ' },
69 | { 'name': 'Eritrea', 'code': 'ER' },
70 | { 'name': 'Estonia', 'code': 'EE' },
71 | { 'name': 'Ethiopia', 'code': 'ET' },
72 | { 'name': 'Falkland Islands (Malvinas)', 'code': 'FK' },
73 | { 'name': 'Faroe Islands', 'code': 'FO' },
74 | { 'name': 'Fiji', 'code': 'FJ' },
75 | { 'name': 'Finland', 'code': 'FI' },
76 | { 'name': 'France', 'code': 'FR' },
77 | { 'name': 'French Guiana', 'code': 'GF' },
78 | { 'name': 'French Polynesia', 'code': 'PF' },
79 | { 'name': 'French Southern Territories', 'code': 'TF' },
80 | { 'name': 'Gabon', 'code': 'GA' },
81 | { 'name': 'Gambia', 'code': 'GM' },
82 | { 'name': 'Georgia', 'code': 'GE' },
83 | { 'name': 'Germany', 'code': 'DE' },
84 | { 'name': 'Ghana', 'code': 'GH' },
85 | { 'name': 'Gibraltar', 'code': 'GI' },
86 | { 'name': 'Greece', 'code': 'GR' },
87 | { 'name': 'Greenland', 'code': 'GL' },
88 | { 'name': 'Grenada', 'code': 'GD' },
89 | { 'name': 'Guadeloupe', 'code': 'GP' },
90 | { 'name': 'Guam', 'code': 'GU' },
91 | { 'name': 'Guatemala', 'code': 'GT' },
92 | { 'name': 'Guernsey', 'code': 'GG' },
93 | { 'name': 'Guinea', 'code': 'GN' },
94 | { 'name': 'Guinea-Bissau', 'code': 'GW' },
95 | { 'name': 'Guyana', 'code': 'GY' },
96 | { 'name': 'Haiti', 'code': 'HT' },
97 | { 'name': 'Heard Island and Mcdonald Islands', 'code': 'HM' },
98 | { 'name': 'Holy See (Vatican City State)', 'code': 'VA' },
99 | { 'name': 'Honduras', 'code': 'HN' },
100 | { 'name': 'Hong Kong', 'code': 'HK' },
101 | { 'name': 'Hungary', 'code': 'HU' },
102 | { 'name': 'Iceland', 'code': 'IS' },
103 | { 'name': 'India', 'code': 'IN' },
104 | { 'name': 'Indonesia', 'code': 'ID' },
105 | { 'name': 'Iran, Islamic Republic Of', 'code': 'IR' },
106 | { 'name': 'Iraq', 'code': 'IQ' },
107 | { 'name': 'Ireland', 'code': 'IE' },
108 | { 'name': 'Isle of Man', 'code': 'IM' },
109 | { 'name': 'Israel', 'code': 'IL' },
110 | { 'name': 'Italy', 'code': 'IT' },
111 | { 'name': 'Jamaica', 'code': 'JM' },
112 | { 'name': 'Japan', 'code': 'JP' },
113 | { 'name': 'Jersey', 'code': 'JE' },
114 | { 'name': 'Jordan', 'code': 'JO' },
115 | { 'name': 'Kazakhstan', 'code': 'KZ' },
116 | { 'name': 'Kenya', 'code': 'KE' },
117 | { 'name': 'Kiribati', 'code': 'KI' },
118 | { 'name': 'Democratic People\'s Republic of Korea', 'code': 'KP' },
119 | { 'name': 'Korea, Republic of', 'code': 'KR' },
120 | { 'name': 'Kosovo', 'code': 'XK' },
121 | { 'name': 'Kuwait', 'code': 'KW' },
122 | { 'name': 'Kyrgyzstan', 'code': 'KG' },
123 | { 'name': 'Lao People\'s Democratic Republic', 'code': 'LA' },
124 | { 'name': 'Latvia', 'code': 'LV' },
125 | { 'name': 'Lebanon', 'code': 'LB' },
126 | { 'name': 'Lesotho', 'code': 'LS' },
127 | { 'name': 'Liberia', 'code': 'LR' },
128 | { 'name': 'Libyan Arab Jamahiriya', 'code': 'LY' },
129 | { 'name': 'Liechtenstein', 'code': 'LI' },
130 | { 'name': 'Lithuania', 'code': 'LT' },
131 | { 'name': 'Luxembourg', 'code': 'LU' },
132 | { 'name': 'Macao', 'code': 'MO' },
133 | { 'name': 'Macedonia, The Former Yugoslav Republic of', 'code': 'MK' },
134 | { 'name': 'Madagascar', 'code': 'MG' },
135 | { 'name': 'Malawi', 'code': 'MW' },
136 | { 'name': 'Malaysia', 'code': 'MY' },
137 | { 'name': 'Maldives', 'code': 'MV' },
138 | { 'name': 'Mali', 'code': 'ML' },
139 | { 'name': 'Malta', 'code': 'MT' },
140 | { 'name': 'Marshall Islands', 'code': 'MH' },
141 | { 'name': 'Martinique', 'code': 'MQ' },
142 | { 'name': 'Mauritania', 'code': 'MR' },
143 | { 'name': 'Mauritius', 'code': 'MU' },
144 | { 'name': 'Mayotte', 'code': 'YT' },
145 | { 'name': 'Mexico', 'code': 'MX' },
146 | { 'name': 'Micronesia, Federated States of', 'code': 'FM' },
147 | { 'name': 'Moldova, Republic of', 'code': 'MD' },
148 | { 'name': 'Monaco', 'code': 'MC' },
149 | { 'name': 'Mongolia', 'code': 'MN' },
150 | { 'name': 'Montenegro', 'code': 'ME' },
151 | { 'name': 'Montserrat', 'code': 'MS' },
152 | { 'name': 'Morocco', 'code': 'MA' },
153 | { 'name': 'Mozambique', 'code': 'MZ' },
154 | { 'name': 'Myanmar', 'code': 'MM' },
155 | { 'name': 'Namibia', 'code': 'NA' },
156 | { 'name': 'Nauru', 'code': 'NR' },
157 | { 'name': 'Nepal', 'code': 'NP' },
158 | { 'name': 'Netherlands', 'code': 'NL' },
159 | { 'name': 'Netherlands Antilles', 'code': 'AN' },
160 | { 'name': 'New Caledonia', 'code': 'NC' },
161 | { 'name': 'New Zealand', 'code': 'NZ' },
162 | { 'name': 'Nicaragua', 'code': 'NI' },
163 | { 'name': 'Niger', 'code': 'NE' },
164 | { 'name': 'Nigeria', 'code': 'NG' },
165 | { 'name': 'Niue', 'code': 'NU' },
166 | { 'name': 'Norfolk Island', 'code': 'NF' },
167 | { 'name': 'Northern Mariana Islands', 'code': 'MP' },
168 | { 'name': 'Norway', 'code': 'NO' },
169 | { 'name': 'Oman', 'code': 'OM' },
170 | { 'name': 'Pakistan', 'code': 'PK' },
171 | { 'name': 'Palau', 'code': 'PW' },
172 | { 'name': 'Palestinian Territory, Occupied', 'code': 'PS' },
173 | { 'name': 'Panama', 'code': 'PA' },
174 | { 'name': 'Papua New Guinea', 'code': 'PG' },
175 | { 'name': 'Paraguay', 'code': 'PY' },
176 | { 'name': 'Peru', 'code': 'PE' },
177 | { 'name': 'Philippines', 'code': 'PH' },
178 | { 'name': 'Pitcairn', 'code': 'PN' },
179 | { 'name': 'Poland', 'code': 'PL' },
180 | { 'name': 'Portugal', 'code': 'PT' },
181 | { 'name': 'Puerto Rico', 'code': 'PR' },
182 | { 'name': 'Qatar', 'code': 'QA' },
183 | { 'name': 'Reunion', 'code': 'RE' },
184 | { 'name': 'Romania', 'code': 'RO' },
185 | { 'name': 'Russian Federation', 'code': 'RU' },
186 | { 'name': 'Rwanda', 'code': 'RW' },
187 | { 'name': 'Saint Helena', 'code': 'SH' },
188 | { 'name': 'Saint Kitts and Nevis', 'code': 'KN' },
189 | { 'name': 'Saint Lucia', 'code': 'LC' },
190 | { 'name': 'Saint Pierre and Miquelon', 'code': 'PM' },
191 | { 'name': 'Saint Vincent and the Grenadines', 'code': 'VC' },
192 | { 'name': 'Samoa', 'code': 'WS' },
193 | { 'name': 'San Marino', 'code': 'SM' },
194 | { 'name': 'Sao Tome and Principe', 'code': 'ST' },
195 | { 'name': 'Saudi Arabia', 'code': 'SA' },
196 | { 'name': 'Senegal', 'code': 'SN' },
197 | { 'name': 'Serbia', 'code': 'RS' },
198 | { 'name': 'Seychelles', 'code': 'SC' },
199 | { 'name': 'Sierra Leone', 'code': 'SL' },
200 | { 'name': 'Singapore', 'code': 'SG' },
201 | { 'name': 'Slovakia', 'code': 'SK' },
202 | { 'name': 'Slovenia', 'code': 'SI' },
203 | { 'name': 'Solomon Islands', 'code': 'SB' },
204 | { 'name': 'Somalia', 'code': 'SO' },
205 | { 'name': 'South Africa', 'code': 'ZA' },
206 | { 'name': 'South Georgia and the South Sandwich Islands', 'code': 'GS' },
207 | { 'name': 'Spain', 'code': 'ES' },
208 | { 'name': 'Sri Lanka', 'code': 'LK' },
209 | { 'name': 'Sudan', 'code': 'SD' },
210 | { 'name': 'Suriname', 'code': 'SR' },
211 | { 'name': 'Svalbard and Jan Mayen', 'code': 'SJ' },
212 | { 'name': 'Swaziland', 'code': 'SZ' },
213 | { 'name': 'Sweden', 'code': 'SE' },
214 | { 'name': 'Switzerland', 'code': 'CH' },
215 | { 'name': 'Syrian Arab Republic', 'code': 'SY' },
216 | { 'name': 'Taiwan', 'code': 'TW' },
217 | { 'name': 'Tajikistan', 'code': 'TJ' },
218 | { 'name': 'Tanzania, United Republic of', 'code': 'TZ' },
219 | { 'name': 'Thailand', 'code': 'TH' },
220 | { 'name': 'Timor-Leste', 'code': 'TL' },
221 | { 'name': 'Togo', 'code': 'TG' },
222 | { 'name': 'Tokelau', 'code': 'TK' },
223 | { 'name': 'Tonga', 'code': 'TO' },
224 | { 'name': 'Trinidad and Tobago', 'code': 'TT' },
225 | { 'name': 'Tunisia', 'code': 'TN' },
226 | { 'name': 'Turkey', 'code': 'TR' },
227 | { 'name': 'Turkmenistan', 'code': 'TM' },
228 | { 'name': 'Turks and Caicos Islands', 'code': 'TC' },
229 | { 'name': 'Tuvalu', 'code': 'TV' },
230 | { 'name': 'Uganda', 'code': 'UG' },
231 | { 'name': 'Ukraine', 'code': 'UA' },
232 | { 'name': 'United Arab Emirates', 'code': 'AE' },
233 | { 'name': 'United Kingdom', 'code': 'GB' },
234 | { 'name': 'United States', 'code': 'US' },
235 | { 'name': 'United States Minor Outlying Islands', 'code': 'UM' },
236 | { 'name': 'Uruguay', 'code': 'UY' },
237 | { 'name': 'Uzbekistan', 'code': 'UZ' },
238 | { 'name': 'Vanuatu', 'code': 'VU' },
239 | { 'name': 'Venezuela', 'code': 'VE' },
240 | { 'name': 'Viet Nam', 'code': 'VN' },
241 | { 'name': 'Virgin Islands, British', 'code': 'VG' },
242 | { 'name': 'Virgin Islands, U.S.', 'code': 'VI' },
243 | { 'name': 'Wallis and Futuna', 'code': 'WF' },
244 | { 'name': 'Western Sahara', 'code': 'EH' },
245 | { 'name': 'Yemen', 'code': 'YE' },
246 | { 'name': 'Zambia', 'code': 'ZM' },
247 | { 'name': 'Zimbabwe', 'code': 'ZW' }
248 | ]
249 |
250 | export default countries
251 |
--------------------------------------------------------------------------------
/src/features/account/main.vue:
--------------------------------------------------------------------------------
1 |
2 | v-container(fluid fill-height style="padding: 0;")
3 | v-layout.my-account(column)
4 |
5 | v-tabs.my-account__tabs(
6 | :color="$vuetify.breakpoint.smAndDown ? 'primary' : ''"
7 | :dark="$vuetify.breakpoint.smAndDown"
8 | v-model="currentTab"
9 | align-with-title
10 | :class="{ 'my-account__mobile-tabs': $vuetify.breakpoint.mdAndUp }"
11 | style="position: fixed; width: 100%; z-index: 1; background: #F4F4F4;"
12 | )
13 | // @TODO: Paths don't matter here, only route names. Need to report this issue to Vuetify.
14 | v-tab(v-bind:to="{ name: 'account', path: '/account' }" ripple) Profile
15 | v-tab(v-bind:to="{ name: 'billing', path: '/billing' }" ripple) Billing
16 | v-tab(v-bind:to="{ name: 'premium', path: '/premium' }" ripple) Premium
17 |
18 | v-container(
19 | fluid
20 | fill-height
21 | v-bind:grid-list-sm="$vuetify.breakpoint.smAndDown"
22 | v-bind:grid-list-lg="$vuetify.breakpoint.mdAndUp"
23 | style="position: relative; margin-top: 40px;"
24 | )
25 |
26 | v-layout(v-show="showPage" row wrap)
27 |
28 | v-flex(d-flex xs12)
29 |
30 | // Column left
31 | v-flex(d-flex xs12 md3 lg4)
32 |
33 | // Column right
34 | v-flex(d-flex xs12 md6 lg4)
35 | v-layout(column)
36 | v-flex(d-flex)
37 | v-card
38 | v-card-text
39 | v-layout
40 | v-flex
41 | Gravatar(:email="profile.email" :size="110")
42 | // img.app-avatar(src="../../assets/images/profile.jpg")
43 | br
44 | a.my-account__avatar-link(href="#") Update Avatar
45 | v-flex.px-2
46 | | {{ profile.name }}
47 | v-btn(flat icon color="primary")
48 | v-icon edit
49 | br
50 | | Joined: January 2017
51 | br
52 | | Last login: 3:40PM EST 4/15/2017
53 |
54 | v-flex(d-flex)
55 | v-card(flat)
56 | v-card-title
57 | .title Credentials
58 | v-card-text
59 | v-layout(row wrap style="align-items: center;")
60 | v-flex(d-flex xs12 sm12 md6)
61 | | Email Address
62 | v-flex(dflex xs12 sm12 md6)
63 | v-layout(style="align-items: center;")
64 | v-flex.text-xs-left(md6) {{ profile.email }}
65 | v-flex.text-xs-right(md6)
66 | v-btn(flat icon color="primary")
67 | v-icon edit
68 | v-flex(d-flex xs12 sm12 md6)
69 | | Password
70 | v-flex(dflex xs12 sm12 md6)
71 | v-layout(style="align-items: center;")
72 | v-flex.text-xs-left(md6) *********
73 | v-flex.text-xs-right(md6)
74 | v-btn(flat icon color="primary")
75 | v-icon edit
76 |
77 | v-flex(d-flex)
78 | v-card(flat)
79 | v-card-title
80 | .title Phone
81 | v-card-text
82 | v-layout(style="align-items: center;")
83 | v-flex
84 | | +1 12343374839
85 | v-flex.text-xs-right
86 | v-btn(flat icon color="primary")
87 | v-icon edit
88 |
89 | v-flex(d-flex)
90 | v-card(flat)
91 | v-card-title
92 | .title Address
93 | v-card-text
94 | v-layout
95 | v-flex
96 | | {{ profile.addressLine1 }}
97 | template(v-if="profile.addressLine2")
98 | br
99 | | {{ profile.addressLine2 }}
100 | br
101 | | {{ profile.city ? profile.city + ', ' : '' }}
102 | | {{ profile.state }} {{ profile.zipcode }}
103 | br
104 | | {{ profile.country }}
105 | v-flex.text-xs-right
106 | v-btn(flat icon color="primary" @click="openDialogFull('AddressEdit')")
107 | v-icon edit
108 |
109 | v-flex(d-flex)
110 | v-card(flat)
111 | v-card-title
112 | .title Account Options
113 | v-card-text
114 | v-layout
115 |
116 | v-dialog(
117 | v-model="dialogFullActive"
118 | fullscreen
119 | transition="dialog-bottom-transition"
120 | :overlay=false
121 | scrollable
122 | )
123 | component(:is="dialogFullComp" :active.sync="dialogFullActive")
124 |
125 |
126 |
127 |
198 |
199 |
216 |
--------------------------------------------------------------------------------
/src/features/account/service.js:
--------------------------------------------------------------------------------
1 | import store from '@/store'
2 | // import auth from '@/auth/helpers'
3 |
4 | export default class Service {
5 | constructor (options) {
6 | this.id = store.state.auth.user.id
7 | }
8 |
9 | getProfile () {
10 | // Mock data.
11 | // Replace this with actual call to backend server below.
12 | const parsed = {
13 | email: 'prograhammer@gmail.com',
14 | name: 'David Graham',
15 | country: 'USA',
16 | addressLine1: '1234 Some St.',
17 | addressLine2: '',
18 | state: 'Texas',
19 | zipcode: '78789'
20 | }
21 |
22 | // Simulate loading time.
23 | return new Promise((resolve) => {
24 | setTimeout(() => { resolve(parsed) }, 500)
25 | })
26 |
27 | /*
28 | return auth.get('/account')
29 | .then((response) => {
30 | const parsed = {
31 | email: response.email,
32 | name: response.name,
33 | country: response.country,
34 | addressLine1: response.address_line1,
35 | addressLine2: response.address_line2,
36 | state: response.state,
37 | zipcode: response.zipcode
38 | }
39 |
40 | return new Promise((resolve) => { resolve(parsed) })
41 | })
42 | .catch((error) => {
43 | return new Promise((resolve, reject) => { reject(error) })
44 | })
45 | */
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/features/account/store.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/src/features/account/store.js
--------------------------------------------------------------------------------
/src/features/billing/main.vue:
--------------------------------------------------------------------------------
1 |
2 | .my-billing
3 |
4 | Appbar
5 |
6 | v-tabs.elevation-0(dark v-model="currentTab" color="primary")
7 | // @TODO: Paths don't matter here, only route names. Make issue to Vuetify.
8 | v-tab(v-bind:to="{ name: 'account', path: '/account' }" ripple) Profile
9 | v-tab(v-bind:to="{ name: 'billing', path: '/billing' }" ripple) Billing
10 | v-tab(v-bind:to="{ name: 'market-link', path: '/market-link' }" ripple) Market Link
11 | v-tab(v-bind:to="{ name: 'prem-pts', path: '/prem-pts' }" ripple) Premium Points
12 |
13 | .my-billing__hero
14 | img.my-billing__cone(src='~/@/assets/images/traffic-cone.svg' alt='Under Construction')
15 | div
16 | |work-in-progress
17 |
18 |
19 |
20 |
40 |
41 |
56 |
--------------------------------------------------------------------------------
/src/features/dashboard/components/chart.vue:
--------------------------------------------------------------------------------
1 |
25 |
--------------------------------------------------------------------------------
/src/features/dashboard/components/line-chart.vue:
--------------------------------------------------------------------------------
1 |
25 |
--------------------------------------------------------------------------------
/src/features/dashboard/main.vue:
--------------------------------------------------------------------------------
1 |
2 | v-container.my-dashboard(
3 | fluid
4 | fill-height
5 | text-xs-center
6 | v-bind:grid-list-sm="$vuetify.breakpoint.smAndDown"
7 | v-bind:grid-list-lg="$vuetify.breakpoint.mdAndUp"
8 | )
9 | v-layout(row wrap)
10 |
11 | // Top Row
12 | v-flex(d-flex xs12 sm12 md6)
13 | v-card
14 | v-card-title
15 | .title Sales
16 | v-card-text(style="position: relative; height: 100%; max-height: 400px;")
17 | chart(style="height: 100%;")
18 | v-flex(d-flex xs12 sm12 md6)
19 | v-card.my-dashboard__pizza-status
20 | v-card-title
21 | .title Status
22 | v-card-text
23 | v-layout(row wrap)
24 | v-flex(xs4)
25 | .sub-title Order
26 | v-flex(xs4)
27 | .sub-title Prep
28 | v-flex(xs4)
29 | .sub-title Delivery
30 | v-flex(d-flex xs4)
31 | v-progress-circular.my-dashboard__order(
32 | :size="100"
33 | :width="15"
34 | :rotate="360"
35 | :value="45"
36 | color="accent"
37 | )
38 | | 45%
39 |
40 | v-flex(d-flex xs4)
41 | v-progress-circular(
42 | :size="100"
43 | :width="15"
44 | :rotate="360"
45 | :value="75"
46 | color="primary"
47 | )
48 | | 75%
49 |
50 | v-flex(d-flex xs4)
51 | v-progress-circular(
52 | :size="100"
53 | :width="15"
54 | :rotate="360"
55 | :value="15"
56 | color="secondary"
57 | )
58 | | 15%
59 | v-flex(d-flex xs12)
60 | line-chart(style="height: 200px; width: 100%;")
61 |
62 | // Bottom Row
63 | v-flex(d-flex xs12 sm12 md6)
64 | v-card
65 | v-card-title
66 | .title(style="margin-bottom: 47px;") Todo
67 | v-date-picker(
68 | v-model="date"
69 | min="2016-06-15"
70 | max="2018-03-20"
71 | full-width
72 | :event-color="date => date[9] % 2 ? 'red' : 'yellow'"
73 | :events="functionEvents"
74 | )
75 | v-flex(d-flex xs12 sm12 md6)
76 | v-card
77 | v-card-media(src="static/images/mountains.png" height="200px")
78 | v-layout.my-dashboard__media(column)
79 | v-card-title(class="white--text pl-5 pt-5")
80 | .display-1.pl-5.pt-5 Main Contacts
81 | v-list(two-line)
82 | v-list-tile(@click="")
83 | v-list-tile-action
84 | v-icon phone
85 | v-list-tile-content
86 | v-list-tile-title (650) 555-1234
87 | v-list-tile-sub-title Mobile
88 |
89 |
90 |
91 |
126 |
127 |
138 |
--------------------------------------------------------------------------------
/src/features/dashboard/service.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/src/features/dashboard/service.js
--------------------------------------------------------------------------------
/src/features/dashboard/store.js:
--------------------------------------------------------------------------------
1 | import store from '@/store'
2 |
3 | store.registerModule('dashboard', {
4 | namespaced: true,
5 |
6 | // State loaded when this component is first loaded.
7 | state: {
8 | test: 0
9 | },
10 |
11 | mutations: {
12 | updateTest (state, newVal) {
13 | state.test = newVal
14 | }
15 | },
16 |
17 | actions: {
18 | updateTest ({ state, commit, rootState, dispatch }, newVal) {
19 | console.log(newVal)
20 | commit('updateTest', newVal)
21 | }
22 | }
23 | })
24 |
--------------------------------------------------------------------------------
/src/features/dashboard/test.css:
--------------------------------------------------------------------------------
1 | .test {
2 | background-color: red;
3 | }
--------------------------------------------------------------------------------
/src/features/login/main.vue:
--------------------------------------------------------------------------------
1 |
2 | v-container.my-login(fluid fill-height)
3 | v-toolbar(
4 | color="primary"
5 | flat
6 | dark
7 | fixed
8 | app
9 | dense
10 | )
11 | v-btn(icon)
12 | img.my-login__logo(src='~/@/assets/images/logo.svg' alt='VuePizza Logo')
13 | v-spacer
14 | v-btn(flat)
15 | | GitHub
16 | v-btn(flat)
17 | | Tutorial
18 | v-layout(justify-center align-center)
19 | v-flex.text-xs-center(xs12 lg6)
20 | v-layout(row wrap)
21 | v-flex(xs12)
22 | .my-login__logo-name
23 | img(src='~/@/assets/images/logo-name.svg' alt='VueExample')
24 | v-flex(xs12)
25 | .my-login__subheading.subheading
26 | | Examples by the Slice!
27 | v-flex(xs12)
28 | v-card.my-login__card
29 | v-card-title.my-login__card-title(primary-title)
30 | v-avatar(:size="100")
31 | img(src='~/@/assets/images/profile.jpg' alt='Avatar')
32 |
33 | v-card-text
34 | v-form
35 | v-text-field(
36 | label='E-mail'
37 | v-model='credentials.username'
38 | required
39 | )
40 | v-text-field(
41 | label='Password'
42 | hint='At least 8 characters'
43 | v-model='credentials.password'
44 | min='8'
45 | :append-icon="passwordHidden ? 'visibility' : 'visibility_off'"
46 | :append-icon-cb='() => (passwordHidden = !passwordHidden)'
47 | :type="passwordHidden ? 'password' : 'text'"
48 | counter=''
49 | )
50 |
51 | v-card-actions.my-login__card-actions
52 | v-btn(
53 | :loading="loading"
54 | @click="login()"
55 | block
56 | color='accent'
57 | dark
58 | ) Login
59 |
60 |
61 |
97 |
98 |
129 |
--------------------------------------------------------------------------------
/src/features/login/service.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/src/features/login/service.js
--------------------------------------------------------------------------------
/src/features/login/store.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/src/features/login/store.js
--------------------------------------------------------------------------------
/src/features/premium/main.vue:
--------------------------------------------------------------------------------
1 |
2 | .my-prem-pts
3 |
4 | Appbar
5 |
6 | v-tabs.elevation-0(dark v-model="currentTab" color="primary")
7 | // @TODO: Paths don't matter here, only route names. Make issue to Vuetify.
8 | v-tab(v-bind:to="{ name: 'account', path: '/account' }" ripple) Profile
9 | v-tab(v-bind:to="{ name: 'billing', path: '/billing' }" ripple) Billing
10 | v-tab(v-bind:to="{ name: 'market-link', path: '/market-link' }" ripple) Market Link
11 | v-tab(v-bind:to="{ name: 'prem-pts', path: '/prem-pts' }" ripple) Premium Points
12 |
13 | .my-prem-pts__hero
14 | img.my-prem-pts__cone(src='~/@/assets/images/traffic-cone.svg' alt='Under Construction')
15 | div
16 | |work-in-progress
17 |
18 |
19 |
20 |
40 |
41 |
56 |
--------------------------------------------------------------------------------
/src/features/tutorial/main.vue:
--------------------------------------------------------------------------------
1 |
2 | v-container.my-tutorial(fluid fill-height text-xs-center)
3 | v-layout(justify-center align-center)
4 | v-flex.my-tutorial__hero(text-xs-center)
5 | div
6 | |Check out the tutorial on the project's Github Wiki.
7 | br
8 | br
9 | div
10 | v-btn(
11 | color="primay"
12 | href="https://github.com/prograhammer/vue-pizza/wiki"
13 | target="_blank"
14 | ) Go to Tutorial
15 |
16 |
17 |
18 |
35 |
36 |
46 |
--------------------------------------------------------------------------------
/src/features/wip/main.vue:
--------------------------------------------------------------------------------
1 |
2 | v-container.my-wip(fluid fill-height text-xs-center)
3 | v-layout(justify-center align-center)
4 | v-flex.my-wip__hero(text-xs-center)
5 | img.my-wip__cone(src='~/@/assets/images/traffic-cone.svg' alt='Work-in-progress')
6 | div
7 | |Work-in-progress
8 |
9 |
10 |
11 |
28 |
29 |
39 |
--------------------------------------------------------------------------------
/src/http/index.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import router from './router'
3 |
4 | export const http = {
5 | install (Vue, options) {
6 | Vue.prototype.$http = Vue.http = axios.create()
7 | }
8 | }
9 |
10 | export { router }
11 |
--------------------------------------------------------------------------------
/src/http/router.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 | import routes from './routes'
4 | import store from '@/store'
5 |
6 | Vue.use(Router)
7 |
8 | /**
9 | * Guard the route from unauthorized users.
10 | *
11 | * @param {Route} to The route we want to access.
12 | * @param {Route} from The route from which we are coming from.
13 | * @param {Function} next Callback for passing a route to be called next.
14 | * @return {void}
15 | */
16 | function guardRoute (to, from, next) {
17 | // work-around to get to the Vuex store (as of Vue 2.0)
18 | const auth = router.app.$options.store.state.auth
19 |
20 | if (!auth.isLoggedIn) {
21 | next({path: '/login', query: { redirect: to.fullPath }})
22 | } else {
23 | next()
24 | }
25 | }
26 |
27 | /**
28 | * The Router instance containing all the routes for the application.
29 | */
30 | const router = new Router({
31 | base: '/app',
32 | // mode: 'history', // <-- uncomment to turn on history mode (preferred)
33 | routes: routes.map(route => ({
34 | name: route.name,
35 | path: route.path,
36 | component: route.component,
37 | beforeEnter: (to, from, next) => {
38 | // Setup some per-page stuff.
39 | document.title = route.title
40 | store.dispatch('common/updateTitle', route.title)
41 | store.dispatch('common/updateLayout', route.layout)
42 |
43 | // Auth navigation guard.
44 | if (!route.isPublic) return guardRoute(to, from, next)
45 |
46 | next()
47 | }
48 | }))
49 | })
50 |
51 | export default router
52 |
--------------------------------------------------------------------------------
/src/http/routes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Every route becomes a chunk, loaded only when used.
3 | * Reduces size of initial App load.
4 | */
5 | const routes = [
6 | {
7 | name: 'login',
8 | path: '/login',
9 | component: () => import(/* webpackChunkName: "login" */ '@/features/login/main.vue'),
10 | title: 'Login',
11 | layout: 'PublicLayout',
12 | isPublic: true
13 | },
14 | {
15 | name: 'home',
16 | path: '/',
17 | component: () => import(/* webpackChunkName: "dashboard" */ '@/features/dashboard/main.vue'),
18 | title: 'Dashboard',
19 | layout: 'DefaultLayout',
20 | isPublic: false
21 | },
22 | {
23 | name: 'dashboard',
24 | path: '/dashboard',
25 | component: () => import(/* webpackChunkName: "dashboard" */ '@/features/dashboard/main.vue'),
26 | title: 'Dashboard',
27 | layout: 'DefaultLayout',
28 | isPublic: false
29 | },
30 | {
31 | name: 'account',
32 | path: '/account',
33 | component: () => import(/* webpackChunkName: "account" */ '@/features/account/main.vue'),
34 | title: 'Account',
35 | layout: 'DefaultLayout',
36 | isPublic: false
37 | },
38 | {
39 | name: 'tutorial',
40 | path: '/examples/tutorial',
41 | component: () => import(/* webpackChunkName: "tutorial" */ '@/features/tutorial/main.vue'),
42 | title: 'Tutorial',
43 | layout: 'DefaultLayout',
44 | isPublic: false
45 | },
46 | {
47 | name: 'wip',
48 | path: '/examples/wip',
49 | component: () => import(/* webpackChunkName: "wip" */ '@/features/wip/main.vue'),
50 | title: 'Wip',
51 | layout: 'DefaultLayout',
52 | isPublic: false
53 | }
54 | ]
55 |
56 | export default routes
57 |
--------------------------------------------------------------------------------
/src/layouts/default/main.vue:
--------------------------------------------------------------------------------
1 |
2 | v-app.my-default-layout
3 | app-sidebar
4 | app-bar
5 | v-content(style="padding-top: 48px;")
6 | transition(name="slide" mode="out-in")
7 | router-view
8 | app-footer
9 | app-dialog
10 | app-snackbar
11 |
12 |
13 |
30 |
31 |
33 |
--------------------------------------------------------------------------------
/src/layouts/public/main.vue:
--------------------------------------------------------------------------------
1 |
2 | v-app
3 | transition(name="slide" mode="out-in")
4 | router-view
5 |
6 |
7 |
12 |
13 |
16 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import store from './store'
3 | import { sync } from 'vuex-router-sync'
4 | import { http, router } from './http'
5 | import auth from './auth'
6 | import Vuetify from 'vuetify'
7 | import URLSearchParams from 'url-search-params'
8 | import App from './app'
9 | import Loading from './components/loading'
10 | import Appbar from './components/app-bar'
11 | import Appfooter from './components/app-footer'
12 |
13 | Vue.config.productionTip = false
14 |
15 | // Polyfills
16 | global.URLSearchParams = URLSearchParams
17 |
18 | // Sync router to store, as `store.state.route`.
19 | sync(store, router)
20 |
21 | // Http and Auth plugins
22 | Vue.use(http)
23 | Vue.use(auth)
24 |
25 | // Vuetify
26 | Vue.use(Vuetify, {
27 | theme: {
28 | primary: '#21CE99',
29 | secondary: '#D81B60',
30 | accent: '#805441'
31 | }
32 | })
33 |
34 | // Styles
35 | require('./styles/scss/main.scss')
36 | require('./styles/stylus/main.styl')
37 |
38 | // Global Components
39 | Vue.component('loading', Loading)
40 | Vue.component('Appbar', Appbar)
41 | Vue.component('Appfooter', Appfooter)
42 |
43 | /* eslint-disable no-new */
44 | new Vue({
45 | el: '#app',
46 | router,
47 | store,
48 | render: h => h(App)
49 | })
50 |
--------------------------------------------------------------------------------
/src/store/common.js:
--------------------------------------------------------------------------------
1 | // Common State.
2 | const defaults = {
3 | sidebar: {
4 | visible: true
5 | },
6 | title: '',
7 | layout: 'DefaultLayout',
8 | dialog: {
9 | visible: false,
10 | text: ''
11 | },
12 | snackbar: {
13 | visible: false,
14 | text: '',
15 | timeout: 6000,
16 | color: ''
17 | },
18 | error: {
19 | code: null,
20 | level: null,
21 | message: ''
22 | }
23 | }
24 |
25 | // Global module loaded on first app load.
26 | export default {
27 | namespaced: true,
28 |
29 | state: Object.assign({}, defaults),
30 |
31 | mutations: {
32 | updateSidebar (state, options) {
33 | state.sidebar = Object.assign({}, defaults.sidebar, options)
34 | },
35 |
36 | updateTitle (state, title) {
37 | state.title = title
38 | },
39 |
40 | updateLayout (state, layout) {
41 | state.layout = layout
42 | },
43 |
44 | updateDialog (state, options) {
45 | state.dialog = Object.assign({}, defaults.dialog, options)
46 | },
47 |
48 | updateSnackbar (state, options) {
49 | state.snackbar = Object.assign({}, defaults.snackbar, options)
50 | },
51 |
52 | error (state, options) {
53 | state.error = Object.assign({}, defaults.error, options)
54 | },
55 |
56 | clear (state) {
57 | state = Object.assign({}, defaults)
58 | }
59 | },
60 |
61 | actions: {
62 | clear ({ state, commit, rootState, dispatch }) {
63 | commit('clear')
64 | dispatch('auth/clear', {}, { root: true })
65 | },
66 |
67 | updateSidebar ({ commit }, options) {
68 | commit('updateSidebar', options)
69 | },
70 |
71 | updateTitle ({ commit }, title) {
72 | commit('updateTitle', title)
73 | },
74 |
75 | updateLayout ({ commit }, layout) {
76 | commit('updateLayout', layout)
77 | },
78 |
79 | updateDialog ({ commit }, options) {
80 | commit('updateDialog', options)
81 | },
82 |
83 | updateSnackbar ({ commit }, options) {
84 | commit('updateSnackbar', options)
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | import auth from '@/auth/store'
4 | import common from './common'
5 | import { localStoragePlugin } from './plugins'
6 |
7 | Vue.use(Vuex)
8 |
9 | export default new Vuex.Store({
10 | modules: { common, auth },
11 | plugins: [localStoragePlugin]
12 | })
13 |
--------------------------------------------------------------------------------
/src/store/plugins.js:
--------------------------------------------------------------------------------
1 | import auth from '@/auth/store'
2 | import * as constants from '@/constants'
3 |
4 | // Sync with local storage.
5 | if (localStorage.getItem(constants.STORAGE_KEY)) {
6 | const syncedState = JSON.parse(localStorage.getItem(constants.STORAGE_KEY))
7 | auth.state = Object.assign(auth.state, syncedState.auth)
8 | }
9 |
10 | // LocalStorage plugin.
11 | const localStoragePlugin = store => {
12 | store.subscribe((mutation, state) => {
13 | const syncedData = { auth: state.auth }
14 |
15 | localStorage.setItem(constants.STORAGE_KEY, JSON.stringify(syncedData))
16 |
17 | if (mutation.type === 'common/clear') {
18 | localStorage.removeItem(constants.STORAGE_KEY)
19 | }
20 | })
21 | }
22 |
23 | export { localStoragePlugin }
24 |
--------------------------------------------------------------------------------
/src/styles/scss/_variables.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/src/styles/scss/_variables.scss
--------------------------------------------------------------------------------
/src/styles/scss/_vendor.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/src/styles/scss/_vendor.scss
--------------------------------------------------------------------------------
/src/styles/scss/main.scss:
--------------------------------------------------------------------------------
1 | @import '_variables';
2 | @import '_vendor';
--------------------------------------------------------------------------------
/src/styles/stylus/1-settings/1-settings.styl:
--------------------------------------------------------------------------------
1 | @import 'vendor'
2 | @import 'variables'
3 |
--------------------------------------------------------------------------------
/src/styles/stylus/1-settings/variables.styl:
--------------------------------------------------------------------------------
1 | $app-primary = #21CE99
2 | $app-secondary = #D81B60
3 | $app-accent = #805441
4 | $app-success = #61B865
5 | $app-light-grey = #E1E2E1
6 | $app-error = #FF6666
--------------------------------------------------------------------------------
/src/styles/stylus/1-settings/vendor.styl:
--------------------------------------------------------------------------------
1 | $color-pack = false // Let's save 30k
2 |
--------------------------------------------------------------------------------
/src/styles/stylus/2-tools/2-tools.styl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/src/styles/stylus/2-tools/2-tools.styl
--------------------------------------------------------------------------------
/src/styles/stylus/3-generic/3-generic.styl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/src/styles/stylus/3-generic/3-generic.styl
--------------------------------------------------------------------------------
/src/styles/stylus/4-elements/4-elements.styl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/src/styles/stylus/4-elements/4-elements.styl
--------------------------------------------------------------------------------
/src/styles/stylus/5-objects/5-objects.styl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/src/styles/stylus/5-objects/5-objects.styl
--------------------------------------------------------------------------------
/src/styles/stylus/6-components/6-components.styl:
--------------------------------------------------------------------------------
1 | @import 'vendor.styl'
2 |
--------------------------------------------------------------------------------
/src/styles/stylus/6-components/vendor.styl:
--------------------------------------------------------------------------------
1 | @import '~vuetify/src/stylus/main'
2 |
3 | .application.theme--light
4 | background: #FFFFFF
--------------------------------------------------------------------------------
/src/styles/stylus/7-utils/7-utils.styl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/src/styles/stylus/7-utils/7-utils.styl
--------------------------------------------------------------------------------
/src/styles/stylus/main.styl:
--------------------------------------------------------------------------------
1 | // Follows ITCSS: https://www.xfive.co/blog/itcss-scalable-maintainable-css-architecture/
2 | // Note: You may need to add a /vendor folder to some of these folders for working
3 | // with vendor styles. Be careful to add them in the order you need.
4 | @import '1-settings'
5 | @import '2-tools'
6 | @import '3-generic'
7 | @import '4-elements'
8 | @import '5-objects'
9 | @import '6-components'
10 | @import '7-utils'
--------------------------------------------------------------------------------
/static/images/mountains.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/static/images/mountains.png
--------------------------------------------------------------------------------
/static/img/icons/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/static/img/icons/android-chrome-192x192.png
--------------------------------------------------------------------------------
/static/img/icons/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/static/img/icons/android-chrome-512x512.png
--------------------------------------------------------------------------------
/static/img/icons/apple-touch-icon-120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/static/img/icons/apple-touch-icon-120x120.png
--------------------------------------------------------------------------------
/static/img/icons/apple-touch-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/static/img/icons/apple-touch-icon-152x152.png
--------------------------------------------------------------------------------
/static/img/icons/apple-touch-icon-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/static/img/icons/apple-touch-icon-180x180.png
--------------------------------------------------------------------------------
/static/img/icons/apple-touch-icon-60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/static/img/icons/apple-touch-icon-60x60.png
--------------------------------------------------------------------------------
/static/img/icons/apple-touch-icon-76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/static/img/icons/apple-touch-icon-76x76.png
--------------------------------------------------------------------------------
/static/img/icons/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/static/img/icons/apple-touch-icon.png
--------------------------------------------------------------------------------
/static/img/icons/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/static/img/icons/favicon-16x16.png
--------------------------------------------------------------------------------
/static/img/icons/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/static/img/icons/favicon-32x32.png
--------------------------------------------------------------------------------
/static/img/icons/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/static/img/icons/favicon.ico
--------------------------------------------------------------------------------
/static/img/icons/msapplication-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/static/img/icons/msapplication-icon-144x144.png
--------------------------------------------------------------------------------
/static/img/icons/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prograhammer/vue-pizza/6d2259e49b45a34e36999ee706838463ebe4a4f0/static/img/icons/mstile-150x150.png
--------------------------------------------------------------------------------
/static/img/icons/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
150 |
--------------------------------------------------------------------------------
/static/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-pizza",
3 | "short_name": "Vue Pizza",
4 | "icons": [
5 | {
6 | "src": "/app/static/img/icons/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "/app/static/img/icons/android-chrome-512x512.png",
12 | "sizes": "512x512",
13 | "type": "image/png"
14 | }
15 | ],
16 | "start_url": "/app/index.html",
17 | "display": "standalone",
18 | "background_color": "#000000",
19 | "theme_color": "#4DBA87"
20 | }
21 |
--------------------------------------------------------------------------------
/test/e2e/custom-assertions/elementCount.js:
--------------------------------------------------------------------------------
1 | // A custom Nightwatch assertion.
2 | // the name of the method is the filename.
3 | // can be used in tests like this:
4 | //
5 | // browser.assert.elementCount(selector, count)
6 | //
7 | // for how to write custom assertions see
8 | // http://nightwatchjs.org/guide#writing-custom-assertions
9 | exports.assertion = function (selector, count) {
10 | this.message = 'Testing if element <' + selector + '> has count: ' + count
11 | this.expected = count
12 | this.pass = function (val) {
13 | return val === this.expected
14 | }
15 | this.value = function (res) {
16 | return res.value
17 | }
18 | this.command = function (cb) {
19 | var self = this
20 | return this.api.execute(function (selector) {
21 | return document.querySelectorAll(selector).length
22 | }, [selector], function (res) {
23 | cb.call(self, res)
24 | })
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/test/e2e/nightwatch.conf.js:
--------------------------------------------------------------------------------
1 | require('babel-register')
2 | var config = require('../../config')
3 |
4 | // http://nightwatchjs.org/gettingstarted#settings-file
5 | module.exports = {
6 | src_folders: ['test/e2e/specs'],
7 | output_folder: 'test/e2e/reports',
8 | custom_assertions_path: ['test/e2e/custom-assertions'],
9 |
10 | selenium: {
11 | start_process: true,
12 | server_path: require('selenium-server').path,
13 | host: '127.0.0.1',
14 | port: 4444,
15 | cli_args: {
16 | 'webdriver.chrome.driver': require('chromedriver').path
17 | }
18 | },
19 |
20 | test_settings: {
21 | default: {
22 | selenium_port: 4444,
23 | selenium_host: 'localhost',
24 | silent: true,
25 | globals: {
26 | devServerURL: 'http://localhost:' + (process.env.PORT || config.dev.port)
27 | }
28 | },
29 |
30 | chrome: {
31 | desiredCapabilities: {
32 | browserName: 'chrome',
33 | javascriptEnabled: true,
34 | acceptSslCerts: true
35 | }
36 | },
37 |
38 | firefox: {
39 | desiredCapabilities: {
40 | browserName: 'firefox',
41 | javascriptEnabled: true,
42 | acceptSslCerts: true
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/test/e2e/runner.js:
--------------------------------------------------------------------------------
1 | // 1. start the dev server using production config
2 | process.env.NODE_ENV = 'testing'
3 | var server = require('../../build/dev-server.js')
4 |
5 | server.ready.then(() => {
6 | // 2. run the nightwatch test suite against it
7 | // to run in additional browsers:
8 | // 1. add an entry in test/e2e/nightwatch.conf.json under "test_settings"
9 | // 2. add it to the --env flag below
10 | // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox`
11 | // For more information on Nightwatch's config file, see
12 | // http://nightwatchjs.org/guide#settings-file
13 | var opts = process.argv.slice(2)
14 | if (opts.indexOf('--config') === -1) {
15 | opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js'])
16 | }
17 | if (opts.indexOf('--env') === -1) {
18 | opts = opts.concat(['--env', 'chrome'])
19 | }
20 |
21 | var spawn = require('cross-spawn')
22 | var runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' })
23 |
24 | runner.on('exit', function (code) {
25 | server.close()
26 | process.exit(code)
27 | })
28 |
29 | runner.on('error', function (err) {
30 | server.close()
31 | throw err
32 | })
33 | })
34 |
--------------------------------------------------------------------------------
/test/e2e/specs/test.js:
--------------------------------------------------------------------------------
1 | // For authoring Nightwatch tests, see
2 | // http://nightwatchjs.org/guide#usage
3 |
4 | module.exports = {
5 | 'default e2e tests': function (browser) {
6 | // automatically uses dev Server port from /config.index.js
7 | // default: http://localhost:8080
8 | // see nightwatch.conf.js
9 | const devServer = browser.globals.devServerURL
10 |
11 | browser
12 | .url(devServer)
13 | .waitForElementVisible('#app', 5000)
14 | .assert.elementPresent('.hello')
15 | .assert.containsText('h1', 'Welcome to Your Vue.js PWA')
16 | .assert.elementCount('img', 1)
17 | .end()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------