LoginStep4Panel.vue 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. <template>
  2. <div class="column col-12">
  3. <q-card class="step4-card col-12">
  4. <div class="bg-surface q-pa-lg">
  5. <div class="text-center q-mb-md">
  6. <div class="text-primary font14 fontbold">
  7. {{ $t("auth.step4_title") }}
  8. </div>
  9. </div>
  10. <q-input
  11. v-model="cep"
  12. class="bg-surface q-mb-md"
  13. hide-bottom-space
  14. input-class="text-text"
  15. lazy-rules
  16. mask="#####-###"
  17. no-error-icon
  18. outlined
  19. rounded
  20. :bottom-slots="false"
  21. :loading="loadingCep"
  22. :placeholder="$t('common.terms.cep')"
  23. @update:model-value="onCepChange"
  24. >
  25. <template #prepend>
  26. <q-icon color="grey-5" name="mdi-map-marker-outline" />
  27. </template>
  28. </q-input>
  29. <q-btn
  30. class="full-width"
  31. color="primary-button"
  32. padding="8px 12px"
  33. rounded
  34. :label="$t('auth.use_location')"
  35. :loading="loadingLocation"
  36. @click="useLocation"
  37. />
  38. </div>
  39. </q-card>
  40. <div class="text-center q-pt-sm">
  41. <q-btn
  42. color="surface"
  43. flat
  44. icon="mdi-chevron-left-circle-outline"
  45. rounded
  46. :label="$t('auth.back_to_register')"
  47. @click="emit('back')"
  48. />
  49. </div>
  50. </div>
  51. </template>
  52. <script setup>
  53. import { ref } from "vue";
  54. import { useQuasar } from "quasar";
  55. import { useI18n } from "vue-i18n";
  56. import { useGeocodingApi } from "src/composables/useGeocodingApi";
  57. import { useGeolocation } from "src/composables/useGeolocation";
  58. const emit = defineEmits(["back", "cep-resolved", "device-location"]);
  59. const $q = useQuasar();
  60. const { t } = useI18n();
  61. const { geocodeCep } = useGeocodingApi();
  62. const { requestPermission, getCurrentPosition } = useGeolocation();
  63. const cep = ref("");
  64. const loadingCep = ref(false);
  65. const loadingLocation = ref(false);
  66. const onCepChange = async (val) => {
  67. const cleaned = val?.replace(/\D/g, "") ?? "";
  68. if (cleaned.length !== 8) {
  69. return;
  70. }
  71. loadingCep.value = true;
  72. try {
  73. const result = await geocodeCep(cleaned);
  74. if (!result) {
  75. $q.notify({
  76. message: t("auth.cep_not_found"),
  77. type: "negative",
  78. });
  79. return;
  80. }
  81. emit("cep-resolved", result);
  82. } catch {
  83. $q.notify({
  84. message: t("auth.cep_not_found"),
  85. type: "negative",
  86. });
  87. } finally {
  88. loadingCep.value = false;
  89. }
  90. };
  91. const useLocation = async () => {
  92. loadingLocation.value = true;
  93. try {
  94. const granted = await requestPermission();
  95. if (!granted) {
  96. $q.notify({
  97. message: t("auth.location_permission_denied"),
  98. type: "warning",
  99. });
  100. return;
  101. }
  102. const position = await getCurrentPosition();
  103. emit("device-location", position);
  104. } catch (err) {
  105. const isPermissionError =
  106. err?.code === 1 || err?.message?.toLowerCase().includes("denied");
  107. $q.notify({
  108. message: isPermissionError
  109. ? t("auth.location_permission_denied")
  110. : t("auth.location_error"),
  111. type: "warning",
  112. });
  113. } finally {
  114. loadingLocation.value = false;
  115. }
  116. };
  117. </script>
  118. <style lang="scss" scoped>
  119. .step4-card {
  120. border-radius: 30px;
  121. max-width: 340px;
  122. position: relative;
  123. width: 100%;
  124. z-index: 1;
  125. :deep(.q-field__marginal) {
  126. height: 44px;
  127. }
  128. }
  129. </style>