ScheduleAcceptedDialog.vue 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. <template>
  2. <q-dialog ref="dialogRef" @hide="onDialogHide">
  3. <q-card class="accepted-dialog-card bg-surface" :flat="false">
  4. <q-card-section class="column items-center q-pt-lg q-pb-sm">
  5. <q-avatar size="80px" :style="avatarStyle" class="text-h5 q-mb-sm">
  6. {{ schedule.provider_name?.slice(0, 2).toUpperCase() ?? '??' }}
  7. </q-avatar>
  8. <div class="font16 fontbold provider-name">
  9. {{ schedule.provider_name }}
  10. </div>
  11. <div class="font14 fontmedium text-text">
  12. {{ schedule.address?.district || '' }}
  13. </div>
  14. </q-card-section>
  15. <q-card-section class="text-center q-pt-xs q-pb-md">
  16. <div class="accepted-title font16 fontbold">
  17. {{ $t('dashboard_client.pending_schedules.accepted_title') }}
  18. </div>
  19. </q-card-section>
  20. <q-separator />
  21. <q-card-section class="q-py-md q-px-lg">
  22. <div class="detail-row">
  23. <span class="text-primary font14 fontmedium">
  24. {{ $t('dashboard_client.pending_schedules.detail_date') }}
  25. </span>
  26. <span class="detail-value">
  27. {{ formattedDate }}
  28. </span>
  29. </div>
  30. <div class="detail-row">
  31. <span class="text-primary font14 fontmedium">
  32. {{ $t('dashboard_client.pending_schedules.detail_time') }}
  33. </span>
  34. <span class="detail-valued text-text">
  35. {{ schedule.start_time?.slice(0, 5) }} {{ $t('dashboard_client.next_schedules.to') }} {{ schedule.end_time?.slice(0, 5) }}
  36. </span>
  37. </div>
  38. <div class="detail-row">
  39. <span class="text-primary font14 fontmedium">
  40. {{ $t('dashboard_client.pending_schedules.detail_value') }}
  41. </span>
  42. <span class="detail-value">
  43. {{ formatCurrency(baseAmount) }}
  44. </span>
  45. </div>
  46. <q-separator class="q-my-sm" />
  47. <div class="detail-row">
  48. <span class="text-primary font14 fontmedium">
  49. {{ $t('dashboard_client.pending_schedules.detail_pix_total') }}
  50. </span>
  51. <span class="total-value font14 fontbold">
  52. {{ formatCurrency(pixTotal) }}
  53. </span>
  54. </div>
  55. <div v-if="pixDiscount > 0" class="detail-row discount-row">
  56. <span class="font13 fontmedium">
  57. {{ $t('dashboard_client.pending_schedules.detail_pix_discount') }}
  58. </span>
  59. <span class="font13 fontbold">
  60. {{ formatCurrency(pixDiscount) }}
  61. </span>
  62. </div>
  63. <div class="detail-row">
  64. <span class="text-primary font14 fontmedium">
  65. {{ $t('dashboard_client.pending_schedules.detail_credit_card_total') }}
  66. </span>
  67. <span class="total-value font14 fontbold">
  68. {{ formatCurrency(creditCardTotal) }}
  69. </span>
  70. </div>
  71. </q-card-section>
  72. <q-card-section class="q-pt-none q-pb-lg q-px-lg column q-gutter-y-sm">
  73. <q-btn
  74. class="payment-btn full-width"
  75. color="primary"
  76. padding="4px 12px"
  77. rounded
  78. :label="$t('dashboard_client.pending_schedules.btn_payment')"
  79. @click="onGoToPayment"
  80. />
  81. <q-btn
  82. class="full-width"
  83. color="grey-6"
  84. flat
  85. no-caps
  86. padding="4px 12px"
  87. :label="$t('dashboard_client.pending_schedules.btn_cancel')"
  88. @click="onDialogHide"
  89. />
  90. </q-card-section>
  91. </q-card>
  92. </q-dialog>
  93. </template>
  94. <script setup>
  95. import { computed, onMounted } from 'vue'
  96. import { formatCurrency } from 'src/helpers/utils'
  97. import { getSchedulePlatformFeeRate } from 'src/helpers/paymentPlatformFees'
  98. import { useDialogPluginComponent, useQuasar } from 'quasar'
  99. import { usePaymentStore } from 'src/stores/payment'
  100. import { usePaymentPlatformFees } from 'src/composables/usePaymentPlatformFees'
  101. import SchedulePaymentDialog from './SchedulePaymentDialog.vue'
  102. import SchedulePaymentPixDialog from './SchedulePaymentPixDialog.vue'
  103. const props = defineProps({
  104. schedule: { type: Object, required: true }
  105. })
  106. defineEmits([...useDialogPluginComponent.emits])
  107. const { dialogRef, onDialogHide, onDialogOK } = useDialogPluginComponent()
  108. const $q = useQuasar()
  109. const paymentStore = usePaymentStore()
  110. const { platformFees, loadPlatformFees } = usePaymentPlatformFees()
  111. const baseAmount = computed(() => Number(props.schedule.total_amount) || 0)
  112. const creditCardTotal = computed(() => parseFloat((baseAmount.value + platformFee('credit_card')).toFixed(2)))
  113. const formattedDate = computed(() => {
  114. if (props.schedule.formatted_date) return props.schedule.formatted_date
  115. const raw = String(props.schedule.date || '')
  116. const m = raw.match(/^(\d{4})-(\d{2})-(\d{2})/)
  117. if (!m) return raw
  118. const d = new Date(+m[1], +m[2] - 1, +m[3])
  119. return d.toLocaleDateString('pt-BR', { day: '2-digit', month: 'long', year: 'numeric' })
  120. })
  121. const pixDiscount = computed(() => Math.max(0, parseFloat((creditCardTotal.value - pixTotal.value).toFixed(2))))
  122. const pixTotal = computed(() => parseFloat((baseAmount.value + platformFee('pix')).toFixed(2)))
  123. const platformFee = (paymentType) => {
  124. const feeRate = getSchedulePlatformFeeRate(props.schedule, paymentType, platformFees.value)
  125. return parseFloat((baseAmount.value * (feeRate ?? 0)).toFixed(2))
  126. }
  127. const onGoToPayment = () => {
  128. const hasValidPixPayment = !!paymentStore.getValidPixPayment(props.schedule.id)
  129. $q.dialog({
  130. component: hasValidPixPayment ? SchedulePaymentPixDialog : SchedulePaymentDialog,
  131. componentProps: {
  132. schedule: props.schedule, ...(hasValidPixPayment ? { total: pixTotal.value } : {}),
  133. },
  134. }).onOk(() => {
  135. onDialogOK()
  136. })
  137. }
  138. const avatarColors = [
  139. { background: '#ffd5df', color: '#932e57' },
  140. { background: '#d7e8ff', color: '#2158a8' },
  141. { background: '#dfd', color: '#2a7a3b' },
  142. { background: '#ffe5cc', color: '#8a4500' },
  143. ]
  144. const avatarStyle = computed(() => avatarColors[props.schedule.id % avatarColors.length])
  145. onMounted(() => {
  146. loadPlatformFees().catch(() => {})
  147. })
  148. </script>
  149. <style scoped lang="scss">
  150. .accepted-dialog-card {
  151. width: 320px;
  152. max-width: 92vw;
  153. border-radius: 20px !important;
  154. overflow: hidden;
  155. }
  156. .accepted-title {
  157. line-height: 1.3;
  158. color: #8B5CF6;
  159. }
  160. .provider-name {
  161. color: #8B5CF6;
  162. }
  163. .detail-row {
  164. display: flex;
  165. justify-content: space-between;
  166. align-items: center;
  167. padding: 4px 0;
  168. }
  169. .detail-label {
  170. color: #8a8a9a;
  171. }
  172. .detail-value {
  173. color: #3a3a4a;
  174. }
  175. .total-value {
  176. color: #3a3a4a;
  177. }
  178. .discount-row {
  179. color: #16845d;
  180. background: rgba(22, 132, 93, 0.08);
  181. border-radius: 8px;
  182. margin: 2px 0;
  183. padding: 5px 8px;
  184. }
  185. .payment-btn {
  186. height: 48px;
  187. }
  188. </style>