├── 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 |
2 |
3 |
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 |
2 |
3 |
4 |
5 |
10 |
--------------------------------------------------------------------------------
/_templates/new/layout/layout.ejs.t:
--------------------------------------------------------------------------------
1 | ---
2 | to: "src/layouts/<%= h.inflection.dasherize(name) %>.vue"
3 | ---
4 |
5 |
6 |
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 |
2 |
3 |
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 |
2 |
3 |
4 |
5 |
15 |
16 |
19 |
--------------------------------------------------------------------------------
/src/views/home/Messaging.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Coming soon…
5 |
6 |
7 |
8 |
9 |
10 |
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 |
12 | We're sorry but ad-exchange doesn't work properly without JavaScript enabled. Please enable it to continue.
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/layouts/auth.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
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('') !== -1) {
6 | %>
7 |
8 |
9 | <%
10 | }
11 | if (blocks.indexOf('
22 | <%
23 | }
24 | if (blocks.indexOf('<%
28 | }
29 | %>
30 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Vuex from 'vuex';
3 | import pathify from './pathify';
4 | import vuexLocal from '@/plugins/vuex-persistence';
5 |
6 | import modules from './modules';
7 |
8 | if (process.env.NODE_ENV === 'development') {
9 | pathify.debug();
10 | }
11 |
12 | Vue.use(Vuex);
13 |
14 | const defaultState = {};
15 | const mutations = {};
16 | const getters = {};
17 | const actions = {};
18 |
19 | const store = new Vuex.Store({
20 | plugins: [pathify.plugin, vuexLocal.plugin],
21 | state: defaultState,
22 | mutations,
23 | actions,
24 | getters,
25 | modules,
26 | strict: process.env.NODE_ENV !== 'production',
27 | });
28 |
29 | export default store;
30 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "env": {
4 | "node": true
5 | },
6 | "extends": ["plugin:vue/recommended", "@vue/prettier"],
7 | "rules": {
8 | "indent": ["error", 4],
9 | "no-console": "error",
10 | "no-debugger": "error",
11 | "no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
12 | "vue/no-unused-vars": "error",
13 | "no-return-await": "error",
14 | "vue/component-name-in-template-casing": ["error", "kebab-case"],
15 | "vue/script-indent": ["error", 4]
16 | },
17 | "overrides": [
18 | {
19 | "files": ["*.vue"],
20 | "rules": {
21 | "indent": "off"
22 | }
23 | }
24 | ],
25 | "parserOptions": {
26 | "parser": "babel-eslint"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "type": "chrome",
6 | "request": "launch",
7 | "name": "vuejs: chrome",
8 | "url": "http://localhost:8080",
9 | "webRoot": "${workspaceFolder}/src",
10 | "breakOnLoad": true,
11 | "sourceMapPathOverrides": {
12 | "webpack:///src/*": "${webRoot}/*"
13 | }
14 | },
15 | {
16 | "type": "firefox",
17 | "request": "launch",
18 | "name": "vuejs: firefox",
19 | "url": "http://localhost:8080",
20 | "webRoot": "${workspaceFolder}/src",
21 | "pathMappings": [{ "url": "webpack:///src/", "path": "${webRoot}/" }]
22 | }
23 | ]
24 | }
--------------------------------------------------------------------------------
/src/router/routes/home.routes.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export const homeRoutes = [
4 | {
5 | path: '',
6 | name: 'home',
7 | component: () => import(/* webpackChunkName: "home" */ '@/views/home/Home'),
8 | meta: {
9 | title: 'home',
10 | },
11 | },
12 | {
13 | path: 'messaging',
14 | name: 'messaging',
15 | component: () => import(/* webpackChunkName: "Messaging" */ '@/views/home/Messaging'),
16 | meta: {
17 | // authRequired: true, // Authentication check
18 | title: 'messaging',
19 | beforeResolve(routeTo, routeFrom, next) {
20 | setTimeout(() => next(), 2000);
21 | },
22 | },
23 | },
24 | ];
25 |
--------------------------------------------------------------------------------
/src/router/routes/index.js:
--------------------------------------------------------------------------------
1 | import { createRouterLayout } from 'vue-router-layout';
2 |
3 | import { authRoutes } from '@/router/routes/auth.routes';
4 | import { homeRoutes } from '@/router/routes/home.routes';
5 |
6 | const RouterLayout = createRouterLayout(layout => {
7 | return import('@/layouts/' + layout + '.vue');
8 | });
9 |
10 | const routes = [
11 | {
12 | path: '/',
13 | component: RouterLayout,
14 | children: [
15 | ...authRoutes,
16 | ...homeRoutes,
17 | {
18 | path: '*',
19 | meta: { title: 'Not Found' },
20 | component: () => import('@/views/Page404'),
21 | },
22 | ],
23 | },
24 | ];
25 |
26 | export default routes;
27 |
--------------------------------------------------------------------------------
/.vuepress/config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | title: 'Awesome vue boilerplate Docs',
3 | description: `An ultimate online platform that helps you create and launch
4 | ads in a few minutes.
5 | Target your audience by exploring our features and start
6 | using the platform!`,
7 | base: process.env.DEPLOY_ENV === 'gh-pages' ? '/awesome-vue-boilerplate/': '/',
8 | themeConfig: {
9 | sidebar: [
10 | ['/', 'Introduction'],
11 | '/docs/development',
12 | '/docs/architecture',
13 | '/docs/tech',
14 | '/docs/routing',
15 | '/docs/state',
16 | '/docs/linting',
17 | '/docs/editors',
18 | '/docs/production',
19 | '/docs/troubleshooting',
20 | ],
21 | },
22 | };
23 |
--------------------------------------------------------------------------------
/src/plugins/vue-awesome.plugin.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Icon from 'vue-awesome/components/Icon';
3 |
4 | import 'vue-awesome/icons/user-tag';
5 | import 'vue-awesome/icons/users';
6 | import 'vue-awesome/icons/user-plus';
7 | import 'vue-awesome/icons/audio-description';
8 | import 'vue-awesome/icons/chart-bar';
9 | import 'vue-awesome/icons/boxes';
10 | import 'vue-awesome/icons/money-check';
11 | import 'vue-awesome/icons/crop';
12 | import 'vue-awesome/icons/user-circle';
13 | import 'vue-awesome/icons/comment';
14 | import 'vue-awesome/icons/comments';
15 | import 'vue-awesome/icons/thumbs-up';
16 | import 'vue-awesome/icons/th-list';
17 | import 'vue-awesome/icons/bell';
18 | import 'vue-awesome/icons/comment-alt';
19 | import 'vue-awesome/icons/ellipsis-h';
20 | import 'vue-awesome/icons/hand-point-right';
21 |
22 | Vue.component('v-icon', Icon);
23 |
--------------------------------------------------------------------------------
/src/styles/transition.scss:
--------------------------------------------------------------------------------
1 | //globl transition css
2 |
3 | /*fade*/
4 | .fade-enter-active,
5 | .fade-leave-active {
6 | transition: opacity 0.28s;
7 | }
8 |
9 | .fade-enter,
10 | .fade-leave-active {
11 | opacity: 0;
12 | }
13 |
14 | /*fade-transform*/
15 | .fade-transform-leave-active,
16 | .fade-transform-enter-active {
17 | transition: all .5s;
18 | }
19 |
20 | .fade-transform-enter {
21 | opacity: 0;
22 | transform: translateX(-30px);
23 | }
24 |
25 | .fade-transform-leave-to {
26 | opacity: 0;
27 | transform: translateX(30px);
28 | }
29 |
30 | /*breadcrumb transition*/
31 | .breadcrumb-enter-active,
32 | .breadcrumb-leave-active {
33 | transition: all .5s;
34 | }
35 |
36 | .breadcrumb-enter,
37 | .breadcrumb-leave-active {
38 | opacity: 0;
39 | transform: translateX(20px);
40 | }
41 |
42 | .breadcrumb-move {
43 | transition: all .5s;
44 | }
45 |
46 | .breadcrumb-leave-active {
47 | position: absolute;
48 | }
49 |
50 |
--------------------------------------------------------------------------------
/src/plugins/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import VueWait from 'vue-wait';
3 | import debounce from 'v-debounce';
4 | import VueResource from 'vue-resource';
5 | import { Validator } from 'vee-validate';
6 | import VeeValidate from 'vee-validate';
7 | import VueConfig from 'vue-configuration';
8 | import { Plugin as FragmentPlugin } from 'vue-fragment';
9 | import dictionary from '@/translations/validation-messages';
10 | import config from '@/config';
11 | import '@/common/validator';
12 |
13 | import './vue-awesome.plugin';
14 | import { EventBus } from '@/services/EventBus';
15 |
16 | Vue.use(FragmentPlugin);
17 |
18 | // global directives
19 |
20 | Vue.directive('debounce', debounce);
21 |
22 | Vue.use(EventBus);
23 | Vue.use(VueWait);
24 | Vue.use(VueResource);
25 | Vue.use(VueConfig, { config });
26 | Vue.use(VeeValidate, { fieldsBagName: 'formFields', events: 'blur|input' });
27 | Validator.localize(dictionary);
28 | Validator.localize('en');
29 |
--------------------------------------------------------------------------------
/src/common/validator.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | import { Validator } from 'vee-validate';
3 |
4 | Validator.extend('validatePhone', {
5 | validate: (value, [isPhoneValid]) => {
6 | return isPhoneValid;
7 | },
8 | });
9 |
10 | Validator.extend('isTermsChecked', {
11 | validate: value => {
12 | return value && value === true;
13 | },
14 | });
15 |
16 | Validator.extend('password_regex', {
17 | validate: value => {
18 | var strongRegex = new RegExp('^[a-zA-Z0-9!@#$%^&*]*$');
19 | return strongRegex.test(value);
20 | },
21 | });
22 |
23 | Validator.extend('username_regex', {
24 | validate: value => {
25 | var strongRegex = new RegExp("^[a-zA-Z0-9-_']+$");
26 | return strongRegex.test(value);
27 | },
28 | });
29 |
30 | Validator.extend('text_regex', {
31 | validate: value => {
32 | var strongRegex = new RegExp('^\\S+(?: \\S+)*$');
33 | return strongRegex.test(value);
34 | },
35 | });
36 |
--------------------------------------------------------------------------------
/src/translations/validation-messages.js:
--------------------------------------------------------------------------------
1 | const dictionary = {
2 | en: {
3 | messages: {
4 | required: () => 'This field is required',
5 | min: (field, value) => `Minimum length of this field must be ${value}`,
6 | max: (field, value) => `Maximum length of this field must be ${value}`,
7 | email: () => 'Please provide valid email address',
8 | regex: () => 'This field is not valid',
9 | confirmed: () => 'Two passwords do not match',
10 | validatePhone: () => 'Please provide valid phone',
11 | isTermsChecked: () => 'Please accept account creation agreement and privacy statement!',
12 | password_regex: () => 'This field must contain digits, letters or one of !@#$%^&* simbols',
13 | username_regex: () => 'Login name format is not valid',
14 | text_regex: () => 'Field format is not valid',
15 | },
16 | },
17 | };
18 |
19 | export default dictionary;
20 |
--------------------------------------------------------------------------------
/docs/editors.md:
--------------------------------------------------------------------------------
1 | # Editor integration
2 |
3 | - [Visual Studio Code](#visual-studio-code)
4 | - [Configuration](#configuration)
5 | - [FAQ](#faq)
6 |
7 | ## Visual Studio Code
8 |
9 | This project is best developed in VS Code. With the [recommended extensions](https://code.visualstudio.com/docs/editor/extension-gallery#_workspace-recommended-extensions) and settings in `.vscode`, you get:
10 |
11 | - Syntax highlighting for all files
12 | - Intellisense for all files
13 | - Lint-on-save for all files
14 | - In-editor results on save for unit tests
15 |
16 | ### Configuration
17 |
18 | To configure
19 |
20 | - `.vscode/extensions.json`
21 | - `.vscode/settings.json`
22 |
23 | ## FAQ
24 |
25 | **What kinds of editor settings and extensions should be added to the project?**
26 |
27 | All additions must:
28 |
29 | - be specific to this project
30 | - not interfere with any team member's workflow
31 |
32 | For example, an extension to add syntax highlighting for an included language will almost certainly be welcome, but a setting to change the editor's color theme wouldn't be appropriate.
33 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import VueWait from 'vue-wait';
3 |
4 | import '@/styles/transition.scss'; // global css
5 | import 'nprogress/nprogress.css'; // nprogress styles
6 | import '@/styles/global.scss'; // global css
7 |
8 | import '@/plugins/main';
9 | import '@/plugins/element-ui.plugin';
10 | import i18n from '@/plugins/i18n.plugin'; // Internationalization
11 | import '@/plugins/vuex-router-sync.plugin';
12 | import * as interceptors from '@/interceptors';
13 | import * as filters from '@/filters'; // global filters
14 |
15 | import router from '@/router';
16 | import store from '@/store';
17 |
18 | import App from './App.vue';
19 |
20 | Vue.config.productionTip = false;
21 | Vue.http.interceptors.push(...Object.values(interceptors));
22 |
23 | // register global utility filters.
24 | for (const key of Object.keys(filters)) {
25 | Vue.filter(key, filters[key]);
26 | }
27 |
28 | export default new Vue({
29 | i18n,
30 | router,
31 | store,
32 | render: h => h(App),
33 | wait: new VueWait({
34 | useVuex: true,
35 | }),
36 | }).$mount('#app');
37 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // See http://go.microsoft.com/fwlink/?LinkId=827846
3 | // for the documentation about the extensions.json format
4 | "recommendations": [
5 | // Syntax highlighting and more for .vue files
6 | // https://github.com/vuejs/vetur
7 | "octref.vetur",
8 |
9 | // Peek and go-to-definition for .vue files
10 | // https://github.com/fuzinato/vscode-vue-peek
11 | "dariofuzinato.vue-peek",
12 |
13 | // Lint-on-save with ESLint
14 | // https://github.com/Microsoft/vscode-eslint
15 | "dbaeumer.vscode-eslint",
16 |
17 | // Lint-on-save with Stylelint
18 | // https://github.com/shinnn/vscode-stylelint
19 | "shinnn.stylelint",
20 |
21 | // Format-on-save with Prettier
22 | // https://github.com/prettier/prettier-vscode
23 | "esbenp.prettier-vscode",
24 |
25 | // SCSS intellisense
26 | // https://github.com/mrmlnc/vscode-scss
27 | "mrmlnc.vscode-scss",
28 |
29 | // Lint markdown in README files
30 | // https://github.com/DavidAnson/vscode-markdownlint
31 | "DavidAnson.vscode-markdownlint"
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/src/mixins/form.mixin.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import { get, find } from 'lodash';
4 |
5 | export const FormMixin = {
6 | provide() {
7 | return { parentValidator: this.$validator };
8 | },
9 | methods: {
10 | getError(path) {
11 | if (get(this.formFields, `${path}.touched`)) {
12 | return this.errors.first(path);
13 | }
14 | },
15 | setErrors(responseErrors) {
16 | for (const error of responseErrors) {
17 | this.errors.add({
18 | field: error.field,
19 | msg: this.$t(`errors.${error.message}`),
20 | });
21 | }
22 | },
23 | markFormTouched() {
24 | for (const field of Object.values(this.$validator.flags)) {
25 | field.touched = true;
26 | const firstField = this.errors.items[0].field;
27 | const element = find(this.$validator.fields.items, ['name', firstField]);
28 | element.el.scrollIntoView();
29 | }
30 | },
31 | },
32 | };
33 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "cSpell.words": [
3 | "devtool",
4 | "mixins",
5 | "vuex"
6 | ],
7 | "editor.insertSpaces": true,
8 | "editor.tabSize": 4,
9 | "editor.trimAutoWhitespace": true,
10 | "files.trimTrailingWhitespace": true,
11 | "files.eol": "\n",
12 | "files.insertFinalNewline": true,
13 | "files.trimFinalNewlines": true,
14 | "files.exclude": {
15 | "**/*.log": true,
16 | "**/*.log*": true,
17 | "**/dist": true,
18 | "**/coverage": true
19 | },
20 | "eslint.run": "onSave",
21 | "eslint.validate": [
22 | { "language": "javascript", "autoFix": true },
23 | { "language": "javascriptreact", "autoFix": true },
24 | { "language": "vue", "autoFix": true },
25 | { "language": "vue-html", "autoFix": true },
26 | { "language": "html", "autoFix": true }
27 | ],
28 | "css.lint.duplicateProperties": "warning",
29 | "css.lint.float": "warning",
30 | "eslint.alwaysShowStatus": true,
31 | "eslint.autoFixOnSave": true,
32 | "scss.lint.duplicateProperties": "warning"
33 | }
34 |
--------------------------------------------------------------------------------
/src/interceptors/auth.interceptor.js:
--------------------------------------------------------------------------------
1 | import { Message } from 'element-ui';
2 |
3 | import store from '@/store';
4 | import i18n from '@/plugins/i18n.plugin';
5 | import router from '@/router';
6 |
7 | export const AuthInterceptor = (request, next) => {
8 | const token = store.get('authState@token');
9 | if (token) {
10 | request.headers.set('Authorization', `Bearer ${token}`);
11 | }
12 | next(AuthResponseInterceptor);
13 | };
14 |
15 | const AuthResponseInterceptor = response => {
16 | let message;
17 | if (response.status >= 500) {
18 | message = 'err_internal';
19 | }
20 | if (response.status === 401) {
21 | message = 'err_unauthorized';
22 | router.push({ name: 'login' });
23 | }
24 | if ([403, 404].includes(response.status)) {
25 | message = response.body.message;
26 | }
27 | if (message) {
28 | Message({
29 | showClose: true,
30 | message: i18n.t(`errors.${message}`),
31 | type: 'error',
32 | });
33 | }
34 | if (/[23]../.test(response.status)) {
35 | return Promise.resolve(response);
36 | }
37 | return Promise.reject(response);
38 | };
39 |
--------------------------------------------------------------------------------
/src/views/home/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
Welcome to Awesome App!
8 |
9 | An ultimate online platform that helps you create and
10 | launch ads in a few minutes.
11 |
12 |
13 | Target your audience by exploring our features and
14 | start using the platform!
15 |
16 |
17 | Continue
18 |
19 |
20 |
21 |
22 |
23 |
29 |
--------------------------------------------------------------------------------
/src/mixins/ResizeHandler.js:
--------------------------------------------------------------------------------
1 | import store from '@/store';
2 |
3 | const { body } = document;
4 | const WIDTH = 1024;
5 | const RATIO = 3;
6 |
7 | export default {
8 | watch: {
9 | $route() {
10 | if (this.device === 'mobile' && this.sidebarOpened) {
11 | store.dispatch('auth', { withoutAnimation: false });
12 | }
13 | },
14 | },
15 | beforeMount() {
16 | window.addEventListener('resize', this.resizeHandler);
17 | },
18 | mounted() {
19 | const isMobile = this.isMobile();
20 | if (isMobile) {
21 | // todo save device type in tore
22 | // store.dispatch('toggleDevice', 'mobile');
23 | store.set('appState/sidebar@opened', false);
24 | }
25 | },
26 | methods: {
27 | isMobile() {
28 | const rect = body.getBoundingClientRect();
29 | return rect.width - RATIO < WIDTH;
30 | },
31 | resizeHandler() {
32 | if (!document.hidden) {
33 | const isMobile = this.isMobile();
34 | // store.dispatch('toggleDevice', isMobile ? 'mobile' : 'desktop');
35 |
36 | if (isMobile) {
37 | store.set('appState/sidebar@opened', false);
38 | }
39 | }
40 | },
41 | },
42 | };
43 |
--------------------------------------------------------------------------------
/src/utils/form.helper.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import { isNull, isArray, isString, isObject, isNumber, toString, isUndefined } from 'lodash';
4 |
5 | /**
6 | *
7 | * @param model
8 | * @param form
9 | * @returns {*|FormData}
10 | */
11 | export function convertModelToFormData(model, form) {
12 | const formData = form || new FormData();
13 | if (isObject(model)) {
14 | for (const [key, value] of Object.entries(model)) {
15 | if (isArray(value)) {
16 | for (const element of Object.values(value)) {
17 | if (isNumber(element) || isString(element)) {
18 | formData.append(key, toString(element));
19 | }
20 | if (element instanceof File) {
21 | formData.append(key, element, element.name);
22 | continue;
23 | }
24 | convertModelToFormData(element, formData);
25 | }
26 | } else if (isObject(value) && !(value instanceof File)) {
27 | convertModelToFormData(value, formData);
28 | } else if (value instanceof File) {
29 | formData.append(key, value, value.name);
30 | } else if (!isNull(value) && !isUndefined(value) && !isNaN(value)) {
31 | formData.append(key, toString(value));
32 | }
33 | }
34 | }
35 | return formData;
36 | }
37 |
--------------------------------------------------------------------------------
/src/plugins.d.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import VueWait, { VueWaitInstance } from 'vue-wait';
3 | import {
4 | HttpOptions,
5 | HttpHeaders,
6 | HttpResponse,
7 | $http,
8 | Resource,
9 | Http,
10 | $resource, ResourceMethod,
11 | } from 'vue-resource/types/vue_resource';
12 |
13 | declare module 'vue/types/options' {
14 | interface ComponentOptions {
15 | http?: (HttpOptions & { headers?: HttpHeaders } & { [key: string]: any });
16 | wait?: VueWait;
17 | }
18 | }
19 |
20 | declare module 'vue/types/vue' {
21 |
22 | interface Vue {
23 | $http: {
24 | (options: HttpOptions): PromiseLike;
25 | get: $http;
26 | post: $http;
27 | put: $http;
28 | patch: $http;
29 | delete: $http;
30 | jsonp: $http;
31 | };
32 | $wait: VueWaitInstance,
33 | $resource: $resource;
34 | }
35 |
36 | interface VueConstructor {
37 | http: Http;
38 | resource: Resource;
39 | }
40 | }
41 | declare module 'vue-resource/types/vue_resource' {
42 |
43 | export interface ResourceMethods {
44 | get: ResourceMethod;
45 | save: ResourceMethod;
46 | query: ResourceMethod;
47 | update: ResourceMethod;
48 | remove: ResourceMethod;
49 | delete: ResourceMethod;
50 | [key: string]: ResourceMethod;
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/_templates/new/component/prompt.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash');
2 |
3 | module.exports = [
4 | {
5 | type: 'input',
6 | name: 'name',
7 | message: 'Name:',
8 | validate(value) {
9 | if (!value.length) {
10 | return 'Components must have a name.';
11 | }
12 | const fileName = _.kebabCase(value);
13 | if (fileName.indexOf('-') === -1) {
14 | return 'Component names should contain at least two words to avoid conflicts with existing and future HTML elements.';
15 | }
16 | return true;
17 | },
18 | },
19 | {
20 | type: 'MultiSelect',
21 | name: 'blocks',
22 | message: 'Blocks:',
23 | initial: ['
49 |
50 |
65 |
--------------------------------------------------------------------------------
/docs/troubleshooting.md:
--------------------------------------------------------------------------------
1 | # Troubleshooting
2 |
3 | These are some troubleshooting tips for more common issues people might run into while developing, including more information on what might be happening and how to fix the problem.
4 |
5 | - [Errors running scripts (e.g. `yarn dev`)](#errors-running-scripts-eg-yarn-dev)
6 | - [Visual Studio (VS) Code formatting issues](#visual-studio-vs-code-formatting-issues)
7 |
8 | ## Errors running scripts (e.g. `yarn dev`)
9 |
10 | Make sure you've followed the instructions for [Setup and development](development.md). If you already have, try deleting the `node_modules` folder and installing fresh:
11 |
12 | ```bash
13 | # 1. Delete all previously-installed dependencies.
14 | rm -rf node_modules
15 |
16 | # 2. Install dependencies fresh.
17 | yarn install
18 | ```
19 |
20 | If that doesn't work, it's possible that a newer version of a dependency is creating a problem. If this is the problem, you can work around it by installing dependencies from the `yarn.lock` file of a previously working branch or commit.
21 |
22 | ```bash
23 | # 1. Delete all previously-installed dependencies.
24 | rm -rf node_modules
25 |
26 | # 2. Use the same yarn.lock as the `origin/master` branch. If the problem
27 | # exists on the `origin/master` as well, instead use the last-known
28 | # working branch or commit.
29 | git checkout origin/master -- yarn.lock
30 |
31 | # 2. Install dependencies fresh, using only the exact versions specified
32 | # in the `yarn.lock` file.
33 | yarn install --frozen-lockfile
34 | ```
35 |
36 | If this solves your problem, you can use `yarn outdated` to see the packages that may have received updates, then upgrade them one at a time with `yarn upgrade the-package-name` to see which upgrade introduces the problem.
37 |
38 | ## Visual Studio (VS) Code formatting issues
39 |
40 | If you're using VS Code and notice that some files are being formatted incorrectly on save, the source is probably a formatter extension you've installed. The reason you're seeing it now is that this project enables the `editor.formatOnSave` setting. Previously, that extension was probably just doing nothing. To fix the problem, you'll need to either properly configure the extension or, if it's simply broken, uninstall it.
41 |
42 | Extensions with known issues include:
43 |
44 | - [Visual Studio Code Format](https://marketplace.visualstudio.com/items?itemName=ryannaddy.vscode-format#review-details)
45 |
--------------------------------------------------------------------------------
/docs/linting.md:
--------------------------------------------------------------------------------
1 | # Linting & formatting
2 |
3 | - [Linting & formatting](#linting--formatting)
4 | - [Languages](#languages)
5 | - [Scripts](#scripts)
6 | - [Terminal](#terminal)
7 | - [Pre-commit](#pre-commit)
8 | - [Editor](#editor)
9 | - [Configuration](#configuration)
10 | - [FAQ](#faq)
11 |
12 | This project uses ESLint, Stylelint, Markdownlint, and Prettier to catch errors and avoid bikeshedding by enforcing a common code style.
13 |
14 | ## Languages
15 |
16 | - **JavaScript** is linted by ESLint and formatted by Prettier
17 | - **HTML** (in templates and JSX) is linted by ESLint
18 | - **CSS** is linted by Stylelint and formatted by Prettier
19 | - **Markdown** is linted by Markdownlint and formatted by Prettier
20 | - **JSON** is formatted by Prettier
21 | - **Images** are minified by `imagemin-lint-staged` (only on pre-commit)
22 |
23 | ## Scripts
24 |
25 | There are a few different contexts in which the linters run.
26 |
27 | ### Terminal
28 |
29 | ```bash
30 | # Lint all files, fixing many violations automatically
31 | yarn lint
32 | ```
33 |
34 | See `package.json` to update.
35 |
36 | ### Pre-commit
37 |
38 | Staged files are automatically linted and tested before each commit. See `lint-staged` in `package.json` to update.
39 |
40 | ### Editor
41 |
42 | In supported editors, all files will be linted and formatted on-save. See [editors.md](editors.md) for details.
43 |
44 | ## Configuration
45 |
46 | This boilerplate ships with opinionated defaults, but you can edit each tools configuration in the following config files:
47 |
48 | - [ESLint](https://eslint.org/docs/user-guide/configuring)
49 | - `.eslintrc.json`
50 | - `.eslintignore`
51 | - [Markdownlint](https://github.com/markdownlint/markdownlint/blob/master/docs/configuration.md)
52 | - `.markdownlintrc`
53 | - [Prettier](https://prettier.io/docs/en/configuration.html)
54 | - `.prettierrc.js`
55 | - `.prettierignore`
56 | - [BrowsersList](https://github.com/browserslist/browserslist)
57 | - `.browserslistrc`
58 |
59 | ## FAQ
60 |
61 | **So many configuration files! Why not move more of this to `package.json`?**
62 |
63 | - Moving all possible configs to `package.json` can make it _really_ packed, so that quickly navigating to a specific config becomes difficult.
64 | - When split out into their own file, many tools provide the option of exporting a config from JS. I do this wherever possible, because dynamic configurations are simply more powerful, able to respond to environment variables and much more.
65 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "awesome-vue-boilerplate",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "NODE_ENV=development vue-cli-service serve",
7 | "build:prd": "NODE_ENV=production vue-cli-service build",
8 | "build:stg": "NODE_ENV=production vue-cli-service build --mode staging",
9 | "report": "NODE_ENV=production vue-cli-service build --report --mode staging && static dist/report.html",
10 | "lint": "vue-cli-service lint",
11 | "new": "hygen new",
12 | "docs": "vuepress dev -p 7070",
13 | "docs:build": "DEPLOY_ENV=gh-pages vuepress build",
14 | "docs:deploy": "gh-pages -d .vuepress/dist"
15 | },
16 | "dependencies": {
17 | "debounce-promise": "^3.1.0",
18 | "element-ui": "~2.5.2",
19 | "lodash": "~4.17.11",
20 | "nprogress": "~0.2.0",
21 | "path-to-regexp": "~2.4.0",
22 | "tailwindcss": "^1.0.0-beta.8",
23 | "v-debounce": "^0.1.2",
24 | "vee-validate": "~2.1.5",
25 | "vue": "~2.5.21",
26 | "vue-awesome": "~3.3.1",
27 | "vue-configuration": "~1.0.3",
28 | "vue-fragment": "~1.5.0",
29 | "vue-i18n": "~8.7.0",
30 | "vue-resource": "~1.5.1",
31 | "vue-router": "~3.0.2",
32 | "vue-router-layout": "~0.1.3",
33 | "vue-wait": "~1.3.3",
34 | "vuex": "~3.1.0",
35 | "vuex-pathify": "~1.1.3",
36 | "vuex-persist": "~2.0.0",
37 | "vuex-router-sync": "~5.0.0"
38 | },
39 | "devDependencies": {
40 | "@babel/plugin-syntax-dynamic-import": "^7.2.0",
41 | "@vue/cli-plugin-babel": "^3.3.0",
42 | "@vue/cli-plugin-eslint": "^3.3.0",
43 | "@vue/cli-service": "^3.3.0",
44 | "@vue/eslint-config-prettier": "^4.0.1",
45 | "babel-eslint": "^10.0.1",
46 | "babel-plugin-component": "^1.1.1",
47 | "babel-plugin-lodash": "^3.3.4",
48 | "eslint": "^5.12.0",
49 | "eslint-plugin-prettier": "^3.0.1",
50 | "eslint-plugin-vue": "^5.1.0",
51 | "gh-pages": "^2.0.1",
52 | "husky": "^1.3.1",
53 | "hygen": "^2.1.1",
54 | "lint-staged": "^8.1.0",
55 | "node-sass": "^4.11.0",
56 | "sass-loader": "^7.1.0",
57 | "style-resources-loader": "^1.2.1",
58 | "vue-cli-plugin-element": "^1.0.1",
59 | "vue-cli-plugin-style-resources-loader": "^0.1.3",
60 | "vue-template-compiler": "^2.5.21",
61 | "vuepress": "^0.14.8"
62 | },
63 | "husky": {
64 | "hooks": {
65 | "pre-commit": "lint-staged"
66 | }
67 | },
68 | "lint-staged": {
69 | "*.js": [
70 | "vue-cli-service lint",
71 | "git add"
72 | ],
73 | "*.vue": [
74 | "vue-cli-service lint",
75 | "git add"
76 | ]
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/components/Breadcrumb.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 | {{ $t(`route.${item.meta.title}`) }}
16 | {{
17 | $t(`route.${item.meta.title}`)
18 | }}
19 |
20 |
21 |
22 |
23 |
24 |
68 |
69 |
81 |
--------------------------------------------------------------------------------
/docs/development.md:
--------------------------------------------------------------------------------
1 | # Setup and development
2 |
3 | - [Setup and development](#setup-and-development)
4 | - [First-time setup](#first-time-setup)
5 | - [Installation](#installation)
6 | - [Dev server](#dev-server)
7 | - [Generators](#generators)
8 | - [Globals](#globals)
9 | - [Base components](#base-components)
10 |
11 | ## First-time setup
12 |
13 | Make sure you have the following installed:
14 |
15 | - [Node](https://nodejs.org/en/) (at least the latest LTS)
16 | - [Yarn](https://yarnpkg.com/lang/en/docs/install/) (at least 1.0)
17 |
18 | Then update the following files to suit your application:
19 |
20 | ## Installation
21 |
22 | ```bash
23 | # Install dependencies from package.json
24 | yarn install
25 | ```
26 |
27 | ## Dev server
28 |
29 | > Note: If you're on Linux and see an `ENOSPC` error when running the commands below, you must [increase the number of available file watchers](https://stackoverflow.com/questions/22475849/node-js-error-enospc#answer-32600959).
30 |
31 | ```bash
32 | # Launch the dev server
33 | yarn dev
34 |
35 | # Launch the dev server and automatically open it in
36 | # your default browser when ready
37 | yarn dev --open
38 | ```
39 |
40 |
41 |
52 |
53 | ## Generators
54 |
55 | This project includes generators to speed up common development tasks. Commands include:
56 |
57 | ```bash
58 | # Generate a new component
59 | yarn new component
60 |
61 | # Generate a new layout component
62 | yarn new layout
63 |
64 | # Generate a new Vuex state
65 | yarn new state
66 | ```
67 |
68 | Update existing or create new generators in the `_templates` folder, with help from the [Hygen docs](http://www.hygen.io/) and [Enquirer docs](https://github.com/enquirer/enquirer).
69 |
70 | ## Globals
71 |
72 | ### Base components
73 |
74 | [Base components](https://vuejs.org/v2/style-guide/#Base-component-names-strongly-recommended) (a.k.a. presentational, dumb, or pure components) that apply app-specific styling and conventions should all begin with the `_base-` prefix. Since these components are typically used in place of raw HTML element (and thus used as frequently), they're automatically globally registered for convenience. This means you don't have to import and locally register them to use them in templates.
75 |
--------------------------------------------------------------------------------
/docs/state.md:
--------------------------------------------------------------------------------
1 | # State management
2 |
3 | - [State management](#state-management)
4 | - [Modules](#modules)
5 | - [Helpers](#helpers)
6 | - [Default Helpers](#default-helpers)
7 |
8 | ## Modules
9 |
10 | The `src/store/modules` directory is where all shared application state lives. Any JS file added here (apart from unit tests) will be automatically registered in the store as a [namespaced module](https://vuex.vuejs.org/en/modules.html#namespacing).
11 |
12 | Read more in the [Vuex modules](https://vuex.vuejs.org/en/modules.html) docs.
13 |
14 | ## Helpers
15 |
16 | The state helpers in `src/store/helpers/*.helper.js` are the components' interface to the Vuex store. Depending on a component's concerns, we can import a subset of these helpers to quickly bring in the data and actions we need.
17 |
18 | You might be thinking, "Why not just automatically inject all of these into every component?" Well, then it would be difficult to figure out where a particular part of state is coming from. As our state becomes increasingly complex, the risk would also increase of accidentally using the same names for internal component state. This way, each component remains traceable, as the necessary `import` will provide a thread back to our helpers file if we ever don't understand where something is coming from.
19 |
20 | Here's an example:
21 |
22 | ```js
23 | import { authComputed } from '@state/helpers/auth'
24 |
25 | export default {
26 | computed: {
27 | ...authComputed,
28 | },
29 | }
30 | ```
31 |
32 | ## Default Helpers
33 |
34 | Vue Awesome Boilerplate has it's own default helpers in `src/store/helpers/default.helper.js`.
35 |
36 | - `makeDefaultState` combines all default state objects
37 | - `makeMutations` combines all mutations objects and adds `resetState` mutation, `resetState` mutation resets specified fields or all data object
38 | - `makeGetters` combines all getters objects
39 | - `makeActions` combines all actions objects
40 | - `withLoading` adds loading to action, which will start loading when action is triggered and stops after complete (success or error)
41 |
42 |
43 | Here's an example:
44 |
45 | ```js
46 | import Auth from '@/resources/Auth';
47 | import { commit } from 'vuex-pathify';
48 | import { makeMutations, makeGetters, makeActions, withLoading } from '@/store/helpers/default.helper';
49 |
50 | const defaultState = {
51 | token: null,
52 | user: null,
53 | };
54 |
55 | const mutations = {};
56 | const getters = {
57 | isAuthenticated: state => !!state.token,
58 | };
59 |
60 | const actions = {
61 | login: withLoading('authState/login', async (_, credentials) => {
62 | const res = await Auth.login(credentials);
63 | commit('authState/SET_TOKEN', res.body.token.access);
64 | commit('authState/SET_USER', res.body.user);
65 | return res;
66 | }),
67 | };
68 |
69 | export default {
70 | state: defaultState,
71 | mutations: makeMutations(defaultState, mutations),
72 | actions: makeActions(defaultState, actions),
73 | getters: makeGetters(defaultState, getters),
74 | };
75 |
76 | ```
77 |
--------------------------------------------------------------------------------
/src/store/modules/index.js:
--------------------------------------------------------------------------------
1 | // Register each file as a corresponding Vuex module. Module nesting
2 | // will mirror [sub-]directory hierarchy and modules are namespaced
3 | // as the camelCase equivalent of their file name.
4 |
5 | import { camelCase } from 'lodash';
6 |
7 | const moduleCache = {};
8 | const root = { modules: {} };
9 | (function updateModules() {
10 | // Allow us to dynamically require all Vuex module files.
11 | // https://webpack.js.org/guides/dependency-management/#require-context
12 | const requireModule = require.context(
13 | // Search for files in the current directory.
14 | '.',
15 | // Search for files in subdirectories.
16 | true,
17 | // Include any .js files that are not this file or a unit test.
18 | /^((?!index|\.unit\.).)*\.js$/
19 | );
20 |
21 | // For every Vuex module...
22 | requireModule.keys().forEach(fileName => {
23 | const moduleDefinition = requireModule(fileName).default;
24 |
25 | // Skip the module during hot reload if it refers to the
26 | // same module definition as the one we have cached.
27 | if (moduleCache[fileName] === moduleDefinition) return;
28 |
29 | // Update the module cache, for efficient hot reloading.
30 | moduleCache[fileName] = moduleDefinition;
31 |
32 | // Get the module path as an array.
33 | const modulePath = fileName
34 | // Remove the "./" from the beginning.
35 | .replace(/^\.\//, '')
36 | // Remove the file extension from the end.
37 | .replace(/\.\w+$/, '')
38 | // Split nested modules into an array path.
39 | .split(/\//)
40 | // camelCase all module namespaces and names.
41 | .map(camelCase);
42 |
43 | // Get the modules object for the current path.
44 | const { modules } = getNamespace(root, modulePath);
45 |
46 | // Add the module to our modules object.
47 | modules[modulePath.pop()] = {
48 | // Modules are namespaced by default.
49 | namespaced: true,
50 | ...moduleDefinition,
51 | };
52 | });
53 |
54 | // If the environment supports hot reloading...
55 | if (module.hot) {
56 | // Whenever any Vuex module is updated...
57 | module.hot.accept(requireModule.id, () => {
58 | // Update `root.modules` with the latest definitions.
59 | updateModules();
60 | // Trigger a hot update in the store.
61 | require('../index').default.hotUpdate({ modules: root.modules });
62 | });
63 | }
64 | })();
65 |
66 | // Recursively get the namespace of a Vuex module, even if nested.
67 | function getNamespace(subtree, path) {
68 | if (path.length === 1) return subtree;
69 |
70 | const namespace = path.shift();
71 | subtree.modules[namespace] = {
72 | modules: {},
73 | ...subtree.modules[namespace],
74 | };
75 | return getNamespace(subtree.modules[namespace], path);
76 | }
77 |
78 | export default root.modules;
79 |
--------------------------------------------------------------------------------
/src/store/helpers/default.helper.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import debouncePromise from 'debounce-promise';
4 | import { make, dispatch } from 'vuex-pathify';
5 | import { set, get, cloneDeep } from 'lodash';
6 |
7 | /**
8 | * make default state
9 | * @param {Object} defaultState
10 | * @param {Object} states
11 | * @returns {Object}
12 | */
13 | export function makeDefaultState(defaultState, ...states) {
14 | const data = { ...defaultState };
15 | for (let d of states) {
16 | Object.assign(data, d);
17 | }
18 | return data;
19 | }
20 |
21 | /**
22 | * make mutations
23 | * @param {Object} defaultState
24 | * @param {Object} mutations
25 | * @returns {Object}
26 | */
27 | export function makeMutations(defaultState, ...mutations) {
28 | const data = { ...make.mutations(defaultState) };
29 | for (let d of mutations) {
30 | Object.assign(data, d);
31 | }
32 |
33 | data.resetState = (state, paths) => {
34 | if (!paths) {
35 | Object.assign(state, cloneDeep(defaultState));
36 | return;
37 | }
38 | for (const path of paths) {
39 | set(state, path, cloneDeep(get(defaultState, path)));
40 | }
41 | };
42 | return data;
43 | }
44 |
45 |
46 | /**
47 | * make getters
48 | * @param {Object} defaultState
49 | * @param {Object} getters
50 | * @returns {Object}
51 | */
52 | export function makeGetters(defaultState, ...getters) {
53 | const data = { ...make.getters(defaultState) };
54 | for (let d of getters) {
55 | Object.assign(data, d);
56 | }
57 | return data;
58 | }
59 |
60 | /**
61 | * make Actions
62 | * @param {Object} defaultState
63 | * @param {Object} actions
64 | * @returns {Object}
65 | */
66 | export function makeActions(defaultState, ...actions) {
67 | const data = { ...make.actions(defaultState) };
68 | for (let d of actions) {
69 | Object.assign(data, d);
70 | }
71 | return data;
72 | }
73 |
74 | /**
75 | * Add loading to action
76 | *
77 | * @export
78 | * @param {String} loaderName
79 | * @param {Function} handler
80 | * @returns {Function}
81 | */
82 | export function withLoading(loaderName, handler) {
83 | return withActions({ loader: loaderName }, handler);
84 | }
85 |
86 | /**
87 | * Add actions to vuex action handler
88 | * @param {{loader: string, debounce: boolean | number = true}} action
89 | * @param handler
90 | * @returns {Promise<(function(...[*]): *)>|(function(...[*]): *)}
91 | */
92 | export function withActions(action, handler) {
93 | const newHandlerFunction = async (...args) => {
94 | let data;
95 | if (action.loader) {
96 | await dispatch('wait/start', action.loader, { root: true });
97 | }
98 | try {
99 | data = await handler(...args);
100 | } finally {
101 | if (action.loader) {
102 | await dispatch('wait/end', action.loader, { root: true });
103 | }
104 | }
105 | return data;
106 | };
107 | if (action.debounce) {
108 | const debounceTime = action.debounce === true ? 300 : action.debounce;
109 | return debouncePromise(newHandlerFunction, debounceTime);
110 | }
111 |
112 | return newHandlerFunction;
113 | }
114 |
--------------------------------------------------------------------------------
/src/views/auth/Login.vue:
--------------------------------------------------------------------------------
1 |
2 |
56 |
57 |
58 |
59 |
93 |
99 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Router from 'vue-router';
3 | import NProgress from 'nprogress';
4 |
5 | import store from '@/store';
6 | import i18n from '@/plugins/i18n.plugin';
7 |
8 | import routes from './routes';
9 |
10 | Vue.use(Router);
11 |
12 | const router = new Router({
13 | mode: 'history',
14 | base: process.env.BASE_URL,
15 | routes,
16 | });
17 |
18 | router.beforeEach(async (to, from, next) => {
19 | function redirectToLogin() {
20 | // Pass the original route to the login component
21 | next({ name: 'login', query: { redirectFrom: to.fullPath } });
22 | }
23 |
24 | // If this isn't an initial page load...
25 | if (from.name !== null) {
26 | // Start the route progress bar.
27 | NProgress.start();
28 | }
29 |
30 | /* FIXME: use https://github.com/declandewet/vue-meta,
31 | https://stackoverflow.com/questions/36612847/how-can-i-bind-the-html-title-content-in-vuejs#answer-40388120
32 | https://github.com/vuejs/vue-router/issues/914#issuecomment-376719306*/
33 | document.title = to.meta.title || i18n.t('app.title');
34 | const authRequired = to.matched.some(record => record.meta.authRequired);
35 |
36 | // If auth isn't required for the route, just continue.
37 | if (!authRequired) return next();
38 |
39 | // If auth is required and the user is logged in...
40 | const token = store.get('authState/isAuthenticated');
41 |
42 | if (token) {
43 | if (store.get('authState@user')) {
44 | return next();
45 | }
46 | // Validate the local user token...
47 | const currentUser = await store.dispatch('authState/fetchCurrentUser');
48 | // Then continue if the token still represents a valid user,
49 | // otherwise redirect to login.
50 | return currentUser ? next() : redirectToLogin();
51 | }
52 |
53 | // If auth is required and the user is NOT currently logged in,
54 | // redirect to login.
55 | redirectToLogin();
56 | });
57 |
58 | router.beforeResolve(async (routeTo, routeFrom, next) => {
59 | // Create a `beforeResolve` hook, which fires whenever
60 | // `beforeRouteEnter` and `beforeRouteUpdate` would. This
61 | // allows us to ensure data is fetched even when params change,
62 | // but the resolved route does not. We put it in `meta` to
63 | // indicate that it's a hook we created, rather than part of
64 | // Vue Router (yet?).
65 | try {
66 | // For each matched route...
67 | for (const route of routeTo.matched) {
68 | await new Promise((resolve, reject) => {
69 | // If a `beforeResolve` hook is defined, call it with
70 | // the same arguments as the `beforeEnter` hook.
71 | if (route.meta && route.meta.beforeResolve) {
72 | route.meta.beforeResolve(routeTo, routeFrom, (...args) => {
73 | // If the user chose to redirect...
74 | if (args.length) {
75 | // If redirecting to the same route we're coming from...
76 | if (routeFrom.name === args[0].name) {
77 | // Complete the animation of the route progress bar.
78 | NProgress.done();
79 | }
80 | // Complete the redirect.
81 | next(...args);
82 | reject(new Error('Redirected'));
83 | } else {
84 | resolve();
85 | }
86 | });
87 | } else {
88 | // Otherwise, continue resolving the route.
89 | resolve();
90 | }
91 | });
92 | }
93 | // If a `beforeResolve` hook chose to redirect, just return.
94 | } catch (error) {
95 | return;
96 | }
97 |
98 | // If we reach this point, continue resolving the route.
99 | next();
100 | });
101 |
102 | // When each route is finished evaluating...
103 | router.afterEach(() => {
104 | // Complete the animation of the route progress bar.
105 | NProgress.done();
106 | });
107 |
108 | export default router;
109 |
--------------------------------------------------------------------------------
/src/translations/en.js:
--------------------------------------------------------------------------------
1 | export default {
2 | app: {
3 | title: 'App Name',
4 | },
5 | keywords: {
6 | login: 'Login',
7 | signUp: 'Sign up',
8 | company_name: 'Company Name',
9 | industry: 'Industry',
10 | business_address: 'Business Address',
11 | country: 'Country',
12 | state: 'State',
13 | city: 'City',
14 | zip_code: 'Zip code',
15 | business_email: 'Business email',
16 | first_name: 'First name',
17 | last_name: 'Last name',
18 | phone_number: 'Phone number',
19 | person_email: 'Contact person email',
20 | username: 'Login Name',
21 | password: 'Password',
22 | confirm_password: 'Confirm password',
23 | signIn: 'Sign In',
24 | all: 'All',
25 | gender: 'Gender',
26 | male: 'Male',
27 | female: 'Female',
28 | start_date: 'Start Date',
29 | end_date: 'End Date',
30 | mobile: 'Mobile',
31 | desktop: 'Desktop',
32 | back: 'Back',
33 | next: 'Next',
34 | age: 'Age',
35 | language: 'Language',
36 | save: 'Save',
37 | avatar: 'Avatar',
38 | reset_filters: 'Reset all filters',
39 | publish: 'Publish',
40 | delete: 'Delete',
41 | cancel: 'Cancel',
42 | no: 'No',
43 | yes: 'Yes',
44 | type_something: 'Type something',
45 | format: 'Format',
46 | pixels: 'pixels',
47 | placement: 'Placement',
48 | preview: 'Preview',
49 | carousel: 'Carousel',
50 | single_image: 'Single image',
51 | single_video: 'Single video',
52 | slide_show: 'Slide show',
53 | feed: 'Feed',
54 | right_column: 'Right column',
55 | left_column: 'Left Column',
56 | instant_article: 'Instant articles',
57 | in_stream_video: 'In-Stream Videos',
58 | stories: 'Stories',
59 | shop_now: 'Shop now',
60 | subscribe: 'Subscribe',
61 | watch_now: 'Watch now',
62 | download: 'Download',
63 | learn_more: 'Learn more',
64 | call_now: 'Call now',
65 | apply_now: 'Apply now',
66 | contact_us: 'Contact us',
67 | id: 'ID',
68 | },
69 | route: {
70 | // [routerName]: translation
71 | home: 'Home',
72 | reporting: 'Reporting',
73 | past_ads: 'Past ads',
74 | current_ads: 'Current ads',
75 | current_requests: 'Current Requests',
76 | pending_requests: 'Pending requests',
77 | approved_requests: 'Approved requests',
78 | not_approved_requests: 'Not approved requests',
79 | rejected_requests: 'Rejected requests',
80 | create_campaign: 'Create campaign',
81 | messaging: 'Messaging',
82 | upload_image: 'Upload image',
83 | login: 'Login',
84 | registration: 'Registration',
85 | },
86 | login: {
87 | title: 'Sign in to continue',
88 | login_placeholder: 'Enter your Login name',
89 | password_placeholder: 'Enter your password',
90 | have_not_account: "Don't have an account?",
91 | },
92 | errors: {
93 | err_required: 'This field is required',
94 | err_unauthorized: 'Please sign in',
95 | err_internal: 'Internal server error!',
96 | err_invalid_credentials: 'Invalid credentials',
97 | err_complete_captcha: 'Please complete captcha',
98 | err_user_username_cannot_be_null: 'Login name is required',
99 | err_user_password_cannot_be_null: 'Password is required',
100 | err_user_personemail_cannot_be_null: 'Person email is required',
101 | err_user_companyemail_cannot_be_null: 'Business email is required',
102 | err_phone_not_valid: 'Phone number is not valid',
103 | err_validation_isemail_on_personemail_failed: 'Person email is not valid',
104 | err_validation_isemail_on_companyemail_failed: 'Business email is not valid',
105 | err_username_must_be_unique: 'Login name must be unique',
106 | err_personemail_must_be_unique: 'Person email must be unique',
107 | err_companyemail_must_be_unique: 'Business email must be unique',
108 | err_phone_must_be_unique: 'Phone number must be unique',
109 | err_invalid_file_type: 'Uploaded file is not a valid image. Only JPG and PNG files are allowed.',
110 | },
111 | };
112 |
--------------------------------------------------------------------------------
/docs/architecture.md:
--------------------------------------------------------------------------------
1 | # Architecture
2 |
3 | - [Architecture](#architecture)
4 | - [`_templates`](#_templates)
5 | - [`.vscode`](#vscode)
6 | - [`docs`](#docs)
7 | - [`.vuepress`](#vuepress)
8 | - [`public`](#public)
9 | - [`index.html`](#indexhtml)
10 | - [`src`](#src)
11 | - [`styles`](#styles)
12 | - [`imports`](#imports)
13 | - [`assets`](#assets)
14 | - [`common`](#common)
15 | - [`filters`](#filters)
16 | - [`interceptors`](#interceptors)
17 | - [`config`](#config)
18 | - [`mixin`](#mixin)
19 | - [`plugins`](#plugins)
20 | - [`resources`](#resources)
21 | - [`components`](#components)
22 | - [`router`](#router)
23 | - [`translations`](#translations)
24 | - [`store`](#store)
25 | - [`modules`](#modules)
26 | - [`utils`](#utils)
27 | - [`App.vue`](#appvue)
28 | - [`main.js`](#mainjs)
29 | - [`.env.*`](#env)
30 | - [`.eslintrc.json`](#eslintrcjson)
31 | - [`tailwind.config.js`](#tailwindjs)
32 |
33 | ## `_templates`
34 |
35 | Generator templates to speed up development. See [the development doc](development.md#generators) for more.
36 |
37 | ## `.vscode`
38 |
39 | Settings and extensions specific to this project, for Visual Studio Code. See [the editors doc](editors.md#visual-studio-code) for more.
40 |
41 | ## `docs`
42 |
43 | You found me! :wink:
44 |
45 | ## `.vuepress`
46 |
47 | Documentation config and destination folder See [VuePress doc](https://vuepress.vuejs.org) for more
48 |
49 | ## `public`
50 |
51 | Where you'll keep any static assets, to be added to the `dist` directory without processing from our build system.
52 |
53 | ### `index.html`
54 |
55 | This one file actually _does_ get processed by our build system, allowing us to inject some information from Webpack with [EJS](http://ejs.co/), such as the title, then add our JS and CSS.
56 |
57 | ## `src`
58 |
59 | Where we keep all our source files.
60 |
61 | ## `styles`
62 |
63 | Where we keep our [design variables and tooling](tech.md#design-variables-and-tooling).
64 |
65 | ### `imports`
66 |
67 | Styles which are stored to this folder will be automatically imported in each scss and component file
68 |
69 | ### `assets`
70 |
71 | This project manages assets via Vue CLI. Learn more about [its asset handling here](https://cli.vuejs.org/guide/html-and-static-assets.html).
72 |
73 | ### `common`
74 |
75 | Where we keep common javascript files, e.g. constants or validator functions. [vue-configuration module uses this](https://github.com/alex-oleshkevich/vue-config#readme)
76 |
77 | ### `filters`
78 |
79 | This folder contains all global [filters (pipes)](https://v1.vuejs.org/guide/custom-filter.html), each filter automatically will be added to global filters list.
80 |
81 | ### `interceptors`
82 |
83 | Where we are keep [interceptors](https://github.com/pagekit/vue-resource/blob/develop/docs/http.md#interceptors) of [Http Client](https://github.com/pagekit/vue-resource)
84 |
85 | ### `config`
86 |
87 | This folder contains all configuration of our application.
88 |
89 | ### `mixin`
90 |
91 | You can store local or global mixins here
92 |
93 | ### `plugins`
94 |
95 | In this folder need to add plugin which we are trying to integrate to our application
96 | You can store local or global mixins here
97 |
98 | ### `resources`
99 |
100 | This folder contains [resource](https://github.com/pagekit/vue-resource/blob/develop/docs/resource.md) Models of [Http Client](https://github.com/pagekit/vue-resource)
101 |
102 | ### `components`
103 |
104 | Where most of the components in our app will live, including our [global base components](development.md#base-components).
105 |
106 | ### `router`
107 |
108 | Where the router, routes, and any routing-related. See [the routing doc](routing.md) for more.
109 | Where we keep our [design variables and tooling](tech.md#design-variables-and-tooling).
110 |
111 | ### `translations`
112 |
113 | Here we are store translation files of i18n. in translation files is enabled [hot module replacement](https://webpack.js.org/concepts/hot-module-replacement/)
114 |
115 | ## `store`
116 |
117 | Where all our global state management lives. See [the state management doc](state.md) for more.
118 |
119 | ### `modules`
120 |
121 | Where all our vuex stores lives. See [vuex modules documentation](https://vuex.vuejs.org/guide/modules.html) for more.
122 |
123 | ### `utils`
124 |
125 | These are utility functions you may want to share between many files in your application. They will always be pure and never have side effects, meaning if you provide a function the same arguments, it will always return the same result. These should also never directly affect the DOM or interface with our Vuex state.
126 |
127 | ### `App.vue`
128 |
129 | The root Vue component that simply delegates to the router view. This is typically the only component to contain global CSS.
130 |
131 | ### `main.js`
132 |
133 | The entry point to our app, were we create our Vue instance and mount it to the DOM.
134 |
135 | ## `.env.*`
136 |
137 | Environment variables which will load before app start and will be stored in `process.env`, (*) is a env name (development, staging, production, ...)
138 |
139 | Note: each env variable name must be start with `VUE_APP_`
140 |
141 | ## `.eslintrc.json`
142 |
143 | Eslint configuration file, See [the eslint doc](https://eslint.org/) for more.
144 | Note: each env variable name must be start with `VUE_APP_`
145 |
146 | ## `tailwind.config.js`
147 |
148 | Configuration file for tailwindcss framework, See [tailwindcss doc](https://tailwindcss.com/docs) for more.
149 |
--------------------------------------------------------------------------------
/src/vuex-pathify.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'vuex-pathify' {
2 | import { Store, CommitOptions, DispatchOptions, GetterTree, ActionTree, MutationTree } from 'vuex';
3 |
4 | /************************************************************************
5 | *** DEFAULT EXPORT ***
6 | ***********************************************************************/
7 | type MappingTypes = "state" | "getters" | "mutations" | "actions";
8 | type MappingPresets = "standard" | "simple";
9 | type MappingFunction = (
10 | type: MappingTypes,
11 | name: string,
12 | formatters: MappingFormatters
13 | ) => string;
14 |
15 | interface MappingFormatters {
16 | camel: (...args: string[]) => string;
17 | snake: (...args: string[]) => string;
18 | const: (...args: string[]) => string;
19 | }
20 |
21 | interface Options {
22 | mapping: MappingPresets | MappingFunction;
23 | deep: 1 | 2 | 3;
24 | strict: boolean;
25 | cache: boolean;
26 | }
27 |
28 | interface DefaultExport {
29 | debug: () => void;
30 | plugin: (store: Store) => void;
31 | options: Options;
32 | }
33 |
34 | const defaultExport: DefaultExport;
35 |
36 | export default defaultExport;
37 | /************************************************************************
38 | *** NAMED EXPORTS ***
39 | ***********************************************************************/
40 |
41 | /*--------------------------------------------------------------------------
42 | SHARED
43 | ------------------------------------------------------------------------*/
44 | type GetAccessor = () => T;
45 | type SetAccessor = (newValue: T) => T; // TODO: Do setters always return same type as input.
46 |
47 | /*--------------------------------------------------------------------------
48 | make
49 | ------------------------------------------------------------------------*/
50 | type StateFunction = () => T;
51 |
52 | interface Make {
53 | mutations: (
54 | state: State | StateFunction
55 | ) => MutationTree;
56 |
57 | actions: (
58 | state: State | StateFunction
59 | ) => ActionTree;
60 |
61 | getters: (
62 | state: State | StateFunction
63 | ) => GetterTree;
64 | }
65 |
66 | export const make: Make;
67 |
68 | /*--------------------------------------------------------------------------
69 | Payload
70 | ------------------------------------------------------------------------*/
71 | // TODO: Not documented/public class, may need refinement by module author.
72 | export class Payload {
73 | constructor(path: string, value: any);
74 | expr: string;
75 | path: string;
76 | value: any;
77 | update(target: T): T; // TODO: Needs details. Define an interface instead of object. And be sure that return type is same as input.
78 | }
79 |
80 | /*--------------------------------------------------------------------------
81 | get/sync/call
82 | ------------------------------------------------------------------------*/
83 | export function get(
84 | path: string | object,
85 | props?: string[] | object
86 | ): { get: GetAccessor };
87 |
88 | export function sync(
89 | path: string | object,
90 | props?: string[] | object
91 | ): { get: GetAccessor; set: SetAccessor };
92 |
93 | export function call(
94 | path: string | object,
95 | props?: string[] | object
96 | ): (payload: any) => any | Promise;
97 |
98 | /*--------------------------------------------------------------------------
99 | commit
100 | ------------------------------------------------------------------------*/
101 | // Copied from vuex types.
102 | export function commit(
103 | type: string,
104 | payload?: any,
105 | options?: CommitOptions
106 | ): void;
107 | export function commit(
108 | payloadWithType: P,
109 | options?: CommitOptions
110 | ): void;
111 |
112 | /*--------------------------------------------------------------------------
113 | dispatch
114 | ------------------------------------------------------------------------*/
115 | // Copied from vuex types.
116 | export function dispatch(
117 | type: string,
118 | payload?: any,
119 | options?: DispatchOptions
120 | ): Promise;
121 | export function dispatch(
122 | payloadWithType: P,
123 | options?: DispatchOptions
124 | ): Promise;
125 |
126 | /*--------------------------------------------------------------------------
127 | registerModule
128 | ------------------------------------------------------------------------*/
129 | export function registerModule(
130 | path: string | string[],
131 | module: object,
132 | callback: (...args: any[]) => any, // TODO: Needs refinement.
133 | options: object
134 | ): object; // TOD
135 | }
136 |
137 | // vuex-pathify
138 |
--------------------------------------------------------------------------------
/src/styles/fonts/icomoon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Generated by IcoMoon
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/layouts/main.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | Notifications
22 |
23 |
24 |
25 | {{ item.name }}
26 | {{ item.time }}
27 |
28 |
{{ item.description }}
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | Home
38 | Settings
39 | Log out
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
156 |
157 |
175 |
--------------------------------------------------------------------------------
/src/views/Page404.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
26 |
27 |
OOPS!
28 |
29 | All rights reserved
30 | Awesome vue boilerplate
35 |
36 |
37 | The webmaster said that you can't enter this page.......
38 |
39 |
40 | Please check that the URL you entered is correct. Please
41 | click the button below to return to the homepage or send an
42 | error report.
43 |
44 |
Back to home
47 |
48 |
49 |
50 |
51 |
52 |
61 |
62 |
256 |
--------------------------------------------------------------------------------
/docs/tech.md:
--------------------------------------------------------------------------------
1 | # Languages and technologies
2 |
3 | - [Languages and technologies](#languages-and-technologies)
4 | - [JavaScript](#javascript)
5 | - [Polyfills](#polyfills)
6 | - [Vue](#vue)
7 | - [Vue Router](#vue-router)
8 | - [Vuex (state management)](#vuex-state-management)
9 | - [JavaScript FAQ](#javascript-faq)
10 | - [HTML](#html)
11 | - [Templates](#templates)
12 | - [Render functions](#render-functions)
13 | - [HTML FAQ](#html-faq)
14 | - [CSS](#css)
15 | - [SCSS](#scss)
16 | - [Importing global modules](#importing-global-modules)
17 | - [Referencing aliased asset URLs](#referencing-aliased-asset-urls)
18 | - [Design variables and tooling](#design-variables-and-tooling)
19 | - [CSS modules](#css-modules)
20 | - [Styling subcomponents](#styling-subcomponents)
21 | - [Sharing SCSS variables with JavaScript](#sharing-scss-variables-with-javascript)
22 | - [Global CSS](#global-css)
23 | - [CSS FAQ](#css-faq)
24 |
25 | ## JavaScript
26 |
27 | Our JavaScript is compiled by Babel, using the [`@vue/babel-preset-app`](https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/babel-preset-app) as a base configuration. You can update this configuration in `.babelrc.js`.
28 |
29 | If you're new to features such as `const`, `let`, and `=>` (arrow functions), take some time to read about the following features in Babel's ES2015 guide:
30 |
31 | - [Arrow functions](https://babeljs.io/learn-es2015/#ecmascript-2015-features-arrows-and-lexical-this)
32 | - [Template literals](https://babeljs.io/learn-es2015/#ecmascript-2015-features-template-strings)
33 | - [Destructuring](https://babeljs.io/learn-es2015/#ecmascript-2015-features-destructuring)
34 | - [Spread operator](https://babeljs.io/learn-es2015/#ecmascript-2015-features-default-rest-spread)
35 | - [`let`/`const`](https://babeljs.io/learn-es2015/#ecmascript-2015-features-let-const)
36 | - [`for`...`of`](https://babeljs.io/learn-es2015/#ecmascript-2015-features-iterators-for-of)
37 |
38 | Reading these sections alone will get you 99% of the way to mastering Babel code. It's also a good idea to read about Promises, if you don't yet feel comfortable with them. Here's a [good intro](https://developers.google.com/web/fundamentals/getting-started/primers/promises).
39 |
40 | ### Polyfills
41 |
42 | This project uses Vue CLI's [modern mode](https://cli.vuejs.org/guide/browser-compatibility.html#modern-mode), which creates two bundles: one modern bundle targeting modern browsers that support [ES modules](https://jakearchibald.com/2017/es-modules-in-browsers/), and one legacy bundle targeting older browsers that do not.
43 |
44 | For each bundle, polyfills for any JavaScript features you use are included based on the target bundle and supported browsers defined by `browserslist` in `package.json`.
45 |
46 | ### Vue
47 |
48 | Since Vue is such a huge part of our app, I strongly recommend everyone read through at least the _Essentials_ of [Vue's guide](https://vuejs.org/v2/guide/).
49 |
50 | ### Vue Router
51 |
52 | To understand how to manage pages with Vue Router, I recommend reading through the _Essentials_ of [those docs](https://router.vuejs.org/en/essentials/getting-started.html). Then you can read more about [routing in this application](routing.md).
53 |
54 | ### Vuex (state management)
55 |
56 | To wrap your head around our state management, I recommend reading through [those docs](https://vuex.vuejs.org/guide), starting at _What is Vuex?_ and stopping before _Application Architecture_. Then skip down and read [_Form Handling_](https://vuex.vuejs.org/en/forms.html) and [_Testing_](https://vuex.vuejs.org/en/testing.html). Finally, read about [state management in this application](state.md).
57 |
58 | ### JavaScript FAQ
59 |
60 | **Why not use TypeScript instead of JavaScript? Isn't that more appropriate for enterprise environments?**
61 |
62 | At its current rate of development, I think TypeScript will eventually _become_ the standard, but I don't think it's there yet. First, I don't believe the advantages are currently that significant:
63 |
64 | - The vast majority of bugs I encounter are _not_ due to type violations. The most powerful tools against bugs remain linting, tests, and code reviews - none of which are made easier by TypeScript.
65 | - TypeScript doesn't guarantee type safety - that still requires discipline. You can still use hundreds of `any` annotations and libraries without any type definitions.
66 | - In Visual Studio Code, users can already get a lot of useful intellisense (including type information) without having to use TypeScript.
67 | - You can get type checks without static types. Just not at compile time. Between Vue's type checks for props, `typeof`, and `instanceof`, developers can still get warnings about type violations during development and tests.
68 |
69 | There are also a few disadvantages I've seen in practice:
70 |
71 | - Despite most bugs having nothing to do with type violations, developers can spend _a lot_ of time working towards full type safety. As I mentioned earlier, I think that time would be better spent on tests and code reviews.
72 | - ESLint remains a much more versatile linter than TSLint and [its TypeScript parser](https://github.com/eslint/typescript-eslint-parser) is still experimental, so may waste time with false positives - or worse, simply miss clear violations.
73 |
74 | ## HTML
75 |
76 | All HTML will exist within [`.vue` files](https://vuejs.org/v2/guide/single-file-components.html), either:
77 |
78 | - in a ``, or
79 | - in a [`render` function](https://vuejs.org/v2/guide/render-function.html), optionally using [JSX](https://vuejs.org/v2/guide/render-function.html#JSX).
80 |
81 | ### [Templates](https://vuejs.org/v2/guide/syntax.html)
82 |
83 | ~95% of HTML will be in `.vue` files. Since Vue has a chance to parse it before the browser does, we can also do a few extra things that normally aren't possible in a browser.
84 |
85 | For example, any element or component can be self-closing:
86 |
87 | ```html
88 |
89 | ```
90 |
91 | The above simply compiles to:
92 |
93 | ```html
94 |
95 | ```
96 |
97 | This feature is especially useful when writing components with long names, but no content:
98 |
99 | ```html
100 |
105 | ```
106 |
107 | ### [Render functions](https://vuejs.org/v2/guide/render-function.html)
108 |
109 | Render functions are _alternatives_ to templates. Components using render functions will be relatively rare, written only when we need either:
110 |
111 | - the full expressive power of JavaScript, or
112 | - better rendering performance through stateless, [functional components](https://vuejs.org/v2/guide/render-function.html#Functional-Components)
113 |
114 | These components can optionally be written using an HTML-like syntax within JavaScript called [JSX](https://vuejs.org/v2/guide/render-function.html#JSX), including support for [some template features](https://github.com/vuejs/babel-preset-vue#supports-event-modifiers).
115 |
116 | ### HTML FAQ
117 |
118 | **Why not use a preprocessor like Jade instead of HTML?**
119 |
120 | Jade offers too little convenience (no new features we'd want, just simpler syntax) and would break `eslint-plugin-vue`'s template linting.
121 |
122 | **If using a render function instead of a template, why not use a `.js(x)` file instead of a `.vue` file?**
123 |
124 | There are no advantages to using a JS(X) file, other than not having to use a `