|
@@ -2,7 +2,109 @@
|
|
|
<q-dialog ref="dialogRef" @hide="onDialogHide">
|
|
<q-dialog ref="dialogRef" @hide="onDialogHide">
|
|
|
<q-card class="solicitation-dialog-card bg-surface shadow-card" :flat="false">
|
|
<q-card class="solicitation-dialog-card bg-surface shadow-card" :flat="false">
|
|
|
|
|
|
|
|
- <template v-if="view === 'details'">
|
|
|
|
|
|
|
+ <div class="text-text">
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <template v-if="isPaid">
|
|
|
|
|
+ <div class="row justify-end q-pt-sm q-pr-sm">
|
|
|
|
|
+ <q-btn flat round dense icon="close" color="grey-6" size="sm" @click="onDialogCancel" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <q-card-section class="column items-center q-pt-xs q-pb-xs">
|
|
|
|
|
+ <q-avatar size="72px" class="q-mb-sm">
|
|
|
|
|
+ <img :src="solicitation.customer_photo || 'https://cdn.quasar.dev/img/avatar.png'" />
|
|
|
|
|
+ </q-avatar>
|
|
|
|
|
+ <div class="text-subtitle1 text-weight-bold text-text">
|
|
|
|
|
+ {{ solicitation.client_name }}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div v-if="solicitation.address?.district" class="text-caption text-grey-6 q-mt-xs">
|
|
|
|
|
+ {{ solicitation.address.district }}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div v-if="solicitation.address" class="text-caption text-grey-5 text-center q-mt-xs">
|
|
|
|
|
+ {{ formatAddressShort(solicitation.address) }}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </q-card-section>
|
|
|
|
|
+
|
|
|
|
|
+ <q-separator class="q-mx-md q-my-xs" />
|
|
|
|
|
+
|
|
|
|
|
+ <q-card-section class="q-pt-xs q-pb-xs q-px-lg">
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-for="service in servicesList"
|
|
|
|
|
+ :key="service.label"
|
|
|
|
|
+ class="row items-center q-gutter-x-sm q-mb-xs"
|
|
|
|
|
+ >
|
|
|
|
|
+ <q-icon
|
|
|
|
|
+ :name="service.active ? 'mdi-check' : 'mdi-close'"
|
|
|
|
|
+ :color="service.active ? 'secondary' : 'grey-4'"
|
|
|
|
|
+ size="16px"
|
|
|
|
|
+ />
|
|
|
|
|
+ <span class="text-body2 text-grey-8">{{ service.label }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </q-card-section>
|
|
|
|
|
+
|
|
|
|
|
+ <q-separator class="q-mx-md q-my-xs" />
|
|
|
|
|
+
|
|
|
|
|
+ <q-card-section class="q-pt-xs q-pb-xs q-px-lg">
|
|
|
|
|
+ <div class="detail-row">
|
|
|
|
|
+ <span class="detail-label text-secondary text-weight-bold">{{ $t('common.terms.date') }}</span>
|
|
|
|
|
+ <span class="detail-value text-grey-9">{{ fullDateLabel }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="detail-row">
|
|
|
|
|
+ <span class="detail-label text-secondary text-weight-bold">{{ $t('common.terms.hour') }}</span>
|
|
|
|
|
+ <span class="detail-value text-grey-9">
|
|
|
|
|
+ {{ solicitation.start_time?.slice(0, 5) }} {{ $t('common.to') }} {{ solicitation.end_time?.slice(0, 5) }}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="detail-row">
|
|
|
|
|
+ <span class="detail-label text-secondary text-weight-bold">{{ $t('common.terms.total_amount') }}</span>
|
|
|
|
|
+ <span class="detail-value text-grey-9">{{ formatCurrency(baseAmount) }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="detail-row">
|
|
|
|
|
+ <span class="detail-label text-secondary text-weight-bold">{{ $t('provider.dashboard.cancel_schedule.service_fee') }}</span>
|
|
|
|
|
+ <span class="detail-value text-grey-9">{{ formatCurrency(serviceFee) }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </q-card-section>
|
|
|
|
|
+
|
|
|
|
|
+ <q-separator class="q-mx-md q-my-xs" />
|
|
|
|
|
+
|
|
|
|
|
+ <q-card-section class="q-pt-xs q-pb-sm text-center">
|
|
|
|
|
+ <span class="text-weight-bold text-grey-9">{{ $t('provider.dashboard.cancel_schedule.total') }}</span>
|
|
|
|
|
+ <span class="total-value text-secondary text-weight-bold q-ml-xs">{{ formatCurrency(solicitation.total_amount) }}</span>
|
|
|
|
|
+ </q-card-section>
|
|
|
|
|
+
|
|
|
|
|
+ <q-card-section class="q-pt-none q-pb-xs q-px-lg">
|
|
|
|
|
+ <q-btn
|
|
|
|
|
+ unelevated
|
|
|
|
|
+ rounded
|
|
|
|
|
+ no-caps
|
|
|
|
|
+ color="secondary"
|
|
|
|
|
+ class="full-width btn-close-paid"
|
|
|
|
|
+ :label="$t('provider.dashboard.cancel_schedule.close')"
|
|
|
|
|
+ @click="onDialogCancel"
|
|
|
|
|
+ />
|
|
|
|
|
+ </q-card-section>
|
|
|
|
|
+
|
|
|
|
|
+ <q-card-section class="q-pt-xs q-pb-md text-center">
|
|
|
|
|
+ <div class="row justify-center q-gutter-x-lg">
|
|
|
|
|
+ <q-btn
|
|
|
|
|
+ flat
|
|
|
|
|
+ no-caps
|
|
|
|
|
+ color="grey-7"
|
|
|
|
|
+ size="sm"
|
|
|
|
|
+ :label="$t('provider.dashboard.cancel_schedule.btn_cancel_order')"
|
|
|
|
|
+ @click="openCancelDialog"
|
|
|
|
|
+ />
|
|
|
|
|
+ <q-btn
|
|
|
|
|
+ flat
|
|
|
|
|
+ no-caps
|
|
|
|
|
+ color="grey-7"
|
|
|
|
|
+ size="sm"
|
|
|
|
|
+ :label="$t('provider.dashboard.cancel_schedule.help')"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </q-card-section>
|
|
|
|
|
+ </template>
|
|
|
|
|
+
|
|
|
|
|
+ <template v-else-if="view === 'details'">
|
|
|
<div class="row justify-end q-pt-sm q-pr-sm">
|
|
<div class="row justify-end q-pt-sm q-pr-sm">
|
|
|
<q-btn flat round dense icon="close" color="grey-6" size="sm" @click="onDialogCancel" />
|
|
<q-btn flat round dense icon="close" color="grey-6" size="sm" @click="onDialogCancel" />
|
|
|
</div>
|
|
</div>
|
|
@@ -166,10 +268,11 @@
|
|
|
|
|
|
|
|
<script setup>
|
|
<script setup>
|
|
|
import { ref, computed } from 'vue'
|
|
import { ref, computed } from 'vue'
|
|
|
-import { useDialogPluginComponent } from 'quasar'
|
|
|
|
|
|
|
+import { useDialogPluginComponent, useQuasar } from 'quasar'
|
|
|
import { useI18n } from 'vue-i18n'
|
|
import { useI18n } from 'vue-i18n'
|
|
|
import { formatCurrency } from 'src/helpers/utils'
|
|
import { formatCurrency } from 'src/helpers/utils'
|
|
|
import { labelsPeriodTypes } from 'src/helpers/arraysOptions/labelsPeriodTypes.js'
|
|
import { labelsPeriodTypes } from 'src/helpers/arraysOptions/labelsPeriodTypes.js'
|
|
|
|
|
+import ScheduleCancelDialog from './ScheduleCancelDialog.vue'
|
|
|
|
|
|
|
|
const props = defineProps({
|
|
const props = defineProps({
|
|
|
solicitation: {
|
|
solicitation: {
|
|
@@ -184,8 +287,11 @@ const props = defineProps({
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
const { t } = useI18n()
|
|
const { t } = useI18n()
|
|
|
|
|
+const $q = useQuasar()
|
|
|
const { dialogRef, onDialogHide, onDialogCancel, onDialogOK } = useDialogPluginComponent()
|
|
const { dialogRef, onDialogHide, onDialogCancel, onDialogOK } = useDialogPluginComponent()
|
|
|
|
|
|
|
|
|
|
+const isPaid = computed(() => props.solicitation.status === 'paid')
|
|
|
|
|
+
|
|
|
const view = ref(props.initialView)
|
|
const view = ref(props.initialView)
|
|
|
const loading = ref(false)
|
|
const loading = ref(false)
|
|
|
|
|
|
|
@@ -217,6 +323,50 @@ const dayMonthLabel = computed(() => {
|
|
|
return d.toLocaleDateString('pt-BR', { day: '2-digit', month: '2-digit' })
|
|
return d.toLocaleDateString('pt-BR', { day: '2-digit', month: '2-digit' })
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+const fullDateLabel = computed(() => {
|
|
|
|
|
+ const d = parseLocalDate(props.solicitation.date)
|
|
|
|
|
+ if (!d) return ''
|
|
|
|
|
+ return d.toLocaleDateString('pt-BR', { day: '2-digit', month: 'long', year: 'numeric' })
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const SERVICE_FEE_RATE = 0.06
|
|
|
|
|
+const serviceFee = computed(() => {
|
|
|
|
|
+ const total = parseFloat(props.solicitation.total_amount) || 0
|
|
|
|
|
+ return parseFloat((total * SERVICE_FEE_RATE).toFixed(2))
|
|
|
|
|
+})
|
|
|
|
|
+const baseAmount = computed(() => {
|
|
|
|
|
+ const total = parseFloat(props.solicitation.total_amount) || 0
|
|
|
|
|
+ return parseFloat((total - serviceFee.value).toFixed(2))
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const servicesList = computed(() => {
|
|
|
|
|
+ const list = []
|
|
|
|
|
+ const s = props.solicitation
|
|
|
|
|
+ if (s.schedule_type === 'custom' && s.specialities?.length) {
|
|
|
|
|
+ s.specialities.forEach(sp => list.push({ label: sp.name, active: true }))
|
|
|
|
|
+ }
|
|
|
|
|
+ if (s.offers_meal !== undefined) {
|
|
|
|
|
+ list.push({
|
|
|
|
|
+ label: s.offers_meal
|
|
|
|
|
+ ? t('provider.dashboard.solicitations.offers_meal')
|
|
|
|
|
+ : t('provider.dashboard.solicitations.not_offers_meal'),
|
|
|
|
|
+ active: !!s.offers_meal
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!list.length) {
|
|
|
|
|
+ list.push({ label: t('provider.dashboard.next_schedules.default'), active: true })
|
|
|
|
|
+ }
|
|
|
|
|
+ return list
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const formatAddressShort = (addr) => {
|
|
|
|
|
+ if (!addr) return ''
|
|
|
|
|
+ const parts = [addr.address, addr.number].filter(Boolean)
|
|
|
|
|
+ const street = parts.join(', ')
|
|
|
|
|
+ const suffix = addr.district ? `, ${addr.district}` : ''
|
|
|
|
|
+ return street + suffix
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
const backFromConfirm = () => {
|
|
const backFromConfirm = () => {
|
|
|
if (props.initialView !== 'details') {
|
|
if (props.initialView !== 'details') {
|
|
|
onDialogCancel()
|
|
onDialogCancel()
|
|
@@ -234,6 +384,15 @@ const confirmReject = () => {
|
|
|
loading.value = true
|
|
loading.value = true
|
|
|
onDialogOK({ action: 'reject', id: props.solicitation.id })
|
|
onDialogOK({ action: 'reject', id: props.solicitation.id })
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+const openCancelDialog = () => {
|
|
|
|
|
+ $q.dialog({
|
|
|
|
|
+ component: ScheduleCancelDialog,
|
|
|
|
|
+ componentProps: { schedule: props.solicitation }
|
|
|
|
|
+ }).onOk(() => {
|
|
|
|
|
+ onDialogOK({ action: 'cancelled', id: props.solicitation.id })
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
<style scoped lang="scss">
|
|
@@ -255,6 +414,12 @@ const confirmReject = () => {
|
|
|
font-weight: 700;
|
|
font-weight: 700;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+.btn-close-paid {
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ padding: 10px 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
.confirm-title {
|
|
.confirm-title {
|
|
|
font-size: 20px;
|
|
font-size: 20px;
|
|
|
line-height: 1.35;
|
|
line-height: 1.35;
|
|
@@ -267,4 +432,23 @@ const confirmReject = () => {
|
|
|
padding: 0 8px;
|
|
padding: 0 8px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+.detail-row {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: baseline;
|
|
|
|
|
+ margin-bottom: 4px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.detail-label {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.detail-value {
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.total-value {
|
|
|
|
|
+ font-size: 22px;
|
|
|
|
|
+}
|
|
|
</style>
|
|
</style>
|