LoginStepFivePanel.vue 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. <template>
  2. <q-card-section class="no-padding">
  3. <div class="text-text font12 fontbold q-mb-sm">{{ $t('provider.login.steps.step_5.daily_price_title') }}</div>
  4. <DefaultCurrencyInput
  5. v-model="form.daily_price_8h"
  6. rounded
  7. no-error-icon
  8. class="bg-surface q-mb-sm custom-currency-input"
  9. input-class="text-text font12 fontmedium"
  10. placeholder="R$ 0,00"
  11. :error="!!priceError"
  12. hide-bottom-space
  13. label=""
  14. />
  15. <div class="text-center text-grey-7 font10 fontmedium q-mb-md">{{ $t('provider.login.steps.step_5.daily_price_min_max') }}</div>
  16. <q-banner class="bg-blue-1 text-primary q-mb-lg q-pa-md bannerRound">
  17. <template #avatar>
  18. <q-icon name="mdi-alert-outline" color="primary" />
  19. </template>
  20. <span class="fontbold font12">{{ $t('provider.login.steps.step_5.dont_worry') }}</span>
  21. <span class="font12 q-ml-xs">
  22. {{ $t('provider.login.steps.step_5.change_anytime') }}
  23. </span>
  24. </q-banner>
  25. <div class="text-text text-center font12 fontmedium q-mb-sm">{{ $t('provider.login.steps.step_5.shorter_services') }}</div>
  26. <div class="row q-col-gutter-sm q-mb-lg q-mt-md">
  27. <div class="col-4">
  28. <div class="text-text text-center fontbold font12">{{ $t('provider.login.steps.step_5.up_to_6h') }}</div>
  29. <q-input :model-value="formatCurrency(form.daily_price_6h)" readonly class="bg-surface" input-class="text-text font12 fontmedium"/>
  30. </div>
  31. <div class="col-4">
  32. <div class="text-text text-center fontbold font12">{{ $t('provider.login.steps.step_5.up_to_4h') }}</div>
  33. <q-input :model-value="formatCurrency(form.daily_price_4h)" readonly class="bg-surface" input-class="text-text font12 fontmedium"/>
  34. </div>
  35. <div class="col-4 column">
  36. <div class="text-text text-center fontbold font12">{{ $t('provider.login.steps.step_5.up_to_2h') }}</div>
  37. <q-input :model-value="formatCurrency(form.daily_price_2h)" readonly class="bg-surface" input-class="text-text font12 fontmedium"/>
  38. </div>
  39. </div>
  40. <div class="text-text q-mb-xs text-center fontbold 12">{{ $t('provider.login.steps.step_5.other_services') }}</div>
  41. <div class="text-grey-7 q-mb-md text-center font10">{{ $t('provider.login.steps.step_5.change_in_profile') }}</div>
  42. <div v-if="loadingServiceTypes" class="row justify-center q-mb-lg">
  43. <q-spinner color="primary" size="28px" />
  44. </div>
  45. <div v-else-if="serviceOptions.length" class="row q-col-gutter-sm q-mb-lg">
  46. <div v-for="service in serviceOptions" :key="service.value" class="col-6">
  47. <q-checkbox
  48. :model-value="selectedServices.includes(service.value)"
  49. :label="service.label"
  50. color="primary"
  51. class="q-mb-md text-text font12 fontmedium"
  52. keep-color
  53. @update:model-value="(checked) => onServiceToggle(service.value, checked)"
  54. />
  55. </div>
  56. </div>
  57. <div v-else class="text-grey-7 q-mb-lg font12 fontmedium">{{ $t('provider.login.steps.step_5.no_services') }}</div>
  58. <div class="text-grey-7 text-center font12 fontmedium">{{ $t('provider.login.steps.step_5.search_visibility') }}</div>
  59. </q-card-section>
  60. </template>
  61. <script setup>
  62. import { computed, onMounted, ref, watch } from 'vue';
  63. import { getPublicServiceTypes } from 'src/api/serviceType';
  64. import DefaultCurrencyInput from 'src/components/defaults/DefaultCurrencyInput.vue';
  65. const form = defineModel({ type: Object, required: true });
  66. const loadingServiceTypes = ref(false);
  67. const serviceOptions = ref([]);
  68. const priceError = computed(() => {
  69. const value = form.value.daily_price_8h;
  70. if (value === null || value === undefined || value === '') {
  71. return 'Este campo é obrigatório';
  72. }
  73. const numericValue = Number(value);
  74. if (Number.isNaN(numericValue)) {
  75. return 'Valor inválido';
  76. }
  77. if (numericValue < 3) {
  78. return 'Valor mínimo R$ 3,00';
  79. }
  80. if (numericValue > 500) {
  81. return 'Valor máximo R$ 500,00';
  82. }
  83. return '';
  84. });
  85. const selectedServices = computed(() => {
  86. if (!Array.isArray(form.value.services_types_ids)) {
  87. return [];
  88. }
  89. return form.value.services_types_ids;
  90. });
  91. const formatCurrency = (value) => {
  92. if (value === null || value === undefined || Number.isNaN(Number(value))) {
  93. return 'R$ 0,00';
  94. }
  95. return Number(value).toLocaleString('pt-BR', {
  96. style: 'currency',
  97. currency: 'BRL',
  98. minimumFractionDigits: 2,
  99. });
  100. };
  101. const normalizeSelectedServiceIds = () => {
  102. if (!Array.isArray(form.value.services_types_ids)) {
  103. form.value.services_types_ids = [];
  104. }
  105. form.value.services_types_ids = form.value.services_types_ids
  106. .map((id) => Number(id))
  107. .filter((id) => Number.isInteger(id) && id > 0);
  108. };
  109. const onServiceToggle = (value, checked) => {
  110. normalizeSelectedServiceIds();
  111. if (checked) {
  112. if (!form.value.services_types_ids.includes(value)) {
  113. form.value.services_types_ids.push(value);
  114. }
  115. return;
  116. }
  117. form.value.services_types_ids = form.value.services_types_ids.filter((serviceId) => serviceId !== value);
  118. };
  119. const loadServiceTypes = async () => {
  120. loadingServiceTypes.value = true;
  121. try {
  122. const serviceTypes = await getPublicServiceTypes();
  123. serviceOptions.value = (Array.isArray(serviceTypes) ? serviceTypes : [])
  124. .filter((item) => item?.is_active !== false)
  125. .map((item) => ({
  126. label: item.description,
  127. value: Number(item.id),
  128. }))
  129. .filter((item) => Number.isInteger(item.value) && item.value > 0);
  130. } catch (error) {
  131. serviceOptions.value = [];
  132. console.error(error);
  133. } finally {
  134. loadingServiceTypes.value = false;
  135. }
  136. };
  137. watch(
  138. () => form.value.daily_price_8h,
  139. (value) => {
  140. const price = Number(value);
  141. if (!price || Number.isNaN(price)) {
  142. form.value.daily_price_6h = null;
  143. form.value.daily_price_4h = null;
  144. form.value.daily_price_2h = null;
  145. return;
  146. }
  147. form.value.daily_price_6h = Number((price * 0.85).toFixed(2));
  148. form.value.daily_price_4h = Number((price * 0.55).toFixed(2));
  149. form.value.daily_price_2h = Number((price * 0.30).toFixed(2));
  150. },
  151. { immediate: true },
  152. );
  153. onMounted(() => {
  154. loadServiceTypes();
  155. });
  156. </script>
  157. <style scoped>
  158. .bannerRound {
  159. border-radius: 18px;
  160. }
  161. :deep(.custom-currency-input .q-field__control) {
  162. border-radius: 28px;
  163. }
  164. </style>