ScheduleCancelDialog.vue 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. <template>
  2. <q-dialog
  3. ref="dialogRef"
  4. @hide="onDialogHide"
  5. >
  6. <q-card
  7. class="cancel-dialog-card bg-surface shadow-card"
  8. :flat="false"
  9. >
  10. <div class="row justify-end q-pt-sm q-pr-sm">
  11. <q-btn
  12. color="grey-6"
  13. dense
  14. flat
  15. icon="close"
  16. round
  17. size="sm"
  18. @click="onDialogCancel"
  19. />
  20. </div>
  21. <q-card-section class="q-pt-none q-pb-sm q-px-lg text-center">
  22. <div class="cancel-title text-primary font16 fontbold">
  23. {{ $t('provider.dashboard.cancel_schedule.title') }}
  24. </div>
  25. </q-card-section>
  26. <q-card-section
  27. v-if="packageWarning"
  28. class="q-pt-none q-pb-sm q-px-lg"
  29. >
  30. <div class="package-warning-box row no-wrap q-gutter-x-sm q-pa-sm">
  31. <q-icon
  32. class="q-mt-xs flex-shrink-0"
  33. color="negative"
  34. name="mdi-package-variant"
  35. size="22px"
  36. />
  37. <span class="font13 fontbold text-negative">
  38. {{ packageWarning }}
  39. </span>
  40. </div>
  41. </q-card-section>
  42. <q-card-section class="q-pt-none q-pb-sm q-px-lg">
  43. <div class="text-grey-8 font14 fontbold q-mb-xs">
  44. {{ $t('provider.dashboard.cancel_schedule.reason_label') }}
  45. </div>
  46. <q-input
  47. v-model="cancelText"
  48. color="secondary"
  49. dense
  50. hide-bottom-space
  51. input-class="text-dark"
  52. outlined
  53. rows="4"
  54. type="textarea"
  55. :placeholder="$t('provider.dashboard.cancel_schedule.reason_placeholder')"
  56. :rules="[val => (val && val.trim().length >= 5) || ' ']"
  57. />
  58. </q-card-section>
  59. <q-card-section class="q-pt-xs q-pb-md q-px-lg">
  60. <div class="warning-box row no-wrap q-gutter-x-sm q-pa-sm">
  61. <q-icon
  62. class="q-mt-xs flex-shrink-0"
  63. color="secondary"
  64. name="mdi-alert-outline"
  65. size="22px"
  66. />
  67. <div class="font12 text-grey-8">
  68. <span>
  69. {{ $t('provider.dashboard.cancel_schedule.warning_before') }}
  70. </span>
  71. <span class="fontbold">
  72. {{ $t('provider.dashboard.cancel_schedule.warning_bold') }}
  73. </span>
  74. <span>
  75. {{ $t('provider.dashboard.cancel_schedule.warning_after') }}
  76. </span>
  77. </div>
  78. </div>
  79. </q-card-section>
  80. <q-card-section class="q-pt-none q-pb-lg q-px-lg">
  81. <div class="row justify-center q-gutter-x-md">
  82. <q-btn
  83. class="btn-action bg-grey-3 text-grey-8"
  84. no-caps
  85. rounded
  86. size="sm"
  87. unelevated
  88. :label="$t('provider.dashboard.cancel_schedule.btn_cancel')"
  89. @click="onDialogCancel"
  90. />
  91. <q-btn
  92. class="btn-action"
  93. color="secondary"
  94. no-caps
  95. rounded
  96. unelevated
  97. size="sm"
  98. :disable="!cancelText || cancelText.trim().length < 5"
  99. :label="$t('provider.dashboard.cancel_schedule.btn_keep')"
  100. :loading="loading"
  101. @click="confirmCancel"
  102. />
  103. </div>
  104. </q-card-section>
  105. </q-card>
  106. </q-dialog>
  107. </template>
  108. <script setup>
  109. import { cancelSchedule } from 'src/api/schedule'
  110. import { useDialogPluginComponent, useQuasar } from 'quasar'
  111. import { useI18n } from 'vue-i18n'
  112. import { computed, ref } from 'vue'
  113. const props = defineProps({
  114. schedule: {
  115. type: Object,
  116. required: true
  117. }
  118. })
  119. const { dialogRef, onDialogHide, onDialogCancel, onDialogOK } = useDialogPluginComponent()
  120. const { t } = useI18n()
  121. const $q = useQuasar()
  122. const cancelText = ref('')
  123. const loading = ref(false)
  124. const packageWarning = computed(() => {
  125. if (!props.schedule?.service_package_id) return null
  126. const count = props.schedule?.package_items?.length
  127. if (count > 1) {
  128. return t('provider.dashboard.cancel_schedule.package_warning', { count })
  129. }
  130. if (count === 1) return null
  131. return t('provider.dashboard.cancel_schedule.package_warning_generic')
  132. })
  133. const confirmCancel = async () => {
  134. if (!cancelText.value || cancelText.value.trim().length < 5) return
  135. loading.value = true
  136. try {
  137. await cancelSchedule(props.schedule.id, cancelText.value.trim())
  138. onDialogOK()
  139. } catch {
  140. $q.notify({ message: t('http.errors.failed'), color: 'negative' })
  141. } finally {
  142. loading.value = false
  143. }
  144. }
  145. </script>
  146. <style scoped lang="scss">
  147. .cancel-dialog-card {
  148. width: 340px;
  149. max-width: 94vw;
  150. border-radius: 20px !important;
  151. overflow: hidden;
  152. }
  153. .cancel-title {
  154. line-height: 1.35;
  155. }
  156. .warning-box {
  157. background: #e8f4fd;
  158. border-radius: 10px;
  159. }
  160. .package-warning-box {
  161. background: #fdecec;
  162. border-radius: 10px;
  163. }
  164. .btn-action {
  165. min-width: 110px;
  166. }
  167. .flex-shrink-0 {
  168. flex-shrink: 0;
  169. }
  170. </style>