|
|
@@ -8,38 +8,21 @@
|
|
|
:columns="columns"
|
|
|
:api-call="getEditableUserTypes"
|
|
|
description="grupos"
|
|
|
+ :female="false"
|
|
|
:add-item="canAdd"
|
|
|
add-item-label="Novo Grupo"
|
|
|
- @on-add-item="openGroup()"
|
|
|
+ @on-add-item="onAdd"
|
|
|
>
|
|
|
<template #body-cell-actions="{ row }">
|
|
|
<q-td>
|
|
|
- <div class="row no-wrap justify-center q-gutter-sm">
|
|
|
- <q-btn
|
|
|
- v-if="canEdit"
|
|
|
- outline
|
|
|
- icon="mdi-shield-key-outline"
|
|
|
- color="primary"
|
|
|
- @click="openPermissions(row)"
|
|
|
- >
|
|
|
+ <div class="row no-wrap justify-center" style="gap: 8px">
|
|
|
+ <q-btn v-if="canEdit" outline icon="mdi-shield-key-outline" style="width: 36px" color="secondary" @click.stop="onEditPermissions(row)">
|
|
|
<q-tooltip>Permissões</q-tooltip>
|
|
|
</q-btn>
|
|
|
- <q-btn
|
|
|
- v-if="canEdit"
|
|
|
- outline
|
|
|
- icon="mdi-pencil-outline"
|
|
|
- color="primary"
|
|
|
- @click="openGroup(row)"
|
|
|
- >
|
|
|
+ <q-btn v-if="canEdit && !row.is_system" outline icon="mdi-pencil-outline" style="width: 36px" color="secondary" @click.stop="onEdit(row)">
|
|
|
<q-tooltip>Editar grupo</q-tooltip>
|
|
|
</q-btn>
|
|
|
- <q-btn
|
|
|
- v-if="canDelete"
|
|
|
- outline
|
|
|
- icon="mdi-trash-can-outline"
|
|
|
- color="negative"
|
|
|
- @click="remove(row)"
|
|
|
- >
|
|
|
+ <q-btn v-if="canDelete && !row.is_system" outline icon="mdi-trash-can-outline" style="width: 36px" color="negative" @click.stop="onDelete(row)">
|
|
|
<q-tooltip>Remover grupo</q-tooltip>
|
|
|
</q-btn>
|
|
|
</div>
|
|
|
@@ -47,219 +30,40 @@
|
|
|
</template>
|
|
|
</DefaultTable>
|
|
|
</div>
|
|
|
-
|
|
|
- <q-dialog v-model="groupDialog">
|
|
|
- <q-card style="min-width: 440px">
|
|
|
- <DefaultDialogHeader
|
|
|
- :title="editingGroup ? 'Editar Grupo' : 'Novo Grupo'"
|
|
|
- @close="groupDialog = false"
|
|
|
- />
|
|
|
- <q-form @submit="saveGroup">
|
|
|
- <q-card-section class="column q-gutter-md">
|
|
|
- <DefaultInput
|
|
|
- v-model="form.label"
|
|
|
- label="Nome"
|
|
|
- outlined
|
|
|
- :rules="[(value) => !!value || 'Campo obrigatório']"
|
|
|
- />
|
|
|
- <DefaultInput
|
|
|
- v-model="form.slug"
|
|
|
- label="Código"
|
|
|
- outlined
|
|
|
- hint="Ex.: ATENDIMENTO"
|
|
|
- :rules="[
|
|
|
- (value) =>
|
|
|
- /^[A-Z0-9_]+$/.test(value) ||
|
|
|
- 'Use letras maiúsculas, números e underline',
|
|
|
- ]"
|
|
|
- />
|
|
|
- </q-card-section>
|
|
|
- <q-card-actions align="right">
|
|
|
- <q-btn v-close-popup label="Cancelar" outline color="primary" />
|
|
|
- <q-btn
|
|
|
- label="Salvar"
|
|
|
- type="submit"
|
|
|
- color="primary"
|
|
|
- :loading="saving"
|
|
|
- />
|
|
|
- </q-card-actions>
|
|
|
- </q-form>
|
|
|
- </q-card>
|
|
|
- </q-dialog>
|
|
|
-
|
|
|
- <q-dialog v-model="permissionsDialog" full-height>
|
|
|
- <q-card class="column no-wrap" style="width: 760px; max-width: 95vw">
|
|
|
- <DefaultDialogHeader
|
|
|
- :title="`Permissões: ${editingGroup?.label ?? ''}`"
|
|
|
- @close="permissionsDialog = false"
|
|
|
- />
|
|
|
- <q-card-section class="col scroll q-pa-none">
|
|
|
- <q-inner-loading :showing="loadingPermissions" />
|
|
|
- <q-list separator>
|
|
|
- <q-item v-for="menu in visibleMenus" :key="menu.scope">
|
|
|
- <q-item-section
|
|
|
- ><q-item-label>{{ menu.label }}</q-item-label></q-item-section
|
|
|
- >
|
|
|
- <q-item-section side>
|
|
|
- <div class="row q-gutter-sm">
|
|
|
- <q-checkbox
|
|
|
- v-for="level in levelsFor(menu.scope)"
|
|
|
- :key="level.key"
|
|
|
- :label="level.label"
|
|
|
- :model-value="hasBit(menu.scope, level.bit)"
|
|
|
- @update:model-value="toggleBit(menu.scope, level.bit)"
|
|
|
- />
|
|
|
- </div>
|
|
|
- </q-item-section>
|
|
|
- </q-item>
|
|
|
- </q-list>
|
|
|
- </q-card-section>
|
|
|
- <q-card-actions align="right" class="q-pa-md">
|
|
|
- <q-btn v-close-popup label="Cancelar" outline color="primary" />
|
|
|
- <q-btn
|
|
|
- label="Salvar"
|
|
|
- color="primary"
|
|
|
- :loading="saving"
|
|
|
- @click="savePermissions"
|
|
|
- />
|
|
|
- </q-card-actions>
|
|
|
- </q-card>
|
|
|
- </q-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { computed, ref, useTemplateRef } from "vue";
|
|
|
+import { computed, 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 DefaultDialogHeader from "src/components/defaults/DefaultDialogHeader.vue";
|
|
|
-import DefaultInput from "src/components/defaults/DefaultInput.vue";
|
|
|
+import { getUserTypes, deleteUserType } from "src/api/user_type";
|
|
|
import { permissionStore } from "src/stores/permission";
|
|
|
-import { MENUS, LEVELS, normalizeBits } from "./permission_menus";
|
|
|
-import {
|
|
|
- getUserTypes,
|
|
|
- createUserType,
|
|
|
- updateUserType,
|
|
|
- deleteUserType,
|
|
|
- getUserTypePermissions,
|
|
|
- updateUserTypePermissions,
|
|
|
-} from "src/api/user_type";
|
|
|
|
|
|
+const AddEditPermissionGroupDialog = defineAsyncComponent(() => import("src/pages/permissions/components/AddEditPermissionGroupDialog.vue"));
|
|
|
+const PermissionGroupDialog = defineAsyncComponent(() => import("src/pages/permissions/components/PermissionGroupDialog.vue"));
|
|
|
const $q = useQuasar();
|
|
|
-const access = permissionStore();
|
|
|
const tableRef = useTemplateRef("tableRef");
|
|
|
-const canAdd = computed(() =>
|
|
|
- access.getAccess("franchisee_permissions", "add"),
|
|
|
-);
|
|
|
-const canEdit = computed(() =>
|
|
|
- access.getAccess("franchisee_permissions", "edit"),
|
|
|
-);
|
|
|
-const canDelete = computed(() =>
|
|
|
- access.getAccess("franchisee_permissions", "delete"),
|
|
|
-);
|
|
|
+const access = permissionStore();
|
|
|
+const canAdd = computed(() => access.getAccess("franchisee_permissions", "add"));
|
|
|
+const canEdit = computed(() => access.getAccess("franchisee_permissions", "edit"));
|
|
|
+const canDelete = computed(() => access.getAccess("franchisee_permissions", "delete"));
|
|
|
+const getEditableUserTypes = async () => (await getUserTypes()).filter((group) => !group.is_system);
|
|
|
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", align: "center", required: true },
|
|
|
+ { 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 groupDialog = ref(false);
|
|
|
-const permissionsDialog = ref(false);
|
|
|
-const editingGroup = ref(null);
|
|
|
-const saving = ref(false);
|
|
|
-const loadingPermissions = ref(false);
|
|
|
-const form = ref({ label: "", slug: "" });
|
|
|
-const permissions = ref({});
|
|
|
-const getEditableUserTypes = async () =>
|
|
|
- (await getUserTypes()).filter((group) => !group.is_system);
|
|
|
-
|
|
|
-const openGroup = (group = null) => {
|
|
|
- editingGroup.value = group;
|
|
|
- form.value = { label: group?.label ?? "", slug: group?.slug ?? "" };
|
|
|
- groupDialog.value = true;
|
|
|
-};
|
|
|
-const saveGroup = async () => {
|
|
|
- saving.value = true;
|
|
|
- try {
|
|
|
- editingGroup.value
|
|
|
- ? await updateUserType(editingGroup.value.id, form.value)
|
|
|
- : await createUserType(form.value);
|
|
|
- groupDialog.value = false;
|
|
|
- tableRef.value?.refresh();
|
|
|
- } finally {
|
|
|
- saving.value = false;
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-const remove = (group) =>
|
|
|
- $q
|
|
|
- .dialog({
|
|
|
- title: "Confirmar exclusão",
|
|
|
- message: `Remover o grupo “${group.label}”?`,
|
|
|
- cancel: true,
|
|
|
- })
|
|
|
- .onOk(async () => {
|
|
|
- await deleteUserType(group.id);
|
|
|
- tableRef.value?.refresh();
|
|
|
- });
|
|
|
-
|
|
|
-const visibleMenus = computed(() =>
|
|
|
- MENUS.filter((menu) => permissions.value[menu.scope]),
|
|
|
-);
|
|
|
-
|
|
|
-const levelsFor = (scope) =>
|
|
|
- LEVELS.filter((level) => permissions.value[scope].available_bits & level.bit);
|
|
|
-
|
|
|
-const hasBit = (scope, bit) => !!(permissions.value[scope]?.bits & bit);
|
|
|
-
|
|
|
-const toggleBit = (scope, bit) => {
|
|
|
- permissions.value[scope].bits = normalizeBits(
|
|
|
- permissions.value[scope].bits ^ bit,
|
|
|
- );
|
|
|
-};
|
|
|
-
|
|
|
-const openPermissions = async (group) => {
|
|
|
- editingGroup.value = group;
|
|
|
- permissionsDialog.value = true;
|
|
|
- loadingPermissions.value = true;
|
|
|
- try {
|
|
|
- const payload = await getUserTypePermissions(group.id);
|
|
|
- permissions.value = Object.fromEntries(
|
|
|
- payload.permissions.map((item) => [
|
|
|
- item.scope,
|
|
|
- { ...item, bits: normalizeBits(item.bits) },
|
|
|
- ]),
|
|
|
- );
|
|
|
- } finally {
|
|
|
- loadingPermissions.value = false;
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-const savePermissions = async () => {
|
|
|
- saving.value = true;
|
|
|
- try {
|
|
|
- await updateUserTypePermissions(
|
|
|
- editingGroup.value.id,
|
|
|
- Object.values(permissions.value).map((item) => ({
|
|
|
- permission_id: item.id,
|
|
|
- bits: item.bits,
|
|
|
- })),
|
|
|
- );
|
|
|
- permissionsDialog.value = false;
|
|
|
- } finally {
|
|
|
- saving.value = false;
|
|
|
- }
|
|
|
-};
|
|
|
+const refresh = () => tableRef.value?.refresh();
|
|
|
+const onAdd = () => $q.dialog({ component: AddEditPermissionGroupDialog }).onOk(refresh);
|
|
|
+const onEdit = (group) => $q.dialog({ component: AddEditPermissionGroupDialog, componentProps: { group } }).onOk(refresh);
|
|
|
+const onEditPermissions = (group) => $q.dialog({ component: PermissionGroupDialog, componentProps: { group } });
|
|
|
+const onDelete = (group) => $q.dialog({
|
|
|
+ title: "Confirmar exclusão",
|
|
|
+ message: `Remover o grupo "${group.label}"?`,
|
|
|
+ cancel: { label: "Cancelar", outline: true, color: "secondary" },
|
|
|
+ ok: { label: "Remover", color: "negative" },
|
|
|
+ persistent: true,
|
|
|
+}).onOk(async () => { await deleteUserType(group.id); refresh(); });
|
|
|
</script>
|