| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- <template>
- <q-dialog ref="dialogRef" full-height @hide="onDialogHide">
- <q-card
- class="column no-wrap"
- style="width: 100%; max-width: 95vw; height: 100%"
- >
- <DefaultDialogHeader
- :title="`Permissões: ${group.label}`"
- @close="onDialogCancel"
- />
- <q-card-section class="col column no-wrap q-pa-none" style="min-height: 0">
- <div class="row items-stretch col no-wrap" style="min-height: 0">
- <!-- Menu -->
- <div class="col-12 col-md-6 column no-wrap" style="min-height: 0">
- <div class="text-center text-subtitle1 text-weight-medium q-pa-sm">
- Menu
- </div>
- <q-separator />
- <q-inner-loading :showing="loading" />
- <q-list v-if="!loading" separator class="col scroll">
- <q-item
- v-for="item in menus"
- :key="item.scope"
- clickable
- :active="selectedScope === item.scope"
- active-class="bg-blue-1 text-primary"
- @click="selectedScope = item.scope"
- >
- <q-item-section avatar>
- <q-icon :name="item.icon" color="dark" />
- </q-item-section>
- <q-item-section>
- <q-item-label>
- {{ item.label }}
- <q-icon
- v-if="item.children?.length"
- name="mdi-chevron-right"
- size="16px"
- class="q-ml-xs"
- />
- </q-item-label>
- <q-item-label caption>
- <PermissionLevelBadges :bits="bitsFor(item.scope)" />
- </q-item-label>
- </q-item-section>
- <q-item-section side>
- <q-btn
- flat
- round
- dense
- icon="mdi-pencil-outline"
- color="dark"
- @click.stop
- >
- <q-tooltip>Editar permissões</q-tooltip>
- <PermissionLevelMenu
- :bits="bitsFor(item.scope)"
- @update:bits="(value) => setBits(item.scope, value)"
- />
- </q-btn>
- </q-item-section>
- </q-item>
- </q-list>
- </div>
- <q-separator vertical />
- <!-- SubCategorias -->
- <div class="col column no-wrap" style="min-height: 0">
- <div class="text-center text-subtitle1 text-weight-medium q-pa-sm">
- SubCategorias
- </div>
- <q-separator />
- <div
- v-if="!selected"
- class="col column flex-center text-center text-foreground q-pa-md"
- >
- <div>Nenhum menu selecionado!</div>
- <div class="text-caption">
- ← Selecione um menu à esquerda para carregar as subcategorias.
- </div>
- </div>
- <div
- v-else-if="!children.length"
- class="col column flex-center text-center text-foreground q-pa-md"
- >
- <div>Nenhuma subcategoria</div>
- <div class="text-caption">
- O menu "{{ selected.label }}" não possui subcategorias.
- </div>
- </div>
- <q-list v-else separator class="col scroll">
- <q-item v-for="child in children" :key="child.scope">
- <q-item-section avatar>
- <q-icon :name="child.icon" color="dark" />
- </q-item-section>
- <q-item-section>
- <q-item-label>{{ child.label }}</q-item-label>
- <q-item-label caption>
- <PermissionLevelBadges :bits="bitsFor(child.scope)" />
- </q-item-label>
- </q-item-section>
- <q-item-section side>
- <q-btn
- flat
- round
- dense
- icon="mdi-pencil-outline"
- color="dark"
- >
- <q-tooltip>Editar permissões</q-tooltip>
- <PermissionLevelMenu
- :bits="bitsFor(child.scope)"
- @update:bits="(value) => setBits(child.scope, value)"
- />
- </q-btn>
- </q-item-section>
- </q-item>
- </q-list>
- </div>
- </div>
- </q-card-section>
- <q-separator />
- <q-card-actions align="right" class="col-auto q-pa-md">
- <q-btn
- label="Cancelar"
- color="primary"
- outline
- @click="onDialogCancel"
- />
- <q-btn
- label="Salvar"
- color="primary"
- :loading="saving"
- :disable="loading"
- @click="onSave"
- />
- </q-card-actions>
- </q-card>
- </q-dialog>
- </template>
- <script setup>
- import { computed, onMounted, ref } from "vue";
- import { Notify, useDialogPluginComponent } from "quasar";
- import DefaultDialogHeader from "src/components/defaults/DefaultDialogHeader.vue";
- import PermissionLevelBadges from "src/pages/permissions/components/PermissionLevelBadges.vue";
- import PermissionLevelMenu from "src/pages/permissions/components/PermissionLevelMenu.vue";
- import {
- getUserTypePermissions,
- updateUserTypePermissions,
- } from "src/api/user_type";
- import {
- FRANCHISOR_PERMISSION_MENUS,
- applyComputedLevels,
- } from "src/pages/permissions/permission_menus";
- const { group } = defineProps({
- group: {
- type: Object,
- required: true,
- },
- });
- defineEmits([...useDialogPluginComponent.emits]);
- const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
- useDialogPluginComponent();
- const loading = ref(false);
- const saving = ref(false);
- const selectedScope = ref(null);
- // permission_id e bits atuais do grupo, indexados pelo scope do menu.
- const permissionIdByScope = ref({});
- const bitsByScope = ref({});
- const menus = computed(() =>
- FRANCHISOR_PERMISSION_MENUS.filter(
- (menu) => permissionIdByScope.value[menu.scope] !== undefined,
- ),
- );
- const selected = computed(
- () => menus.value.find((menu) => menu.scope === selectedScope.value) ?? null,
- );
- const children = computed(() =>
- (selected.value?.children ?? []).filter(
- (child) => permissionIdByScope.value[child.scope] !== undefined,
- ),
- );
- const bitsFor = (scope) => bitsByScope.value[scope] ?? 0;
- const setBits = (scope, value) => {
- bitsByScope.value[scope] = value;
- };
- const scopesInMenu = () =>
- FRANCHISOR_PERMISSION_MENUS.flatMap((menu) => [
- menu.scope,
- ...(menu.children ?? []).map((child) => child.scope),
- ]);
- const fetchPermissions = async () => {
- loading.value = true;
- try {
- const payload = await getUserTypePermissions(group.slug);
- const byScope = Object.fromEntries(
- payload.permissions.map((p) => [p.scope, p]),
- );
- permissionIdByScope.value = {};
- bitsByScope.value = {};
- for (const scope of scopesInMenu()) {
- const permission = byScope[scope];
- if (!permission) continue;
- permissionIdByScope.value[scope] = permission.id;
- // Normaliza dados anteriores à regra (ex.: Visualizar gravado sem Menu).
- bitsByScope.value[scope] = applyComputedLevels(permission.bits);
- }
- } catch {
- Notify.create({
- message: "Não foi possível carregar as permissões do grupo.",
- type: "negative",
- });
- } finally {
- loading.value = false;
- }
- };
- const onSave = async () => {
- saving.value = true;
- try {
- await updateUserTypePermissions(
- group.slug,
- Object.entries(permissionIdByScope.value).map(([scope, id]) => ({
- permission_id: id,
- bits: bitsByScope.value[scope] ?? 0,
- })),
- );
- Notify.create({ message: "Permissões atualizadas.", type: "positive" });
- onDialogOK();
- } catch {
- Notify.create({
- message: "Não foi possível salvar as permissões.",
- type: "negative",
- });
- } finally {
- saving.value = false;
- }
- };
- onMounted(fetchPermissions);
- </script>
|