removed some eslint file changes

pull/1097/head
smit95tpatel 2021-12-17 14:11:37 +05:30
parent 1aee4294c1
commit 9ebb8e717d
5 changed files with 337 additions and 510 deletions

3
.gitignore vendored
View File

@ -41,4 +41,5 @@ tmp
/frontend/cypress/videos/
services.yml
statping.wiki
assets/
assets/
.vscode

View File

@ -1,59 +1,56 @@
<template>
<div id="app">
<router-view />
<Footer v-if="$route.path !== '/setup'" />
<router-view/>
<Footer v-if="$route.path !== '/setup'"/>
</div>
</template>
<script>
const Footer = () => import(/* webpackChunkName: "index" */ './components/Index/Footer');
export default {
name: 'App',
const Footer = () => import(/* webpackChunkName: "index" */ "./components/Index/Footer");
export default {
name: 'app',
components: {
Footer
Footer
},
data () {
return {
loaded: false,
version: '',
};
data() {
return {
loaded: false,
version: "",
}
},
computed: {
core () {
return this.$store.getters.core;
}
core() {
return this.$store.getters.core
}
},
async beforeMount () {
await this.$store.dispatch('loadCore');
this.$i18n.locale = this.core.language || 'en';
// this.$i18n.locale = "ru";
if (!this.core.setup) {
this.$router.push('/setup');
}
if (this.$route.path !== '/setup') {
if (this.$store.state.admin) {
await this.$store.dispatch('loadAdmin');
} else {
await this.$store.dispatch('loadRequired');
}
this.loaded = true;
async beforeMount() {
await this.$store.dispatch('loadCore')
this.$i18n.locale = this.core.language || "en";
// this.$i18n.locale = "ru";
if (!this.core.setup) {
this.$router.push('/setup')
}
if (this.$route.path !== '/setup') {
if (this.$store.state.admin) {
await this.$store.dispatch('loadAdmin')
} else {
await this.$store.dispatch('loadRequired')
}
this.loaded = true
}
},
async mounted () {
if (this.$route.path !== '/setup') {
if (this.$store.state.admin) {
this.logged_in = true;
// await this.$store.dispatch('loadAdmin')
}
async mounted() {
if (this.$route.path !== '/setup') {
if (this.$store.state.admin) {
this.logged_in = true
// await this.$store.dispatch('loadAdmin')
}
}
}
};
}
</script>
<style lang="scss">
@import "./assets/css/bootstrap.min.css";
@import "./assets/scss/index";
</style>
</style>

View File

@ -1,302 +1,200 @@
<template>
<div>
<div v-if="servicesList.length === 0">
<div class="alert alert-dark d-block mt-3 mb-0">
You currently don't have any services!
</div>
</div>
<table
v-else
class="table"
>
<thead>
<div>
<div v-if="servicesList.length === 0">
<div class="alert alert-dark d-block mt-3 mb-0">
You currently don't have any services!
</div>
</div>
<table v-else class="table">
<thead>
<tr>
<th scope="col">
{{ $t('name') }}
</th>
<th
scope="col"
class="d-none d-md-table-cell"
>
{{ $t('status') }}
</th>
<th
scope="col"
class="d-none d-md-table-cell"
>
{{ $t('visibility') }}
</th>
<th
scope="col"
class="d-none d-md-table-cell"
>
{{ $t('group') }}
</th>
<th
scope="col"
class="d-none d-md-table-cell"
style="width: 130px"
>
{{ $t('failures') }}
<div
class="btn-group float-right"
role="group"
>
<a
type="button"
class="small"
:class="{'text-success': list_timeframe==='3h', 'text-muted': list_timeframe!=='3h'}"
@click="list_timeframe='3h'"
>
3h
</a>
<a
type="button"
class="small"
:class="{'text-success': list_timeframe==='12h', 'text-muted': list_timeframe!=='12h'}"
@click="list_timeframe='12h'"
>
12h
</a>
<a
type="button"
class="small"
:class="{'text-success': list_timeframe==='24h', 'text-muted': list_timeframe!=='24h'}"
@click="list_timeframe='24h'"
>
24h
</a>
<a
type="button"
class="small"
:class="{'text-success': list_timeframe==='7d', 'text-muted': list_timeframe!=='7d'}"
@click="list_timeframe='7d'"
>
7d
</a>
</div>
</th>
<th scope="col" />
<th scope="col">{{$t('name')}}</th>
<th scope="col" class="d-none d-md-table-cell">{{$t('status')}}</th>
<th scope="col" class="d-none d-md-table-cell">{{$t('visibility')}}</th>
<th scope="col" class="d-none d-md-table-cell">{{ $t('group') }}</th>
<th scope="col" class="d-none d-md-table-cell" style="width: 130px">
{{$t('failures')}}
<div class="btn-group float-right" role="group">
<a @click="list_timeframe='3h'" type="button" class="small" :class="{'text-success': list_timeframe==='3h', 'text-muted': list_timeframe!=='3h'}">3h</a>
<a @click="list_timeframe='12h'" type="button" class="small" :class="{'text-success': list_timeframe==='12h', 'text-muted': list_timeframe!=='12h'}">12h</a>
<a @click="list_timeframe='24h'" type="button" class="small" :class="{'text-success': list_timeframe==='24h', 'text-muted': list_timeframe!=='24h'}">24h</a>
<a @click="list_timeframe='7d'" type="button" class="small" :class="{'text-success': list_timeframe==='7d', 'text-muted': list_timeframe!=='7d'}">7d</a>
</div>
</th>
<th scope="col"></th>
</tr>
</thead>
<Draggable
id="services_list"
v-model="servicesList"
tag="tbody"
handle=".drag_icon"
>
<tr
v-for="(service, index) in servicesList"
:key="service.id"
>
<td>
<span
v-if="$store.state.admin"
class="drag_icon d-none d-md-inline"
>
<FontAwesomeIcon
icon="bars"
class="mr-3"
/>
</span> {{ service.name }}
</td>
<td class="d-none d-md-table-cell">
<span
class="badge text-uppercase"
:class="{'badge-success': service.online, 'badge-danger': !service.online}"
>
{{ service.online ? $t('online') : $t('offline') }}
</span>
</td>
<td class="d-none d-md-table-cell">
<span
class="badge text-uppercase"
:class="{'badge-primary': service.public, 'badge-secondary': !service.public}"
>
{{ service.public ? $t('public') : $t('private') }}
</span>
</td>
<td class="d-none d-md-table-cell">
<div v-if="service.group_id !== 0">
<span class="badge badge-secondary">
{{ serviceGroup(service) }}
</span>
</div>
</td>
<td class="d-none d-md-table-cell">
<ServiceSparkList
:service="service"
:timeframe="list_timeframe"
/>
</td>
<td class="text-right">
<div class="btn-group">
<button
v-if="$store.state.admin"
:disabled="loading"
class="btn btn-sm btn-outline-secondary"
@click.prevent="goto({path: `/dashboard/edit_service/${service.id}`, params: {service: service} })"
>
<FontAwesomeIcon icon="edit" />
</button>
<button
:disabled="loading"
class="btn btn-sm btn-outline-secondary"
@click.prevent="goto({path: serviceLink(service), params: {service: service} })"
>
<FontAwesomeIcon icon="chart-area" />
</button>
<button
v-if="$store.state.admin"
:disabled="loading"
class="btn btn-sm btn-danger"
@click.prevent="deleteService(service)"
>
<FontAwesomeIcon
v-if="!loading"
icon="times"
/>
<FontAwesomeIcon
v-if="loading"
icon="circle-notch"
spin
/>
</button>
</div>
</td>
</tr>
</Draggable>
</thead>
<draggable id="services_list" tag="tbody" v-model="servicesList" handle=".drag_icon">
<tr v-for="(service, index) in servicesList" :key="service.id">
<td>
<span v-if="$store.state.admin" class="drag_icon d-none d-md-inline">
<font-awesome-icon icon="bars" class="mr-3"/>
</span> {{service.name}}
</td>
<td class="d-none d-md-table-cell">
<span class="badge text-uppercase" :class="{'badge-success': service.online, 'badge-danger': !service.online}">
{{service.online ? $t('online') : $t('offline')}}
</span>
</td>
<td class="d-none d-md-table-cell">
<span class="badge text-uppercase" :class="{'badge-primary': service.public, 'badge-secondary': !service.public}">
{{service.public ? $t('public') : $t('private')}}
</span>
</td>
<td class="d-none d-md-table-cell">
<div v-if="service.group_id !== 0">
<span class="badge badge-secondary">{{serviceGroup(service)}}</span>
</div>
</td>
<td class="d-none d-md-table-cell">
<ServiceSparkList :service="service" :timeframe="list_timeframe"/>
</td>
<td class="text-right">
<div class="btn-group">
<button :disabled="loading" v-if="$store.state.admin" @click.prevent="goto({path: `/dashboard/edit_service/${service.id}`, params: {service: service} })" class="btn btn-sm btn-outline-secondary">
<font-awesome-icon icon="edit" />
</button>
<button :disabled="loading" @click.prevent="goto({path: serviceLink(service), params: {service: service} })" class="btn btn-sm btn-outline-secondary">
<font-awesome-icon icon="chart-area" />
</button>
<button :disabled="loading" v-if="$store.state.admin" @click.prevent="deleteService(service)" class="btn btn-sm btn-danger">
<font-awesome-icon v-if="!loading" icon="times" />
<font-awesome-icon v-if="loading" icon="circle-notch" spin/>
</button>
</div>
</td>
</tr>
</draggable>
</table>
</div>
</div>
</template>
<script>
import Api from '../../API';
import ServiceSparkList from '@/components/Service/ServiceSparkList';
import Modal from '@/components/Elements/Modal';
const Draggable = () => import(/* webpackChunkName: "dashboard" */ 'vuedraggable');
import Api from "../../API";
import ServiceSparkList from "@/components/Service/ServiceSparkList";
import Modal from "@/components/Elements/Modal";
const draggable = () => import(/* webpackChunkName: "dashboard" */ 'vuedraggable')
const ToggleSwitch = () => import(/* webpackChunkName: "dashboard" */ '../../forms/ToggleSwitch');
export default {
name: 'ServicesList',
name: 'ServicesList',
components: {
Modal,
ServiceSparkList,
Modal,
ServiceSparkList,
ToggleSwitch,
Draggable
draggable
},
data () {
data() {
return {
loading: false,
list_timeframe: '12h',
chartOpts: {
chart: {
type: 'bar',
height: 50,
sparkline: {
enabled: true
},
loading: false,
list_timeframe: "12h",
chartOpts: {
chart: {
type: 'bar',
height: 50,
sparkline: {
enabled: true
},
},
xaxis: {
type: 'numeric',
},
showPoint: false,
fullWidth:true,
chartPadding: {top: 0,right: 0,bottom: 0,left: 0},
stroke: {
curve: 'straight'
},
fill: {
opacity: 0.8,
},
yaxis: {
min: 0
},
plotOptions: {
bar: {
colors: {
ranges: [{
from: 0,
to: 1,
color: '#39c10a'
}, {
from: 2,
to: 90,
color: '#e01a1a'
}]
},
xaxis: {
type: 'numeric',
},
showPoint: false,
fullWidth:true,
chartPadding: { top: 0,right: 0,bottom: 0,left: 0 },
stroke: {
curve: 'straight'
},
fill: {
opacity: 0.8,
},
yaxis: {
min: 0
},
plotOptions: {
bar: {
colors: {
ranges: [ {
from: 0,
to: 1,
color: '#39c10a'
}, {
from: 2,
to: 90,
color: '#e01a1a'
} ]
},
},
},
tooltip: {
theme: false,
enabled: false,
},
title: {
enabled: false,
},
subtitle: {
enabled: false,
}
},
},
tooltip: {
theme: false,
enabled: false,
},
title: {
enabled: false,
},
subtitle: {
enabled: false,
}
};
},
}
}
},
computed: {
servicesList: {
get () {
return this.$store.getters.servicesInOrder;
return this.$store.getters.servicesInOrder
},
set (value) {
this.updateOrder(value);
this.updateOrder(value)
}
}
},
methods: {
goto (to) {
this.$router.push(to);
methods: {
goto(to) {
this.$router.push(to)
},
async updateOrder (value) {
const data = [];
value.forEach((s, k) => {
data.push({ service: s.id, order: k + 1 });
});
await Api.services_reorder(data);
await this.update();
async updateOrder(value) {
let data = [];
value.forEach((s, k) => {
data.push({ service: s.id, order: k + 1 })
});
await Api.services_reorder(data)
await this.update()
},
tester(s) {
console.log(s)
},
tester (s) {
console.log(s);
async delete(s) {
this.loading = true
await Api.service_delete(s.id)
await this.update()
this.loading = false
},
async delete (s) {
this.loading = true;
await Api.service_delete(s.id);
await this.update();
this.loading = false;
},
async deleteService (s) {
async deleteService(s) {
const modal = {
visible: true,
title: 'Delete Service',
body: `Are you sure you want to delete service ${s.name}? This will also delete all failures, checkins, and incidents for this service.`,
btnColor: 'btn-danger',
btnText: 'Delete Service',
func: () => this.delete(s),
};
this.$store.commit('setModal', modal);
},
serviceGroup (s) {
const group = this.$store.getters.groupById(s.group_id);
if (group) {
return group.name;
visible: true,
title: "Delete Service",
body: `Are you sure you want to delete service ${s.name}? This will also delete all failures, checkins, and incidents for this service.`,
btnColor: "btn-danger",
btnText: "Delete Service",
func: () => this.delete(s),
}
return '';
},
async update () {
const services = await Api.services();
this.$store.commit('setServices', services);
}
}
};
this.$store.commit("setModal", modal)
},
serviceGroup(s) {
let group = this.$store.getters.groupById(s.group_id)
if (group) {
return group.name
}
return ""
},
async update() {
const services = await Api.services()
this.$store.commit('setServices', services)
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
</style>

View File

@ -1,198 +1,129 @@
<template>
<div>
<form
autocomplete="on"
@submit.prevent="login"
>
<div class="form-group row">
<label
for="username"
class="col-4 col-form-label"
>
{{ $t('username') }}
</label>
<div class="col-8">
<input
id="username"
v-model="username"
type="text"
autocomplete="username"
name="username"
class="form-control"
placeholder="admin"
autocorrect="off"
autocapitalize="none"
@keyup="checkForm"
@change="checkForm"
>
<div>
<form @submit.prevent="login" autocomplete="on">
<div class="form-group row">
<label for="username" class="col-4 col-form-label">{{$t('username')}}</label>
<div class="col-8">
<input @keyup="checkForm" @change="checkForm" type="text" v-model="username" autocomplete="username" name="username" class="form-control" id="username" placeholder="admin" autocorrect="off" autocapitalize="none">
</div>
</div>
</div>
<div class="form-group row">
<label
for="password"
class="col-4 col-form-label"
>
{{ $t('password') }}
</label>
<div class="col-8">
<input
id="password"
v-model="password"
type="password"
autocomplete="current-password"
name="password"
class="form-control"
placeholder="************"
@keyup="checkForm"
@change="checkForm"
>
<div class="form-group row">
<label for="password" class="col-4 col-form-label">{{$t('password')}}</label>
<div class="col-8">
<input @keyup="checkForm" @change="checkForm" type="password" v-model="password" autocomplete="current-password" name="password" class="form-control" id="password" placeholder="************">
</div>
</div>
</div>
<div class="form-group row">
<div class="col-sm-12">
<div
v-if="error"
class="alert alert-danger"
role="alert"
>
{{ $t('wrong_login') }}
</div>
<button
type="submit"
class="btn btn-block btn-primary"
:disabled="disabled || loading"
@click.prevent="login"
>
<FontAwesomeIcon
v-if="loading"
icon="circle-notch"
class="mr-2"
spin
/>{{ loading ? $t('loading') : $t('sign_in') }}
</button>
<div class="form-group row">
<div class="col-sm-12">
<div v-if="error" class="alert alert-danger" role="alert">
{{$t('wrong_login')}}
</div>
<button @click.prevent="login" type="submit" class="btn btn-block btn-primary" :disabled="disabled || loading">
<font-awesome-icon v-if="loading" icon="circle-notch" class="mr-2" spin/>{{loading ? $t('loading') : $t('sign_in')}}
</button>
</div>
</div>
</div>
</form>
<a
v-if="oauth && oauth.gh_client_id"
href="#"
class="mt-4 btn btn-block btn-outline-dark"
@click.prevent="GHlogin"
>
<FontAwesomeIcon :icon="['fab', 'github']" /> Login with Github
</a>
<a v-if="oauth && oauth.gh_client_id" @click.prevent="GHlogin" href="#" class="mt-4 btn btn-block btn-outline-dark">
<font-awesome-icon :icon="['fab', 'github']" /> Login with Github
</a>
<a
v-if="oauth && oauth.slack_client_id"
href="#"
class="btn btn-block btn-outline-dark"
@click.prevent="Slacklogin"
>
<FontAwesomeIcon :icon="['fab', 'slack']" /> Login with Slack
</a>
<a v-if="oauth && oauth.slack_client_id" @click.prevent="Slacklogin" href="#" class="btn btn-block btn-outline-dark">
<font-awesome-icon :icon="['fab', 'slack']" /> Login with Slack
</a>
<a
v-if="oauth && oauth.google_client_id"
href="#"
class="btn btn-block btn-outline-dark"
@click.prevent="Googlelogin"
>
<FontAwesomeIcon :icon="['fab', 'google']" /> Login with Google
</a>
<a v-if="oauth && oauth.google_client_id" @click.prevent="Googlelogin" href="#" class="btn btn-block btn-outline-dark">
<font-awesome-icon :icon="['fab', 'google']" /> Login with Google
</a>
<a
v-if="oauth && oauth.custom_client_id"
href="#"
class="btn btn-block btn-outline-dark"
@click.prevent="Customlogin"
>
<FontAwesomeIcon :icon="['fas', 'address-card']" /> Login with {{ oauth.custom_name }}
</a>
</div>
<a v-if="oauth && oauth.custom_client_id" @click.prevent="Customlogin" href="#" class="btn btn-block btn-outline-dark">
<font-awesome-icon :icon="['fas', 'address-card']" /> Login with {{oauth.custom_name}}
</a>
</div>
</template>
<script>
import Api from '../API';
export default {
name: 'FormLogin',
data () {
return {
username: '',
password: '',
import Api from "../API";
export default {
name: 'FormLogin',
computed: {
core() {
return this.$store.getters.core
},
oauth() {
return this.$store.getters.oauth
}
},
data() {
return {
username: "",
password: "",
auth: {},
loading: false,
error: false,
disabled: true,
google_scope: 'https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email',
slack_scope: 'identity.email,identity.basic'
};
},
computed: {
core () {
return this.$store.getters.core;
},
oauth () {
return this.$store.getters.oauth;
}
},
mounted () {
this.$cookies.remove('statping_auth');
google_scope: "https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email",
slack_scope: "identity.email,identity.basic"
}
},
mounted() {
this.$cookies.remove("statping_auth")
},
methods: {
checkForm () {
if (!this.username || !this.password) {
this.disabled = true;
} else {
this.disabled = false;
}
},
async login () {
this.loading = true;
this.error = false;
const auth = await Api.login(this.username, this.password);
if (auth.error) {
this.error = true;
} else if (auth.token) {
this.$cookies.set('statping_auth', auth.token);
await this.$store.dispatch('loadAdmin');
this.$store.commit('setAdmin', auth.admin);
this.$store.commit('setLoggedIn', true);
this.$router.push('/dashboard');
}
this.loading = false;
},
encode (val) {
return encodeURI(val);
},
custom_scopes () {
const scopes = [];
if (this.oauth.custom_open_id) {
scopes.push('openid');
}
scopes.push(this.oauth.custom_scopes.split(','));
if (scopes.length !== 0) {
return '&scopes='+scopes.join(',');
}
return '';
},
GHlogin () {
window.location = `https://github.com/login/oauth/authorize?client_id=${this.oauth.gh_client_id}&redirect_uri=${this.encode(this.core.domain+'/oauth/github')}&scope=read:user,read:org`;
},
Slacklogin () {
window.location = `https://slack.com/oauth/authorize?client_id=${this.oauth.slack_client_id}&redirect_uri=${this.encode(this.core.domain+'/oauth/slack')}&scope=identity.basic`;
},
Googlelogin () {
window.location = `https://accounts.google.com/signin/oauth?client_id=${this.oauth.google_client_id}&redirect_uri=${this.encode(this.core.domain+'/oauth/google')}&response_type=code&scope=https://www.googleapis.com/auth/userinfo.profile+https://www.googleapis.com/auth/userinfo.email`;
},
Customlogin () {
window.location = `${this.oauth.custom_endpoint_auth}?client_id=${this.oauth.custom_client_id}&redirect_uri=${this.encode(this.core.domain+'/oauth/custom')}&response_type=code${this.custom_scopes()}`;
checkForm() {
if (!this.username || !this.password) {
this.disabled = true
} else {
this.disabled = false
}
},
async login() {
this.loading = true
this.error = false
const auth = await Api.login(this.username, this.password)
if (auth.error) {
this.error = true
} else if (auth.token) {
this.$cookies.set("statping_auth", auth.token)
await this.$store.dispatch('loadAdmin')
this.$store.commit('setAdmin', auth.admin)
this.$store.commit('setLoggedIn', true)
this.$router.push('/dashboard')
}
this.loading = false
},
encode(val) {
return encodeURI(val)
},
custom_scopes() {
let scopes = []
if (this.oauth.custom_open_id) {
scopes.push("openid")
}
}
};
scopes.push(this.oauth.custom_scopes.split(","))
if (scopes.length !== 0) {
return "&scopes="+scopes.join(",")
}
return ""
},
GHlogin() {
window.location = `https://github.com/login/oauth/authorize?client_id=${this.oauth.gh_client_id}&redirect_uri=${this.encode(this.core.domain+"/oauth/github")}&scope=read:user,read:org`
},
Slacklogin() {
window.location = `https://slack.com/oauth/authorize?client_id=${this.oauth.slack_client_id}&redirect_uri=${this.encode(this.core.domain+"/oauth/slack")}&scope=identity.basic`
},
Googlelogin() {
window.location = `https://accounts.google.com/signin/oauth?client_id=${this.oauth.google_client_id}&redirect_uri=${this.encode(this.core.domain+"/oauth/google")}&response_type=code&scope=https://www.googleapis.com/auth/userinfo.profile+https://www.googleapis.com/auth/userinfo.email`
},
Customlogin() {
window.location = `${this.oauth.custom_endpoint_auth}?client_id=${this.oauth.custom_client_id}&redirect_uri=${this.encode(this.core.domain+"/oauth/custom")}&response_type=code${this.custom_scopes()}`
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
</style>

View File

@ -1,19 +1,19 @@
import Vue from 'vue';
import VueRouter from 'vue-router';
import VueApexCharts from 'vue-apexcharts';
import VueObserveVisibility from 'vue-observe-visibility';
import VueClipboard from 'vue-clipboard2';
import VueCookies from 'vue-cookies';
import VueI18n from 'vue-i18n';
import router from './routes';
import './mixin';
import './icons';
import store from './store';
import language from './languages';
import Vue from 'vue'
import VueRouter from 'vue-router'
import VueApexCharts from 'vue-apexcharts'
import VueObserveVisibility from 'vue-observe-visibility'
import VueClipboard from 'vue-clipboard2'
import VueCookies from 'vue-cookies'
import VueI18n from 'vue-i18n'
import router from './routes'
import "./mixin"
import "./icons"
import store from './store'
import language from './languages'
const App = () => import(/* webpackChunkName: "index" */ '@/App.vue');
const App = () => import(/* webpackChunkName: "index" */ '@/App.vue')
Vue.component('apexchart', VueApexCharts);
Vue.component('apexchart', VueApexCharts)
Vue.use(VueClipboard);
Vue.use(VueRouter);
@ -22,19 +22,19 @@ Vue.use(VueCookies);
Vue.use(VueI18n);
const i18n = new VueI18n({
fallbackLocale: 'en',
messages: language
fallbackLocale: "en",
messages: language
});
Vue.$cookies.config('3d');
Vue.$cookies.config('3d')
Vue.config.productionTip = process.env.NODE_ENV !== 'production';
Vue.config.devtools = process.env.NODE_ENV !== 'production';
Vue.config.performance = process.env.NODE_ENV !== 'production';
Vue.config.productionTip = process.env.NODE_ENV !== 'production'
Vue.config.devtools = process.env.NODE_ENV !== 'production'
Vue.config.performance = process.env.NODE_ENV !== 'production'
new Vue({
router,
store,
i18n,
render: h => h(App),
}).$mount('#app');
router,
store,
i18n,
render: h => h(App),
}).$mount('#app')