Przeglądaj źródła

feat: :sparkles: crud tipos servicos dentro de providers

crud tipos servicos dentro de providers
Gustavo Zanatta 1 miesiąc temu
rodzic
commit
8d640bacd9

+ 17 - 0
src/api/providerServicesType.js

@@ -0,0 +1,17 @@
+import api from "src/api";
+
+export const getProviderServiceTypes = async (providerId) => {
+  const response = await api.get(`/provider/service-types/${providerId}`)
+  return response.data
+}
+
+export const createProviderServiceType = async (providerId, data) => {
+    console.log(data)
+  const response = await api.post(`/provider/service-types/${providerId}`, data)
+  return response.data
+}
+
+export const deleteProviderServiceType = async (providerId, id) => {
+  const response = await api.delete(`/provider/service-types/${providerId}/${id}`)
+  return response.data
+}

+ 16 - 11
src/components/serviceType/ServiceTypeSelect.vue

@@ -27,11 +27,11 @@
 
 <script setup>
 import { getServiceTypes } from "src/api/serviceType";
-import { ref, onMounted } from "vue";
+import { ref, onMounted, computed } from "vue";
 import { normalizeString } from "src/helpers/utils";
 import { useI18n } from "vue-i18n";
 
-const { label, rules, initialId } = defineProps({
+const { label, rules, initialId, excludeIds } = defineProps({
   label: {
     type: String,
     default: () => useI18n().t("ui.navigation.service_type"),
@@ -53,21 +53,28 @@ const { label, rules, initialId } = defineProps({
     type: String,
     default: "",
   },
+  excludeIds: {
+    type: Array,
+    default: () => [],
+  },
 });
 
 const selectedServiceType = defineModel({ type: Object });
 
 const loading = ref(false);
 const baseOptions = ref([]);
+const filteredOptions = computed(() => {
+  if (!excludeIds || excludeIds.length === 0) {
+    return baseOptions.value;
+  }
+  return baseOptions.value.filter(option => !excludeIds.includes(option.value));
+});
 const serviceTypeOptions = ref([]);
 
 const filterFn = async (val, update) => {
   const needle = normalizeString(val);
-  serviceTypeOptions.value = baseOptions.value.filter((v) => {
-    return (
-      normalizeString(v.label).includes(needle) ||
-      normalizeString(v.document).includes(needle)
-    );
+  serviceTypeOptions.value = filteredOptions.value.filter((v) => {
+    return normalizeString(v.label).includes(needle);
   });
   update();
 };
@@ -91,12 +98,10 @@ onMounted(async () => {
     loading.value = true;
     const baseServiceTypes = await getServiceTypes();
     baseOptions.value = baseServiceTypes.map((serviceType) => ({
-      label: serviceType.user?.name || serviceType.document,
+      label: serviceType.description,
       value: serviceType.id,
-      document: serviceType.document,
-      user_id: serviceType.user_id,
     }));
-    serviceTypeOptions.value = baseOptions.value;
+    serviceTypeOptions.value = filteredOptions.value;
     if (initialId) {
       selectServiceTypeById(initialId);
     }

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

@@ -317,6 +317,13 @@
       "daily_price": "Value between R$ 100.00 and R$ 500.00"
     }
   },
+  "provider_services_types": {
+    "header": "Service Types",
+    "add_button": "Add",
+    "select_placeholder": "Select a service type",
+    "empty_state": "No service types added",
+    "remove_confirm": "Are you sure you want to remove this service type?"
+  },
   "orders": {
     "singular": "Order",
     "plural": "Orders",

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

@@ -317,6 +317,13 @@
       "daily_price": "Valor entre R$ 100,00 y R$ 500,00"
     }
   },
+  "provider_services_types": {
+    "header": "Tipos de Servicios",
+    "add_button": "Agregar",
+    "select_placeholder": "Seleccione un tipo de servicio",
+    "empty_state": "No se han agregado tipos de servicios",
+    "remove_confirm": "¿Está seguro de que desea eliminar este tipo de servicio?"
+  },
   "orders": {
     "singular": "Pedido",
     "plural": "Pedidos",

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

@@ -317,6 +317,13 @@
       "daily_price": "Valor entre R$ 100,00 e R$ 500,00"
     }
   },
+  "provider_services_types": {
+    "header": "Tipos de Serviços",
+    "add_button": "Adicionar",
+    "select_placeholder": "Selecione um tipo de serviço",
+    "empty_state": "Nenhum tipo de serviço adicionado",
+    "remove_confirm": "Tem certeza que deseja remover este tipo de serviço?"
+  },
   "orders": {
     "singular": "Pedido",
     "plural": "Pedidos",

+ 3 - 0
src/pages/provider/components/AddEditProviderDialog.vue

@@ -97,6 +97,8 @@
                 />
               </div>
 
+              <ProviderServicesTypesPanel v-if="provider" :provider-id="provider.id" />
+
               <div class="col-12 q-mt-md">
                 <div class="row q-col-gutter-md">
                   <div class="col-auto flex items-center">
@@ -175,6 +177,7 @@ import UserSelect from "src/components/user/UserSelect.vue";
 import DefaultInputDatePicker from "src/components/defaults/DefaultInputDatePicker.vue";
 import DefaultCurrencyInput from "src/components/defaults/DefaultCurrencyInput.vue";
 import AddressesPanel from "src/pages/address/components/AddressesPanel.vue";
+import ProviderServicesTypesPanel from "./ProviderServicesTypesPanel.vue";
 
 defineEmits([
   ...useDialogPluginComponent.emits,

+ 165 - 0
src/pages/provider/components/ProviderServicesTypesPanel.vue

@@ -0,0 +1,165 @@
+<template>
+  <div class="col-12">
+    <div class="text-h6 q-mb-md">{{ $t('provider_services_types.header') }}</div>
+
+    <div class="row items-center col-12">
+      <ServiceTypeSelect
+        v-model="selectedServiceType"
+        :label="$t('provider_services_types.select_placeholder')"
+        :disable="!providerId || loading"
+        :exclude-ids="excludedServiceTypeIds"
+        class="col-md-6 col-12 q-pb-none q-mr-sm"
+      />
+
+      <div class="col-auto">
+        <q-btn
+          color="primary"
+          :label="$t('provider_services_types.add_button')"
+          :disable="!selectedServiceType || loading"
+          :loading="adding"
+          padding="16px 16px"
+          @click="handleAdd"
+        />
+      </div>
+    </div>
+
+    <div v-if="loading" class="q-mt-md text-center">
+      <q-spinner color="primary" size="40px" />
+    </div>
+
+    <div v-else-if="providerServiceTypes.length > 0" class="q-mt-md q-gutter-sm">
+      <q-chip
+        v-for="item in providerServiceTypes"
+        :key="item.id"
+        removable
+        color="primary"
+        text-color="white"
+        @remove="handleRemove(item)"
+      >
+        {{ item.service_type?.description || '-' }}
+      </q-chip>
+    </div>
+
+    <div v-else class="q-mt-md text-grey-7 text-center">
+      {{ $t('provider_services_types.empty_state') }}
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, watch, onMounted } from 'vue'
+import { useQuasar } from 'quasar'
+import { useI18n } from 'vue-i18n'
+import ServiceTypeSelect from 'src/components/serviceType/ServiceTypeSelect.vue'
+import {
+  getProviderServiceTypes,
+  createProviderServiceType,
+  deleteProviderServiceType
+} from 'src/api/providerServicesType.js'
+
+const props = defineProps({
+  providerId: {
+    type: Number,
+    default: null
+  }
+})
+
+const $q = useQuasar()
+const { t } = useI18n()
+
+const providerServiceTypes = ref([])
+const selectedServiceType = ref(null)
+const loading = ref(false)
+const adding = ref(false)
+
+const excludedServiceTypeIds = computed(() => {
+  return providerServiceTypes.value.map(item => item.service_type_id)
+})
+
+const loadProviderServiceTypes = async () => {
+  if (!props.providerId) {
+    providerServiceTypes.value = []
+    return
+  }
+
+  try {
+    loading.value = true
+    const response = await getProviderServiceTypes(props.providerId)
+    console.log(response)
+    providerServiceTypes.value = response.payload || []
+  } catch (error) {
+    console.error('Error loading provider service types:', error)
+    $q.notify({
+      type: 'negative',
+      message: error.response?.data?.message || t('http.errors.failed')
+    })
+  } finally {
+    loading.value = false
+  }
+}
+
+const handleAdd = async () => {
+  if (!selectedServiceType.value) return
+
+  try {
+    adding.value = true
+    await createProviderServiceType(props.providerId, {
+      service_type_id: selectedServiceType.value.value
+    })
+
+    $q.notify({
+      type: 'positive',
+      message: t('http.success')
+    })
+
+    selectedServiceType.value = null
+    await loadProviderServiceTypes()
+  } catch (error) {
+    console.error('Error adding service type:', error)
+    $q.notify({
+      type: 'negative',
+      message: error.response?.data?.message || t('http.errors.failed')
+    })
+  } finally {
+    adding.value = false
+  }
+}
+
+const handleRemove = async (item) => {
+  $q.dialog({
+    title: t('common.messages.confirm'),
+    message: t('provider_services_types.remove_confirm'),
+    cancel: true,
+    persistent: true
+  }).onOk(async () => {
+    try {
+      await deleteProviderServiceType(props.providerId, item.id)
+
+      $q.notify({
+        type: 'positive',
+        message: t('common.messages.deleted_successfully')
+      })
+
+      await loadProviderServiceTypes()
+    } catch (error) {
+      console.error('Error removing service type:', error)
+      $q.notify({
+        type: 'negative',
+        message: error.response?.data?.message || t('common.messages.error_deleting')
+      })
+    }
+  })
+}
+
+watch(() => props.providerId, () => {
+  loadProviderServiceTypes()
+}, { immediate: true })
+
+onMounted(() => {
+  loadProviderServiceTypes()
+})
+
+defineExpose({
+  loadProviderServiceTypes
+})
+</script>