| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- <template>
- <q-dialog ref="dialogRef" @hide="onDialogHide">
- <q-card class="q-dialog-plugin" style="width: 700px; max-width: 90vw">
- <DefaultDialogHeader :title="title" @close="onDialogCancel" />
- <q-form ref="formRef" @submit="onOKClick">
- <q-card-section class="row q-col-gutter-sm">
- <q-input
- v-model="form.card_number"
- :label="$t('client_payment_methods.card_number')"
- :rules="[inputRules.required, validateCardNumber]"
- :error="!!serverErrors?.card_number"
- :error-message="serverErrors?.card_number"
- mask="**** **** **** ####"
- unmasked-value
- class="col-12"
- @update:model-value="onCardNumberChange"
- >
- <template #append>
- <q-icon name="mdi-credit-card-outline" />
- </template>
- </q-input>
- <q-input
- v-model="form.holder_name"
- :label="$t('client_payment_methods.holder_name')"
- :rules="[inputRules.required]"
- :error="!!serverErrors?.holder_name"
- :error-message="serverErrors?.holder_name"
- class="col-12"
- @update:model-value="serverErrors.holder_name = null"
- />
- <q-input
- v-model="form.expiration"
- :label="$t('client_payment_methods.expiration')"
- :rules="[inputRules.required, validateExpiration]"
- :error="!!serverErrors?.expiration"
- :error-message="serverErrors?.expiration"
- mask="##/####"
- placeholder="MM/YYYY"
- class="col-md-6 col-12"
- @update:model-value="serverErrors.expiration = null"
- >
- <template #append>
- <q-icon name="mdi-calendar-outline" />
- </template>
- </q-input>
- <q-input
- v-model="form.cvv"
- :label="$t('client_payment_methods.cvv')"
- :rules="[inputRules.required]"
- :error="!!serverErrors?.cvv"
- :error-message="serverErrors?.cvv"
- mask="####"
- unmasked-value
- type="password"
- class="col-md-6 col-12"
- @update:model-value="serverErrors.cvv = null"
- >
- <template #append>
- <q-icon name="mdi-lock-outline" />
- </template>
- </q-input>
- <q-input
- v-model="form.card_name"
- :label="$t('client_payment_methods.card_name')"
- :error="!!serverErrors?.card_name"
- :error-message="serverErrors?.card_name"
- class="col-md-6 col-12"
- @update:model-value="serverErrors.card_name = null"
- />
- <q-input
- v-model="form.brand"
- :label="$t('client_payment_methods.brand')"
- :error="!!serverErrors?.brand"
- :error-message="serverErrors?.brand"
- readonly
- class="col-md-6 col-12"
- >
- <template #append>
- <q-icon name="mdi-credit-card-check-outline" />
- </template>
- </q-input>
- <q-checkbox
- v-model="form.is_active"
- :label="$t('client_payment_methods.is_active')"
- class="col-12"
- />
- </q-card-section>
- <q-card-actions align="right">
- <q-btn
- flat
- :label="$t('common.actions.cancel')"
- color="negative"
- @click="onDialogCancel"
- />
- <q-btn
- type="submit"
- :label="$t('common.actions.save')"
- :loading="loading"
- :disable="!hasUpdatedFields"
- color="primary"
- />
- </q-card-actions>
- </q-form>
- </q-card>
- </q-dialog>
- </template>
- <script setup>
- import { ref, onMounted } from 'vue'
- import { useDialogPluginComponent } from 'quasar'
- import { useI18n } from 'vue-i18n'
- import { useFormUpdateTracker } from 'src/composables/useFormUpdateTracker'
- import { useSubmitHandler } from 'src/composables/useSubmitHandler'
- import {
- createClientPaymentMethod,
- updateClientPaymentMethod
- } from 'src/api/clientPaymentMethod'
- import DefaultDialogHeader from 'src/components/defaults/DefaultDialogHeader.vue'
- import { useInputRules } from 'src/composables/useInputRules'
- import {
- validateCardNumberLuhn,
- detectCardBrand,
- validateCardExpiration
- } from 'src/helpers/utils'
- const props = defineProps({
- paymentMethod: {
- type: Object,
- default: null
- },
- clientId: {
- type: Number,
- required: true
- },
- title: {
- type: Function,
- default: () => ''
- }
- })
- defineEmits([...useDialogPluginComponent.emits])
- const { t } = useI18n()
- const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent()
- const { inputRules } = useInputRules()
- const formRef = ref(null)
- const { form, getUpdatedFields, hasUpdatedFields } = useFormUpdateTracker({
- client_id: props.paymentMethod ? props.paymentMethod.client_id : props.clientId,
- card_number: props.paymentMethod ? props.paymentMethod.card_number : null,
- holder_name: props.paymentMethod ? props.paymentMethod.holder_name : null,
- expiration: props.paymentMethod ? props.paymentMethod.expiration : null,
- cvv: props.paymentMethod ? props.paymentMethod.cvv : null,
- card_name: props.paymentMethod ? props.paymentMethod.card_name : null,
- brand: props.paymentMethod ? props.paymentMethod.brand : null,
- last_four_digits: props.paymentMethod ? props.paymentMethod.last_four_digits : null,
- is_active: props.paymentMethod ? props.paymentMethod.is_active : true
- })
- const {
- loading,
- serverErrors,
- execute: submitForm,
- } = useSubmitHandler({
- onSuccess: () => onDialogOK(true),
- formRef: formRef,
- })
- const validateCardNumber = (val) => {
- if (!val) return true
- if (!validateCardNumberLuhn(val)) {
- return t('client_payment_methods.invalid_card_number')
- }
- return true
- }
- const validateExpiration = (val) => {
- if (!val) return true
- if (!validateCardExpiration(val)) {
- return t('client_payment_methods.expired_card')
- }
- return true
- }
- const onCardNumberChange = () => {
- serverErrors.card_number = null
-
- if (form.card_number && form.card_number.length >= 6 && !form.card_number.includes('*')) {
- const brand = detectCardBrand(form.card_number)
- if (brand) {
- form.brand = brand
- }
-
- const digits = form.card_number.replace(/\D/g, '')
- if (digits.length >= 4) {
- form.last_four_digits = digits.slice(-4)
- }
- }
- }
- const onOKClick = async () => {
- if (props.paymentMethod) {
- await submitForm(() => updateClientPaymentMethod(props.paymentMethod.id, getUpdatedFields.value))
- } else {
- await submitForm(() => createClientPaymentMethod({ ...form }))
- }
- }
- onMounted(() => {
- if (props.clientId) {
- form.client_id = props.clientId
- }
- })
- </script>
|