Просмотр исходного кода

feat: ✨ feat (prestador pendente) adicionada nova tela de prestador pendente na dashboard

foi adicionada nova tela indicando que o prestador logado ainda ta pendente (antes a tela era 'fora do sistema')

fase:dev | origin:escopo
Gustavo Zanatta 1 неделя назад
Родитель
Сommit
2f0c16df8a

+ 4 - 0
src/components/dashboard/DashboardHeaderBar.vue

@@ -44,6 +44,7 @@
     <q-btn
     <q-btn
       color="grey-7"
       color="grey-7"
       dense
       dense
+      :disable="isPendingProvider"
       flat
       flat
       round
       round
       size="sm"
       size="sm"
@@ -70,12 +71,15 @@
 
 
 <script setup>
 <script setup>
 import { computed } from 'vue'
 import { computed } from 'vue'
+import { useProviderApproval } from 'src/composables/useProviderApproval'
 import { useRouter } from 'vue-router'
 import { useRouter } from 'vue-router'
 
 
 import LogoDiariaColorida from 'src/assets/logo_diaria_colorido_sem_texto.svg'
 import LogoDiariaColorida from 'src/assets/logo_diaria_colorido_sem_texto.svg'
 
 
 const router = useRouter()
 const router = useRouter()
 
 
+const { isPendingProvider } = useProviderApproval()
+
 const props = defineProps({
 const props = defineProps({
   data: {
   data: {
     type: Object,
     type: Object,

+ 50 - 0
src/components/dashboard/DashboardPendingApproval.vue

@@ -0,0 +1,50 @@
+<template>
+  <div class="q-mx-md q-mb-md">
+    <div class="font16 fontbold gradient-diarista q-mb-sm section-title">
+      {{ $t("provider.dashboard.pending_approval.title") }}
+    </div>
+
+    <div class="pending-alert column items-center text-center">
+      <q-icon
+        class="pending-alert__icon q-mb-sm"
+        name="mdi-alert-outline"
+        size="28px"
+      />
+
+      <div class="pending-alert__text font14 fontbold q-mb-md">
+        {{ $t("provider.dashboard.pending_approval.analyzing") }}
+      </div>
+
+      <div class="pending-alert__text font14 fontbold q-mb-md">
+        {{ $t("provider.dashboard.pending_approval.release") }}
+      </div>
+
+      <div class="pending-alert__text font14 fontregular">
+        {{ $t("provider.dashboard.pending_approval.come_back_later") }}
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+defineOptions({
+  name: "DashboardPendingApproval",
+});
+</script>
+
+<style lang="scss" scoped>
+.pending-alert {
+  background: #e3efff;
+  border-radius: 8px;
+  padding: 24px 20px;
+}
+
+.pending-alert__icon {
+  color: #6554d9;
+}
+
+.pending-alert__text {
+  color: #6554d9;
+  line-height: 1.3;
+}
+</style>

+ 27 - 4
src/composables/useAuth.js

@@ -1,6 +1,7 @@
 import api from "src/api";
 import api from "src/api";
 import { permissionStore } from "src/stores/permission";
 import { permissionStore } from "src/stores/permission";
 import { useAuthStorage } from "src/composables/useAuthStorage";
 import { useAuthStorage } from "src/composables/useAuthStorage";
+import { APPROVAL_STATUS, useProviderApproval } from "src/composables/useProviderApproval";
 import { userStore } from "src/stores/user";
 import { userStore } from "src/stores/user";
 
 
 let refreshPromise = null;
 let refreshPromise = null;
@@ -29,10 +30,7 @@ export const useAuth = () => {
       await store.fetchUser();
       await store.fetchUser();
     }
     }
 
 
-    if (
-      String(store.user?.type).toUpperCase() === "PROVIDER" &&
-      store.user?.provider?.approval_status === "pending"
-    ) {
+    if (useProviderApproval().isPendingProvider.value) {
       permissionStore().resetScopes();
       permissionStore().resetScopes();
 
 
       return;
       return;
@@ -41,6 +39,30 @@ export const useAuth = () => {
     await permissionStore().fetchScopes();
     await permissionStore().fetchScopes();
   };
   };
 
 
+  /**
+   * Reconsulta o cadastro do prestador em análise e, se ele já tiver sido
+   * aprovado, carrega os escopos para liberar o app sem exigir novo login.
+   *
+   * @return {Promise<boolean>} true quando o cadastro acabou de ser aprovado.
+   */
+  const refreshApprovalStatus = async () => {
+    const { approvalStatus, isPendingProvider } = useProviderApproval();
+
+    if (!isPendingProvider.value) {
+      return false;
+    }
+
+    await userStore().fetchUser();
+
+    if (approvalStatus.value !== APPROVAL_STATUS.ACCEPTED) {
+      return false;
+    }
+
+    await permissionStore().fetchScopes();
+
+    return true;
+  };
+
   //
   //
 
 
   const login = async (email, password) => {
   const login = async (email, password) => {
@@ -115,6 +137,7 @@ export const useAuth = () => {
     login,
     login,
     logout,
     logout,
     refresh,
     refresh,
+    refreshApprovalStatus,
     setAuthDataFromPayload,
     setAuthDataFromPayload,
   };
   };
 };
 };

+ 33 - 0
src/composables/useProviderApproval.js

@@ -0,0 +1,33 @@
+import { computed } from "vue";
+import { userStore } from "src/stores/user";
+
+export const APPROVAL_STATUS = Object.freeze({
+  ACCEPTED: "accepted",
+  PENDING: "pending",
+  REJECTED: "rejected",
+});
+
+export const useProviderApproval = () => {
+  const user = userStore();
+
+  const isProvider = computed(
+    () => String(user.user?.type).toUpperCase() === "PROVIDER",
+  );
+
+  const approvalStatus = computed(() => user.user?.provider?.approval_status ?? null);
+
+  const isPendingProvider = computed(
+    () => isProvider.value && approvalStatus.value === APPROVAL_STATUS.PENDING,
+  );
+
+  const isRejectedProvider = computed(
+    () => isProvider.value && approvalStatus.value === APPROVAL_STATUS.REJECTED,
+  );
+
+  return {
+    approvalStatus,
+    isPendingProvider,
+    isProvider,
+    isRejectedProvider,
+  };
+};

+ 7 - 0
src/i18n/locales/en.json

@@ -265,6 +265,13 @@
       }
       }
     },
     },
     "dashboard": {
     "dashboard": {
+      "pending_approval": {
+        "title": "Profile under review",
+        "analyzing": "Your profile is being reviewed by the diária app team.",
+        "release": "Your access will be released shortly, if approved.",
+        "come_back_later": "Please come back later.",
+        "approved": "Registration approved! Welcome to diária app."
+      },
       "summary": {
       "summary": {
         "welcome": "Hello,",
         "welcome": "Hello,",
         "my_schedules": "My schedules"
         "my_schedules": "My schedules"

+ 7 - 0
src/i18n/locales/es.json

@@ -265,6 +265,13 @@
       }
       }
     },
     },
     "dashboard": {
     "dashboard": {
+      "pending_approval": {
+        "title": "Perfil en revisión",
+        "analyzing": "Tu perfil está siendo analizado por el equipo de diária app.",
+        "release": "En breve se liberará tu acceso, en caso de ser aprobado.",
+        "come_back_later": "Por favor, vuelve más tarde.",
+        "approved": "¡Registro aprobado! Bienvenido a diária app."
+      },
       "summary": {
       "summary": {
         "welcome": "Hola,",
         "welcome": "Hola,",
         "my_schedules": "Mis citas"
         "my_schedules": "Mis citas"

+ 7 - 0
src/i18n/locales/pt.json

@@ -265,6 +265,13 @@
       }
       }
     },
     },
     "dashboard": {
     "dashboard": {
+      "pending_approval": {
+        "title": "Perfil em análise",
+        "analyzing": "Seu perfil está sendo analisado pela equipe do diária app.",
+        "release": "Em breve será liberado seu acesso, caso seja aprovado.",
+        "come_back_later": "Por favor, volte mais tarde.",
+        "approved": "Cadastro aprovado! Boas-vindas ao diária app."
+      },
       "summary": {
       "summary": {
         "welcome": "Olá,",
         "welcome": "Olá,",
         "my_schedules": "Minhas diárias"
         "my_schedules": "Minhas diárias"

+ 25 - 4
src/layouts/MainLayout.vue

@@ -29,11 +29,15 @@
 
 
     <q-footer class="provider-bottom-nav bg-white">
     <q-footer class="provider-bottom-nav bg-white">
       <nav class="provider-bottom-nav-inner">
       <nav class="provider-bottom-nav-inner">
-        <router-link
+        <component
+          :is="item.disabled ? 'div' : 'router-link'"
           v-for="item in navItems"
           v-for="item in navItems"
           :key="item.name"
           :key="item.name"
-          :class="{ 'provider-bottom-nav-item--active': isNavItemActive(item) }"
-          :to="{ name: item.name }"
+          :class="{
+            'provider-bottom-nav-item--active': isNavItemActive(item),
+            'provider-bottom-nav-item--disabled': item.disabled,
+          }"
+          :to="item.disabled ? undefined : { name: item.name }"
           class="provider-bottom-nav-item"
           class="provider-bottom-nav-item"
         >
         >
           <q-icon :name="item.icon" class="provider-bottom-nav-icon font30" />
           <q-icon :name="item.icon" class="provider-bottom-nav-icon font30" />
@@ -44,7 +48,7 @@
           >
           >
             {{ item.label }}
             {{ item.label }}
           </span>
           </span>
-        </router-link>
+        </component>
       </nav>
       </nav>
 
 
       <div class="system-bar-spacer provider-bottom-nav-safe-area"></div>
       <div class="system-bar-spacer provider-bottom-nav-safe-area"></div>
@@ -57,6 +61,7 @@ import { computed, onMounted, useTemplateRef, watch } from "vue";
 import { useI18n } from "vue-i18n";
 import { useI18n } from "vue-i18n";
 import { useQuasar } from "quasar";
 import { useQuasar } from "quasar";
 import { useRoute } from "vue-router";
 import { useRoute } from "vue-router";
+import { useProviderApproval } from "src/composables/useProviderApproval";
 import { userStore } from "src/stores/user";
 import { userStore } from "src/stores/user";
 import MissingBankDataDialog from "src/components/profile/MissingBankDataDialog.vue";
 import MissingBankDataDialog from "src/components/profile/MissingBankDataDialog.vue";
 import ProfileBankDataDialog from "src/components/profile/ProfileBankDataDialog.vue";
 import ProfileBankDataDialog from "src/components/profile/ProfileBankDataDialog.vue";
@@ -70,6 +75,7 @@ const scrollAreaRef = useTemplateRef("scrollAreaRef");
 const { t } = useI18n();
 const { t } = useI18n();
 const $q = useQuasar();
 const $q = useQuasar();
 const user = userStore();
 const user = userStore();
+const { isPendingProvider } = useProviderApproval();
 
 
 let oldValue = route.path;
 let oldValue = route.path;
 
 
@@ -80,21 +86,25 @@ const navItems = computed(() => [
     name: "DashboardPage",
     name: "DashboardPage",
     label: t('nav.home'),
     label: t('nav.home'),
     icon: "mdi-home-outline",
     icon: "mdi-home-outline",
+    disabled: false,
   },
   },
   {
   {
     name: "PaymentsPage",
     name: "PaymentsPage",
     label: t('nav.payments'),
     label: t('nav.payments'),
     icon: "mdi-credit-card-outline",
     icon: "mdi-credit-card-outline",
+    disabled: isPendingProvider.value,
   },
   },
   {
   {
     name: "CalendarPage",
     name: "CalendarPage",
     label: t('nav.agenda'),
     label: t('nav.agenda'),
     icon: "mdi-calendar-blank-outline",
     icon: "mdi-calendar-blank-outline",
+    disabled: isPendingProvider.value,
   },
   },
   {
   {
     name: "ProfilePage",
     name: "ProfilePage",
     label: t('nav.profile'),
     label: t('nav.profile'),
     icon: "mdi-account-circle-outline",
     icon: "mdi-account-circle-outline",
+    disabled: false,
   },
   },
 ]);
 ]);
 
 
@@ -107,6 +117,11 @@ const checkMissingBankData = async () => {
     return;
     return;
   }
   }
 
 
+  // Cadastro em análise não edita dados bancários.
+  if (isPendingProvider.value) {
+    return;
+  }
+
   try {
   try {
     await user.fetchUser();
     await user.fetchUser();
   } catch (error) {
   } catch (error) {
@@ -248,6 +263,12 @@ onMounted(() => {
   color: #ff00ea;
   color: #ff00ea;
 }
 }
 
 
+.provider-bottom-nav-item--disabled {
+  color: #d4d4d4;
+  cursor: default;
+  pointer-events: none;
+}
+
 .provider-bottom-nav-icon {
 .provider-bottom-nav-icon {
   font-size: 24px;
   font-size: 24px;
   line-height: 1;
   line-height: 1;

+ 4 - 145
src/pages/LoginPage.vue

@@ -25,53 +25,7 @@
       </div>
       </div>
 
 
       <div v-else key="flow" class="flow-screen">
       <div v-else key="flow" class="flow-screen">
-        <div
-          v-if="registrationCompleted"
-          class="registration-flow"
-        >
-          <div class="flow-header row items-center">
-            <q-btn
-              class="flow-back-btn"
-              color="primary"
-              dense
-              flat
-              round
-              :aria-label="t('auth.logout')"
-              icon="mdi-logout"
-              @click="goBack"
-            />
-          </div>
-
-          <div class="flow-logo q-my-xl">
-            <q-img
-              style="max-width: 180px"
-              :src="LogoDiariaCampos"
-            />
-          </div>
-
-          <div class="registration-complete">
-            <div class="registration-complete__card row no-wrap items-start">
-              <q-icon
-                class="registration-complete__icon"
-                name="mdi-clock-time-four-outline"
-                size="32px"
-              />
-
-              <div class="registration-complete__content">
-                <div class="registration-complete__title fontbold">
-                  {{ t("provider.login.registration_complete.title") }}
-                </div>
-
-                <div class="registration-complete__message font12">
-                  {{ t("provider.login.registration_complete.message") }}
-                </div>
-              </div>
-            </div>
-          </div>
-        </div>
-
         <q-form
         <q-form
-          v-else
           ref="loginForm"
           ref="loginForm"
           autocapitalize="off"
           autocapitalize="off"
           autocomplete="off"
           autocomplete="off"
@@ -186,7 +140,6 @@ import { useQuasar } from "quasar";
 import { useRouter } from "vue-router";
 import { useRouter } from "vue-router";
 import { useScroll } from "src/composables/useScroll";
 import { useScroll } from "src/composables/useScroll";
 import { useSubmitHandler } from "src/composables/useSubmitHandler";
 import { useSubmitHandler } from "src/composables/useSubmitHandler";
-import { userStore } from "src/stores/user";
 
 
 import BackgroundLogin from "src/assets/background-login.svg";
 import BackgroundLogin from "src/assets/background-login.svg";
 import FotoDiarista from "src/assets/foto_diarista_login.svg";
 import FotoDiarista from "src/assets/foto_diarista_login.svg";
@@ -201,8 +154,7 @@ import LoginStep6Panel from "src/components/login/LoginStep6Panel.vue";
 
 
 const $q = useQuasar();
 const $q = useQuasar();
 const router = useRouter();
 const router = useRouter();
-const user = userStore();
-const { clearAuthData, logout, setAuthDataFromPayload } = useAuth();
+const { setAuthDataFromPayload } = useAuth();
 const { t } = useI18n();
 const { t } = useI18n();
 const { scrollToComponent } = useScroll();
 const { scrollToComponent } = useScroll();
 
 
@@ -263,16 +215,9 @@ const email = ref("");
 const isLogin = ref(false);
 const isLogin = ref(false);
 const loginForm = ref(null);
 const loginForm = ref(null);
 const phone = ref("");
 const phone = ref("");
-const registrationCompleted = ref(false);
 const showSubStep = ref(false);
 const showSubStep = ref(false);
 const smsEnabled = ref(false);
 const smsEnabled = ref(false);
 
 
-const isPendingProvider = computed(
-  () =>
-    String(user.user?.type).toUpperCase() === "PROVIDER" &&
-    user.user?.provider?.approval_status === "pending",
-);
-
 const createInitialStepFiveForm = () => ({
 const createInitialStepFiveForm = () => ({
   daily_price_2h: null,
   daily_price_2h: null,
   daily_price_4h: null,
   daily_price_4h: null,
@@ -341,21 +286,11 @@ const actionLabel = computed(() => {
   return t("provider.login.steps.step_3.action");
   return t("provider.login.steps.step_3.action");
 });
 });
 
 
-const showBackButton = computed(
-  () => registrationCompleted.value || steps.value >= 4,
-);
+const showBackButton = computed(() => steps.value >= 4);
 
 
 //
 //
 
 
 const goBack = async () => {
 const goBack = async () => {
-  if (registrationCompleted.value) {
-    await logout();
-    await resetToLoginStart();
-    router.replace({ name: "LoginPage" });
-
-    return;
-  }
-
   if (steps.value === 4) {
   if (steps.value === 4) {
     steps.value = 3;
     steps.value = 3;
   } else if (steps.value === 5) {
   } else if (steps.value === 5) {
@@ -365,24 +300,6 @@ const goBack = async () => {
   }
   }
 };
 };
 
 
-const resetToLoginStart = async () => {
-  await clearAuthData();
-
-  code.value = "";
-  email.value = "";
-  isLogin.value = false;
-  phone.value = "";
-  registrationCompleted.value = false;
-  showSubStep.value = false;
-  loading.value = false;
-  stepFiveForm.value = createInitialStepFiveForm();
-  stepFourForm.value = createInitialStepFourForm();
-  stepSixForm.value = createInitialStepSixForm();
-  stepThreeForm.value = createInitialStepThreeForm();
-  steps.value = 1;
-  loginForm.value?.resetValidation?.();
-};
-
 //
 //
 
 
 const hasWorkingDaySelected = () => mapWorkingDays().length > 0;
 const hasWorkingDaySelected = () => mapWorkingDays().length > 0;
@@ -431,7 +348,6 @@ const onValidationError = async (field) => {
 };
 };
 
 
 const onSubmit = async () => {
 const onSubmit = async () => {
-  if (registrationCompleted.value) return;
   if (showSubStep.value) return;
   if (showSubStep.value) return;
 
 
   const isValid = await loginForm.value.validate();
   const isValid = await loginForm.value.validate();
@@ -638,7 +554,8 @@ const registerUserAndProvider = async () => {
 
 
   if (response?.status >= 200 && response.status < 300) {
   if (response?.status >= 200 && response.status < 300) {
     await setAuthDataFromPayload(response.data.payload);
     await setAuthDataFromPayload(response.data.payload);
-    registrationCompleted.value = true;
+
+    router.push({ name: "DashboardPage" });
   }
   }
 
 
   return response;
   return response;
@@ -674,12 +591,6 @@ const validateCodeInput = async () => {
     if (isLogin.value === true) {
     if (isLogin.value === true) {
       await setAuthDataFromPayload(response.data.payload);
       await setAuthDataFromPayload(response.data.payload);
 
 
-      if (isPendingProvider.value) {
-        registrationCompleted.value = true;
-
-        return;
-      }
-
       router.push({ name: "DashboardPage" });
       router.push({ name: "DashboardPage" });
 
 
       return;
       return;
@@ -735,13 +646,6 @@ const validateCurrentStep = async () => {
 onMounted(() => {
 onMounted(() => {
   loadServiceConfiguration();
   loadServiceConfiguration();
 
 
-  if (user.accessToken && isPendingProvider.value) {
-    registrationCompleted.value = true;
-    clicked.value = true;
-
-    return;
-  }
-
   setTimeout(() => {
   setTimeout(() => {
     clicked.value = true;
     clicked.value = true;
   }, 1500);
   }, 1500);
@@ -879,49 +783,4 @@ onMounted(() => {
   background: var(--q-surface-dark);
   background: var(--q-surface-dark);
 }
 }
 
 
-.registration-flow {
-  height: 100%;
-  display: flex;
-  flex-direction: column;
-  padding: 16px 20px;
-}
-
-.registration-complete {
-  flex: 1;
-  min-height: 0;
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-  padding: 24px 0 80px;
-  transform: translateY(-64px);
-
-  &__card {
-    width: 100%;
-    max-width: 420px;
-    gap: 14px;
-    padding: 18px;
-    background: #e3efff;
-    border-radius: 12px;
-    color: #6554d9;
-  }
-
-  &__icon {
-    flex: 0 0 auto;
-  }
-
-  &__content {
-    min-width: 0;
-  }
-
-  &__title {
-    font-size: 16px;
-    line-height: 1.25;
-    margin-bottom: 6px;
-  }
-
-  &__message {
-    line-height: 1.35;
-  }
-}
 </style>
 </style>

+ 107 - 33
src/pages/dashboard/DashboardPage.vue

@@ -15,37 +15,41 @@
 
 
         <DashboardSummaryInfos :data="summaryInfos" />
         <DashboardSummaryInfos :data="summaryInfos" />
 
 
-        <DashboardPriceSuggest :data="priceSuggestion" />
-
-        <DashboardTodayServices
-          v-if="todayServices?.length > 0"
-          :data="todayServices"
-          @refresh="loadDashboard"
-          @rate="openRatingDialog"
-        />
-
-        <DashboardScrollAreaSchedules />
-
-        <DashboardSolicitations
-          v-if="solicitations?.length > 0"
-          :data="solicitations"
-          @accept="(item) => openDetailsDialog(item)"
-          @reject="(item) => openDetailsDialog(item, 'confirm-reject')"
-          @view-details="(item) => openDetailsDialog(item)"
-        />
-
-        <DashboardPendingConfirmation
-         v-if="pendingConfirmation.length > 0"
-        :data="pendingConfirmation"
-        @view-details="(item) => openNextScheduleDialog(item)" 
-        />
-
-        <DashboardNextSchedules
-           :data="nextSchedules"
-          @view-details="(item) => openNextScheduleDialog(item)"
-        />
-
-        <DashboardOpportunities :data="opportunities" />
+        <DashboardPendingApproval v-if="isPendingProvider" />
+
+        <template v-else>
+          <DashboardPriceSuggest :data="priceSuggestion" />
+
+          <DashboardTodayServices
+            v-if="todayServices?.length > 0"
+            :data="todayServices"
+            @refresh="loadDashboard"
+            @rate="openRatingDialog"
+          />
+
+          <DashboardScrollAreaSchedules />
+
+          <DashboardSolicitations
+            v-if="solicitations?.length > 0"
+            :data="solicitations"
+            @accept="(item) => openDetailsDialog(item)"
+            @reject="(item) => openDetailsDialog(item, 'confirm-reject')"
+            @view-details="(item) => openDetailsDialog(item)"
+          />
+
+          <DashboardPendingConfirmation
+            v-if="pendingConfirmation.length > 0"
+            :data="pendingConfirmation"
+            @view-details="(item) => openNextScheduleDialog(item)"
+          />
+
+          <DashboardNextSchedules
+            :data="nextSchedules"
+            @view-details="(item) => openNextScheduleDialog(item)"
+          />
+
+          <DashboardOpportunities :data="opportunities" />
+        </template>
       </q-pull-to-refresh>
       </q-pull-to-refresh>
     </template>
     </template>
   </div>
   </div>
@@ -53,14 +57,18 @@
 
 
 <script setup>
 <script setup>
 import { dadosDashboard } from "src/api/dashboard";
 import { dadosDashboard } from "src/api/dashboard";
-import { onMounted, ref, watch } from "vue";
+import { onMounted, onUnmounted, ref, watch } from "vue";
 import { acceptServicePackage, rejectServicePackage, updateScheduleStatus } from "src/api/schedule";
 import { acceptServicePackage, rejectServicePackage, updateScheduleStatus } from "src/api/schedule";
+import { useI18n } from "vue-i18n";
 import { useQuasar } from "quasar";
 import { useQuasar } from "quasar";
+import { useAuth } from "src/composables/useAuth";
+import { useProviderApproval } from "src/composables/useProviderApproval";
 import { useRoute, useRouter } from "vue-router";
 import { useRoute, useRouter } from "vue-router";
 
 
 import DashboardHeaderBar from "src/components/dashboard/DashboardHeaderBar.vue";
 import DashboardHeaderBar from "src/components/dashboard/DashboardHeaderBar.vue";
 import DashboardNextSchedules from "src/components/dashboard/DashboardNextSchedules.vue";
 import DashboardNextSchedules from "src/components/dashboard/DashboardNextSchedules.vue";
 import DashboardOpportunities from "src/components/dashboard/DashboardOpportunities.vue";
 import DashboardOpportunities from "src/components/dashboard/DashboardOpportunities.vue";
+import DashboardPendingApproval from "src/components/dashboard/DashboardPendingApproval.vue";
 import DashboardPriceSuggest from "src/components/dashboard/DashboardPriceSuggest.vue";
 import DashboardPriceSuggest from "src/components/dashboard/DashboardPriceSuggest.vue";
 import DashboardScrollAreaSchedules from "src/components/dashboard/DashboardScrollAreaSchedules.vue";
 import DashboardScrollAreaSchedules from "src/components/dashboard/DashboardScrollAreaSchedules.vue";
 import DashboardSolicitations from "src/components/dashboard/DashboardSolicitations.vue";
 import DashboardSolicitations from "src/components/dashboard/DashboardSolicitations.vue";
@@ -76,6 +84,15 @@ import DashboardPendingConfirmation from "src/components/dashboard/DashboardPend
 const $q = useQuasar();
 const $q = useQuasar();
 const router = useRouter();
 const router = useRouter();
 const route = useRoute();
 const route = useRoute();
+const { t } = useI18n();
+const { refreshApprovalStatus } = useAuth();
+const { isPendingProvider } = useProviderApproval();
+
+// Enquanto o cadastro está em análise, reconsulta a aprovação periodicamente
+// para liberar a dashboard completa sem exigir novo login.
+const APPROVAL_POLL_INTERVAL_MS = 3 * 60 * 1000;
+
+let approvalPollId = null;
 
 
 const headerBar = ref({});
 const headerBar = ref({});
 const loading = ref(true);
 const loading = ref(true);
@@ -124,9 +141,60 @@ const loadDashboard = async () => {
   }
   }
 };
 };
 
 
-const onRefresh = async (done) => {
+const checkApprovalStatus = async () => {
+  if (!isPendingProvider.value) {
+    return false;
+  }
+
   try {
   try {
+    const approved = await refreshApprovalStatus();
+
+    if (!approved) {
+      return false;
+    }
+
+    stopApprovalPolling();
+
+    $q.notify({
+      message: t("provider.dashboard.pending_approval.approved"),
+      type: "positive",
+    });
+
     await loadDashboard();
     await loadDashboard();
+
+    return true;
+  } catch (error) {
+    console.error(error);
+
+    return false;
+  }
+};
+
+const startApprovalPolling = () => {
+  if (approvalPollId || !isPendingProvider.value) {
+    return;
+  }
+
+  approvalPollId = setInterval(checkApprovalStatus, APPROVAL_POLL_INTERVAL_MS);
+};
+
+const stopApprovalPolling = () => {
+  if (!approvalPollId) {
+    return;
+  }
+
+  clearInterval(approvalPollId);
+
+  approvalPollId = null;
+};
+
+const onRefresh = async (done) => {
+  try {
+    const approved = await checkApprovalStatus();
+
+    if (!approved) {
+      await loadDashboard();
+    }
   } finally {
   } finally {
     done();
     done();
   }
   }
@@ -191,6 +259,12 @@ watch(
 onMounted(async () => {
 onMounted(async () => {
   await loadDashboard();
   await loadDashboard();
   loading.value = false;
   loading.value = false;
+
+  startApprovalPolling();
+});
+
+onUnmounted(() => {
+  stopApprovalPolling();
 });
 });
 </script>
 </script>
 
 

+ 39 - 8
src/pages/profile/ProfilePage.vue

@@ -21,11 +21,12 @@
           <div class="fonte-email-profile font9 fontmedium text-grey-6 q-my-sm">{{ user.email || '—' }}</div>
           <div class="fonte-email-profile font9 fontmedium text-grey-6 q-my-sm">{{ user.email || '—' }}</div>
           <div class="fonte-telefone-profile font9 fontmedium text-grey-7">{{ user.phone || '—' }}</div> 
           <div class="fonte-telefone-profile font9 fontmedium text-grey-7">{{ user.phone || '—' }}</div> 
           
           
-          <q-btn 
-            outline 
-            no-caps 
+          <q-btn
+            v-if="!isPendingProvider"
+            outline
+            no-caps
             padding="6px 16px"
             padding="6px 16px"
-            class="full-width q-mt-sm btn-edit-profile text-text" 
+            class="full-width q-mt-sm btn-edit-profile text-text"
             @click="openEditProfile"
             @click="openEditProfile"
           >
           >
             <div class="row items-center q-gutter-x-sm">
             <div class="row items-center q-gutter-x-sm">
@@ -38,7 +39,15 @@
     </div>
     </div>
 
 
     <div class="q-mt-md q-px-md column">
     <div class="q-mt-md q-px-md column">
-      <div class="menu-item row items-center no-wrap cursor-pointer q-py-sm" @click="openBankDataDialog">
+      <div v-if="isPendingProvider" class="pending-notice font12 fontregular q-mb-md">
+        {{ $t('provider.dashboard.pending_approval.analyzing') }}
+      </div>
+
+      <div
+        v-if="!isPendingProvider"
+        class="menu-item row items-center no-wrap cursor-pointer q-py-sm"
+        @click="openBankDataDialog"
+      >
         <div class="column">
         <div class="column">
           <span class="font16 fontbold menu-title gradient-diarista">{{ $t('profile.bank_data.title') }}</span>
           <span class="font16 fontbold menu-title gradient-diarista">{{ $t('profile.bank_data.title') }}</span>
           <span class="font12 fontregular text-text">{{ $t('profile.bank_data.description') }}</span>
           <span class="font12 fontregular text-text">{{ $t('profile.bank_data.description') }}</span>
@@ -47,7 +56,11 @@
         <q-icon name="mdi-chevron-right" color="primary" size="md" />
         <q-icon name="mdi-chevron-right" color="primary" size="md" />
       </div>
       </div>
 
 
-      <div class="menu-item row items-center no-wrap cursor-pointer q-py-sm" @click="openAvailabilityDialog">
+      <div
+        v-if="!isPendingProvider"
+        class="menu-item row items-center no-wrap cursor-pointer q-py-sm"
+        @click="openAvailabilityDialog"
+      >
         <div class="column">
         <div class="column">
           <span class="font16 fontbold menu-title gradient-diarista">{{ $t('profile.availability.title') }}</span>
           <span class="font16 fontbold menu-title gradient-diarista">{{ $t('profile.availability.title') }}</span>
           <span class="font12 fontregular text-text">{{ $t('profile.availability.description') }}</span>
           <span class="font12 fontregular text-text">{{ $t('profile.availability.description') }}</span>
@@ -56,7 +69,11 @@
         <q-icon name="mdi-chevron-right" color="primary" size="md" />
         <q-icon name="mdi-chevron-right" color="primary" size="md" />
       </div>
       </div>
 
 
-      <div class="menu-item row items-center no-wrap cursor-pointer q-py-sm" @click="openServiceDataDialog">
+      <div
+        v-if="!isPendingProvider"
+        class="menu-item row items-center no-wrap cursor-pointer q-py-sm"
+        @click="openServiceDataDialog"
+      >
         <div class="column">
         <div class="column">
           <span class="font16 fontbold menu-title gradient-diarista">{{ $t('profile.service_data.title') }}</span>
           <span class="font16 fontbold menu-title gradient-diarista">{{ $t('profile.service_data.title') }}</span>
           <span class="font12 fontregular text-text">{{ $t('profile.service_data.description') }}</span>
           <span class="font12 fontregular text-text">{{ $t('profile.service_data.description') }}</span>
@@ -65,7 +82,11 @@
         <q-icon name="mdi-chevron-right" color="primary" size="md" />
         <q-icon name="mdi-chevron-right" color="primary" size="md" />
       </div>
       </div>
 
 
-      <div class="menu-item row items-center no-wrap cursor-pointer q-py-sm" @click="openAddressDialog">
+      <div
+        v-if="!isPendingProvider"
+        class="menu-item row items-center no-wrap cursor-pointer q-py-sm"
+        @click="openAddressDialog"
+      >
         <div class="column">
         <div class="column">
           <span class="font16 fontbold menu-title gradient-diarista">{{ $t('profile.address.title') }}</span>
           <span class="font16 fontbold menu-title gradient-diarista">{{ $t('profile.address.title') }}</span>
           <span class="font12 fontregular text-text">{{ $t('profile.address.description') }}</span>
           <span class="font12 fontregular text-text">{{ $t('profile.address.description') }}</span>
@@ -129,6 +150,7 @@ import { ref, onMounted } from 'vue';
 import { useQuasar } from 'quasar';
 import { useQuasar } from 'quasar';
 import { Browser } from '@capacitor/browser';
 import { Browser } from '@capacitor/browser';
 import { useAuth } from 'src/composables/useAuth';
 import { useAuth } from 'src/composables/useAuth';
+import { useProviderApproval } from 'src/composables/useProviderApproval';
 import { getUser } from 'src/api/user';
 import { getUser } from 'src/api/user';
 import ProfileEditDialog from './ProfileEditDialog.vue';
 import ProfileEditDialog from './ProfileEditDialog.vue';
 import ProfileAddressDialog from 'src/components/profile/ProfileAddressDialog.vue';
 import ProfileAddressDialog from 'src/components/profile/ProfileAddressDialog.vue';
@@ -145,6 +167,7 @@ const SUPPORT_PAGE_URL   = 'https://politicas.softpar.inf.br/suportes/suporteDia
 const $q = useQuasar();
 const $q = useQuasar();
 
 
 const { logout } = useAuth();
 const { logout } = useAuth();
+const { isPendingProvider } = useProviderApproval();
 const router = useRouter();
 const router = useRouter();
 const user = ref({
 const user = ref({
   name: '',
   name: '',
@@ -231,6 +254,14 @@ onMounted(async () => {
   border-radius: 6px;
   border-radius: 6px;
 }
 }
 
 
+.pending-notice {
+  background: #e3efff;
+  border-radius: 8px;
+  color: #6554d9;
+  line-height: 1.3;
+  padding: 12px 14px;
+}
+
 .menu-item {
 .menu-item {
   transition: background 0.3s;
   transition: background 0.3s;
   &:active {
   &:active {

+ 12 - 10
src/router/index.js

@@ -11,9 +11,13 @@ import { permissionStore } from "src/stores/permission";
 import { route } from "quasar/wrappers";
 import { route } from "quasar/wrappers";
 import { useAuth } from "src/composables/useAuth";
 import { useAuth } from "src/composables/useAuth";
 import { userStore } from "src/stores/user";
 import { userStore } from "src/stores/user";
+import { useProviderApproval } from "src/composables/useProviderApproval";
 
 
 import routes from "./routes";
 import routes from "./routes";
 
 
+// Rotas liberadas para o prestador com cadastro em análise.
+const PENDING_PROVIDER_ROUTES = ["DashboardPage", "ProfilePage"];
+
 /*
 /*
  * If not building with SSR mode, you can
  * If not building with SSR mode, you can
  * directly export the Router instantiation;
  * directly export the Router instantiation;
@@ -55,22 +59,20 @@ export default route(function (/* { store, ssrContext } */) {
 
 
     const user = userStore();
     const user = userStore();
 
 
-    const isPendingProvider =
-      String(user.user?.type).toUpperCase() === "PROVIDER" &&
-      user.user?.provider?.approval_status === "pending";
+    const { isPendingProvider } = useProviderApproval();
 
 
-    if (user.accessToken && isPendingProvider) {
-      if (to.name !== "LoginPage") {
-        return next({ name: "LoginPage" });
+    if (user.accessToken) {
+      if (to.name == "LoginPage") {
+        return next({ name: "DashboardPage" });
       }
       }
-
-      return next();
     }
     }
 
 
-    if (user.accessToken) {
-      if (to.name == "LoginPage") {
+    if (user.accessToken && isPendingProvider.value) {
+      if (!PENDING_PROVIDER_ROUTES.includes(to.name)) {
         return next({ name: "DashboardPage" });
         return next({ name: "DashboardPage" });
       }
       }
+
+      return next();
     }
     }
 
 
     if (to.meta.requiredPermission) {
     if (to.meta.requiredPermission) {

+ 7 - 7
src/stores/permission.js

@@ -95,13 +95,13 @@ export const permissionStore = defineStore("permission", () => {
   const fetchScopes = async () => {
   const fetchScopes = async () => {
     try {
     try {
       const { accessToken } = userStore();
       const { accessToken } = userStore();
-      if (accessToken) {
-        const userPermissions = await getUserPermissions();
-        permissions.value = userPermissions;
-      } else {
-        const response = await getGuestPermissions();
-        permissions.value = response;
-      }
+      const response = accessToken
+        ? await getUserPermissions()
+        : await getGuestPermissions();
+
+      // Rotas bloqueadas para prestador em análise respondem 202 com outro
+      // formato de payload; nesse caso o app segue sem escopo algum.
+      permissions.value = Array.isArray(response) ? response : null;
     } catch (error) {
     } catch (error) {
       console.error("Error fetching permissions:", error);
       console.error("Error fetching permissions:", error);
     }
     }