- Mail in a Box: comandi per aggiungere/modificare/rimuovere le caselle di posta
- aggiunto idMyGroup: ospiti siti di gruppi (AbitareGliIblei)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { defineComponent, onMounted, ref, watch, onUnmounted, nextTick } from 'vue'
|
||||
import { defineComponent, onMounted, ref, computed, watch, onUnmounted, nextTick } from 'vue'
|
||||
|
||||
import { CTitleBanner } from '../../../components/CTitleBanner'
|
||||
import { CDateTime } from '../../../components/CDateTime'
|
||||
@@ -68,11 +68,28 @@ export default defineComponent({
|
||||
const optionsApi = ref(<any>[])
|
||||
const arrZones = ref(<any>[])
|
||||
const arrDNS = ref(<any>[])
|
||||
const arrEmails = ref(<any>[])
|
||||
const optZones = ref(<any>[])
|
||||
|
||||
const selected = ref(<any>[])
|
||||
const selZoneId = ref('')
|
||||
|
||||
const showNewEmail = ref(false)
|
||||
const showchangePwd = ref(false)
|
||||
const newEmail = ref('')
|
||||
const newPassword = ref('')
|
||||
const changeEmail = ref('')
|
||||
const changePassword = ref('')
|
||||
const isPwd = ref(true)
|
||||
const emailSel = ref('')
|
||||
|
||||
const isFormValid = computed(() => {
|
||||
return newEmail.value && newEmail.value.includes('@') && newPassword.value.length >= 8
|
||||
})
|
||||
const isFormValidChange = computed(() => {
|
||||
return emailSel.value && emailSel.value.includes('@') && changePassword.value.length >= 8
|
||||
})
|
||||
|
||||
const columnsZones = [
|
||||
{ name: 'id', align: 'right', label: 'ID', field: 'id', visible: true },
|
||||
{ name: 'name', required: true, label: 'Nome', align: 'left', field: 'name', sortable: true, visible: true },
|
||||
@@ -86,22 +103,24 @@ export default defineComponent({
|
||||
{ name: 'name', required: true, label: 'Nome', align: 'left', field: 'name', sortable: true, visible: true },
|
||||
{ name: 'type', required: true, label: 'Tipo', align: 'left', field: 'type', sortable: true, visible: true },
|
||||
{ name: 'proxied', required: true, align: 'center', label: 'Proxy', field: 'proxied', sortable: true, visible: true },
|
||||
{ name: 'priority', required: true, align: 'center', label: 'Priorità', field: 'priority', sortable: true, visible: true },
|
||||
{ name: 'priority', required: true, align: 'center', label: 'Pri', field: 'priority', sortable: true, visible: true },
|
||||
{ name: 'ttl', required: true, align: 'center', label: 'TTL', field: 'ttl', sortable: false, visible: true },
|
||||
{ name: 'content', required: true, label: 'Valore', align: 'left', field: 'content', sortable: true, visible: true },
|
||||
{ name: 'delete', required: true, label: 'Azioni', align: 'left', field: 'delete', sortable: true, visible: true },
|
||||
];
|
||||
|
||||
const viscolumnsDNS = [
|
||||
{ name: 'name', required: true, label: 'Nome', align: 'left', field: 'name', sortable: true, visible: true, editable: true },
|
||||
{ name: 'type', required: true, label: 'Tipo', align: 'left', field: 'type', sortable: true, visible: true, editable: true },
|
||||
{ name: 'proxied', required: true, align: 'center', label: 'Proxy', field: 'proxied', sortable: true, visible: true, editable: true },
|
||||
{ name: 'priority', required: true, align: 'center', label: 'Priorità', field: 'priority', sortable: true, visible: true },
|
||||
{ name: 'priority', required: true, align: 'center', label: 'Pri', field: 'priority', sortable: true, visible: true },
|
||||
{ name: 'ttl', required: true, align: 'center', label: 'TTL', field: 'ttl', sortable: false, visible: true },
|
||||
{ name: 'content', required: true, label: 'Valore', align: 'left', field: 'content', sortable: true, visible: true, editable: true },
|
||||
{ name: 'delete', required: true, label: 'Azioni', align: 'left', field: 'delete', sortable: true, visible: true },
|
||||
];
|
||||
|
||||
const pagination = {
|
||||
rowsPerPage: 10,
|
||||
rowsPerPage: 20,
|
||||
}
|
||||
|
||||
watch(() => selectedApi.value, async (to: any, from: any) => {
|
||||
@@ -156,6 +175,76 @@ export default defineComponent({
|
||||
arrDNS.value = await globalStore.getCloudFlareTok("getDNS", selectedApi.value, selZoneId.value)
|
||||
}
|
||||
|
||||
async function getEmails() {
|
||||
arrEmails.value = []
|
||||
arrEmails.value = await globalStore.getMailInABox("getEmails", { domain: tools.getDomainSite() })
|
||||
}
|
||||
|
||||
async function addEmailServer(email: string, pwd: string) {
|
||||
|
||||
incaricamento.value = true
|
||||
|
||||
const ris = await globalStore.getMailInABox("addEmail", { domain: tools.getDomainSite(), email, pwd })
|
||||
|
||||
if (ris)
|
||||
tools.showPositiveNotif($q, ris)
|
||||
else {
|
||||
tools.showNegativeNotif($q, t('miab.err_addemail'))
|
||||
}
|
||||
|
||||
await checkDNSSite()
|
||||
|
||||
incaricamento.value = false
|
||||
|
||||
}
|
||||
|
||||
async function setMailUserPassword(email: string, pwd: string) {
|
||||
|
||||
incaricamento.value = true
|
||||
|
||||
const ris = await globalStore.getMailInABox("setMailUserPassword", { domain: tools.getDomainSite(), email, pwd })
|
||||
|
||||
if (ris)
|
||||
tools.showPositiveNotif($q, ris)
|
||||
else {
|
||||
tools.showNegativeNotif($q, t('miab.err_setmailuserpwd'))
|
||||
}
|
||||
|
||||
await checkDNSSite()
|
||||
|
||||
incaricamento.value = false
|
||||
|
||||
}
|
||||
|
||||
async function removeEmails(email: String) {
|
||||
$q.dialog({
|
||||
message: t('miab.deleteemail') + email + ' ?',
|
||||
ok: {
|
||||
label: t('dialog.yes'),
|
||||
push: true,
|
||||
},
|
||||
cancel: {
|
||||
label: t('dialog.cancel'),
|
||||
},
|
||||
title: 'Funzione:',
|
||||
}).onOk(async () => {
|
||||
|
||||
incaricamento.value = true
|
||||
|
||||
const ris = await globalStore.getMailInABox("removeEmails", { domain: tools.getDomainSite(), email })
|
||||
|
||||
if (ris)
|
||||
tools.showPositiveNotif($q, ris)
|
||||
else {
|
||||
tools.showNegativeNotif($q, t('miab.err_removed'))
|
||||
}
|
||||
|
||||
await checkDNSSite()
|
||||
|
||||
incaricamento.value = false
|
||||
})
|
||||
}
|
||||
|
||||
watch(() => selZoneId.value, async (to: any, from: any) => {
|
||||
if (selZoneId.value) {
|
||||
incaricamento.value = true
|
||||
@@ -375,11 +464,15 @@ export default defineComponent({
|
||||
const getLabelSelApi = () => {
|
||||
const myrec = optionsApi.value.find((opt: any) => opt.value === selectedApi.value)
|
||||
return myrec ? myrec.label : ''
|
||||
};
|
||||
}
|
||||
const getNameBySelZoneinId = () => {
|
||||
const myrec = arrZones.value.find((zone: any) => zone.id === selZoneId.value)
|
||||
return myrec ? myrec.name : ''
|
||||
};
|
||||
if (arrZones.value) {
|
||||
const myrec = arrZones.value.find((zone: any) => zone.id === selZoneId.value)
|
||||
return myrec ? myrec.name : ''
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
const getSelectedString = () => {
|
||||
return selected.value.length === 0 ? '' : `${selected.value[0].name} selezionata.`
|
||||
@@ -442,12 +535,16 @@ export default defineComponent({
|
||||
|
||||
const setCorrectIpsOnDNS = async () => {
|
||||
|
||||
incaricamento.value = true
|
||||
|
||||
let myrecord = {
|
||||
name: tools.getDomainSite()
|
||||
}
|
||||
await globalStore.getCloudFlareTok("setCorrectIpsOnDNS", selectedApi.value, selZoneId.value, null, myrecord)
|
||||
|
||||
await checkDNSSite()
|
||||
|
||||
incaricamento.value = false
|
||||
}
|
||||
|
||||
|
||||
@@ -469,6 +566,60 @@ export default defineComponent({
|
||||
|
||||
}
|
||||
|
||||
const delRecordDNS = async (row: any) => {
|
||||
|
||||
$q.dialog({
|
||||
message: t('db.deletetherecord') + ' type = ' + row.type + ' ?',
|
||||
ok: {
|
||||
label: t('dialog.yes'),
|
||||
push: true,
|
||||
},
|
||||
cancel: {
|
||||
label: t('dialog.cancel'),
|
||||
},
|
||||
title: 'Funzione:',
|
||||
}).onOk(async () => {
|
||||
|
||||
const recupdated = await globalStore.getCloudFlareTok("delRecordDNS", selectedApi.value, selZoneId.value, row.id, null)
|
||||
|
||||
if (recupdated) {
|
||||
// update record
|
||||
updateArrDns()
|
||||
|
||||
tools.showPositiveNotif($q, t('db.deletedrecord'))
|
||||
} else {
|
||||
tools.showNegativeNotif($q, t('db.recfailed'))
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
function getResultCheckDnsParam(title: string, recordReal: any, paramReal: string, strExpected: string) {
|
||||
|
||||
let mystr = ''
|
||||
try {
|
||||
if (recordReal) {
|
||||
const contentReal = recordReal[paramReal]
|
||||
|
||||
if (strExpected !== contentReal) {
|
||||
errorDNS.value = true
|
||||
mystr += `<span style="color: red;">${title}: NO</span><br> 👉🏻 🔴 Attuale__: ${contentReal}<br> 👉🏻 🔵 Aspettato: ${strExpected} <br>`
|
||||
} else {
|
||||
mystr += `<span style="color: green;">${title}: 🟢 OK (${contentReal})</span><br>`
|
||||
}
|
||||
|
||||
} else {
|
||||
errorDNS.value = true
|
||||
mystr += `<span style="color: gray;">${title}: 🔴 N/A</span><br>`
|
||||
}
|
||||
} catch (error) {
|
||||
mystr += `<span style="color: gray;">${title}: 🔴 N/A Errore nell\'estrazione del parametro ... </span><br>`
|
||||
}
|
||||
|
||||
return mystr
|
||||
}
|
||||
|
||||
|
||||
async function checkDNSSite() {
|
||||
incaricamento.value = true
|
||||
await updateArrDns()
|
||||
@@ -478,35 +629,91 @@ export default defineComponent({
|
||||
errorDNS.value = false
|
||||
|
||||
const domainSite = tools.getDomainSite();
|
||||
const domains = [
|
||||
domainSite,
|
||||
'api.' + domainSite,
|
||||
'test.' + domainSite,
|
||||
'testapi.' + domainSite
|
||||
const domainsData = [
|
||||
{ url: domainSite, expected: globalStore.site.host_ip, type: 'A' },
|
||||
{ url: 'api.' + domainSite, expected: globalStore.site.host_api_ip, type: 'A' },
|
||||
{ url: 'test.' + domainSite, expected: globalStore.site.host_test_ip, type: 'A' },
|
||||
{ url: 'testapi.' + domainSite, expected: globalStore.site.host_testapi_ip, type: 'A' },
|
||||
{ url: 'www.' + domainSite, expected: domainSite, type: 'CNAME' }
|
||||
];
|
||||
|
||||
const ips = [
|
||||
globalStore.site.host_ip,
|
||||
globalStore.site.host_api_ip,
|
||||
globalStore.site.host_test_ip,
|
||||
globalStore.site.host_testapi_ip
|
||||
];
|
||||
|
||||
// Controllo DNS
|
||||
if (dnsPageActive.value) {
|
||||
let checkDomainA1 = '';
|
||||
errorDNS.value = false
|
||||
for (let i = 0; i < domains.length; i++) {
|
||||
let ris = tools.getHostPuntamento(arrDNS.value, domains[i], ips[i]!)
|
||||
checkDomainA1 += ris.text
|
||||
for (let i = 0; i < domainsData.length; i++) {
|
||||
let ris = tools.getHostPuntamento(arrDNS.value, domainsData[i].url, domainsData[i].expected!, domainsData[i].type);
|
||||
checkDomainA1 += ris.text;
|
||||
if (!ris.ok) {
|
||||
errorDNS.value = true
|
||||
errorDNS.value = true;
|
||||
}
|
||||
}
|
||||
rischeckDNS.value = checkDomainA1
|
||||
}
|
||||
|
||||
incaricamento.value = false
|
||||
let checkServerMailEnabled = '<br>';
|
||||
|
||||
if (globalStore.site.enable_servermail) {
|
||||
// Controlla type MX
|
||||
const mxRecord = arrDNS.value.find((record: any) => record.type === 'MX' && record.name === domainSite);
|
||||
checkServerMailEnabled += getResultCheckDnsParam('Server mail enabled', mxRecord, 'content', globalStore.site.servermail!)
|
||||
|
||||
// *** Controlla type TXT spf1 ***
|
||||
let contentTXTspf1Expected = `v=spf1 a mx:${globalStore.site.servermail} ip4:${globalStore.site.servermailip} ~all`;
|
||||
const mxRecordTXTspf1 = arrDNS.value.find((record: any) => record.type === 'TXT' && record.name === domainSite && (record.content.indexOf('v=spf1') > -1))
|
||||
|
||||
checkServerMailEnabled += getResultCheckDnsParam('TXT spf1 RECORD', mxRecordTXTspf1, 'content', contentTXTspf1Expected)
|
||||
|
||||
let nameDkimtoFind = `mail._domainkey.${domainSite}`;
|
||||
|
||||
// Controllo DKIM:
|
||||
const mxRecordTXTDKIM = arrDNS.value.find((record: any) => record.type === 'TXT' && record.name === nameDkimtoFind)
|
||||
let contentTXTDKIMExpected = `v=DKIM1; h=sha256; k=rsa; s=email; p=${globalStore.site.dkim}`
|
||||
|
||||
checkServerMailEnabled += getResultCheckDnsParam('DKIM1', mxRecordTXTDKIM, 'content', contentTXTDKIMExpected)
|
||||
|
||||
// Controllo DMARC:
|
||||
const mxRecordTXTDMARC = arrDNS.value.find((record: any) => record.type === 'TXT' && record.name === `_dmarc.${domainSite}`)
|
||||
let contentTXTMARCExpected = `v=DMARC1; p=quarantine; ruf=mailto:dmarc@${domainSite};`;
|
||||
|
||||
checkServerMailEnabled += getResultCheckDnsParam('DMARC RECORD', mxRecordTXTDMARC, 'content', contentTXTMARCExpected)
|
||||
|
||||
// Controlla autoconfig
|
||||
const recAutoConfig = arrDNS.value.find((record: any) => record.type === 'CNAME' && record.name === `autoconfig.${domainSite}`);
|
||||
checkServerMailEnabled += getResultCheckDnsParam('AutoConfig CNAME', recAutoConfig, 'content', globalStore.site.servermail!)
|
||||
|
||||
// Controlla autodiscover
|
||||
const recAutoDiscover = arrDNS.value.find((record: any) => record.type === 'CNAME' && record.name === `autodiscover.${domainSite}`);
|
||||
checkServerMailEnabled += getResultCheckDnsParam('AutoDiscover CNAME', recAutoDiscover, 'content', globalStore.site.servermail!)
|
||||
|
||||
await getEmails()
|
||||
}
|
||||
rischeckDNS.value = checkDomainA1 + checkServerMailEnabled
|
||||
|
||||
incaricamento.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function addEmail() {
|
||||
// Implementa qui la logica per aggiungere la nuova email
|
||||
console.log('Aggiunta nuova email:', newEmail.value)
|
||||
await addEmailServer(newEmail.value, newPassword.value)
|
||||
|
||||
// Resetta i campi e chiudi il dialog
|
||||
newEmail.value = ''
|
||||
newPassword.value = ''
|
||||
showNewEmail.value = false
|
||||
}
|
||||
|
||||
async function setMailUserPwd() {
|
||||
// Implementa qui la logica per aggiungere la nuova email
|
||||
await setMailUserPassword(emailSel.value, changePassword.value)
|
||||
|
||||
// Resetta i campi e chiudi il dialog
|
||||
emailSel.value = ''
|
||||
changePassword.value = ''
|
||||
showchangePwd.value = false
|
||||
}
|
||||
|
||||
|
||||
onMounted(mounted)
|
||||
|
||||
return {
|
||||
@@ -566,6 +773,21 @@ export default defineComponent({
|
||||
globalStore,
|
||||
setCorrectIpsOnDNS,
|
||||
errorDNS,
|
||||
delRecordDNS,
|
||||
arrEmails,
|
||||
removeEmails,
|
||||
addEmail,
|
||||
newEmail,
|
||||
newPassword,
|
||||
changeEmail,
|
||||
changePassword,
|
||||
showNewEmail,
|
||||
showchangePwd,
|
||||
isFormValid,
|
||||
isPwd,
|
||||
setMailUserPwd,
|
||||
emailSel,
|
||||
isFormValidChange,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -71,6 +71,76 @@
|
||||
@click="checkDNSSite"
|
||||
>
|
||||
</q-btn>
|
||||
<div v-if="dnsPageActive && arrEmails" class="q-ma-md">
|
||||
<q-list bordered class="rounded-borders" style="max-width: 600px">
|
||||
<q-item-label header>Caselle di Posta:</q-item-label>
|
||||
|
||||
<q-btn
|
||||
label="Aggiungi Nuova Casella"
|
||||
color="primary"
|
||||
@click="showNewEmail = true"
|
||||
class="q-my-sm"
|
||||
></q-btn>
|
||||
|
||||
<div v-if="arrEmails.length === 0">
|
||||
<span style="color: red; font-weight: bold"
|
||||
>Nessuna casella di posta creata</span
|
||||
>
|
||||
</div>
|
||||
|
||||
<q-item v-for="(email, index) in arrEmails" :key="index">
|
||||
<q-item-section avatar top>
|
||||
<q-icon name="email" color="black" size="34px" />
|
||||
</q-item-section>
|
||||
|
||||
<q-item-section top class="col-2 gt-sm">
|
||||
<q-item-label class="q-mt-sm">{{
|
||||
email.slice(0, email.indexOf('@'))
|
||||
}}</q-item-label>
|
||||
</q-item-section>
|
||||
|
||||
<q-item-section top>
|
||||
<q-item-label lines="1">
|
||||
<span class="text-weight-medium">{{ email }}</span>
|
||||
<span class="text-grey-8"></span>
|
||||
</q-item-label>
|
||||
<q-item-label
|
||||
lines="1"
|
||||
class="q-mt-xs text-body2 text-weight-bold text-primary text-uppercase"
|
||||
>
|
||||
<span class="cursor-pointer">Leggi Email</span>
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
|
||||
<q-item-section top side>
|
||||
<div class="text-grey-8 q-gutter-xs">
|
||||
<q-btn
|
||||
class="gt-xs"
|
||||
size="12px"
|
||||
flat
|
||||
dense
|
||||
round
|
||||
icon="delete"
|
||||
@click="removeEmails(email)"
|
||||
/>
|
||||
<q-btn
|
||||
class="gt-xs"
|
||||
size="12px"
|
||||
flat
|
||||
dense
|
||||
round
|
||||
icon="edit"
|
||||
@click="
|
||||
emailSel = email;
|
||||
showchangePwd = true;
|
||||
"
|
||||
/>
|
||||
<!--<q-btn size="12px" flat dense round icon="more_vert" />-->
|
||||
</div>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</div>
|
||||
<div v-if="rischeckDNS" v-html="rischeckDNS"></div>
|
||||
<q-btn
|
||||
v-if="errorDNS"
|
||||
@@ -132,7 +202,12 @@
|
||||
@click.stop="toggleSelect(props.row)"
|
||||
></q-checkbox>
|
||||
</q-td>
|
||||
<q-td v-for="col in viscolumnsDNS" :key="col.name" :props="props">
|
||||
<q-td
|
||||
v-for="col in viscolumnsDNS"
|
||||
:key="col.name"
|
||||
:props="props"
|
||||
style="max-width: 240px; white-space: pre-wrap; overflow: auto"
|
||||
>
|
||||
<q-popup-edit
|
||||
v-if="col.name === 'content'"
|
||||
v-model="props.row.content"
|
||||
@@ -166,6 +241,15 @@
|
||||
autogrow
|
||||
/>
|
||||
</q-popup-edit>
|
||||
<q-btn
|
||||
v-if="col.name === 'delete'"
|
||||
icon="delete"
|
||||
color="negative"
|
||||
dense
|
||||
size="sm"
|
||||
@click="delRecordDNS(props.row)"
|
||||
>
|
||||
</q-btn>
|
||||
<q-popup-edit
|
||||
v-if="col.name === 'priority'"
|
||||
v-model="props.row.priority"
|
||||
@@ -277,6 +361,96 @@
|
||||
</template>
|
||||
</q-field>
|
||||
</div>
|
||||
<!-- Dialog per inserire nuova email -->
|
||||
<q-dialog v-model="showNewEmail">
|
||||
<q-card style="min-width: 350px">
|
||||
<q-card-section>
|
||||
<div class="text-h6">Aggiungi Nuova Casella Email</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section class="q-pt-none">
|
||||
<q-input
|
||||
v-model="newEmail"
|
||||
label="Indirizzo Email"
|
||||
type="email"
|
||||
:rules="[(val) => !!val || 'Email è obbligatoria']"
|
||||
/>
|
||||
<q-input
|
||||
v-model="newPassword"
|
||||
label="Password"
|
||||
:type="isPwd ? 'password' : 'text'"
|
||||
:rules="[
|
||||
(val) =>
|
||||
val.length >= 8 ||
|
||||
'La password deve essere di almeno 8 caratteri',
|
||||
]"
|
||||
>
|
||||
<template v-slot:append>
|
||||
<q-icon
|
||||
:name="isPwd ? 'visibility_off' : 'visibility'"
|
||||
class="cursor-pointer"
|
||||
@click="isPwd = !isPwd"
|
||||
/>
|
||||
</template>
|
||||
</q-input>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-actions align="right" class="text-primary">
|
||||
<q-btn flat label="Annulla" v-close-popup />
|
||||
<q-btn
|
||||
flat
|
||||
label="Aggiungi Casella"
|
||||
@click="addEmail"
|
||||
:disable="!isFormValid"
|
||||
/>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
<q-dialog v-model="showchangePwd">
|
||||
<q-card style="min-width: 350px">
|
||||
<q-card-section>
|
||||
<div class="text-h6">Aggiorna Password Email</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section class="q-pt-none">
|
||||
<q-input
|
||||
v-model="emailSel"
|
||||
label="Indirizzo Email"
|
||||
type="email"
|
||||
:disable="true"
|
||||
/>
|
||||
<q-input
|
||||
v-model="changePassword"
|
||||
autofocus
|
||||
label="Password"
|
||||
:type="isPwd ? 'password' : 'text'"
|
||||
:rules="[
|
||||
(val) =>
|
||||
val.length >= 8 ||
|
||||
'La password deve essere di almeno 8 caratteri',
|
||||
]"
|
||||
>
|
||||
<template v-slot:append>
|
||||
<q-icon
|
||||
:name="isPwd ? 'visibility_off' : 'visibility'"
|
||||
class="cursor-pointer"
|
||||
@click="isPwd = !isPwd"
|
||||
/>
|
||||
</template>
|
||||
</q-input>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-actions align="right" class="text-primary">
|
||||
<q-btn flat label="Annulla" v-close-popup />
|
||||
<q-btn
|
||||
flat
|
||||
label="Aggiorna Password"
|
||||
@click="setMailUserPwd"
|
||||
:disable="!isFormValidChange"
|
||||
/>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</template>
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user