Prechádzať zdrojové kódy

feat(permissions): Menu passa a ser derivado de Visualizar

Menu deixa de ser editável à mão: marcar Visualizar liga o menu e desmarcar
desliga, evitando item de menu que leva a 403 (e o inverso). O checkbox
continua visível, porém travado e com tooltip explicando.

applyComputedLevels também normaliza o que vem da API, corrigindo dados
gravados antes da regra.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ebagabee 2 týždňov pred
rodič
commit
cf761cc6ea

+ 6 - 2
src/pages/permissions/components/PermissionGroupDialog.vue

@@ -162,7 +162,10 @@ import {
   getUserTypePermissions,
   updateUserTypePermissions,
 } from "src/api/user_type";
-import { FRANCHISOR_PERMISSION_MENUS } from "src/pages/permissions/permission_menus";
+import {
+  FRANCHISOR_PERMISSION_MENUS,
+  applyComputedLevels,
+} from "src/pages/permissions/permission_menus";
 
 const { group } = defineProps({
   group: {
@@ -228,7 +231,8 @@ const fetchPermissions = async () => {
       if (!permission) continue;
 
       permissionIdByScope.value[scope] = permission.id;
-      bitsByScope.value[scope] = permission.bits;
+      // Normaliza dados anteriores à regra (ex.: Visualizar gravado sem Menu).
+      bitsByScope.value[scope] = applyComputedLevels(permission.bits);
     }
   } catch {
     Notify.create({

+ 17 - 6
src/pages/permissions/components/PermissionLevelMenu.vue

@@ -1,18 +1,19 @@
 <template>
   <q-menu anchor="bottom right" self="top right">
-    <q-list style="min-width: 180px">
+    <q-list style="min-width: 200px">
       <q-item-label header class="q-pb-xs">Permissões</q-item-label>
 
       <q-item
         v-for="level in PERMISSION_LEVELS"
         :key="level.key"
-        v-ripple
-        clickable
+        v-ripple="!level.computed"
+        :clickable="!level.computed"
         @click="toggle(level.bit)"
       >
         <q-item-section side>
           <q-checkbox
             :model-value="has(level.bit)"
+            :disable="level.computed"
             dense
             @update:model-value="toggle(level.bit)"
             @click.stop
@@ -20,15 +21,22 @@
         </q-item-section>
 
         <q-item-section>
-          <q-item-label>{{ level.label }}</q-item-label>
+          <q-item-label :class="{ 'text-foreground': level.computed }">
+            {{ level.label }}
+          </q-item-label>
         </q-item-section>
+
+        <q-tooltip v-if="level.computed">{{ level.computedHint }}</q-tooltip>
       </q-item>
     </q-list>
   </q-menu>
 </template>
 
 <script setup>
-import { PERMISSION_LEVELS } from "src/pages/permissions/permission_menus";
+import {
+  PERMISSION_LEVELS,
+  applyComputedLevels,
+} from "src/pages/permissions/permission_menus";
 
 const { bits } = defineProps({
   bits: {
@@ -42,6 +50,9 @@ const emit = defineEmits(["update:bits"]);
 const has = (bit) => (bits & bit) !== 0;
 
 const toggle = (bit) => {
-  emit("update:bits", has(bit) ? bits & ~bit : bits | bit);
+  const level = PERMISSION_LEVELS.find((item) => item.bit === bit);
+  if (level?.computed) return;
+
+  emit("update:bits", applyComputedLevels(has(bit) ? bits & ~bit : bits | bit));
 };
 </script>

+ 28 - 5
src/pages/permissions/permission_menus.js

@@ -1,14 +1,37 @@
+/** Bits de permissão, espelhando App\Models\Permission na API. */
+export const PERMISSION_BITS = Object.freeze({
+  menu: 256,
+  view: 1,
+  add: 2,
+  edit: 4,
+});
+
 /**
  * Níveis de permissão expostos na tela, na ordem em que aparecem.
- * Os bits espelham App\Models\Permission na API.
+ * `computed` marca o nível que não é editável à mão — ver applyComputedLevels.
  */
 export const PERMISSION_LEVELS = Object.freeze([
-  { key: "menu", label: "Menu", bit: 256 },
-  { key: "view", label: "Visualizar", bit: 1 },
-  { key: "add", label: "Cadastrar", bit: 2 },
-  { key: "edit", label: "Editar", bit: 4 },
+  {
+    key: "menu",
+    label: "Menu",
+    bit: PERMISSION_BITS.menu,
+    computed: true,
+    computedHint: "Ativado automaticamente por Visualizar.",
+  },
+  { key: "view", label: "Visualizar", bit: PERMISSION_BITS.view },
+  { key: "add", label: "Cadastrar", bit: PERMISSION_BITS.add },
+  { key: "edit", label: "Editar", bit: PERMISSION_BITS.edit },
 ]);
 
+/**
+ * Menu é derivado de Visualizar: quem enxerga a tela enxerga o menu, e quem
+ * não enxerga não deve ter o item no menu levando a um 403.
+ */
+export const applyComputedLevels = (bits) =>
+  bits & PERMISSION_BITS.view
+    ? bits | PERMISSION_BITS.menu
+    : bits & ~PERMISSION_BITS.menu;
+
 /**
  * Menus da Franqueadora e o scope de permissão que governa cada fluxo.
  * A granularidade é o menu: quem tem o menu tem o fluxo inteiro por trás dele.