ServiceSelectionSheet.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. <template>
  2. <q-dialog
  3. ref="dialogRef"
  4. position="bottom"
  5. @hide="onDialogHide"
  6. >
  7. <q-card class="bg-surface text-text full-width sheet-card">
  8. <q-card-section class="row items-center q-pb-none">
  9. <q-space />
  10. <q-btn
  11. color="grey-6"
  12. dense
  13. flat
  14. icon="mdi-close-circle-outline"
  15. round
  16. @click="onDialogCancel"
  17. />
  18. </q-card-section>
  19. <q-separator class="q-mt-sm" />
  20. <q-card-section class="q-pt-sm q-pb-md">
  21. <div v-if="availableServiceTypes.length === 0" class="text-center text-grey-6 text-body2 q-py-md">
  22. {{ $t('scheduling_page.no_slots_available') }}
  23. </div>
  24. <div
  25. v-for="type in availableServiceTypes"
  26. :key="type.key"
  27. class="row items-center no-wrap q-py-sm"
  28. >
  29. <div class="col">
  30. <div class="row items-center no-wrap q-gutter-x-xs">
  31. <span class="font14 fontbold text-text">
  32. {{ type.label }}
  33. </span>
  34. <q-btn
  35. color="primary"
  36. dense
  37. flat
  38. icon="mdi-information-outline"
  39. size="xs"
  40. @click="openInfo(type)"
  41. />
  42. </div>
  43. <div class="font12 fontmedium text-grey-6">
  44. {{ type.hours }}
  45. </div>
  46. </div>
  47. <!-- <div class="font16 fontbold text-text q-mx-md" style="white-space: nowrap;">
  48. {{ type.price != null ? formatPrice(type.price) : $t('scheduling_page.no_price') }}
  49. </div> -->
  50. <div class="row no-wrap q-gutter-x-xs">
  51. <q-btn
  52. color="secondary"
  53. no-caps
  54. padding="2px 16px"
  55. rounded
  56. size="sm"
  57. unelevated
  58. :label="$t('common.actions.select')"
  59. @click="onDialogOK({
  60. action: 'cart',
  61. serviceType: type,
  62. date: selectedDate,
  63. provider
  64. })"
  65. />
  66. </div>
  67. </div>
  68. </q-card-section>
  69. </q-card>
  70. </q-dialog>
  71. </template>
  72. <script setup>
  73. import { computed } from 'vue';
  74. import { useDialogPluginComponent, useQuasar } from 'quasar';
  75. import { useI18n } from 'vue-i18n';
  76. import ServiceTypeInfoDialog from './ServiceTypeInfoDialog.vue';
  77. const props = defineProps({
  78. partialBlocks: { type: Array, required: false, default: () => [] },
  79. provider: { type: Object, required: true },
  80. selectedDate: { type: String, required: true },
  81. });
  82. defineEmits([...useDialogPluginComponent.emits]);
  83. const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent();
  84. const $q = useQuasar();
  85. const { t } = useI18n();
  86. const availableServiceTypes = computed(() =>
  87. [
  88. {
  89. description: t('scheduling_page.service_types.integral.description'),
  90. hours: t('scheduling_page.service_types.integral.hours'),
  91. hoursCount: 8,
  92. key: 'integral',
  93. label: t('scheduling_page.service_types.integral.label'),
  94. basePrice: props.provider?.daily_price_8h_base ?? props.provider?.daily_price_8h ?? null,
  95. price: props.provider?.daily_price_8h ?? null,
  96. },
  97. {
  98. description: t('scheduling_page.service_types.padrao.description'),
  99. hours: t('scheduling_page.service_types.padrao.hours'),
  100. hoursCount: 6,
  101. key: 'padrao',
  102. label: t('scheduling_page.service_types.padrao.label'),
  103. basePrice: props.provider?.daily_price_6h_base ?? props.provider?.daily_price_6h ?? null,
  104. price: props.provider?.daily_price_6h ?? null,
  105. },
  106. {
  107. description: t('scheduling_page.service_types.meio_periodo.description'),
  108. hours: t('scheduling_page.service_types.meio_periodo.hours'),
  109. hoursCount: 4,
  110. key: 'meio_periodo',
  111. label: t('scheduling_page.service_types.meio_periodo.label'),
  112. basePrice: props.provider?.daily_price_4h_base ?? props.provider?.daily_price_4h ?? null,
  113. price: props.provider?.daily_price_4h ?? null,
  114. },
  115. {
  116. description: t('scheduling_page.service_types.diaria_rapida.description'),
  117. hours: t('scheduling_page.service_types.diaria_rapida.hours'),
  118. hoursCount: 2,
  119. key: 'diaria_rapida',
  120. label: t('scheduling_page.service_types.diaria_rapida.label'),
  121. basePrice: props.provider?.daily_price_2h_base ?? props.provider?.daily_price_2h ?? null,
  122. price: props.provider?.daily_price_2h ?? null,
  123. },
  124. ].filter(type => hasValidSlots(type.hoursCount))
  125. );
  126. // const formatPrice = (value) =>Number(value).toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' });
  127. const hasValidSlots = (hoursCount) => {
  128. for (let s = 7; s + hoursCount <= 20; s++) {
  129. if (!slotConflicts(s, s + hoursCount, props.partialBlocks)) return true;
  130. }
  131. return false;
  132. };
  133. const openInfo = (type) => {
  134. $q.dialog({ component: ServiceTypeInfoDialog, componentProps: { serviceType: type } });
  135. };
  136. const slotConflicts = (slotStart, slotEnd, blocks) =>
  137. blocks.some(b => {
  138. const blockEnd = parseInt(b.end_hour);
  139. const blockStart = parseInt(b.init_hour);
  140. return slotEnd >= blockStart && slotStart <= blockEnd;
  141. });
  142. </script>
  143. <style scoped lang="scss">
  144. .sheet-card {
  145. border-radius: 20px 20px 0 0;
  146. }
  147. </style>