Started import work

This commit is contained in:
Spitap
2023-04-18 15:44:08 +02:00
committed by Antoine Nguyen
parent 844c1e482e
commit 415aefc764
66 changed files with 3802 additions and 8 deletions
@@ -0,0 +1,15 @@
import repository from '@/api/repository'
const resource = 'migrations'
export default {
getMigrations (query) {
return repository.get(`/${resource}/`, query)
},
getMigration (slug, query) {
return repository.get(`/${resource}/${slug}/`, query)
},
deleteMigration (slug) {
return repository.delete(`/${resource}/${slug}/`)
}
}
@@ -0,0 +1,27 @@
import repository from '@/api/repository'
const resource = 'email-providers'
export default {
getProviders (params) {
return repository.get(`/${resource}/`, { params })
},
getProvider (providerId) {
return repository.get(`/${resource}/${providerId}/`)
},
createProvider (params) {
return repository.post(`/${resource}/`, params)
},
updateProvider (slug, params) {
return repository.put(`/${resource}/${slug}/`, params)
},
patchProvider (providerId, params) {
return repository.patch(`/${resource}/${providerId}/`, params)
},
deleteProvider (slug) {
return repository.delete(`/${resource}/${slug}/`)
},
checkProvider (params) {
return repository.post(`/${resource}/check_connection/`, params)
}
}
@@ -0,0 +1,118 @@
<template>
<v-card class="mt-6">
<v-toolbar flat>
<v-menu offset-y>
<template v-slot:activator="{ on, attrs }">
<v-btn v-bind="attrs" v-on="on" small>
Actions <v-icon right>mdi-chevron-down</v-icon>
</v-btn>
</template>
<v-list dense>
</v-list>
</v-menu>
<v-spacer></v-spacer>
<v-text-field
v-model="search"
prepend-inner-icon="mdi-magnify"
placeholder="Search"
filled
outlined
dense
hide-details
></v-text-field>
</v-toolbar>
<v-data-table
v-model="selected"
:headers="headers"
:items="migrations"
:search="search"
item-key="pk"
:options.sync="options"
:server-items-length="totalMigrations"
:loading="loading"
class="elevation-1"
show-select
>
<template v-slot:item.actions="{ item }">
<v-menu offset-y>
<template v-slot:activator="{ on, attrs }">
<v-btn icon v-bind="attrs" v-on="on">
<v-icon>mdi-dots-horizontal</v-icon>
</v-btn>
</template>
<menu-items :items="getMenuItems(item)" :object="item" />
</v-menu>
</template>
</v-data-table>
</v-card>
</template>
<script>
import Migration from '@/api/imap_migration/migrations'
import MenuItems from '@/components/tools/MenuItems'
import { bus } from '@/main'
export default {
components: {
MenuItems
},
data () {
return {
migrations: [],
headers: [
{ text: this.$gettext('Provider'), value: 'provider' },
{ text: this.$gettext('Old account'), value: 'old_account' },
{ text: this.$gettext('New account'), value: 'new_account' },
{ text: this.$gettext('Actions'), value: 'actions', sortable: false, align: 'right' }
],
selected: [],
loading: true,
search: '',
options: {},
totalMigrations: 0
}
},
methods: {
deleteMigration (migration) {
this.$dialog.confirm(
this.$gettext('Remove this migration?'), {
cancelText: this.$gettext('Cancel'),
okText: this.$gettext('Proceed')
}
).then(dialog => {
Migration.deleteMigration(migration.id).then(response => {
bus.$emit('notification', { msg: this.$gettext('Provider deleted') })
this.migrations = this.migrations.filter(item => {
return item.id !== migration.id
})
})
})
},
getMigrations (filter) {
this.loading = true
let query = {}
if (filter !== undefined) {
query = { params: { search: filter } }
}
Migration.getMigrations(query).then(response => {
this.migrations = response.data
this.loading = false
})
},
getMenuItems (item) {
const result = []
result.push({
label: this.$gettext('Delete'),
icon: 'mdi-delete-outline',
onClick: this.deleteMigration,
color: 'red'
})
return result
}
},
mounted () {
this.getMigrations()
}
}
</script>
@@ -0,0 +1,117 @@
<template>
<validation-observer ref="observer">
<validation-provider
v-slot="{ errors }"
rules="required"
vid="initialdomain"
>
<v-text-field
ref="initialdomainField"
v-model="domain.initialdomain"
:error-messages="errors"
:placeholder="'provider.domain' | translate"
outlined
dense
@input="update"
@keydown="onKeyDown"
/>
</validation-provider>
<validation-provider
v-slot="{ errors }"
rules="required"
vid="newd_omain"
>
<v-text-field
ref="newdomainField"
v-model="domain.new_domain"
:error-messages="errors"
:placeholder="'local.domain' | translate"
outlined
dense
@input="update"
@keydown="onKeyDown"
/>
</validation-provider>
<v-btn
color="primary"
@click="addDomain"
>
<v-icon>mdi-plus</v-icon>
</v-btn>
<v-spacer />
<v-chip
v-for="(domain, index) in domains"
:key="index"
class="mr-2 mt-2"
close
@click:close="removeDomain(index)"
>
<template v-if="domain.new_domain">
{{domain.name}} --> {{domain.new_domain}}
</template>
<template v-else>
{{domain.name}} --> {{domain.name}}
</template>
</v-chip>
</validation-observer>
</template>
<script>
export default {
props: ['value'],
data () {
return {
domains: [],
domain: {
initialdomain: '',
new_domain: ''
}
}
},
methods: {
async validateForm () {
return await this.$refs.observer.validate()
},
update () {
this.$emit('input', this.domains)
},
async addDomain () {
if (await this.validateForm()) {
this.domains.push({
name: this.domain.initialdomain,
new_domain: this.domain.new_domain
})
this.$emit('input', this.domains)
this.$refs.initialdomainField.reset()
this.$refs.newdomainField.reset()
}
},
removeDomain (index) {
this.domains.splice(index, 1)
this.$emit('input', this.domains)
},
onKeyDown (e) {
const keyCode = e.keyCode
if (keyCode === 13 || keyCode === 9) {
// on enter or tab
this.addDomain()
e.preventDefault()
}
}
},
watch: {
value: {
handler: function (newValue) {
if (newValue) {
this.domains = [...newValue]
} else {
this.domains = []
}
},
immediate: true
}
}
}
</script>
@@ -0,0 +1,118 @@
<template>
<creation-form
:title="'New provider'|translate"
:steps="steps"
:form-observer-getter="getObserver"
:validate-object="validateProvider"
:summary-sections="summarySections"
@close="close"
@create="submit"
>
<template v-slot:form.general="{ step }">
<provider-general-form :ref="`form_${step}`" v-model="provider" />
</template>
<template v-slot:form.associated="{ step }">
<provider-associated-form :ref="`form_${step}`" v-model="provider.domains" />
</template>
</creation-form>
</template>
<script>
import { bus } from '@/main'
import CreationForm from '@/components/tools/CreationForm'
import ProviderAssociatedForm from './ProviderAssociatedForm'
import ProviderGeneralForm from './ProviderGeneralForm'
export default {
props: ['value'],
components: {
CreationForm,
'provider-associated-form': ProviderAssociatedForm,
ProviderGeneralForm
},
computed: {
formatAssociatedDomains () {
let associatedDomains = ''
for (const domain of this.provider.domains) {
associatedDomains += `${domain.name} => ${domain.new_domain}, `
}
return associatedDomains
},
summarySections () {
const result = [
{
title: this.$gettext('General'),
items: [
{ key: this.$gettext('Name'), value: this.provider.name },
{ key: this.$gettext('Address'), value: this.provider.address },
{ key: this.$gettext('Port'), value: this.provider.port },
{ key: this.$gettext('Secured'), value: this.provider.secured, type: 'yesno' }
]
},
{
title: this.$gettext('Associated domains'),
items: [
{ key: this.$gettext('Perform a connection check'), value: this.formatAssociatedDomains }
]
}
]
return result
},
steps () {
return [
{ name: 'general', title: this.$gettext('General') },
{ name: 'associated', title: this.$gettext('Associated domains') }
]
}
},
data () {
return {
provider: {
domains: []
},
formErrors: {}
}
},
mounted () {
this.initProvider()
},
methods: {
initProvider () {
this.provider = {
name: '',
address: '',
port: 993,
secured: true,
domains: []
}
},
close () {
this.initProvider()
this.$emit('close')
},
getObserver (step) {
return this.$refs[`form_${step}`].$refs.observer
},
validateProvider () {
},
performConnectionCheck () {
},
submit () {
const data = JSON.parse(JSON.stringify(this.provider))
console.log(this.provider)
this.$store.dispatch('providers/createProvider', data).then(resp => {
bus.$emit('notification', { msg: this.$gettext('Provider created') })
this.close()
})
}
}
}
</script>
<style lang="scss">
.highligth {
background-color: #515D78;
}
</style>
@@ -0,0 +1,138 @@
<template>
<div>
<v-expansion-panels v-model="panel">
<v-expansion-panel>
<v-expansion-panel-header v-slot="{ open }">
<v-row no-gutters>
<v-col cols="4">
<translate>General</translate>
</v-col>
<v-col
cols="8"
class="text--secondary"
>
<v-fade-transition leave-absolute>
<span v-if="open"></span>
<v-row
v-else
no-gutters
style="width: 100%"
>
<v-col cols="6">
<translate>Name: </translate> {{ editedProvider.name }}
</v-col>
</v-row>
</v-fade-transition>
</v-col>
</v-row>
</v-expansion-panel-header>
<v-expansion-panel-content>
<provider-general-form ref="generalForm" v-model="editedProvider" />
</v-expansion-panel-content>
</v-expansion-panel>
<v-expansion-panel>
<v-expansion-panel-header v-slot="{ open }">
<v-row no-gutters>
<v-col cols="4">
<translate>Associated domains</translate>
</v-col>
<v-col
cols="8"
class="text--secondary"
>
<v-fade-transition leave-absolute>
<span v-if="open"></span>
<v-row
v-else
no-gutters
style="width: 100%"
>
<v-col v-if="editedProvider.domains.length > 0" cols="6">
<translate>Number of associated domains: </translate> {{ editedProvider.domains.length }}
</v-col>
<v-col v-else cols="6">
<translate>No associated domains</translate>
</v-col>
</v-row>
</v-fade-transition>
</v-col>
</v-row>
</v-expansion-panel-header>
<v-expansion-panel-content>
<provider-associated-form ref="associatedForm" v-model="editedProvider.domains" />
</v-expansion-panel-content>
</v-expansion-panel>
</v-expansion-panels>
<div class="mt-4 d-flex justify-end">
<v-btn @click="$router.go(-1)">
<translate>Cancel</translate>
</v-btn>
<v-btn class="ml-4" color="primary darken-1" @click="save">
<translate>Save</translate>
</v-btn>
</div>
</div>
</template>
<script>
import { bus } from '@/main'
import providers from '@/api/imap_migration/providers'
import ProviderGeneralForm from './ProviderGeneralForm'
import ProviderAssociatedForm from './ProviderAssociatedForm'
export default {
components: {
ProviderGeneralForm,
ProviderAssociatedForm
},
props: ['provider'],
data () {
return {
editedProvider: {},
panel: 0
}
},
methods: {
async save () {
if (this.$refs.generalForm !== undefined) {
const valid = await this.$refs.generalForm.validateForm()
if (!valid) {
return
}
}
if (this.$refs.associatedForm !== undefined) {
const valid = await this.$refs.associatedForm.validateForm()
if (!valid) {
return
}
}
try {
const data = JSON.parse(JSON.stringify(this.editedProvider))
await providers.patch(this.editedProvider.id, data).then(resp => {
bus.$emit('notification', { msg: this.$gettext('Provider updated') })
})
} catch (error) {
if (this.$refs.generalForm) {
this.$refs.generalForm.$refs.observer.setErrors(error.response.data)
}
if (this.$refs.associatedForm) {
this.$refs.associatedForm.$refs.observer.setErrors(error.response.data)
}
}
}
},
watch: {
provider: {
handler: function (val) {
if (val) {
this.editedProvider = { ...val }
if (!this.editedProvider.domains) {
this.editedProvider.domains = []
}
}
},
immediate: true
}
}
}
</script>
@@ -0,0 +1,167 @@
<template>
<validation-observer ref="observer">
<validation-provider
v-slot="{ errors }"
rules="required"
>
<v-text-field
:label="'Provider name' | translate"
v-model="form.name"
:error-messages="errors"
outlined
@input="update"
/>
</validation-provider>
<validation-provider
v-slot="{ errors }"
rules="required"
>
<v-text-field
:label="'Address (hostname or IP)' | translate"
v-model="form.address"
:error-messages="errors"
outlined
@input="update"
/>
</validation-provider>
<validation-provider
v-slot="{ errors }"
rules="required|portNumber"
>
<v-text-field
:label="'IMAP Port' | translate"
v-model="form.port"
type="number"
:error-messages="errors"
outlined
@input="update"
/>
</validation-provider>
<v-switch
:label="'Secured' | translate"
v-model="form.secured"
:hint="'Control if the IMAP is secured through SSL/TLS or Start SSL' | translate"
persistent-hint
@change="update"
/>
<div class="my-7">
<v-btn
:loading="loading"
color="primary"
@click="checkConnection"
>
<translate>Check connection: </translate>
<v-icon
v-if="connectionStatus === 0"
color="blue-grey-darken-2"
>
mdi-help-circle-outline
</v-icon>
<v-icon
v-else-if="connectionStatus === 1"
color="success"
>
mdi-check-all
</v-icon>
<v-icon
v-else
color="warning"
>
mdi-alert-circle-outline
</v-icon>
<template v-slot:loader>
<span class="custom-loader">
<v-icon light>mdi-cached</v-icon>
</span>
</template>
</v-btn>
</div>
</validation-observer>
</template>
<script>
import Provider from '@/api/imap_migration/providers'
export default {
props: ['value'],
data () {
return {
form: {},
connectionStatus: 0,
loading: false
}
},
methods: {
async checkConnection () {
if (await this.validateForm()) {
this.loading = true
const params = {
address: this.form.address,
port: this.form.port,
secured: this.form.secured
}
console.error(params)
Provider.checkProvider(params).then(response => {
this.connectionStatus = 1
this.loading = false
}).catch(error => {
console.log(error)
this.loading = false
this.connectionStatus = 2
})
}
},
async validateForm () {
return await this.$refs.observer.validate()
},
update () {
this.connectionStatus = 0
console.log(this.form)
this.$emit('input', this.form)
}
},
mounted () {
this.form = { ...this.value }
}
}
</script>
<style>
.custom-loader {
animation: loader 1s infinite;
display: flex;
}
@-moz-keyframes loader {
from {
transform: rotate(0);
}
to {
transform: rotate(360deg);
}
}
@-webkit-keyframes loader {
from {
transform: rotate(0);
}
to {
transform: rotate(360deg);
}
}
@-o-keyframes loader {
from {
transform: rotate(0);
}
to {
transform: rotate(360deg);
}
}
@keyframes loader {
from {
transform: rotate(0);
}
to {
transform: rotate(360deg);
}
}
</style>
@@ -0,0 +1,154 @@
<template>
<v-card class="mt-6">
<v-toolbar flat>
<v-menu offset-y>
<template v-slot:activator="{ on, attrs }">
<v-btn v-bind="attrs" v-on="on" small>
Actions <v-icon right>mdi-chevron-down</v-icon>
</v-btn>
</template>
<v-list dense>
</v-list>
</v-menu>
<v-spacer></v-spacer>
<v-text-field
v-model="search"
prepend-inner-icon="mdi-magnify"
placeholder="Search"
filled
outlined
dense
hide-details
></v-text-field>
</v-toolbar>
<v-data-table
v-model="selected"
:headers="headers"
:items="providers"
:search="search"
item-key="name"
single-expand
:expanded.sync="expanded"
class="elevation-1"
show-select
@click:row="(item, slot) => slot.expand(!slot.isExpanded)"
>
<template v-slot:item.secured="{ item }">
<v-icon
v-if="item.secured === true"
color="green"
>
mdi-lock
</v-icon>
<v-icon
color="red"
v-else
>
mdi-lock-off
</v-icon>
</template>
<template v-slot:item.domains="{ item }">
<v-chip
v-if="item.domains.length > 0"
>
{{item.domains.length}} associated domain(s)
</v-chip>
<v-chip
v-else
>
No associated domain
</v-chip>
</template>
<template v-slot:expanded-item="{ headers, item }">
<td :colspan="headers.length">
<v-chip
v-for="(domain, index) in item.domains"
:key="index"
>
<template v-if="domain.new_domain">
{{domain.name}} --> {{domain.new_domain}}
</template>
<template v-else>
{{domain.name}} --> {{domain.name}}
</template>
</v-chip>
</td>
</template>
<template v-slot:item.actions="{ item }">
<v-menu offset-y>
<template v-slot:activator="{ on, attrs }">
<v-btn icon v-bind="attrs" v-on="on">
<v-icon>mdi-dots-horizontal</v-icon>
</v-btn>
</template>
<menu-items :items="getMenuItems(item)" :object="item" />
</v-menu>
</template>
</v-data-table>
</v-card>
</template>
<script>
import { mapGetters } from 'vuex'
import { bus } from '@/main'
import MenuItems from '@/components/tools/MenuItems'
export default {
components: {
MenuItems
},
computed: mapGetters({
providers: 'providers/providers'
}),
data () {
return {
headers: [
{ text: this.$gettext('Name'), value: 'name' },
{ text: this.$gettext('Address'), value: 'address' },
{ text: this.$gettext('Port'), value: 'port' },
{ text: this.$gettext('Secured'), value: 'secured' },
{ text: this.$gettext('Associated domains'), value: 'domains' },
{ text: this.$gettext('Actions'), value: 'actions', sortable: false, align: 'right' }
],
selected: [],
search: '',
expanded: []
}
},
methods: {
deleteProvider (provider) {
this.$store.dispatch('providers/deleteProvider', { id: provider.id }).then(resp => {
bus.$emit('notification', { msg: this.$gettext('Provider deleted') })
})
},
editProvider (provider) {
this.$router.push({ name: 'ProviderEdit', params: { id: provider.id } })
},
loadAssociatedDomains ({ item, value }) {
if (!value) {
return
}
this.$set(this.item.domains)
},
getMenuItems (item) {
const result = []
result.push({
label: this.$gettext('Delete'),
icon: 'mdi-delete-outline',
onClick: this.deleteProvider,
color: 'red'
})
result.push({
label: this.$gettext('Edit'),
icon: 'mdi-circle-edit-outline',
onClick: this.editProvider
})
return result
}
},
created () {
this.$store.dispatch('providers/getProviders')
}
}
</script>
+18 -1
View File
@@ -168,6 +168,23 @@ export default {
icon: 'mdi-domain',
roles: ['DomainAdmins', 'Resellers', 'SuperAdmins']
},
{
icon: 'mdi-email-sync-outline',
text: this.$gettext('Migrations'),
roles: ['SuperAdmins', 'Resellers'],
children: [
{
text: this.$gettext('Email providers'),
to: { name: 'ProvdiersList' },
roles: ['SuperAdmins', 'Resellers']
},
{
text: this.$gettext('Migrations'),
to: { name: 'MigrationsList' },
roles: ['Resellers', 'SuperAdmins']
}
]
},
{
text: this.$gettext('Identities'),
to: { name: 'Identities' },
@@ -261,7 +278,7 @@ export default {
created () {
parameters.getApplications().then(response => {
response.data.forEach(item => {
this.mainMenuItems[5].children.push({
this.mainMenuItems[6].children.push({
text: item.label,
to: { name: 'ParametersEdit', params: { app: item.name } }
})
@@ -0,0 +1,174 @@
<template>
<div>
<v-text-field
ref="input"
v-model="input"
outlined
dense
v-bind="$attrs"
v-on="$listeners"
@input="update"
@keydown="onKeyDown"
/>
<div class="menu"
v-if="showMenu"
:style="`width: ${width}px;`"
>
<v-list class="list">
<v-list-item
v-for="(domain, index) in filteredDomains"
:key="index"
@click="selectDomain(domain)"
:class="selectionIndex === index ? 'selected' : ''"
>
<v-list-item-title>{{ domain.name }}</v-list-item-title>
</v-list-item>
</v-list>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
props: {
value: String,
allowAdd: {
type: Boolean,
default: false
}
},
computed: {
...mapGetters({
domains: 'identities/domains'
}),
filteredDomains () {
return this.domains.filter(domain => domain.name.startsWith(this.input))
}
},
data () {
return {
input: this.value,
selectionIndex: 0,
showMenu: false,
width: 500
}
},
methods: {
onKeyDown (e) {
const keyCode = e.keyCode
if (keyCode === 40 || keyCode === 34) {
// on arrow down or page down
if (!this.showMenu) {
this.showMenu = true
} else {
this.increaseSelectionIndex()
}
e.preventDefault()
} else if (keyCode === 38 || keyCode === 33) {
// on arrow up or page up
if (!this.showMenu) {
this.showMenu = true
} else {
this.decreaseSelectionIndex()
}
e.preventDefault()
} else if (keyCode === 13 || keyCode === 9) {
// on enter or tab
if (this.filteredDomains.length > 0) {
this.selectDomain(this.filteredDomains[this.selectionIndex])
} else if (this.allowAdd) {
this.selectDomain()
}
e.preventDefault()
} else if (keyCode === 27) {
// on escape
if (this.filteredDomains.length > 0) {
this.showMenu = false
e.preventDefault()
}
}
},
increaseSelectionIndex () {
if (this.selectionIndex >= this.filteredDomains.length - 1) {
this.selectionIndex = 0
} else {
this.selectionIndex += 1
}
},
decreaseSelectionIndex () {
if (this.selectionIndex <= 0) {
this.selectionIndex = this.filteredDomains.length - 1
} else {
this.selectionIndex -= 1
}
},
selectDomain (domain) {
if (domain !== undefined) {
this.input = domain.name
}
this.showMenu = false
this.$emit('input', this.input)
this.$emit('domain-selected')
},
update (value) {
if (this.allowAdd) {
this.showMenu = this.filteredDomains.length > 0
} else {
this.showMenu = true
}
this.$emit('input', this.input)
},
onResize () {
this.updateMinWidthProperty()
},
updateMinWidthProperty () {
this.width = this.$refs.input.$el.clientWidth
},
reset () {
this.input = ''
}
},
mounted () {
// window.addEventListener('resize', this.onResize)
},
updated () {
this.$nextTick(() => {
this.updateMinWidthProperty()
})
},
watch: {
value (newValue) {
this.input = newValue
}
}
}
</script>
<style scoped>
.menu {
display: inline-block;
position: fixed;
margin-top: -20px;
padding: 5px 0;
box-shadow: 0 1px 5px 1px rgba(0,0,0,0.3);
overflow-y: auto;
overflow-x: hidden;
contain: content;
z-index: 99;
}
.list {
width: 100%;
height: 100%;
font-size: 1.1em;
overflow-x: hidden;
padding: 0;
}
.selected {
background-color: cornsilk;
}
</style>
+9
View File
@@ -36,6 +36,15 @@ extend('validPassword', {
},
message: $gettext('Invalid password')
})
extend('portNumber', {
validate: async (value) => {
if (value < 0 || value > 65535) {
return false
}
return true
},
message: $gettext('Specify a valid port number')
})
// Register components globally
Vue.component('ValidationObserver', ValidationObserver)
+27
View File
@@ -56,6 +56,33 @@ const routes = [
allowedRoles: ['DomainAdmins', 'Resellers', 'SuperAdmins']
}
},
{
path: '/imap_migration/migrations',
name: 'MigrationsList',
component: () => import('../components/imap_migration/MigrationsList.vue'),
meta: {
requiresAuth: true,
allowedRoles: ['Resellers', 'SuperAdmins']
}
},
{
path: '/imap_migration/providers',
name: 'ProvdiersList',
component: () => import('../views/imap_migration/Providers.vue'),
meta: {
requiresAuth: true,
allowedRoles: ['Resellers', 'SuperAdmins']
}
},
{
path: '/imap_migration/providers/:id/edit',
name: 'ProviderEdit',
component: () => import('../views/imap_migration/ProviderEdit.vue'),
meta: {
requiresAuth: true,
allowedRoles: ['Resellers', 'SuperAdmins']
}
},
{
path: '/identities',
name: 'Identities',
+3 -1
View File
@@ -4,6 +4,7 @@ import Vuex from 'vuex'
import auth from '@/store/modules/auth'
import domains from '@/store/modules/domains'
import identities from '@/store/modules/identities'
import providers from '@/store/modules/providers'
Vue.use(Vuex)
@@ -11,7 +12,8 @@ export default new Vuex.Store({
modules: {
auth,
domains,
identities
identities,
providers
},
strict: process.env.NODE_ENV !== 'production'
})
+73
View File
@@ -0,0 +1,73 @@
import Vue from 'vue'
import providerApi from '@/api/imap_migration/providers'
// mutation types
export const ADD_PROVIDER = 'ADD_PROVIDER'
export const UPDATE_PROVIDER = 'SET_PROVIDER'
export const SET_PROVIDERS = 'SET_PROVIDERS'
export const DELETE_PROVIDER = 'DELETE_PROVIDER'
const state = {
providersLoaded: false,
providers: []
}
const getters = {
providers: state => state.providers
}
const actions = {
getProviders ({ commit }, params) {
return providerApi.getProviders(params).then(response => {
commit(SET_PROVIDERS, { providers: response.data })
})
},
createProvider ({ commit }, data) {
return new Promise((resolve, reject) => {
providerApi.createProvider(data).then(response => {
commit(ADD_PROVIDER, { provider: response.data })
resolve(response)
}).catch(error => {
reject(error)
})
})
},
updateProvider ({ commit }, data) {
return providerApi.updateProvider(data.id, data).then(response => {
commit(UPDATE_PROVIDER, { provider: response.data })
})
},
deleteProvider ({ commit }, data) {
return providerApi.deleteProvider(data.id).then(response => {
commit(DELETE_PROVIDER, data)
})
}
}
const mutations = {
[SET_PROVIDERS] (state, { providers }) {
state.providersLoaded = true
state.providers = providers
},
[ADD_PROVIDER] (state, { provider }) {
state.providers.push(provider)
},
[UPDATE_PROVIDER] (state, { provider }) {
state.providers.forEach(function (item, pos) {
if (item.pk === provider.pk) {
Vue.set(state.providers, pos, provider)
}
})
},
[DELETE_PROVIDER] (state, data) {
state.providers = state.providers.filter(item => item.id !== data.id)
}
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
@@ -0,0 +1,28 @@
<template>
<div>
<span class="text-h4"><translate>Edit provider</translate></span>
<div class="mt-4" />
<provider-form :provider="provider" />
</div>
</template>
<script>
import providers from '@/api/imap_migration/providers'
import ProviderForm from '@/components/imap_migration/ProviderForm'
export default {
components: {
ProviderForm
},
data () {
return {
provider: {}
}
},
mounted () {
providers.getProvider(this.$route.params.id).then(resp => {
this.provider = resp.data
})
}
}
</script>
@@ -0,0 +1,56 @@
<template>
<div>
<v-toolbar flat>
<v-toolbar-title><translate>Providers</translate></v-toolbar-title>
<v-spacer />
<v-btn
offset-y
color="primary"
@click="showProviderWizard = true"
>
<v-icon left>mdi-plus</v-icon>
<translate>New</translate>
</v-btn>
</v-toolbar>
<providers-list />
<v-dialog
v-model="showProviderWizard"
fullscreen
scrollable
transition="dialog-bottom-transition"
>
<provider-creation-form @close="showProviderWizard = false" />
<template v-slot:help>
<ul>
<li><em>domain; name; quota; default mailbox quota; enabled</em></li>
<li><em>domainalias; name; targeted domain; enabled</em></li>
</ul>
</template>
</v-dialog>
</div>
</template>
<script>
import ProviderCreationForm from '@/components/imap_migration/ProviderCreationForm'
import ProvidersList from '@/components/imap_migration/ProvidersList'
export default {
components: {
ProviderCreationForm,
ProvidersList
},
data () {
return {
showProviderWizard: false
}
}
}
</script>
<style scoped>
.v-toolbar {
background-color: #f7f8fa !important;
}
</style>