|
|
@@ -2,7 +2,7 @@
|
|
|
<q-card-section class="no-padding">
|
|
|
<div class="text-text">
|
|
|
<span class="font12 fontbold">
|
|
|
- {{ $t('provider.login.steps.step_3.full_name') }}
|
|
|
+ {{ $t("provider.login.steps.step_3.full_name") }}
|
|
|
</span>
|
|
|
</div>
|
|
|
|
|
|
@@ -21,7 +21,7 @@
|
|
|
|
|
|
<div class="text-text">
|
|
|
<span class="font12 fontbold">
|
|
|
- {{ $t('common.terms.phone') }}
|
|
|
+ {{ $t("common.terms.phone") }}
|
|
|
</span>
|
|
|
</div>
|
|
|
|
|
|
@@ -41,7 +41,7 @@
|
|
|
|
|
|
<div class="text-text">
|
|
|
<span class="font12 fontbold">
|
|
|
- {{ $t('common.terms.email') }}
|
|
|
+ {{ $t("common.terms.email") }}
|
|
|
</span>
|
|
|
</div>
|
|
|
|
|
|
@@ -62,7 +62,7 @@
|
|
|
|
|
|
<div class="text-text">
|
|
|
<span class="font12 fontbold">
|
|
|
- {{ $t('provider.login.steps.step_3.rg') }}
|
|
|
+ {{ $t("provider.login.steps.step_3.rg") }}
|
|
|
</span>
|
|
|
</div>
|
|
|
|
|
|
@@ -83,7 +83,7 @@
|
|
|
|
|
|
<div class="text-text">
|
|
|
<span class="font12 fontbold">
|
|
|
- {{ $t('common.terms.cpf') }}
|
|
|
+ {{ $t("common.terms.cpf") }}
|
|
|
</span>
|
|
|
</div>
|
|
|
|
|
|
@@ -104,7 +104,7 @@
|
|
|
|
|
|
<div class="text-text">
|
|
|
<span class="font12 fontbold">
|
|
|
- {{ $t('provider.login.steps.step_3.birth_date') }}
|
|
|
+ {{ $t("provider.login.steps.step_3.birth_date") }}
|
|
|
</span>
|
|
|
</div>
|
|
|
|
|
|
@@ -124,7 +124,7 @@
|
|
|
|
|
|
<div class="text-text">
|
|
|
<span class="font12 fontbold">
|
|
|
- {{ $t('common.terms.cep') }}
|
|
|
+ {{ $t("common.terms.cep") }}
|
|
|
</span>
|
|
|
</div>
|
|
|
|
|
|
@@ -145,9 +145,19 @@
|
|
|
@update:model-value="onCepChange"
|
|
|
/>
|
|
|
|
|
|
+ <q-btn
|
|
|
+ class="full-width q-mb-md"
|
|
|
+ color="primary-button"
|
|
|
+ padding="8px 12px"
|
|
|
+ rounded
|
|
|
+ :label="$t('provider.login.steps.step_3.use_location')"
|
|
|
+ :loading="loadingLocation"
|
|
|
+ @click="useLocation"
|
|
|
+ />
|
|
|
+
|
|
|
<div class="text-text">
|
|
|
<span class="font12 fontbold">
|
|
|
- {{ $t('common.terms.address') }}
|
|
|
+ {{ $t("common.terms.address") }}
|
|
|
</span>
|
|
|
</div>
|
|
|
|
|
|
@@ -159,15 +169,98 @@
|
|
|
lazy-rules
|
|
|
no-error-icon
|
|
|
outlined
|
|
|
+ readonly
|
|
|
rounded
|
|
|
- :placeholder="$t('provider.login.steps.step_3.address_placeholder')"
|
|
|
+ :placeholder="`${$t('common.terms.address')}...`"
|
|
|
:rules="[inputRules.required]"
|
|
|
/>
|
|
|
|
|
|
- <div class="text-center">
|
|
|
+ <div class="row q-col-gutter-sm">
|
|
|
+ <div class="col-4">
|
|
|
+ <div class="text-text">
|
|
|
+ <span class="font12 fontbold">
|
|
|
+ {{ $t("common.terms.address_number") }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <q-input
|
|
|
+ v-model="form.number"
|
|
|
+ class="bg-surface q-mb-md q-mt-sm"
|
|
|
+ hide-bottom-space
|
|
|
+ input-class="font12 fontmedium text-text"
|
|
|
+ lazy-rules
|
|
|
+ no-error-icon
|
|
|
+ outlined
|
|
|
+ placeholder="0000"
|
|
|
+ rounded
|
|
|
+ :rules="[inputRules.required]"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="col-8">
|
|
|
+ <div class="text-text">
|
|
|
+ <span class="font12 fontbold">
|
|
|
+ {{ $t("common.terms.district") }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <q-input
|
|
|
+ v-model="form.district"
|
|
|
+ class="bg-surface q-mb-md q-mt-sm"
|
|
|
+ hide-bottom-space
|
|
|
+ input-class="font12 fontmedium text-text"
|
|
|
+ no-error-icon
|
|
|
+ outlined
|
|
|
+ readonly
|
|
|
+ rounded
|
|
|
+ :placeholder="`${$t('common.terms.district')}...`"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="row q-col-gutter-sm">
|
|
|
+ <div class="col-8">
|
|
|
+ <div class="text-text">
|
|
|
+ <span class="font12 fontbold">
|
|
|
+ {{ $t("common.terms.city") }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <q-input
|
|
|
+ v-model="form.city"
|
|
|
+ class="bg-surface q-mb-md q-mt-sm"
|
|
|
+ input-class="font12 fontmedium text-text"
|
|
|
+ no-error-icon
|
|
|
+ outlined
|
|
|
+ readonly
|
|
|
+ rounded
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="col-4">
|
|
|
+ <div class="text-text">
|
|
|
+ <span class="font12 fontbold">
|
|
|
+ {{ $t("common.terms.state") }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <q-input
|
|
|
+ v-model="form.state"
|
|
|
+ class="bg-surface q-mb-md q-mt-sm"
|
|
|
+ hide-bottom-space
|
|
|
+ input-class="font12 fontmedium text-text"
|
|
|
+ no-error-icon
|
|
|
+ outlined
|
|
|
+ readonly
|
|
|
+ rounded
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div>
|
|
|
<q-checkbox
|
|
|
v-model="form.no_complement"
|
|
|
- class="q-mb-md text-text"
|
|
|
+ class="q-mb-md text-text font12 fontbold"
|
|
|
color="primary"
|
|
|
keep-color
|
|
|
:label="$t('provider.login.steps.step_3.no_complement')"
|
|
|
@@ -177,7 +270,7 @@
|
|
|
<template v-if="!form.no_complement">
|
|
|
<div class="text-text">
|
|
|
<span class="font12 fontbold">
|
|
|
- {{ $t('common.terms.complement') }}
|
|
|
+ {{ $t("common.terms.complement") }}
|
|
|
</span>
|
|
|
</div>
|
|
|
|
|
|
@@ -194,14 +287,72 @@
|
|
|
:rules="[inputRules.required]"
|
|
|
/>
|
|
|
</template>
|
|
|
+
|
|
|
+ <div class="text-text">
|
|
|
+ <span class="font12 fontbold">
|
|
|
+ {{ $t("provider.login.steps.step_3.address_nickname") }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <q-input
|
|
|
+ v-model="form.nickname"
|
|
|
+ class="bg-surface q-mb-md q-mt-sm"
|
|
|
+ hide-bottom-space
|
|
|
+ input-class="font12 fontmedium text-text"
|
|
|
+ lazy-rules
|
|
|
+ no-error-icon
|
|
|
+ outlined
|
|
|
+ rounded
|
|
|
+ :placeholder="`${$t('common.ui.misc.example')}: Casa`"
|
|
|
+ />
|
|
|
+
|
|
|
+ <div class="text-text">
|
|
|
+ <span class="font12 fontbold">
|
|
|
+ {{ $t("provider.login.steps.step_3.address_instructions") }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <q-input
|
|
|
+ v-model="form.instructions"
|
|
|
+ autogrow
|
|
|
+ class="bg-surface q-mb-md q-mt-sm"
|
|
|
+ hide-bottom-space
|
|
|
+ input-class="font12 fontmedium text-text"
|
|
|
+ lazy-rules
|
|
|
+ no-error-icon
|
|
|
+ outlined
|
|
|
+ rounded
|
|
|
+ rows="3"
|
|
|
+ type="textarea"
|
|
|
+ />
|
|
|
+
|
|
|
+ <div class="row q-gutter-sm q-mt-xs">
|
|
|
+ <q-chip
|
|
|
+ v-for="type in addressTypes"
|
|
|
+ :key="type.value"
|
|
|
+ :selected="form.address_type === type.value"
|
|
|
+ clickable
|
|
|
+ color="primary"
|
|
|
+ text-color="surface"
|
|
|
+ :icon="type.icon"
|
|
|
+ :icon-selected="type.icon"
|
|
|
+ :outline="form.address_type !== type.value"
|
|
|
+ @click="form.address_type = type.value"
|
|
|
+ >
|
|
|
+ {{ $t(type.label) }}
|
|
|
+ </q-chip>
|
|
|
+ </div>
|
|
|
</q-card-section>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import axios from 'axios';
|
|
|
-import { useInputRules } from 'src/composables/useInputRules';
|
|
|
-import { ref } from 'vue';
|
|
|
-import { useI18n } from 'vue-i18n';
|
|
|
+import axios from "axios";
|
|
|
+import { useQuasar } from "quasar";
|
|
|
+import LocationMapDialog from "src/components/shared/LocationMapDialog.vue";
|
|
|
+import { useGeolocation } from "src/composables/useGeolocation";
|
|
|
+import { useInputRules } from "src/composables/useInputRules";
|
|
|
+import { ref } from "vue";
|
|
|
+import { useI18n } from "vue-i18n";
|
|
|
|
|
|
const form = defineModel({
|
|
|
required: true,
|
|
|
@@ -210,11 +361,60 @@ const form = defineModel({
|
|
|
|
|
|
const { inputRules } = useInputRules();
|
|
|
const { t } = useI18n();
|
|
|
+const $q = useQuasar();
|
|
|
+const { requestPermission, getCurrentPosition } = useGeolocation();
|
|
|
|
|
|
const loadingCep = ref(false);
|
|
|
+const loadingLocation = ref(false);
|
|
|
+
|
|
|
+const addressTypes = [
|
|
|
+ {
|
|
|
+ icon: "mdi-home-outline",
|
|
|
+ label: "provider.login.steps.step_3.address_type_home",
|
|
|
+ value: "home",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ icon: "mdi-briefcase-variant-outline",
|
|
|
+ label: "provider.login.steps.step_3.address_type_commercial",
|
|
|
+ value: "commercial",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ icon: "mdi-map-marker-outline",
|
|
|
+ label: "provider.login.steps.step_3.address_type_other",
|
|
|
+ value: "other",
|
|
|
+ },
|
|
|
+];
|
|
|
+
|
|
|
+const clearCepFields = () => {
|
|
|
+ form.value.address = "";
|
|
|
+ form.value.district = "";
|
|
|
+ form.value.city = "";
|
|
|
+ form.value.state = "";
|
|
|
+ form.value.latitude = null;
|
|
|
+ form.value.longitude = null;
|
|
|
+};
|
|
|
+
|
|
|
+const fillAddressFields = (data) => {
|
|
|
+ const hasField = (field) => Object.prototype.hasOwnProperty.call(data, field);
|
|
|
+
|
|
|
+ form.value.address = data.address ?? "";
|
|
|
+ form.value.number = data.number || form.value.number || "";
|
|
|
+ form.value.district = data.district ?? "";
|
|
|
+ form.value.city = data.city ?? "";
|
|
|
+ form.value.state = data.state ?? "";
|
|
|
+ form.value.zip_code = data.zip_code || form.value.zip_code || "";
|
|
|
+
|
|
|
+ if (hasField("lat")) {
|
|
|
+ form.value.latitude = data.lat;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (hasField("lng")) {
|
|
|
+ form.value.longitude = data.lng;
|
|
|
+ }
|
|
|
+};
|
|
|
|
|
|
const fetchCep = async (rawCep) => {
|
|
|
- const cleaned = rawCep.replace(/\D/g, '');
|
|
|
+ const cleaned = rawCep.replace(/\D/g, "");
|
|
|
|
|
|
if (cleaned.length !== 8) {
|
|
|
return;
|
|
|
@@ -223,43 +423,89 @@ const fetchCep = async (rawCep) => {
|
|
|
loadingCep.value = true;
|
|
|
|
|
|
try {
|
|
|
- const { data } = await axios.get(`https://viacep.com.br/ws/${cleaned}/json/`);
|
|
|
+ const { data } = await axios.get(
|
|
|
+ `https://viacep.com.br/ws/${cleaned}/json/`,
|
|
|
+ );
|
|
|
|
|
|
if (!data.erro) {
|
|
|
- form.value.address = `${data.logradouro}, ${data.bairro} - ${data.localidade}/${data.uf}`;
|
|
|
- form.value.city = data.localidade;
|
|
|
- form.value.state = data.uf;
|
|
|
+ fillAddressFields({
|
|
|
+ address: data.logradouro,
|
|
|
+ district: data.bairro,
|
|
|
+ city: data.localidade,
|
|
|
+ state: data.uf,
|
|
|
+ lat: null,
|
|
|
+ lng: null,
|
|
|
+ });
|
|
|
} else {
|
|
|
- form.value.address = '';
|
|
|
- form.value.city = '';
|
|
|
- form.value.state = '';
|
|
|
+ clearCepFields();
|
|
|
}
|
|
|
} catch {
|
|
|
- form.value.address = '';
|
|
|
- form.value.city = '';
|
|
|
- form.value.state = '';
|
|
|
+ clearCepFields();
|
|
|
} finally {
|
|
|
loadingCep.value = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
const onCepChange = (value) => {
|
|
|
- const cleaned = value?.replace(/\D/g, '') || '';
|
|
|
+ const cleaned = value?.replace(/\D/g, "") || "";
|
|
|
|
|
|
if (cleaned.length === 8) {
|
|
|
fetchCep(value);
|
|
|
+ } else {
|
|
|
+ clearCepFields();
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const useLocation = async () => {
|
|
|
+ loadingLocation.value = true;
|
|
|
+
|
|
|
+ try {
|
|
|
+ const granted = await requestPermission();
|
|
|
+
|
|
|
+ if (!granted) {
|
|
|
+ $q.notify({
|
|
|
+ message: t("provider.login.steps.step_3.location_permission_denied"),
|
|
|
+ type: "warning",
|
|
|
+ });
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const position = await getCurrentPosition();
|
|
|
+
|
|
|
+ $q.dialog({
|
|
|
+ component: LocationMapDialog,
|
|
|
+ componentProps: {
|
|
|
+ initialLat: position.lat,
|
|
|
+ initialLng: position.lng,
|
|
|
+ },
|
|
|
+ }).onOk((geoData) => {
|
|
|
+ fillAddressFields(geoData);
|
|
|
+ });
|
|
|
+ } catch (err) {
|
|
|
+ const isPermissionError =
|
|
|
+ err?.code === 1 || err?.message?.toLowerCase().includes("denied");
|
|
|
+
|
|
|
+ $q.notify({
|
|
|
+ message: isPermissionError
|
|
|
+ ? t("provider.login.steps.step_3.location_permission_denied")
|
|
|
+ : t("provider.login.steps.step_3.location_error"),
|
|
|
+ type: "warning",
|
|
|
+ });
|
|
|
+ } finally {
|
|
|
+ loadingLocation.value = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
const validateBirthDate = (value) => {
|
|
|
if (!value) {
|
|
|
- return t('validation.rules.required');
|
|
|
+ return t("validation.rules.required");
|
|
|
}
|
|
|
|
|
|
const matches = /^(\d{2})\/(\d{2})\/(\d{4})$/.exec(value);
|
|
|
|
|
|
if (!matches) {
|
|
|
- return t('provider.login.steps.step_3.date_invalid');
|
|
|
+ return t("provider.login.steps.step_3.date_invalid");
|
|
|
}
|
|
|
|
|
|
const day = Number(matches[1]);
|
|
|
@@ -268,24 +514,26 @@ const validateBirthDate = (value) => {
|
|
|
|
|
|
const date = new Date(year, month - 1, day);
|
|
|
const isValidDate =
|
|
|
- date.getFullYear() === year
|
|
|
- && date.getMonth() === month - 1
|
|
|
- && date.getDate() === day;
|
|
|
+ date.getFullYear() === year &&
|
|
|
+ date.getMonth() === month - 1 &&
|
|
|
+ date.getDate() === day;
|
|
|
|
|
|
if (!isValidDate) {
|
|
|
- return t('provider.login.steps.step_3.date_invalid');
|
|
|
+ return t("provider.login.steps.step_3.date_invalid");
|
|
|
}
|
|
|
|
|
|
if (date > new Date()) {
|
|
|
- return t('provider.login.steps.step_3.date_invalid');
|
|
|
+ return t("provider.login.steps.step_3.date_invalid");
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
};
|
|
|
|
|
|
const validateRG = (value) => {
|
|
|
- const cleanedValue = value?.replace(/\D/g, '') || '';
|
|
|
+ const cleanedValue = value?.replace(/\D/g, "") || "";
|
|
|
|
|
|
- return cleanedValue.length >= 8 || t('provider.login.steps.step_3.rg_invalid');
|
|
|
+ return (
|
|
|
+ cleanedValue.length >= 8 || t("provider.login.steps.step_3.rg_invalid")
|
|
|
+ );
|
|
|
};
|
|
|
-</script>
|
|
|
+</script>
|