├── src ├── filters │ └── index.js ├── mixins │ ├── index.js │ ├── data.mixin.js │ ├── form.mixin.js │ └── ResizeHandler.js ├── layouts │ ├── default.vue │ ├── auth.vue │ └── main.vue ├── interceptors │ ├── index.js │ └── auth.interceptor.js ├── shims-vue.d.ts ├── common │ ├── constants │ │ └── appConstants.js │ └── validator.js ├── translations │ ├── index.js │ ├── validation-messages.js │ └── en.js ├── styles │ ├── fonts │ │ ├── icomoon.eot │ │ ├── icomoon.ttf │ │ ├── icomoon.woff │ │ └── icomoon.svg │ ├── transition.scss │ ├── imports │ │ ├── _mixin.scss │ │ └── _variables.scss │ ├── tailwind.scss │ └── global.scss ├── assets │ ├── 404_images │ │ ├── 404.png │ │ └── 404_cloud.png │ └── images │ │ ├── illustration.png │ │ └── auth-background.jpg ├── store │ ├── pathify.js │ ├── modules │ │ ├── app.state.js │ │ ├── auth.state.js │ │ └── index.js │ ├── index.js │ └── helpers │ │ └── default.helper.js ├── App.vue ├── resources │ ├── Reporting.js │ ├── Auth.js │ ├── GoogleMapApi.js │ └── Campaign.js ├── views │ ├── EmptyRoute.vue │ ├── home │ │ ├── Messaging.vue │ │ └── Home.vue │ ├── auth │ │ └── Login.vue │ └── Page404.vue ├── components │ ├── Sidebar │ │ ├── Sidebar.scss │ │ └── Sidebar.jsx │ ├── TelInput.vue │ ├── Hamburger.vue │ └── Breadcrumb.vue ├── services │ └── EventBus.js ├── router │ ├── routes │ │ ├── auth.routes.js │ │ ├── home.routes.js │ │ └── index.js │ └── index.js ├── plugins │ ├── vuex-persistence.js │ ├── vuex-router-sync.plugin.js │ ├── vue-awesome.plugin.js │ ├── main.js │ ├── element-ui.plugin.js │ └── i18n.plugin.js ├── shims-tsx.d.ts ├── utils │ ├── file.helper.js │ └── form.helper.js ├── main.js ├── plugins.d.ts ├── config │ └── index.js └── vuex-pathify.d.ts ├── .dockerignore ├── .eslintignore ├── .browserslistrc ├── .env.development ├── .env.production ├── .env.staging ├── public ├── favicon.ico └── index.html ├── .prettierrc ├── postcss.config.js ├── _templates └── new │ ├── layout │ ├── layout.ejs.t │ └── prompt.js │ ├── state │ ├── prompt.js │ └── state.ejs.t │ └── component │ ├── component.ejs.t │ └── prompt.js ├── docker-compose.yml ├── babel.config.js ├── .gitignore ├── nginx_config ├── nginx.conf └── default.conf ├── vue.config.js ├── docs ├── production.md ├── editors.md ├── routing.md ├── troubleshooting.md ├── linting.md ├── development.md ├── state.md ├── architecture.md └── tech.md ├── Dockerfile ├── .eslintrc.json ├── .vscode ├── launch.json ├── extensions.json └── settings.json ├── .vuepress └── config.js ├── README.md ├── package.json └── tailwind.config.js /src/filters/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/**/*.js 2 | src/assets 3 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not ie <= 8 4 | -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | VUE_APP_API_URL=https://awesome.app.com/v1 2 | -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | VUE_APP_API_URL=https://awesome.app.com/v1 2 | -------------------------------------------------------------------------------- /.env.staging: -------------------------------------------------------------------------------- 1 | VUE_APP_API_URL=https://awesome.app.com/v1 2 | -------------------------------------------------------------------------------- /src/mixins/index.js: -------------------------------------------------------------------------------- 1 | export * from './data.mixin'; 2 | export * from './form.mixin'; 3 | -------------------------------------------------------------------------------- /src/layouts/default.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NarHakobyan/awesome-vue-boilerplate/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/interceptors/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export { AuthInterceptor } from './auth.interceptor'; 4 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue'; 3 | export default Vue; 4 | } 5 | -------------------------------------------------------------------------------- /src/common/constants/appConstants.js: -------------------------------------------------------------------------------- 1 | export const AUTH_RESOURCE_URL = `${process.env.VUE_APP_API_URL}/users`; 2 | -------------------------------------------------------------------------------- /src/translations/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import en from './en'; 4 | 5 | export default { 6 | en, 7 | }; 8 | -------------------------------------------------------------------------------- /src/styles/fonts/icomoon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NarHakobyan/awesome-vue-boilerplate/HEAD/src/styles/fonts/icomoon.eot -------------------------------------------------------------------------------- /src/styles/fonts/icomoon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NarHakobyan/awesome-vue-boilerplate/HEAD/src/styles/fonts/icomoon.ttf -------------------------------------------------------------------------------- /src/assets/404_images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NarHakobyan/awesome-vue-boilerplate/HEAD/src/assets/404_images/404.png -------------------------------------------------------------------------------- /src/styles/fonts/icomoon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NarHakobyan/awesome-vue-boilerplate/HEAD/src/styles/fonts/icomoon.woff -------------------------------------------------------------------------------- /src/assets/404_images/404_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NarHakobyan/awesome-vue-boilerplate/HEAD/src/assets/404_images/404_cloud.png -------------------------------------------------------------------------------- /src/assets/images/illustration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NarHakobyan/awesome-vue-boilerplate/HEAD/src/assets/images/illustration.png -------------------------------------------------------------------------------- /src/store/pathify.js: -------------------------------------------------------------------------------- 1 | import pathify from 'vuex-pathify'; 2 | 3 | pathify.options.mapping = 'standard'; 4 | 5 | export default pathify; 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 4, 4 | "semi": true, 5 | "singleQuote": true, 6 | "printWidth": 150 7 | } 8 | -------------------------------------------------------------------------------- /src/assets/images/auth-background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NarHakobyan/awesome-vue-boilerplate/HEAD/src/assets/images/auth-background.jpg -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | const tailwindcss = require('tailwindcss'); 2 | 3 | module.exports = { 4 | plugins: [tailwindcss('./tailwind.config.js')], 5 | }; 6 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /_templates/new/layout/layout.ejs.t: -------------------------------------------------------------------------------- 1 | --- 2 | to: "src/layouts/<%= h.inflection.dasherize(name) %>.vue" 3 | --- 4 | 7 | 8 | 10 | -------------------------------------------------------------------------------- /src/resources/Reporting.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { REPORTING_RESOURCE_URL } from '@/common/constants/appConstants'; 3 | 4 | const actions = {}; 5 | 6 | export default Vue.resource(REPORTING_RESOURCE_URL, {}, actions); 7 | -------------------------------------------------------------------------------- /src/views/EmptyRoute.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 13 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | app: 4 | image: "${PROJECT_NAME}:${VERSION}" 5 | container_name: "${PROJECT_NAME}-frontend" 6 | build: . 7 | expose: 8 | - "${PORT}" 9 | ports: 10 | - "${PORT}:80" 11 | -------------------------------------------------------------------------------- /src/components/Sidebar/Sidebar.scss: -------------------------------------------------------------------------------- 1 | .menu-title { 2 | margin-left: 10px; 3 | } 4 | .aside-vertical-menu { 5 | height: 100%; 6 | 7 | &:not(.el-menu--collapse) { 8 | width: 200px; 9 | min-height: 400px; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/services/EventBus.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Vue from 'vue'; 4 | 5 | export const EventBus = {}; 6 | 7 | const bus = new Vue(); 8 | 9 | EventBus.install = function(Vue, _options) { 10 | // 4. add an instance method 11 | Vue.prototype.$bus = bus; 12 | }; 13 | -------------------------------------------------------------------------------- /src/router/routes/auth.routes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export const authRoutes = [ 4 | { 5 | path: '/auth/login', 6 | name: 'login', 7 | meta: { title: 'login' }, 8 | component: () => import(/* webpackChunkName: "Login" */ '@/views/auth/Login.vue'), 9 | }, 10 | ]; 11 | -------------------------------------------------------------------------------- /src/plugins/vuex-persistence.js: -------------------------------------------------------------------------------- 1 | import VuexPersistence from 'vuex-persist'; 2 | 3 | const mutations = ['authState/SET_TOKEN']; 4 | 5 | export default new VuexPersistence({ 6 | storage: window.localStorage, 7 | modules: ['authState'], 8 | filter: mutation => mutations.includes(mutation.type), 9 | }); 10 | -------------------------------------------------------------------------------- /_templates/new/state/prompt.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | type: 'input', 4 | name: 'name', 5 | message: 'Name:', 6 | validate(value) { 7 | if (!value.length) { 8 | return 'Vuex state must have a name.'; 9 | } 10 | return true; 11 | }, 12 | }, 13 | ]; 14 | -------------------------------------------------------------------------------- /_templates/new/layout/prompt.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | type: 'input', 4 | name: 'name', 5 | message: 'Name:', 6 | validate(value) { 7 | if (!value.length) { 8 | return 'Layout components must have a name.'; 9 | } 10 | return true; 11 | }, 12 | }, 13 | ]; 14 | -------------------------------------------------------------------------------- /src/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from 'vue' 2 | 3 | declare global { 4 | namespace JSX { 5 | // tslint:disable no-empty-interface 6 | interface Element extends VNode {} 7 | // tslint:disable no-empty-interface 8 | interface ElementClass extends Vue {} 9 | interface IntrinsicElements { 10 | [elem: string]: any 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['@vue/app'], 3 | plugins: [ 4 | 'lodash', 5 | [ 6 | 'component', 7 | { 8 | libraryName: 'element-ui', 9 | styleLibraryName: 'theme-chalk', 10 | }, 11 | ], 12 | '@babel/plugin-syntax-dynamic-import', 13 | ], 14 | }; 15 | -------------------------------------------------------------------------------- /src/plugins/vuex-router-sync.plugin.js: -------------------------------------------------------------------------------- 1 | /* ============ 2 | * Vuex Router Sync 3 | * ============ 4 | * 5 | * Sync vue-router's current $route as part of vuex store's state. 6 | * 7 | * https://github.com/vuejs/vuex-router-sync 8 | */ 9 | 10 | import VuexRouterSync from 'vuex-router-sync'; 11 | import store from '@/store'; 12 | import router from '@/router'; 13 | 14 | VuexRouterSync.sync(store, router); 15 | -------------------------------------------------------------------------------- /src/components/TelInput.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | 16 | 19 | -------------------------------------------------------------------------------- /src/views/home/Messaging.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | /.vuepress/dist 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode/* 18 | !.vscode/settings.json 19 | !.vscode/tasks.json 20 | !.vscode/launch.json 21 | !.vscode/extensions.json 22 | *.suo 23 | *.ntvs* 24 | *.njsproj 25 | *.sln 26 | *.sw* 27 | -------------------------------------------------------------------------------- /src/store/modules/app.state.js: -------------------------------------------------------------------------------- 1 | import { makeMutations, makeActions, makeGetters } from '@/store/helpers/default.helper'; 2 | 3 | const defaultState = { 4 | sidebar: { 5 | opened: true, 6 | }, 7 | }; 8 | 9 | export default { 10 | namespaced: true, 11 | state: defaultState, 12 | mutations: makeMutations(defaultState), 13 | actions: makeActions(defaultState), 14 | getters: makeGetters(defaultState), 15 | }; 16 | -------------------------------------------------------------------------------- /src/resources/Auth.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { AUTH_RESOURCE_URL } from '@/common/constants/appConstants'; 3 | 4 | const actions = { 5 | activate: { 6 | method: 'GET', 7 | url: `${AUTH_RESOURCE_URL}/activations`, 8 | }, 9 | login: { method: 'POST', url: `${AUTH_RESOURCE_URL}/login` }, 10 | me: { method: 'GET', url: `${AUTH_RESOURCE_URL}/me` }, 11 | }; 12 | 13 | export default Vue.resource(AUTH_RESOURCE_URL, {}, actions); 14 | -------------------------------------------------------------------------------- /nginx_config/nginx.conf: -------------------------------------------------------------------------------- 1 | user nginx; 2 | worker_processes 1; 3 | 4 | error_log /var/log/nginx/error.log warn; 5 | pid /var/run/nginx.pid; 6 | 7 | events { 8 | worker_connections 1024; 9 | } 10 | 11 | http { 12 | include /etc/nginx/mime.types; 13 | default_type application/octet-stream; 14 | 15 | sendfile off; 16 | 17 | keepalive_timeout 60; 18 | 19 | gzip on; 20 | 21 | include /etc/nginx/conf.d/*.conf; 22 | } 23 | -------------------------------------------------------------------------------- /src/mixins/data.mixin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { get, set } from 'lodash'; 4 | 5 | export const DataMixin = { 6 | methods: { 7 | resetData(path) { 8 | const defaultState = this.$options.data(); 9 | 10 | if (path) { 11 | set(this.$data, path, get(defaultState, path)); 12 | } else { 13 | Object.assign(this.$data, defaultState); 14 | } 15 | }, 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /src/resources/GoogleMapApi.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { GOOGLE_MAP_RESOURCE_URL } from '@/common/constants/appConstants'; 3 | 4 | const actions = { 5 | autocompleteLocation: { 6 | method: 'GET', 7 | url: `${GOOGLE_MAP_RESOURCE_URL}/autocomplete/json`, 8 | }, 9 | getLocation: { 10 | method: 'GET', 11 | url: `${GOOGLE_MAP_RESOURCE_URL}/details/json`, 12 | }, 13 | }; 14 | export default Vue.resource(GOOGLE_MAP_RESOURCE_URL, {}, actions); 15 | -------------------------------------------------------------------------------- /src/resources/Campaign.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { CAMPAIGN_RESOURCE_URL } from '@/common/constants/appConstants'; 3 | 4 | const actions = { 5 | autocompleteLocation: { 6 | method: 'GET', 7 | url: 'https://maps.googleapis.com/maps/api/place/autocomplete/json', 8 | }, 9 | getLocation: { 10 | method: 'GET', 11 | url: 'https://maps.googleapis.com/maps/api/place/details/json', 12 | }, 13 | }; 14 | export default Vue.resource(CAMPAIGN_RESOURCE_URL, {}, actions); 15 | -------------------------------------------------------------------------------- /src/utils/file.helper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * 5 | * @param dataUrl 6 | * @param fileName 7 | * @returns {File} 8 | */ 9 | export function dataURLtoFile(dataUrl, fileName) { 10 | const arr = dataUrl.split(','); 11 | let mime = arr[0].match(/:(.*?);/)[1]; 12 | let bstr = atob(arr[1]); 13 | let n = bstr.length; 14 | let u8arr = new Uint8Array(n); 15 | while (n--) { 16 | u8arr[n] = bstr.charCodeAt(n); 17 | } 18 | return new File([u8arr], fileName, { type: mime }); 19 | } 20 | -------------------------------------------------------------------------------- /nginx_config/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80 default_server; 3 | listen [::]:80 default_server; 4 | 5 | root /var/www/html; 6 | 7 | index index.html; 8 | 9 | location / { 10 | try_files $uri $uri/ @rewrites; 11 | } 12 | 13 | location @rewrites { 14 | rewrite ^(.+)$ /index.html last; 15 | } 16 | 17 | location ~* \.(?:ico|css|js|gif|jpe?g|png)$ { 18 | # Some basic cache-control for static files to be sent to the browser 19 | expires max; 20 | add_header Pragma public; 21 | add_header Cache-Control "public, must-revalidate, proxy-revalidate"; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const developmentMode = process.env.NODE_ENV === 'development'; 4 | module.exports = { 5 | lintOnSave: false, 6 | transpileDependencies: [/\bvue-awesome\b/, 'vuex-persist'], 7 | pluginOptions: { 8 | 'style-resources-loader': { 9 | preProcessor: 'scss', 10 | patterns: [path.resolve(__dirname, './src/styles/imports/*.scss')], 11 | }, 12 | }, 13 | configureWebpack: { 14 | devtool: developmentMode ? 'source-map' : false, 15 | }, 16 | css: { 17 | sourceMap: developmentMode, 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /_templates/new/state/state.ejs.t: -------------------------------------------------------------------------------- 1 | --- 2 | to: src/store/modules/<%= h.inflection.dasherize(name).toLowerCase() %>.state.js 3 | --- 4 | import { makeMutations, makeActions, makeGetters, makeDefaultState } from '@/utils/state.helper'; 5 | 6 | const defaultState = {}; 7 | 8 | const mutations = {}; 9 | 10 | const actions = {}; 11 | 12 | const getters = {}; 13 | 14 | export default { 15 | namespaced: true, 16 | state: makeDefaultState(defaultState), 17 | mutations: makeMutations(defaultState, mutations), 18 | actions: makeActions(defaultState, actions), 19 | getters: makeGetters(defaultState, getters), 20 | }; 21 | -------------------------------------------------------------------------------- /docs/production.md: -------------------------------------------------------------------------------- 1 | # Building and deploying to production 2 | 3 | - [Building and deploying to production](#building-and-deploying-to-production) 4 | - [From the terminal](#from-the-terminal) 5 | - [By docker-compose](#by-docker-compose) 6 | 7 | ## From the terminal 8 | 9 | ```bash 10 | # Build for production with minification 11 | yarn build:prd 12 | ``` 13 | 14 | This results in your compiled application in a `dist` directory. 15 | 16 | ## By docker-compose 17 | 18 | ```bash 19 | PROJECT_NAME=your_project PORT=8080 VERSION=0.0.1 docker-compose up -d 20 | ``` 21 | 22 | after building application will run on background in port 8080 -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Awesome Vue boilerplate 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/layouts/auth.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 28 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:lts-alpine as build-stage 2 | 3 | WORKDIR /app 4 | 5 | COPY package.json yarn.lock ./ 6 | 7 | RUN yarn install 8 | 9 | COPY . . 10 | 11 | RUN yarn run build:prd 12 | 13 | # production stage 14 | FROM nginx:stable-alpine as production-stage 15 | 16 | # Create the directories we will need 17 | RUN mkdir -p /var/log/nginx 18 | RUN mkdir -p /var/www/html 19 | 20 | # Copy the respective nginx configuration files 21 | COPY nginx_config/nginx.conf /etc/nginx/nginx.conf 22 | COPY nginx_config/default.conf /etc/nginx/conf.d/default.conf 23 | 24 | COPY --from=build-stage /app/dist /var/www/html 25 | RUN chown nginx:nginx /var/www/html 26 | 27 | EXPOSE 80 28 | 29 | CMD ["nginx", "-g", "daemon off;"] 30 | -------------------------------------------------------------------------------- /_templates/new/component/component.ejs.t: -------------------------------------------------------------------------------- 1 | --- 2 | to: "src/components/<%= h.inflection.dasherize(name).toLowerCase().slice(0, 5) === 'base-' ? '_' : '' %><%= h.inflection.dasherize(name) %>.vue" 3 | --- 4 | <% 5 | if (blocks.indexOf('