Просмотр исходного кода

feat(permissions): menu e tela de Grupos de Permissões

Nova tela em /permissions com DefaultHeaderPage 'Grupos de Permissões' e
tabela de grupos (listar, adicionar e remover), consumindo a API user-type.
Item 'Permissões' adicionado ao menu lateral da franqueadora.

Parte da task 02 (front primeiro).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ebagabee 2 недель назад
Родитель
Сommit
d6376b680e

+ 91 - 0
src/pages/permissions/PermissionsPage.vue

@@ -0,0 +1,91 @@
+<template>
+  <div>
+    <DefaultHeaderPage title="Grupos de Permissões" :show-filter-icon="false" />
+
+    <div class="q-px-sm">
+      <DefaultTable
+        ref="tableRef"
+        title="Lista de Grupos de Permissões"
+        :columns="columns"
+        :api-call="getUserTypes"
+        description="grupos"
+        :female="false"
+        add-item
+        add-item-label="Novo Grupo"
+        @on-add-item="onAdd"
+      >
+        <template #body-cell-actions="{ row }">
+          <q-td auto-width>
+            <q-btn
+              v-if="!row.is_system"
+              outline
+              icon="mdi-trash-can-outline"
+              style="width: 36px"
+              color="negative"
+              @click.prevent.stop="onDelete(row)"
+            >
+              <q-tooltip>Remover grupo</q-tooltip>
+            </q-btn>
+          </q-td>
+        </template>
+      </DefaultTable>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { defineAsyncComponent, useTemplateRef } from "vue";
+import { useQuasar } from "quasar";
+import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
+import DefaultTable from "src/components/defaults/DefaultTable.vue";
+import { getUserTypes, deleteUserType } from "src/api/user_type";
+
+const AddPermissionGroupDialog = defineAsyncComponent(
+  () => import("src/pages/permissions/components/AddPermissionGroupDialog.vue"),
+);
+
+const $q = useQuasar();
+const tableRef = useTemplateRef("tableRef");
+
+const columns = [
+  {
+    name: "label",
+    label: "Nome",
+    field: "label",
+    align: "left",
+    sortable: true,
+  },
+  {
+    name: "slug",
+    label: "Código",
+    field: "slug",
+    align: "left",
+    sortable: true,
+  },
+  {
+    name: "actions",
+    label: "Ações",
+    field: null,
+    align: "center",
+    required: true,
+  },
+];
+
+const onAdd = () => {
+  $q.dialog({ component: AddPermissionGroupDialog }).onOk(() => {
+    tableRef.value?.refresh();
+  });
+};
+
+const onDelete = (row) => {
+  $q.dialog({
+    title: "Confirmar exclusão",
+    message: `Remover o grupo "${row.label}"?`,
+    cancel: true,
+    persistent: true,
+  }).onOk(async () => {
+    await deleteUserType(row.id);
+    tableRef.value?.refresh();
+  });
+};
+</script>

+ 66 - 0
src/pages/permissions/components/AddPermissionGroupDialog.vue

@@ -0,0 +1,66 @@
+<template>
+  <q-dialog ref="dialogRef" @hide="onDialogHide">
+    <q-card style="min-width: 400px">
+      <DefaultDialogHeader
+        title="Novo Grupo de Permissões"
+        @close="onDialogCancel"
+      />
+
+      <q-form ref="formRef" @submit="onSave">
+        <q-card-section>
+          <DefaultInput
+            v-model="form.label"
+            label="Nome"
+            outlined
+            autofocus
+            :rules="[inputRules.required]"
+          />
+        </q-card-section>
+
+        <q-card-actions align="right">
+          <q-btn
+            label="Cancelar"
+            color="primary"
+            outline
+            @click="onDialogCancel"
+          />
+          <q-btn
+            label="Salvar"
+            color="primary"
+            type="submit"
+            :loading="loading"
+          />
+        </q-card-actions>
+      </q-form>
+    </q-card>
+  </q-dialog>
+</template>
+
+<script setup>
+import { ref, useTemplateRef } from "vue";
+import { useDialogPluginComponent } from "quasar";
+import { useInputRules } from "src/composables/useInputRules";
+import { useSubmitHandler } from "src/composables/useSubmitHandler";
+import { createUserType } from "src/api/user_type";
+import DefaultDialogHeader from "src/components/defaults/DefaultDialogHeader.vue";
+import DefaultInput from "src/components/defaults/DefaultInput.vue";
+
+defineEmits([...useDialogPluginComponent.emits]);
+
+const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
+  useDialogPluginComponent();
+
+const { inputRules } = useInputRules();
+const formRef = useTemplateRef("formRef");
+
+const form = ref({ label: "" });
+
+const { loading, execute } = useSubmitHandler({
+  formRef,
+  onSuccess: () => onDialogOK(),
+});
+
+const onSave = async () => {
+  await execute(() => createUserType({ label: form.value.label }));
+};
+</script>

+ 25 - 0
src/router/routes/permission.route.js

@@ -0,0 +1,25 @@
+export default [
+  {
+    path: "/permissions",
+    name: "PermissionsPage",
+    component: () => import("pages/permissions/PermissionsPage.vue"),
+    meta: {
+      title: {
+        value: "Permissões",
+        translate: false,
+      },
+      requireAuth: true,
+      requiredPermission: "config.user-type",
+      breadcrumbs: [
+        {
+          name: "DashboardPage",
+          title: "Dashboard",
+        },
+        {
+          name: "PermissionsPage",
+          title: "Permissões",
+        },
+      ],
+    },
+  },
+];

+ 9 - 0
src/stores/navigation.js

@@ -123,6 +123,15 @@ export const navigationStore = defineStore("navigation", () => {
       permission: false,
       permissionScope: "dashboard",
     },
+    {
+      type: "single",
+      title: "Permissões",
+      name: "PermissionsPage",
+      icon: "mdi-shield-key-outline",
+      disable: false,
+      permission: false,
+      permissionScope: "config.user-type",
+    },
   ]);
 
   const getNavigationAccess = () => {