|
|
@@ -1,11 +1,13 @@
|
|
|
<template>
|
|
|
<q-dialog ref="dialogRef" @hide="onDialogHide">
|
|
|
- <q-card class="q-dialog-plugin" style="width: 1000px; max-width: 90vw">
|
|
|
+ <q-card
|
|
|
+ class="q-dialog-plugin column full-width"
|
|
|
+ style="width: 900px; max-width: 80vw; height: 90vh"
|
|
|
+ >
|
|
|
<DefaultDialogHeader :title="title" @close="onDialogCancel" />
|
|
|
-
|
|
|
+
|
|
|
<q-tabs
|
|
|
v-model="tab"
|
|
|
- dense
|
|
|
class="text-grey"
|
|
|
active-color="primary"
|
|
|
indicator-color="primary"
|
|
|
@@ -13,133 +15,185 @@
|
|
|
>
|
|
|
<q-tab name="data" label="Dados" />
|
|
|
<q-tab v-if="client" name="addresses" :label="$t('address.tab')" />
|
|
|
- <q-tab v-if="client" name="favorites" :label="$t('client_favorite_providers.header')" />
|
|
|
- <q-tab v-if="client" name="payment_methods" :label="$t('client_payment_methods.header')" />
|
|
|
- <q-tab v-if="client" name="blocked_providers" :label="$t('client_provider_blocks.tab')" />
|
|
|
- <q-tab v-if="client" name="sent_reviews" :label="$t('client_reviews.sent_tab')" />
|
|
|
- <q-tab v-if="client" name="received_reviews" :label="$t('client_reviews.received_tab')" />
|
|
|
+ <q-tab
|
|
|
+ v-if="client"
|
|
|
+ name="favorites"
|
|
|
+ :label="$t('client_favorite_providers.header')"
|
|
|
+ />
|
|
|
+ <q-tab
|
|
|
+ v-if="client"
|
|
|
+ name="payment_methods"
|
|
|
+ :label="$t('client_payment_methods.header')"
|
|
|
+ />
|
|
|
+ <q-tab
|
|
|
+ v-if="client"
|
|
|
+ name="blocked_providers"
|
|
|
+ :label="$t('client_provider_blocks.tab')"
|
|
|
+ />
|
|
|
+ <q-tab
|
|
|
+ v-if="client"
|
|
|
+ name="sent_reviews"
|
|
|
+ :label="$t('client_reviews.sent_tab')"
|
|
|
+ />
|
|
|
+ <q-tab
|
|
|
+ v-if="client"
|
|
|
+ name="received_reviews"
|
|
|
+ :label="$t('client_reviews.received_tab')"
|
|
|
+ />
|
|
|
</q-tabs>
|
|
|
|
|
|
<q-separator v-if="client" />
|
|
|
|
|
|
- <q-tab-panels v-model="tab" animated>
|
|
|
- <q-tab-panel name="data">
|
|
|
- <q-form ref="formRef" @submit="onOKClick">
|
|
|
- <q-card-section class="row q-col-gutter-sm">
|
|
|
- <div v-if="client" class="col-12 flex flex-center column q-pb-sm">
|
|
|
- <q-avatar size="80px" color="grey-3">
|
|
|
- <img v-if="avatarPreview" :src="avatarPreview" style="object-fit: cover; width: 100%; height: 100%;" @error="avatarPreview = null" />
|
|
|
- <q-icon v-else name="mdi-account" size="50px" color="grey-6" />
|
|
|
- </q-avatar>
|
|
|
- <input
|
|
|
- ref="fileInputRef"
|
|
|
- type="file"
|
|
|
- accept="image/jpeg,image/png,image/webp"
|
|
|
- style="display: none;"
|
|
|
- @change="onFileSelected"
|
|
|
+ <q-scroll-area class="col">
|
|
|
+ <q-tab-panels v-model="tab" animated>
|
|
|
+ <q-tab-panel name="data">
|
|
|
+ <q-form id="client-form" ref="formRef" @submit="onOKClick">
|
|
|
+ <q-card-section class="row q-col-gutter-sm no-padding">
|
|
|
+ <UserSelect
|
|
|
+ v-model="selectedUser"
|
|
|
+ :label="$t('common.terms.user')"
|
|
|
+ :rules="[inputRules.required]"
|
|
|
+ :error="!!serverErrors?.user_id"
|
|
|
+ :error-message="serverErrors?.user_id"
|
|
|
+ :initial-id="client ? client.user_id : null"
|
|
|
+ class="col-md-6 col-12"
|
|
|
+ @update:model-value="serverErrors.user_id = null"
|
|
|
/>
|
|
|
- <q-btn
|
|
|
- flat
|
|
|
- no-caps
|
|
|
- dense
|
|
|
- color="primary"
|
|
|
- size="sm"
|
|
|
- class="q-mt-xs"
|
|
|
- label="Alterar foto"
|
|
|
- @click="fileInputRef.click()"
|
|
|
+
|
|
|
+ <q-input
|
|
|
+ v-model="form.document"
|
|
|
+ :mask="documentMask"
|
|
|
+ fill-mask
|
|
|
+ unmasked-value
|
|
|
+ :label="$t('client.fields.document')"
|
|
|
+ :rules="[inputRules.required, validateDocument]"
|
|
|
+ :error="!!serverErrors?.document"
|
|
|
+ :error-message="serverErrors?.document"
|
|
|
+ class="col-md-6 col-12"
|
|
|
+ @update:model-value="serverErrors.document = null"
|
|
|
/>
|
|
|
- </div>
|
|
|
-
|
|
|
- <UserSelect
|
|
|
- v-model="selectedUser"
|
|
|
- :label="$t('common.terms.user')"
|
|
|
- :rules="[inputRules.required]"
|
|
|
- :error="!!serverErrors?.user_id"
|
|
|
- :error-message="serverErrors?.user_id"
|
|
|
- :initial-id="client ? client.user_id : null"
|
|
|
- class="col-12"
|
|
|
- @update:model-value="serverErrors.user_id = null"
|
|
|
- />
|
|
|
-
|
|
|
- <q-input
|
|
|
- v-model="form.document"
|
|
|
- :mask="documentMask"
|
|
|
- fill-mask
|
|
|
- unmasked-value
|
|
|
- :label="$t('client.fields.document')"
|
|
|
- :rules="[inputRules.required, validateDocument]"
|
|
|
- :error="!!serverErrors?.document"
|
|
|
- :error-message="serverErrors?.document"
|
|
|
- class="col-12"
|
|
|
- @update:model-value="serverErrors.document = null"
|
|
|
- />
|
|
|
- </q-card-section>
|
|
|
- <q-card-actions align="center">
|
|
|
- <q-btn color="primary" label="Cancel" @click="onDialogCancel" />
|
|
|
- <q-space />
|
|
|
- <q-btn
|
|
|
- color="primary"
|
|
|
- label="OK"
|
|
|
- :type="'submit'"
|
|
|
- :loading="loading"
|
|
|
- :disable="!hasUpdatedFields && !avatarFile"
|
|
|
- />
|
|
|
- </q-card-actions>
|
|
|
- </q-form>
|
|
|
- </q-tab-panel>
|
|
|
-
|
|
|
- <q-tab-panel v-if="client" name="addresses">
|
|
|
- <AddressesPanel
|
|
|
- :source="'client'"
|
|
|
- :source-id="client.id"
|
|
|
- />
|
|
|
- </q-tab-panel>
|
|
|
-
|
|
|
- <q-tab-panel v-if="client" name="favorites">
|
|
|
- <ClientFavoriteProvidersPanel :client-id="client.id" />
|
|
|
- </q-tab-panel>
|
|
|
-
|
|
|
- <q-tab-panel v-if="client" name="payment_methods">
|
|
|
- <ClientPaymentMethodsPanel :client-id="client.id" />
|
|
|
- </q-tab-panel>
|
|
|
-
|
|
|
- <q-tab-panel v-if="client" name="blocked_providers">
|
|
|
- <ClientProvidersBlocksPanel :client-id="client.id" />
|
|
|
- </q-tab-panel>
|
|
|
-
|
|
|
- <q-tab-panel v-if="client" name="sent_reviews">
|
|
|
- <ClientReviewsPanel :client-id="client.id" type="sent" />
|
|
|
- </q-tab-panel>
|
|
|
-
|
|
|
- <q-tab-panel v-if="client" name="received_reviews">
|
|
|
- <ClientReviewsPanel :client-id="client.id" type="received" />
|
|
|
- </q-tab-panel>
|
|
|
- </q-tab-panels>
|
|
|
+
|
|
|
+ <div v-if="client" class="col-12 q-mt-md">
|
|
|
+ <div class="text-h6 q-mb-md">
|
|
|
+ {{ $t("client.fields.selfie_verified") }}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="column items-start q-mb-md">
|
|
|
+ <div
|
|
|
+ class="profile-frame"
|
|
|
+ :class="{ 'profile-frame--clickable': avatarPreview }"
|
|
|
+ @click="openAvatarInNewTab"
|
|
|
+ >
|
|
|
+ <img
|
|
|
+ v-if="avatarPreview"
|
|
|
+ :src="avatarPreview"
|
|
|
+ class="profile-image"
|
|
|
+ @error="avatarPreview = null"
|
|
|
+ />
|
|
|
+ <q-icon
|
|
|
+ v-else
|
|
|
+ name="mdi-account"
|
|
|
+ size="64px"
|
|
|
+ color="grey-6"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <input
|
|
|
+ ref="fileInputRef"
|
|
|
+ type="file"
|
|
|
+ accept="image/jpeg,image/png,image/webp"
|
|
|
+ style="display: none"
|
|
|
+ @change="onFileSelected"
|
|
|
+ />
|
|
|
+ <q-btn
|
|
|
+ flat
|
|
|
+ no-caps
|
|
|
+ dense
|
|
|
+ color="primary"
|
|
|
+ size="sm"
|
|
|
+ class="q-mt-xs"
|
|
|
+ :label="$t('client.fields.change_photo')"
|
|
|
+ @click="fileInputRef.click()"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <q-checkbox
|
|
|
+ v-model="form.selfie_verified"
|
|
|
+ :label="$t('client.fields.selfie_verified')"
|
|
|
+ color="primary"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </q-card-section>
|
|
|
+ </q-form>
|
|
|
+ </q-tab-panel>
|
|
|
+
|
|
|
+ <q-tab-panel v-if="client" name="addresses">
|
|
|
+ <AddressesPanel source="client" :source-id="client.id" />
|
|
|
+ </q-tab-panel>
|
|
|
+
|
|
|
+ <q-tab-panel v-if="client" name="favorites">
|
|
|
+ <ClientFavoriteProvidersPanel :client-id="client.id" />
|
|
|
+ </q-tab-panel>
|
|
|
+
|
|
|
+ <q-tab-panel v-if="client" name="payment_methods">
|
|
|
+ <ClientPaymentMethodsPanel :client-id="client.id" />
|
|
|
+ </q-tab-panel>
|
|
|
+
|
|
|
+ <q-tab-panel v-if="client" name="blocked_providers">
|
|
|
+ <ClientProvidersBlocksPanel :client-id="client.id" />
|
|
|
+ </q-tab-panel>
|
|
|
+
|
|
|
+ <q-tab-panel v-if="client" name="sent_reviews">
|
|
|
+ <ClientReviewsPanel :client-id="client.id" type="sent" />
|
|
|
+ </q-tab-panel>
|
|
|
+
|
|
|
+ <q-tab-panel v-if="client" name="received_reviews">
|
|
|
+ <ClientReviewsPanel :client-id="client.id" type="received" />
|
|
|
+ </q-tab-panel>
|
|
|
+ </q-tab-panels>
|
|
|
+ </q-scroll-area>
|
|
|
+
|
|
|
+ <q-separator />
|
|
|
+ <q-card-actions align="right" class="q-pa-md">
|
|
|
+ <q-btn
|
|
|
+ :label="$t('common.actions.close')"
|
|
|
+ outline
|
|
|
+ color="primary"
|
|
|
+ @click="onDialogCancel"
|
|
|
+ />
|
|
|
+ <q-btn
|
|
|
+ color="secondary"
|
|
|
+ :label="$t('common.actions.save')"
|
|
|
+ type="submit"
|
|
|
+ form="client-form"
|
|
|
+ unelevated
|
|
|
+ :loading="loading"
|
|
|
+ :disable="!hasUpdatedFields && !avatarFile"
|
|
|
+ />
|
|
|
+ </q-card-actions>
|
|
|
</q-card>
|
|
|
</q-dialog>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, useTemplateRef, watch, computed, onMounted } from 'vue';
|
|
|
-import { useInputRules } from 'src/composables/useInputRules';
|
|
|
-import { useDialogPluginComponent } from 'quasar';
|
|
|
-import { useI18n } from 'vue-i18n';
|
|
|
-import { createClient, updateClient } from 'src/api/client';
|
|
|
-import { useFormUpdateTracker } from 'src/composables/useFormUpdateTracker';
|
|
|
-import { useSubmitHandler } from 'src/composables/useSubmitHandler';
|
|
|
-import { dynamicCpfCnpjMask, validateCpfCnpj } from 'src/helpers/utils';
|
|
|
-
|
|
|
-import DefaultDialogHeader from 'src/components/defaults/DefaultDialogHeader.vue';
|
|
|
-import UserSelect from 'src/components/user/UserSelect.vue';
|
|
|
-import AddressesPanel from 'src/pages/address/components/AddressesPanel.vue';
|
|
|
-import ClientFavoriteProvidersPanel from './ClientFavoriteProvidersPanel.vue';
|
|
|
-import ClientPaymentMethodsPanel from './ClientPaymentMethodsPanel.vue';
|
|
|
-import ClientProvidersBlocksPanel from './ClientProvidersBlocksPanel.vue';
|
|
|
-import ClientReviewsPanel from './ClientReviewsPanel.vue';
|
|
|
-
|
|
|
-defineEmits([
|
|
|
- ...useDialogPluginComponent.emits,
|
|
|
-]);
|
|
|
+import { ref, useTemplateRef, watch, computed, onMounted } from "vue";
|
|
|
+import { useInputRules } from "src/composables/useInputRules";
|
|
|
+import { useDialogPluginComponent } from "quasar";
|
|
|
+import { useI18n } from "vue-i18n";
|
|
|
+import { createClient, getClient, updateClient } from "src/api/client";
|
|
|
+import { useFormUpdateTracker } from "src/composables/useFormUpdateTracker";
|
|
|
+import { useSubmitHandler } from "src/composables/useSubmitHandler";
|
|
|
+import { dynamicCpfCnpjMask, validateCpfCnpj } from "src/helpers/utils";
|
|
|
+
|
|
|
+import DefaultDialogHeader from "src/components/defaults/DefaultDialogHeader.vue";
|
|
|
+import UserSelect from "src/components/user/UserSelect.vue";
|
|
|
+import AddressesPanel from "src/pages/address/components/AddressesPanel.vue";
|
|
|
+import ClientFavoriteProvidersPanel from "./ClientFavoriteProvidersPanel.vue";
|
|
|
+import ClientPaymentMethodsPanel from "./ClientPaymentMethodsPanel.vue";
|
|
|
+import ClientProvidersBlocksPanel from "./ClientProvidersBlocksPanel.vue";
|
|
|
+import ClientReviewsPanel from "./ClientReviewsPanel.vue";
|
|
|
+
|
|
|
+defineEmits([...useDialogPluginComponent.emits]);
|
|
|
|
|
|
const { client, title } = defineProps({
|
|
|
client: {
|
|
|
@@ -148,7 +202,7 @@ const { client, title } = defineProps({
|
|
|
},
|
|
|
title: {
|
|
|
type: Function,
|
|
|
- default: () => useI18n().t('common.terms.title'),
|
|
|
+ default: () => useI18n().t("common.terms.title"),
|
|
|
},
|
|
|
});
|
|
|
|
|
|
@@ -158,15 +212,21 @@ const { inputRules } = useInputRules();
|
|
|
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
|
|
|
useDialogPluginComponent();
|
|
|
|
|
|
-const formRef = useTemplateRef('formRef');
|
|
|
-const fileInputRef = useTemplateRef('fileInputRef');
|
|
|
-const tab = ref('data');
|
|
|
+const formRef = useTemplateRef("formRef");
|
|
|
+const fileInputRef = useTemplateRef("fileInputRef");
|
|
|
+const tab = ref("data");
|
|
|
const avatarFile = ref(null);
|
|
|
const avatarPreview = ref(null);
|
|
|
|
|
|
-const { form, getUpdatedFields, hasUpdatedFields } = useFormUpdateTracker({
|
|
|
+const {
|
|
|
+ form,
|
|
|
+ getUpdatedFields,
|
|
|
+ hasUpdatedFields,
|
|
|
+ setUpdateFormAsOriginal,
|
|
|
+} = useFormUpdateTracker({
|
|
|
user_id: client ? client?.user_id : null,
|
|
|
- document: client ? client?.document : '',
|
|
|
+ document: client ? client?.document : "",
|
|
|
+ selfie_verified: client ? client?.selfie_verified : false,
|
|
|
});
|
|
|
|
|
|
const {
|
|
|
@@ -186,7 +246,10 @@ const documentMask = computed(() => {
|
|
|
|
|
|
const validateDocument = (val) => {
|
|
|
if (!val) return true;
|
|
|
- return validateCpfCnpj(val) || t('validation.rules.cpf') + ' / ' + t('validation.rules.cnpj');
|
|
|
+ return (
|
|
|
+ validateCpfCnpj(val) ||
|
|
|
+ t("validation.rules.cpf") + " / " + t("validation.rules.cnpj")
|
|
|
+ );
|
|
|
};
|
|
|
|
|
|
const onFileSelected = (event) => {
|
|
|
@@ -196,6 +259,12 @@ const onFileSelected = (event) => {
|
|
|
avatarPreview.value = URL.createObjectURL(file);
|
|
|
};
|
|
|
|
|
|
+const openAvatarInNewTab = () => {
|
|
|
+ if (avatarPreview.value) {
|
|
|
+ window.open(avatarPreview.value, "_blank", "noopener,noreferrer");
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
const onOKClick = async () => {
|
|
|
if (client) {
|
|
|
if (avatarFile.value) {
|
|
|
@@ -203,11 +272,14 @@ const onOKClick = async () => {
|
|
|
const fields = getUpdatedFields.value;
|
|
|
Object.entries(fields).forEach(([key, value]) => {
|
|
|
if (value !== null && value !== undefined) {
|
|
|
- formData.append(key, String(value));
|
|
|
+ formData.append(
|
|
|
+ key,
|
|
|
+ typeof value === "boolean" ? (value ? "1" : "0") : String(value),
|
|
|
+ );
|
|
|
}
|
|
|
});
|
|
|
- formData.append('avatar', avatarFile.value);
|
|
|
- formData.append('_method', 'PUT');
|
|
|
+ formData.append("avatar", avatarFile.value);
|
|
|
+ formData.append("_method", "PUT");
|
|
|
await submitForm(() => updateClient(formData, client.id, true));
|
|
|
} else {
|
|
|
await submitForm(() => updateClient(getUpdatedFields.value, client.id));
|
|
|
@@ -217,9 +289,18 @@ const onOKClick = async () => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-onMounted(() => {
|
|
|
+onMounted(async () => {
|
|
|
if (client) {
|
|
|
+ selectedUser.value = {
|
|
|
+ label: client.user?.name || "",
|
|
|
+ value: client.user?.id,
|
|
|
+ };
|
|
|
avatarPreview.value = client.profile_media?.url ?? null;
|
|
|
+
|
|
|
+ const full = await getClient(client.id);
|
|
|
+ avatarPreview.value = full.profile_media?.url ?? avatarPreview.value;
|
|
|
+ form.selfie_verified = full.selfie_verified ?? form.selfie_verified;
|
|
|
+ setUpdateFormAsOriginal();
|
|
|
}
|
|
|
});
|
|
|
|
|
|
@@ -227,3 +308,27 @@ watch(selectedUser, () => {
|
|
|
form.user_id = selectedUser.value?.value;
|
|
|
});
|
|
|
</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.profile-frame {
|
|
|
+ width: 300px;
|
|
|
+ height: 170px;
|
|
|
+ border: 1px solid #e0e0e0;
|
|
|
+ border-radius: 6px;
|
|
|
+ background: #fafafa;
|
|
|
+ overflow: hidden;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+
|
|
|
+.profile-image {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ object-fit: contain;
|
|
|
+}
|
|
|
+
|
|
|
+.profile-frame--clickable {
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+</style>
|