| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304 |
- <template>
- <q-dialog ref="dialogRef" persistent maximized transition-show="slide-up" transition-hide="slide-down" @hide="onDialogHide">
- <div class="bg-page full-height column">
- <div class="row items-center q-px-md q-pt-md q-pb-sm bg-surface shadow-header">
- <q-btn icon="mdi-chevron-left" flat round dense color="primary" @click="onDialogCancel" />
- <q-space />
- <span class="text-subtitle1 text-weight-bold text-primary">
- {{ $t('payment.title') }}
- </span>
- <q-space />
- <div style="width: 32px" />
- </div>
- <div class="col overflow-auto q-px-md q-pt-lg q-pb-xl">
- <div class="section-label q-mb-sm">{{ $t('payment.schedule_address') }}</div>
- <div class="address-box row items-center no-wrap q-mb-lg">
- <div class="col">
- <div class="address-type-label">{{ addressTypeLabel }}</div>
- <div class="address-full-text text-grey-7">{{ addressFullText }}</div>
- </div>
- <q-icon name="mdi-chevron-down" color="grey-5" size="22px" />
- </div>
- <div class="section-label q-mb-sm">{{ $t('payment.pay_with') }}</div>
- <div class="row q-gutter-sm q-mb-sm">
- <div
- class="payment-option-card col column items-center justify-center q-pa-md cursor-pointer"
- :class="{ 'payment-option-selected': selectedMethod === 'pix' }"
- @click="selectedMethod = 'pix'"
- >
- <span class="payment-option-title">{{ $t('payment.pix') }}</span>
- <q-icon name="mdi-cash-fast" size="32px" color="teal" class="q-mt-xs" />
- </div>
- <div
- class="payment-option-card col column items-center justify-center q-pa-md cursor-pointer"
- :class="{ 'payment-option-selected': selectedMethod === 'new_card' }"
- @click="openAddCard"
- >
- <span class="payment-option-title">{{ $t('payment.add_card') }}</span>
- <q-icon name="mdi-plus-circle-outline" size="22px" color="grey-5" class="q-mt-xs" />
- <span class="payment-option-sub">{{ $t('payment.credit_debit') }}</span>
- </div>
- </div>
- <div v-if="loadingCards" class="flex flex-center q-py-md">
- <q-spinner color="primary" size="2em" />
- </div>
- <div v-else-if="paymentMethods.length > 0" class="column q-gutter-y-sm q-mb-lg">
- <div
- v-for="card in paymentMethods"
- :key="card.id"
- class="saved-card-box row items-center no-wrap q-pa-md cursor-pointer"
- :class="{ 'payment-option-selected': selectedMethod === `card_${card.id}` }"
- @click="selectedMethod = `card_${card.id}`"
- >
- <div class="col column">
- <span class="card-titular-label">{{ $t('payment.card_holder') }}</span>
- <span class="card-holder-name">{{ card.holder_name }}</span>
- </div>
- <div class="column items-end">
- <span class="card-brand-text">{{ brandDisplay(card.brand) }}</span>
- <span class="card-last-four">{{ '**** **** **** ' + card.last_four_digits }}</span>
- <span class="card-expiry-text">{{ card.expiration }}</span>
- </div>
- </div>
- </div>
- <q-separator class="q-my-lg" />
- <div class="row items-center q-mb-lg">
- <q-checkbox v-model="agreedToTerms" color="primary" keep-color />
- <span class="terms-text">
- {{ $t('payment.agree_prefix') }}
- <span class="text-primary text-weight-bold cursor-pointer text-underline">{{ $t('payment.terms_link') }}</span>
- </span>
- </div>
- <q-btn
- unelevated
- rounded
- no-caps
- color="primary"
- class="full-width confirm-btn"
- :label="$t('payment.confirm_btn')"
- :disable="!canConfirm"
- @click="onConfirm"
- />
- </div>
- </div>
- </q-dialog>
- </template>
- <script setup>
- import { ref, computed, onMounted } from 'vue'
- import { useDialogPluginComponent, useQuasar } from 'quasar'
- import { useI18n } from 'vue-i18n'
- import { userStore } from 'src/stores/user'
- import { getClientPaymentMethods } from 'src/api/clientPaymentMethod'
- import ProfilePaymentAddDialog from 'src/components/profile/ProfilePaymentAddDialog.vue'
- import SchedulePaymentPixDialog from './SchedulePaymentPixDialog.vue'
- import SchedulePaymentProcessingDialog from './SchedulePaymentProcessingDialog.vue'
- const props = defineProps({
- schedule: {
- type: Object,
- required: true,
- },
- total: {
- type: Number,
- required: true,
- },
- })
- defineEmits([...useDialogPluginComponent.emits])
- const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent()
- const $q = useQuasar()
- const { t } = useI18n()
- const store = userStore()
- const selectedMethod = ref(null)
- const agreedToTerms = ref(false)
- const paymentMethods = ref([])
- const loadingCards = ref(false)
- const addressTypeLabel = computed(() => {
- const type = props.schedule.address?.address_type
- if (!type) return ''
- return t(`profile.address.type.${type}`, type)
- })
- const addressFullText = computed(() => {
- const a = props.schedule.address
- if (!a) return ''
- const parts = [a.address, a.number, a.district].filter(Boolean)
- return parts.join(', ')
- })
- const canConfirm = computed(() => selectedMethod.value !== null && agreedToTerms.value)
- const brandDisplay = (brand) => {
- if (!brand) return ''
- const map = { visa: 'VISA', mastercard: 'Mastercard', elo: 'Elo', hipercard: 'Hipercard', diners: 'Diners', discover: 'Discover' }
- return map[brand] ?? brand.toUpperCase()
- }
- const loadCards = async () => {
- loadingCards.value = true
- try {
- paymentMethods.value = await getClientPaymentMethods(store.user?.client_id)
- } catch (e) {
- console.error(e)
- } finally {
- loadingCards.value = false
- }
- }
- const openAddCard = () => {
- $q.dialog({
- component: ProfilePaymentAddDialog,
- componentProps: { clientId: store.user?.client_id },
- }).onOk(() => {
- loadCards()
- })
- }
- const onConfirm = () => {
- if (selectedMethod.value === 'pix') {
- $q.dialog({
- component: SchedulePaymentPixDialog,
- componentProps: { schedule: props.schedule, total: props.total },
- }).onOk(() => {
- onDialogOK()
- })
- } else {
- $q.dialog({
- component: SchedulePaymentProcessingDialog,
- componentProps: { schedule: props.schedule },
- }).onOk(() => {
- onDialogOK()
- })
- }
- }
- onMounted(() => {
- loadCards()
- })
- </script>
- <style scoped lang="scss">
- .shadow-header {
- box-shadow: 0px 1px 8px rgba(0, 0, 0, 0.1);
- }
- .section-label {
- font-size: 15px;
- font-weight: 700;
- color: #3a3a4a;
- }
- .address-box {
- border: 1px solid #e0e0e0;
- border-radius: 10px;
- padding: 12px 16px;
- background: #fff;
- }
- .address-type-label {
- font-size: 14px;
- font-weight: 700;
- color: #3a3a4a;
- }
- .address-full-text {
- font-size: 12px;
- line-height: 1.4;
- margin-top: 2px;
- }
- .payment-option-card {
- border: 1.5px solid #e0e0e0;
- border-radius: 12px;
- background: #fff;
- min-height: 90px;
- text-align: center;
- transition: border-color 0.2s;
- }
- .payment-option-selected {
- border-color: #22c55e !important;
- box-shadow: 0 0 0 1px #22c55e;
- }
- .payment-option-title {
- font-size: 15px;
- font-weight: 700;
- color: #3a3a4a;
- }
- .payment-option-sub {
- font-size: 11px;
- color: #9a9aaa;
- margin-top: 2px;
- }
- .saved-card-box {
- border: 1.5px solid #e0e0e0;
- border-radius: 12px;
- background: #fff;
- transition: border-color 0.2s;
- }
- .card-titular-label {
- font-size: 11px;
- color: #9a9aaa;
- }
- .card-holder-name {
- font-size: 14px;
- font-weight: 600;
- color: #3a3a4a;
- }
- .card-brand-text {
- font-size: 14px;
- font-weight: 700;
- color: #3a3a4a;
- text-align: right;
- }
- .card-last-four {
- font-size: 13px;
- color: #6a6a7a;
- letter-spacing: 1px;
- }
- .card-expiry-text {
- font-size: 12px;
- color: #9a9aaa;
- }
- .terms-text {
- font-size: 13px;
- color: #5a5a6a;
- line-height: 1.4;
- }
- .text-underline {
- text-decoration: underline;
- }
- .confirm-btn {
- font-size: 16px;
- font-weight: 700;
- height: 52px;
- }
- </style>
|