Untitled
unknown
javascript
a year ago
26 kB
4
Indexable
<template> <LayoutPage> <PageTitle :title-page="$t('catalog.title')" /> <Alert v-if="errorResponse" type="danger" :is-dismissible="true" :text="responseMsg" @handle-close="errorResponse = false" style="margin:-20px 0 -40px 0; z-index:1;" /> <div v-if="generalLoading" class="d-flex justify-content-center align-items-center vh-100"> <SpinnerLoading /> </div> <div v-else :class="['card p-3', { 'block-section': errorResponse }]"> <div class="card-body"> <div class="row mb-3"> <div class="col-12 bg-soft-primary rounded py-3 px-4 mb-5"> <template v-if="showCVComponent"> <template v-if="isVerifiedNumberSuccess"> <div class="container pt-3 pb-5"> <div class="row justify-content-center"> <div class="col-12 col-md-6 text-center"> <i class="fas fa-check fa-3x text-success"></i> <p class="fw-bold mt-4"> {{ $t('verifiedNumber.verifiedTitle', { value: verifiedResponse.phoneNumber }) }} </p> <p class="text-muted"> {{ $t('verifiedNumber.verifiedMsg') }} </p> <p> <button type="button" class="btn btn-deep px-3" @click="initCFComponent" > {{ $t('buttons.accept')}} </button> </p> </div> </div> </div> </template> <template v-else-if="isVerifiedNumberError"> <div class="container pt-3 pb-5"> <div class="row justify-content-center"> <div class="col-12 col-md-6 text-center"> <i class="far fa-times-circle fa-3x text-danger"></i> <p class="fw-bold mt-4"> {{ $t('verifiedNumber.notVerifiedTitle', { value: verifiedResponse.phoneNumber }) }} </p> <p class="text-muted" v-html="$t('verifiedNumber.notVerifiedMsg', { value: 'support@flynode.mx' })"></p> <p> <button type="button" class="btn btn-deep px-3" @click="initCFComponent" > {{ $t('buttons.accept')}} </button> </p> </div> </div> </div> </template> <template v-else> <CodeVerification :statusResponse="verifiedResponse" @onAlert="triggerModal" :label="verifiedNumberLabels" :submitCode="verifyCode" v-model="verificationCode" :sendNew="sendNewCode" /> </template> </template> <template v-else> <h5 class="mb-3">{{$t('verifiedNumber.verifyNewNumber')}}</h5> <div class="row gx-5"> <div class="col-md-3 mb-5"> <Multiselect v-model="verifyModel.country_code_id" :placeholder="$t('selectOption')" track-by="country_name" valueProp="country_code" label="country_name" :options="countryCodes" @open="initGetCountryCodes" @change="handleChange" :searchable="true" :noOptionsText="$t('loading')" :class="['multiselect-custom', { 'border-danger': errors.country_code_id }]" > <template v-slot:singlelabel="{ value }"> <div class="multiselect-single-label"> <img class="option-image" :src="`${urlAssets}/${value.path_to_flag}`" width="40" height="24" style="width:40px; height:24px;"> ({{value.country_code}}) {{ value.country_name }} </div> </template> <template v-slot:option="{ option }"> <img class="option-image" :src="`${urlAssets}/${option.path_to_flag}`" width="40" height="24" style="width:40px; height:24px;">({{option.country_code}} ) {{ option.country_name }} </template> </Multiselect> <div v-if="errors.country_code_id" class="invalid-feedback d-block">{{errors.country_code_id}}</div> </div> <div v-if="togglePhoneType" class="col-md-4 mb-5"> <select :class="['form-select bg-white', { 'is-invalid': errors.type }]" v-model="verifyModel.type"> <option value="null" disabled>{{$t('selectOption')}}</option> <option value="mobile">{{$t('verifiedNumber.mobile')}}</option> <option value="permanent">{{$t('verifiedNumber.permanent')}}</option> </select> <div v-if="errors.type" class="invalid-feedback">{{errors.type}}</div> </div> <div class="col-md-4 mb-5"> <input type="text" :class="['form-control bg-white', { 'is-invalid': errors.phone_number }]" :placeholder="$t('verifiedNumber.addPhoneNumber')" v-model="verifyModel.phone_number"/> <div v-if="errors.phone_number" class="invalid-feedback">{{errors.phone_number}}</div> </div> </div> <div class="text-center"> <button type="button" class="btn btn-orange text-white fw-bold" @click="submitVerificationCode" :disabled="isSubmitLoading" > <template v-if="isSubmitLoading"> <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> <span class="visually-hidden">Loading...</span> </template> {{$t('verifiedNumber.sendVerificationCode')}}</button> <div class="d-inline ms-2"> <CustomTooltip placement="right" data-bs-html="true" :title="`<div>${$t('verifiedNumber.sendTooltip')}</div>`" /> </div> </div> </template> </div> <div class="col-md-9"> <h5 class="fw-bold">{{$t('verifiedNumber.title')}}</h5> </div> <div class="col-md-3 text-end d-none"> <router-link class="btn btn-orange text-white fw-bold px-4" :to="{name: 'NewVerifiedNumbers'}" > <i class="fas fa-plus fa-sm"></i> {{$t('verifiedNumber.newOption')}} </router-link> </div> </div> <DataTable :url="NUMBERS_URL" :columns="tableColumnsName" :search-input="true" @emitData="emitTableData" > <tbody> <tr v-if="data.length === 0"> {{$t('table.noResultsFound')}} </tr> <tr v-else v-for="(item, key) in data" :key="key" class="text-center"> <td>{{ item.id }}</td> <td>{{ item.number }}</td> <td>{{ item.type }}</td> <td>{{ item.country_name }}</td> <td> <CBadge :colorClass="statusClass(item.status_code)"> {{$t(`status.${item.status}`)}} </CBadge> </td> <td class="d-flexjustify-content-center"> <template v-if="item.block_edit"> <span class="btn btn-primary btn-sm mx-1" style="cursor:default;"> <i class="fas fa-lock"></i> </span> </template> <template v-else> <router-link class="btn btn-primary btn-sm mx-1 d-none" :to="{name: 'EditVerifiedNumbers', params: {id: item.id} }"> <i class="fas fa-pen"></i> </router-link> <button class="btn btn-primary btn-sm mx-1" @click="deleteRow(item)"> <i class="fas fa-trash"></i> </button> </template> </td> </tr> </tbody> </DataTable> </div> </div> </LayoutPage> </template> <script setup> import LayoutPage from '@/components/LayoutPage.vue'; import PageTitle from '@/components/PageTitle.vue'; import Alert from '@/components/Alert.vue'; import SingleAccordion from '@/components/SingleAccordion'; import CustomTooltip from "@/components/CustomTooltip.vue"; import flatPickr from 'vue-flatpickr-component'; import Multiselect from '@vueform/multiselect'; import CodeVerification from '@/components/CodeVerification.vue'; import CBadge from '@/components/CBadge.vue'; import Swal from 'sweetalert2'; import DataTable from '@/components/organismos/DataTable'; import i18n from "@/i18n"; import { reactive, ref, onMounted } from 'vue'; import {useRouter} from 'vue-router'; import axios from 'axios'; import { throwErrorMessage } from '@/composables/helper'; import {filter} from '@/validators/catalogueSchema.js'; import {getCountryCodes} from '@/composables/sip'; import {verifySchema, verifiedNumbers} from '@/validators/verifyNumberSchema.js'; const generalLoading = ref(false);//temporaly waiting for services const filterData = ref({}); const filterErrors = ref({}); const data = ref([]); const deleteSuccess = ref(false); const selectedStatus = ref({}); const showCVComponent = ref(false); const isVerifiedNumberSuccess = ref(null), isVerifiedNumberError = ref(null); const status = ref([]); const errorResponse = ref(false); const responseMsg = ref(''); const errors = ref({}); const urlAssets = process.env.VUE_APP_ASSETS_URL; const togglePhoneType = ref(false); const verifiedResponse = reactive({ status:true, statusName: "sending", phoneNumber: null }); const countryCodes = ref([]); const verifyModel = ref({}); const isSubmitLoading = ref(false); const router = useRouter(); const NUMBERS_URL = ref(`${process.env.VUE_APP_API_URL}/api/dids/status/`); const tableColumnsName = ref([ { label: '#', name: 'id', sort: false }, { label: i18n.global.t('table.number'), name: 'number', sort: false }, { label: i18n.global.t('table.type'), name: 'type', sort: true }, { label: i18n.global.t('country'), name: 'country', sort: true }, { label: i18n.global.t('table.status'), name: 'status', sort: true }, { label: i18n.global.t('table.actions'), name: 'actions', sort: false }, ]); const attempts = ref(3); const verificationCode = ref(""); const verifiedNumberLabels = reactive({ titleLabel : i18n.global.t('verifiedNumber.component.titleLabel'), instructionLabel: i18n.global.t('verifiedNumber.component.instructionLabel'), verificationCodeLabel: i18n.global.t('verifiedNumber.component.verificationCodeLabel'), verifyButtonLabel: i18n.global.t('verifiedNumber.component.verifyButtonLabel'), cancelButtonLabel: i18n.global.t('verifiedNumber.component.cancelButtonLabel'), codeNotReceivedLabel: i18n.global.t('verifiedNumber.component.codeNotReceivedLabel'), clickHereLabel: i18n.global.t('verifiedNumber.component.clickHereLabel'), codeSentLabel: i18n.global.t('verifiedNumber.component.codeSentLabel'), sendNewCodeLabel: i18n.global.t('verifiedNumber.component.sendNewCodeLabel'), attemptsLabel: i18n.global.t('verifiedNumber.component.attemptsLabel', { attempts: attempts.value}) }); const emitTableData = (source) => data.value = source; const getStatus = async() => { try { const { data: { data: {payload} } } = await axios.get(`${process.env.VUE_APP_API_URL}/api/status/`); status.value = payload.filter( e => (e.status == 'inactive' || e.status == 'active' || e.status == 'eliminated' ? e : null)); } catch (error) { console.log(error) } } const statusClass = (val) => { return val == 1 ? 'bg-success' : val == 0 ? 'bg-danger' : 'bg-secondary'; }; const handleChange = (option) => { if(option && option == 52) { return togglePhoneType.value = false; } if(option && option != 52) return togglePhoneType.value = true; } const filterDataTable = async() => { try { filterErrors.value = {}; await filter.validate( filterData.value, { abortEarly: false }); DIDSTATUS_URL.value = `${process.env.VUE_APP_API_URL}/api/dids/status/?date_from=${filterData.value.startDate} 00:00:00&date_to=${filterData.value.endDate} 23:59:59&filter_key=status&filter_key_value=${filterData.value.status}`; } catch (error) { console.log(error); if(error?.name == 'ValidationError'){ error.inner.forEach((err) => { filterErrors.value = { ...filterErrors.value, [err.path]: i18n.global.t(err.message) }; }); }else{ const errorMsg = throwErrorMessage(error); errorResponse.value = true; responseMsg.value = errorMsg; } } }; const deleteRow = async(obj) => { const {id,status_name} = obj; Swal.fire({ html: ` <i class="fas fa-times-circle fa-3x text-danger mt-4 mb-2"></i> <p class="text-muted fw-bold mt-3 mb-2">${i18n.global.t('deleteQuestion', { value: status_name })}</p>`, showCancelButton: true, cancelButtonText: i18n.global.t('buttons.cancel'), confirmButtonText: i18n.global.t('buttons.accept'), showCloseButton: true, reverseButtons: true, customClass: { cancelButton: 'btn btn-soft-secondary fw-bold px-5 mx-3', confirmButton: 'btn btn-primary fw-bold px-5 mx-3', htmlContainer: 'container fs-6', validationMessage: 'bg-soft-danger' }, buttonsStyling: false, preConfirm: async() => { try { const response = await axios.delete(`${process.env.VUE_APP_API_URL}/api/dids/status/${id}`); deleteSuccess.value = true; } catch (error) { console.log(error); Swal.showValidationMessage( `Request failed: ${error}` ) } } }).then((result) => { if (result.isConfirmed) { Swal.fire({ html: ` <i class="fas fa-check-circle fa-3x text-success mt-4 mb-2"></i> <p class="text-muted fw-bold mt-3 mb-2">${i18n.global.t('deleteSuccessMsg')}</p>`, confirmButtonText: i18n.global.t('buttons.exit'), showCloseButton: true, customClass: { confirmButton: 'btn btn-soft-secondary fw-bold px-5 mx-3', htmlContainer: 'container fs-6', }, buttonsStyling: false }).then((result) => { if (result.isDismissed || result.isConfirmed) { router.go(); } }) } }) } const triggerModal = () => { Swal.fire({ iconHtml: '<i class="far fa-times-circle text-danger"></i>', html: ` <h5 class="text-danger text-center">${i18n.global.t('verifiedNumber.noConnectionTitle')}</h5> <p class="text-secondary text-start fs-6 mt-3 fst-italic mb-0">${i18n.global.t('verifiedNumber.noConnectionMsg')}</p>`, showCancelButton: true, cancelButtonText: i18n.global.t('buttons.cancel'), confirmButtonText: i18n.global.t('buttons.call'), showCloseButton: false, reverseButtons: true, allowOutsideClick: false, customClass: { icon: 'border-0', cancelButton: 'btn btn-secondary fw-bold w-150px mx-3', confirmButton: 'btn btn-primary fw-bold w-150px mx-3', actions: '', validationMessage: 'bg-soft-danger', htmlContainer: 'mt-0', }, buttonsStyling: false, preConfirm: async() => { try { // const response = await axios.put(`${process.env.VUE_APP_API_URL}/api/[endpoint]/${id}`); // updateSuccess.value = true; } catch (error) { console.log(error); Swal.showValidationMessage( `Request failed: ${error}` ) } } }).then((result) => { console.log(result); if (result.isConfirmed){ console.log("running something behind!") /* Swal.fire({ html: ` <p class="text-muted fw-bold mt-3 mb-2">success</p>`, confirmButtonText: 'Exit', showCloseButton: true, customClass: { confirmButton: 'btn btn-secondary fw-bold px-5 mx-3', actions: 'd-flex justify-content-end me-5 ms-0', }, buttonsStyling: false }) */ }else{ console.log(result.isConfirmed, result.dismiss) isVerifiedNumberError.value = true; } }) }; const submitVerificationCode = async() => { try { errors.value = {}; isSubmitLoading.value = true; const savedNumbers = ["2291387761","2291345567"]; await verifySchema.validate( verifyModel.value, { abortEarly: false }); await verifiedNumbers(savedNumbers).validate({'phone_number': verifyModel.value.phone_number}, { abortEarly: false}); //send initial code testCodeSent(true) } catch (error) { isSubmitLoading.value = false; if(error?.name == 'ValidationError'){ error.inner.forEach((err) => { errors.value = { ...errors.value, [err.path]: i18n.global.t(err.message) }; }); }else{ const errorMsg = throwErrorMessage(error); errorResponse.value = true; responseMsg.value = errorMsg; } } } const testCodeSent = (val) => { isSubmitLoading.value = true; setTimeout(() =>{ isSubmitLoading.value = false; showCVComponent.value = true; },1600); setTimeout(() => { verifiedResponse.phoneNumber = verifyModel.value.phone_number; if(val){ verifiedResponse.status = true; verifiedResponse.statusName = 'sent'; }else{ verifiedResponse.status = false; verifiedResponse.statusName = 'notSent'; } }, 15000); } const verifyCode = () => { const code = 3316; let count = 0; // compare the verification code, count the failed captured attempts console.log(`captured code: ${verificationCode.value}, attempts ${attempts.value}`); if(verificationCode.value != code && attempts.value > 0){ attempts.value = attempts.value - 1; verifiedNumberLabels.attemptsLabel = i18n.global.t('verifiedNumber.component.attemptsLabel', { attempts: attempts.value}); verifiedResponse.status = true; verifiedResponse.statusName = 'failed_attempts'; if(attempts.value == 0) { attempts.value = 3; isVerifiedNumberError.value = true; } }else{ isVerifiedNumberSuccess.value = true; attempts.value = 3; verifyModel.value.phoneNumber = ""; } } const sendNewCode = () => { console.log("reset status") verificationCode.value = ""; verifiedResponse.status = true; verifiedResponse.statusName = 'sending'; verifiedResponse.phoneNumber = verifyModel.value.phone_number; setTimeout(() => { verifiedResponse.statusName = 'sent'; }, 14000); } const initCFComponent = () => { attempts.value = 3; showCVComponent.value = false; isVerifiedNumberSuccess.value = false; isVerifiedNumberError.value = false; verificationCode.value = ""; verifyModel.value = {}; togglePhoneType.value = false; } //const loading = ref(true); const initGetCountryCodes = async() => { if(countryCodes.value.length > 0) return; const { data : { data: {payload} } } = await getCountryCodes(); countryCodes.value = payload; loading.value = false; } getStatus(); /* getCountryCodes() .then(e => { countryCodes.value = e.data.data.payload; }).catch(error => { console.warn(error) }); */ </script> <style src="@vueform/multiselect/themes/default.css"></style> <style> .w-150px { width: 150px!important; } </style> <style scoped> .multiselect-custom { --ms-tag-bg: #2A7DDC; --ms-option-bg-selected-pointed: #2A7DDC; } .custom-input-group.with-icon>i { -webkit-transform: translateY(-50%); -ms-transform: translateY(-50%); transform: translateY(-50%); pointer-events: none; position: absolute; z-index: 3; top: 50%; } .custom-input-group.with-icon>i, .custom-input-group.with-icon.icon-right>i{ right: .7rem; } .custom-input-group.with-icon>input, .custom-input-group.with-icon.icon-right>input { padding-right: 1.7rem; } /* .swal2-actions{ justify-content: end !important; margin: 1.25em 2rem 0px; } */ .swal2-html-container{ padding-right: 3rem !important; padding-left: 3rem !important; } </style>
Editor is loading...