SolicitationDetailsDialog.vue 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. <template>
  2. <q-dialog ref="dialogRef" @hide="onDialogHide">
  3. <q-card class="solicitation-dialog-card bg-surface shadow-card" :flat="false">
  4. <template v-if="view === 'details'">
  5. <div class="row justify-end q-pt-sm q-pr-sm">
  6. <q-btn flat round dense icon="close" color="grey-6" size="sm" @click="onDialogCancel" />
  7. </div>
  8. <q-card-section class="column items-center q-pt-sm q-pb-sm">
  9. <q-avatar size="72px" class="q-mb-sm">
  10. <img :src="solicitation.customer_photo || 'https://cdn.quasar.dev/img/avatar.png'" />
  11. </q-avatar>
  12. <div class="text-subtitle1 text-weight-bold text-text">{{ solicitation.client_name }}</div>
  13. <div class="text-price q-mt-xs">{{ formatCurrency(solicitation.total_amount) }}</div>
  14. <div class="text-caption text-grey-6">{{ periodLabel }}</div>
  15. </q-card-section>
  16. <q-card-section class="column items-center q-pt-none q-pb-sm text-text">
  17. <div class="text-body2 text-center">
  18. <span class="text-weight-bold">{{ weekdayLabel + ' ,' + dayMonthLabel }}</span>
  19. </div>
  20. <div class="text-body2 text-center">
  21. {{ $t('common.from') }} <strong>{{ solicitation.start_time?.slice(0, 5) }}</strong>
  22. {{ $t('common.to') }} <strong>{{ solicitation.end_time?.slice(0, 5) }}</strong>
  23. </div>
  24. </q-card-section>
  25. <q-card-section class="column items-center q-pt-none q-pb-xs">
  26. <div class="row items-center q-gutter-x-xs">
  27. <q-icon
  28. :name="solicitation.offers_meal ? 'mdi-food' : 'mdi-food-off'"
  29. :color="solicitation.offers_meal ? 'positive' : 'grey-5'"
  30. size="18px"
  31. />
  32. <span class="text-body2 text-grey-7">
  33. {{ solicitation.offers_meal
  34. ? $t('provider.dashboard.solicitations.offers_meal')
  35. : $t('provider.dashboard.solicitations.not_offers_meal') }}
  36. </span>
  37. </div>
  38. </q-card-section>
  39. <q-card-section class="column items-center q-pt-xs q-pb-sm text-text">
  40. <div class="row items-center q-gutter-x-xs">
  41. <q-icon name="mdi-map-marker" color="secondary" size="20px" />
  42. <span class="text-body1 text-weight-bold">{{ solicitation.address?.district || 'N/A' }}</span>
  43. </div>
  44. <div class="text-caption text-grey-6 text-center q-mt-xs">
  45. {{ $t('provider.dashboard.solicitations.distance_prefix') }}
  46. <strong>{{ (solicitation.distance || 0) + ' km' }}</strong>
  47. {{ $t('provider.dashboard.solicitations.distance_suffix') }}
  48. </div>
  49. <div class="q-mt-xs">
  50. <span class="text-caption text-grey-6">{{ $t('provider.dashboard.solicitations.via_agenda_text') }} </span>
  51. <span class="text-caption text-secondary text-weight-bold">{{ $t('provider.dashboard.solicitations.via_agenda') }}</span>
  52. </div>
  53. </q-card-section>
  54. <q-separator />
  55. <q-card-section class="q-py-sm">
  56. <div class="row justify-center q-gutter-x-md">
  57. <q-btn
  58. unelevated
  59. rounded
  60. no-caps
  61. class="btn-action bg-grey-3 text-grey-8"
  62. :label="$t('common.refuse')"
  63. @click="view = 'confirm-reject'"
  64. />
  65. <q-btn
  66. unelevated
  67. rounded
  68. no-caps
  69. color="secondary"
  70. class="btn-action"
  71. :label="$t('common.accept')"
  72. @click="view = 'confirm-accept'"
  73. />
  74. </div>
  75. <div class="row justify-center q-mt-xs">
  76. <q-btn flat no-caps color="grey-7" size="sm" :label="$t('common.actions.back')" @click="onDialogCancel" />
  77. </div>
  78. </q-card-section>
  79. <div class="q-pa-md">
  80. <span class="text-caption text-warning text-weight-bold">{{ $t('provider.dashboard.solicitations.same_day_warning_label') }} </span>
  81. <span class="text-caption text-grey-8">{{ $t('provider.dashboard.solicitations.same_day_warning') }}</span>
  82. </div>
  83. </template>
  84. <template v-else-if="view === 'confirm-accept'">
  85. <q-card-section class="column items-center q-pt-xl q-pb-lg text-center">
  86. <div class="confirm-title text-secondary text-weight-bold">
  87. {{ $t('provider.dashboard.solicitations.confirm_accept') }}
  88. </div>
  89. </q-card-section>
  90. <q-card-section class="q-pt-none q-pb-xl">
  91. <div class="row justify-center q-gutter-x-md">
  92. <q-btn
  93. unelevated
  94. rounded
  95. no-caps
  96. color="purple-5"
  97. class="btn-action"
  98. :label="$t('common.actions.back')"
  99. @click="backFromConfirm"
  100. />
  101. <q-btn
  102. unelevated
  103. rounded
  104. no-caps
  105. color="secondary"
  106. class="btn-action"
  107. :loading="loading"
  108. :label="$t('common.accept')"
  109. @click="confirmAccept"
  110. />
  111. </div>
  112. </q-card-section>
  113. </template>
  114. <template v-else-if="view === 'confirm-reject'">
  115. <q-card-section class="column items-center q-pt-xl q-pb-md text-center">
  116. <div class="confirm-title text-secondary text-weight-bold">
  117. {{ $t('provider.dashboard.solicitations.confirm_reject') }}
  118. </div>
  119. <div class="q-mt-md text-body2 text-grey-7 confirm-tip">
  120. <strong>{{ $t('provider.dashboard.solicitations.tip_label') }} </strong>
  121. {{ $t('provider.dashboard.solicitations.reject_tip') }}
  122. </div>
  123. </q-card-section>
  124. <q-card-section class="q-pt-none q-pb-xl">
  125. <div class="row justify-center q-gutter-x-md">
  126. <q-btn
  127. unelevated
  128. rounded
  129. no-caps
  130. color="purple-5"
  131. class="btn-action"
  132. :label="$t('common.actions.back')"
  133. @click="backFromConfirm"
  134. />
  135. <q-btn
  136. unelevated
  137. rounded
  138. no-caps
  139. color="secondary"
  140. class="btn-action"
  141. :loading="loading"
  142. :label="$t('common.refuse')"
  143. @click="confirmReject"
  144. />
  145. </div>
  146. </q-card-section>
  147. </template>
  148. </q-card>
  149. </q-dialog>
  150. </template>
  151. <script setup>
  152. import { ref, computed } from 'vue'
  153. import { useDialogPluginComponent } from 'quasar'
  154. import { useI18n } from 'vue-i18n'
  155. import { formatCurrency } from 'src/helpers/utils'
  156. import { labelsPeriodTypes } from 'src/helpers/arraysOptions/labelsPeriodTypes.js'
  157. const props = defineProps({
  158. solicitation: {
  159. type: Object,
  160. required: true
  161. },
  162. initialView: {
  163. type: String,
  164. default: 'details',
  165. validator: (v) => ['details', 'confirm-accept', 'confirm-reject'].includes(v)
  166. }
  167. })
  168. const { t } = useI18n()
  169. const { dialogRef, onDialogHide, onDialogCancel, onDialogOK } = useDialogPluginComponent()
  170. const view = ref(props.initialView)
  171. const loading = ref(false)
  172. const periodLabel = computed(() => {
  173. const found = labelsPeriodTypes.find(l => l.value == props.solicitation.period_type)
  174. return found ? t(found.label) : ''
  175. })
  176. const parseLocalDate = (dateStr) => {
  177. if (!dateStr) return null
  178. const s = String(dateStr)
  179. const iso = s.match(/^(\d{4})-(\d{2})-(\d{2})/)
  180. if (iso) return new Date(+iso[1], +iso[2] - 1, +iso[3])
  181. const dmy = s.match(/^(\d{2})\/(\d{2})\/(\d{4})/)
  182. if (dmy) return new Date(+dmy[3], +dmy[2] - 1, +dmy[1])
  183. return null
  184. }
  185. const weekdayLabel = computed(() => {
  186. const d = parseLocalDate(props.solicitation.date)
  187. if (!d) return ''
  188. const w = d.toLocaleDateString('pt-BR', { weekday: 'long' })
  189. return w.charAt(0).toUpperCase() + w.slice(1)
  190. })
  191. const dayMonthLabel = computed(() => {
  192. const d = parseLocalDate(props.solicitation.date)
  193. if (!d) return ''
  194. return d.toLocaleDateString('pt-BR', { day: '2-digit', month: '2-digit' })
  195. })
  196. const backFromConfirm = () => {
  197. if (props.initialView !== 'details') {
  198. onDialogCancel()
  199. } else {
  200. view.value = 'details'
  201. }
  202. }
  203. const confirmAccept = () => {
  204. loading.value = true
  205. onDialogOK({ action: 'accept', id: props.solicitation.id })
  206. }
  207. const confirmReject = () => {
  208. loading.value = true
  209. onDialogOK({ action: 'reject', id: props.solicitation.id })
  210. }
  211. </script>
  212. <style scoped lang="scss">
  213. .solicitation-dialog-card {
  214. width: 320px;
  215. max-width: 92vw;
  216. border-radius: 20px !important;
  217. overflow: hidden;
  218. }
  219. .text-price {
  220. font-size: 24px;
  221. font-weight: 700;
  222. color: var(--q-secondary);
  223. }
  224. .btn-action {
  225. min-width: 110px;
  226. font-weight: 700;
  227. }
  228. .confirm-title {
  229. font-size: 20px;
  230. line-height: 1.35;
  231. padding: 0 12px;
  232. }
  233. .confirm-tip {
  234. font-size: 13px;
  235. line-height: 1.5;
  236. padding: 0 8px;
  237. }
  238. </style>