0
0
Fork 0

Updated SMS component

This commit is contained in:
Joan Sanchez 2023-03-09 14:59:10 +01:00
parent c43f743804
commit f7c9da6ad1
5 changed files with 212 additions and 102 deletions

View File

@ -44,6 +44,11 @@ function responseError(error) {
let message = error.message; let message = error.message;
let logOut = false; let logOut = false;
const response = error.response;
if (response && response.data.error) {
message = response.data.error.message;
}
switch (error.response?.status) { switch (error.response?.status) {
case 401: case 401:
message = 'login.loginError'; message = 'login.loginError';

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { ref, computed, reactive } from 'vue'; import { ref, computed } from 'vue';
import { useDialogPluginComponent } from 'quasar'; import { useDialogPluginComponent } from 'quasar';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
@ -21,22 +21,35 @@ const props = defineProps({
type: String, type: String,
required: true, required: true,
}, },
locale: {
type: String,
required: false,
default: 'es',
},
send: {
type: Function,
required: true,
},
data: {
type: Object,
required: false,
default: null,
},
}); });
const maxLength = 160; const maxLength = 160;
const locale = ref('es'); const locale = ref(props.locale);
const subject = ref(props.subject); const subject = ref(props.subject);
const phone = ref(props.phone); const phone = ref(props.phone);
const message = ref(''); const message = ref('');
// const templates = ['pendingPayment', 'minAmount'];
const template = reactive(props.template);
updateMessage(); updateMessage();
function updateMessage() { function updateMessage() {
const params = { orderId: 123 }; const params = props.data;
message.value = t(`templates['${template}']`, params, { locale: locale.value }); const key = `templates['${props.template}']`;
message.value = t(key, params, { locale: locale.value });
} }
const totalLength = computed(() => message.value.length); const totalLength = computed(() => message.value.length);
@ -47,6 +60,18 @@ const color = computed(() => {
}); });
const languages = availableLocales.map((locale) => ({ label: t(locale), value: locale })); const languages = availableLocales.map((locale) => ({ label: t(locale), value: locale }));
const isLoading = ref(false);
async function send() {
isLoading.value = true;
await props.send({
destination: phone.value,
message: message.value,
});
isLoading.value = false;
onDialogOK();
}
</script> </script>
<template> <template>
@ -57,11 +82,16 @@ const languages = availableLocales.map((locale) => ({ label: t(locale), value: l
{{ t('Send SMS') }} {{ t('Send SMS') }}
</span> </span>
<q-space /> <q-space />
<q-btn icon="close" flat round dense v-close-popup /> <q-btn icon="close" :disable="isLoading" flat round dense v-close-popup />
</q-card-section> </q-card-section>
<q-card-section> <q-card-section>
<q-banner class="bg-warning" rounded dense> <q-banner class="bg-amber text-white" rounded dense>
This user uses "" as default language <template #avatar>
<q-icon name="warning" />
</template>
<span
v-html="t('CustomerDefaultLanguage', { locale: t(props.locale) })"
></span>
</q-banner> </q-banner>
</q-card-section> </q-card-section>
<q-card-section class="q-pb-xs"> <q-card-section class="q-pb-xs">
@ -111,7 +141,6 @@ const languages = availableLocales.map((locale) => ({ label: t(locale), value: l
stack-label stack-label
outlined outlined
autofocus autofocus
dense
> >
<template #append> <template #append>
<q-icon <q-icon
@ -129,8 +158,19 @@ const languages = availableLocales.map((locale) => ({ label: t(locale), value: l
</q-input> </q-input>
</q-card-section> </q-card-section>
<q-card-actions align="right"> <q-card-actions align="right">
<q-btn :label="t('globals.cancel')" color="primary" flat v-close-popup /> <q-btn
<q-btn :label="t('globals.confirm')" color="primary" @click="confirm" /> :label="t('globals.cancel')"
color="primary"
:disable="isLoading"
flat
v-close-popup
/>
<q-btn
:label="t('globals.confirm')"
@click="send()"
:loading="isLoading"
color="primary"
/>
</q-card-actions> </q-card-actions>
</q-card> </q-card>
</q-dialog> </q-dialog>
@ -141,38 +181,53 @@ const languages = availableLocales.map((locale) => ({ label: t(locale), value: l
transition: background 0.36s; transition: background 0.36s;
} }
.q-card { .q-card {
width: 400px; width: 500px;
} }
</style> </style>
<i18n> <i18n>
en: en:
Message: Message CustomerDefaultLanguage: This customer uses <strong>{locale}</strong> as their default language
en: English en: English
es: Spanish es: Spanish
fr: French fr: French
templates: templates:
pendingPayment: 'Your order is pending of payment. pendingPayment: 'Your order is pending of payment.
Please, enter the website and make the payment with a credit card. Thank you.' Please, enter the website and make the payment with a credit card. Thank you.'
minAmount: Test 2 minAmount: 'A minimum amount of 50 (VAT excluded) is required for your order
{ orderId } of { shipped } to receive it without additional shipping costs.'
orderChanges: 'Order {orderId} of { shipped }\r
{ changes }'
es: es:
Send SMS: Enviar SMS
CustomerDefaultLanguage: Este cliente utiliza <strong>{locale}</strong> como idioma por defecto
Language: Idioma Language: Idioma
Phone: Móvil
Subject: Asunto
Message: Mensaje Message: Mensaje
templates: templates:
pendingPayment: 'Su pedido está pendiente de pago. pendingPayment: 'Su pedido está pendiente de pago.
Por favor, entre en la página web y efectue el pago con tarjeta. Muchas gracias.' Por favor, entre en la página web y efectue el pago con tarjeta. Muchas gracias.'
minAmount: 'Es necesario un importe mínimo de 50€ (Sin IVA) en su pedido { orderId } del día 08/03/2023 para recibirlo sin portes adicionales.' minAmount: 'Es necesario un importe mínimo de 50 (Sin IVA) en su pedido
{ orderId } del día { shipped } para recibirlo sin portes adicionales.'
orderChanges: 'Pedido {orderId} día { shipped }
{ changes }'
en: Inglés en: Inglés
es: Español es: Español
fr: Francés fr: Francés
fr: fr:
Send SMS: Envoyer SMS
CustomerDefaultLanguage: Ce client utilise l'{locale} comme langue par défaut
Language: Langage Language: Langage
Phone: Mobile
Subject: Affaire
Message: Message Message: Message
templates: templates:
pendingPayment: 'Votre commande est en attente de paiement. pendingPayment: 'Votre commande est en attente de paiement.
Veuillez vous connecter sur le site web et effectuer le paiement par carte. Merci beaucoup.' Veuillez vous connecter sur le site web et effectuer le paiement par carte. Merci beaucoup.'
minAmount: 'Un montant minimum de 50 (TVA non incluse) est requis pour votre commande minAmount: 'Un montant minimum de 50 (TVA non incluse) est requis pour votre commande
4101055 du 03/08/2023 afin de la recevoir sans frais de port supplémentaires.' { orderId } du { shipped } afin de la recevoir sans frais de port supplémentaires.'
orderChanges: 'Commande { orderId } du { shipped }\r { changes }'
en: Anglais en: Anglais
es: Espagnol es: Espagnol
fr: Français fr: Français

View File

@ -59,9 +59,9 @@ watch(props, async () => {
:to="{ name: `${module}Summary`, params: { id: entity.id } }" :to="{ name: `${module}Summary`, params: { id: entity.id } }"
> >
<q-btn round flat dense size="md" icon="launch" color="white"> <q-btn round flat dense size="md" icon="launch" color="white">
<q-tooltip>{{ <q-tooltip>
t('components.cardDescriptor.summary') {{ t('components.cardDescriptor.summary') }}
}}</q-tooltip> </q-tooltip>
</q-btn> </q-btn>
</router-link> </router-link>

View File

@ -24,11 +24,25 @@ const entityId = computed(() => {
const filter = { const filter = {
include: [ include: [
{
relation: 'address',
scope: {
fields: ['id', 'name', 'mobile', 'phone'],
},
},
{ {
relation: 'client', relation: 'client',
scope: { scope: {
fields: ['id', 'name', 'salesPersonFk'], fields: ['id', 'name', 'salesPersonFk', 'phone', 'mobile'],
include: { relation: 'salesPersonUser' }, include: [
{
relation: 'user',
scope: {
fields: ['id', 'lang'],
},
},
{ relation: 'salesPersonUser' },
],
}, },
}, },
{ {
@ -125,6 +139,16 @@ function stateColor(state) {
</q-item-section> </q-item-section>
</q-item> </q-item>
</q-list> </q-list>
<q-card-actions class="q-gutter-md">
<q-icon
v-if="entity.isDeleted == true"
name="vn:deletedTicket"
size="xs"
color="primary"
>
<q-tooltip>{{ t('This ticket is deleted') }}</q-tooltip>
</q-icon>
</q-card-actions>
<q-card-actions> <q-card-actions>
<q-btn <q-btn
@ -139,3 +163,8 @@ function stateColor(state) {
</template> </template>
</card-descriptor> </card-descriptor>
</template> </template>
<i18n>
es:
This ticket is deleted: Este ticket está eliminado
</i18n>

View File

@ -3,13 +3,14 @@ import axios from 'axios';
import { ref } from 'vue'; import { ref } from 'vue';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router'; import { useRouter, useRoute } from 'vue-router';
import { usePrintService } from 'composables/usePrintService'; import { usePrintService } from 'composables/usePrintService';
import SendEmailDialog from 'components/common/SendEmailDialog.vue'; // import SendEmailDialog from 'components/common/SendEmailDialog.vue';
import VnConfirm from 'components/ui/VnConfirm.vue'; import VnConfirm from 'components/ui/VnConfirm.vue';
import VnSmsDialog from 'components/common/VnSmsDialog.vue'; import VnSmsDialog from 'components/common/VnSmsDialog.vue';
import toDate from 'filters/toDate';
const $props = defineProps({ const props = defineProps({
ticket: { ticket: {
type: Object, type: Object,
required: true, required: true,
@ -17,11 +18,12 @@ const $props = defineProps({
}); });
const router = useRouter(); const router = useRouter();
const route = useRoute();
const quasar = useQuasar(); const quasar = useQuasar();
const { t } = useI18n(); const { t } = useI18n();
const { openReport, sendEmail } = usePrintService(); const { openReport, sendEmail } = usePrintService();
const ticket = ref($props.ticket); const ticket = ref(props.ticket);
function openDeliveryNote(type = 'deliveryNote', documentType = 'pdf') { function openDeliveryNote(type = 'deliveryNote', documentType = 'pdf') {
const path = `Tickets/${ticket.value.id}/delivery-note-${documentType}`; const path = `Tickets/${ticket.value.id}/delivery-note-${documentType}`;
@ -34,31 +36,63 @@ function openDeliveryNote(type = 'deliveryNote', documentType = 'pdf') {
function sendDeliveryNote(type = 'deliveryNote', documentType = 'pdf') { function sendDeliveryNote(type = 'deliveryNote', documentType = 'pdf') {
const id = ticket.value.id; const id = ticket.value.id;
const customer = ticket.value.client; const customer = ticket.value.client;
return sendEmail(`Tickets/${id}/delivery-note-email`, { let pathName = 'delivery-note-email';
if (documentType == 'csv') pathName = 'delivery-note-csv-email';
const path = `Tickets/${id}/${pathName}`;
return sendEmail(path, {
recipientId: customer.id, recipientId: customer.id,
type: type, type: type,
}); });
} }
// function confirmPickupOrder() { const shipped = toDate(ticket.value.shipped);
// const customer = ticket.value.client; function showSmsDialog(template, customData) {
// quasar.dialog({ const ticket = props.ticket;
// component: SendEmailDialog, const address = ticket.address;
// componentProps: { const client = ticket.client;
// address: customer.email, const phone =
// send: sendPickupOrder, route.params.phone ||
// }, address.mobile ||
// }); address.phone ||
// } client.mobile ||
client.phone;
const data = {
orderId: ticket.id,
shipped: shipped,
};
if (typeof customData === 'object') {
console.log('merge');
Object.assign(data, customData);
}
function sendSms() {
quasar.dialog({ quasar.dialog({
component: VnSmsDialog, component: VnSmsDialog,
componentProps: { componentProps: {
phone: '123', phone: phone,
template: 'pendingPayment' template: template,
} locale: client.user.lang,
}) send: sendSms,
data: data,
},
});
}
async function showSmsDialogWithChanges() {
const query = `TicketLogs/${route.params.id}/getChanges`;
const response = await axios.get(query);
showSmsDialog('orderChanges', { changes: response.data });
}
async function sendSms(body) {
await axios.post(`Tickets/${route.params.id}/sendSms`, body);
quasar.notify({
message: 'Notification sent',
type: 'positive',
});
} }
function confirmDelete() { function confirmDelete() {
@ -70,12 +104,16 @@ function confirmDelete() {
} }
async function remove() { async function remove() {
const id = ticket.value.id; const id = route.params.id;
await axios.delete(`Claims/${id}`); await axios.post(`Tickets/${id}/setDeleted`);
quasar.notify({ quasar.notify({
message: t('globals.dataDeleted'), message: t('Ticket deleted'),
type: 'positive', type: 'positive',
icon: 'check', });
quasar.notify({
message: t('You can undo this action within the first hour'),
icon: 'info',
}); });
await router.push({ name: 'TicketList' }); await router.push({ name: 'TicketList' });
} }
@ -83,42 +121,27 @@ async function remove() {
<template> <template>
<q-item v-ripple clickable> <q-item v-ripple clickable>
<q-item-section avatar> <q-item-section avatar>
<q-icon name="summarize" /> <q-icon name="picture_as_pdf" />
</q-item-section> </q-item-section>
<q-item-section>{{ t('Open Delivery Note...') }}</q-item-section> <q-item-section>{{ t('Open Delivery Note...') }}</q-item-section>
<q-item-section side> <q-item-section side>
<q-icon name="keyboard_arrow_right" /> <q-icon name="keyboard_arrow_right" />
</q-item-section> </q-item-section>
<q-menu anchor="top end" self="top start" auto-close> <q-menu anchor="top end" self="top start" auto-close bordered>
<q-list> <q-list>
<q-item @click="openDeliveryNote()" v-ripple clickable> <q-item @click="openDeliveryNote('deliveryNote')" v-ripple clickable>
<q-item-section avatar>
<q-icon name="picture_as_pdf" />
</q-item-section>
<q-item-section>{{ t('With prices') }}</q-item-section> <q-item-section>{{ t('With prices') }}</q-item-section>
</q-item> </q-item>
<q-item @click="openDeliveryNote('withoutPrices')" v-ripple clickable> <q-item @click="openDeliveryNote('withoutPrices')" v-ripple clickable>
<q-item-section avatar> <q-item-section>{{ t('Without prices') }}</q-item-section>
<q-icon name="picture_as_pdf" />
</q-item-section>
<q-item-section>{{ t('Without Prices') }}</q-item-section>
</q-item> </q-item>
<q-item <q-item
@click="openDeliveryNote('deliveryNote', 'csv')" @click="openDeliveryNote('deliveryNote', 'csv')"
v-ripple v-ripple
clickable clickable
> >
<q-item-section avatar>
<q-icon name="picture_as_pdf" />
</q-item-section>
<q-item-section>{{ t('As CSV') }}</q-item-section> <q-item-section>{{ t('As CSV') }}</q-item-section>
</q-item> </q-item>
<q-item @click="confirmPickupOrder" v-ripple clickable>
<q-item-section avatar>
<q-icon name="send" />
</q-item-section>
<q-item-section>{{ t('Send Delivery Note') }}</q-item-section>
</q-item>
</q-list> </q-list>
</q-menu> </q-menu>
</q-item> </q-item>
@ -132,11 +155,11 @@ async function remove() {
</q-item-section> </q-item-section>
<q-menu anchor="top end" self="top start" auto-close> <q-menu anchor="top end" self="top start" auto-close>
<q-list> <q-list>
<q-item @click="sendDeliveryNote()" v-ripple clickable> <q-item @click="sendDeliveryNote('deliveryNote')" v-ripple clickable>
<q-item-section>{{ t('With prices') }}</q-item-section> <q-item-section>{{ t('With prices') }}</q-item-section>
</q-item> </q-item>
<q-item @click="sendDeliveryNote('withoutPrices')" v-ripple clickable> <q-item @click="sendDeliveryNote('withoutPrices')" v-ripple clickable>
<q-item-section>{{ t('Without Prices') }}</q-item-section> <q-item-section>{{ t('Without prices') }}</q-item-section>
</q-item> </q-item>
<q-item <q-item
@click="sendDeliveryNote('deliveryNote', 'csv')" @click="sendDeliveryNote('deliveryNote', 'csv')"
@ -164,48 +187,46 @@ async function remove() {
</q-item-section> </q-item-section>
<q-menu anchor="top end" self="top start" auto-close> <q-menu anchor="top end" self="top start" auto-close>
<q-list> <q-list>
<q-item @click="sendSms()" v-ripple clickable> <q-item @click="showSmsDialog('pendingPayment')" v-ripple clickable>
<q-item-section avatar>
<q-icon name="picture_as_pdf" />
</q-item-section>
<q-item-section>{{ t('Pending payment') }}</q-item-section> <q-item-section>{{ t('Pending payment') }}</q-item-section>
</q-item> </q-item>
<q-item @click="showSmsDialog('minAmount')" v-ripple clickable>
<q-item-section>{{ t('Minimum amount') }}</q-item-section>
</q-item>
<q-item
@click="showSmsDialogWithChanges('orderChanges')"
v-ripple
clickable
>
<q-item-section>{{ t('Order changes') }}</q-item-section>
</q-item>
</q-list> </q-list>
</q-menu> </q-menu>
</q-item> </q-item>
<q-separator /> <template v-if="!ticket.isDeleted">
<q-item @click="confirmDelete()" v-ripple clickable> <q-separator />
<q-item-section avatar> <q-item @click="confirmDelete()" v-ripple clickable>
<q-icon name="delete" /> <q-item-section avatar>
</q-item-section> <q-icon name="delete" />
<q-item-section>{{ t('Delete ticket') }}</q-item-section> </q-item-section>
</q-item> <q-item-section>{{ t('Delete ticket') }}</q-item-section>
</q-item>
</template>
</template> </template>
<i18n> <i18n>
es: es:
Delivery Note: Albarán Open Delivery Note...: Abrir albarán...
Open Delivery Note: Abrir albarán Send Delivery Note...: Enviar albarán...
With prices: Con precios
Without prices: Sin precios
As CSV: Como CSV
Open Proforma Invoice: Abrir factura proforma
Delete ticket: Eliminar ticket Delete ticket: Eliminar ticket
Send SMS...: Enviar SMS
Pending payment: Pago pendiente
Minimum amount: Importe mínimo
Order changes: Cambios del pedido
Ticket deleted: Ticket eliminado
You can undo this action within the first hour: Puedes deshacer esta acción dentro de la primera hora
</i18n> </i18n>
<!--
<i18n>
{
"en": {
"pickupOrder": "Pickup order",
"openPickupOrder": "Open pickup order",
"sendPickupOrder": "Send pickup order",
"deleteClaim": "Delete claim",
"confirmDeletion": "Confirm deletion",
"confirmDeletionMessage": "Are you sure you want to delete this claim?"
},
"es": {
"pickupOrder": "Orden de recogida",
"openPickupOrder": "Abrir orden de recogida",
"sendPickupOrder": "Enviar orden de recogida",
"deleteClaim": "Eliminar reclamación",
"confirmDeletion": "Confirmar eliminación",
"confirmDeletionMessage": "Seguro que quieres eliminar esta reclamación?"
}
}
</i18n> -->