diff --git a/.babelrc b/.babelrc index f005187..6bba5f0 100644 --- a/.babelrc +++ b/.babelrc @@ -1,26 +1,31 @@ { "presets": [ [ - "env", + "@babel/preset-env", { - "modules": false + "modules": false, + "loose": false, + "useBuiltIns": "usage" } ], - "stage-2" + [ + "@babel/preset-stage-2", + { + "modules": false, + "loose": false, + "useBuiltIns": true, + "decoratorsLegacy": true + } + ] ], "plugins": [ - "transform-runtime" + [ + "@babel/transform-runtime", + { + "polyfill": false, + "regenerator": false + } + ] ], - "comments": false, - "env": { - "test": { - "presets": [ - "env", - "stage-2" - ], - "plugins": [ - "istanbul" - ] - } - } + "comments": false } diff --git a/.eslintignore b/.eslintignore index 2fece73..0157478 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,3 @@ -build/*.js -config/*.js -dist/*.js +/dist +quasar.conf.js +config/ diff --git a/.eslintrc.js b/.eslintrc.js index f0dd8e4..f015c60 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,9 +5,7 @@ module.exports = { sourceType: 'module' }, env: { - browser: true, - node: true, - es6: true, + browser: true }, extends: [ // https://github.com/eslint/eslint/blob/master/conf/eslint-recommended.js @@ -20,28 +18,50 @@ module.exports = { ], // required to lint *.vue files plugins: [ - 'html', - 'import' + 'vue', + 'import', ], globals: { + 'ga': true, // Google Analytics 'cordova': true, - 'DEV': true, - 'PROD': true, - '__THEME': true + '__statics': true, + 'process': true, }, // add your custom rules here 'rules': { + // company standard + 'vue/html-indent': ['error', 4, { + 'attribute': 1, + 'closeBracket': 0, + 'alignAttributesVertically': true, + 'ignores': [] + }], + 'vue/script-indent': ['error', 4, { + 'baseIndent': 1, + 'switchCase': 1, + 'ignores': [] + }], + 'semi': ['error', 'always'], + + // allow async-await + 'generator-star-spacing': 'off', + // allow paren-less arrow functions - 'arrow-parens': 0, - 'one-var': 0, - 'import/first': 0, - 'import/named': 2, - 'import/namespace': 2, - 'import/default': 2, - 'import/export': 2, + 'arrow-parens': 'off', + 'one-var': 'off', + + 'import/first': 'off', + 'import/named': 'error', + 'import/namespace': 'error', + 'import/default': 'error', + 'import/export': 'error', + 'import/extensions': 'off', + 'import/no-unresolved': 'off', + 'import/no-extraneous-dependencies': 'off', + // allow console and debugger during development 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'warn', 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'warn', - 'brace-style': [2, 'stroustrup', { 'allowSingleLine': true }] + 'brace-style': ['error', 'stroustrup', { 'allowSingleLine': true }] } }; diff --git a/.gitignore b/.gitignore index c64dc5f..47bab81 100644 --- a/.gitignore +++ b/.gitignore @@ -62,13 +62,16 @@ typings/ # dependencies lock file package-lock.json +yarn.lock # Quasar Framework ignored files +.quasar .DS_Store .thumbs.db dist/ -cordova/platforms -cordova/plugins +/src-cordova/platforms +/src-cordova/plugins +/src-cordova/www # esdoc docs/ diff --git a/.postcssrc.js b/.postcssrc.js new file mode 100644 index 0000000..084a680 --- /dev/null +++ b/.postcssrc.js @@ -0,0 +1,8 @@ +// https://github.com/michael-ciniawsky/postcss-load-config + +module.exports = { + plugins: [ + // to edit target browsers: use "browserslist" field in package.json + require('autoprefixer') + ] +}; diff --git a/build/css-utils.js b/build/css-utils.js deleted file mode 100644 index 27d231c..0000000 --- a/build/css-utils.js +++ /dev/null @@ -1,90 +0,0 @@ -const ExtractTextPlugin = require('extract-text-webpack-plugin'); -const autoprefixer = require('autoprefixer'); -const purify = require('purify-css'); -const glob = require('glob'); -const path = require('path'); -const fs = require('fs'); - -module.exports.postcss = [autoprefixer()]; - -module.exports.styleLoaders = function (options) { - options = options || {}; - - function generateLoaders(loaders) { - if (options.postcss) { - loaders.splice(1, 0, 'postcss'); - } - - const sourceLoader = loaders.map(function (loader) { - let extraParamChar; - if (/\?/.test(loader)) { - loader = loader.replace(/\?/, '-loader?'); - extraParamChar = '&'; - } - else { - loader = loader + '-loader'; - extraParamChar = '?'; - } - return loader + (options.sourceMap ? extraParamChar + 'sourceMap' : ''); - }).join('!'); - - if (options.extract) { - return ExtractTextPlugin.extract({ - use: sourceLoader, - fallback: 'vue-style-loader' - }); - } - else { - return ['vue-style-loader', sourceLoader].join('!'); - } - } - - return { - css: generateLoaders(['css']), - less: generateLoaders(['css', 'less']), - sass: generateLoaders(['css', 'sass?indentedSyntax']), - scss: generateLoaders(['css', 'sass']), - styl: generateLoaders(['css', 'stylus']), - stylus: generateLoaders(['css', 'stylus']) - }; -}; - -module.exports.styleRules = function (options) { - var output = []; - var loaders = exports.styleLoaders(options); - for (const extension in loaders) { - const loader = loaders[extension]; - output.push({ - test: new RegExp('\\.' + extension + '$'), - loader: loader - }); - } - return output; -}; - -function getSize(size) { - return (size / 1024).toFixed(2) + 'kb'; -} - -module.exports.purify = function (cb) { - const css = glob.sync(path.join(__dirname, '../dist/**/*.css')); - const js = glob.sync(path.join(__dirname, '../dist/**/*.js')); - - Promise.all(css.map(function (file) { - return new Promise(function (resolve) { - console.log('\n Purifying ' + path.relative(path.join(__dirname, '../dist'), file).bold + '...'); - purify(js, [file], { minify: true }, function (purified) { - const oldSize = fs.statSync(file).size; - fs.writeFileSync(file, purified); - const newSize = fs.statSync(file).size; - - console.log( - ' * Reduced size by ' + ((1 - newSize / oldSize) * 100).toFixed(2) + '%, from ' + - getSize(oldSize) + ' to ' + getSize(newSize) + '.' - ); - resolve(); - }); - }); - })) - .then(cb); -}; diff --git a/build/env-utils.js b/build/env-utils.js deleted file mode 100644 index 46be7e1..0000000 --- a/build/env-utils.js +++ /dev/null @@ -1,12 +0,0 @@ -const config = require('../config'); -const theme = process.argv[2] || config.defaultTheme; - -module.exports = { - dev: process.env.NODE_ENV === 'development', - prod: process.env.NODE_ENV === 'production', - - platform: { - theme: theme, - cordovaAssets: './cordova/platforms/' + (theme === 'mat' ? 'android' : 'ios') + '/platform_www' - } -}; diff --git a/build/fs-utils.js b/build/fs-utils.js deleted file mode 100644 index 8eb95f4..0000000 --- a/build/fs-utils.js +++ /dev/null @@ -1,78 +0,0 @@ -const _ = require('lodash'); -const fs = require('fs'); -const path = require('path'); -const UglifyJS = require('uglify-es'); - - -function loadMinified(filePath, bindings) { - let code = fs.readFileSync(filePath, 'utf-8'); - - if (_.isPlainObject(bindings) && !_.isEmpty(bindings)) { - code = _.reduce(bindings, (result, value, key) => { - return _.replace(result, new RegExp(`{{\\s*${key}\\s*}}`), value); - }, code); - } - - const result = UglifyJS.minify(code); - - if (result.error) { - return ''; - } - - return result.code; -} - -function loadAvailableLanguages(source) { - if (!fs.statSync(source).isDirectory()) { - throw new Error('source must be a directory'); - } - - return _.filter( - fs.readdirSync(source), - dir => fs.statSync(path.join(source, dir)).isDirectory() - ); -} - -function loadTranslationResources(source) { - /** - * get language directories - * should follow the format - * dir/ - * |_ en - * |___index. - * |_ de - * |___index. - * ... - */ - const languageDirs = loadAvailableLanguages(source); - /** - * construct i18next resource map - * e.g. - * resources: { - * en: { - * namespace1: { - * key: 'hello from namespace 1' - * }, - * namespace2: { - * key: 'hello from namespace 2' - * } - * }, - * } - */ - return _.transform(languageDirs, (resource, dir) => { - const namespaces = _.filter( - fs.readdirSync(path.join(source, dir)), - file => /^\.(js|json)$/.test(path.extname(file)) - ); - - resource[dir] = _.transform(namespaces, (namespace, file) => { - namespace[_.replace(file, path.extname(file), '')] = require(path.join(source, dir, file)); - }, {}); - }, {}); -} - -module.exports = { - loadMinified, - loadAvailableLanguages, - loadTranslationResources, -}; diff --git a/build/google-analytics.js b/build/google-analytics.js deleted file mode 100644 index b0251c5..0000000 --- a/build/google-analytics.js +++ /dev/null @@ -1,12 +0,0 @@ -(function () { - 'use strict'; - - window.dataLayer = window.dataLayer || []; - - function gtag() { - window.dataLayer.push(arguments); - } - - gtag('js', new Date()); - gtag('config', {{ googleAnalyticsId }}); -})(); diff --git a/build/hot-reload.js b/build/hot-reload.js deleted file mode 100644 index dea1da4..0000000 --- a/build/hot-reload.js +++ /dev/null @@ -1,3 +0,0 @@ -/* eslint-disable */ -require('eventsource-polyfill'); -require('webpack-hot-middleware/client?noInfo=true&reload=true'); diff --git a/build/script.build.js b/build/script.build.js deleted file mode 100644 index 6d983f5..0000000 --- a/build/script.build.js +++ /dev/null @@ -1,53 +0,0 @@ -process.env.NODE_ENV = 'production'; - -require('colors'); - -const env = require('./env-utils'); -const css = require('./css-utils'); -const config = require('../config'); -const webpack = require('webpack'); -const webpackConfig = require('./webpack.prod.conf'); - -console.log(' WARNING!'.bold); -console.log(' Do NOT use VueRouter\'s "history" mode if'); -console.log(' building for Cordova or Electron.\n'); - -if (!config.build.env.googleAnalyticsId) { - console.log(' WARNING!'.bold); - console.log(' Your app is missing a Google Analytics ID.\n'); -} - -require('./script.clean.js'); -console.log((' Building Quasar App with "' + env.platform.theme + '" theme...\n').bold); - -function finalize() { - console.log(( - '\n Build complete with "' + env.platform.theme.bold + '" theme in ' + - '"/dist"'.bold + ' folder.\n').cyan); - - console.log(' Built files are meant to be served over an HTTP server.'.bold); - console.log(' Opening index.html over file:// won\'t work.'.bold); -} - -webpack(webpackConfig, function (err, stats) { - if (err) throw err; - - process.stdout.write(stats.toString({ - colors: true, - modules: false, - children: false, - chunks: false, - chunkModules: false - }) + '\n'); - - if (stats.hasErrors()) { - process.exit(1); - } - - if (config.build.purifyCSS) { - css.purify(finalize); - } - else { - finalize(); - } -}); diff --git a/build/script.clean.js b/build/script.clean.js deleted file mode 100644 index 497d6d8..0000000 --- a/build/script.clean.js +++ /dev/null @@ -1,6 +0,0 @@ -const shell = require('shelljs'); -const path = require('path'); - -shell.rm('-rf', path.resolve(__dirname, '../dist/*')); -shell.rm('-rf', path.resolve(__dirname, '../dist/.*')); -console.log(' Cleaned build artifacts.\n'); diff --git a/build/script.dev.js b/build/script.dev.js deleted file mode 100644 index 9abc0fc..0000000 --- a/build/script.dev.js +++ /dev/null @@ -1,98 +0,0 @@ -require('colors'); - -const path = require('path'); -const express = require('express'); -const webpack = require('webpack'); -const env = require('./env-utils'); -const config = require('../config'); -const opn = require('opn'); -const proxyMiddleware = require('http-proxy-middleware'); -const webpackConfig = require('./webpack.dev.conf'); -const app = express(); -const port = process.env.PORT || config.dev.port; -const uri = 'http://localhost:' + port; - -console.log(' Starting dev server with "' + (process.argv[2] || env.platform.theme).bold + '" theme...'); -console.log(' Will listen at ' + uri.bold); -if (config.dev.openBrowser) { - console.log(' Browser will open when build is ready.\n'); -} - -const compiler = webpack(webpackConfig); - -// Define HTTP proxies to your custom API backend -// https://github.com/chimurai/http-proxy-middleware -const proxyTable = config.dev.proxyTable; - -const devMiddleware = require('webpack-dev-middleware')(compiler, { - publicPath: webpackConfig.output.publicPath, - quiet: true -}); - -const hotMiddleware = require('webpack-hot-middleware')(compiler, { - log: function () { - } -}); - -// force page reload when html-webpack-plugin template changes -compiler.plugin('compilation', function (compilation) { - compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { - hotMiddleware.publish({ action: 'reload' }); - cb(); - }); -}); - -// proxy requests like API. See /config/index.js -> dev.proxyTable -// https://github.com/chimurai/http-proxy-middleware -Object.keys(proxyTable).forEach(function (context) { - let options = proxyTable[context]; - if (typeof options === 'string') { - options = { target: options }; - } - app.use(proxyMiddleware(context, options)); -}); - -// handle fallback for HTML5 history API -app.use(require('connect-history-api-fallback')()); - -// serve webpack bundle output -app.use(devMiddleware); - -// enable hot-reload and state-preserving -// compilation error display -app.use(hotMiddleware); - -// serve pure static assets -var staticsPath = path.posix.join(webpackConfig.output.publicPath, 'statics/'); -app.use(staticsPath, express.static('./src/statics')); - -// try to serve Cordova statics for Play App -app.use(express.static(env.platform.cordovaAssets)); - -console.log('> Starting server...'); -const ready = new Promise(function (resolve) { - devMiddleware.waitUntilValid(function () { - console.log('> Listening at ' + uri + '\n'); - // open only on dev env - if (config.dev.openBrowser && process.env.NODE_ENV === 'development') { - opn(uri); - } - - resolve(); - }); -}); - -const server = app.listen(port, function (err) { - if (err) { - console.log(err); - process.exit(1); - } -}); - -module.exports = { - server, - ready, - close: function () { - server.close(); - } -}; diff --git a/build/service-worker-dev.js b/build/service-worker-dev.js deleted file mode 100644 index 979e196..0000000 --- a/build/service-worker-dev.js +++ /dev/null @@ -1,17 +0,0 @@ -// This service worker file is effectively a 'no-op' that will reset any -// previous service worker registered for the same host:port combination. -// In the production build, this file is replaced with an actual service worker -// file that will precache your site's local assets. -// See https://github.com/facebookincubator/create-react-app/issues/2272#issuecomment-302832432 - -self.addEventListener('install', () => self.skipWaiting()); - -self.addEventListener('activate', () => { - self.clients.matchAll({ type: 'window' }).then(windowClients => { - for (let windowClient of windowClients) { - // Force open pages to refresh, so that they have a chance to load the - // fresh navigation response from the local dev server. - windowClient.navigate(windowClient.url); - } - }); -}); \ No newline at end of file diff --git a/build/service-worker-prod.js b/build/service-worker-prod.js deleted file mode 100644 index dd6f006..0000000 --- a/build/service-worker-prod.js +++ /dev/null @@ -1,55 +0,0 @@ -(function() { - 'use strict'; - - // Check to make sure service workers are supported in the current browser, - // and that the current page is accessed from a secure origin. Using a - // service worker from an insecure origin will trigger JS console errors. - const isLocalhost = Boolean(window.location.hostname === 'localhost' || - // [::1] is the IPv6 localhost address. - window.location.hostname === '[::1]' || - // 127.0.0.1/8 is considered localhost for IPv4. - window.location.hostname.match( - /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ - ) - ); - - window.addEventListener('load', function() { - if ('serviceWorker' in navigator && - (window.location.protocol === 'https:' || isLocalhost)) { - navigator.serviceWorker.register('service-worker.js') - .then(function(registration) { - // updatefound is fired if service-worker.js changes. - registration.onupdatefound = function() { - // updatefound is also fired the very first time the SW is installed, - // and there's no need to prompt for a reload at that point. - // So check here to see if the page is already controlled, - // i.e. whether there's an existing service worker. - if (navigator.serviceWorker.controller) { - // The updatefound event implies that registration.installing is set - const installingWorker = registration.installing; - - installingWorker.onstatechange = function() { - switch (installingWorker.state) { - case 'installed': - // At this point, the old content will have been purged and the - // fresh content will have been added to the cache. - // It's the perfect time to display a "New content is - // available; please refresh." message in the page's interface. - break; - - case 'redundant': - throw new Error('The installing ' + - 'service worker became redundant.'); - - default: - // Ignore - } - }; - } - }; - }).catch(function(e) { - console.error('Error during service worker registration:', e); - }); - } - }); -})(); diff --git a/build/webpack.base.conf.js b/build/webpack.base.conf.js deleted file mode 100644 index 3c08cd1..0000000 --- a/build/webpack.base.conf.js +++ /dev/null @@ -1,125 +0,0 @@ -const path = require('path'); -const webpack = require('webpack'); -const config = require('../config'); -const cssUtils = require('./css-utils'); -const env = require('./env-utils'); -const merge = require('webpack-merge'); -const projectRoot = path.resolve(__dirname, '../'); -const ProgressBarPlugin = require('progress-bar-webpack-plugin'); -const useCssSourceMap = - (env.dev && config.dev.cssSourceMap) || - (env.prod && config.build.productionSourceMap); - -function resolve(dir) { - return path.join(__dirname, '..', dir); -} - -module.exports = { - entry: { - app: './src/main.js' - }, - output: { - path: path.resolve(__dirname, '../dist'), - publicPath: config[env.prod ? 'build' : 'dev'].publicPath, - filename: 'js/[name].js', - chunkFilename: 'js/[id].[chunkhash].js' - }, - resolve: { - extensions: ['.js', '.vue', '.json'], - modules: [ - resolve('src'), - resolve('node_modules') - ], - alias: config.aliases - }, - module: { - rules: [ - { // eslint - enforce: 'pre', - test: /\.(vue|js)$/, - loader: 'eslint-loader', - include: projectRoot, - exclude: /node_modules/, - options: { - formatter: require('eslint-friendly-formatter') - } - }, - { - test: /\.js$/, - loader: 'babel-loader', - include: projectRoot, - exclude: /node_modules/ - }, - { - test: /\.vue$/, - loader: 'vue-loader', - options: { - postcss: cssUtils.postcss, - loaders: merge({ js: 'babel-loader' }, cssUtils.styleLoaders({ - sourceMap: useCssSourceMap, - extract: env.prod - })), - transformToRequire: { - video: 'src', - source: 'src', - img: 'src', - image: 'xlink:href' - } - } - }, - { - test: /\.json$/, - loader: 'json-loader' - }, - { - test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, - loader: 'url-loader', - options: { - limit: 10000, - name: 'img/[name].[hash:7].[ext]' - } - }, - { - test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, - loader: 'url-loader', - options: { - limit: 10000, - name: 'fonts/[name].[hash:7].[ext]' - } - } - ] - }, - plugins: [ - new webpack.DefinePlugin({ - 'process.env': config[env.prod ? 'build' : 'dev'].env, - 'DEV': env.dev, - 'PROD': env.prod, - '__THEME': '"' + env.platform.theme + '"' - }), - new webpack.LoaderOptionsPlugin({ - minimize: env.prod, - options: { - context: path.resolve(__dirname, '../src'), - postcss: cssUtils.postcss - } - }), - new ProgressBarPlugin({ - format: config.progressFormat - }) - ], - performance: { - hints: false - }, - node: { - // prevent webpack from injecting useless setImmediate polyfill because Vue - // source contains it (although only uses it if it's native). - setImmediate: false, - // prevent webpack from injecting mocks to Node native modules - // that does not make sense for the client - dgram: 'empty', - fs: 'empty', - net: 'empty', - tls: 'empty', - child_process: 'empty' - } -}; diff --git a/build/webpack.dev.conf.js b/build/webpack.dev.conf.js deleted file mode 100644 index f2e4188..0000000 --- a/build/webpack.dev.conf.js +++ /dev/null @@ -1,44 +0,0 @@ -const fs = require('fs'); -const path = require('path'); -const config = require('../config'); -const webpack = require('webpack'); -const merge = require('webpack-merge'); -const cssUtils = require('./css-utils'); -const baseWebpackConfig = require('./webpack.base.conf'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin'); - -// add hot-reload related code to entry chunks -Object.keys(baseWebpackConfig.entry).forEach(function (name) { - baseWebpackConfig.entry[name] = ['./build/hot-reload.js', baseWebpackConfig.entry[name]]; -}); - -module.exports = merge(baseWebpackConfig, { - // eval-source-map is faster for development - devtool: '#cheap-module-eval-source-map', - devServer: { - historyApiFallback: true, - noInfo: true - }, - module: { - rules: cssUtils.styleRules({ - sourceMap: config.dev.cssSourceMap, - postcss: true - }) - }, - plugins: [ - new webpack.HotModuleReplacementPlugin(), - new webpack.NoEmitOnErrorsPlugin(), - new HtmlWebpackPlugin({ - title: config.appTitle, - filename: 'index.html', - template: 'src/index.html', - inject: true, - serviceWorkerLoader: `` - }), - new FriendlyErrorsPlugin({ - clearConsole: config.dev.clearConsoleOnRebuild - }) - ] -}); diff --git a/build/webpack.prod.conf.js b/build/webpack.prod.conf.js deleted file mode 100644 index 880c589..0000000 --- a/build/webpack.prod.conf.js +++ /dev/null @@ -1,123 +0,0 @@ -const path = require('path'); -const config = require('../config'); -const cssUtils = require('./css-utils'); -const webpack = require('webpack'); -const merge = require('webpack-merge'); -const baseWebpackConfig = require('./webpack.base.conf'); -const ExtractTextPlugin = require('extract-text-webpack-plugin'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin'); -const CopyWebpackPlugin = require('copy-webpack-plugin'); -const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin'); -const WebpackMd5Hash = require('webpack-md5-hash'); -const fsUtils = require('./fs-utils'); -const pkg = require('../package.json'); - -let googleAnalyticsId = config.build.env.googleAnalyticsId; - -try { - googleAnalyticsId = JSON.parse(googleAnalyticsId); -} -catch (e) { - console.error(e); -} - -module.exports = merge(baseWebpackConfig, { - module: { - rules: cssUtils.styleRules({ - sourceMap: config.build.productionSourceMap, - extract: true, - postcss: true - }) - }, - devtool: config.build.productionSourceMap ? '#source-map' : false, - plugins: [ - new webpack.optimize.UglifyJsPlugin({ - sourceMap: config.build.productionSourceMap, - minimize: true, - compress: { - warnings: false - } - }), - // Compress extracted CSS. We are using this plugin so that possible - // duplicated CSS from different components can be deduped. - new OptimizeCSSPlugin({ - cssProcessorOptions: { - safe: true - } - }), - // extract css into its own file - new ExtractTextPlugin({ - filename: '[name].[contenthash].css' - }), - new HtmlWebpackPlugin({ - title: config.appTitle, - filename: path.resolve(__dirname, '../dist/index.html'), - template: 'src/index.html', - inject: true, - minify: { - removeComments: true, - collapseWhitespace: true, - removeAttributeQuotes: true - // more options: - // https://github.com/kangax/html-minifier#options-quick-reference - }, - // necessary to consistently work with multiple chunks via CommonsChunkPlugin - chunksSortMode: 'dependency', - serviceWorkerLoader: ` - `, - googleAnalyticsScript: googleAnalyticsId - && ` - `, - }), - // keep module.id stable when vendor modules does not change - new webpack.HashedModuleIdsPlugin(), - // split vendor js into its own file - new webpack.optimize.CommonsChunkPlugin({ - name: 'vendor', - minChunks(module, count) { - // any required modules inside node_modules are extracted to vendor - return ( - module.resource && - /\.js$/.test(module.resource) && - ( - module.resource.indexOf('quasar') > -1 || - module.resource.indexOf( - path.join(__dirname, '../node_modules') - ) === 0 - ) - ); - } - }), - // extract webpack runtime and module manifest to its own file in order to - // prevent vendor hash from being updated whenever app bundle is updated - new webpack.optimize.CommonsChunkPlugin({ - name: 'manifest', - chunks: ['vendor'] - }), - // Webpack has a bug wherein chunkhash does not always generate unique hash - // https://github.com/webpack/webpack/issues/1155 - new WebpackMd5Hash(), - // copy custom static assets - new CopyWebpackPlugin([ - { - from: path.resolve(__dirname, '../src/statics'), - to: 'statics', - ignore: ['.*'] - } - ]), - // service worker caching - new SWPrecacheWebpackPlugin({ - cacheId: pkg.name, - filename: 'service-worker.js', - staticFileGlobs: ['dist/**/*.{js,html,css,woff,ttf,eof,woff2,json,svg,gif,jpg,png,mp3}'], - minify: true, - stripPrefix: 'dist/' - }), - ] -}); diff --git a/build/webpack.test.conf.js b/build/webpack.test.conf.js deleted file mode 100644 index bf3e23e..0000000 --- a/build/webpack.test.conf.js +++ /dev/null @@ -1,49 +0,0 @@ -const webpack = require('webpack'); -const merge = require('webpack-merge'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); - -const cssUtils = require('./css-utils'); -const baseWebpackConfig = require('./webpack.base.conf'); - -const webpackConfig = merge(baseWebpackConfig, { - module: { - rules: cssUtils - .styleRules({ - sourceMap: true, - postcss: true - }) - .concat([{ - test: /\.html$/, - loader: 'html-loader' - }]) - }, - // use inline sourcemap for karma-sourcemap-loader - devtool: '#inline-source-map', - resolveLoader: { - alias: { - /** - * Necessary to to make lang="scss" work when using vue-loader's ?inject option - * The issue with this is that lang="scss" actually needs to use sass-loader, - * so when it goes to load it can't find scss-loader as that is not a loader available. - * see discussion at https://github.com/vuejs/vue-loader/issues/724 - * for more info: https://webpack.github.io/docs/loaders.html - */ - 'scss-loader': 'sass-loader' - } - }, - plugins: [ - new webpack.DefinePlugin({ - 'process.env': require('../config/env/test') - }), - new HtmlWebpackPlugin({ - filename: 'index.html', - template: 'src/index.html', - inject: true - }) - ] -}); - -// no need for app entry during tests -delete webpackConfig.entry; - -module.exports = webpackConfig; diff --git a/config/env/development/index.js b/config/env/development/index.js index 4926ac1..90b1898 100644 --- a/config/env/development/index.js +++ b/config/env/development/index.js @@ -1,60 +1,6 @@ -const path = require('path'); - -const { stringifyConfig } = require('../../../build/config-utils'); -const { loadTranslationResources, loadAvailableLanguages } = require('../../../build/fs-utils'); - -const languageResourcesPath = path.resolve(__dirname, '../../../resources/lang/'); -const translation = { - baseURL: '', - paths: { - getAvailableLocalesURL: '', - fetchLocaleURL: '' - }, - bustString: '' +module.exports = { + NODE_ENV: 'development', + GOOGLE_ANALYTICS_ID: 'UA-CHANGE-X', + SERVER_URL: '', + GOOGLE_CLIENT_ID: '', }; -const translationServer = { - getAvailableLocalesURL: `${translation.baseURL}${translation.paths.getAvailableLocalesURL}?${translation.bustString}`, - fetchLocaleURL: `${translation.baseURL}${translation.paths.fetchLocaleURL}&${translation.bustString}`, -}; -const lookupKey = 'lang'; - -module.exports = stringifyConfig({ - NODE_ENV: 'development', - SERVERS: { - translation: translationServer - }, - TRANSLATION: { - availableLanguages: loadAvailableLanguages(languageResourcesPath) - }, - I18NEXT: { - // i18next options - resources: loadTranslationResources(languageResourcesPath), - lng: 'en', - preload: ['en'], - fallbackLng: 'en', - ns: [ - 'index', // files within a language directory - ], - defaultNS: 'index', - initImmediate: false, // set to false to prevent displaying keys while rendering the page - returnNull: false, - returnEmptyString: false, - returnObjects: false, - debug: true, - - // i18next-xhr-backend options - backend: { - loadPath: translationServer.fetchLocaleURL, - crossDomain: true - }, - - // i18next-browser-languagedetector options - detection: { - order: ['querystring', 'localStorage', 'cookie', 'navigator'], - lookupQuerystring: lookupKey, - lookupCookie: lookupKey, - lookupLocalStorage: lookupKey, - caches: ['localStorage', 'cookie'] - } - }, -}); diff --git a/config/env/test/index.js b/config/env/test/index.js deleted file mode 100644 index 0f6b35e..0000000 --- a/config/env/test/index.js +++ /dev/null @@ -1,60 +0,0 @@ -const path = require('path'); - -const { stringifyConfig } = require('../../../build/config-utils'); -const { loadTranslationResources, loadAvailableLanguages } = require('../../../build/fs-utils'); - -const languageResourcesPath = path.resolve(__dirname, '../../../resources/lang/'); -const translation = { - baseURL: '', - paths: { - getAvailableLocalesURL: '', - fetchLocaleURL: '' - }, - bustString: '' -}; -const translationServer = { - getAvailableLocalesURL: `${translation.baseURL}${translation.paths.getAvailableLocalesURL}?${translation.bustString}`, - fetchLocaleURL: `${translation.baseURL}${translation.paths.fetchLocaleURL}&${translation.bustString}`, -}; -const lookupKey = 'lang'; - -module.exports = stringifyConfig({ - NODE_ENV: 'test', - SERVERS: { - translation: translationServer - }, - TRANSLATION: { - availableLanguages: loadAvailableLanguages(languageResourcesPath) - }, - I18NEXT: { - // i18next options - resources: loadTranslationResources(languageResourcesPath), - lng: 'en', - preload: ['en'], - fallbackLng: 'en', - ns: [ - 'index', // files within a language directory - ], - defaultNS: 'index', - initImmediate: false, // set to false to prevent displaying keys while rendering the page - returnNull: false, - returnEmptyString: false, - returnObjects: false, - debug: true, - - // i18next-xhr-backend options - backend: { - loadPath: translationServer.fetchLocaleURL, - crossDomain: true - }, - - // i18next-browser-languagedetector options - detection: { - order: ['querystring', 'localStorage', 'cookie', 'navigator'], - lookupQuerystring: lookupKey, - lookupCookie: lookupKey, - lookupLocalStorage: lookupKey, - caches: ['localStorage', 'cookie'] - } - }, -}); diff --git a/config/index.js b/config/index.js index 991f9b8..9ffd6de 100644 --- a/config/index.js +++ b/config/index.js @@ -1,68 +1,12 @@ -const path = require('path'); -const pkg = require('../package.json'); -const env = require('../build/env-utils'); - -module.exports = { - projectName: pkg.name, - appTitle: 'Quasar Boilerplate', - // Webpack aliases - aliases: { - quasar: path.resolve(__dirname, '../node_modules/quasar-framework/'), - src: path.resolve(__dirname, '../src'), - assets: path.resolve(__dirname, '../src/assets'), - '@': path.resolve(__dirname, '../src/components'), - variables: path.resolve(__dirname, '../src/themes/quasar.variables.styl') - }, - - // Progress Bar Webpack plugin format - // https://github.com/clessg/progress-bar-webpack-plugin#options - progressFormat: ' [:bar] ' + ':percent'.bold + ' (:msg)', - - // Default theme to build with ('ios' or 'mat') - defaultTheme: 'mat', - - build: { - env: env.prod && require('./env/production'), - publicPath: '', - productionSourceMap: false, - - // Remove unused CSS - // Disable it if it has side-effects for your specific app - purifyCSS: true - }, - dev: { - env: require('./env/development'), - cssSourceMap: true, - // auto open browser or not - openBrowser: true, - publicPath: '/', - port: 8081, - - // If for example you are using Quasar Play - // to generate a QR code then on each dev (re)compilation - // you need to avoid clearing out the console, so set this - // to "false", otherwise you can set it to "true" to always - // have only the messages regarding your last (re)compilation. - clearConsoleOnRebuild: false, +/** + * This file should only contain build time configurations. + * Runtime configurations should be placed in /src/config. + */ +const env = process.env.NODE_ENV || 'development'; +const envConfig = require(`./env/${env}`); +const { stringifyConfig } = require('./utils'); - // Proxy your API if using any. - // Also see /build/script.dev.js and search for "proxy api requests" - // https://github.com/chimurai/http-proxy-middleware - proxyTable: {} - } -}; -/* - * proxyTable example: - * - proxyTable: { - // proxy all requests starting with /api - '/api': { - target: 'https://some.address.com/api', - changeOrigin: true, - pathRewrite: { - '^/api': '' - } - } - } - */ +module.exports = stringifyConfig({ + env: envConfig, +}); diff --git a/build/config-utils.js b/config/utils.js similarity index 67% rename from build/config-utils.js rename to config/utils.js index 8f52fec..80f11a5 100644 --- a/build/config-utils.js +++ b/config/utils.js @@ -1,7 +1,10 @@ -'use strict'; - const _ = require('lodash'); + +module.exports = { + stringifyConfig +}; + /** * Stringifies all string values nested inside config object. * - If the value is a string it will be used as a code fragment. @@ -13,15 +16,11 @@ const _ = require('lodash'); * @returns {Object} */ function stringifyConfig(config) { - return _.mapValues(config, value => { - if (_.isPlainObject(value)) { - return stringifyConfig(value); - } + return _.mapValues(config, value => { + if (_.isPlainObject(value)) { + return stringifyConfig(value); + } - return _.isString(value) || _.isArray(value) ? JSON.stringify(value) : value; - }); + return _.isString(value) || _.isArray(value) ? JSON.stringify(value) : value; + }); } - -module.exports = { - stringifyConfig -}; diff --git a/package.json b/package.json index 0ed084c..fe0f28b 100644 --- a/package.json +++ b/package.json @@ -2,14 +2,16 @@ "name": "anytv-quasar-boilerplate", "version": "0.0.1", "description": "AnyTV Quasar Boilerplate", - "author": "MCN Freedom! Tech Inc. ", + "repository": "https://github.com/anyTV/quasar-boilerplate", + "productName": "Freedom! App", + "cordovaId": "org.cordova.quasar.app", + "author": { + "name": "MCN Freedom! Tech Inc.", + "email": "development@freedom.tm", + "url": "www.freedom.tm" + }, "scripts": { - "clean": "node build/script.clean.js", - "dev": "cross-env NODE_ENV=development node build/script.dev.js", - "build": "cross-env NODE_ENV=production node build/script.build.js", "lint": "eslint --ext .js,.vue src", - "test": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run", - "tdd": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js", "docs": "esdoc" }, "dependencies": { @@ -18,92 +20,38 @@ "i18next": "^10.2.2", "i18next-browser-languagedetector": "^2.1.0", "i18next-xhr-backend": "^1.5.1", - "jsonwebtoken": "^8.1.1", - "lodash": "^4.17.4", + "jsonwebtoken": "^8.2.1", + "lodash-es": "^4.17.10", "moment": "^2.20.1", - "qs": "^6.5.1", - "quasar-extras": "0.x", - "quasar-framework": "^0.14.6", - "vue": "^2.5.2", - "vue-router": "^2.7.0", - "vue-router-multiguard": "^1.0.3", - "vuelidate": "^0.6.1", + "qs": "^6.5.2", + "vue": "^2.5.16", + "vue-analytics": "^5.12.1", + "vue-router": "^3.0.1", + "vuelidate": "^0.6.2", "vuex": "^3.0.1" }, "devDependencies": { - "@vue/test-utils": "^1.0.0-beta.10", - "autoprefixer": "^6.4.0", - "babel-core": "^6.0.0", - "babel-eslint": "^7.0.0", - "babel-loader": "^7.1.2", - "babel-plugin-istanbul": "^4.1.5", - "babel-plugin-transform-runtime": "^6.0.0", - "babel-preset-env": "^1.6.1", - "babel-preset-stage-2": "^6.0.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "chai": "^4.1.2", - "colors": "^1.1.2", - "connect-history-api-fallback": "^1.1.0", - "copy-webpack-plugin": "^4.1.0", - "cross-env": "^5.1.3", - "css-loader": "^0.28.7", - "es6-promise": "^4.1.1", - "esdoc": "^1.0.4", + "babel-eslint": "^8.2.1", + "esdoc": "^1.1.0", "esdoc-standard-plugin": "^1.0.0", - "eslint": "^4.8.0", - "eslint-config-standard": "^10.2.1", - "eslint-friendly-formatter": "^3.0.0", - "eslint-loader": "^1.9.0", - "eslint-plugin-html": "^3.2.2", - "eslint-plugin-import": "^2.7.0", - "eslint-plugin-node": "^5.2.0", - "eslint-plugin-promise": "^3.5.0", - "eslint-plugin-standard": "^3.0.1", - "eslint-plugin-vue": "^4.2.0", - "eventsource-polyfill": "^0.9.6", - "express": "^4.16.1", - "extract-text-webpack-plugin": "^3.0.0", - "file-loader": "^0.11.1", - "friendly-errors-webpack-plugin": "^1.1.3", - "glob": "^7.1.2", - "html-loader": "^0.5.4", - "html-webpack-plugin": "^2.30.1", - "http-proxy-middleware": "^0.17.0", - "jsdom": "^11.5.1", - "jsdom-global": "^3.0.2", - "json-loader": "^0.5.7", - "karma": "^2.0.0", - "karma-chrome-launcher": "^2.2.0", - "karma-coverage": "^1.1.1", - "karma-mocha": "^1.3.0", - "karma-phantomjs-launcher": "^1.0.4", - "karma-phantomjs-shim": "^1.5.0", - "karma-sinon-chai": "^1.3.3", - "karma-sourcemap-loader": "^0.3.7", - "karma-spec-reporter": "0.0.32", - "karma-webpack": "^2.0.9", - "mocha": "^4.1.0", - "opn": "^5.0.0", - "optimize-css-assets-webpack-plugin": "^3.2.0", - "postcss-loader": "^1.0.0", - "progress-bar-webpack-plugin": "^1.10.0", - "purify-css": "^1.2.6", - "shelljs": "^0.7.0", - "sinon": "^4.1.5", - "sinon-chai": "^2.14.0", - "stylus": "^0.54.5", - "stylus-loader": "^3.0.1", - "sw-precache-webpack-plugin": "^0.11.4", - "uglify-es": "^3.1.3", - "url-loader": "^0.5.7", - "vue-loader": "^13.0.5", - "vue-style-loader": "^3.0.3", - "vue-template-compiler": "^2.5.2", - "webpack": "^3.6.0", - "webpack-dev-middleware": "^1.12.0", - "webpack-hot-middleware": "^2.19.1", - "webpack-md5-hash": "0.0.6", - "webpack-merge": "^4.1.0" - } + "eslint": "^4.18.2", + "eslint-friendly-formatter": "^4.0.1", + "eslint-loader": "^2.0.0", + "eslint-plugin-import": "^2.9.0", + "eslint-plugin-node": "^6.0.1", + "eslint-plugin-promise": "^3.7.0", + "eslint-plugin-vue": "^4.3.0", + "lodash": "^4.17.10", + "quasar-cli": "^0.16.0", + "webpack": "^4.8.3" + }, + "engines": { + "node": ">= 8.9.0", + "npm": ">= 5.6.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 10" + ] } diff --git a/quasar.conf.js b/quasar.conf.js new file mode 100644 index 0000000..073f628 --- /dev/null +++ b/quasar.conf.js @@ -0,0 +1,149 @@ +const path = require('path'); +const webpack = require('webpack'); +const config = require('./config'); +const pkg = require('./package'); + +// Configuration for your app +module.exports = function (ctx) { + return { + // app plugins (/src/plugins) + plugins: [ + /** + * Enable only the plugins that your app is going to use. + */ + // 'axios', + // 'google-api', + // 'jwt', + // 'vue-analytics', + // 'vue-i18next', + // 'vuelidate', + ], + css: [ + 'app.styl' + ], + extras: [ + ctx.theme.mat ? 'roboto-font' : null, + 'material-icons', + ctx.theme.ios ? 'ionicons' : null, + // 'mdi', + // 'fontawesome' + ], + supportIE: true, + build: { + // gzip: true, // use if deployment server does not have gzip by default + scopeHoisting: true, + vueRouterMode: 'history', + publicPath: '/', + analyze: process.env.ANALYZE, + devtool: 'source-map', + env: config.env, + extendWebpack(cfg) { + cfg.module.rules.push({ + enforce: 'pre', + test: /\.(js|vue)$/, + loader: 'eslint-loader', + exclude: /(node_modules|quasar)/ + }); + + cfg.resolve.alias = { + ...cfg.resolve.alias, + // add custom aliases below + '@': path.resolve(__dirname, './src/components'), + 'lodash': 'lodash-es', + }; + + // ignore moment locales + cfg.plugins.push(new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)); + } + }, + devServer: { + https: true, + port: 8080, + open: true, // opens browser window automatically + // proxy: { + // '/api': { + // target: '', + // changeOrigin: true, + // pathRewrite: { + // '^/api': '' + // } + // } + // } + }, + // framework: 'all' --- includes everything; for dev only! + framework: { + components: [], + directives: [], + // Quasar plugins + plugins: [] + }, + // animations: 'all' --- includes all animations + animations: [], + pwa: { + // workboxPluginMode: 'InjectManifest', + // workboxOptions: {}, + manifest: { + name: pkg.productName, + short_name: pkg.name, + description: pkg.description, + display: 'standalone', + orientation: 'portrait', + background_color: '#ffffff', + theme_color: '#027be3', + icons: [ + { + 'src': 'statics/icons/icon-128x128.png', + 'sizes': '128x128', + 'type': 'image/png' + }, + { + 'src': 'statics/icons/icon-192x192.png', + 'sizes': '192x192', + 'type': 'image/png' + }, + { + 'src': 'statics/icons/icon-256x256.png', + 'sizes': '256x256', + 'type': 'image/png' + }, + { + 'src': 'statics/icons/icon-384x384.png', + 'sizes': '384x384', + 'type': 'image/png' + }, + { + 'src': 'statics/icons/icon-512x512.png', + 'sizes': '512x512', + 'type': 'image/png' + } + ] + } + }, + cordova: { + // id: 'org.cordova.quasar.app' + }, + electron: { + // bundler: 'builder', // or 'packager' + extendWebpack(cfg) { + // do something with Electron process Webpack cfg + }, + packager: { + // https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options + + // OS X / Mac App Store + // appBundleId: '', + // appCategoryType: '', + // osxSign: '', + // protocol: 'myapp://path', + + // Window only + // win32metadata: { ... } + }, + builder: { + // https://www.electron.build/configuration/configuration + + // appId: 'quasar-app' + } + } + }; +}; diff --git a/src-pwa/custom-service-worker.js b/src-pwa/custom-service-worker.js new file mode 100644 index 0000000..b38bbdf --- /dev/null +++ b/src-pwa/custom-service-worker.js @@ -0,0 +1,6 @@ +/* + * This file (which will be your service worker) + * is picked up by the build system if BOTH conditions are met: + * - You are building for production + * - quasar.conf > pwa > workboxPluginMode is set to "InjectManifest" + */ diff --git a/src-pwa/register-service-worker.js b/src-pwa/register-service-worker.js new file mode 100644 index 0000000..c81ab3b --- /dev/null +++ b/src-pwa/register-service-worker.js @@ -0,0 +1,24 @@ +/* + * This file is picked up by the build system only + * when building for PRODUCTION + */ + +import { register } from 'register-service-worker'; + +register(process.env.SERVICE_WORKER_FILE, { + ready() { + console.log('App is being served from cache by a service worker.'); + }, + cached() { + console.log('Content has been cached for offline use.'); + }, + updated() { + console.log('New content is available; please refresh.'); + }, + offline() { + console.log('No internet connection found. App is running in offline mode.'); + }, + error(err) { + console.error('Error during service worker registration:', err); + } +}); diff --git a/src/App.vue b/src/App.vue index eafebf6..387720c 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,29 +1,14 @@ - diff --git a/src/assets/.gitkeep b/src/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/components/.gitkeep b/src/components/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/components/LanguageSelector.vue b/src/components/LanguageSelector.vue new file mode 100644 index 0000000..2110961 --- /dev/null +++ b/src/components/LanguageSelector.vue @@ -0,0 +1,79 @@ + + + diff --git a/src/components/partials/form/FormField.vue b/src/components/partials/form/FormField.vue deleted file mode 100644 index 21049de..0000000 --- a/src/components/partials/form/FormField.vue +++ /dev/null @@ -1,97 +0,0 @@ - - - - - diff --git a/src/config/google-api.js b/src/config/google-api.js index ad08265..77ac58d 100644 --- a/src/config/google-api.js +++ b/src/config/google-api.js @@ -1,12 +1,12 @@ export default { clientURL: 'https://apis.google.com/js/api.js', - clientId: '', + clientId: process.env.GOOGLE_CLIENT_ID, scope: [ 'email', 'profile', ].join(' '), libraries: [ 'client', - 'auth2' - ].join(':'), + 'auth2', + ].join(' '), }; diff --git a/src/config/i18next.js b/src/config/i18next.js new file mode 100644 index 0000000..84249de --- /dev/null +++ b/src/config/i18next.js @@ -0,0 +1,30 @@ +import { merge } from 'lodash'; +import resources from 'src/i18n'; + +const i18nextEnv = process.env.I18NEXT; +const lookupKey = 'lang'; + +const i18nextOptions = { + resources, + lng: 'en', + preload: ['en'], + fallbackLng: 'en', + ns: ['index'], + defaultNS: 'index', + initImmediate: false, // set to false to prevent displaying keys while rendering the page + returnNull: false, + returnEmptyString: false, + returnObjects: false, + debug: true, + + // i18next-browser-languagedetector options + detection: { + order: ['querystring', 'localStorage', 'cookie', 'navigator'], + lookupQuerystring: lookupKey, + lookupCookie: lookupKey, + lookupLocalStorage: lookupKey, + caches: ['localStorage', 'cookie'] + } +}; + +export default merge(i18nextOptions, i18nextEnv); diff --git a/src/config/index.js b/src/config/index.js index 06e46b2..64538fc 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -1,7 +1,8 @@ +/** + * This file should only contain runtime configurations. + * E.g. plugin config, vue config, static data, etc. + * Build configurations should be placed in /config directory outside the /src directory. + */ export default { - server: { - baseURL: '', - }, - - ACCESS_TOKEN_KEY: '', + JWT_STORAGE_KEY: 'JWT_STORAGE_KEY', }; diff --git a/src/config/translation.js b/src/config/translation.js deleted file mode 100644 index 1beed51..0000000 --- a/src/config/translation.js +++ /dev/null @@ -1,9 +0,0 @@ -const i18nextConfig = process.env.I18NEXT; -const translationServer = process.env.SERVERS && process.env.SERVERS.translation; -const availableLanguages = process.env.TRANSLATION && process.env.TRANSLATION.availableLanguages; - -export default { - server: translationServer, - availableLanguages, - i18nextConfig, -}; diff --git a/src/css/app.styl b/src/css/app.styl new file mode 100644 index 0000000..e3e5a1e --- /dev/null +++ b/src/css/app.styl @@ -0,0 +1 @@ +// app global css diff --git a/src/css/themes/common.variables.styl b/src/css/themes/common.variables.styl new file mode 100644 index 0000000..4d659aa --- /dev/null +++ b/src/css/themes/common.variables.styl @@ -0,0 +1,25 @@ +// App Shared Variables +// -------------------------------------------------- +// To customize the look and feel of this app, you can override +// the Stylus variables found in Quasar's source Stylus files. Setting +// variables before Quasar's Stylus will use these variables rather than +// Quasar's default Stylus variable values. Stylus variables specific +// to the themes belong in either the variables.ios.styl or variables.mat.styl files. + +// Check documentation for full list of Quasar variables + + +// App Shared Color Variables +// -------------------------------------------------- +// It's highly recommended to change the default colors +// to match your app's branding. + +$primary = #027be3 +$secondary = #26A69A +$tertiary = #555 + +$neutral = #E0E1E2 +$positive = #21BA45 +$negative = #DB2828 +$info = #31CCEC +$warning = #F2C037 diff --git a/src/css/themes/variables.ios.styl b/src/css/themes/variables.ios.styl new file mode 100644 index 0000000..953b825 --- /dev/null +++ b/src/css/themes/variables.ios.styl @@ -0,0 +1,7 @@ +// App Shared Variables +// -------------------------------------------------- +// Shared Stylus variables go in the common.variables.styl file +@import 'common.variables' + +// iOS only Quasar variables overwrites +// ----------------------------------------- diff --git a/src/css/themes/variables.mat.styl b/src/css/themes/variables.mat.styl new file mode 100644 index 0000000..8169a52 --- /dev/null +++ b/src/css/themes/variables.mat.styl @@ -0,0 +1,7 @@ +// App Shared Variables +// -------------------------------------------------- +// Shared Stylus variables go in the common.variables.styl file +@import 'common.variables' + +// Material only Quasar variables overwrites +// ----------------------------------------- diff --git a/src/helpers/axios.js b/src/helpers/axios.js new file mode 100644 index 0000000..2496af5 --- /dev/null +++ b/src/helpers/axios.js @@ -0,0 +1,9 @@ +import axios from 'axios'; +import qs from 'qs'; + +const baseAxios = axios.create({ + baseURL: process.env.SERVER_URL, + paramsSerializer: params => qs.stringify(params, { arrayFormat: 'brackets' }), +}); + +export default baseAxios; diff --git a/src/helpers/google-api-client.js b/src/helpers/google-api-client.js index d36e4db..cd94426 100644 --- a/src/helpers/google-api-client.js +++ b/src/helpers/google-api-client.js @@ -1,4 +1,4 @@ -import _ from 'lodash'; +import { pick } from 'lodash'; /** * Helper function for using google api from CDN @@ -15,6 +15,10 @@ export default function GoogleAPIClient(options) { return Promise.resolve(window.gapi); // already injected } + if (!options.clientId) { + throw new Error('Google Client ID is required. You can set it in config/env//index.js'); + } + const callback = '__googleAPIOnLoadCallback'; const src = options.clientURL; // need to wrap to a native promise object since google has its own goog.Thenable @@ -37,17 +41,16 @@ export default function GoogleAPIClient(options) { }; document.body.appendChild(script); - }) - .then(() => { - + }).then(() => { return new Promise(resolve => { - window.gapi.client.init(_.pick(options, [ - 'apiKey', - 'clientId', - 'discoveryDocs', - 'scope', - ])) - .then(() => resolve(window.gapi)); + window.gapi.client + .init(pick(options, [ + 'apiKey', + 'clientId', + 'discoveryDocs', + 'scope', + ])) + .then(() => resolve(window.gapi)); }); }); } diff --git a/src/helpers/i18n.js b/src/helpers/i18n.js deleted file mode 100644 index 0a38cbd..0000000 --- a/src/helpers/i18n.js +++ /dev/null @@ -1,19 +0,0 @@ -import Vue from 'vue'; -import i18next from 'i18next'; -import i18nextXHRBackend from 'i18next-xhr-backend'; -import i18nextLangDetector from 'i18next-browser-languagedetector'; -import VueI18next from '@panter/vue-i18next'; -import translation from 'src/config/translation'; - -Vue.use(VueI18next); - -i18next.use(i18nextLangDetector); - -// load resources/lang for translations in non-production environments -if (PROD) { - i18next.use(i18nextXHRBackend); -} - -i18next.init(translation.i18nextConfig); - -export default new VueI18next(i18next); diff --git a/src/helpers/jwt.js b/src/helpers/jwt.js index 287d6ad..e51dc34 100644 --- a/src/helpers/jwt.js +++ b/src/helpers/jwt.js @@ -1,5 +1,6 @@ import jwt from 'jsonwebtoken'; import { LocalStorage } from 'quasar'; +import { get } from 'lodash'; export default class JWT { @@ -26,10 +27,16 @@ export default class JWT { return this; } - decode() { - return jwt.decode(this.getToken(), { + decode(path = null, defaultValue = null) { + const decoded = jwt.decode(this.getToken(), { complete: true, force: true }); + + if (!path) { + return decoded; + } + + return get(decoded, path, defaultValue); } } diff --git a/src/helpers/utils.js b/src/helpers/utils.js new file mode 100644 index 0000000..ddd3aab --- /dev/null +++ b/src/helpers/utils.js @@ -0,0 +1,5 @@ +import { every, has } from 'lodash'; + +export function hasKeys(obj, keys) { + return every(keys, key => has(obj, key)); +} diff --git a/resources/lang/en/index.json b/src/i18n/en/index.json similarity index 100% rename from resources/lang/en/index.json rename to src/i18n/en/index.json diff --git a/src/i18n/index.js b/src/i18n/index.js new file mode 100644 index 0000000..a9ae65c --- /dev/null +++ b/src/i18n/index.js @@ -0,0 +1,10 @@ +import { transform } from 'lodash'; + +// These resources are used in development +// Production uses i18nextxhrbackend +import en from './en'; + +export default transform( + { en }, // you can add other resources here for testing translations + (resources, value, key) => resources[key] = { index: value } +); diff --git a/src/index.html b/src/index.html deleted file mode 100644 index 8e94d6f..0000000 --- a/src/index.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - <%= htmlWebpackPlugin.options.title %> - - - - - - - - - - - - - - - - - <% for (var chunk of webpack.chunks) { - for (var file of chunk.files) { - if (file.match(/\.(js|css)$/)) { %> - - <% }}} %> - - - -
- -<%= htmlWebpackPlugin.options.serviceWorkerLoader %> -<%= htmlWebpackPlugin.options.googleAnalyticsScript %> - - - diff --git a/src/index.template.html b/src/index.template.html new file mode 100644 index 0000000..3cc40b3 --- /dev/null +++ b/src/index.template.html @@ -0,0 +1,26 @@ + + + + + + + + + <%= htmlWebpackPlugin.options.productName %> + + + + + + + + + +
+ + + + diff --git a/src/layouts/.gitkeep b/src/layouts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/main.js b/src/main.js deleted file mode 100644 index 06dc7b9..0000000 --- a/src/main.js +++ /dev/null @@ -1,44 +0,0 @@ -import _ from 'lodash'; -import { Platform } from 'quasar'; -import Vue from 'vue'; - -import router from 'src/router'; -import store from 'src/store'; -import i18n from 'src/helpers/i18n'; -import plugins from 'src/plugins'; -// Quasar has a known issue in which modal cannot be closed when the modal is open and the page is refreshed. -// This will be fixed in Quasar v0.15 (https://github.com/quasarframework/quasar/issues/994). -// Workaround is to manually set popstate (https://github.com/quasarframework/quasar/issues/823). -Platform.has.popstate = false; - -// enable more verbose logs on non-production builds -Vue.config.productionTip = process.env.NODE_ENV !== 'production'; - -// Install plugins -_.each(plugins, plugin => Vue.use(plugin)); - -// Quasar has a known issue in which CSS import order between "quasar dev" and "quasar build" are not the same, -// thus messing up styles on some components (https://github.com/quasarframework/quasar-template-default/issues/47). -// Workaround is to reorder the imports (https://github.com/tdamsma/quasar-css-import-bug/pull/1/files). -import 'quasar-extras/material-icons'; - -// === DEFAULT / CUSTOM STYLE === -// WARNING! always comment out ONE of the two require() calls below. -// 1. use next line to activate CUSTOM STYLE (./src/themes) -require(`./themes/app.${__THEME}.styl`); -// 2. or, use next line to activate DEFAULT QUASAR STYLE -// require(`quasar/dist/quasar.${__THEME}.css`); -// ============================== - -// Uncomment the following lines if you need IE11/Edge support -require(`quasar/dist/quasar.ie`); -require(`quasar/dist/quasar.ie.${__THEME}.css`); - -/* eslint-disable no-new */ -new Vue({ - el: '#q-app', - router, - store, - i18n, - render: h => h(require('./App').default) -}); diff --git a/src/mixins/i18n.js b/src/mixins/i18n.js deleted file mode 100644 index eb04a4a..0000000 --- a/src/mixins/i18n.js +++ /dev/null @@ -1,58 +0,0 @@ -import _ from 'lodash'; -import axios from 'axios'; -import translation from 'src/config/translation'; - -/** - * Mixin containing helper methods for i18n - */ -export default { - methods: { - /** - * Fetches list of available languages - * @return {Promise} - */ - async getAvailableLanguages() { - if (!PROD) { - return translation.availableLanguages; - } - - let response; - - try { - response = await axios.get(translation.server.getAvailableLocalesURL); - } - catch (error) { - throw error; - } - - const languages = response.data.data.languages; - languages.unshift('en'); - - return _.uniq(languages); - }, - - /** - * Get current language used - * @return {string} current language - */ - getCurrentLanguage() { - return this.$i18n.i18next.language; - }, - - /** - * Set current language - * @param {string} lang - language abbreviation - * @example - * { - * mixins: [i18nMixin], - * ... - * mounted() { - * this.changeLanguage('en'); - * } - * } - */ - changeLanguage(lang) { - this.$i18n.i18next.changeLanguage(lang); - } - } -}; diff --git a/src/mixins/partials/form/form-field.js b/src/mixins/partials/form/form-field.js deleted file mode 100644 index 1546def..0000000 --- a/src/mixins/partials/form/form-field.js +++ /dev/null @@ -1,42 +0,0 @@ -import _ from 'lodash'; -import { uid } from 'quasar'; - - -function generateMethods(methodNames) { - return _.transform(methodNames, (methods, methodName) => { - methods[methodName] = function () { - return this.$refs[this.ref][methodName](...arguments); - }; - }, {}); -} - -export default { - - data() { - return { - ref: uid(), - }; - }, - - methods: generateMethods([ - 'abort', - 'add', - 'blur', - 'clear', - 'clearAndFocus', - 'close', - 'focus', - 'move', - 'open', - 'pick', - 'remove', - 'select', - 'set', - 'setCurrentSelection', - 'setValue', - 'toggleAmPm', - 'togglePass', - 'trigger', - 'upload', - ]), -}; diff --git a/src/pages/.gitkeep b/src/pages/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/plugins/.gitkeep b/src/plugins/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/plugins/axios.js b/src/plugins/axios.js index 9d271ed..365b425 100644 --- a/src/plugins/axios.js +++ b/src/plugins/axios.js @@ -1,54 +1,11 @@ -import _ from 'lodash'; -import axios from 'axios'; -import config from 'src/config'; -import qs from 'qs'; +import axiosHelper from 'src/helpers/axios'; -const defaultConfig = { - baseURL: config.server.baseURL, - paramsSerializer(params) { - return qs.stringify(params, { arrayFormat: 'brackets' }); - }, -}; - -/** - * Plugin for injecting axios globally as default $http resource - * @example - * import AxiosPlugin from 'src/plugins/axios-plugin'; - * ... - * Vue.use(AxiosPlugin); // defaultConfig will be merged with axios defaults - * Vue.use(AxiosPlugin, options); // options will be merged to defaultConfig and axios defaults - * @example - * // In a component you can use it like: - * this.$http(...); - * this.axios(...); - * @example - * // Creating new axios instance: - * const otherResourceXHR = this.axios.create(options); - */ -const AxiosPlugin = { - install(Vue, options) { - - _.merge(axios.defaults, defaultConfig, options); - - Vue.axios = axios; - - Object.defineProperties(Vue.prototype, { - axios: { - get() { - return axios; - } - }, - $http: { - get() { - return axios; - } +export default ({ Vue }) => { + Object.defineProperties(Vue.prototype, { + $axios: { + get() { + return axiosHelper; } - }); - } + } + }); }; - -if (typeof window !== 'undefined' && window.Vue) { - window.Vue.use(AxiosPlugin); -} - -export default AxiosPlugin; diff --git a/src/plugins/google-api.js b/src/plugins/google-api.js index 2839660..eb891ea 100644 --- a/src/plugins/google-api.js +++ b/src/plugins/google-api.js @@ -1,39 +1,16 @@ -import _ from 'lodash'; +import GoogleAPIClient from 'src/helpers/google-api-client'; +import googleAPIConfig from 'src/config/google-api'; -import googleAPIConfig from 'config/google-api'; -import GoogleAPIClient from 'helpers/google-api-client'; +export default async ({ Vue }) => { + const client = await GoogleAPIClient(googleAPIConfig); -/** - * Plugin for injecting google api - * @example - * import Vue from 'vue'; - * import GoogleAPIPlugin from 'src/plugins/google-api-plugin'; - * ... - * Vue.use(GoogleAPIPlugin); - * ... - * const googleClient = await this.$googleAPI - */ -const GoogleAPIPlugin = { - async install(Vue, options) { + Vue.googleAPI = client; - const config = _.defaults(options, googleAPIConfig); - - const client = await GoogleAPIClient(config); - - Vue.googleAPI = client; - - Object.defineProperties(Vue.prototype, { - $googleAPI: { - get() { - return client; - }, + Object.defineProperties(Vue.prototype, { + $googleAPI: { + get() { + return client; }, - }); - } + }, + }); }; - -if (typeof window !== 'undefined' && window.Vue) { - window.Vue.use(GoogleAPIPlugin); -} - -export default GoogleAPIPlugin; diff --git a/src/plugins/i18n.js b/src/plugins/i18n.js deleted file mode 100644 index 458967e..0000000 --- a/src/plugins/i18n.js +++ /dev/null @@ -1,107 +0,0 @@ -import _ from 'lodash'; - -import i18n from 'src/helpers/i18n'; - -/** - * Plugin for injecting i18n directive and filter - * @example - * import Vue from 'vue'; - * import i18nPlugin from 'src/plugins/i18n'; - * ... - * Vue.use(i18nPlugin); - * @example - * - * @example - * {{ 'key' | $t }} - * @example - * const label = this.$t('key'); - * this.$trans(['key1', 'key2']); // returns array of translated keys - * this.$trans(obj, ['key1', 'key2']); // returns obj with translated specified properties - * this.$trans(obj, ['key1', 'key2'], true); // returns obj with the translated specified properties only - * this.$trans(obj, ['nested.key']); // also supports nested properties - * this.$trans(obj, 'single-key'); // also supports single key using string - * this.$trans([obj1, obj2], 'single-key'); // also supports array of objects using single key using string - * this.$trans([obj1, obj2], ['key1', 'key2']); // also supports array of objects using multiple keys using array - */ -function translate (key, data) { - return i18n.i18next.t(key, data) -} - -const i18nPlugin = { - install(Vue) { - - Vue.directive('t', { - inserted: translateInnerHTML, - componentUpdated: translateInnerHTML - }); - Vue.filter('$t', translate); - Vue.mixin({ - methods: { - $trans(obj, props, trim = false) { - /** - * string key is passed: - * this.$trans('key') - */ - if (_.isString(obj)) { - return translate(obj); - } - - /** - * array of keys is passed: - * this.$trans(['key1', 'key2']) - */ - if (_.isArray(obj)) { - // array of objects and props is a path to the key - if (props) { - return _.map(obj, item => this.$trans(item, props, trim)); - } - - return _.map(obj, key => this.$trans(key)); - } - - /** - * translate some properties of an object: - * this.$trans(obj, 'key1'); - * this.$trans(obj, ['key1', 'key2']) - * trim other properties - * this.$trans(obj, ['key1', 'key2'], true) - */ - if (_.isPlainObject(obj) && (_.isString(props) || _.isArray(props))) { - props = _.isArray(props) ? props : [props]; - // Create a new object to avoid unnecessary behaviors when parameter is mutated. - const accumulator = _.pick(obj, trim ? props : _.keys(obj)); - - return _.transform(props, (result, prop) => { - if (_.has(result, prop)) { - _.set(result, prop, this.$trans(_.get(result, prop))); - } - }, accumulator); - } - - /** - * translate object format { key, data } - * this.$trans({ key: 'translate-me', data: { message: 'hello' } }) - */ - if (_.isPlainObject(obj) && _.has(obj, 'key') && !props) { - return translate(obj.key, obj.data || {}); - } - - return obj; - } - } - }) - } -}; - -function translateInnerHTML(element, binding) { - element.innerHTML = i18n.i18next.t( - binding.value.path || binding.value, - binding.value.args - ); -} - -if (typeof window !== 'undefined' && window.Vue) { - window.Vue.use(i18nPlugin); -} - -export default i18nPlugin; diff --git a/src/plugins/index.js b/src/plugins/index.js deleted file mode 100644 index 0b5e210..0000000 --- a/src/plugins/index.js +++ /dev/null @@ -1,17 +0,0 @@ -import Quasar from 'quasar'; -import Vuelidate from 'vuelidate'; -import AxiosPlugin from './axios'; -import ToastPlugin from './toast'; -import i18nPlugin from './i18n'; -import GoogleAPIPlugin from './google-api'; -import JWTPlugin from './jwt'; - -export default [ - Quasar, - Vuelidate, - AxiosPlugin, - ToastPlugin, - i18nPlugin, - GoogleAPIPlugin, - JWTPlugin, -]; diff --git a/src/plugins/jwt.js b/src/plugins/jwt.js index 6949a0b..a33664f 100644 --- a/src/plugins/jwt.js +++ b/src/plugins/jwt.js @@ -1,37 +1,16 @@ import JWT from 'src/helpers/jwt'; +import config from 'src/config'; -import config from 'config'; +export default ({ Vue }) => { + const jwt = new JWT(config.JWT_STORAGE_KEY); -/** - * Plugin for injecting axios globally as default $http resource - * @example - * import JWTPlugin from 'src/plugins/jwt'; - * ... - * Vue.use(JWTPlugin); - * @example - * // In a component you can use it like: - * this.$jwt.decode(...); - * Vue.jwt.decode(...); - */ -const JWTPlugin = { - install(Vue) { + Vue.jwt = jwt; - const jwt = new JWT(config.ACCESS_TOKEN_KEY); - - Vue.jwt = jwt; - - Object.defineProperties(Vue.prototype, { - $jwt: { - get() { - return jwt; - } + Object.defineProperties(Vue.prototype, { + $jwt: { + get() { + return jwt; } - }); - } + } + }); }; - -if (typeof window !== 'undefined' && window.Vue) { - window.Vue.use(JWTPlugin); -} - -export default JWTPlugin; diff --git a/src/plugins/toast.js b/src/plugins/toast.js deleted file mode 100644 index f0c2db2..0000000 --- a/src/plugins/toast.js +++ /dev/null @@ -1,47 +0,0 @@ -import _ from 'lodash'; -import { Toast } from 'quasar'; -import i18n from 'src/helpers/i18n'; - -const defaultConfig = { - timeout: 5000, -}; - -/** - * Plugin for injecting toast helper methods - * @example - * import Vue from 'vue'; - * import ToastPlugin from 'src/plugins/toast'; - * ... - * Vue.use(ToastPlugin); - * @example - * this.$toast.error('error-occurred', error.type); - */ -const ToastPlugin = { - install(Vue, options) { - - Toast.setDefaults(_.merge(defaultConfig, options)); - - const toastHandlers = { - error: (msg, param) => Toast.create.negative(i18n.i18next.t(msg, param)), - success: (msg, param) => Toast.create.positive(i18n.i18next.t(msg, param)), - warning: (msg, param) => Toast.create.warning(i18n.i18next.t(msg, param)), - info: (msg, param) => Toast.create.info(i18n.i18next.t(msg, param)) - }; - - Vue.toast = toastHandlers; - - Object.defineProperties(Vue.prototype, { - $toast: { - get() { - return toastHandlers; - } - } - }) - } -}; - -if (typeof window !== 'undefined' && window.Vue) { - window.Vue.use(ToastPlugin); -} - -export default ToastPlugin; diff --git a/src/plugins/vue-analytics.js b/src/plugins/vue-analytics.js new file mode 100644 index 0000000..a86c8b1 --- /dev/null +++ b/src/plugins/vue-analytics.js @@ -0,0 +1,25 @@ +import VueAnalytics from 'vue-analytics'; +import router from 'src/router'; + +const PROD = process.env.PROD; +/** + * Set Google Analytics ID in src/config/env//index.js > GOOGLE_ANALYTICS_ID. + */ +const GOOGLE_ANALYTICS_ID = process.env.GOOGLE_ANALYTICS_ID; + +export default ({ Vue }) => { + Vue.use(VueAnalytics, { + id: GOOGLE_ANALYTICS_ID, + router, + autoTracking: { + transformQueryString: false, + skipSamePath: true, + exception: true, + exceptionLogs: false, + }, + debug: { + sendHitTask: PROD, + enabled: !PROD, + } + }); +}; diff --git a/src/plugins/vue-i18next.js b/src/plugins/vue-i18next.js new file mode 100644 index 0000000..040ffe7 --- /dev/null +++ b/src/plugins/vue-i18next.js @@ -0,0 +1,19 @@ +import i18next from 'i18next'; +import i18nextXHRBackend from 'i18next-xhr-backend'; +import i18nextLangDetector from 'i18next-browser-languagedetector'; +import VueI18next from '@panter/vue-i18next'; +import i18nextConfig from 'src/config/i18next'; + +export default ({ app, Vue }) => { + Vue.use(VueI18next); + + i18next.use(i18nextLangDetector); + + if (i18nextConfig.backend) { + i18next.use(i18nextXHRBackend); + } + + i18next.init(i18nextConfig); + + app.i18n = new VueI18next(i18next); +}; diff --git a/src/plugins/vuelidate.js b/src/plugins/vuelidate.js new file mode 100644 index 0000000..3f08d22 --- /dev/null +++ b/src/plugins/vuelidate.js @@ -0,0 +1,5 @@ +import Vuelidate from 'vuelidate'; + +export default ({ Vue }) => { + Vue.use(Vuelidate); +}; diff --git a/src/router.js b/src/router.js deleted file mode 100644 index a750242..0000000 --- a/src/router.js +++ /dev/null @@ -1,30 +0,0 @@ -import Vue from 'vue'; -import VueRouter from 'vue-router'; - -Vue.use(VueRouter); - -/* - * Uncomment this section and use "load()" if you want - * to lazy load routes. - */ -/* function load (component) { - // '@' is aliased to src/components - return () => import(`@/${component}.vue`) -} */ - -export default new VueRouter({ - /* - * NOTE! VueRouter "history" mode DOESN'T works for Cordova builds, - * it is only to be used only for websites. - * - * If you decide to go with "history" mode, please also open /config/index.js - * and set "build.publicPath" to something other than an empty string. - * Example: '/' instead of current '' - * - * If switching back to default "hash" mode, don't forget to set the - * build publicPath back to '' so Cordova builds work again. - */ - - routes: [ - ] -}); diff --git a/src/router/index.js b/src/router/index.js new file mode 100644 index 0000000..4a87889 --- /dev/null +++ b/src/router/index.js @@ -0,0 +1,24 @@ +import Vue from 'vue'; +import VueRouter from 'vue-router'; + +import routes from './routes'; + +Vue.use(VueRouter); + +const Router = new VueRouter({ + /* + * NOTE! Change Vue Router mode from quasar.conf.js -> build -> vueRouterMode + * + * When going with "history" mode, please also make sure "build.publicPath" + * is set to something other than an empty string. + * Example: '/' instead of '' + */ + + // Leave as is and change from quasar.conf.js instead! + mode: process.env.VUE_ROUTER_MODE, + base: process.env.VUE_ROUTER_BASE, + scrollBehavior: () => ({ y: 0 }), + routes +}); + +export default Router; diff --git a/src/router/routes.js b/src/router/routes.js new file mode 100644 index 0000000..d6d1738 --- /dev/null +++ b/src/router/routes.js @@ -0,0 +1 @@ +export default []; diff --git a/src/statics/app-logo.png b/src/statics/app-logo.png new file mode 100644 index 0000000..590e8ce Binary files /dev/null and b/src/statics/app-logo.png differ diff --git a/src/statics/icons/apple-icon-152x152.png b/src/statics/icons/apple-icon-152x152.png new file mode 100644 index 0000000..c918acd Binary files /dev/null and b/src/statics/icons/apple-icon-152x152.png differ diff --git a/src/statics/icons/favicon-16x16.png b/src/statics/icons/favicon-16x16.png new file mode 100644 index 0000000..177c86e Binary files /dev/null and b/src/statics/icons/favicon-16x16.png differ diff --git a/src/statics/icons/favicon-32x32.png b/src/statics/icons/favicon-32x32.png new file mode 100644 index 0000000..b8e6cdf Binary files /dev/null and b/src/statics/icons/favicon-32x32.png differ diff --git a/src/statics/icons/icon-128x128.png b/src/statics/icons/icon-128x128.png new file mode 100644 index 0000000..459937d Binary files /dev/null and b/src/statics/icons/icon-128x128.png differ diff --git a/src/statics/icons/icon-192x192.png b/src/statics/icons/icon-192x192.png new file mode 100644 index 0000000..d9226ca Binary files /dev/null and b/src/statics/icons/icon-192x192.png differ diff --git a/src/statics/icons/icon-256x256.png b/src/statics/icons/icon-256x256.png new file mode 100644 index 0000000..62305cf Binary files /dev/null and b/src/statics/icons/icon-256x256.png differ diff --git a/src/statics/icons/icon-384x384.png b/src/statics/icons/icon-384x384.png new file mode 100644 index 0000000..ce4da3b Binary files /dev/null and b/src/statics/icons/icon-384x384.png differ diff --git a/src/statics/icons/icon-512x512.png b/src/statics/icons/icon-512x512.png new file mode 100644 index 0000000..902d41d Binary files /dev/null and b/src/statics/icons/icon-512x512.png differ diff --git a/src/statics/icons/ms-icon-144x144.png b/src/statics/icons/ms-icon-144x144.png new file mode 100644 index 0000000..b0880e8 Binary files /dev/null and b/src/statics/icons/ms-icon-144x144.png differ diff --git a/src/statics/manifest.json b/src/statics/manifest.json deleted file mode 100644 index 3b0f4c0..0000000 --- a/src/statics/manifest.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "Quasar App", - "short_name": "Quasar-PWA", - "start_url": "/index.html", - "display": "standalone", - "background_color": "#FFFFFF", - "theme_color": "#1A82DC" -} diff --git a/src/store/actions.js b/src/store/actions.js deleted file mode 100644 index 86d8c8f..0000000 --- a/src/store/actions.js +++ /dev/null @@ -1,4 +0,0 @@ -'use strict'; - -export default { -}; diff --git a/src/store/getters.js b/src/store/getters.js deleted file mode 100644 index 86d8c8f..0000000 --- a/src/store/getters.js +++ /dev/null @@ -1,4 +0,0 @@ -'use strict'; - -export default { -}; diff --git a/src/store/index.js b/src/store/index.js index e083e42..f9585c2 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,21 +1,12 @@ -'use strict'; - import Vue from 'vue'; import Vuex from 'vuex'; -import modules from './modules'; -import state from './state'; -import getters from './getters'; -import actions from './actions'; -import mutations from './mutations'; Vue.use(Vuex); -export default new Vuex.Store({ - modules, - state, - getters, - actions, - mutations, - strict: !PROD, +const store = new Vuex.Store({ + modules: {}, + strict: !process.env.PROD }); + +export default store; diff --git a/src/store/modules/index.js b/src/store/modules/index.js deleted file mode 100644 index 86d8c8f..0000000 --- a/src/store/modules/index.js +++ /dev/null @@ -1,4 +0,0 @@ -'use strict'; - -export default { -}; diff --git a/src/store/mutations.js b/src/store/mutations.js deleted file mode 100644 index 3e0f608..0000000 --- a/src/store/mutations.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; - -/** - * Contains map of mutation types and methods - * @example - * import { mutationTypes } from './mutations'; - * ... - * async exampleAction({ commit }, payload) { - * commit(mutationTypes.SOME_METHOD, payload); - * } - */ -export const mutationTypes = { -}; - -export default { -}; diff --git a/src/store/state.js b/src/store/state.js deleted file mode 100644 index 86d8c8f..0000000 --- a/src/store/state.js +++ /dev/null @@ -1,4 +0,0 @@ -'use strict'; - -export default { -}; diff --git a/src/themes/app.ios.styl b/src/themes/app.ios.styl deleted file mode 100644 index 9066fd8..0000000 --- a/src/themes/app.ios.styl +++ /dev/null @@ -1,14 +0,0 @@ -// This file is included in the build if src/main.js imports it. -// Otherwise the default iOS CSS file is bundled. -// Check "DEFAULT / CUSTOM STYLE" in src/main.js - -// App Shared Variables -// -------------------------------------------------- -// Shared Stylus variables go in the app.variables.styl file -@import 'app.variables' - -// Quasar iOS Design Stylus -// -------------------------------------------------- -// Custom App variables must be declared before importing Quasar. -// Quasar will use its default values when a custom variable isn't provided. -@import '~quasar-framework/dist/quasar.ios.styl' diff --git a/src/themes/app.mat.styl b/src/themes/app.mat.styl deleted file mode 100644 index 6554060..0000000 --- a/src/themes/app.mat.styl +++ /dev/null @@ -1,14 +0,0 @@ -// This file is included in the build if src/main.js imports it. -// Otherwise the default Material CSS file is bundled. -// Check "DEFAULT / CUSTOM STYLE" in src/main.js - -// App Shared Variables -// -------------------------------------------------- -// Shared Stylus variables go in the app.variables.styl file -@import 'app.variables' - -// Quasar Material Design Stylus -// -------------------------------------------------- -// Custom App variables must be declared before importing Quasar. -// Quasar will use its default values when a custom variable isn't provided. -@import '~quasar-framework/dist/quasar.mat.styl' diff --git a/src/themes/app.variables.styl b/src/themes/app.variables.styl deleted file mode 100644 index c16901b..0000000 --- a/src/themes/app.variables.styl +++ /dev/null @@ -1,38 +0,0 @@ -// This file is included in the build if src/main.js imports -// either app.mat.styl or app.ios.styl. -// Check "DEFAULT / CUSTOM STYLE" in src/main.js - -// App Shared Variables -// -------------------------------------------------- -// To customize the look and feel of this app, you can override -// the Stylus variables found in Quasar's source Stylus files. Setting -// variables before Quasar's Stylus will use these variables rather than -// Quasar's default Stylus variable values. Stylus variables specific -// to the themes belong in either the app.ios.styl or app.mat.styl files. - -// App Shared Color Variables -// -------------------------------------------------- -// It's highly recommended to change the default colors -// to match your app's branding. - -// Style Guide -// https://docs.google.com/document/d/1PepJ-RaraW2YsiCAO2hFv-zGqaYysIHOL5TRalj-Tpk/edit# - -@import url('https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700') - -$typography-font-family = 'Open Sans', '-apple-system', 'Helvetica Neue', Helvetica, Arial, sans-serif - -$primary = #1A82DC -$secondary = #FFFFFF -$tertiary = #E6E6E6 - -$neutral = #E0E1E2 -$positive = #27AE60 -$negative = #E74C3C -$info = #2196F3 -$warning = #F1C40F - -$white = #FFFFFF -$light = #F4F4F4 -$dark = #2E2E2E -$faded = #E8E8E8 diff --git a/src/themes/quasar.variables.styl b/src/themes/quasar.variables.styl deleted file mode 100644 index 1532d22..0000000 --- a/src/themes/quasar.variables.styl +++ /dev/null @@ -1,24 +0,0 @@ -// -// Webpack alias "variables" points to this file. -// So you can import it in your app's *.vue files -// inside the - - -// First we load app's Stylus variables -@import 'app.variables' - -// Then we load Quasar Stylus variables. -// Any variables defined in "app.variables.styl" -// will override Quasar's ones. -// -// NOTICE that we only import Core Quasar Variables -// like colors, media breakpoints, and so. -// No component variable will be included. -@import '~quasar/dist/core.variables' diff --git a/templates/component.vue b/templates/component.vue deleted file mode 100644 index c94d042..0000000 --- a/templates/component.vue +++ /dev/null @@ -1,14 +0,0 @@ - - - - - diff --git a/templates/form.vue b/templates/form.vue deleted file mode 100644 index 9b2da7f..0000000 --- a/templates/form.vue +++ /dev/null @@ -1,51 +0,0 @@ - - - - - diff --git a/templates/layout.vue b/templates/layout.vue deleted file mode 100644 index 6ada642..0000000 --- a/templates/layout.vue +++ /dev/null @@ -1,26 +0,0 @@ - - - - - diff --git a/templates/page.vue b/templates/page.vue deleted file mode 100644 index 694631b..0000000 --- a/templates/page.vue +++ /dev/null @@ -1,17 +0,0 @@ - - - - - diff --git a/test/.eslintrc.js b/test/.eslintrc.js deleted file mode 100644 index 5aa86e0..0000000 --- a/test/.eslintrc.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - env: { - mocha: true - }, - globals: { - should: true, - sinon: true - } -}; diff --git a/test/unit/index.js b/test/unit/index.js deleted file mode 100644 index 51fda86..0000000 --- a/test/unit/index.js +++ /dev/null @@ -1,21 +0,0 @@ -require('jsdom-global')(); - -import Vue from 'vue'; -import Quasar from 'quasar'; - - -Vue.config.productionTip = false; - -Vue.use(Quasar); - -// require all test files (files that ends with .spec.js) -const testsContext = require.context('./specs', true, /\.spec$/); -testsContext.keys().forEach(testsContext); - -// require all files to be subjected under code coverage. -const srcContext = require.context( - 'src', - true, - /^\.\/((?!(main|router)(\.js)?$|data\/|config\/|statics\/|assets\/))/ -); -srcContext.keys().forEach(srcContext); diff --git a/test/unit/karma.conf.js b/test/unit/karma.conf.js deleted file mode 100644 index 2f321d9..0000000 --- a/test/unit/karma.conf.js +++ /dev/null @@ -1,31 +0,0 @@ -const webpackConfig = require('../../build/webpack.test.conf'); - -module.exports = function (config) { - config.set({ - // to run in additional browsers: - // 1. install corresponding karma launcher - // http://karma-runner.github.io/0.13/config/browsers.html - // 2. add it to the `browsers` array below. - browsers: ['PhantomJS'], - frameworks: ['mocha', 'sinon-chai', 'phantomjs-shim'], - reporters: ['spec', 'coverage'], - files: ['./index.js'], - preprocessors: { - './index.js': ['webpack', 'sourcemap'], - }, - webpack: webpackConfig, - webpackMiddleware: { - noInfo: true - }, - coverageReporter: { - dir: './coverage', - reporters: [ - { type: 'lcov', subdir: '.' }, - { type: 'text-summary' } - ], - instrumenterOptions: { - istanbul: { noCompact: true } - } - }, - }); -}; diff --git a/test/unit/specs/components/partials/form/FormField.spec.js b/test/unit/specs/components/partials/form/FormField.spec.js deleted file mode 100644 index 446633a..0000000 --- a/test/unit/specs/components/partials/form/FormField.spec.js +++ /dev/null @@ -1,142 +0,0 @@ -import _ from 'lodash'; -import { - mount, - shallow, - createLocalVue -} from '@vue/test-utils'; -import Vuelidate from 'vuelidate'; -import { - QCheckbox, - QChipsInput, - QDatetime, - QDatetimeRange, - QDialogSelect, - QField, - QInput, - QKnob, - QOptionGroup, - QRadio, - QRange, - QRating, - QSearch, - QSelect, - QSlider, - QToggle, - QUploader, -} from 'quasar'; - -import FormField from '@/partials/form/FormField'; - -describe('FormField.vue', function () { - - const localVue = createLocalVue(); - localVue.use(Vuelidate); - - it('should load the proper quasar component', function () { - - // test all wrapped components except QInlineDatetime since it is a functional component - const quasarComponents = { - 'checkbox': QCheckbox, - 'chips-input': QChipsInput, - 'datetime': QDatetime, - 'datetime-range': QDatetimeRange, - 'dialog-select': QDialogSelect, - 'input': QInput, - 'knob': QKnob, - 'option-group': QOptionGroup, - 'radio': QRadio, - 'range': QRange, - 'rating': QRating, - 'search': QSearch, - 'select': QSelect, - 'slider': QSlider, - 'toggle': QToggle, - 'uploader': QUploader, - }; - - const stubComponent = '
'; - - _.forOwn(quasarComponents, (value, key) => { - const wrapper = mount(FormField, { - propsData: { - type: key, - model: '', - validation: {}, - fieldProps: {} - }, - localVue, - stubs: { - [`q-${key}`]: stubComponent, - } - }); - - wrapper.contains(QField).should.be.equals(true); - wrapper.contains('.quasar').should.be.equals(true); - }); - }); - - it('should have access to methods of quasar components', function () { - - // test all components that have methods - const quasarComponents = { - 'chips-input': [ - 'add', - 'remove', - 'focus', - 'select', - ], - 'datetime': [ - 'open', - 'close', - 'clear', - ], - 'dialog-select': [ - 'pick', - 'close', - ], - 'inline-datetime': [ - 'clear', - 'toggleAmPm' - ], - 'input': [ - 'clear', - 'togglePass', - 'focus', - 'blur', - 'select', - ], - 'rating': [ - 'set' - ], - 'search': [ - 'clear', - 'clearAndFocus', - 'focus', - 'blur', - 'select', - ], - 'select': [ - 'open', - 'close', - ], - 'uploader': [ - 'abort', - 'upload', - ], - }; - - _.forOwn(quasarComponents, (value, key) => { - const wrapper = shallow(FormField, { - propsData: { - type: key, - model: '', - validation: {}, - fieldProps: {} - }, - localVue, - }); - - _.every(value, _.partial(_.has, wrapper.vm)).should.be.equals(true); - }); - }); -}); diff --git a/test/unit/specs/helpers/google-api-client.spec.js b/test/unit/specs/helpers/google-api-client.spec.js deleted file mode 100644 index de0d4ae..0000000 --- a/test/unit/specs/helpers/google-api-client.spec.js +++ /dev/null @@ -1,44 +0,0 @@ -import _ from 'lodash'; -import sinon from 'sinon'; - -import googleConfig from 'src/config/google-api'; -import GoogleAPIClient from 'src/helpers/google-api-client'; - - -const sandbox = sinon.createSandbox({ - useFakeServer: true -}); -/** - * @test {GoogleAPIClient} - */ -describe('GoogleAPIClient', function () { - - afterEach(function () { - sandbox.restore(); - }); - /** - * @test {GoogleAPIClient} - */ - it('GoogleAPIClient should request for google api script', function () { - - sandbox.useFakeServer(); - const content = '(function(){})()'; - sandbox.server.respondWith('GET', googleConfig.clientURL, [ - 200, - { 'Content-Type': 'application/javascript' }, - content - ]); - const callback = sandbox.spy(); - const body = sandbox.stub(document.body, 'appendChild'); - - delete window.gapi; - - GoogleAPIClient(_.pick(googleConfig, ['clientURL', 'libraries'])) - .then(callback); - - sandbox.server.respond(); - - callback.calledWith.should.be.a('function'); - body.calledOnce.should.be.equals(true); - }); -}); diff --git a/test/unit/specs/helpers/i18n.spec.js b/test/unit/specs/helpers/i18n.spec.js deleted file mode 100644 index 62ab567..0000000 --- a/test/unit/specs/helpers/i18n.spec.js +++ /dev/null @@ -1,12 +0,0 @@ -import i18nHelper from 'src/helpers/i18n'; -import VueI18next from '@panter/vue-i18next'; - -/** - * @test {i18nHelper} - */ -describe('i18nHelper', function () { - - it('should be an instance of VueI18Next', function () { - i18nHelper.should.be.an.instanceof(VueI18next); - }); -}); diff --git a/test/unit/specs/helpers/jwt.spec.js b/test/unit/specs/helpers/jwt.spec.js deleted file mode 100644 index 391bbf3..0000000 --- a/test/unit/specs/helpers/jwt.spec.js +++ /dev/null @@ -1,63 +0,0 @@ -import { LocalStorage } from 'quasar'; -import jwt from 'jsonwebtoken'; -import JWTHelper from 'src/helpers/jwt'; - -/** - * @test {jwtHelper} - */ -describe('JWTHelper', function () { - - beforeEach(function () { - LocalStorage.clear(); - }); - - it('should be able to create an instance of JWTHelper', function () { - (new JWTHelper('')).should.be.instanceof(JWTHelper); - }); - - it('JWTHelper#hasToken should check token presence', function () { - const key = 'foo'; - const jwtHelper = new JWTHelper(key); - - jwtHelper.hasToken().should.be.equals(false); - LocalStorage.set(key, 'value'); - jwtHelper.hasToken().should.be.equals(true); - }); - - it('JWTHelper#getToken should return token', function () { - const key = 'foo'; - const jwtHelper = new JWTHelper(key); - - should.equal(jwtHelper.getToken(), null); - LocalStorage.set(key, 'value'); - jwtHelper.getToken().should.be.equals('value'); - }); - - it('JWTHelper#setToken should check token presence', function () { - const key = 'foo'; - const jwtHelper = new JWTHelper(key); - - jwtHelper.setToken('value'); - LocalStorage.get.item(key).should.be.equals('value'); - }); - - it('JWTHelper#removeToken should check token presence', function () { - const key = 'foo'; - const jwtHelper = new JWTHelper(key); - - LocalStorage.set(key, 'value'); - jwtHelper.removeToken(); - jwtHelper.hasToken().should.be.equals(false); - }); - - it('JWTHelper#decode should check token presence', function () { - const key = 'foo'; - const jwtHelper = new JWTHelper(key); - - LocalStorage.set(key, jwt.sign({ some: 'value' }, 'someSecret')); - const token = jwtHelper.decode(); - token.should.have.property('header'); - token.should.have.property('payload'); - token.payload.some.should.be.equals('value'); - }); -}); diff --git a/test/unit/specs/plugins/axios.spec.js b/test/unit/specs/plugins/axios.spec.js deleted file mode 100644 index 0c05af8..0000000 --- a/test/unit/specs/plugins/axios.spec.js +++ /dev/null @@ -1,27 +0,0 @@ -import { createLocalVue, shallow } from '@vue/test-utils'; -import axios from 'axios'; - -import AxiosPlugin from 'src/plugins/axios'; - -/** - * @test {AxiosPlugin} - */ -describe('AxiosPlugin', function () { - - it('should install axios into `axios` and `$http` Vue instance property', function () { - - const localVue = createLocalVue(); - - localVue.use(AxiosPlugin); - - const component = { - template: `

`, - }; - const wrapper = shallow(component, { - localVue - }); - - wrapper.vm.$http.should.be.equals(axios); - wrapper.vm.axios.should.be.equals(axios); - }); -}); diff --git a/test/unit/specs/plugins/google-api.spec.js b/test/unit/specs/plugins/google-api.spec.js deleted file mode 100644 index b4eecb6..0000000 --- a/test/unit/specs/plugins/google-api.spec.js +++ /dev/null @@ -1,35 +0,0 @@ -import _ from 'lodash'; - -import { createLocalVue, shallow } from '@vue/test-utils'; - -import GoogleAPIPlugin from 'src/plugins/google-api'; - -/** - * @test {GoogleAPIPlugin} - */ -describe('GoogleAPIPlugin', function () { - - it('should install google api client into `$googleAPI` Vue instance property and `googleAPI` global property', function (done) { - - const localVue = createLocalVue(); - - // mock window.gapi to resolve the plugin immediately - window.gapi = {}; - - localVue.use(GoogleAPIPlugin); - - const component = { - template: `

`, - }; - const wrapper = shallow(component, { - localVue - }); - - _.defer(() => { - localVue.googleAPI.should.be.equals(window.gapi); - wrapper.vm.$googleAPI.should.be.equals(window.gapi); - delete window.gapi; - done(); - }); - }); -}); diff --git a/test/unit/specs/plugins/i18n.spec.js b/test/unit/specs/plugins/i18n.spec.js deleted file mode 100644 index f73c0d5..0000000 --- a/test/unit/specs/plugins/i18n.spec.js +++ /dev/null @@ -1,186 +0,0 @@ -import i18next from 'i18next'; -import VueI18next from '@panter/vue-i18next'; -import { createLocalVue, shallow } from '@vue/test-utils'; - -import i18nPlugin from 'src/plugins/i18n'; - -/** - * @test {i18nPlugin} - */ -describe('i18nPlugin', function () { - - const localVue = createLocalVue(); - - i18next.init({ - resources: { - en: { - index: { - key1: 'value1', - key2: 'value2', - key3: 'value: {{ data }}' - } - } - }, - defaultNS: 'index', - lng: 'en', - debug: true - }); - - localVue.use(VueI18next); - localVue.use(i18nPlugin); - - const component = { - template: `

{{ 'key2' | $t }}

{{ 'key3' | $t({ data: 'foo' }) }}

`, - }; - const wrapper = shallow(component, { - localVue, - i18n: new VueI18next(i18next) - }); - - it('should install i18n directive and filter', function (done) { - wrapper.vm.$t.should.be.a('function'); - localVue.nextTick(() => { - wrapper.html().should.be.equals(`

value1

value2

value: foo

`); - done(); - }); - }); - - it('$t filter should translate string key', function () { - wrapper.vm.$t('key1').should.be.equals('value1'); - }); - - it('$t filter should interpolate data', function () { - wrapper.vm.$t('key3', { data: 'foo' }).should.equals('value: foo'); - }); - - it('$trans mixin method should translate string key', function () { - wrapper.vm.$trans('key1').should.be.equals('value1'); - }); - - it('$trans mixin method should return null when given null', function () { - should.equal(wrapper.vm.$trans(null), null); - }); - - it('$trans mixin method should return undefined when no keys passed', function () { - should.equal(wrapper.vm.$trans(undefined), undefined); - }); - - it('$trans mixin method should return untranslated key', function () { - wrapper.vm.$trans('unknown-key').should.be.equals('unknown-key'); - }); - - it('$trans mixin method should translate array of keys', function () { - wrapper.vm.$trans(['key1', 'key2']).should.deep.equals(['value1', 'value2']); - }); - - it('$trans mixin method should translate array of keys and return untranslated keys', function () { - wrapper.vm.$trans(['key1', 'unknown-key']).should.deep.equals(['value1', 'unknown-key']); - }); - - it('$trans mixin method should return empty array when given an empty array', function () { - wrapper.vm.$trans([]).should.deep.equals([]); - }); - - it('$trans mixin method should translate specified keys of an object', function () { - wrapper.vm.$trans({ key1: 'key1', key2: 'key2', extraKey: 'extra' }, ['key1', 'key2']) - .should.deep.equals({ key1: 'value1', key2: 'value2', extraKey: 'extra' }); - }); - - it( - '$trans mixin method should not inject a new property when translating a key which is not an object propery', - function () { - wrapper.vm.$trans({ key1: 'key1', key2: 'key2', extraKey: 'extra' }, ['key1', 'key2', 'missing-key']) - .should.deep.equals({ key1: 'value1', key2: 'value2', extraKey: 'extra' }); - } - ); - - it('$trans mixin method should trim other properties of an object when trim is true', function () { - wrapper.vm.$trans({ key1: 'key1', key2: 'key2', extraKey: 'extra' }, ['key1', 'key2'], true) - .should.deep.equals({ key1: 'value1', key2: 'value2' }); - }); - - it('$trans mixin method should translate a property of an object mapped to an array of keys', function () { - wrapper.vm.$trans({ keys: ['key1', 'key2'], extraKey: 'extra' }, ['keys'], true) - .should.deep.equals({ keys: ['value1', 'value2'] }); - }); - - it('$trans mixin method should not translate a property of an object mapped to an object', function () { - wrapper.vm.$trans({ keys: {}, extraKey: 'extra' }, ['keys'], true) - .should.deep.equals({ keys: {} }); - }); - - it('$trans mixin method should translate a nested property of an object', function () { - wrapper.vm.$trans({ keys: { key1: 'key1' }, extraKey: 'extra' }, ['keys.key1'], true) - .should.deep.equals({ keys: { key1: 'value1' } }); - }); - - it('$trans mixin method should translate a nested property of an object mapped to an array of keys', function () { - wrapper.vm.$trans({ keys: { subkeys: ['key1', 'key2'] }, extraKey: 'extra' }, ['keys.subkeys'], true) - .should.deep.equals({ keys: { subkeys: ['value1', 'value2'] } }); - }); - - it('$trans mixin method should not translate a nested property of an object mapped to an object', function () { - wrapper.vm.$trans({ keys: { subkeys: {} }, extraKey: 'extra' }, ['keys.subkeys'], true) - .should.deep.equals({ keys: { subkeys: {} } }); - }); - - it('$trans mixin method should return the object when props is not given', function () { - wrapper.vm.$trans({ key1: 'key1', key2: 'key2', extraKey: 'extra' }) - .should.deep.equals({ key1: 'key1', key2: 'key2', extraKey: 'extra' }); - }); - - it('$trans mixin method should return the object when props is null', function () { - wrapper.vm.$trans({ key1: 'key1', key2: 'key2', extraKey: 'extra' }, null) - .should.deep.equals({ key1: 'key1', key2: 'key2', extraKey: 'extra' }); - }); - - it('$trans mixin method should support single property for object using string', function () { - wrapper.vm.$trans({ key1: 'key1', key2: 'key2', extraKey: 'extra' }, 'key1') - .should.deep.equals({ key1: 'value1', key2: 'key2', extraKey: 'extra' }); - }); - - it('$trans mixin method should not inject a missing property to the object', function () { - wrapper.vm.$trans({ key1: 'key1', key2: 'key2', extraKey: 'extra' }, 'missing-key') - .should.deep.equals({ key1: 'key1', key2: 'key2', extraKey: 'extra' }); - }); - - it('$trans mixin method should return property value when no matching translation', function () { - wrapper.vm.$trans({ key1: 'key1', key2: 'key2', extraKey: 'extra' }, 'extraKey') - .should.deep.equals({ key1: 'key1', key2: 'key2', extraKey: 'extra' }); - }); - - it('$trans mixin method should translate array of properties on an array of objects', function () { - wrapper.vm.$trans([{ label: 'key1' }, { label: 'key2' }], ['label'], true) - .should.deep.equals([{ label: 'value1'}, { label: 'value2'}]); - }); - - it('$trans mixin method should translate a string property on an array of objects', function () { - wrapper.vm.$trans([{ label: 'key1'}, { label: 'key2'}], 'label') - .should.deep.equals([{ label: 'value1'}, { label: 'value2'}]); - }); - - it('$trans mixin method should return the array when props does not exist', function () { - wrapper.vm.$trans([{ label: 'key1'}, { label: 'key2'}], 'nonexisting') - .should.deep.equals([{ label: 'key1'}, { label: 'key2'}]); - }); - - it('$trans mixin method should still return the translated array when some props do not exist', function () { - wrapper.vm.$trans([{ label: 'key1'}, { label: 'key2'}], ['label', 'nonexisting']) - .should.deep.equals([{ label: 'value1'}, { label: 'value2'}]); - }); - - it('$trans mixin method should translate object with format { key, data }', function () { - wrapper.vm.$trans({ key: 'key3', data: { data: 'data3' } }) - .should.be.equals('value: data3'); - }); - - it('$trans mixin method should translate object with format { key, data } inside an array', function () { - wrapper.vm.$trans([{ key: 'key3', data: { data: 'data3' } }]) - .should.deep.equals(['value: data3']); - }); - - it('$trans mixin method should translate object with format { key, data } inside an object', function () { - wrapper.vm.$trans({ prop: { key: 'key3', data: { data: 'data3' } } }, 'prop') - .should.deep.equals({ prop: 'value: data3' }); - }); -}); diff --git a/test/unit/specs/plugins/jwt.spec.js b/test/unit/specs/plugins/jwt.spec.js deleted file mode 100644 index 0007836..0000000 --- a/test/unit/specs/plugins/jwt.spec.js +++ /dev/null @@ -1,27 +0,0 @@ -import { createLocalVue, shallow } from '@vue/test-utils'; - -import JWTPlugin from 'src/plugins/jwt'; -import JWT from 'src/helpers/jwt'; - -/** - * @test {JWTPlugin} - */ -describe('JWTPlugin', function () { - - it('should install jwt into `jwt` and `$jwt` Vue instance property', function () { - - const localVue = createLocalVue(); - - localVue.use(JWTPlugin); - - const component = { - template: `

`, - }; - const wrapper = shallow(component, { - localVue - }); - - localVue.jwt.should.be.instanceof(JWT); - wrapper.vm.$jwt.should.be.instanceof(JWT); - }); -}); diff --git a/test/unit/specs/plugins/toast.spec.js b/test/unit/specs/plugins/toast.spec.js deleted file mode 100644 index c589498..0000000 --- a/test/unit/specs/plugins/toast.spec.js +++ /dev/null @@ -1,35 +0,0 @@ -import { createLocalVue, shallow } from '@vue/test-utils'; - -import ToastPlugin from 'src/plugins/toast'; - -/** - * @test {ToastPlugin} - */ -describe('ToastPlugin', function () { - - it('should install helper methods for toast', function () { - - const localVue = createLocalVue(); - - localVue.use(ToastPlugin); - - const component = { - template: `

`, - }; - const wrapper = shallow(component, { - localVue - }); - - wrapper.vm.$toast.should.be.an('object'); - wrapper.vm.$toast.error.should.be.a('function'); - wrapper.vm.$toast.info.should.be.a('function'); - wrapper.vm.$toast.success.should.be.a('function'); - wrapper.vm.$toast.warning.should.be.a('function'); - - localVue.toast.should.be.an('object'); - localVue.toast.error.should.be.a('function'); - localVue.toast.info.should.be.a('function'); - localVue.toast.success.should.be.a('function'); - localVue.toast.warning.should.be.a('function'); - }); -});