Browse Source

feat: :sparkles: crud tipos melhorias

crud tipos melhorias
Gustavo Zanatta 1 tháng trước cách đây
mục cha
commit
ef73353eed

+ 26 - 0
src/api/improvementType.js

@@ -0,0 +1,26 @@
+import api from "src/api";
+
+export const getImprovementType = async (id) => {
+  const { data } = await api.get("/improvement-type/" + id);
+  return data.payload;
+};
+
+export const getImprovementTypes = async () => {
+  const { data } = await api.get("/improvement-types");
+  return data.payload;
+};
+
+export const createImprovementType = async (improvementType) => {
+  const { data } = await api.post("/improvement-type", improvementType);
+  return data.payload;
+};
+
+export const updateImprovementType = async (improvementType, id) => {
+  const { data } = await api.put(`/improvement-type/${id}`, improvementType);
+  return data.payload;
+};
+
+export const deleteImprovementType = async (id) => {
+  const { data } = await api.delete(`/improvement-type/${id}`);
+  return data.payload;
+};

+ 136 - 0
src/components/improvementType/ImprovementTypeSelect.vue

@@ -0,0 +1,136 @@
+<template>
+  <q-select
+    v-model="selectedImprovementType"
+    v-bind="$attrs"
+    use-input
+    hide-selected
+    fill-input
+    clearable
+    :options="improvementTypeOptions"
+    :label
+    :rules
+    :loading
+    :placeholder="$t('common.actions.search') + ' ' + $t('ui.navigation.improvement_type')"
+    :error
+    :error-message
+    @filter="filterFn"
+  >
+    <template #no-option>
+      <q-item>
+        <q-item-section class="text-grey">
+          {{ $t("http.errors.no_records_found") }}
+        </q-item-section>
+      </q-item>
+    </template>
+  </q-select>
+</template>
+
+<script setup>
+import { getImprovementTypes } from "src/api/improvementType";
+import { ref, onMounted } from "vue";
+import { normalizeString } from "src/helpers/utils";
+import { useI18n } from "vue-i18n";
+
+const { label, rules, initialId, client, provider, both } = defineProps({
+  label: {
+    type: String,
+    default: () => useI18n().t("ui.navigation.improvement_type"),
+  },
+  rules: {
+    type: Array,
+    default: () => [],
+  },
+  initialId: {
+    type: Number,
+    required: false,
+    default: null,
+  },
+  error: {
+    type: Boolean,
+    default: false,
+  },
+  errorMessage: {
+    type: String,
+    default: "",
+  },
+  client: {
+    type: Boolean,
+    default: false,
+  },
+  provider: {
+    type: Boolean,
+    default: false,
+  },
+  both: {
+    type: Boolean,
+    default: false,
+  },
+});
+
+const selectedImprovementType = defineModel({ type: Object });
+
+const loading = ref(false);
+const baseOptions = ref([]);
+const improvementTypeOptions = ref([]);
+
+const filterFn = async (val, update) => {
+  const needle = normalizeString(val);
+  improvementTypeOptions.value = baseOptions.value.filter((v) => {
+    return normalizeString(v.label).includes(needle);
+  });
+  update();
+};
+
+const selectImprovementTypeByName = (name) => {
+  if (selectedImprovementType.value?.label === name) {
+    return;
+  }
+  selectedImprovementType.value = baseOptions.value.find((improvementType) => improvementType.label === name);
+};
+
+const selectImprovementTypeById = (id) => {
+  if (selectedImprovementType.value?.value === id) {
+    return;
+  }
+  selectedImprovementType.value = baseOptions.value.find((improvementType) => improvementType.value === id);
+};
+
+onMounted(async () => {
+  try {
+    loading.value = true;
+    const baseImprovementTypes = await getImprovementTypes();
+    
+    // Filtrar apenas ativos
+    let filteredTypes = baseImprovementTypes.filter((improvementType) => improvementType.is_active === true);
+    
+    // Aplicar filtros baseados nas props
+    if (client || provider || both) {
+      filteredTypes = filteredTypes.filter((improvementType) => {
+        if (client && improvementType.improvement_type === 'client') return true;
+        if (provider && improvementType.improvement_type === 'provider') return true;
+        if (both && improvementType.improvement_type === 'both') return true;
+        return false;
+      });
+    }
+    
+    baseOptions.value = filteredTypes.map((improvementType) => ({
+      label: improvementType.description,
+      value: improvementType.id,
+      improvement_type: improvementType.improvement_type,
+    }));
+    improvementTypeOptions.value = baseOptions.value;
+    if (initialId) {
+      selectImprovementTypeById(initialId);
+    }
+  } catch (e) {
+    console.log(e);
+  } finally {
+    loading.value = false;
+  }
+});
+
+defineExpose({
+  selectImprovementTypeByName,
+  selectImprovementTypeById,
+});
+</script>

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

@@ -281,6 +281,21 @@
       "description": "Description"
     }
   },
+  "improvement_type": {
+    "singular": "Improvement Type",
+    "plural": "Improvement Types",
+    "add": "Add Improvement Type",
+    "edit": "Edit Improvement Type",
+    "fields": {
+      "description": "Description",
+      "improvement_type": "Type"
+    },
+    "types": {
+      "client": "Client",
+      "provider": "Provider",
+      "both": "Both"
+    }
+  },
   "provider": {
     "singular": "Provider",
     "plural": "Providers",
@@ -363,6 +378,7 @@
       "exit": "Exit",
       "admin": "Admin",
       "user": "User",
+      "improvement_type": "Improvement Type",
       "service_type": "Service Type",
       "speciality": "Speciality"
     }

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

@@ -281,6 +281,21 @@
       "description": "Descripción"
     }
   },
+  "improvement_type": {
+    "singular": "Tipo de Mejora",
+    "plural": "Tipos de Mejoras",
+    "add": "Agregar Tipo de Mejora",
+    "edit": "Editar Tipo de Mejora",
+    "fields": {
+      "description": "Descripción",
+      "improvement_type": "Tipo"
+    },
+    "types": {
+      "client": "Cliente",
+      "provider": "Proveedor",
+      "both": "Ambos"
+    }
+  },
   "provider": {
     "singular": "Proveedor",
     "plural": "Proveedores",
@@ -363,6 +378,7 @@
       "exit": "Salir",
       "admin": "Admin",
       "user": "Usuario",
+      "improvement_type": "Tipo de Mejora",
       "service_type": "Tipo de Servicio",
       "speciality": "Especialidad"
     }

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

@@ -281,6 +281,21 @@
       "description": "Descrição"
     }
   },
+  "improvement_type": {
+    "singular": "Tipo de Melhoria",
+    "plural": "Tipos de Melhorias",
+    "add": "Adicionar Tipo de Melhoria",
+    "edit": "Editar Tipo de Melhoria",
+    "fields": {
+      "description": "Descrição",
+      "improvement_type": "Tipo"
+    },
+    "types": {
+      "client": "Cliente",
+      "provider": "Prestador",
+      "both": "Ambos"
+    }
+  },
   "provider": {
     "singular": "Prestador",
     "plural": "Prestadores",
@@ -363,6 +378,7 @@
       "exit": "Sair",
       "admin": "Admin",
       "user": "Usuário",
+      "improvement_type": "Tipo de Melhoria",
       "service_type": "Tipo de Serviço",
       "speciality": "Especialidade"
     }

+ 128 - 0
src/pages/improvementType/ImprovementTypesPage.vue

@@ -0,0 +1,128 @@
+<template>
+  <div>
+    <DefaultHeaderPage />
+    <div>
+      <DefaultTable
+        ref="tableRef"
+        :columns="columns"
+        :api-call="getImprovementTypes"
+        :delete-function="deleteImprovementType"
+        :mostrar-selecao-de-colunas="false"
+        :mostrar-botao-fullscreen="false"
+        :mostrar-toggle-inativos="false"
+        open-item
+        add-item
+        @on-row-click="onRowClick"
+        @on-add-item="onAddItem"
+      />
+    </div>
+  </div>
+</template>
+<script setup>
+import { defineAsyncComponent, useTemplateRef, computed } from "vue";
+import { useQuasar } from "quasar";
+import { useI18n } from "vue-i18n";
+import { permissionStore } from "src/stores/permission";
+import { getImprovementTypes, deleteImprovementType } from "src/api/improvementType";
+
+import DefaultTable from "src/components/defaults/DefaultTable.vue";
+import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
+
+const AddEditImprovementTypeDialog = defineAsyncComponent(() =>
+  import("src/pages/improvementType/components/AddEditImprovementTypeDialog.vue")
+);
+
+const permission_store = permissionStore();
+const $q = useQuasar();
+const tableRef = useTemplateRef("tableRef");
+const { t } = useI18n();
+
+const getImprovementTypeLabel = (type) => {
+  const types = {
+    client: t("improvement_type.types.client"),
+    provider: t("improvement_type.types.provider"),
+    both: t("improvement_type.types.both"),
+  };
+  return types[type] || type;
+};
+
+const columns = computed(() => [
+  {
+    name: "description",
+    label: t("improvement_type.fields.description"),
+    field: "description",
+    align: "left",
+    sortable: true,
+  },
+  {
+    name: "improvement_type",
+    label: t("improvement_type.fields.improvement_type"),
+    field: (row) => getImprovementTypeLabel(row.improvement_type),
+    align: "left",
+    sortable: true,
+  },
+  {
+    name: "is_active",
+    label: t("common.status.active"),
+    field: (row) => (row.is_active ? t("common.status.yes") : t("common.status.no")),
+    align: "left",
+    sortable: true,
+  },
+  {
+    name: "created_at",
+    label: t("common.terms.created_at"),
+    field: "created_at",
+    align: "left",
+    sortable: true,
+  },
+  {
+    name: "actions",
+    required: true,
+  },
+]);
+
+const onRowClick = ({ row }) => {
+  if (permission_store.getAccess("config.improvement_type", "edit") === false) {
+    $q.loading.hide();
+    $q.notify({
+      type: "negative",
+      message: t("validation.permissions.edit"),
+    });
+    return;
+  }
+  $q.dialog({
+    component: AddEditImprovementTypeDialog,
+    componentProps: {
+      improvementType: row,
+      title: () =>
+        useI18n().t("common.actions.edit") + " " + useI18n().t("ui.navigation.improvement_type"),
+    },
+  }).onOk(async (success) => {
+    if (success) {
+      tableRef.value.refresh();
+    }
+  });
+};
+
+const onAddItem = () => {
+  if (permission_store.getAccess("config.improvement_type", "add") === false) {
+    $q.loading.hide();
+    $q.notify({
+      type: "negative",
+      message: t("validation.permissions.add"),
+    });
+    return;
+  }
+  $q.dialog({
+    component: AddEditImprovementTypeDialog,
+    componentProps: {
+      title: () =>
+        useI18n().t("common.actions.add") + " " + useI18n().t("ui.navigation.improvement_type"),
+    },
+  }).onOk(async (success) => {
+    if (success) {
+      tableRef.value.refresh();
+    }
+  });
+};
+</script>

+ 109 - 0
src/pages/improvementType/components/AddEditImprovementTypeDialog.vue

@@ -0,0 +1,109 @@
+<template>
+  <q-dialog ref="dialogRef" @hide="onDialogHide">
+    <q-card class="q-dialog-plugin overflow-hidden" style="width: 1000px; max-width: 90vw">
+      <DefaultDialogHeader :title="title" @close="onDialogCancel" />
+        <q-form ref="formRef" @submit="onOKClick">
+          <q-card-section class="row q-col-gutter-sm">
+            <q-input
+              v-model="form.description"
+              fill-mask
+              unmasked-value
+              :label="$t('improvement_type.fields.description')"
+              :rules="[inputRules.required]"
+              :error="!!serverErrors?.description"
+              :error-message="serverErrors?.description"
+              class="col-12"
+            />
+
+            <q-select
+              v-model="form.improvement_type"
+              :options="improvementTypeOptions"
+              :label="$t('improvement_type.fields.improvement_type')"
+              :rules="[inputRules.required]"
+              :error="!!serverErrors?.improvement_type"
+              :error-message="serverErrors?.improvement_type"
+              emit-value
+              map-options
+              class="col-12"
+            />
+
+            <div class="col-12">
+              <q-checkbox
+                v-model="form.is_active"
+                :label="$t('common.status.active')"
+              />
+            </div>
+
+          </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" />
+          </q-card-actions>
+        </q-form>
+    </q-card>
+  </q-dialog>
+</template>
+<script setup>
+import { useTemplateRef, computed } from "vue";
+import { useInputRules } from "src/composables/useInputRules";
+import { useDialogPluginComponent } from "quasar";
+import { useI18n } from "vue-i18n";
+import { createImprovementType, updateImprovementType } from "src/api/improvementType";
+import { useFormUpdateTracker } from "src/composables/useFormUpdateTracker";
+import { useSubmitHandler } from "src/composables/useSubmitHandler";
+import DefaultDialogHeader from "src/components/defaults/DefaultDialogHeader.vue";
+
+defineEmits([
+  ...useDialogPluginComponent.emits,
+]);
+
+const { improvementType, title } = defineProps({
+  improvementType: {
+    type: Object,
+    default: null,
+  },
+  title: {
+    type: Function,
+    default: () => useI18n().t("common.terms.title"),
+  },
+});
+
+const { t } = useI18n();
+const { inputRules } = useInputRules();
+
+const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
+  useDialogPluginComponent();
+
+const formRef = useTemplateRef("formRef");
+const { form, getUpdatedFields, hasUpdatedFields } = useFormUpdateTracker({
+  description: improvementType ? improvementType?.description : "",
+  improvement_type: improvementType ? improvementType?.improvement_type : "both",
+  is_active: improvementType ? improvementType?.is_active : true,
+});
+
+const improvementTypeOptions = computed(() => [
+  { label: t("improvement_type.types.client"), value: "client" },
+  { label: t("improvement_type.types.provider"), value: "provider" },
+  { label: t("improvement_type.types.both"), value: "both" },
+]);
+
+const {
+  loading,
+  serverErrors,
+  execute: submitForm,
+} = useSubmitHandler({
+  onSuccess: () => onDialogOK(true),
+  formRef: formRef,
+});
+
+
+const onOKClick = async () => {
+  if (improvementType) {
+    await submitForm(() => updateImprovementType(getUpdatedFields.value, improvementType.id));
+  } else {
+    await submitForm(() => createImprovementType({ ...form }));
+  }
+};
+
+</script>

+ 22 - 0
src/router/routes/improvementType.route.js

@@ -0,0 +1,22 @@
+export default [
+  {
+    path: "/improvement-type",
+    name: "ImprovementTypesPage",
+    component: () => import("pages/improvementType/ImprovementTypesPage.vue"),
+    meta: {
+      title: "ui.navigation.improvement_type",
+      requireAuth: true,
+      requiredPermission: "config.improvement_type",
+      breadcrumbs: [
+        {
+          name: "DashboardPage",
+          title: "ui.navigation.dashboard",
+        },
+        {
+          name: "ImprovementTypePage",
+          title: "ui.navigation.improvement_type",
+        },
+      ],
+    },
+  },
+];

+ 9 - 0
src/stores/navigation.js

@@ -66,6 +66,15 @@ export const navigationStore = defineStore("navigation", () => {
           permission: false,
           permissionScope: "config.provider",
         },
+        {
+          type: "single",
+          title: "ui.navigation.improvement_type",
+          name: "ImprovementTypesPage",
+          icon: "mdi-chart-line-variant",
+          disable: false,
+          permission: false,
+          permissionScope: "config.improvement_type",
+        },
         {
           type: "single",
           title: "ui.navigation.service_type",