ViewScheduleDialog.vue 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. <template>
  2. <q-dialog ref="dialogRef" @hide="onDialogHide">
  3. <q-card class="q-dialog-plugin" style="width: 700px; max-width: 90vw">
  4. <DefaultDialogHeader :title="() => $t('schedules.schedule_details')" @close="onDialogCancel" />
  5. <q-card-section>
  6. <div class="row q-col-gutter-md">
  7. <div class="col-12 col-md-6">
  8. <div class="text-caption text-grey-7">{{ $t('schedules.client') }}</div>
  9. <div class="text-body1">{{ schedule?.client_name || 'N/A' }}</div>
  10. </div>
  11. <div class="col-12 col-md-6">
  12. <div class="text-caption text-grey-7">{{ $t('schedules.provider') }}</div>
  13. <div class="text-body1">{{ schedule?.provider_name || 'N/A' }}</div>
  14. </div>
  15. <div class="col-12">
  16. <div class="text-caption text-grey-7">{{ $t('schedules.address') }}</div>
  17. <div class="text-body1">{{ formatAddress(schedule?.address) }}</div>
  18. </div>
  19. <div class="col-12 col-md-6">
  20. <div class="text-caption text-grey-7">{{ $t('schedules.date') }}</div>
  21. <div class="text-body1">{{ schedule?.date }}</div>
  22. </div>
  23. <div class="col-12 col-md-6">
  24. <div class="text-caption text-grey-7">{{ $t('schedules.period') }}</div>
  25. <div class="text-body1">{{ schedule?.period_type }} {{ $t('schedules.hours') }}</div>
  26. </div>
  27. <div class="col-12 col-md-6">
  28. <div class="text-caption text-grey-7">{{ $t('schedules.start_time') }}</div>
  29. <div class="text-body1">{{ schedule?.start_time?.substring(0, 5) }}</div>
  30. </div>
  31. <div class="col-12 col-md-6">
  32. <div class="text-caption text-grey-7">{{ $t('schedules.end_time') }}</div>
  33. <div class="text-body1">{{ schedule?.end_time?.substring(0, 5) }}</div>
  34. </div>
  35. <div class="col-12 col-md-6">
  36. <div class="text-caption text-grey-7">{{ $t('schedules.total_amount') }}</div>
  37. <div class="text-body1 text-weight-bold text-positive">
  38. {{ formatToBRLCurrency(schedule?.total_amount) }}
  39. </div>
  40. </div>
  41. <div class="col-12 col-md-6">
  42. <div class="text-caption text-grey-7">{{ $t('schedules.status') }}</div>
  43. <q-badge :color="getStatusColor(schedule?.status)" class="q-pa-sm">
  44. {{ $t(`schedules.statuses.${schedule?.status}`) }}
  45. </q-badge>
  46. </div>
  47. <div class="col-6">
  48. <div class="text-caption text-grey-7">{{ $t('schedules.code') }}</div>
  49. <div class="text-body1">
  50. {{ schedule?.code }}
  51. </div>
  52. </div>
  53. <div class="col-6 col-md-6">
  54. <div class="text-caption text-grey-7">{{ $t('schedules.code_verified') }}</div>
  55. <q-icon
  56. :name="schedule.code_verified ? 'check_circle' : 'cancel'"
  57. :color="schedule.code_verified ? 'positive' : 'negative'"
  58. size="sm"
  59. />
  60. </div>
  61. </div>
  62. </q-card-section>
  63. <q-card-actions align="right" class="q-px-md q-pb-md">
  64. <q-btn
  65. :label="$t('common.actions.close')"
  66. flat
  67. color="primary"
  68. @click="onDialogCancel"
  69. />
  70. <q-btn
  71. v-if="schedule?.status === 'paid'"
  72. unelevated
  73. :label="$t('schedules.cancel_schedule')"
  74. color="negative"
  75. @click="handleCancel"
  76. />
  77. <template v-if="viewMode === 'provider'">
  78. <template v-if="schedule?.status === 'pending'">
  79. <q-btn
  80. unelevated
  81. :label="$t('schedules.reject')"
  82. color="negative"
  83. @click="handleReject"
  84. />
  85. <q-btn
  86. unelevated
  87. :label="$t('schedules.accept')"
  88. color="positive"
  89. @click="handleAccept"
  90. />
  91. </template>
  92. <q-btn
  93. v-if="schedule?.status === 'paid'"
  94. unelevated
  95. :label="$t('schedules.fill_code')"
  96. color="secondary"
  97. @click="fillCode"
  98. />
  99. </template>
  100. <template v-if="viewMode === 'client'">
  101. <q-btn
  102. v-if="schedule?.status === 'accepted'"
  103. unelevated
  104. :label="$t('schedules.mark_as_paid')"
  105. color="positive"
  106. @click="handleMarkAsPaid"
  107. />
  108. </template>
  109. </q-card-actions>
  110. </q-card>
  111. </q-dialog>
  112. </template>
  113. <script setup>
  114. import { useDialogPluginComponent, useQuasar } from 'quasar'
  115. import { verifyScheduleCode } from 'src/api/customSchedule'
  116. import { useI18n } from 'vue-i18n'
  117. import DefaultDialogHeader from 'src/components/defaults/DefaultDialogHeader.vue'
  118. import { formatToBRLCurrency } from 'src/helpers/utils'
  119. const props = defineProps({
  120. schedule: {
  121. type: Object,
  122. required: true
  123. },
  124. viewMode: {
  125. type: String,
  126. required: true,
  127. validator: (value) => ['client', 'provider'].includes(value)
  128. }
  129. })
  130. const emit = defineEmits([
  131. ...useDialogPluginComponent.emits,
  132. // 'accept',
  133. // 'reject',
  134. // 'mark-as-paid',
  135. // 'cancel'
  136. ])
  137. const $q = useQuasar()
  138. const { t } = useI18n()
  139. const { dialogRef, onDialogHide, onDialogCancel, onDialogOK } = useDialogPluginComponent()
  140. const formatAddress = (address) => {
  141. if (!address) return 'N/A'
  142. const parts = [
  143. address.address,
  144. address.complement,
  145. address.zip_code,
  146. address.city,
  147. address.state
  148. ].filter(Boolean)
  149. return parts.join(', ')
  150. }
  151. const getStatusColor = (status) => {
  152. const colors = {
  153. pending: 'warning',
  154. accepted: 'positive',
  155. rejected: 'negative',
  156. paid: 'info',
  157. cancelled: 'dark',
  158. started: 'primary',
  159. finished: 'positive'
  160. }
  161. return colors[status] || 'grey'
  162. }
  163. const handleAccept = () => {
  164. $q.dialog({
  165. title: t('schedules.accept'),
  166. message: t('common.ui.messages.confirm_action'),
  167. cancel: true,
  168. persistent: true
  169. }).onOk(async () => {
  170. emit('accept', props.schedule.id);
  171. onDialogOK();
  172. })
  173. }
  174. const handleReject = () => {
  175. $q.dialog({
  176. title: t('schedules.reject'),
  177. message: t('common.ui.messages.confirm_action'),
  178. cancel: true,
  179. persistent: true
  180. }).onOk(async () => {
  181. emit('reject', props.schedule.id);
  182. onDialogOK();
  183. })
  184. }
  185. const handleMarkAsPaid = () => {
  186. $q.dialog({
  187. title: t('schedules.mark_as_paid'),
  188. message: t('common.ui.messages.confirm_action'),
  189. cancel: true,
  190. persistent: true
  191. }).onOk(async () => {
  192. onDialogOK();
  193. emit('mark-as-paid', props.schedule.id);
  194. })
  195. }
  196. const handleCancel = () => {
  197. $q.dialog({
  198. title: t('schedules.cancel_schedule'),
  199. message: t('common.ui.messages.confirm_action'),
  200. cancel: true,
  201. persistent: true
  202. }).onOk(async () => {
  203. onDialogOK();
  204. emit('cancel', props.schedule.id);
  205. })
  206. }
  207. const fillCode = () => {
  208. $q.dialog({
  209. title: t('schedules.fill_code'),
  210. message: t('schedules.enter_code'),
  211. prompt: {
  212. model: '',
  213. type: 'text'
  214. },
  215. cancel: true,
  216. persistent: true
  217. }).onOk(async (code) => {
  218. const response = await verifyScheduleCode(props.schedule.id, code)
  219. if(response.data.success) {
  220. $q.notify({
  221. type: 'positive',
  222. message: t('schedules.code_verified_success'),
  223. position: 'top'
  224. })
  225. } else {
  226. $q.notify({
  227. type: 'negative',
  228. message: t('schedules.code_verified_failed'),
  229. position: 'top'
  230. })
  231. }
  232. emit('refreshData');
  233. onDialogOK();
  234. })
  235. }
  236. </script>