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

refactor: update API endpoints to use franchisee-specific routes

- Changed kanban-related API calls to point to /franchisee/activities instead of /kanban.
- Updated kanban media API calls to use /franchisee/activities/{kanbanId}/medias.
- Modified kanban reply API calls to use /franchisee/activities/{kanbanId}/replies.
- Adjusted support ticket API calls to use /franchisee/support/tickets.
- Updated unit-related API calls to use /franchisee/unit and /franchisee/unit/histories.
- Changed user-related API calls to use /franchisee/users.
- Updated permission checks in various components to ensure proper access control.
- Adjusted navigation store to reflect new permission scopes for franchisee integrations, support, and activities.
ebagabee 1 неделя назад
Родитель
Сommit
74cd3a90b1

+ 5 - 5
src/api/kanban.js

@@ -1,22 +1,22 @@
 import api from "src/api";
 
 export const getKanbans = async () => {
-  const { data } = await api.get("/kanban");
+  const { data } = await api.get("/franchisee/activities");
   return data.payload;
 };
 
 export const createKanban = async (payload) => {
-  const { data } = await api.post("/kanban", payload);
+  const { data } = await api.post("/franchisee/activities", payload);
   return data.payload;
 };
 
 export const updateKanban = async (id, payload) => {
-  const { data } = await api.put(`/kanban/${id}`, payload);
+  const { data } = await api.put(`/franchisee/activities/${id}`, payload);
   return data.payload;
 };
 
 export const deleteKanban = async (id) => {
-  const { data } = await api.delete(`/kanban/${id}`);
+  const { data } = await api.delete(`/franchisee/activities/${id}`);
   return data;
 };
 
@@ -25,6 +25,6 @@ export const deleteKanban = async (id) => {
  * @param {Array<{id: number, phase: string, order: number}>} items
  */
 export const reorderKanbans = async (items) => {
-  const { data } = await api.post("/kanban/reorder", { items });
+  const { data } = await api.post("/franchisee/activities/reorder", { items });
   return data;
 };

+ 3 - 3
src/api/kanban_media.js

@@ -1,20 +1,20 @@
 import api from "src/api";
 
 export const getKanbanMedias = async (kanbanId) => {
-  const { data } = await api.get(`/kanban/${kanbanId}/medias`);
+  const { data } = await api.get(`/franchisee/activities/${kanbanId}/medias`);
   return data.payload;
 };
 
 export const uploadKanbanMedia = async (kanbanId, file) => {
   const form = new FormData();
   form.append("file", file);
-  const { data } = await api.post(`/kanban/${kanbanId}/medias`, form, {
+  const { data } = await api.post(`/franchisee/activities/${kanbanId}/medias`, form, {
     headers: { "Content-Type": "multipart/form-data" },
   });
   return data.payload;
 };
 
 export const deleteKanbanMedia = async (kanbanId, id) => {
-  const { data } = await api.delete(`/kanban/${kanbanId}/medias/${id}`);
+  const { data } = await api.delete(`/franchisee/activities/${kanbanId}/medias/${id}`);
   return data;
 };

+ 4 - 4
src/api/kanban_reply.js

@@ -1,21 +1,21 @@
 import api from "src/api";
 
 export const getKanbanReplies = async (kanbanId) => {
-  const { data } = await api.get(`/kanban/${kanbanId}/replies`);
+  const { data } = await api.get(`/franchisee/activities/${kanbanId}/replies`);
   return data.payload;
 };
 
 export const createKanbanReply = async (kanbanId, payload) => {
-  const { data } = await api.post(`/kanban/${kanbanId}/replies`, payload);
+  const { data } = await api.post(`/franchisee/activities/${kanbanId}/replies`, payload);
   return data.payload;
 };
 
 export const updateKanbanReply = async (kanbanId, id, payload) => {
-  const { data } = await api.put(`/kanban/${kanbanId}/replies/${id}`, payload);
+  const { data } = await api.put(`/franchisee/activities/${kanbanId}/replies/${id}`, payload);
   return data.payload;
 };
 
 export const deleteKanbanReply = async (kanbanId, id) => {
-  const { data } = await api.delete(`/kanban/${kanbanId}/replies/${id}`);
+  const { data } = await api.delete(`/franchisee/activities/${kanbanId}/replies/${id}`);
   return data;
 };

+ 4 - 4
src/api/support_reply.js

@@ -1,21 +1,21 @@
 import api from "src/api";
 
 export const getSupportReplies = async (ticketId) => {
-  const { data } = await api.get(`/support-ticket/${ticketId}/replies`);
+  const { data } = await api.get(`/franchisee/support/tickets/${ticketId}/replies`);
   return data.payload;
 };
 
 export const createSupportReply = async (ticketId, payload) => {
-  const { data } = await api.post(`/support-ticket/${ticketId}/replies`, payload);
+  const { data } = await api.post(`/franchisee/support/tickets/${ticketId}/replies`, payload);
   return data.payload;
 };
 
 export const updateSupportReply = async (ticketId, id, payload) => {
-  const { data } = await api.put(`/support-ticket/${ticketId}/replies/${id}`, payload);
+  const { data } = await api.put(`/franchisee/support/tickets/${ticketId}/replies/${id}`, payload);
   return data.payload;
 };
 
 export const deleteSupportReply = async (ticketId, id) => {
-  const { data } = await api.delete(`/support-ticket/${ticketId}/replies/${id}`);
+  const { data } = await api.delete(`/franchisee/support/tickets/${ticketId}/replies/${id}`);
   return data;
 };

+ 4 - 4
src/api/support_ticket.js

@@ -1,21 +1,21 @@
 import api from "src/api";
 
 export const getSupportTickets = async () => {
-  const { data } = await api.get("/support-ticket");
+  const { data } = await api.get("/franchisee/support/tickets");
   return data.payload;
 };
 
 export const createSupportTicket = async (payload) => {
-  const { data } = await api.post("/support-ticket", payload);
+  const { data } = await api.post("/franchisee/support/tickets", payload);
   return data.payload;
 };
 
 export const updateSupportTicket = async (id, payload) => {
-  const { data } = await api.put(`/support-ticket/${id}`, payload);
+  const { data } = await api.put(`/franchisee/support/tickets/${id}`, payload);
   return data.payload;
 };
 
 export const deleteSupportTicket = async (id) => {
-  const { data } = await api.delete(`/support-ticket/${id}`);
+  const { data } = await api.delete(`/franchisee/support/tickets/${id}`);
   return data;
 };

+ 2 - 2
src/api/unit.js

@@ -13,14 +13,14 @@ export const getUnit = async (id) => {
 //
 
 export const getUnitMe = async () => {
-  const { data } = await api.get("/unit/me");
+  const { data } = await api.get("/franchisee/unit");
   return data.payload;
 };
 
 export const updateUnitMe = async (formData) => {
   formData.append("_method", "PUT");
 
-  const { data } = await api.post("/unit/me", formData, {
+  const { data } = await api.post("/franchisee/unit", formData, {
     headers: { "Content-Type": "multipart/form-data" },
   });
 

+ 1 - 1
src/api/unit_history.js

@@ -1,6 +1,6 @@
 import api from "src/api";
 
 export const getHistoriesByUnit = async () => {
-  const { data } = await api.get("/unit-history/me");
+  const { data } = await api.get("/franchisee/unit/histories");
   return data.payload;
 };

+ 2 - 2
src/api/unit_media.js

@@ -1,12 +1,12 @@
 import api from "src/api";
 
 export const getMediasByUnit = async () => {
-  const { data } = await api.get("/unit-media/me");
+  const { data } = await api.get("/franchisee/unit/medias");
   return data.payload;
 };
 
 export const createMedia = async (formData) => {
-  const { data } = await api.post("/unit-media/me", formData, {
+  const { data } = await api.post("/franchisee/unit/medias", formData, {
     headers: { "Content-Type": "multipart/form-data" },
   });
 

+ 4 - 4
src/api/unit_partner.js

@@ -1,12 +1,12 @@
 import api from "src/api";
 
 export const getPartnersByUnit = async () => {
-  const { data } = await api.get("/unit-partner/me");
+  const { data } = await api.get("/franchisee/unit/partners");
   return data.payload;
 };
 
 export const createPartner = async (formData) => {
-  const { data } = await api.post("/unit-partner/me", formData, {
+  const { data } = await api.post("/franchisee/unit/partners", formData, {
     headers: { "Content-Type": "multipart/form-data" },
   });
 
@@ -14,7 +14,7 @@ export const createPartner = async (formData) => {
 };
 
 export const updatePartner = async (id, formData) => {
-  const { data } = await api.put(`/unit-partner/me/${id}`, formData, {
+  const { data } = await api.put(`/franchisee/unit/partners/${id}`, formData, {
     headers: { "Content-Type": "multipart/form-data" },
   });
 
@@ -22,6 +22,6 @@ export const updatePartner = async (id, formData) => {
 };
 
 export const deletePartner = async (id) => {
-  const { data } = await api.delete(`/unit-partner/me/${id}`);
+  const { data } = await api.delete(`/franchisee/unit/partners/${id}`);
   return data;
 };

+ 2 - 2
src/api/unit_payment_account.js

@@ -1,11 +1,11 @@
 import api from "src/api";
 
 export const getPaymentAccountMe = async () => {
-  const { data } = await api.get("/unit-payment-account/me");
+  const { data } = await api.get("/franchisee/integrations/asaas");
   return data.payload;
 };
 
 export const savePaymentAccountMe = async (payload) => {
-  const { data } = await api.post("/unit-payment-account/me", payload);
+  const { data } = await api.post("/franchisee/integrations/asaas", payload);
   return data.payload;
 };

+ 8 - 8
src/api/user.js

@@ -6,32 +6,32 @@ export const getUser = async () => {
 };
 
 export const getUsers = async () => {
-  const { data } = await api.get("/user");
+  const { data } = await api.get("/franchisee/users");
   return data.payload;
 };
 
 export const getUserById = async (id) => {
-  const { data } = await api.get(`/user/${id}`);
+  const { data } = await api.get(`/franchisee/users/${id}`);
   return data.payload;
 };
 
 export const getUsersByUnit = async () => {
-  const { data } = await api.get("/user/unit");
+  const { data } = await api.get("/franchisee/users");
   return data.payload;
 };
 
 export const createUser = async (user) => {
-  const { data } = await api.post("/user", user);
+  const { data } = await api.post("/franchisee/users", user);
   return data.payload;
 };
 
 export const updateUser = async (user, id) => {
   if (user instanceof FormData) {
     user.append("_method", "PUT");
-    const { data } = await api.post(`/user/${id}`, user);
+    const { data } = await api.post(`/franchisee/users/${id}`, user);
     return data.payload;
   }
-  const { data } = await api.put(`/user/${id}`, user);
+  const { data } = await api.put(`/franchisee/users/${id}`, user);
   return data.payload;
 };
 
@@ -41,11 +41,11 @@ export const updateUserMe = async (user) => {
 };
 
 export const deleteUser = async (id) => {
-  const { data } = await api.delete(`/user/${id}`);
+  const { data } = await api.delete(`/franchisee/users/${id}`);
   return data.payload;
 };
 
 export const userTypes = async () => {
-  const { data } = await api.get("/user/all/types");
+  const { data } = await api.get("/franchisee/users/types");
   return data.payload;
 };

+ 9 - 2
src/pages/apis/ApisPage.vue

@@ -38,7 +38,7 @@
 
           <div class="row justify-end items-center q-mt-md" style="gap: 12px">
             <q-btn
-              v-if="account.asaas_configured"
+              v-if="canEdit && account.asaas_configured"
               flat
               color="negative"
               label="Remover chave"
@@ -46,6 +46,7 @@
               @click="onRemove"
             />
             <q-btn
+              v-if="canEdit"
               color="primary-2"
               label="Salvar"
               :disable="!apiKey"
@@ -60,7 +61,7 @@
 </template>
 
 <script setup>
-import { onMounted, ref } from "vue";
+import { computed, onMounted, ref } from "vue";
 import { useSubmitHandler } from "src/composables/useSubmitHandler";
 import {
   getPaymentAccountMe,
@@ -69,6 +70,12 @@ import {
 
 import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
 import DefaultPasswordInput from "src/components/defaults/DefaultPasswordInput.vue";
+import { permissionStore } from "src/stores/permission";
+
+const permissions = permissionStore();
+const canEdit = computed(() =>
+  permissions.getAccess("franchisee_integrations", "edit"),
+);
 
 const formRef = ref(null);
 const apiKey = ref("");

+ 10 - 1
src/pages/kanban/KanbanPage.vue

@@ -45,6 +45,7 @@
               style="font-size: 11px"
             />
             <q-btn
+              v-if="canAdd"
               flat
               round
               dense
@@ -63,6 +64,7 @@
           group="kanban"
           item-key="id"
           :animation="180"
+          :disabled="!canEdit"
           ghost-class="drag-ghost"
           drag-class="drag-active"
           style="display: flex; flex-direction: column; gap: 8px; min-height: 48px; flex: 1"
@@ -71,6 +73,8 @@
           <template #item="{ element }">
             <KanbanCard
               :card="element"
+              :can-edit="canEdit"
+              :can-delete="canDelete"
               @edit="openDialog(element, column.phase)"
               @delete="removeCard($event, column.phase)"
             />
@@ -82,7 +86,7 @@
 </template>
 
 <script setup>
-import { ref, reactive, defineAsyncComponent, onMounted, watch } from "vue";
+import { computed, ref, reactive, defineAsyncComponent, onMounted, watch } from "vue";
 import { useQuasar } from "quasar";
 import draggable from "vuedraggable";
 
@@ -90,6 +94,7 @@ import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
 import KanbanCard from "./components/KanbanCard.vue";
 import { getKanbans, reorderKanbans } from "src/api/kanban";
 import { userStore } from "src/stores/user";
+import { permissionStore } from "src/stores/permission";
 
 const AddEditKanbanDialog = defineAsyncComponent(
   () => import("./components/AddEditKanbanDialog.vue"),
@@ -97,6 +102,10 @@ const AddEditKanbanDialog = defineAsyncComponent(
 
 const $q = useQuasar();
 const { selectedUnit } = userStore();
+const permissions = permissionStore();
+const canAdd = computed(() => permissions.getAccess("franchisee_activities", "add"));
+const canEdit = computed(() => permissions.getAccess("franchisee_activities", "edit"));
+const canDelete = computed(() => permissions.getAccess("franchisee_activities", "delete"));
 const loading = ref(false);
 
 const columns = [

+ 11 - 4
src/pages/kanban/components/AddEditKanbanDialog.vue

@@ -53,7 +53,7 @@
 
                 <!-- Responsável — só aparece quando destino é Interno -->
                 <DefaultSelect
-                  v-if="form.scope === 'internal'"
+                  v-if="canViewUsers && form.scope === 'internal'"
                   v-model="form.responsible_user_id"
                   label="Responsável"
                   :options="userOptions"
@@ -103,7 +103,7 @@
 
             <!-- Tab: Comentários -->
             <div v-show="currentTab === 'comentarios'">
-              <div class="flex justify-end q-mb-sm">
+              <div v-if="canAdd" class="flex justify-end q-mb-sm">
                 <q-btn
                   color="primary"
                   icon="mdi-plus"
@@ -141,7 +141,7 @@
 
             <!-- Tab: Mídias -->
             <div v-show="currentTab === 'midias'">
-              <div class="flex justify-end q-mb-sm">
+              <div v-if="canAdd" class="flex justify-end q-mb-sm">
                 <q-btn
                   color="primary"
                   icon="mdi-upload-outline"
@@ -188,7 +188,7 @@
 
           <q-card-actions align="right" class="q-px-md q-pb-md" style="flex-shrink: 0">
             <q-btn outline color="primary" label="Cancelar" @click="onDialogCancel" />
-            <q-btn color="primary" label="Salvar" type="submit" :loading="loading" />
+            <q-btn v-if="canSave" color="primary" label="Salvar" type="submit" :loading="loading" />
           </q-card-actions>
         </q-form>
       </q-card>
@@ -221,6 +221,7 @@ import {
   deleteKanbanMedia,
 } from "src/api/kanban_media";
 import { getUsersByUnit } from "src/api/user";
+import { permissionStore } from "src/stores/permission";
 
 defineEmits([...useDialogPluginComponent.emits]);
 
@@ -233,6 +234,11 @@ const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
   useDialogPluginComponent();
 
 const $q = useQuasar();
+const permissions = permissionStore();
+const canAdd = computed(() => permissions.getAccess("franchisee_activities", "add"));
+const canEdit = computed(() => permissions.getAccess("franchisee_activities", "edit"));
+const canSave = computed(() => card?.id ? canEdit.value : canAdd.value);
+const canViewUsers = computed(() => permissions.getAccess("franchisee_users", "view"));
 
 const formRef        = ref(null);
 const loading        = ref(false);
@@ -310,6 +316,7 @@ const buildPayload = () => ({
 });
 
 const loadUsers = async () => {
+  if (!canViewUsers.value) return;
   try {
     const users = await getUsersByUnit();
     userOptions.value = users.map((u) => ({ label: u.name, value: u.id }));

+ 5 - 2
src/pages/kanban/components/KanbanCard.vue

@@ -2,7 +2,7 @@
   <q-card
     class="kanban-card cursor-pointer"
     :style="{ borderLeft: `4px solid ${phaseColor}` }"
-    @click="$emit('edit', card)"
+    @click="canEdit && $emit('edit', card)"
   >
     <!-- Header: Creator name + Priority badge + Delete button -->
     <div class="row items-center justify-between q-px-md q-pt-md">
@@ -17,6 +17,7 @@
           style="padding: 4px 8px; font-size: 11px"
         />
         <q-btn
+          v-if="canDelete"
           flat
           dense
           round
@@ -73,8 +74,10 @@ import { deleteKanban } from "src/api/kanban";
 
 const emit = defineEmits(["edit", "delete"]);
 
-const { card } = defineProps({
+const { card, canEdit, canDelete } = defineProps({
   card: { type: Object, required: true },
+  canEdit: { type: Boolean, default: false },
+  canDelete: { type: Boolean, default: false },
 });
 
 const $q = useQuasar();

+ 8 - 0
src/pages/kanban/components/KanbanCommentCard.vue

@@ -11,6 +11,7 @@
       </div>
       <div style="display: flex; gap: 4px; flex-shrink: 0">
         <q-btn
+          v-if="canEdit"
           flat
           dense
           round
@@ -20,6 +21,7 @@
           @click="$emit('edit')"
         />
         <q-btn
+          v-if="canDelete"
           flat
           dense
           round
@@ -34,6 +36,12 @@
 </template>
 
 <script setup>
+import { computed } from "vue";
+import { permissionStore } from "src/stores/permission";
+
+const permissions = permissionStore();
+const canEdit = computed(() => permissions.getAccess("franchisee_activities", "edit"));
+const canDelete = computed(() => permissions.getAccess("franchisee_activities", "delete"));
 defineEmits(["edit", "delete"]);
 
 defineProps({

+ 5 - 0
src/pages/kanban/components/KanbanMediaCard.vue

@@ -37,6 +37,7 @@
           target="_blank"
         />
         <q-btn
+          v-if="canDelete"
           flat
           dense
           round
@@ -52,6 +53,10 @@
 
 <script setup>
 import { computed } from "vue";
+import { permissionStore } from "src/stores/permission";
+
+const permissions = permissionStore();
+const canDelete = computed(() => permissions.getAccess("franchisee_activities", "delete"));
 
 defineEmits(["delete"]);
 

+ 9 - 4
src/pages/support/SupportPage.vue

@@ -5,7 +5,7 @@
       ref="tableRef"
       :columns="columns"
       :api-call="getSupportTickets"
-      add-item
+      :add-item="canAdd"
       title="Tickets"
       :female="false"
       description="tickets"
@@ -45,7 +45,7 @@
           />
 
           <q-btn
-            v-if="canManage(row) && row.status === 'in_progress'"
+            v-if="canEdit && canManage(row) && row.status === 'in_progress'"
             outline
             icon="mdi-check-circle-outline"
             style="width: 36px"
@@ -53,7 +53,7 @@
           />
 
           <q-btn
-            v-if="canManage(row) && row.status === 'in_progress'"
+            v-if="canDelete && canManage(row) && row.status === 'in_progress'"
             outline
             icon="mdi-trash-can-outline"
             style="width: 36px"
@@ -66,13 +66,14 @@
 </template>
 
 <script setup>
-import { defineAsyncComponent, useTemplateRef, watch } from "vue";
+import { computed, defineAsyncComponent, useTemplateRef, watch } from "vue";
 import { useQuasar } from "quasar";
 
 import DefaultTable from "src/components/defaults/DefaultTable.vue";
 import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
 import { getSupportTickets, deleteSupportTicket } from "src/api/support_ticket";
 import { userStore } from "src/stores/user";
+import { permissionStore } from "src/stores/permission";
 
 const AddEditTicketDialog = defineAsyncComponent(
   () => import("src/pages/support/components/AddEditTicketDialog.vue"),
@@ -84,6 +85,10 @@ const CloseTicketDialog = defineAsyncComponent(
 
 const $q = useQuasar();
 const store = userStore();
+const permissions = permissionStore();
+const canAdd = computed(() => permissions.getAccess("franchisee_support", "add"));
+const canEdit = computed(() => permissions.getAccess("franchisee_support", "edit"));
+const canDelete = computed(() => permissions.getAccess("franchisee_support", "delete"));
 const tableRef = useTemplateRef("tableRef");
 
 watch(

+ 8 - 3
src/pages/support/components/AddEditTicketDialog.vue

@@ -77,7 +77,7 @@
             <!-- Tab: Comentários -->
             <div v-show="currentTab === 'comentarios'">
               <div
-                v-if="ticket?.status === 'in_progress'"
+                v-if="canAdd && ticket?.status === 'in_progress'"
                 class="flex justify-end q-mb-sm"
               >
                 <q-btn
@@ -127,14 +127,14 @@
               @click="onDialogCancel"
             />
             <q-btn
-              v-if="canManage && ticket?.id && ticket?.status === 'in_progress'"
+              v-if="canEdit && canManage && ticket?.id && ticket?.status === 'in_progress'"
               outline
               color="negative"
               label="Encerrar"
               @click="onCloseTicket"
             />
             <q-btn
-              v-if="canManage && (!ticket?.id || ticket?.status === 'in_progress')"
+              v-if="canSave && canManage && (!ticket?.id || ticket?.status === 'in_progress')"
               color="primary"
               label="Salvar"
               type="submit"
@@ -165,6 +165,7 @@ import {
 import CloseTicketDialog from "./CloseTicketDialog.vue";
 import AddEditReplyDialog from "./AddEditReplyDialog.vue";
 import { getSupportReplies, deleteSupportReply } from "src/api/support_reply";
+import { permissionStore } from "src/stores/permission";
 
 defineEmits([...useDialogPluginComponent.emits]);
 
@@ -180,6 +181,10 @@ const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
 
 const $q = useQuasar();
 const { user } = userStore();
+const permissions = permissionStore();
+const canAdd = computed(() => permissions.getAccess("franchisee_support", "add"));
+const canEdit = computed(() => permissions.getAccess("franchisee_support", "edit"));
+const canSave = computed(() => ticket?.id ? canEdit.value : canAdd.value);
 
 const formRef = ref(null);
 const loading = ref(false);

+ 8 - 0
src/pages/support/components/TicketCommentCard.vue

@@ -11,6 +11,7 @@
       </div>
       <div style="display: flex; gap: 4px; flex-shrink: 0">
         <q-btn
+          v-if="canEdit"
           flat
           dense
           round
@@ -20,6 +21,7 @@
           @click="$emit('edit')"
         />
         <q-btn
+          v-if="canDelete"
           flat
           dense
           round
@@ -34,6 +36,12 @@
 </template>
 
 <script setup>
+import { computed } from "vue";
+import { permissionStore } from "src/stores/permission";
+
+const permissions = permissionStore();
+const canEdit = computed(() => permissions.getAccess("franchisee_support", "edit"));
+const canDelete = computed(() => permissions.getAccess("franchisee_support", "delete"));
 defineEmits(["edit", "delete"]);
 
 defineProps({

+ 6 - 1
src/pages/unit/tabs/MediasTab.vue

@@ -6,6 +6,7 @@
         <div class="row justify-between items-center q-mb-md">
           <span class="text-subtitle1 text-weight-medium">Mídias</span>
           <q-btn
+            v-if="canAdd"
             icon="add"
             color="primary-2"
             style="height: 40px; width: 40px; border-radius: 8px"
@@ -111,10 +112,14 @@
 </template>
 
 <script setup>
-import { ref, onMounted } from "vue";
+import { computed, ref, onMounted } from "vue";
 import { useQuasar } from "quasar";
 import { getMediasByUnit } from "src/api/unit_media";
 import AddMediaDialog from "src/pages/unit/components/AddMediaDialog.vue";
+import { permissionStore } from "src/stores/permission";
+
+const permissions = permissionStore();
+const canAdd = computed(() => permissions.getAccess("franchisee_unit", "add"));
 
 const props = defineProps({
   unitId: { type: Number, default: null },

+ 10 - 2
src/pages/unit/tabs/PartnersTab.vue

@@ -2,6 +2,7 @@
   <div class="q-pa-md">
     <div class="row justify-end q-mb-md">
       <q-btn
+        v-if="canAdd"
         icon="add"
         color="primary-2"
         style="height: 40px; width: 40px"
@@ -69,9 +70,10 @@
             class="col-xs-12 col-sm-6 col-md-3"
           >
             <div class="relative-position">
-              <PartnerCardComponent :partner @click="openDialog(partner)" />
+              <PartnerCardComponent :partner @click="canEdit && openDialog(partner)" />
 
               <q-btn
+                v-if="canDelete"
                 round
                 unelevated
                 size="xs"
@@ -93,11 +95,17 @@
 </template>
 
 <script setup>
-import { ref, onMounted, onUnmounted } from "vue";
+import { computed, ref, onMounted, onUnmounted } from "vue";
 import { useQuasar } from "quasar";
 import PartnerCardComponent from "src/components/shared/PartnerCardComponent.vue";
 import AddEditPartnerDialog from "src/pages/unit/components/AddEditPartnerDialog.vue";
 import { getPartnersByUnit, deletePartner } from "src/api/unit_partner";
+import { permissionStore } from "src/stores/permission";
+
+const permissions = permissionStore();
+const canAdd = computed(() => permissions.getAccess("franchisee_unit", "add"));
+const canEdit = computed(() => permissions.getAccess("franchisee_unit", "edit"));
+const canDelete = computed(() => permissions.getAccess("franchisee_unit", "delete"));
 
 const props = defineProps({
   unitId: {

+ 5 - 0
src/pages/unit/tabs/UnitDataTab.vue

@@ -158,6 +158,7 @@
 
         <div class="row justify-end q-mt-md items-end full-width q-px-xs">
           <q-btn
+            v-if="canEdit"
             color="primary-2"
             label="Salvar"
             :disable="!hasChanges"
@@ -180,6 +181,10 @@ import { useSubmitHandler } from "src/composables/useSubmitHandler";
 import DefaultInput from "src/components/defaults/DefaultInput.vue";
 import DefaultPasswordInput from "src/components/defaults/DefaultPasswordInput.vue";
 import AvatarImageComponent from "src/components/shared/AvatarImageComponent.vue";
+import { permissionStore } from "src/stores/permission";
+
+const permissions = permissionStore();
+const canEdit = computed(() => permissions.getAccess("franchisee_unit", "edit"));
 
 const props = defineProps({
   getFormAsFormData: { type: Function, required: true },

+ 6 - 0
src/pages/users/UserActionPage.vue

@@ -81,6 +81,7 @@
             @click="router.push({ name: 'UsersPage' })"
           />
           <q-btn
+            v-if="canSave"
             label="Salvar"
             color="primary"
             :loading="loading"
@@ -106,6 +107,7 @@ import { useInputRules } from "src/composables/useInputRules";
 import { useSubmitHandler } from "src/composables/useSubmitHandler";
 import { createUser, updateUser, getUserById } from "src/api/user";
 import { userStore } from "src/stores/user";
+import { permissionStore } from "src/stores/permission";
 import masks from "src/helpers/masks";
 
 const router = useRouter();
@@ -113,6 +115,7 @@ const route = useRoute();
 const $q = useQuasar();
 const { inputRules } = useInputRules();
 const store = userStore();
+const permissions = permissionStore();
 const { user } = storeToRefs(store);
 
 const formRef = ref(null);
@@ -121,6 +124,9 @@ const avatarFile = ref(null);
 const originalUserType = ref(null);
 
 const isEdit = computed(() => !!route.params.id);
+const canSave = computed(() =>
+  permissions.getAccess("franchisee_users", isEdit.value ? "edit" : "add"),
+);
 const isEditingSelf = computed(() => isEdit.value && parseInt(route.params.id) === user.value?.id);
 const canChangeUserType = computed(() => user.value?.user_type === "ADMIN_FRANCHISEE");
 

+ 6 - 1
src/pages/users/UsersPage.vue

@@ -10,12 +10,13 @@
         :api-call="getUsersByUnit"
         descricao="usuários"
         :feminino="false"
-        add-item
+        :add-item="canAdd"
         add-item-route="UserAddPage"
       >
         <template #body-cell-actions="{ row }">
           <q-td align="center">
             <q-btn
+              v-if="canEdit"
               outline
               icon="mdi-account-edit-outline"
               style="width: 36px"
@@ -37,9 +38,13 @@ import { useRouter } from "vue-router";
 import { formatDateYMDtoDMY } from "src/helpers/utils";
 import { computed } from "vue";
 import { userStore } from "src/stores/user";
+import { permissionStore } from "src/stores/permission";
 
 const router = useRouter();
 const store = userStore();
+const permissions = permissionStore();
+const canAdd = computed(() => permissions.getAccess("franchisee_users", "add"));
+const canEdit = computed(() => permissions.getAccess("franchisee_users", "edit"));
 // Chave reativa: força remount do DefaultTable ao mudar de unidade
 const tableKey = computed(() => store.selectedUnit?.id ?? 0);
 

+ 1 - 0
src/router/routes/apis.route.js

@@ -6,6 +6,7 @@ export default [
     meta: {
       title: { value: "APIs", translate: false },
       requireAuth: true,
+      requiredPermission: "franchisee_integrations",
       breadcrumbs: [
         { name: "DashboardPage", title: "Dashboard" },
         { name: "ApisPage", title: "APIs" },

+ 3 - 0
src/router/routes/config.route.js

@@ -110,6 +110,7 @@ export default [
     meta: {
       title: "Cadastro de Usuário",
       requireAuth: true,
+      requiredPermission: "franchisee_users",
       breadcrumbs: [
         { name: "UsersPage", title: "Usuários" },
         { name: "UserAddPage", title: "Cadastro de Usuário" },
@@ -123,6 +124,7 @@ export default [
     meta: {
       title: "Editar Usuário",
       requireAuth: true,
+      requiredPermission: "franchisee_users",
       breadcrumbs: [
         { name: "UsersPage", title: "Usuários" },
         { name: "UserEditPage", title: "Editar Usuário" },
@@ -143,6 +145,7 @@ export default [
         translate: true,
       },
       requireAuth: true,
+      requiredPermission: "franchisee_users",
       breadcrumbs: [
         {
           name: "UsersPage",

+ 1 - 1
src/router/routes/kanban.route.js

@@ -9,7 +9,7 @@ export default [
         translate: false,
       },
       requireAuth: true,
-      requiredPermission: "dashboard",
+      requiredPermission: "franchisee_activities",
       breadcrumbs: [
         {
           name: "KanbanPage",

+ 1 - 0
src/router/routes/support.route.js

@@ -6,6 +6,7 @@ export default [
     meta: {
       title: { value: "Suporte", translate: false },
       requireAuth: true,
+      requiredPermission: "franchisee_support",
       breadcrumbs: [
         { name: "SupportPage", title: "Suporte" },
       ],

+ 1 - 0
src/router/routes/unit.route.js

@@ -6,6 +6,7 @@ export default [
     meta: {
       title: { value: "Dados da Unidade", translate: false },
       requireAuth: true,
+      requiredPermission: "franchisee_unit",
       breadcrumbs: [
         {
           name: "UnitDataPage",

+ 10 - 5
src/stores/navigation.js

@@ -110,7 +110,8 @@ export const navigationStore = defineStore("navigation", () => {
       name: "UsersPage",
       icon: "mdi-account-multiple-outline",
       disable: false,
-      permission: true,
+      permission: false,
+      permissionScope: "franchisee_users",
     },
     {
       type: "single",
@@ -118,7 +119,8 @@ export const navigationStore = defineStore("navigation", () => {
       name: "UnitDataPage",
       icon: "mdi-cog-outline",
       disable: false,
-      permission: true,
+      permission: false,
+      permissionScope: "franchisee_unit",
     },
     {
       type: "single",
@@ -126,7 +128,8 @@ export const navigationStore = defineStore("navigation", () => {
       name: "ApisPage",
       icon: "mdi-api",
       disable: false,
-      permission: true,
+      permission: false,
+      permissionScope: "franchisee_integrations",
     },
     {
       type: "single",
@@ -134,7 +137,8 @@ export const navigationStore = defineStore("navigation", () => {
       name: "SupportPage",
       icon: "mdi-lifebuoy",
       disable: false,
-      permission: true,
+      permission: false,
+      permissionScope: "franchisee_support",
     },
     {
       type: "single",
@@ -142,7 +146,8 @@ export const navigationStore = defineStore("navigation", () => {
       name: "KanbanPage",
       icon: "mdi-clipboard-list-outline",
       disable: false,
-      permission: true,
+      permission: false,
+      permissionScope: "franchisee_activities",
     },
   ]);