2 コミット ead5129434 ... 74ef1cf371

作者 SHA1 メッセージ 日付
  Gustavo Mantovani 74ef1cf371 Merge remote-tracking branch 'origin/feature/diaria-kay-listar-documentos-do-prestador' into development 6 日 前
  kayo henrique f840307d9d feat: :sparkles: feat(listar docuemtos do prestador) Foi criado no template a opção de listar os docuemntos do prestador na hora de aceitar ou recusar o usuario 1 週間 前

+ 1 - 1
package.json

@@ -10,7 +10,7 @@
     "lint": "eslint -c ./eslint.config.js \"./src*/**/*.{js,cjs,mjs,vue}\"",
     "format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore",
     "test": "echo \"No test specified\" && exit 0",
-    "dev": "APP_ENV=dev quasar dev",
+    "dev": "cross-env APP_ENV=dev quasar dev",
     "dev:staging": "APP_ENV=staging quasar dev",
     "build": "APP_ENV=prod quasar build",
     "build:dev": "APP_ENV=dev quasar build",

+ 3 - 1
src/i18n/locales/pt.json

@@ -368,7 +368,9 @@
       "btn_reject": "Recusar",
       "approved_success": "Prestador aprovado com sucesso.",
       "rejected_success": "Prestador recusado.",
-      "pending_table_title": "Prestadores Aguardando Aprovação"
+      "pending_table_title": "Prestadores Aguardando Aprovação",
+      "section_documents": "Documentos",
+      "selfie": "Selfie"
     }
   },
   "provider_specialities": {

+ 5 - 5
src/pages/dashboard/components/PendingProvidersTable.vue

@@ -67,11 +67,11 @@ const { t } = useI18n();
 const tableRef = ref(null);
 
 const columns = [
-  { name: 'name',            label: t('common.terms.name'),              field: (row) => row.user?.name || '—', align: 'left',  sortable: false, style: 'width: 25%' },
-  { name: 'document',        label: t('provider.fields.document'),        field: 'document',                     align: 'left',  sortable: false, style: 'width: 25%' },
-  { name: 'created_at',      label: t('common.terms.created_at'),         field: 'created_at',                   align: 'left',  sortable: false, style: 'width: 25%' },
-  { name: 'approval_status', label: t('provider.fields.approval_status'), field: 'approval_status',              align: 'left',  sortable: false, style: 'width: 25%' },
-  { name: 'actions',         label: '',                                    field: 'actions',                      align: 'right', sortable: false, style: 'width: 25%' },
+  { name: 'name', label: t('common.terms.name'), field: (row) => row.user?.name || '—', align: 'left', sortable: false, style: 'width: 25%' },
+  { name: 'document', label: t('provider.fields.document'), field: 'document', align: 'left', sortable: false, style: 'width: 25%' },
+  { name: 'created_at', label: t('common.terms.created_at'), field: 'created_at', align: 'left', sortable: false, style: 'width: 25%' },
+  { name: 'approval_status', label: t('provider.fields.approval_status'), field: (row) => t(`provider.approval_status.${row.approval_status}`), align: 'left', sortable: false, style: 'width: 25%' },
+  { name: 'actions', label: '', field: 'actions', align: 'right', sortable: false, style: 'width: 25%' },
 ];
 
 const formatDate = (value) => {

+ 407 - 97
src/pages/dashboard/components/ProviderApprovalDialog.vue

@@ -1,9 +1,16 @@
 <template>
-  <q-dialog ref="dialogRef" @hide="onDialogHide">
-    <q-card class="q-dialog-plugin column" style="width: 700px; max-width: 95vw; max-height: 90vh;">
-      <DefaultDialogHeader :title="() => $t('provider.approval.dialog_title')" @close="onDialogCancel" />
+  <q-dialog
+    ref="dialogRef"
+    @hide="onDialogHide"
+  >
+    <q-card class="q-dialog-plugin column approval-dialog">
+      <DefaultDialogHeader
+        :title="() => $t('provider.approval.dialog_title')"
+        @close="onDialogCancel"
+      />
 
       <div class="col scroll q-pa-md">
+
         <div class="row items-center q-mb-md">
           <q-badge
             :color="statusColor"
@@ -12,138 +19,418 @@
           />
         </div>
 
-        <div class="text-subtitle1 text-weight-bold q-mb-sm">{{ $t('provider.approval.section_personal') }}</div>
-        <q-list bordered separator class="rounded-borders q-mb-md">
-          <q-item>
-            <q-item-section>
-              <q-item-label caption>{{ $t('common.terms.name') }}</q-item-label>
-              <q-item-label>{{ provider.user?.name || '—' }}</q-item-label>
-            </q-item-section>
-          </q-item>
-          <q-item>
-            <q-item-section>
-              <q-item-label caption>{{ $t('common.terms.email') }}</q-item-label>
-              <q-item-label>{{ provider.user?.email || '—' }}</q-item-label>
-            </q-item-section>
-          </q-item>
-          <q-item>
-            <q-item-section>
-              <q-item-label caption>{{ $t('provider.fields.document') }}</q-item-label>
-              <q-item-label>{{ provider.document || '—' }}</q-item-label>
-            </q-item-section>
-          </q-item>
-          <q-item>
-            <q-item-section>
-              <q-item-label caption>{{ $t('provider.fields.rg') }}</q-item-label>
-              <q-item-label>{{ provider.rg || '—' }}</q-item-label>
-            </q-item-section>
-          </q-item>
-          <q-item>
-            <q-item-section>
-              <q-item-label caption>{{ $t('provider.fields.birth_date') }}</q-item-label>
-              <q-item-label>{{ provider.birth_date || '—' }}</q-item-label>
-            </q-item-section>
-          </q-item>
-        </q-list>
-
-        <div class="text-subtitle1 text-weight-bold q-mb-sm">{{ $t('provider.approval.section_pricing') }}</div>
-        <q-list bordered separator class="rounded-borders q-mb-md">
-          <q-item>
-            <q-item-section>
-              <q-item-label caption>{{ $t('provider.fields.daily_price_8h') }}</q-item-label>
-              <q-item-label>{{ formatCurrency(provider.daily_price_8h) }}</q-item-label>
-            </q-item-section>
-          </q-item>
-          <q-item>
-            <q-item-section>
-              <q-item-label caption>{{ $t('provider.fields.daily_price_6h') }}</q-item-label>
-              <q-item-label>{{ formatCurrency(provider.daily_price_6h) }}</q-item-label>
-            </q-item-section>
-          </q-item>
-          <q-item>
-            <q-item-section>
-              <q-item-label caption>{{ $t('provider.fields.daily_price_4h') }}</q-item-label>
-              <q-item-label>{{ formatCurrency(provider.daily_price_4h) }}</q-item-label>
-            </q-item-section>
-          </q-item>
-          <q-item>
-            <q-item-section>
-              <q-item-label caption>{{ $t('provider.fields.daily_price_2h') }}</q-item-label>
-              <q-item-label>{{ formatCurrency(provider.daily_price_2h) }}</q-item-label>
-            </q-item-section>
-          </q-item>
-        </q-list>
-
-        <div class="text-caption text-grey-6">
-          {{ $t('common.terms.created_at') + ': ' + (provider.created_at || '—') }}
+        <div class="row q-col-gutter-md">
+
+          <!-- Dados pessoais -->
+          <div class="col-12 col-lg-4">
+            <div class="text-subtitle1 text-weight-bold q-mb-sm">
+              {{ $t("provider.approval.section_personal") }}
+            </div>
+
+            <q-list
+              bordered
+              separator
+              class="rounded-borders"
+            >
+              <q-item>
+                <q-item-section>
+                  <q-item-label caption>
+                    {{ $t("common.terms.name") }}
+                  </q-item-label>
+
+                  <q-item-label>
+                    {{ provider.user?.name || "—" }}
+                  </q-item-label>
+                </q-item-section>
+              </q-item>
+
+              <q-item>
+                <q-item-section>
+                  <q-item-label caption>
+                    {{ $t("common.terms.email") }}
+                  </q-item-label>
+
+                  <q-item-label>
+                    {{ provider.user?.email || "—" }}
+                  </q-item-label>
+                </q-item-section>
+              </q-item>
+
+              <q-item>
+                <q-item-section>
+                  <q-item-label caption>
+                    {{ $t("provider.fields.document") }}
+                  </q-item-label>
+
+                  <q-item-label>
+                    {{ provider.document || "—" }}
+                  </q-item-label>
+                </q-item-section>
+              </q-item>
+
+              <q-item>
+                <q-item-section>
+                  <q-item-label caption>
+                    {{ $t("provider.fields.rg") }}
+                  </q-item-label>
+
+                  <q-item-label>
+                    {{ provider.rg || "—" }}
+                  </q-item-label>
+                </q-item-section>
+              </q-item>
+
+              <q-item>
+                <q-item-section>
+                  <q-item-label caption>
+                    {{ $t("provider.fields.birth_date") }}
+                  </q-item-label>
+
+                  <q-item-label>
+                    {{ provider.birth_date || "—" }}
+                  </q-item-label>
+                </q-item-section>
+              </q-item>
+            </q-list>
+          </div>
+
+          <!-- Preços -->
+          <div class="col-12 col-lg-4">
+            <div class="text-subtitle1 text-weight-bold q-mb-sm">
+              {{ $t("provider.approval.section_pricing") }}
+            </div>
+
+            <q-list
+              bordered
+              separator
+              class="rounded-borders"
+            >
+              <q-item>
+                <q-item-section>
+                  <q-item-label caption>
+                    {{ $t("provider.fields.daily_price_8h") }}
+                  </q-item-label>
+
+                  <q-item-label>
+                    {{ formatCurrency(provider.daily_price_8h) }}
+                  </q-item-label>
+                </q-item-section>
+              </q-item>
+
+              <q-item>
+                <q-item-section>
+                  <q-item-label caption>
+                    {{ $t("provider.fields.daily_price_6h") }}
+                  </q-item-label>
+
+                  <q-item-label>
+                    {{ formatCurrency(provider.daily_price_6h) }}
+                  </q-item-label>
+                </q-item-section>
+              </q-item>
+
+              <q-item>
+                <q-item-section>
+                  <q-item-label caption>
+                    {{ $t("provider.fields.daily_price_4h") }}
+                  </q-item-label>
+
+                  <q-item-label>
+                    {{ formatCurrency(provider.daily_price_4h) }}
+                  </q-item-label>
+                </q-item-section>
+              </q-item>
+
+              <q-item>
+                <q-item-section>
+                  <q-item-label caption>
+                    {{ $t("provider.fields.daily_price_2h") }}
+                  </q-item-label>
+
+                  <q-item-label>
+                    {{ formatCurrency(provider.daily_price_2h) }}
+                  </q-item-label>
+                </q-item-section>
+              </q-item>
+            </q-list>
+          </div>
+
+        <!-- Documentos -->
+          <div class="col-12 col-lg-4">
+            <div class="text-subtitle1 text-weight-bold q-mb-sm">
+              {{ $t("provider.approval.section_documents") }}
+            </div>
+
+            <!-- Selfie -->
+            <div class="q-mb-md">
+              <div class="text-caption text-grey-6 q-mb-xs">
+                {{ $t("provider.approval.selfie") }}
+              </div>
+
+              <div
+                class="doc-frame"
+                :class="{ 'doc-frame--clickable': avatarPreview }"
+                @click="avatarPreview && (docLightbox = avatarPreview)"
+              >
+                <q-img
+                  v-if="avatarPreview"
+                  :src="avatarPreview"
+                  fit="cover"
+                  style="width: 100%; height: 100%;"
+                />
+
+                <div
+                  v-else
+                  class="column flex-center text-grey-5"
+                >
+                  <q-icon
+                    name="mdi-account"
+                    size="40px"
+                  />
+                </div>
+              </div>
+            </div>
+
+            <!-- Documento Frente -->
+            <div class="q-mb-md">
+              <div class="text-caption text-grey-6 q-mb-xs">
+                {{ $t("provider.fields.document_front") }}
+              </div>
+
+              <div
+                class="doc-frame"
+                :class="{ 'doc-frame--clickable': documentFrontUrl }"
+                @click="documentFrontUrl && (docLightbox = documentFrontUrl)"
+              >
+                <q-img
+                  v-if="documentFrontUrl"
+                  :src="documentFrontUrl"
+                  fit="contain"
+                  style="width:100%;height:100%;"
+                />
+
+                <div
+                  v-else
+                  class="column flex-center text-grey-5"
+                >
+                  <q-icon
+                    name="mdi-image-off-outline"
+                    size="36px"
+                  />
+                </div>
+              </div>
+            </div>
+
+            <!-- Documento Verso -->
+            <div>
+              <div class="text-caption text-grey-6 q-mb-xs">
+                {{ $t("provider.fields.document_back") }}
+              </div>
+
+              <div
+                class="doc-frame"
+                :class="{ 'doc-frame--clickable': documentBackUrl }"
+                @click="documentBackUrl && (docLightbox = documentBackUrl)"
+              >
+                <q-img
+                  v-if="documentBackUrl"
+                  :src="documentBackUrl"
+                  fit="contain"
+                  style="width:100%;height:100%;"
+                />
+
+                <div
+                  v-else
+                  class="column flex-center text-grey-5"
+                >
+                  <q-icon
+                    name="mdi-image-off-outline"
+                    size="36px"
+                  />
+                </div>
+              </div>
+            </div>
+          </div>
+
+        </div>
+
+        <div class="text-caption text-grey-6 q-mt-md">
+          {{ provider.created_at || "—" }}
         </div>
+
       </div>
 
-      <q-card-actions align="right" class="q-pa-md">
-        <q-btn
-          flat
-          :label="$t('common.actions.cancel')"
-          color="grey-7"
-          @click="onDialogCancel"
-        />
-        <q-btn
+      <q-card-actions class="q-pa-md row q-col-gutter-sm">
+
+        <div class="col-12 col-sm-auto">
+          <q-btn
+            class="full-width"
+            flat
+            :label="$t('common.actions.cancel')"
+            color="grey-7"
+            @click="onDialogCancel"
+          />
+        </div>
+
+        <div
           v-if="provider.approval_status !== 'rejected'"
-          unelevated
-          :label="$t('provider.approval.btn_reject')"
-          color="negative"
-          :loading="loadingReject"
-          @click="onReject"
-        />
-        <q-btn
+          class="col-12 col-sm-auto"
+        >
+          <q-btn
+            class="full-width"
+            unelevated
+            color="negative"
+            :label="$t('provider.approval.btn_reject')"
+            :loading="loadingReject"
+            @click="onReject"
+          />
+        </div>
+
+        <div
           v-if="provider.approval_status !== 'accepted'"
-          unelevated
-          :label="$t('provider.approval.btn_approve')"
-          color="positive"
-          :loading="loadingApprove"
-          @click="onApprove"
-        />
+          class="col-12 col-sm-auto"
+        >
+          <q-btn
+            class="full-width"
+            unelevated
+            color="positive"
+            :label="$t('provider.approval.btn_approve')"
+            :loading="loadingApprove"
+            @click="onApprove"
+          />
+        </div>
+
       </q-card-actions>
     </q-card>
   </q-dialog>
+
+  <q-dialog
+    v-model="docLightboxOpen"
+    maximized
+  >
+    <div
+      class="flex flex-center bg-black"
+      style="height:100%;"
+      @click="docLightbox = null"
+    >
+      <q-img
+        :src="docLightbox"
+        fit="contain"
+        style="max-width:100vw;max-height:100vh;"
+      />
+
+      <q-btn
+        flat
+        round
+        color="white"
+        icon="mdi-close"
+        class="absolute-top-right q-ma-sm"
+        @click.stop="docLightbox = null"
+      />
+    </div>
+  </q-dialog>
 </template>
 
 <script setup>
-import { computed, ref } from "vue";
+import { computed, ref, onMounted } from "vue";
 import { useDialogPluginComponent, useQuasar } from "quasar";
 import { useI18n } from "vue-i18n";
-import { approveProvider, rejectProvider } from "src/api/provider";
+import {
+  approveProvider,
+  rejectProvider,
+  getProvider,
+} from "src/api/provider";
 import DefaultDialogHeader from "src/components/defaults/DefaultDialogHeader.vue";
 
 const props = defineProps({
-  provider: { type: Object, required: true },
+  provider: {
+    type: Object,
+    required: true,
+  },
 });
 
 defineEmits([...useDialogPluginComponent.emits]);
-const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent();
+
+const {
+  dialogRef,
+  onDialogHide,
+  onDialogOK,
+  onDialogCancel,
+} = useDialogPluginComponent();
 
 const $q = useQuasar();
 const { t } = useI18n();
 
 const loadingApprove = ref(false);
-const loadingReject  = ref(false);
+const loadingReject = ref(false);
+
+// Imagens
+const avatarPreview = ref(null);
+const documentFrontUrl = ref(null);
+const documentBackUrl = ref(null);
+
+// Lightbox
+const docLightbox = ref(null);
+
+const docLightboxOpen = computed({
+  get: () => !!docLightbox.value,
+  set: (value) => {
+    if (!value) {
+      docLightbox.value = null;
+    }
+  },
+});
 
 const statusColor = computed(() => {
-  const map = { pending: "warning", accepted: "positive", rejected: "negative" };
+  const map = {
+    pending: "warning",
+    accepted: "positive",
+    rejected: "negative",
+  };
+
   return map[props.provider.approval_status] ?? "grey";
 });
 
 const formatCurrency = (value) => {
   if (!value) return "—";
-  return new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(value);
+
+  return new Intl.NumberFormat("pt-BR", {
+    style: "currency",
+    currency: "BRL",
+  }).format(value);
 };
 
+onMounted(async () => {
+  try {
+    const full = await getProvider(props.provider.id);
+
+    console.log("FULL", full);
+    console.log("PROFILE", full.profile_media);
+    console.log("URL", full.profile_media?.url);
+
+    avatarPreview.value = full.profile_media?.url ?? null;
+
+    console.log("AVATAR", avatarPreview.value);
+
+    documentFrontUrl.value = full.document_front_media?.url ?? null;
+    documentBackUrl.value = full.document_back_media?.url ?? null;
+  } catch (error) {
+    console.error(error);
+  }
+});
+
 const onApprove = async () => {
   loadingApprove.value = true;
+
   try {
     await approveProvider(props.provider.id);
     onDialogOK({ action: "approved" });
   } catch {
-    $q.notify({ type: "negative", message: t("http.errors.failed"), position: "top" });
+    $q.notify({
+      type: "negative",
+      message: t("http.errors.failed"),
+      position: "top",
+    });
   } finally {
     loadingApprove.value = false;
   }
@@ -151,13 +438,36 @@ const onApprove = async () => {
 
 const onReject = async () => {
   loadingReject.value = true;
+
   try {
     await rejectProvider(props.provider.id);
     onDialogOK({ action: "rejected" });
   } catch {
-    $q.notify({ type: "negative", message: t("http.errors.failed"), position: "top" });
+    $q.notify({
+      type: "negative",
+      message: t("http.errors.failed"),
+      position: "top",
+    });
   } finally {
     loadingReject.value = false;
   }
 };
 </script>
+
+<style scoped>
+.approval-dialog {
+  width: 700px;
+  max-width: 95vw;
+  max-height: 90vh;
+}
+
+@media (max-width: 768px) {
+  .approval-dialog {
+    width: 100vw;
+    max-width: 100vw;
+    height: 100vh;
+    max-height: 100vh;
+    border-radius: 0;
+  }
+}
+</style>