|
|
@@ -1,795 +1,11 @@
|
|
|
<template>
|
|
|
- <div class="cadastro-page q-mr-md">
|
|
|
- <DefaultHeaderPage :title="{ value: pageTitle }" />
|
|
|
-
|
|
|
- <div class="cadastro-page__tabs q-mb-md">
|
|
|
- <div
|
|
|
- v-for="tab in tabsItems"
|
|
|
- :key="tab.name"
|
|
|
- class="cadastro-page__tab"
|
|
|
- :class="{
|
|
|
- 'cadastro-page__tab--active': activeTab === tab.name,
|
|
|
- 'cadastro-page__tab--disabled': tab.disable,
|
|
|
- }"
|
|
|
- @click="!tab.disable && (activeTab = tab.name)"
|
|
|
- >
|
|
|
- {{ tab.label }}
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <q-tab-panels v-model="activeTab" animated keep-alive>
|
|
|
-
|
|
|
- <q-tab-panel name="dados" class="q-pa-none">
|
|
|
- <q-form ref="formDadosRef" @submit="saveDados">
|
|
|
- <div class="q-pr-md q-pb-md bg-violet-light">
|
|
|
- <DefaultFilePicker
|
|
|
- v-model="logoFile"
|
|
|
- type="image"
|
|
|
- :label="$t('parceiro.logo')"
|
|
|
- :initial-image="partner?.logo?.url"
|
|
|
- style="height: 220px; max-width: 280px"
|
|
|
- />
|
|
|
- </div>
|
|
|
-
|
|
|
- <q-card flat class="cadastro-card q-pa-md">
|
|
|
- <div class="row q-col-gutter-sm">
|
|
|
- <DefaultInput
|
|
|
- v-model="formDados.company_name"
|
|
|
- v-model:error="errorsDados.company_name"
|
|
|
- :rules="[inputRules.required]"
|
|
|
- :label="$t('parceiro.company_name')"
|
|
|
- class="col-12"
|
|
|
- />
|
|
|
- <DefaultInput
|
|
|
- v-model="formDados.cnpj"
|
|
|
- v-model:error="errorsDados.cnpj"
|
|
|
- :label="$t('common.terms.cnpj')"
|
|
|
- :placeholder="'00.000.000/0000-00'"
|
|
|
- :mask="'##.###.###/####-##'"
|
|
|
- class="col-12"
|
|
|
- />
|
|
|
- <PartnerAgreementCategorySelect
|
|
|
- v-model="selectedCategory"
|
|
|
- v-model:error="errorsDados.category_id"
|
|
|
- class="col-12"
|
|
|
- />
|
|
|
-
|
|
|
- <!-- Novo usuário — apenas na criação -->
|
|
|
- <template v-if="!partnerId">
|
|
|
- <DefaultInput
|
|
|
- v-model="newUser.name"
|
|
|
- :rules="[inputRules.required]"
|
|
|
- :label="$t('common.terms.name')"
|
|
|
- class="col-12"
|
|
|
- />
|
|
|
- <DefaultInput
|
|
|
- v-model="newUser.email"
|
|
|
- :rules="[inputRules.required, inputRules.email]"
|
|
|
- :label="$t('common.terms.email')"
|
|
|
- type="email"
|
|
|
- class="col-12"
|
|
|
- />
|
|
|
- <DefaultInput
|
|
|
- v-model="newUser.password"
|
|
|
- :rules="[inputRules.required]"
|
|
|
- :label="$t('common.terms.password')"
|
|
|
- type="password"
|
|
|
- class="col-12"
|
|
|
- />
|
|
|
- </template>
|
|
|
-
|
|
|
- <!-- Usuário vinculado — apenas na edição -->
|
|
|
- <DefaultInput
|
|
|
- v-else-if="partner?.user"
|
|
|
- :model-value="partner.user.name"
|
|
|
- :label="$t('parceiro.linked_user')"
|
|
|
- disable
|
|
|
- class="col-12"
|
|
|
- />
|
|
|
-
|
|
|
- <DefaultInput
|
|
|
- v-model="formDados.responsible"
|
|
|
- v-model:error="errorsDados.responsible"
|
|
|
- :label="$t('parceiro.responsible')"
|
|
|
- class="col-12"
|
|
|
- />
|
|
|
- <DefaultInput
|
|
|
- v-model="formDados.discount_percentage"
|
|
|
- v-model:error="errorsDados.discount_percentage"
|
|
|
- :label="$t('parceiro.discount_percentage')"
|
|
|
- type="number"
|
|
|
- suffix="%"
|
|
|
- class="col-12"
|
|
|
- />
|
|
|
- <DefaultInput
|
|
|
- v-model="formDados.description"
|
|
|
- v-model:error="errorsDados.description"
|
|
|
- :label="$t('common.terms.description')"
|
|
|
- type="textarea"
|
|
|
- autogrow
|
|
|
- class="col-12"
|
|
|
- />
|
|
|
- </div>
|
|
|
- </q-card>
|
|
|
-
|
|
|
- <div class="row justify-end q-py-md bg-violet-light">
|
|
|
- <q-btn
|
|
|
- unelevated
|
|
|
- class="btn-gradient"
|
|
|
- :label="$t('common.actions.save')"
|
|
|
- icon="mdi-check"
|
|
|
- type="submit"
|
|
|
- :loading="loadingDados"
|
|
|
- :disable="!hasUpdatedDados"
|
|
|
- />
|
|
|
- </div>
|
|
|
- </q-form>
|
|
|
- </q-tab-panel>
|
|
|
-
|
|
|
- <q-tab-panel name="contato" class="q-pa-none">
|
|
|
- <q-form ref="formContatoRef" @submit="saveContato">
|
|
|
- <q-card flat class="cadastro-card q-pa-md">
|
|
|
- <div class="row q-col-gutter-sm">
|
|
|
- <DefaultInput
|
|
|
- v-model="formContato.email"
|
|
|
- v-model:error="errorsContato.email"
|
|
|
- :rules="[inputRules.email]"
|
|
|
- :label="$t('common.terms.email')"
|
|
|
- class="col-12"
|
|
|
- />
|
|
|
- <DefaultInput
|
|
|
- v-model="formContato.website"
|
|
|
- v-model:error="errorsContato.website"
|
|
|
- :label="$t('parceiro.website')"
|
|
|
- class="col-12"
|
|
|
- />
|
|
|
- <DefaultInput
|
|
|
- v-model="formContato.phone"
|
|
|
- v-model:error="errorsContato.phone"
|
|
|
- :label="$t('common.terms.phone')"
|
|
|
- :mask="'(##) #####-####'"
|
|
|
- class="col-12"
|
|
|
- />
|
|
|
- <DefaultInput
|
|
|
- v-model="formContato.whatsapp"
|
|
|
- v-model:error="errorsContato.whatsapp"
|
|
|
- :label="$t('common.terms.whatsapp')"
|
|
|
- :mask="'(##) #####-####'"
|
|
|
- class="col-12"
|
|
|
- />
|
|
|
- </div>
|
|
|
- </q-card>
|
|
|
-
|
|
|
- <div class="row justify-end bg-violet-light q-pt-md">
|
|
|
- <q-btn
|
|
|
- unelevated
|
|
|
- class="btn-gradient"
|
|
|
- :label="$t('common.actions.save')"
|
|
|
- icon="mdi-check"
|
|
|
- type="submit"
|
|
|
- :loading="loadingContato"
|
|
|
- :disable="!hasUpdatedContato"
|
|
|
- />
|
|
|
- </div>
|
|
|
- </q-form>
|
|
|
- </q-tab-panel>
|
|
|
-
|
|
|
- <q-tab-panel name="endereco" class="q-pa-none">
|
|
|
- <q-form ref="formEnderecoRef" @submit="saveEndereco">
|
|
|
- <q-card flat class="cadastro-card q-pa-md">
|
|
|
- <div class="row q-col-gutter-sm">
|
|
|
- <DefaultInput
|
|
|
- v-model="formEndereco.zip_code"
|
|
|
- v-model:error="errorsEndereco.zip_code"
|
|
|
- :label="$t('parceiro.zip_code')"
|
|
|
- :placeholder="'00000-000'"
|
|
|
- :mask="'#####-###'"
|
|
|
- class="col-md-4 col-12"
|
|
|
- @update:model-value="onCepChange"
|
|
|
- />
|
|
|
- <DefaultInput
|
|
|
- v-model="formEndereco.address"
|
|
|
- v-model:error="errorsEndereco.address"
|
|
|
- :label="$t('parceiro.address')"
|
|
|
- :loading="loadingCep"
|
|
|
- class="col-md-8 col-12"
|
|
|
- />
|
|
|
- <DefaultInput
|
|
|
- v-model="formEndereco.neighborhood"
|
|
|
- v-model:error="errorsEndereco.neighborhood"
|
|
|
- :label="$t('parceiro.neighborhood')"
|
|
|
- :loading="loadingCep"
|
|
|
- class="col-12"
|
|
|
- />
|
|
|
- <StateSelect
|
|
|
- v-model="selectedState"
|
|
|
- v-model:error="errorsEndereco.state_id"
|
|
|
- class="col-md-4 col-12 input-violet"
|
|
|
- />
|
|
|
- <CitySelect
|
|
|
- v-model="selectedCity"
|
|
|
- v-model:error="errorsEndereco.city_id"
|
|
|
- :state="selectedState"
|
|
|
- class="col-md-8 col-12 input-violet"
|
|
|
- />
|
|
|
- </div>
|
|
|
- </q-card>
|
|
|
-
|
|
|
- <div class="row justify-end q-pt-md bg-violet-light">
|
|
|
- <q-btn
|
|
|
- unelevated
|
|
|
- class="btn-gradient"
|
|
|
- :label="$t('common.actions.save')"
|
|
|
- icon="mdi-check"
|
|
|
- type="submit"
|
|
|
- :loading="loadingEndereco"
|
|
|
- :disable="!hasUpdatedEndereco"
|
|
|
- />
|
|
|
- </div>
|
|
|
- </q-form>
|
|
|
- </q-tab-panel>
|
|
|
-
|
|
|
- <q-tab-panel name="horario" class="q-pa-none">
|
|
|
- <q-form ref="formHorarioRef" @submit="saveHorario">
|
|
|
- <q-card flat class="cadastro-card q-pa-md">
|
|
|
- <div class="row q-col-gutter-sm">
|
|
|
- <DefaultInput
|
|
|
- v-model="formHorario.working_hours"
|
|
|
- v-model:error="errorsHorario.working_hours"
|
|
|
- :label="$t('parceiro.working_hours')"
|
|
|
- :placeholder="$t('parceiro.working_hours_placeholder')"
|
|
|
- class="col-12"
|
|
|
- />
|
|
|
- <DefaultInputDatePicker
|
|
|
- v-model:untreated-date="formHorario.contract_start"
|
|
|
- v-model:error="errorsHorario.contract_start"
|
|
|
- :label="$t('parceiro.contract_start')"
|
|
|
- class="col-md-6 col-12"
|
|
|
- />
|
|
|
- <DefaultInputDatePicker
|
|
|
- v-model:untreated-date="formHorario.contract_end"
|
|
|
- v-model:error="errorsHorario.contract_end"
|
|
|
- :label="$t('parceiro.contract_end')"
|
|
|
- class="col-md-6 col-12"
|
|
|
- />
|
|
|
- </div>
|
|
|
-
|
|
|
- <div v-if="contractMedia.length > 0" class="q-mt-md">
|
|
|
- <div class="text-subtitle2 text-violet-normal q-mb-xs">{{ $t("parceiro.contract_files") }}</div>
|
|
|
- <div class="row q-col-gutter-xs">
|
|
|
- <div v-for="media in contractMedia" :key="media.id" class="col-auto">
|
|
|
- <q-chip
|
|
|
- removable
|
|
|
- clickable
|
|
|
- color="violet-light"
|
|
|
- text-color="violet-normal"
|
|
|
- icon="mdi-file-outline"
|
|
|
- :label="media.name"
|
|
|
- @click="openMedia(media)"
|
|
|
- @remove="removeMedia(media)"
|
|
|
- />
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </q-card>
|
|
|
-
|
|
|
- <div class="row justify-end items-center q-pt-md bg-violet-light" style="gap: 8px">
|
|
|
- <q-file
|
|
|
- ref="mediaFileInputRef"
|
|
|
- v-model="newMediaFile"
|
|
|
- accept=".pdf,.doc,.docx,.png,.jpg,.jpeg"
|
|
|
- style="display: none"
|
|
|
- @update:model-value="onMediaFileSelected"
|
|
|
- />
|
|
|
- <q-btn
|
|
|
- unelevated
|
|
|
- class="btn-gradient"
|
|
|
- :label="$t('parceiro.add_file')"
|
|
|
- icon="mdi-paperclip"
|
|
|
- :disable="!partnerId"
|
|
|
- @click="triggerMediaUpload"
|
|
|
- />
|
|
|
- <q-btn
|
|
|
- unelevated
|
|
|
- class="btn-gradient"
|
|
|
- :label="$t('common.actions.save')"
|
|
|
- icon="mdi-check"
|
|
|
- type="submit"
|
|
|
- :loading="loadingHorario"
|
|
|
- :disable="!hasUpdatedHorario"
|
|
|
- />
|
|
|
- </div>
|
|
|
- </q-form>
|
|
|
- </q-tab-panel>
|
|
|
-
|
|
|
- <q-tab-panel name="servicos" class="q-pa-none">
|
|
|
- <div class="bg-violet-light q-pb-md">
|
|
|
- <div class="row justify-end q-mb-sm q-gutter-sm">
|
|
|
- <q-btn
|
|
|
- unelevated
|
|
|
- icon="mdi-upload"
|
|
|
- :label="$t('common.actions.import')"
|
|
|
- padding="6px 12px"
|
|
|
- :disable="!partnerId"
|
|
|
- class="btn-gradient"
|
|
|
- />
|
|
|
- <q-btn
|
|
|
- unelevated
|
|
|
- icon="mdi-plus"
|
|
|
- :label="$t('common.actions.new')"
|
|
|
- padding="6px 12px"
|
|
|
- :disable="!partnerId"
|
|
|
- class="btn-gradient"
|
|
|
- @click="onAddService"
|
|
|
- />
|
|
|
- </div>
|
|
|
-
|
|
|
- <q-input
|
|
|
- v-model="serviceSearch"
|
|
|
- :placeholder="$t('parceiro.search_placeholder')"
|
|
|
- outlined
|
|
|
- dense
|
|
|
- clearable
|
|
|
- color="violet-normal"
|
|
|
- class="q-mb-sm service-search"
|
|
|
- >
|
|
|
- <template #prepend>
|
|
|
- <q-icon name="mdi-magnify" color="violet-normal" />
|
|
|
- </template>
|
|
|
- </q-input>
|
|
|
- </div>
|
|
|
-
|
|
|
- <q-card class="">
|
|
|
- <div v-if="loadingServices" class="flex flex-center q-pa-xl">
|
|
|
- <q-spinner color="violet-normal" size="40px" />
|
|
|
- </div>
|
|
|
-
|
|
|
- <DefaultTable
|
|
|
- v-else
|
|
|
- v-model:rows="filteredServices"
|
|
|
- :columns="serviceColumns"
|
|
|
- no-api-call
|
|
|
- :show-search-field="false"
|
|
|
- open-item
|
|
|
- @on-row-click="({ row }) => onEditService(row)"
|
|
|
- />
|
|
|
- </q-card>
|
|
|
- </q-tab-panel>
|
|
|
-
|
|
|
- </q-tab-panels>
|
|
|
- </div>
|
|
|
+ <CadastroFormPanel
|
|
|
+ entity-type="partner"
|
|
|
+ cadastro-route-name="ParceiroCadastroPage"
|
|
|
+ :show-scheduling="false"
|
|
|
+ />
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, reactive, computed, watch, onMounted, nextTick, useTemplateRef } from "vue";
|
|
|
-import { useRoute, useRouter } from "vue-router";
|
|
|
-import { useQuasar } from "quasar";
|
|
|
-import { useI18n } from "vue-i18n";
|
|
|
-import { useInputRules } from "src/composables/useInputRules";
|
|
|
-import { useFormUpdateTracker } from "src/composables/useFormUpdateTracker";
|
|
|
-import { useSubmitHandler } from "src/composables/useSubmitHandler";
|
|
|
-import {
|
|
|
- getPartnerAgreement,
|
|
|
- createPartnerAgreement,
|
|
|
- updatePartnerAgreement,
|
|
|
- uploadPartnerLogo,
|
|
|
- uploadPartnerMedia,
|
|
|
- deletePartnerMedia,
|
|
|
-} from "src/api/partnerAgreement";
|
|
|
-import { getServicesByPartner } from "src/api/partnerAgreementService";
|
|
|
-import axios from "axios";
|
|
|
-
|
|
|
-import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
|
|
|
-import DefaultInput from "src/components/defaults/DefaultInput.vue";
|
|
|
-import DefaultInputDatePicker from "src/components/defaults/DefaultInputDatePicker.vue";
|
|
|
-import DefaultFilePicker from "src/components/defaults/DefaultFilePicker.vue";
|
|
|
-import DefaultTable from "src/components/defaults/DefaultTable.vue";
|
|
|
-import PartnerAgreementCategorySelect from "src/components/selects/PartnerAgreementCategorySelect.vue";
|
|
|
-import CitySelect from "src/components/selects/CitySelect.vue";
|
|
|
-import StateSelect from "src/components/selects/StateSelect.vue";
|
|
|
-
|
|
|
-const route = useRoute();
|
|
|
-const router = useRouter();
|
|
|
-const $q = useQuasar();
|
|
|
-const { t } = useI18n();
|
|
|
-const { inputRules } = useInputRules();
|
|
|
-
|
|
|
-const partnerId = computed(() => (route.params.id ? Number(route.params.id) : null));
|
|
|
-const partner = ref(null);
|
|
|
-const activeTab = ref(route.query.tab ?? "dados");
|
|
|
-const panelContainerRef = ref(null);
|
|
|
-
|
|
|
-watch(activeTab, async () => {
|
|
|
- const container = panelContainerRef.value;
|
|
|
- if (!container) return;
|
|
|
-
|
|
|
- const fromHeight = container.offsetHeight;
|
|
|
-
|
|
|
- container.style.transition = "none";
|
|
|
- container.style.overflow = "hidden";
|
|
|
- container.style.height = fromHeight + "px";
|
|
|
-
|
|
|
- await nextTick();
|
|
|
-
|
|
|
- void container.offsetHeight;
|
|
|
-
|
|
|
- requestAnimationFrame(() => {
|
|
|
- const activePanel = container.querySelector(".panel-item:not(.panel-item--hidden)");
|
|
|
- const toHeight = activePanel ? activePanel.offsetHeight : fromHeight;
|
|
|
-
|
|
|
- container.style.transition = "height 0.5s ease";
|
|
|
- container.style.height = toHeight + "px";
|
|
|
-
|
|
|
- setTimeout(() => {
|
|
|
- container.style.height = "auto";
|
|
|
- container.style.overflow = "";
|
|
|
- container.style.transition = "";
|
|
|
- }, 520);
|
|
|
- });
|
|
|
-}, { flush: "pre" });
|
|
|
-
|
|
|
-const pageTitle = computed(() =>
|
|
|
- partnerId.value ? t("parceiro.dados_parceiro") : t("parceiro.cadastro_parceiro"),
|
|
|
-);
|
|
|
-
|
|
|
-const tabsItems = computed(() => [
|
|
|
- { name: "dados", label: t("parceiro.tab_dados") },
|
|
|
- { name: "contato", label: t("parceiro.tab_contato"), disable: !partnerId.value },
|
|
|
- { name: "endereco", label: t("parceiro.tab_endereco"), disable: !partnerId.value },
|
|
|
- { name: "horario", label: t("parceiro.tab_horario"), disable: !partnerId.value },
|
|
|
- { name: "servicos", label: t("parceiro.tab_servicos"), disable: !partnerId.value },
|
|
|
-]);
|
|
|
-
|
|
|
-const formDadosRef = useTemplateRef("formDadosRef");
|
|
|
-const logoFile = ref(null);
|
|
|
-const selectedCategory = ref(null);
|
|
|
-const newUser = reactive({ name: "", email: "", password: "" });
|
|
|
-
|
|
|
-const {
|
|
|
- form: formDados,
|
|
|
- getUpdatedFields: updatedDados,
|
|
|
- hasUpdatedFields: hasUpdatedDados,
|
|
|
-} = useFormUpdateTracker({
|
|
|
- company_name: "",
|
|
|
- cnpj: "",
|
|
|
- category_id: null,
|
|
|
- responsible: "",
|
|
|
- discount_percentage: null,
|
|
|
- description: "",
|
|
|
-});
|
|
|
-
|
|
|
-const {
|
|
|
- loading: loadingDados,
|
|
|
- validationErrors: errorsDados,
|
|
|
- execute: execDados,
|
|
|
-} = useSubmitHandler({ formRef: formDadosRef });
|
|
|
-
|
|
|
-watch(selectedCategory, (val) => {
|
|
|
- formDados.category_id = val?.value ?? null;
|
|
|
-});
|
|
|
-
|
|
|
-const saveDados = async () => {
|
|
|
- await execDados(async () => {
|
|
|
- let savedPartner;
|
|
|
- if (partnerId.value) {
|
|
|
- savedPartner = await updatePartnerAgreement(partnerId.value, updatedDados.value);
|
|
|
- } else {
|
|
|
- savedPartner = await createPartnerAgreement({
|
|
|
- ...formDados,
|
|
|
- user_name: newUser.name,
|
|
|
- user_email: newUser.email,
|
|
|
- user_password: newUser.password,
|
|
|
- });
|
|
|
- await router.replace({ name: "ParceiroCadastroPage", params: { id: savedPartner.id } });
|
|
|
- }
|
|
|
- if (logoFile.value instanceof File) {
|
|
|
- const uploadedMedia = await uploadPartnerLogo(savedPartner.id ?? partnerId.value, logoFile.value);
|
|
|
- savedPartner = { ...savedPartner, logo: uploadedMedia };
|
|
|
- }
|
|
|
- partner.value = savedPartner;
|
|
|
- populateForms(savedPartner);
|
|
|
- logoFile.value = null;
|
|
|
- $q.notify({ type: "positive", message: t("http.success") });
|
|
|
- });
|
|
|
-};
|
|
|
-
|
|
|
-const formContatoRef = useTemplateRef("formContatoRef");
|
|
|
-const {
|
|
|
- form: formContato,
|
|
|
- getUpdatedFields: updatedContato,
|
|
|
- hasUpdatedFields: hasUpdatedContato,
|
|
|
-} = useFormUpdateTracker({ email: "", phone: "", whatsapp: "", website: "" });
|
|
|
-
|
|
|
-const {
|
|
|
- loading: loadingContato,
|
|
|
- validationErrors: errorsContato,
|
|
|
- execute: execContato,
|
|
|
-} = useSubmitHandler({ formRef: formContatoRef });
|
|
|
-
|
|
|
-const saveContato = async () => {
|
|
|
- await execContato(async () => {
|
|
|
- const saved = await updatePartnerAgreement(partnerId.value, updatedContato.value);
|
|
|
- partner.value = saved;
|
|
|
- $q.notify({ type: "positive", message: t("http.success") });
|
|
|
- });
|
|
|
-};
|
|
|
-
|
|
|
-const formEnderecoRef = useTemplateRef("formEnderecoRef");
|
|
|
-const selectedState = ref(null);
|
|
|
-const selectedCity = ref(null);
|
|
|
-const loadingCep = ref(false);
|
|
|
-
|
|
|
-const {
|
|
|
- form: formEndereco,
|
|
|
- getUpdatedFields: updatedEndereco,
|
|
|
- hasUpdatedFields: hasUpdatedEndereco,
|
|
|
-} = useFormUpdateTracker({
|
|
|
- zip_code: "",
|
|
|
- address: "",
|
|
|
- neighborhood: "",
|
|
|
- city_id: null,
|
|
|
- state_id: null,
|
|
|
-});
|
|
|
-
|
|
|
-const {
|
|
|
- loading: loadingEndereco,
|
|
|
- validationErrors: errorsEndereco,
|
|
|
- execute: execEndereco,
|
|
|
-} = useSubmitHandler({ formRef: formEnderecoRef });
|
|
|
-
|
|
|
-watch(selectedState, (val) => { formEndereco.state_id = val?.value ?? null; });
|
|
|
-watch(selectedCity, (val) => { formEndereco.city_id = val?.value ?? null; });
|
|
|
-
|
|
|
-const onCepChange = async (val) => {
|
|
|
- const digits = (val ?? "").replace(/\D/g, "");
|
|
|
- if (digits.length !== 8) return;
|
|
|
- loadingCep.value = true;
|
|
|
- try {
|
|
|
- const { data } = await axios.get(`https://viacep.com.br/ws/${digits}/json/`);
|
|
|
- if (!data.erro) {
|
|
|
- formEndereco.address = data.logradouro ?? formEndereco.address;
|
|
|
- formEndereco.neighborhood = data.bairro ?? formEndereco.neighborhood;
|
|
|
- }
|
|
|
- } catch {
|
|
|
- // silent — user fills manually
|
|
|
- } finally {
|
|
|
- loadingCep.value = false;
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-const saveEndereco = async () => {
|
|
|
- await execEndereco(async () => {
|
|
|
- const saved = await updatePartnerAgreement(partnerId.value, updatedEndereco.value);
|
|
|
- partner.value = saved;
|
|
|
- $q.notify({ type: "positive", message: t("http.success") });
|
|
|
- });
|
|
|
-};
|
|
|
-
|
|
|
-const formHorarioRef = useTemplateRef("formHorarioRef");
|
|
|
-const mediaFileInputRef = useTemplateRef("mediaFileInputRef");
|
|
|
-const newMediaFile = ref(null);
|
|
|
-const contractMedia = ref([]);
|
|
|
-
|
|
|
-const {
|
|
|
- form: formHorario,
|
|
|
- getUpdatedFields: updatedHorario,
|
|
|
- hasUpdatedFields: hasUpdatedHorario,
|
|
|
-} = useFormUpdateTracker({ working_hours: "", contract_start: null, contract_end: null });
|
|
|
-
|
|
|
-const {
|
|
|
- loading: loadingHorario,
|
|
|
- validationErrors: errorsHorario,
|
|
|
- execute: execHorario,
|
|
|
-} = useSubmitHandler({ formRef: formHorarioRef });
|
|
|
-
|
|
|
-const onMediaFileSelected = async (file) => {
|
|
|
- if (!file || !partnerId.value) return;
|
|
|
- try {
|
|
|
- const media = await uploadPartnerMedia(partnerId.value, file);
|
|
|
- contractMedia.value.push(media);
|
|
|
- } catch {
|
|
|
- $q.notify({ type: "negative", message: t("http.errors.failed") });
|
|
|
- } finally {
|
|
|
- newMediaFile.value = null;
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-const triggerMediaUpload = () => {
|
|
|
- mediaFileInputRef.value?.pickFiles();
|
|
|
-};
|
|
|
-
|
|
|
-const removeMedia = async (media) => {
|
|
|
- try {
|
|
|
- await deletePartnerMedia(partnerId.value, media.id);
|
|
|
- contractMedia.value = contractMedia.value.filter((m) => m.id !== media.id);
|
|
|
- } catch {
|
|
|
- $q.notify({ type: "negative", message: t("http.errors.failed") });
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-const openMedia = (media) => {
|
|
|
- window.open(media.url, "_blank");
|
|
|
-};
|
|
|
-
|
|
|
-const saveHorario = async () => {
|
|
|
- await execHorario(async () => {
|
|
|
- const saved = await updatePartnerAgreement(partnerId.value, updatedHorario.value);
|
|
|
- partner.value = saved;
|
|
|
- $q.notify({ type: "positive", message: t("http.success") });
|
|
|
- });
|
|
|
-};
|
|
|
-
|
|
|
-const services = ref([]);
|
|
|
-const loadingServices = ref(false);
|
|
|
-const serviceSearch = ref("");
|
|
|
-
|
|
|
-const filteredServices = computed(() => {
|
|
|
- if (!serviceSearch.value) return services.value;
|
|
|
- const needle = serviceSearch.value.toLowerCase();
|
|
|
- return services.value.filter((s) =>
|
|
|
- [s.name, s.category?.name, s.price, s.associate_price]
|
|
|
- .some((f) => f != null && String(f).toLowerCase().includes(needle))
|
|
|
- );
|
|
|
-});
|
|
|
-
|
|
|
-const serviceColumns = computed(() => [
|
|
|
- { name: "name", label: t("common.terms.name"), field: "name", align: "left", sortable: true },
|
|
|
- { name: "category", label: t("parceiro.service_category"), field: (r) => r.category?.name ?? "—", align: "left" },
|
|
|
- { name: "price", label: t("parceiro.price"), field: "price", align: "left" },
|
|
|
- { name: "associate_price", label: t("parceiro.associate_price"), field: "associate_price", align: "left" },
|
|
|
- { name: "actions", label: t("common.terms.actions"), align: "right", required: true },
|
|
|
-]);
|
|
|
-
|
|
|
-const loadServices = async () => {
|
|
|
- if (!partnerId.value) return;
|
|
|
- loadingServices.value = true;
|
|
|
- try {
|
|
|
- services.value = await getServicesByPartner(partnerId.value);
|
|
|
- } finally {
|
|
|
- loadingServices.value = false;
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-const onAddService = () => {
|
|
|
- router.push({
|
|
|
- name: "ParceiroServicoCadastroPage",
|
|
|
- params: { id: partnerId.value },
|
|
|
- });
|
|
|
-};
|
|
|
-
|
|
|
-const onEditService = (svc) => {
|
|
|
- router.push({
|
|
|
- name: "ParceiroServicoCadastroPage",
|
|
|
- params: { id: partnerId.value, serviceId: svc.id },
|
|
|
- });
|
|
|
-};
|
|
|
-
|
|
|
-const populateForms = (p) => {
|
|
|
- if (!p) return;
|
|
|
-
|
|
|
- formDados.company_name = p.company_name ?? "";
|
|
|
- formDados.cnpj = p.cnpj ?? "";
|
|
|
- formDados.category_id = p.category_id ?? null;
|
|
|
- formDados.responsible = p.responsible ?? "";
|
|
|
- formDados.discount_percentage = p.discount_percentage != null ? String(p.discount_percentage) : null;
|
|
|
- formDados.description = p.description ?? "";
|
|
|
-
|
|
|
- selectedCategory.value = p.category
|
|
|
- ? { label: p.category.name, value: p.category.id }
|
|
|
- : null;
|
|
|
-
|
|
|
- formContato.email = p.email ?? "";
|
|
|
- formContato.phone = p.phone ?? "";
|
|
|
- formContato.whatsapp = p.whatsapp ?? "";
|
|
|
- formContato.website = p.website ?? "";
|
|
|
-
|
|
|
- formEndereco.zip_code = p.zip_code ?? "";
|
|
|
- formEndereco.address = p.address ?? "";
|
|
|
- formEndereco.neighborhood = p.neighborhood ?? "";
|
|
|
- formEndereco.city_id = p.city_id ?? null;
|
|
|
- formEndereco.state_id = p.state_id ?? null;
|
|
|
-
|
|
|
- selectedState.value = p.state ? { label: p.state.name, value: p.state.id } : null;
|
|
|
- selectedCity.value = p.city ? { label: p.city.name, value: p.city.id } : null;
|
|
|
-
|
|
|
- formHorario.working_hours = p.working_hours ?? "";
|
|
|
- formHorario.contract_start = p.contract_start ?? null;
|
|
|
- formHorario.contract_end = p.contract_end ?? null;
|
|
|
-
|
|
|
- contractMedia.value = p.media ?? [];
|
|
|
-};
|
|
|
-
|
|
|
-onMounted(async () => {
|
|
|
- if (partnerId.value) {
|
|
|
- try {
|
|
|
- const p = await getPartnerAgreement(partnerId.value);
|
|
|
- partner.value = p;
|
|
|
- populateForms(p);
|
|
|
- } catch {
|
|
|
- $q.notify({ type: "negative", message: t("http.errors.failed") });
|
|
|
- }
|
|
|
- if (activeTab.value === "servicos") loadServices();
|
|
|
- }
|
|
|
-});
|
|
|
-
|
|
|
-watch(activeTab, (tab) => {
|
|
|
- if (tab === "servicos") loadServices();
|
|
|
-});
|
|
|
+import CadastroFormPanel from "./components/CadastroFormPanel.vue";
|
|
|
</script>
|
|
|
-
|
|
|
-<style lang="scss" scoped>
|
|
|
-@use "src/css/quasar.variables.scss";
|
|
|
-
|
|
|
-.cadastro-page {
|
|
|
- &__tabs {
|
|
|
- display: flex;
|
|
|
- flex-wrap: wrap;
|
|
|
- gap: 6px;
|
|
|
- }
|
|
|
-
|
|
|
- &__tab {
|
|
|
- padding: 4px 16px;
|
|
|
- border-radius: 6px;
|
|
|
- font-size: 14px;
|
|
|
- font-weight: 600;
|
|
|
- cursor: pointer;
|
|
|
- background: white;
|
|
|
- color: $violet-normal;
|
|
|
- border: 1.5px solid rgba($violet-normal, 0.3);
|
|
|
- transition: background 0.15s, color 0.15s;
|
|
|
- user-select: none;
|
|
|
-
|
|
|
- &--active {
|
|
|
- background: $violet-normal;
|
|
|
- color: white;
|
|
|
- border-color: $violet-normal;
|
|
|
- }
|
|
|
-
|
|
|
- &--disabled {
|
|
|
- opacity: 0.4;
|
|
|
- cursor: not-allowed;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.tab-panels-wrapper {
|
|
|
- :deep(.q-panel-parent) {
|
|
|
- overflow: hidden !important;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.panels-container {
|
|
|
- position: relative;
|
|
|
- overflow: hidden;
|
|
|
-}
|
|
|
-
|
|
|
-.panel-item--hidden {
|
|
|
- position: absolute !important;
|
|
|
- top: 0;
|
|
|
- left: 0;
|
|
|
- right: 0;
|
|
|
- visibility: hidden !important;
|
|
|
- pointer-events: none !important;
|
|
|
- z-index: -1;
|
|
|
-}
|
|
|
-
|
|
|
-.cadastro-card {
|
|
|
- border-radius: 12px;
|
|
|
- background: white;
|
|
|
-}
|
|
|
-
|
|
|
-.btn-gradient {
|
|
|
- background: linear-gradient(90deg, #4d1658 0%, #8b30a5 100%) !important;
|
|
|
- color: white !important;
|
|
|
- border-radius: 8px !important;
|
|
|
- padding: 8px 16px;
|
|
|
-}
|
|
|
-
|
|
|
-.btn-gradient :deep(.q-icon) {
|
|
|
- color: white !important;
|
|
|
-}
|
|
|
-
|
|
|
-.service-search :deep(.q-field__control) {
|
|
|
- border-radius: 24px !important;
|
|
|
-}
|
|
|
-</style>
|