| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- <template>
- <q-dialog ref="dialogRef" persistent maximized transition-show="slide-left" transition-hide="slide-right">
- <q-card class="bg-white full-width full-height">
- <div class="row items-center q-px-md q-pt-md q-pb-sm bg-white shadow-profile">
- <q-btn flat round dense icon="mdi-chevron-left" color="primary" @click="onDialogCancel" />
- <q-space />
- <span class="text-subtitle1 text-primary font16 fontbold gradient-diarista">{{ $t('profile.edit_data') }}</span>
- <q-space />
- <div style="width: 32px"></div>
- </div>
- <div v-if="loading" class="flex flex-center q-pa-xl">
- <q-spinner color="primary" size="3em" />
- </div>
- <template v-else>
- <q-scroll-area class="col" style="height: calc(100vh - 72px)">
- <div class="column items-center q-mt-xl q-mb-md">
- <q-avatar size="140px" color="indigo-1" text-color="indigo-4" class="text-h2 shadow-1">
- <img v-if="avatarPreview" :src="avatarPreview" style="object-fit: cover; width: 100%; height: 100%;" />
- <span v-else>{{ form.name ? form.name.charAt(0).toUpperCase() : '' }}</span>
- </q-avatar>
- <input
- ref="fileInputRef"
- type="file"
- accept="image/jpeg,image/png,image/webp"
- class="hidden"
- @change="onFileSelected"
- />
- <q-btn flat no-caps color="grey-6" class="q-mt-sm" :label="$t('profile.change_photo')" @click="fileInputRef.click()" />
- </div>
- <q-form ref="formRef" @submit.prevent="onSubmit">
- <div class="q-px-xl q-gutter-y-lg">
- <div>
- <div class="text-grey-8 q-mb-sm font12 fontbold">{{ $t('profile.full_name') }}</div>
- <q-input
- v-model="form.name"
- outlined
- dense
- input-class="text-text"
- :placeholder="$t('profile.placeholder_name')"
- :rules="[inputRules.required]"
- :error="!!serverErrors.name"
- :error-message="serverErrors.name"
- @update:model-value="serverErrors.name = null"
- />
- </div>
- <div>
- <div class="text-grey-8 q-mb-sm font12 fontbold">{{ $t('profile.email') }}</div>
- <q-input
- v-model="form.email"
- outlined
- dense
- input-class="text-text"
- :placeholder="$t('profile.placeholder_email')"
- :rules="[inputRules.email]"
- :error="!!serverErrors.email"
- :error-message="serverErrors.email"
- @update:model-value="serverErrors.email = null"
- />
- </div>
- <div>
- <div class="text-grey-8 q-mb-sm font12 fontbold">{{ $t('profile.phone') }}</div>
- <q-input
- v-model="form.phone"
- outlined
- dense
- input-class="text-text"
- mask="(##) #####-####"
- unmasked-value
- :placeholder="$t('profile.placeholder_phone')"
- :error="!!serverErrors.phone"
- :error-message="serverErrors.phone"
- @update:model-value="serverErrors.phone = null"
- />
- </div>
- <div>
- <div class="text-grey-8 q-mb-sm font12 fontbold">{{ $t('profile.document') }}</div>
- <q-input
- v-model="form.document"
- outlined
- dense
- input-class="text-text"
- mask="###.###.###-##"
- unmasked-value
- :placeholder="$t('profile.placeholder_document')"
- :rules="[inputRules.cpf]"
- :error="!!serverErrors.document"
- :error-message="serverErrors.document"
- @update:model-value="serverErrors.document = null"
- />
- </div>
- <div>
- <div class="text-grey-8 q-mb-sm font12 fontbold">{{ $t('profile.language') }}</div>
- <div class="row">
- <q-radio v-model="selectedLocale" val="pt" :label="$t('profile.lang_pt')" color="primary" class="text-text col-4 font10 fontmedium" keep-color @update:model-value="onLocaleChange" />
- <q-radio v-model="selectedLocale" val="en" :label="$t('profile.lang_en')" color="primary" class="text-text col-4 font10 fontmedium" keep-color @update:model-value="onLocaleChange" />
- <q-radio v-model="selectedLocale" val="es" :label="$t('profile.lang_es')" color="primary" class="text-text col-4 font10 fontmedium" keep-color @update:model-value="onLocaleChange" />
- </div>
- </div>
- </div>
- <q-space />
- <div class="q-pa-xl q-mt-md">
- <q-btn
- type="submit"
- unelevated
- rounded
- no-caps
- padding="8px 16px"
- class="full-width q-py-md"
- :label="$t('profile.update')"
- :color="hasUpdatedFields || avatarFile ? 'primary' : 'grey-4'"
- :disable="!hasUpdatedFields && !avatarFile"
- :loading="submitting"
- />
- </div>
- </q-form>
- </q-scroll-area>
- </template>
- </q-card>
- </q-dialog>
- </template>
- <script setup>
- import { ref, onMounted } from 'vue';
- import { useDialogPluginComponent, Cookies } from 'quasar';
- import { updateMe } from 'src/api/user';
- import { useFormUpdateTracker } from 'src/composables/useFormUpdateTracker';
- import { useSubmitHandler } from 'src/composables/useSubmitHandler';
- import { useInputRules } from 'src/composables/useInputRules';
- import { i18n } from 'src/boot/i18n';
- const props = defineProps({
- userData: {
- type: Object,
- default: null
- }
- });
- defineEmits([...useDialogPluginComponent.emits]);
- const normalizeLocale = (loc) => {
- if (!loc) return 'pt';
- const l = String(loc).toLowerCase();
- if (l.startsWith('pt')) return 'pt';
- if (l.startsWith('en')) return 'en';
- if (l.startsWith('es')) return 'es';
- return 'pt';
- };
- const { dialogRef, onDialogOK, onDialogCancel } = useDialogPluginComponent();
- const { form, hasUpdatedFields, setUpdateFormAsOriginal } = useFormUpdateTracker({
- name: '',
- email: '',
- phone: '',
- document: '',
- });
- const { loading: submitting, serverErrors, execute: submitForm } = useSubmitHandler((data) => {
- setUpdateFormAsOriginal(data);
- onDialogOK(data);
- });
- const { inputRules } = useInputRules();
- const formRef = ref(null);
- const fileInputRef = ref(null);
- const loading = ref(false);
- const avatarFile = ref(null);
- const avatarPreview = ref(null);
- const selectedLocale = ref(normalizeLocale(i18n.global.locale.value ?? i18n.global.locale));
- const onLocaleChange = (val) => {
- i18n.global.locale.value = val;
- Cookies.set('locale', val, { expires: 365, path: '/' });
- };
- const onFileSelected = (event) => {
- const file = event.target.files[0];
- if (!file) return;
- avatarFile.value = file;
- avatarPreview.value = URL.createObjectURL(file);
- };
- const onSubmit = async () => {
- const valid = await formRef.value.validate();
- if (!valid) return;
- let payload;
- if (avatarFile.value) {
- payload = new FormData();
- payload.append('name', form.name);
- payload.append('email', form.email);
- payload.append('phone', form.phone);
- if (form.document) payload.append('document', form.document);
- payload.append('avatar', avatarFile.value);
- payload.append('_method', 'PUT');
- } else {
- payload = {
- name: form.name,
- email: form.email,
- phone: form.phone,
- document: form.document || null,
- };
- }
- await submitForm(() => updateMe(payload, !!avatarFile.value));
- };
- onMounted(async () => {
- if (props.userData) {
- const data = props.userData;
- form.name = data.name || '';
- form.email = data.email || '';
- form.phone = data.phone || '';
- form.document = data.client_document || '';
- avatarPreview.value = data.client?.profile_media?.url ?? null;
- setUpdateFormAsOriginal(data);
- return;
- }
- loading.value = true;
- });
- </script>
- <style scoped lang="scss">
- :deep(.q-field--outlined .q-field__control) {
- border-radius: 8px;
- &::before { border: 1px solid #e0e0e0; }
- }
- .hidden {
- display: none;
- }
- </style>
|