- Add Button Whatsapp Chat

- Fixed 'Ask Info' and 'Book' if the email arrived...
- Added "Settings" table: URL_FACEBOOK, TELEGRAM_SUPPORT, URL_INSTAGRAM, WHATSAPP_CELL, INT_CODE, MAIN_EMAIL, CONTACTS_EMAIL_CELL, CALL_WORKING_DAYS, EVENTS_CAL, MSG_REPLY_AFTER_BOOKING.
-
This commit is contained in:
Paolo Arena
2019-11-05 23:53:18 +01:00
parent b27f7e3cbf
commit 571e948d39
16 changed files with 467 additions and 16 deletions

View File

@@ -27,7 +27,7 @@ export const shared_consts = {
}, },
fieldsUserToChange() { fieldsUserToChange() {
return ['username', 'email', 'cell', 'name', 'surname', 'perm', 'date_reg', 'verified_email', 'img', 'ipaddr', 'lasttimeonline', 'profile'] return ['_id', 'username', 'email', 'cell', 'name', 'surname', 'perm', 'date_reg', 'verified_email', 'img', 'ipaddr', 'lasttimeonline', 'profile']
} }
} }

View File

@@ -20,7 +20,15 @@
<div v-if="myop.certifications" class="text-subtitle-certificato">{{myop.certifications}}</div> <div v-if="myop.certifications" class="text-subtitle-certificato">{{myop.certifications}}</div>
<div class="op__cell"> <div class="op__cell">
<q-icon class="flex-icon" name="mobile_friendly"></q-icon> <q-icon class="flex-icon" name="mobile_friendly"></q-icon>
{{myop.cell}} <span class="q-mx-sm">{{myop.cell}}</span>
<q-btn fab-mini icon="fab fa-whatsapp"
color="green" type="a"
size="xs"
:href="tools.getHttpForWhatsapp(myop.cell)" target="__blank">
</q-btn>
</div> </div>
<div class="op__email"> <div class="op__email">
<q-icon class="flex-icon" name="contact_mail"> </q-icon>&nbsp; <q-icon class="flex-icon" name="contact_mail"> </q-icon>&nbsp;

View File

@@ -0,0 +1,4 @@
.myflex{
display: flex;
flex: 1;
}

View File

@@ -0,0 +1,48 @@
import Vue from 'vue'
import { Component, Prop, Watch } from 'vue-property-decorator'
import { tools } from '../../store/Modules/tools'
import { UserStore } from '../../store/Modules'
@Component({
name: 'CMyAvatar'
})
export default class CMyAvatar extends Vue {
@Prop({ required: false, default: '' }) public myimg
@Prop({ required: false, default: '40px' }) public size
public myicon: string = ''
public myimgint: string = ''
get tools() {
return tools
}
@Watch('GlobalStore.state.my.profile.img')
public imgChanged() {
// console.log('imgChanged')
this.refresh()
}
@Watch('myimg')
public imglocalChanged() {
this.myimgint = ''
// console.log('myimg')
this.refresh()
}
public refresh() {
if (!this.myimg) {
this.myicon = 'fas fa-user-circle'
} else {
this.myimgint = this.myimg
}
// console.log('myimgint', this.myimgint)
}
public mounted() {
this.refresh()
}
}

View File

@@ -0,0 +1,17 @@
<template>
<div>
<q-avatar v-if="!myimgint" class="q-mb-sx center_img" :icon="myicon" :font-size="size">
</q-avatar>
<q-avatar v-if="myimgint" class="q-mb-sx center_img">
<img :src="myimgint" :font-size="size">
</q-avatar>
</div>
</template>
<script lang="ts" src="./CMyAvatar.ts">
</script>
<style lang="scss" scoped>
@import './CMyAvatar.scss';
</style>

View File

@@ -0,0 +1 @@
export {default as CMyAvatar} from './CMyAvatar.vue'

View File

@@ -0,0 +1,138 @@
import Vue from 'vue'
import { Component, Prop } from 'vue-property-decorator'
import { tools } from '../../store/Modules/tools'
import { toolsext } from '@src/store/Modules/toolsext'
import { IColGridTable } from '../../model'
import { fieldsTable } from '../../store/Modules/fieldsTable'
import { CMyChipList } from '../CMyChipList'
import { CDateTime } from '../CDateTime'
import { CMyToggleList } from '../CMyToggleList'
import { CMySelect } from '../CMySelect'
@Component({
name: 'CMyPopupEdit',
components: {CMyChipList, CDateTime, CMyToggleList, CMySelect}
})
export default class CMyPopupEdit extends Vue {
@Prop({ required: true }) public row
@Prop({ required: true }) public col
@Prop({ required: false, default: false }) public canEdit
@Prop({ required: false, default: '' }) public field
@Prop({ required: false, default: '' }) public subfield
public myvalue = ''
get tools() {
return tools
}
get db_fieldsTable() {
return fieldsTable
}
public changeval(newval) {
this.$emit('update:row', newval)
}
public mounted() {
if ((this.subfield !== '') && (this.subfield !== '')) {
if (this.row[this.field] === undefined) {
this.row[this.field] = {}
this.myvalue = ''
} else {
this.myvalue = this.row[this.field][this.subfield]
}
} else {
if (this.field !== '')
this.myvalue = this.row[this.field]
else
this.myvalue = this.row
}
}
public OpenEdit() {
console.log('OpenEdit')
this.$emit('show')
}
public SaveValueInt(newVal, valinitial) {
console.log('SaveValueInt', newVal)
// Update value in table memory
if (this.subfield !== '') {
if (this.row[this.field] === undefined)
this.row[this.field] = {}
this.row[this.field][this.subfield] = newVal
} else {
if (this.field !== '')
this.row[this.field] = newVal
else
this.row = newVal
}
this.$emit('save', newVal, valinitial)
}
public visuValByType(val, col: IColGridTable, row) {
if (col === undefined || row === undefined)
return
// let val = ''
// if (col.subfield !== '') {
// if (row[col.field] === undefined)
// row[col.field] = {}
//
// val = row[col.field][col.subfield]
// } else {
// val = row[col.field]
// }
//
if (col.fieldtype === tools.FieldType.date) {
if (val === undefined) {
return '[]'
} else {
return tools.getstrDateTime(val)
}
} else if (col.fieldtype === tools.FieldType.boolean) {
return (val) ? this.$t('dialog.yes') : this.$t('dialog.no')
} else if (col.fieldtype === tools.FieldType.binary) {
if (val === undefined)
return '[---]'
else
return fieldsTable.getArrStrByValueBinary(this, col, val)
} else if (col.fieldtype === tools.FieldType.select) {
if (val === undefined)
return '[---]'
else
return fieldsTable.getValueByTable(col, val)
} else {
if (val === undefined)
return '[]'
else if (val === '') {
return '[]'
} else {
let mystr = tools.firstchars(val, tools.MAX_CHARACTERS)
if (val) {
if (val.length > tools.MAX_CHARACTERS)
mystr += '...'
} else {
return val
}
return mystr
}
}
}
public getclassCol(col) {
if (col) {
let mycl = (col.disable || !this.canEdit) ? '' : 'colmodif'
mycl += (col.fieldtype === tools.FieldType.date) ? ' coldate flex flex-container' : ''
return mycl
} else {
return ''
}
}
}

View File

@@ -0,0 +1,103 @@
<template>
<div :class="getclassCol(col)">
<div v-if="col.fieldtype === tools.FieldType.date">
<CDateTime
:label="col.label"
class="cursor-pointer"
:valueDate="myvalue"
:readonly="false"
:dense="true"
:canEdit="canEdit"
@savetoclose="SaveValueInt"
@show="OpenEdit">
</CDateTime>
</div>
<div v-else>
<div v-if="col.fieldtype === tools.FieldType.binary">
<CMyChipList
:value="myvalue"
:options="db_fieldsTable.getTableJoinByName(col.jointable)"
:optval="db_fieldsTable.getKeyByTable(col.jointable)"
:optlab="db_fieldsTable.getLabelByTable(col.jointable)"
:opticon="db_fieldsTable.getIconByTable(col.jointable)"></CMyChipList>
</div>
<!-- Show Value -->
<div v-else>
{{ visuValByType(myvalue, col, row) }}
</div>
<!--<q-select v-model="myvalue"-->
<!--rounded-->
<!--outlined-->
<!--dense-->
<!--:options="db_fieldsTable.getTableJoinByName(col.jointable)"-->
<!--:display-value="db_fieldsTable.getLabelByTable(col.jointable)"-->
<!--emit-value-->
<!--@input="SaveValueInt"-->
<!--&gt;-->
<!--</q-select>-->
<!-- Edit Value -->
<q-popup-edit
v-if="canEdit"
v-model="myvalue"
:disable="col.disable"
:title="col.title"
buttons
@save="SaveValueInt"
@show="OpenEdit">
<div v-if="col.fieldtype === tools.FieldType.boolean">
<q-checkbox v-model="myvalue" :label="col.title">
</q-checkbox>
{{ visuValByType(myvalue, col, row) }}
</div>
<div v-else-if="col.fieldtype === tools.FieldType.string">
<q-input v-model="myvalue"
autogrow
autofocus>
</q-input>
</div>
<div v-else-if="col.fieldtype === tools.FieldType.number">
<q-input v-model="myvalue" type="number"
autofocus>
</q-input>
</div>
<div v-else-if="col.fieldtype === tools.FieldType.binary">
<CMyToggleList :label="col.title"
:options="db_fieldsTable.getTableJoinByName(col.jointable)"
:value.sync="myvalue"
:optval="db_fieldsTable.getKeyByTable(col.jointable)"
:optlab="db_fieldsTable.getLabelByTable(col.jointable)">
</CMyToggleList>
</div>
<div v-else-if="col.fieldtype === tools.FieldType.html">
<q-input v-model="myvalue"
autofocus
@keyup.enter.stop
type="textarea"></q-input>
</div>
<div v-else-if="col.fieldtype === tools.FieldType.select">
<CMySelect :label="col.title"
:value.sync="myvalue"
:optval="db_fieldsTable.getKeyByTable(col.jointable)"
:optlab="db_fieldsTable.getLabelByTable(col.jointable)"
:options="db_fieldsTable.getTableJoinByName(col.jointable)"
:useinput="false">
</CMySelect>
</div>
</q-popup-edit>
</div>
</div>
</template>
<script lang="ts" src="./CMyPopupEdit.ts">
</script>
<style lang="scss" scoped>
@import './CMyPopupEdit.scss';
</style>

View File

@@ -0,0 +1 @@
export {default as CMyPopupEdit} from './CMyPopupEdit.vue'

View File

@@ -13,8 +13,10 @@ import { static_data } from '@src/db/static_data'
import Quasar from 'quasar' import Quasar from 'quasar'
import { FormNewsletter } from '../FormNewsletter' import { FormNewsletter } from '../FormNewsletter'
import { IUserState } from '../../model' import { IUserState } from '../../model'
import MixinBase from '../../mixins/mixin-base'
@Component({ @Component({
mixins: [MixinBase],
name: 'Footer', name: 'Footer',
components: { Logo, FormNewsletter } components: { Logo, FormNewsletter }
}) })
@@ -54,4 +56,8 @@ export default class Footer extends Vue {
return static_data return static_data
} }
get ChatWhatsapp() {
return tools.getHttpForWhatsapp(this.Whatsapp_Cell)
}
} }

View File

@@ -26,7 +26,34 @@
<p class="mycontacts_title">{{$t('homepage.titlecontatti')}}</p> <p class="mycontacts_title">{{$t('homepage.titlecontatti')}}</p>
<p class="mycontacts_text" v-html="$t('homepage.contacts')"></p> <p class="mycontacts_text">
<i v-if="getValDb('MAIN_EMAIL')" aria-hidden="true"
class="q-icon fas fa-envelope q-mx-sm"></i>
<a :href="`mailto:` + getValDb('MAIN_EMAIL')" class="links">{{ getValDb('MAIN_EMAIL')
}}</a><br>
<br>
<span v-for="rec in getarrValDb('CONTACTS_EMAIL_CELL')">
{{ rec.name }}: {{ rec.phone }}
<a v-if="!!tools.getHttpForWhatsapp(rec.phone)" :href="tools.getHttpForWhatsapp(rec.phone)" target="_blank">
<i aria-hidden="true" class="q-icon fab fa-whatsapp icon_contact links"></i></a>
<br>
<i v-if="rec.email" aria-hidden="true"
class="q-icon fas fa-envelope q-mx-sm"></i> <a :href="`mailto:`+ rec.email "
class="links">{{rec.email}}</a>
<br>
</span>
<span v-if="getValDb('CALL_WORKING_DAYS')"><br>orari per chiamate:<br>
<span v-html="getValDb('CALL_WORKING_DAYS')"></span></span>
<!--Elisa Ghizzardi: 338-9344724 <a href="mailto:elisa.ghizzardi@yahoo.com" class="links">elisa.ghizzardi@yahoo.com</a><br>
Cristina Barattoni: 335-8233721 <a href="mailto:info@cristinabarattoni.it"
class="links">info@cristinabarattoni.it</a><br><br>'
+
'orari per chiamate:<br>lun-ven: 12:30-13:30; 17:00-19.30<br>sab-dom: 10-18
-->
</p>
</div> </div>
<div class="landing__footer-icons row flex-center"> <div class="landing__footer-icons row flex-center">
<a v-if="!!FBPage" :href="FBPage" target="_blank"> <a v-if="!!FBPage" :href="FBPage" target="_blank">
@@ -38,6 +65,9 @@
<a v-if="!!TelegramSupport" :href="TelegramSupport" target="_blank"> <a v-if="!!TelegramSupport" :href="TelegramSupport" target="_blank">
<i aria-hidden="true" class="q-icon fab fa-telegram icon_contact links"></i></a> <i aria-hidden="true" class="q-icon fab fa-telegram icon_contact links"></i></a>
<a v-if="!!Whatsapp_Cell" :href="ChatWhatsapp" target="_blank">
<i aria-hidden="true" class="q-icon fab fa-whatsapp icon_contact links"></i></a>
<!--<a href="" target="_blank"><i aria-hidden="true" class="q-icon fab fa-github"> </i></a>--> <!--<a href="" target="_blank"><i aria-hidden="true" class="q-icon fab fa-github"> </i></a>-->
<!--<a href="https://twitter.com/" target="_blank"><i aria-hidden="true" class="q-icon fab fa-twitter"> </i></a>--> <!--<a href="https://twitter.com/" target="_blank"><i aria-hidden="true" class="q-icon fab fa-twitter"> </i></a>-->
@@ -52,7 +82,8 @@
</div> </div>
<p class="text-center"> <p class="text-center">
<router-link v-if="static_data.functionality.SHOW_ONLY_POLICY" to="/policy"><span class="footer_link">{{$t('privacy_policy')}}</span></router-link> <router-link v-if="static_data.functionality.SHOW_ONLY_POLICY" to="/policy"><span
class="footer_link">{{$t('privacy_policy')}}</span></router-link>
</p> </p>
</div> </div>
@@ -77,11 +108,12 @@
</div> </div>
</div> </div>
</section> </section>
<q-page-scroller position="bottom-right" :scroll-offset="850" :offset="[18, 18]" style="opacity: 0.3"> <q-page-scroller position="bottom-right" :scroll-offset="850" :offset="[18, 78]" style="opacity: 0.3">
<q-btn fab icon="keyboard_arrow_up" color="accent"/> <q-btn fab icon="keyboard_arrow_up" color="accent"/>
</q-page-scroller> </q-page-scroller>
<q-page-sticky position="bottom-right" :offset="[18, 78]"> <q-page-sticky v-if="ChatWhatsapp" position="bottom-right" :offset="[18, 18]">
<q-btn fab icon="fab fa-whatsapp" color="green" /> <q-btn fab icon="fab fa-whatsapp" color="green" type="a" :href="ChatWhatsapp" target="__blank"
class="mybtn_sticky"/>
</q-page-sticky> </q-page-sticky>
</div> </div>
</template> </template>

43
src/mixins/mixin-base.ts Normal file
View File

@@ -0,0 +1,43 @@
import Vue from 'vue'
import Component from 'vue-class-component'
import { func_tools } from '../store/Modules/toolsext'
import { tools } from '../store/Modules/tools'
import { toolsext } from '@src/store/Modules/toolsext'
import { GlobalStore } from '../store/Modules'
// You can declare a mixin as the same style as components.
@Component
export default class MixinBase extends Vue {
public mythis() {
return this
}
get toolsext() {
return toolsext
}
get func_tools() {
return func_tools
}
get tools() {
return tools
}
public getValDb(keystr) {
return GlobalStore.getters.getValueSettingsByKey(keystr)
}
public getarrValDb(keystr) {
const myval = GlobalStore.getters.getValueSettingsByKey(keystr)
// console.log('myval', myval)
if (myval) {
const myrec = JSON.parse(myval)
// console.log('*************** getarrValDb')
// console.table(myrec)
return myrec
} else {
return []
}
}
}

View File

@@ -0,0 +1,30 @@
import Vue from 'vue'
import Component from 'vue-class-component'
import { CalendarStore } from '../store/Modules'
import { UserStore } from '@modules'
@Component
export default class MixinOperator extends Vue {
public getOperators() {
return CalendarStore.state.operators
}
public getOperatorByUsername(username) {
return CalendarStore.getters.getOperatorByUsername(username)
}
public getImgTeacherByUsername(username) {
return `statics/images/` + CalendarStore.getters.getImgTeacherByUsername(username)
}
public getTeacherByUsername(username) {
const op = this.getOperatorByUsername(username)
if (!!op) {
return op.name + ' ' + op.surname
} else {
return ''
}
}
}

View File

@@ -328,6 +328,7 @@ namespace Mutations {
const mylist = Getters.getters.getListByTable(table) const mylist = Getters.getters.getListByTable(table)
const mykey = fieldsTable.getKeyByTable(table) const mykey = fieldsTable.getKeyByTable(table)
if (!!mylist) {
const myrec = mylist.find((event) => event[mykey] === id) const myrec = mylist.find((event) => event[mykey] === id)
// console.log('myrec', myrec) // console.log('myrec', myrec)
if (myrec) { if (myrec) {
@@ -336,6 +337,7 @@ namespace Mutations {
myrec[key] = value myrec[key] = value
} }
} }
}
} catch (e) { } catch (e) {
console.error(e) console.error(e)
} }

View File

@@ -1379,7 +1379,7 @@ export const tools = {
notify: par.param2 === true ? '1' : '0' notify: par.param2 === true ? '1' : '0'
}).then((ris) => { }).then((ris) => {
if (ris) { if (ris) {
tools.showPositiveNotif(myself.$q, myself.$t('cal.canceledbooking') + ' "' + par.param1.title + '"') tools.showPositiveNotif(myself.$q, myself.$t('cal.canceledbooking') + ' "' + par.param3 + '"')
if (myself.bookEventpage) if (myself.bookEventpage)
myself.bookEventpage.show = false myself.bookEventpage.show = false
} else } else
@@ -2476,7 +2476,8 @@ export const tools = {
console.log('CancelBookingEvent ', eventparam) console.log('CancelBookingEvent ', eventparam)
tools.askConfirm(mythis.$q, translate('cal.titlebooking'), translate('cal.cancelbooking') + ' ' + tools.gettextevent(mythis, eventparam) + '?', translate('dialog.yes'), translate('dialog.no'), mythis, '', lists.MenuAction.DELETE, 0, { tools.askConfirm(mythis.$q, translate('cal.titlebooking'), translate('cal.cancelbooking') + ' ' + tools.gettextevent(mythis, eventparam) + '?', translate('dialog.yes'), translate('dialog.no'), mythis, '', lists.MenuAction.DELETE, 0, {
param1: bookeventid, param1: bookeventid,
param2: notify param2: notify,
param3: eventparam.title
}) })
} }
, ,
@@ -2607,8 +2608,25 @@ export const tools = {
const duration = 500 const duration = 500
console.log('target', target, 'offset', offset, 'duration', duration) console.log('target', target, 'offset', offset, 'duration', duration)
setScrollPosition(target, offset, duration) setScrollPosition(target, offset, duration)
} },
getCellForWhatsapp(numbercell) {
let mynum = numbercell.replace(/\-/g, '')
const intcode = GlobalStore.getters.getValueSettingsByKey('INT_CODE')
if (numbercell.substring(0, 1) !== '+')
mynum = intcode + mynum
else
mynum = mynum.substring(1)
return mynum
},
getHttpForWhatsapp(numbercell) {
const mynum = this.getCellForWhatsapp(numbercell)
if (mynum)
return 'https://wa.me/' + mynum
else
return ''
}
// getLocale() { // getLocale() {
// if (navigator.languages && navigator.languages.length > 0) { // if (navigator.languages && navigator.languages.length > 0) {