Explorar el Código

feat: :sparkles: feat (layout pos login) criacao dos leftmenulayouts personalizados

foi criado um leftmenulayout para cada tipo de usuario com seu proprio layout customizado, carregando corretamente pelo tipo de usuario pós login

fase:dev | origin:escopo
Gustavo Zanatta hace 2 semanas
padre
commit
fb99f423b9

+ 2 - 2
package-lock.json

@@ -1,11 +1,11 @@
 {
-  "name": "quasar-skeleton",
+  "name": "serprati-digital",
   "version": "0.0.1",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
-      "name": "quasar-skeleton",
+      "name": "serprati-digital",
       "version": "0.0.1",
       "dependencies": {
         "@bufbuild/protobuf": "^2.5.1",

+ 2 - 2
quasar.config.js

@@ -55,7 +55,7 @@ export default defineConfig((ctx) => {
       // publicPath: '/',
       // analyze: true,
       env: {
-        APP_NAME: "skeleton",
+        APP_NAME: "serprati-digital",
         API_URL: ctx.dev ? "http://localhost:3000" : "http://localhost:8000",
         PASSWORD: ctx.dev ? "S@ft2080." : "",
         WEBSOCKET_API: ctx.dev
@@ -222,7 +222,7 @@ export default defineConfig((ctx) => {
       builder: {
         // https://www.electron.build/configuration/configuration
 
-        appId: "quasar-skeleton",
+        appId: "serprati-digital",
       },
     },
 

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 6 - 0
src/assets/logo_serprati_associado.svg


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 6 - 0
src/assets/logo_serprati_associado_small.svg


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 6 - 0
src/assets/logo_serprati_parceiro.svg


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 6 - 0
src/assets/logo_serprati_parceiro_small.svg


BIN
src/assets/logo_serprati_small.png


+ 199 - 188
src/components/layout/LeftMenuLayout.vue

@@ -5,102 +5,84 @@
     show-if-above
     no-swipe-close
     no-swipe-open
-    :width="214"
-    :mini-width="60"
+    :width="235"
+    :mini-width="64"
     :breakpoint="500"
     :mini="miniState"
     :behavior="'desktop'"
-    class="detached-container"
+    class="left-menu-drawer"
   >
-    <div class="column full-height no-wrap">
-      <div class="overflow-hidden" style="border-radius: 8px 8px 0px 0px">
-        <div
-          class="flex flex-center full-width q-pa-sm"
-          style="height: 60px"
-        >
-          <q-img
-            v-if="!miniState"
-            :src="Logo"
-            style="width: 92px; height: 32px"
+    <div class="column full-height no-wrap left-menu-inner">
+
+      <div class="left-menu-logo-wrapper" :class="{ 'left-menu-logo-wrapper--mini': miniState }">
+        <q-img
+          v-if="!miniState"
+          :src="Logo"
+          fit="contain"
+          class="left-menu-logo"
+        />
+        <q-img
+          v-else
+          :src="LogoSmall"
+          fit="contain"
+          class="left-menu-logo--mini"
+        />
+      </div>
+
+      <div
+        v-if="!$q.screen.lt.md"
+        class="toggle-button-wrapper absolute"
+        style="top: 2px; right: -32px; z-index: 1"
+      >
+        <div @click="miniState = !miniState">
+          <q-icon
+            size="sm"
+            :name="miniState ? 'mdi-page-layout-sidebar-right' : 'mdi-page-layout-sidebar-left'"
+            color="violet-normal"
           />
-          <q-img v-else :src="Logo" style="width: 32px" />
+          <q-tooltip anchor="center right" self="center left" :offset="[10, 10]">
+            {{ miniState ? $t('ui.navigation.expand_menu') : $t('ui.navigation.collapse_menu') }}
+          </q-tooltip>
         </div>
       </div>
 
-      <div class="column full-height no-wrap">
-        <div
-          v-if="!$q.screen.lt.md"
-          class="toggle-button-wrapper absolute"
-          style="top: 2px; right: -32px; z-index: 1"
-        >
-          <div @click="miniState = !miniState">
-            <q-icon
-              size="sm"
-              :name="
-                miniState
-                  ? 'mdi-page-layout-sidebar-right'
-                  : 'mdi-page-layout-sidebar-left'
-              "
-              color="primary"
-            />
-            <q-tooltip
-              anchor="center right"
-              self="center left"
-              :offset="[10, 10]"
-              >{{
-                miniState
-                  ? $t("ui.navigation.expand_menu")
-                  : $t("ui.navigation.collapse_menu")
-              }}</q-tooltip
-            >
-          </div>
-        </div>
-        <q-list class="column no-wrap">
-          <template v-for="(item, index) in navigation_store.navigationItems">
+      <div class="left-menu-items-wrapper column full-height no-wrap">
+        <div class="left-menu-bg-overlay" />
+
+        <q-list class="left-menu-list column no-wrap">
+          <template v-for="(item, index) in navigation_store.navigationItems" :key="index">
             <template v-if="item.permission">
               <q-item
                 v-if="item.type === 'single'"
-                :key="item.name"
                 v-ripple
                 clickable
-                :exact="item.name == 'HomePage'"
+                :exact="item.name === 'HomePage'"
                 exact-active-class="menu-selected"
                 active-class="menu-selected"
                 :to="{ name: item.name }"
+                class="left-menu-item"
               >
                 <q-item-section avatar>
-                  <q-icon :name="item.icon" style="font-size: 20px" />
+                  <q-icon :name="item.icon" class="left-menu-icon" />
                 </q-item-section>
-                <q-item-section>{{ $t(item.title) }}</q-item-section>
+                <q-item-section v-if="!miniState">{{ $t(item.title) }}</q-item-section>
                 <q-tooltip
                   v-if="miniState"
                   anchor="center right"
                   self="center left"
                   :offset="[10, 10]"
-                  >{{ $t(item.title) }}</q-tooltip
-                >
+                >{{ $t(item.title) }}</q-tooltip>
               </q-item>
-              <!-- Expansive Menu with children -->
-              <div v-else :key="item.title">
+              <div v-else>
                 <template v-if="!miniState">
-                  <q-tooltip
-                    v-if="miniState"
-                    anchor="center right"
-                    self="center left"
-                    :offset="[10, 10]"
-                    >{{ $t(item.title) }}</q-tooltip
-                  >
                   <q-expansion-item
                     v-model="isExpasionItemExpanded[index]"
-                    :class="{
-                      'menu-selected':
-                        childrenAreActive(item.childrens) &&
-                        !isExpasionItemExpanded[index],
-                    }"
+                    :class="{ 'menu-selected': childrenAreActive(item.childrens) && !isExpasionItemExpanded[index] }"
+                    class="left-menu-item"
                   >
                     <template #header>
                       <q-item-section avatar>
-                        <q-icon :name="item.icon" style="font-size: 20px" />
+                        <q-icon :name="item.icon" class="left-menu-icon" />
                       </q-item-section>
                       <q-item-section>{{ $t(item.title) }}</q-item-section>
                     </template>
@@ -111,19 +93,12 @@
                         :to="{ name: child.name }"
                         exact
                         exact-active-class="menu-selected"
-                        class="q-pl-lg"
+                        class="q-pl-lg left-menu-item"
                       >
                         <q-item-section avatar>
-                          <q-icon :name="child.icon" style="font-size: 20px" />
+                          <q-icon :name="child.icon" class="left-menu-icon" />
                         </q-item-section>
                         <q-item-section>{{ $t(child.title) }}</q-item-section>
-                        <q-tooltip
-                          v-if="miniState"
-                          anchor="center right"
-                          self="center left"
-                          :offset="[10, 10]"
-                          >{{ $t(child.title) }}</q-tooltip
-                        >
                       </q-item>
                     </div>
                   </q-expansion-item>
@@ -132,28 +107,16 @@
                   <q-item
                     v-ripple
                     clickable
-                    exact
-                    exact-active-class="menu-selected"
-                    :class="{
-                      'menu-selected': childrenAreActive(item.childrens),
-                    }"
+                    :class="{ 'menu-selected': childrenAreActive(item.childrens) }"
+                    class="left-menu-item"
                   >
                     <q-item-section avatar>
-                      <q-icon :name="item.icon" style="font-size: 20px" />
+                      <q-icon :name="item.icon" class="left-menu-icon" />
                     </q-item-section>
-                    <q-item-section>{{ $t(item.title) }}</q-item-section>
-                    <q-tooltip
-                      v-if="miniState"
-                      anchor="center right"
-                      self="center left"
-                      :offset="[10, 10]"
-                      >{{ $t(item.title) }}</q-tooltip
-                    >
-                    <q-menu
-                      class="menu-drawer"
-                      anchor="center right"
-                      self="top start"
-                    >
+                    <q-tooltip anchor="center right" self="center left" :offset="[10, 10]">
+                      {{ $t(item.title) }}
+                    </q-tooltip>
+                    <q-menu class="menu-drawer" anchor="center right" self="top start">
                       <q-list>
                         <q-item
                           v-for="child in item.childrens"
@@ -167,10 +130,7 @@
                           class="menu-drawer"
                         >
                           <q-item-section avatar>
-                            <q-icon
-                              :name="child.icon"
-                              style="font-size: 20px"
-                            />
+                            <q-icon :name="child.icon" class="left-menu-icon" />
                           </q-item-section>
                           <q-item-section>{{ $t(child.title) }}</q-item-section>
                         </q-item>
@@ -179,93 +139,43 @@
                   </q-item>
                 </template>
               </div>
+
             </template>
           </template>
         </q-list>
-        <q-list class="column q-mb-md no-wrap" style="border-radius: 6px">
-        </q-list>
-        <q-list class="q-mt-auto">
-          <q-item v-ripple clickable @click="changeTheme">
-            <div class="flex">
-              <q-item-section avatar>
-                <q-icon
-                  :name="
-                    $q.dark.isActive ? 'mdi-weather-sunny' : 'mdi-weather-night'
-                  "
-                  style="font-size: 20px"
-                />
-              </q-item-section>
-              <q-item-section>{{
-                $q.dark.isActive
-                  ? $t("common.terms.light")
-                  : $t("common.terms.dark")
-              }}</q-item-section>
-            </div>
-            <q-tooltip
-              v-if="miniState"
-              anchor="center right"
-              self="center left"
-              :offset="[10, 10]"
-              >{{
-                $q.dark.isActive
-                  ? $t("common.terms.light")
-                  : $t("common.terms.dark")
-              }}</q-tooltip
-            >
+        <q-list class="q-mt-auto left-menu-footer">
+          <q-item v-ripple class="left-menu-item left-menu-logout text-center" clickable @click="logoutFn">
+            <q-item-section avatar >
+              <q-icon name="logout" class="left-menu-icon left-menu-logout__icon"/>
+            </q-item-section>
+            <q-item-section v-if="!miniState" class="text-violet-normal">{{ $t('auth.logout') }}</q-item-section>
+            <q-tooltip v-if="miniState" anchor="center right" self="center left" :offset="[10, 10]">
+              {{ $t('auth.logout') }}
+            </q-tooltip>
           </q-item>
-          <q-item v-ripple clickable @click="logoutFn">
-            <div class="flex">
-              <q-item-section avatar>
-                <q-icon
-                  name="logout"
-                  color="negative"
-                  style="font-size: 20px"
-                />
-              </q-item-section>
-              <q-item-section>{{ $t("auth.logout") }}</q-item-section>
-            </div>
-            <q-tooltip
-              v-if="miniState"
-              anchor="center right"
-              self="center left"
-              :offset="[10, 10]"
-              >{{ $t("auth.logout") }}</q-tooltip
-            >
-          </q-item>
-          <q-item v-ripple clickable @click="openUrl('https://softpar.inf.br')">
+
+          <q-item v-ripple class="left-menu-item" clickable @click="openUrl('https://softpar.inf.br')">
             <div class="flex full-width justify-center">
               <q-img
-                :src="
-                  miniState || $q.screen.lt.md
-                    ? LogoSoftparMini
-                    : $q.dark.isActive
-                      ? LogoSoftparLight
-                      : LogoSoftparDark
-                "
-                style="width: 100%; height: 30px"
-                :style="
-                  miniState || $q.screen.lt.md
-                    ? 'max-width: 48px'
-                    : 'max-width: 100px'
-                "
+                :src="miniState || $q.screen.lt.md ? LogoSoftparMini : $q.dark.isActive ? LogoSoftparLight : LogoSoftparDark"
+                style="height: 30px"
+                :style="miniState || $q.screen.lt.md ? 'max-width: 48px; width: 48px' : 'max-width: 100px; width: 100%'"
               />
             </div>
           </q-item>
         </q-list>
+
         <div
-          class="full-width text-center text-subtitle3 q-pb-xs cursor-pointer"
+          class="full-width text-center q-pb-xs cursor-pointer left-menu-version"
           @click="gotoVersionPage()"
         >
-          <span
-            v-if="!(miniState || $q.screen.lt.md)"
-            class="text-caption text-weight-light"
-            >{{ $t("common.terms.version") + " " + version }}</span
-          >
-          <span v-else class="text-caption text-weight-light">{{
-            version
-          }}</span>
+          <span v-if="!(miniState || $q.screen.lt.md)" class="text-caption text-weight-light">
+            {{ $t('common.terms.version') + ' ' + version }}
+          </span>
+          <span v-else class="text-caption text-weight-light">{{ version }}</span>
         </div>
       </div>
+
     </div>
   </q-drawer>
 </template>
@@ -277,7 +187,8 @@ import { navigationStore } from "src/stores/navigation";
 import { useQuasar, Cookies } from "quasar";
 import { version } from "src/../package.json";
 
-import Logo from "src/assets/logo.png";
+import Logo from "src/assets/logo_serprati.svg";
+import LogoSmall from "src/assets/logo_serprati_small.png";
 import LogoSoftparLight from "src/assets/softpar_logo_light.svg";
 import LogoSoftparDark from "src/assets/softpar_logo_dark.svg";
 import LogoSoftparMini from "src/assets/softpar_logo_mini.svg";
@@ -288,9 +199,10 @@ const route = useRoute();
 const navigation_store = navigationStore();
 const $q = useQuasar();
 
-const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
-  ? "dark"
-  : "light";
+// const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
+//   ? "dark"
+//   : "light";
+// const systemTheme = "light";
 const miniStateCookies = Cookies.get("miniState");
 const miniState = ref(miniStateCookies === "true" ? true : false);
 
@@ -312,15 +224,6 @@ watchEffect(() => {
   }
 });
 
-const changeTheme = async () => {
-  const theme = $q.cookies.get("theme") || systemTheme;
-  if (theme == "dark") {
-    $q.dark.set(false);
-  } else {
-    $q.dark.set(true);
-  }
-};
-
 const logoutFn = async () => {
   await logout();
   router.push({ name: "LoginPage" });
@@ -348,16 +251,124 @@ onMounted(() => {
 </script>
 
 <style lang="scss" scoped>
-@import "/src/css/quasar.variables.scss";
+@use "sass:map";
+@use "src/css/quasar.variables.scss" as vars;
+
+:deep(.q-drawer) {
+  background: transparent !important;
+  box-shadow: none !important;
+  border: none !important;
+}
+
+.left-menu-drawer {
+  background: transparent;
+}
+
+.left-menu-inner {
+  height: 100%;
+  background: transparent;
+}
 
-.text-subtitle3 {
-  font-size: 1.1rem !important;
-  font-weight: 400 !important;
+.left-menu-logo-wrapper {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background-color: vars.$neutral-light;
+  border-radius: 8px 8px 0 0;
+  padding: 10px 16px;
+  min-height: 64px;
+  flex-shrink: 0;
+}
+
+.left-menu-logo-wrapper--mini {
+  padding: 10px 8px;
+}
+
+.left-menu-logo {
+  width: 110px;
+  height: 40px;
+}
+
+.left-menu-logo--mini {
+  width: 36px;
+  height: 36px;
+}
+
+.left-menu-items-wrapper {
+  position: relative;
+  flex: 1;
+  overflow: hidden;
+  border-radius: 0 0 8px 8px;
+
+  .left-menu-bg-overlay {
+    position: absolute;
+    inset: 0;
+    background-image: url("src/assets/pessoas_fundo.jpg");
+    background-size: cover;
+    background-position: center top;
+    opacity: 0.5;
+    z-index: 0;
+    pointer-events: none;
+  }
+
+  &::before {
+    content: "";
+    position: absolute;
+    inset: 0;
+    background-color: vars.$violet-normal;
+    z-index: 0;
+  }
+}
+
+.left-menu-list {
+  position: relative;
+  z-index: 1;
+  padding-top: 8px;
+}
+
+.left-menu-item {
+  position: relative;
+  z-index: 1;
+  color: vars.$neutral-light !important;
+  border-radius: 6px;
+  margin: 2px 8px;
+
+  &:hover {
+    background-color: rgba(vars.$neutral-light, 0.12);
+  }
+}
+
+.left-menu-icon {
+  font-size: 20px;
+  color: vars.$neutral-light;
 }
 
 .menu-selected {
-  background-color: rgba($primary, 0.1);
-  color: $primary;
+  background-color: rgba(vars.$neutral-light, 0.2) !important;
+  color: vars.$neutral-light !important;
+}
+
+:deep(.q-expansion-item__header) {
+  color: vars.$neutral-light;
+}
+
+.left-menu-footer {
+  position: relative;
+  z-index: 1;
+  border-top: 1px solid rgba(vars.$neutral-light, 0.15);
+  padding-top: 4px;
+}
+
+.left-menu-logout__icon {
+  color: map.get(vars.$colors, "violet-normal") !important;
+}
+
+.left-menu-version {
+  position: relative;
+  z-index: 1;
+  color: rgba(vars.$neutral-light, 0.6);
+  font-size: 0.75rem;
+  padding-bottom: 6px;
 }
 
 .toggle-button-wrapper {

+ 382 - 0
src/components/layout/LeftMenuLayoutAdministrador.vue

@@ -0,0 +1,382 @@
+<template>
+  <q-drawer
+    v-bind="$attrs"
+    :model-value="true"
+    show-if-above
+    no-swipe-close
+    no-swipe-open
+    :width="235"
+    :mini-width="64"
+    :breakpoint="500"
+    :mini="miniState"
+    :behavior="'desktop'"
+    class="left-menu-drawer"
+  >
+    <div class="column full-height no-wrap left-menu-inner">
+      <div class="left-menu-logo-wrapper" :class="{ 'left-menu-logo-wrapper--mini': miniState }">
+        <q-img
+          v-if="!miniState"
+          :src="Logo"
+          fit="contain"
+          class="left-menu-logo"
+        />
+        <q-img
+          v-else
+          :src="LogoSmall"
+          fit="contain"
+          class="left-menu-logo--mini"
+        />
+      </div>
+      <div
+        v-if="!$q.screen.lt.md"
+        class="toggle-button-wrapper absolute"
+        style="top: 2px; right: -32px; z-index: 1"
+      >
+        <div @click="miniState = !miniState">
+          <q-icon
+            size="sm"
+            :name="miniState ? 'mdi-page-layout-sidebar-right' : 'mdi-page-layout-sidebar-left'"
+            color="violet-normal"
+          />
+          <q-tooltip anchor="center right" self="center left" :offset="[10, 10]">
+            {{ miniState ? $t('ui.navigation.expand_menu') : $t('ui.navigation.collapse_menu') }}
+          </q-tooltip>
+        </div>
+      </div>
+      <div class="left-menu-items-wrapper column full-height no-wrap">
+        <div class="left-menu-bg-overlay" />
+        <div class="left-menu-bg-tint" />
+
+        <q-list class="left-menu-list column no-wrap">
+          <template v-for="(item, index) in navigation_store.navigationItems" :key="index">
+            <template v-if="item.permission">
+              <q-item
+                v-if="item.type === 'single'"
+                v-ripple
+                clickable
+                :exact="item.name === 'HomePage'"
+                exact-active-class="menu-selected"
+                active-class="menu-selected"
+                :to="{ name: item.name }"
+                class="left-menu-item"
+              >
+                <q-item-section avatar>
+                  <q-icon :name="item.icon" class="left-menu-icon" />
+                </q-item-section>
+                <q-item-section v-if="!miniState">{{ $t(item.title) }}</q-item-section>
+                <q-tooltip
+                  v-if="miniState"
+                  anchor="center right"
+                  self="center left"
+                  :offset="[10, 10]"
+                >{{ $t(item.title) }}</q-tooltip>
+              </q-item>
+              <div v-else>
+                <template v-if="!miniState">
+                  <q-expansion-item
+                    v-model="isExpasionItemExpanded[index]"
+                    :class="{ 'menu-selected': childrenAreActive(item.childrens) && !isExpasionItemExpanded[index] }"
+                    class="left-menu-item"
+                  >
+                    <template #header>
+                      <q-item-section avatar>
+                        <q-icon :name="item.icon" class="left-menu-icon" />
+                      </q-item-section>
+                      <q-item-section>{{ $t(item.title) }}</q-item-section>
+                    </template>
+                    <div v-for="child in item.childrens" :key="child.name">
+                      <q-item
+                        v-ripple
+                        clickable
+                        :to="{ name: child.name }"
+                        exact
+                        exact-active-class="menu-selected"
+                        class="q-pl-lg left-menu-item"
+                      >
+                        <q-item-section avatar>
+                          <q-icon :name="child.icon" class="left-menu-icon" />
+                        </q-item-section>
+                        <q-item-section>{{ $t(child.title) }}</q-item-section>
+                      </q-item>
+                    </div>
+                  </q-expansion-item>
+                </template>
+                <template v-else>
+                  <q-item
+                    v-ripple
+                    clickable
+                    :class="{ 'menu-selected': childrenAreActive(item.childrens) }"
+                    class="left-menu-item"
+                  >
+                    <q-item-section avatar>
+                      <q-icon :name="item.icon" class="left-menu-icon" />
+                    </q-item-section>
+                    <q-tooltip anchor="center right" self="center left" :offset="[10, 10]">
+                      {{ $t(item.title) }}
+                    </q-tooltip>
+                    <q-menu class="menu-drawer" anchor="center right" self="top start">
+                      <q-list>
+                        <q-item
+                          v-for="child in item.childrens"
+                          :key="child.name"
+                          v-ripple
+                          v-close-popup
+                          clickable
+                          :to="{ name: child.name }"
+                          exact
+                          exact-active-class="menu-selected"
+                          class="menu-drawer"
+                        >
+                          <q-item-section avatar>
+                            <q-icon :name="child.icon" class="left-menu-icon" />
+                          </q-item-section>
+                          <q-item-section>{{ $t(child.title) }}</q-item-section>
+                        </q-item>
+                      </q-list>
+                    </q-menu>
+                  </q-item>
+                </template>
+              </div>
+            </template>
+          </template>
+        </q-list>
+        <q-list class="q-mt-auto left-menu-footer">
+          <q-item v-ripple class="left-menu-item left-menu-logout text-center" clickable @click="logoutFn">
+            <q-item-section avatar >
+              <q-icon name="logout" class="left-menu-icon left-menu-logout__icon"/>
+            </q-item-section>
+            <q-item-section v-if="!miniState" class="text-violet-normal">{{ $t('auth.logout') }}</q-item-section>
+            <q-tooltip v-if="miniState" anchor="center right" self="center left" :offset="[10, 10]">
+              {{ $t('auth.logout') }}
+            </q-tooltip>
+          </q-item>
+
+          <q-item v-ripple class="left-menu-item" clickable @click="openUrl('https://softpar.inf.br')">
+            <div class="flex full-width justify-center">
+              <q-img
+                :src="miniState || $q.screen.lt.md ? LogoSoftparMini : $q.dark.isActive ? LogoSoftparLight : LogoSoftparDark"
+                style="height: 30px"
+                :style="miniState || $q.screen.lt.md ? 'max-width: 48px; width: 48px' : 'max-width: 100px; width: 100%'"
+              />
+            </div>
+          </q-item>
+        </q-list>
+
+        <div
+          class="full-width text-center q-pb-xs cursor-pointer left-menu-version"
+          @click="gotoVersionPage()"
+        >
+          <span v-if="!(miniState || $q.screen.lt.md)" class="text-caption text-weight-light">
+            {{ $t('common.terms.version') + ' ' + version }}
+          </span>
+          <span v-else class="text-caption text-weight-light">{{ version }}</span>
+        </div>
+      </div>
+    </div>
+  </q-drawer>
+</template>
+<script setup>
+import { ref, watch, watchEffect, onMounted } from "vue";
+import { useAuth } from "src/composables/useAuth";
+import { useRouter, useRoute } from "vue-router";
+import { navigationStore } from "src/stores/navigation";
+import { useQuasar, Cookies } from "quasar";
+import { version } from "src/../package.json";
+
+import Logo from "src/assets/logo_serprati.svg";
+import LogoSmall from "src/assets/logo_serprati_small.png";
+import LogoSoftparLight from "src/assets/softpar_logo_light.svg";
+import LogoSoftparDark from "src/assets/softpar_logo_dark.svg";
+import LogoSoftparMini from "src/assets/softpar_logo_mini.svg";
+
+const { logout } = useAuth();
+const router = useRouter();
+const route = useRoute();
+const navigation_store = navigationStore();
+const $q = useQuasar();
+
+// const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
+//   ? "dark"
+//   : "light";
+// const systemTheme = "light";
+const miniStateCookies = Cookies.get("miniState");
+const miniState = ref(miniStateCookies === "true" ? true : false);
+
+const childrenAreActive = (children) => {
+  if (!Array.isArray(children) || children.length === 0) {
+    return false;
+  }
+  return children.some((child) => route?.name === child?.name);
+};
+
+const isExpasionItemExpanded = ref([]);
+
+watchEffect(() => {
+  if ($q.screen.lt.md) {
+    miniState.value = true;
+    isExpasionItemExpanded.value.forEach((expansion, index) => {
+      isExpasionItemExpanded.value[index] = false;
+    });
+  }
+});
+
+const logoutFn = async () => {
+  await logout();
+  router.push({ name: "LoginPage" });
+};
+
+const openUrl = (url) => {
+  window.open(url, "_blank");
+};
+
+const gotoVersionPage = () => {
+  router.push({ name: "SystemVersionsPage" });
+};
+
+watch(miniState, () => {
+  Cookies.set("miniState", miniState.value, { path: "/", sameSite: "Lax" });
+});
+
+onMounted(() => {
+  navigation_store.navigationItems.forEach((item, index) => {
+    if (childrenAreActive(item.childrens)) {
+      isExpasionItemExpanded.value[index] = true;
+    }
+  });
+});
+</script>
+
+<style lang="scss" scoped>
+@use "sass:map";
+@use "src/css/quasar.variables.scss" as vars;
+
+:deep(.q-drawer) {
+  background: transparent !important;
+  box-shadow: none !important;
+  border: none !important;
+}
+
+.left-menu-drawer {
+  background: transparent;
+}
+
+.left-menu-inner {
+  height: 100%;
+  background: transparent;
+}
+
+.left-menu-logo-wrapper {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background-color: vars.$neutral-light;
+  border-radius: 8px 8px 0 0;
+  padding: 10px 16px;
+  min-height: 64px;
+  flex-shrink: 0;
+}
+
+.left-menu-logo-wrapper--mini {
+  padding: 10px 8px;
+}
+
+.left-menu-logo {
+  width: 110px;
+  height: 40px;
+}
+
+.left-menu-logo--mini {
+  width: 36px;
+  height: 36px;
+}
+
+.left-menu-items-wrapper {
+  position: relative;
+  flex: 1;
+  overflow: hidden;
+  border-radius: 0 0 8px 8px;
+
+  &::before {
+    content: "";
+    position: absolute;
+    inset: 0;
+    background-color: vars.$violet-light;
+    z-index: 0;
+  }
+
+  .left-menu-bg-overlay {
+    position: absolute;
+    inset: 0;
+    background-image: url("src/assets/pessoas_fundo.jpg");
+    background-size: cover;
+    background-position: center top;
+    opacity: 0.15;
+    mix-blend-mode: multiply;
+    z-index: 1;
+    pointer-events: none;
+  }
+
+  .left-menu-bg-tint {
+    display: none;
+  }
+}
+
+.left-menu-list {
+  position: relative;
+  z-index: 3;
+  padding-top: 8px;
+}
+
+.left-menu-item {
+  position: relative;
+  z-index: 3;
+  color: vars.$violet-normal !important;
+  border-radius: 6px;
+  margin: 2px 8px;
+
+  &:hover {
+    background-color: rgba(vars.$violet-normal, 0.08);
+  }
+}
+
+.left-menu-icon {
+  font-size: 20px;
+  color: vars.$violet-normal;
+}
+
+.menu-selected {
+  background-color: rgba(vars.$violet-normal, 0.12) !important;
+  color: vars.$violet-normal !important;
+}
+
+:deep(.q-expansion-item__header) {
+  color: vars.$violet-normal;
+}
+
+.left-menu-footer {
+  position: relative;
+  z-index: 3;
+  border-top: 1px solid rgba(vars.$violet-normal, 0.15);
+  padding-top: 4px;
+}
+
+.left-menu-logout__icon {
+  color: map.get(vars.$colors, "violet-normal") !important;
+}
+
+.left-menu-version {
+  position: relative;
+  z-index: 3;
+  color: rgba(vars.$violet-normal, 0.6);
+  font-size: 0.75rem;
+  padding-bottom: 6px;
+}
+
+.toggle-button-wrapper {
+  transition: transform 0.3s ease;
+}
+
+.toggle-button-wrapper:hover {
+  transform: scale(1.1);
+}
+</style>

+ 390 - 0
src/components/layout/LeftMenuLayoutAssociado.vue

@@ -0,0 +1,390 @@
+<template>
+  <q-drawer
+    v-bind="$attrs"
+    :model-value="true"
+    show-if-above
+    no-swipe-close
+    no-swipe-open
+    :width="235"
+    :mini-width="64"
+    :breakpoint="500"
+    :mini="miniState"
+    :behavior="'desktop'"
+    class="left-menu-drawer"
+  >
+    <div class="column full-height no-wrap left-menu-inner">
+
+      <!-- ── Logo ──────────────────────────────────────────────── -->
+      <div class="left-menu-logo-wrapper" :class="{ 'left-menu-logo-wrapper--mini': miniState }">
+        <q-img
+          v-if="!miniState"
+          :src="Logo"
+          fit="contain"
+          class="left-menu-logo"
+        />
+        <q-img
+          v-else
+          :src="LogoSmall"
+          fit="contain"
+          class="left-menu-logo--mini"
+        />
+      </div>
+
+      <!-- ── Toggle expand/collapse ────────────────────────────── -->
+      <div
+        v-if="!$q.screen.lt.md"
+        class="toggle-button-wrapper absolute"
+        style="top: 2px; right: -32px; z-index: 1"
+      >
+        <div @click="miniState = !miniState">
+          <q-icon
+            size="sm"
+            :name="miniState ? 'mdi-page-layout-sidebar-right' : 'mdi-page-layout-sidebar-left'"
+            color="violet-normal"
+          />
+          <q-tooltip anchor="center right" self="center left" :offset="[10, 10]">
+            {{ miniState ? $t('ui.navigation.expand_menu') : $t('ui.navigation.collapse_menu') }}
+          </q-tooltip>
+        </div>
+      </div>
+
+      <!-- ── Menu items (background image section) ─────────────── -->
+      <div class="left-menu-items-wrapper column full-height no-wrap">
+        <div class="left-menu-bg-overlay" />
+        <div class="left-menu-bg-tint" />
+
+        <q-list class="left-menu-list column no-wrap">
+          <template v-for="(item, index) in navigation_store.navigationItems" :key="index">
+            <template v-if="item.permission">
+
+              <!-- Single item -->
+              <q-item
+                v-if="item.type === 'single'"
+                v-ripple
+                clickable
+                :exact="item.name === 'HomePage'"
+                exact-active-class="menu-selected"
+                active-class="menu-selected"
+                :to="{ name: item.name }"
+                class="left-menu-item"
+              >
+                <q-item-section avatar>
+                  <q-icon :name="item.icon" class="left-menu-icon" />
+                </q-item-section>
+                <q-item-section v-if="!miniState">{{ $t(item.title) }}</q-item-section>
+                <q-tooltip
+                  v-if="miniState"
+                  anchor="center right"
+                  self="center left"
+                  :offset="[10, 10]"
+                >{{ $t(item.title) }}</q-tooltip>
+              </q-item>
+
+              <!-- Expansive item -->
+              <div v-else>
+                <template v-if="!miniState">
+                  <q-expansion-item
+                    v-model="isExpasionItemExpanded[index]"
+                    :class="{ 'menu-selected': childrenAreActive(item.childrens) && !isExpasionItemExpanded[index] }"
+                    class="left-menu-item"
+                  >
+                    <template #header>
+                      <q-item-section avatar>
+                        <q-icon :name="item.icon" class="left-menu-icon" />
+                      </q-item-section>
+                      <q-item-section>{{ $t(item.title) }}</q-item-section>
+                    </template>
+                    <div v-for="child in item.childrens" :key="child.name">
+                      <q-item
+                        v-ripple
+                        clickable
+                        :to="{ name: child.name }"
+                        exact
+                        exact-active-class="menu-selected"
+                        class="q-pl-lg left-menu-item"
+                      >
+                        <q-item-section avatar>
+                          <q-icon :name="child.icon" class="left-menu-icon" />
+                        </q-item-section>
+                        <q-item-section>{{ $t(child.title) }}</q-item-section>
+                      </q-item>
+                    </div>
+                  </q-expansion-item>
+                </template>
+                <template v-else>
+                  <q-item
+                    v-ripple
+                    clickable
+                    :class="{ 'menu-selected': childrenAreActive(item.childrens) }"
+                    class="left-menu-item"
+                  >
+                    <q-item-section avatar>
+                      <q-icon :name="item.icon" class="left-menu-icon" />
+                    </q-item-section>
+                    <q-tooltip anchor="center right" self="center left" :offset="[10, 10]">
+                      {{ $t(item.title) }}
+                    </q-tooltip>
+                    <q-menu class="menu-drawer" anchor="center right" self="top start">
+                      <q-list>
+                        <q-item
+                          v-for="child in item.childrens"
+                          :key="child.name"
+                          v-ripple
+                          v-close-popup
+                          clickable
+                          :to="{ name: child.name }"
+                          exact
+                          exact-active-class="menu-selected"
+                          class="menu-drawer"
+                        >
+                          <q-item-section avatar>
+                            <q-icon :name="child.icon" class="left-menu-icon" />
+                          </q-item-section>
+                          <q-item-section>{{ $t(child.title) }}</q-item-section>
+                        </q-item>
+                      </q-list>
+                    </q-menu>
+                  </q-item>
+                </template>
+              </div>
+
+            </template>
+          </template>
+        </q-list>
+
+        <!-- ── Footer ──────────────────────────────────────────── -->
+        <q-list class="q-mt-auto left-menu-footer">
+          <q-item v-ripple class="left-menu-item left-menu-logout text-center" clickable @click="logoutFn">
+            <q-item-section avatar >
+              <q-icon name="logout" class="left-menu-icon left-menu-logout__icon"/>
+            </q-item-section>
+            <q-item-section v-if="!miniState" class="text-violet-normal">{{ $t('auth.logout') }}</q-item-section>
+            <q-tooltip v-if="miniState" anchor="center right" self="center left" :offset="[10, 10]">
+              {{ $t('auth.logout') }}
+            </q-tooltip>
+          </q-item>
+
+          <q-item v-ripple class="left-menu-item" clickable @click="openUrl('https://softpar.inf.br')">
+            <div class="flex full-width justify-center">
+              <q-img
+                :src="miniState || $q.screen.lt.md ? LogoSoftparMini : $q.dark.isActive ? LogoSoftparLight : LogoSoftparDark"
+                style="height: 30px"
+                :style="miniState || $q.screen.lt.md ? 'max-width: 48px; width: 48px' : 'max-width: 100px; width: 100%'"
+              />
+            </div>
+          </q-item>
+        </q-list>
+
+        <div
+          class="full-width text-center q-pb-xs cursor-pointer left-menu-version"
+          @click="gotoVersionPage()"
+        >
+          <span v-if="!(miniState || $q.screen.lt.md)" class="text-caption text-weight-light">
+            {{ $t('common.terms.version') + ' ' + version }}
+          </span>
+          <span v-else class="text-caption text-weight-light">{{ version }}</span>
+        </div>
+      </div>
+
+    </div>
+  </q-drawer>
+</template>
+<script setup>
+import { ref, watch, watchEffect, onMounted } from "vue";
+import { useAuth } from "src/composables/useAuth";
+import { useRouter, useRoute } from "vue-router";
+import { navigationStore } from "src/stores/navigation";
+import { useQuasar, Cookies } from "quasar";
+import { version } from "src/../package.json";
+
+import Logo from "src/assets/logo_serprati_associado.svg";
+import LogoSmall from "src/assets/logo_serprati_associado_small.svg";
+import LogoSoftparLight from "src/assets/softpar_logo_light.svg";
+import LogoSoftparDark from "src/assets/softpar_logo_dark.svg";
+import LogoSoftparMini from "src/assets/softpar_logo_mini.svg";
+
+const { logout } = useAuth();
+const router = useRouter();
+const route = useRoute();
+const navigation_store = navigationStore();
+const $q = useQuasar();
+
+// const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
+//   ? "dark"
+//   : "light";
+// const systemTheme = "light";
+const miniStateCookies = Cookies.get("miniState");
+const miniState = ref(miniStateCookies === "true" ? true : false);
+
+const childrenAreActive = (children) => {
+  if (!Array.isArray(children) || children.length === 0) {
+    return false;
+  }
+  return children.some((child) => route?.name === child?.name);
+};
+
+const isExpasionItemExpanded = ref([]);
+
+watchEffect(() => {
+  if ($q.screen.lt.md) {
+    miniState.value = true;
+    isExpasionItemExpanded.value.forEach((expansion, index) => {
+      isExpasionItemExpanded.value[index] = false;
+    });
+  }
+});
+
+const logoutFn = async () => {
+  await logout();
+  router.push({ name: "LoginPage" });
+};
+
+const openUrl = (url) => {
+  window.open(url, "_blank");
+};
+
+const gotoVersionPage = () => {
+  router.push({ name: "SystemVersionsPage" });
+};
+
+watch(miniState, () => {
+  Cookies.set("miniState", miniState.value, { path: "/", sameSite: "Lax" });
+});
+
+onMounted(() => {
+  navigation_store.navigationItems.forEach((item, index) => {
+    if (childrenAreActive(item.childrens)) {
+      isExpasionItemExpanded.value[index] = true;
+    }
+  });
+});
+</script>
+
+<style lang="scss" scoped>
+@use "sass:map";
+@use "src/css/quasar.variables.scss" as vars;
+
+// ── Drawer container ───────────────────────────────────────────────────────
+:deep(.q-drawer) {
+  background: transparent !important;
+  box-shadow: none !important;
+  border: none !important;
+}
+
+.left-menu-drawer {
+  background: transparent;
+}
+
+.left-menu-inner {
+  height: 100%;
+  background: transparent;
+}
+
+// ── Logo wrapper ───────────────────────────────────────────────────────────
+.left-menu-logo-wrapper {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background-color: vars.$violet-normal;
+  border-radius: 8px 8px 0 0;
+  min-height: 64px;
+  flex-shrink: 0;
+}
+
+.left-menu-logo-wrapper--mini {
+  padding: 10px 8px;
+}
+
+.left-menu-logo {
+  width: 120px;
+  height: 60px;
+}
+
+.left-menu-logo--mini {
+  width: 36px;
+  height: 36px;
+}
+
+.left-menu-items-wrapper {
+  position: relative;
+  flex: 1;
+  overflow: hidden;
+  border-radius: 0 0 8px 8px;
+
+  &::before {
+    content: "";
+    position: absolute;
+    inset: 0;
+    background: linear-gradient(
+      to top,
+      vars.$violet-normal 0%,
+      vars.$violet-light 70%
+    );
+    z-index: 0;
+  }
+
+  .left-menu-bg-overlay,
+  .left-menu-bg-tint {
+    display: none;
+  }
+}
+
+.left-menu-list {
+  position: relative;
+  z-index: 3;
+  padding-top: 8px;
+}
+
+.left-menu-item {
+  position: relative;
+  z-index: 3;
+  color: vars.$violet-normal !important;
+  border-radius: 6px;
+  margin: 2px 8px;
+
+  &:hover {
+    background-color: rgba(vars.$violet-normal, 0.08);
+  }
+}
+
+.left-menu-icon {
+  font-size: 20px;
+  color: vars.$violet-normal;
+}
+
+.menu-selected {
+  background-color: rgba(vars.$violet-normal, 0.12) !important;
+  color: vars.$violet-normal !important;
+}
+
+:deep(.q-expansion-item__header) {
+  color: vars.$violet-normal;
+}
+
+.left-menu-footer {
+  position: relative;
+  z-index: 3;
+  border-top: 1px solid rgba(vars.$neutral-light, 0.3);
+  padding-top: 4px;
+}
+
+.left-menu-logout__icon {
+  color: vars.$neutral-light !important;
+}
+
+.left-menu-version {
+  position: relative;
+  z-index: 3;
+  color: rgba(vars.$neutral-light, 0.7);
+  font-size: 0.75rem;
+  padding-bottom: 6px;
+}
+
+.toggle-button-wrapper {
+  transition: transform 0.3s ease;
+}
+
+.toggle-button-wrapper:hover {
+  transform: scale(1.1);
+}
+</style>

+ 382 - 0
src/components/layout/LeftMenuLayoutParceiro.vue

@@ -0,0 +1,382 @@
+<template>
+  <q-drawer
+    v-bind="$attrs"
+    :model-value="true"
+    show-if-above
+    no-swipe-close
+    no-swipe-open
+    :width="235"
+    :mini-width="64"
+    :breakpoint="500"
+    :mini="miniState"
+    :behavior="'desktop'"
+    class="left-menu-drawer"
+  >
+    <div class="column full-height no-wrap left-menu-inner">
+      <div class="left-menu-logo-wrapper" :class="{ 'left-menu-logo-wrapper--mini': miniState }">
+        <q-img
+          v-if="!miniState"
+          :src="Logo"
+          fit="contain"
+          class="left-menu-logo"
+        />
+        <q-img
+          v-else
+          :src="LogoSmall"
+          fit="contain"
+          class="left-menu-logo--mini"
+        />
+      </div>
+      <div
+        v-if="!$q.screen.lt.md"
+        class="toggle-button-wrapper absolute"
+        style="top: 2px; right: -32px; z-index: 1"
+      >
+        <div @click="miniState = !miniState">
+          <q-icon
+            size="sm"
+            :name="miniState ? 'mdi-page-layout-sidebar-right' : 'mdi-page-layout-sidebar-left'"
+            color="violet-normal"
+          />
+          <q-tooltip anchor="center right" self="center left" :offset="[10, 10]">
+            {{ miniState ? $t('ui.navigation.expand_menu') : $t('ui.navigation.collapse_menu') }}
+          </q-tooltip>
+        </div>
+      </div>
+      <div class="left-menu-items-wrapper column full-height no-wrap">
+        <div class="left-menu-bg-overlay" />
+        <div class="left-menu-bg-tint" />
+        <q-list class="left-menu-list column no-wrap">
+          <template v-for="(item, index) in navigation_store.navigationItems" :key="index">
+            <template v-if="item.permission">
+              <q-item
+                v-if="item.type === 'single'"
+                v-ripple
+                clickable
+                :exact="item.name === 'HomePage'"
+                exact-active-class="menu-selected"
+                active-class="menu-selected"
+                :to="{ name: item.name }"
+                class="left-menu-item"
+              >
+                <q-item-section avatar>
+                  <q-icon :name="item.icon" class="left-menu-icon" />
+                </q-item-section>
+                <q-item-section v-if="!miniState">{{ $t(item.title) }}</q-item-section>
+                <q-tooltip
+                  v-if="miniState"
+                  anchor="center right"
+                  self="center left"
+                  :offset="[10, 10]"
+                >{{ $t(item.title) }}</q-tooltip>
+              </q-item>
+              <div v-else>
+                <template v-if="!miniState">
+                  <q-expansion-item
+                    v-model="isExpasionItemExpanded[index]"
+                    :class="{ 'menu-selected': childrenAreActive(item.childrens) && !isExpasionItemExpanded[index] }"
+                    class="left-menu-item"
+                  >
+                    <template #header>
+                      <q-item-section avatar>
+                        <q-icon :name="item.icon" class="left-menu-icon" />
+                      </q-item-section>
+                      <q-item-section>{{ $t(item.title) }}</q-item-section>
+                    </template>
+                    <div v-for="child in item.childrens" :key="child.name">
+                      <q-item
+                        v-ripple
+                        clickable
+                        :to="{ name: child.name }"
+                        exact
+                        exact-active-class="menu-selected"
+                        class="q-pl-lg left-menu-item"
+                      >
+                        <q-item-section avatar>
+                          <q-icon :name="child.icon" class="left-menu-icon" />
+                        </q-item-section>
+                        <q-item-section>{{ $t(child.title) }}</q-item-section>
+                      </q-item>
+                    </div>
+                  </q-expansion-item>
+                </template>
+                <template v-else>
+                  <q-item
+                    v-ripple
+                    clickable
+                    :class="{ 'menu-selected': childrenAreActive(item.childrens) }"
+                    class="left-menu-item"
+                  >
+                    <q-item-section avatar>
+                      <q-icon :name="item.icon" class="left-menu-icon" />
+                    </q-item-section>
+                    <q-tooltip anchor="center right" self="center left" :offset="[10, 10]">
+                      {{ $t(item.title) }}
+                    </q-tooltip>
+                    <q-menu class="menu-drawer" anchor="center right" self="top start">
+                      <q-list>
+                        <q-item
+                          v-for="child in item.childrens"
+                          :key="child.name"
+                          v-ripple
+                          v-close-popup
+                          clickable
+                          :to="{ name: child.name }"
+                          exact
+                          exact-active-class="menu-selected"
+                          class="menu-drawer"
+                        >
+                          <q-item-section avatar>
+                            <q-icon :name="child.icon" class="left-menu-icon" />
+                          </q-item-section>
+                          <q-item-section>{{ $t(child.title) }}</q-item-section>
+                        </q-item>
+                      </q-list>
+                    </q-menu>
+                  </q-item>
+                </template>
+              </div>
+
+            </template>
+          </template>
+        </q-list>
+        <q-list class="q-mt-auto left-menu-footer">
+          <q-item v-ripple class="left-menu-item left-menu-logout text-center" clickable @click="logoutFn">
+            <q-item-section avatar >
+              <q-icon name="logout" class="left-menu-icon left-menu-logout__icon"/>
+            </q-item-section>
+            <q-item-section v-if="!miniState" class="text-violet-normal">{{ $t('auth.logout') }}</q-item-section>
+            <q-tooltip v-if="miniState" anchor="center right" self="center left" :offset="[10, 10]">
+              {{ $t('auth.logout') }}
+            </q-tooltip>
+          </q-item>
+
+          <q-item v-ripple class="left-menu-item" clickable @click="openUrl('https://softpar.inf.br')">
+            <div class="flex full-width justify-center">
+              <q-img
+                :src="miniState || $q.screen.lt.md ? LogoSoftparMini : $q.dark.isActive ? LogoSoftparLight : LogoSoftparDark"
+                style="height: 30px"
+                :style="miniState || $q.screen.lt.md ? 'max-width: 48px; width: 48px' : 'max-width: 100px; width: 100%'"
+              />
+            </div>
+          </q-item>
+        </q-list>
+
+        <div
+          class="full-width text-center q-pb-xs cursor-pointer left-menu-version"
+          @click="gotoVersionPage()"
+        >
+          <span v-if="!(miniState || $q.screen.lt.md)" class="text-caption text-weight-light">
+            {{ $t('common.terms.version') + ' ' + version }}
+          </span>
+          <span v-else class="text-caption text-weight-light">{{ version }}</span>
+        </div>
+      </div>
+
+    </div>
+  </q-drawer>
+</template>
+<script setup>
+import { ref, watch, watchEffect, onMounted } from "vue";
+import { useAuth } from "src/composables/useAuth";
+import { useRouter, useRoute } from "vue-router";
+import { navigationStore } from "src/stores/navigation";
+import { useQuasar, Cookies } from "quasar";
+import { version } from "src/../package.json";
+
+import Logo from "src/assets/logo_serprati_parceiro.svg";
+import LogoSmall from "src/assets/logo_serprati_parceiro_small.svg";
+import LogoSoftparLight from "src/assets/softpar_logo_light.svg";
+import LogoSoftparDark from "src/assets/softpar_logo_dark.svg";
+import LogoSoftparMini from "src/assets/softpar_logo_mini.svg";
+
+const { logout } = useAuth();
+const router = useRouter();
+const route = useRoute();
+const navigation_store = navigationStore();
+const $q = useQuasar();
+
+// const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
+//   ? "dark"
+//   : "light";
+// const systemTheme = "light";
+const miniStateCookies = Cookies.get("miniState");
+const miniState = ref(miniStateCookies === "true" ? true : false);
+
+const childrenAreActive = (children) => {
+  if (!Array.isArray(children) || children.length === 0) {
+    return false;
+  }
+  return children.some((child) => route?.name === child?.name);
+};
+
+const isExpasionItemExpanded = ref([]);
+
+watchEffect(() => {
+  if ($q.screen.lt.md) {
+    miniState.value = true;
+    isExpasionItemExpanded.value.forEach((expansion, index) => {
+      isExpasionItemExpanded.value[index] = false;
+    });
+  }
+});
+
+const logoutFn = async () => {
+  await logout();
+  router.push({ name: "LoginPage" });
+};
+
+const openUrl = (url) => {
+  window.open(url, "_blank");
+};
+
+const gotoVersionPage = () => {
+  router.push({ name: "SystemVersionsPage" });
+};
+
+watch(miniState, () => {
+  Cookies.set("miniState", miniState.value, { path: "/", sameSite: "Lax" });
+});
+
+onMounted(() => {
+  navigation_store.navigationItems.forEach((item, index) => {
+    if (childrenAreActive(item.childrens)) {
+      isExpasionItemExpanded.value[index] = true;
+    }
+  });
+});
+</script>
+
+<style lang="scss" scoped>
+@use "sass:map";
+@use "src/css/quasar.variables.scss" as vars;
+
+:deep(.q-drawer) {
+  background: transparent !important;
+  box-shadow: none !important;
+  border: none !important;
+}
+
+.left-menu-drawer {
+  background: transparent;
+}
+
+.left-menu-inner {
+  height: 100%;
+  background: transparent;
+}
+
+.left-menu-logo-wrapper {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background-color: vars.$violet-normal;
+  border-radius: 8px 8px 0 0;
+  min-height: 64px;
+  flex-shrink: 0;
+}
+
+.left-menu-logo-wrapper--mini {
+  padding: 10px 8px;
+}
+
+.left-menu-logo {
+  width: 120px;
+  height: 60px;
+}
+
+.left-menu-logo--mini {
+  width: 36px;
+  height: 36px;
+}
+
+.left-menu-items-wrapper {
+  position: relative;
+  flex: 1;
+  overflow: hidden;
+  border-radius: 0 0 8px 8px;
+
+  &::before {
+    content: "";
+    position: absolute;
+    inset: 0;
+    background-color: vars.$violet-light;
+    z-index: 0;
+  }
+
+  .left-menu-bg-overlay {
+    position: absolute;
+    inset: 0;
+    background-image: url("src/assets/pessoas_fundo.jpg");
+    background-size: cover;
+    background-position: 80% top;
+    opacity: 0.15;
+    mix-blend-mode: multiply;
+    z-index: 1;
+    pointer-events: none;
+  }
+
+  .left-menu-bg-tint {
+    display: none;
+  }
+}
+
+.left-menu-list {
+  position: relative;
+  z-index: 3;
+  padding-top: 8px;
+}
+
+.left-menu-item {
+  position: relative;
+  z-index: 3;
+  color: vars.$violet-normal !important;
+  border-radius: 6px;
+  margin: 2px 8px;
+
+  &:hover {
+    background-color: rgba(vars.$violet-normal, 0.08);
+  }
+}
+
+.left-menu-icon {
+  font-size: 20px;
+  color: vars.$violet-normal;
+}
+
+.menu-selected {
+  background-color: rgba(vars.$violet-normal, 0.12) !important;
+  color: vars.$violet-normal !important;
+}
+
+:deep(.q-expansion-item__header) {
+  color: vars.$violet-normal;
+}
+
+.left-menu-footer {
+  position: relative;
+  z-index: 3;
+  border-top: 1px solid rgba(vars.$violet-normal, 0.15);
+  padding-top: 4px;
+}
+
+.left-menu-logout__icon {
+  color: map.get(vars.$colors, "violet-normal") !important;
+}
+
+.left-menu-version {
+  position: relative;
+  z-index: 3;
+  color: rgba(vars.$violet-normal, 0.6);
+  font-size: 0.75rem;
+  padding-bottom: 6px;
+}
+
+.toggle-button-wrapper {
+  transition: transform 0.3s ease;
+}
+
+.toggle-button-wrapper:hover {
+  transform: scale(1.1);
+}
+</style>

+ 38 - 7
src/css/quasar.variables.scss

@@ -1,13 +1,44 @@
 // Quasar SCSS Variables with Material Design Color System
 // --------------------------------------------------
 
-// Primary Theme Colors
-// $primary: #1976d2; // Material Blue 700
-// $secondary: #661D75; // SerpRati Brand Purple
-// $accent: #e91e63; // Material Pink 500
-
-// Dark Theme Base Colors
-// $dark: #1d1d1d;
+// ─── Standalone variables (usable directly in SCSS via $var-name) ────────────
+
+// Brand
+$primary:             #35a30a;
+$primary-4:           #cde8c2;
+
+// Violet (SerpRati brand)
+$violet-light:        #f0e8f1;
+$violet-light-hover:  #e8ddea;
+$violet-light-active: #d0b9d4;
+$violet-normal:       #661d75;
+$violet-normal-hover: #5c1a69;
+$violet-normal-active:#52175e;
+$violet-dark:         #4d1658;
+$violet-dark-hover:   #3d1146;
+$violet-dark-active:  #2e0d35;
+$violet-darker:       #240a29;
+
+// Neutral
+$neutral-light:        #fefefe;
+$neutral-light-hover:  #fdfdfd;
+$neutral-light-active: #fbfbfb;
+$neutral-normal:       #f2f2f2;
+$neutral-normal-hover: #dadada;
+$neutral-normal-active:#c2c2c2;
+$neutral-dark:         #b6b6b6;
+$neutral-dark-hover:   #919191;
+$neutral-dark-active:  #6d6d6d;
+$neutral-darker:       #555555;
+
+// Text
+$color-text:  #161616;
+$color-text-2:#505050;
+$white-2:     #fefcff;
+$color-border:#c0c0c0;
+
+// Dark base
+$dark: #1d1d1d;
 
 // Status Colors
 $positive: #2e7d32; // Material Green 800

+ 10 - 2
src/i18n/locales/en.json

@@ -340,7 +340,7 @@
       "state": "State",
       "country": "Country",
       "exit": "Exit",
-      "partners": "Partners & Agreements",
+      "partners": "Partners/Agreements",
       "partner_agreements": "Agreements",
       "partner_services": "Services",
       "store": "Store",
@@ -352,7 +352,15 @@
       "notifications": "Notifications",
       "categories": "Categories",
       "my_profile": "My Profile",
-      "my_services": "My Services"
+      "my_services": "My Services",
+      "associados": "Member Management",
+      "relatorios": "Reports",
+      "validar_carteirinha": "Validate Card",
+      "meus_dados": "My Data",
+      "meu_perfil": "My Profile",
+      "carteirinha": "Membership Card",
+      "convenios": "Agreements",
+      "dependentes": "Dependents"
     }
   },
   "charts": {

+ 10 - 2
src/i18n/locales/es.json

@@ -340,7 +340,7 @@
       "state": "Estado/Provincia",
       "country": "País",
       "exit": "Salir",
-      "partners": "Socios y Convenios",
+      "partners": "Socios/Convenios",
       "partner_agreements": "Convenios",
       "partner_services": "Servicios",
       "store": "Tienda",
@@ -352,7 +352,15 @@
       "notifications": "Notificaciones",
       "categories": "Categorías",
       "my_profile": "Mi Perfil",
-      "my_services": "Mis Servicios"
+      "my_services": "Mis Servicios",
+      "associados": "Gestión de Asociados",
+      "relatorios": "Informes",
+      "validar_carteirinha": "Validar Carnet",
+      "meus_dados": "Mis Datos",
+      "meu_perfil": "Mi Perfil",
+      "carteirinha": "Carnet de Asociado",
+      "convenios": "Convenios",
+      "dependentes": "Dependientes"
     }
   },
   "charts": {

+ 11 - 3
src/i18n/locales/pt.json

@@ -317,7 +317,7 @@
       "home": "Início",
       "versions": "Versões",
       "expand_menu": "Expandir menu",
-      "dashboard": "Painel",
+      "dashboard": "Dashboard",
       "explore": "Explorar",
       "advertise": "Anunciar",
       "my_advertisements": "Meus Anúncios",
@@ -340,7 +340,7 @@
       "state": "Estados",
       "country": "Países",
       "exit": "Sair",
-      "partners": "Parceiros e Convênios",
+      "partners": "Parceiros/Convênios",
       "partner_agreements": "Convênios",
       "partner_services": "Serviços",
       "store": "Loja",
@@ -352,7 +352,15 @@
       "notifications": "Notificações",
       "categories": "Categorias",
       "my_profile": "Meu Perfil",
-      "my_services": "Meus Serviços"
+      "my_services": "Meus Serviços",
+      "associados": "Gestão Associados",
+      "relatorios": "Relatórios",
+      "validar_carteirinha": "Validar Carteirinha",
+      "meus_dados": "Meus Dados",
+      "meu_perfil": "Meu Perfil",
+      "carteirinha": "Carteirinha",
+      "convenios": "Convênios",
+      "dependentes": "Dependentes"
     }
   },
   "charts": {

+ 13 - 7
src/layouts/MainLayout.vue

@@ -1,7 +1,9 @@
 <template>
   <q-layout class="relative" view="hHh lpR fFf">
-    <LeftMenuLayout v-if="!$q.screen.lt.sm" />
-    <LeftMenuLayoutMobile v-else v-model="leftDrawerOpen" />
+    <LeftMenuLayoutAdministrador v-if="!$q.screen.lt.sm && store.userTipo == 'administrador'" />
+    <LeftMenuLayoutAssociado v-if="!$q.screen.lt.sm && store.userTipo == 'associado'" />
+    <LeftMenuLayoutParceiro v-if="!$q.screen.lt.sm && store.userTipo == 'parceiro'" />
+    <LeftMenuLayoutMobile v-if="$q.screen.lt.sm" v-model="leftDrawerOpen" />
     <q-header v-if="$q.screen.lt.sm" class="bg-transparent q-pa-sm">
       <q-toolbar
         class="flex justify-between bg-surface"
@@ -60,10 +62,13 @@ import { ref, useTemplateRef, watch } from "vue";
 import { useRoute, useRouter } from "vue-router";
 import { useAuth } from "src/composables/useAuth";
 import { useQuasar } from "quasar";
-
-import LeftMenuLayout from "src/components/layout/LeftMenuLayout.vue";
+import { userStore } from "src/stores/user";
+import LeftMenuLayoutAdministrador from "src/components/layout/LeftMenuLayoutAdministrador.vue";
+import LeftMenuLayoutAssociado from "src/components/layout/LeftMenuLayoutAssociado.vue";
+import LeftMenuLayoutParceiro from "src/components/layout/LeftMenuLayoutParceiro.vue";
 import LeftMenuLayoutMobile from "src/components/layout/LeftMenuLayoutMobile.vue";
 
+const store = userStore();
 const router = useRouter();
 const { logout } = useAuth();
 const route = useRoute();
@@ -73,9 +78,10 @@ const leftDrawerOpen = ref(false);
 const scrollAreaRef = useTemplateRef("scrollAreaRef");
 
 let oldValue = route.path;
-const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
-  ? "dark"
-  : "light";
+// const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
+//   ? "dark"
+//   : "light";
+const systemTheme = "light";
 
 const logoutFn = async () => {
   await logout();

+ 6 - 351
src/pages/dashboard/DashboardPage.vue

@@ -1,168 +1,10 @@
 <template>
   <div>
     <DefaultHeaderPage>
-      <template #after>
-        <q-btn
-          outline
-          icon="mdi-calendar"
-          color="primary"
-          @click="showFilter"
-        />
-      </template>
     </DefaultHeaderPage>
-    <q-expansion-item
-      v-model="filter"
-      dense
-      hide-expand-icon
-      class="remove-header-expansion-item"
-    >
-      <DatePeriodSelector
-        v-model:selected-period="defaultPeriod"
-        v-model:selected-event-id="defaultEventId"
-        class="q-pa-sm"
-      />
-    </q-expansion-item>
 
     <div v-if="!isLoading" class="column gap q-pa-sm">
-      <div class="flex full-width gap">
-        <div class="flex flex-grow gap">
-          <CardIconMiniChart
-            class="flex-grow"
-            :title="t('dashboard.cards.total_earnings')"
-            :icon="'mdi-currency-usd'"
-            :number-porcent="paymentsChart.percentage_change"
-            :number-card="
-              t('dashboard.currency_format', {
-                value: paymentsChart.current_total,
-              })
-            "
-          >
-            <template #chart>
-              <MiniLineChart
-                :data="paymentsChart.trend_data"
-                fill-color="rgba(0, 0, 0, 0)"
-              />
-            </template>
-          </CardIconMiniChart>
-          <CardIconMiniChart
-            class="flex-grow"
-            :title="t('orders.plural')"
-            :icon="'mdi-package-variant'"
-            :number-porcent="ordersChart.percentage_change"
-            :number-card="ordersChart.current_total"
-          >
-            <template #chart>
-              <MiniBarChart
-                :data="ordersChart.trend_data"
-                fill-color="rgba(0, 0, 0, 0)"
-              />
-            </template>
-          </CardIconMiniChart>
-        </div>
-        <div class="flex flex-grow gap">
-          <CardIconMiniChart
-            class="flex-grow"
-            :title="t('dashboard.cards.tickets_sold')"
-            :icon="'mdi-ticket-outline'"
-            :number-porcent="ticketsSoldChart.percentage_change"
-            :number-card="ticketsSoldChart.current_total"
-          >
-            <template #chart>
-              <MiniLineChart
-                :data="ticketsSoldChart.trend_data"
-                fill-color="rgba(0, 0, 0, 0)"
-              />
-            </template>
-          </CardIconMiniChart>
-          <CardIconMiniChart
-            class="flex-grow"
-            :title="t('dashboard.cards.registrations')"
-            :icon="'mdi-account-group-outline'"
-            :number-porcent="participantsChart.percentage_change"
-            :number-card="participantsChart.current_total"
-          >
-            <template #chart>
-              <MiniBarChart
-                :data="participantsChart.trend_data"
-                fill-color="rgba(0, 0, 0, 0)"
-              />
-            </template>
-          </CardIconMiniChart>
-        </div>
-      </div>
-
-      <div class="flex full-width gap">
-        <div class="flex flex-grow">
-          <CardIconChart
-            :title="t('dashboard.charts.tickets_by_type.title')"
-            :icon="'mdi-ticket-account'"
-            class="flex-grow"
-          >
-            <template #chart>
-              <BarChart
-                :data="eventTicketsByTypeChart"
-                :data-set-label="t('events.tickets.plural')"
-                :label-x="t('events.tickets.types_singular')"
-                :label-y="t('common.terms.quantity')"
-                :show-legend="true"
-              />
-            </template>
-          </CardIconChart>
-        </div>
-        <div class="flex flex-grow">
-          <CardIconChart
-            :title="t('dashboard.charts.participants_by_document.title')"
-            :icon="'mdi-badge-account'"
-            class="flex-grow"
-          >
-            <template #chart>
-              <DoughnutChart
-                :data="eventParticipantsByCNPJAndCPF"
-                :data-set-label="t('events.attendance.participant_plural')"
-              />
-            </template>
-          </CardIconChart>
-        </div>
-      </div>
-
-      <div class="flex full-width gap">
-        <div class="flex flex-grow">
-          <CardIconChart
-            :title="t('dashboard.charts.sales_over_time.title')"
-            :icon="'mdi-chart-line'"
-            class="flex-grow"
-          >
-            <template #chart>
-              <LineChart
-                :data="salesOverTimeLineChart"
-                :data-set-label="t('ui.navigation.sales')"
-                :label-x="t('common.terms.month')"
-                :label-y="
-                  t('dashboard.charts.sales_over_time.y_label', {
-                    currency: 'R$',
-                  })
-                "
-              />
-            </template>
-          </CardIconChart>
-        </div>
-        <div class="flex flex-grow">
-          <CardIconChart
-            :title="t('dashboard.charts.registration_source.title')"
-            :icon="'mdi-chart-pie'"
-            class="flex-grow"
-          >
-            <template #chart>
-              <PieChart
-                :data="eventSourcePieChart"
-                :data-set-label="
-                  t('dashboard.charts.registration_source.source')
-                "
-              />
-            </template>
-          </CardIconChart>
-        </div>
-      </div>
+      <div>Administrador: {{ store.userTipo }}</div>
     </div>
 
     <div v-else class="flex flex-center full-width q-pa-xl">
@@ -172,205 +14,18 @@
 </template>
 
 <script setup>
-import { onMounted, ref, watch, defineAsyncComponent } from "vue";
-import { useI18n } from "vue-i18n";
+import { onMounted, ref } from "vue";
 import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
-import DatePeriodSelector from "./components/DatePeriodSelector.vue";
-
-const MiniLineChart = defineAsyncComponent(
-  () => import("src/components/charts/mini/MiniLineChart.vue"),
-);
-const MiniBarChart = defineAsyncComponent(
-  () => import("src/components/charts/mini/MiniBarChart.vue"),
-);
-const CardIconMiniChart = defineAsyncComponent(
-  () => import("src/components/charts/CardIconMiniChart.vue"),
-);
-const CardIconChart = defineAsyncComponent(
-  () => import("src/components/charts/CardIconChart.vue"),
-);
-const BarChart = defineAsyncComponent(
-  () => import("src/components/charts/normal/BarChart.vue"),
-);
-const DoughnutChart = defineAsyncComponent(
-  () => import("src/components/charts/normal/DoughnutChart.vue"),
-);
-const LineChart = defineAsyncComponent(
-  () => import("src/components/charts/normal/LineChart.vue"),
-);
-const PieChart = defineAsyncComponent(
-  () => import("src/components/charts/normal/PieChart.vue"),
-);
+import { userStore } from "src/stores/user";
 
-const { t } = useI18n();
+const store = userStore();
 
 const isLoading = ref(true);
-const filter = ref(false);
-const defaultPeriod = ref("month");
-const defaultEventId = ref(1);
-
-const ordersChart = ref({});
-const participantsChart = ref({});
-const paymentsChart = ref({});
-const ticketsSoldChart = ref({});
-const eventTicketsByTypeChart = ref({});
-const eventParticipantsByCNPJAndCPF = ref({});
-const salesOverTimeLineChart = ref({});
-const eventSourcePieChart = ref({});
-
-const showFilter = () => {
-  filter.value = !filter.value;
-};
-
-const generateMockData = () => {
-  const createMiniChartData = (currentTotal, percentage) => ({
-    current_total: currentTotal,
-    percentage_change: percentage,
-    trend_data: Array.from({ length: 10 }, () =>
-      Math.floor(Math.random() * 100),
-    ),
-  });
-
-  const barChartDataRaw = [
-    {
-      label: t("dashboard.charts.tickets_by_type.labels.vip"),
-      value: Math.floor(Math.random() * 300),
-    },
-    {
-      label: t("dashboard.charts.tickets_by_type.labels.track"),
-      value: Math.floor(Math.random() * 800),
-    },
-    {
-      label: t("dashboard.charts.tickets_by_type.labels.box"),
-      value: Math.floor(Math.random() * 400),
-    },
-    {
-      label: t("dashboard.charts.tickets_by_type.labels.courtesy"),
-      value: Math.floor(Math.random() * 50),
-    },
-  ];
-
-  const doughnutDataRaw = [
-    {
-      label: t("common.terms.cpf"),
-      value: Math.floor(Math.random() * 900 + 100),
-    },
-    {
-      label: t("common.terms.cnpj"),
-      value: Math.floor(Math.random() * 100 + 10),
-    },
-  ];
-  const doughnutTotal = doughnutDataRaw.reduce(
-    (sum, item) => sum + item.value,
-    0,
-  );
-
-  const lineChartDataRaw = [
-    {
-      label: t("common.months.january"),
-      value: Math.floor(1200 + Math.random() * 500),
-    },
-    {
-      label: t("common.months.february"),
-      value: Math.floor(1900 + Math.random() * 500),
-    },
-    {
-      label: t("common.months.march"),
-      value: Math.floor(3000 + Math.random() * 500),
-    },
-    {
-      label: t("common.months.april"),
-      value: Math.floor(5000 + Math.random() * 500),
-    },
-    {
-      label: t("common.months.may"),
-      value: Math.floor(2300 + Math.random() * 500),
-    },
-    {
-      label: t("common.months.june"),
-      value: Math.floor(3200 + Math.random() * 500),
-    },
-  ];
-
-  const pieDataRaw = [
-    {
-      label: t("dashboard.charts.registration_source.sources.instagram"),
-      value: Math.floor(450 + Math.random() * 50),
-    },
-    {
-      label: t("dashboard.charts.registration_source.sources.facebook"),
-      value: Math.floor(250 + Math.random() * 50),
-    },
-    {
-      label: t("dashboard.charts.registration_source.sources.google"),
-      value: Math.floor(180 + Math.random() * 50),
-    },
-    {
-      label: t("dashboard.charts.registration_source.sources.referral"),
-      value: Math.floor(120 + Math.random() * 50),
-    },
-  ];
-  const pieTotal = pieDataRaw.reduce((sum, item) => sum + item.value, 0);
 
-  return {
-    payments: createMiniChartData(
-      (Math.random() * 20000 + 5000).toFixed(2),
-      (Math.random() * 20 - 5).toFixed(2),
-    ),
-    orders: createMiniChartData(
-      Math.floor(Math.random() * 500 + 50),
-      (Math.random() * 15 - 5).toFixed(2),
-    ),
-    tickets_sold: createMiniChartData(
-      Math.floor(Math.random() * 1500 + 200),
-      (Math.random() * 25 - 5).toFixed(2),
-    ),
-    participants: createMiniChartData(
-      Math.floor(Math.random() * 1000 + 100),
-      (Math.random() * 10 - 5).toFixed(2),
-    ),
-    barData: {
-      chart_data: barChartDataRaw,
-    },
-    doughnutData: {
-      chart_data: doughnutDataRaw,
-      current_total: doughnutTotal,
-    },
-    lineData: {
-      chart_data: lineChartDataRaw,
-    },
-    pieData: {
-      chart_data: pieDataRaw,
-      current_total: pieTotal,
-    },
-  };
-};
-
-const updateDashboardData = async () => {
-  isLoading.value = true;
+onMounted(async () => {
   setTimeout(() => {
-    const mockData = generateMockData();
-
-    ordersChart.value = mockData.orders;
-    participantsChart.value = mockData.participants;
-    paymentsChart.value = mockData.payments;
-    ticketsSoldChart.value = mockData.tickets_sold;
-
-    eventTicketsByTypeChart.value = mockData.barData;
-    eventParticipantsByCNPJAndCPF.value = mockData.doughnutData;
-    salesOverTimeLineChart.value = mockData.lineData;
-    eventSourcePieChart.value = mockData.pieData;
-
     isLoading.value = false;
-  }, 500);
-};
-
-watch([defaultPeriod, defaultEventId], async () => {
-  await updateDashboardData();
-});
-
-onMounted(async () => {
-  await updateDashboardData();
+  }, 1000);
 });
 </script>
 

+ 4 - 5
src/pages/home/HomePage.vue

@@ -1,13 +1,12 @@
 <template>
   <div class="column flex-center flex-grow">
     <span class="text-h4 text-primary q-mb-md">
-      {{ $t("common.ui.messages.welcome") + " " + $t("common.ui.table.to") }}
+      {{ store.userTipo + ' ' + $t("common.ui.messages.welcome") }}
     </span>
-    <div class="column flex-center full-width q-px-md">
-      <q-img :src="Logo" style="max-width: 430px" />
-    </div>
   </div>
 </template>
 <script setup>
-import Logo from "src/assets/logo.png";
+import { userStore } from "src/stores/user";
+
+const store = userStore();
 </script>

+ 60 - 134
src/stores/navigation.js

@@ -5,119 +5,41 @@ import { userStore } from "src/stores/user";
 
 export const navigationStore = defineStore("navigation", () => {
   const navigationStructure = Object.freeze([
-    // ─── Comum ───────────────────────────────────────────────
-    {
-      type: "single",
-      title: "ui.navigation.home",
-      name: "HomePage",
-      icon: "mdi-home-outline",
-      permission: true,
-      allowedTypes: [],
-    },
-
-    // ─── ADMINISTRADOR ───────────────────────────────────────────────
     {
       type: "single",
       title: "ui.navigation.dashboard",
       name: "DashboardPage",
-      icon: "mdi-poll",
+      icon: "mdi-view-dashboard-outline",
       permission: false,
       permissionScope: "dashboard",
       allowedTypes: ["administrador"],
     },
     {
-      type: "expansive",
-      title: "ui.navigation.registration",
-      icon: "mdi-plus",
+      type: "single",
+      title: "ui.navigation.associados",
+      name: "GestaoAssociadosPage",
+      icon: "mdi-account-group-outline",
       permission: false,
-      permissionScope: "config",
+      permissionScope: "associado",
       allowedTypes: ["administrador"],
-      childrens: [
-        {
-          type: "single",
-          title: "ui.navigation.users",
-          name: "UsersPage",
-          icon: "mdi-account-multiple-outline",
-          permission: false,
-          permissionScope: "config.user",
-        },
-        {
-          type: "single",
-          title: "ui.navigation.city",
-          name: "CityPage",
-          icon: "mdi-city-variant-outline",
-          permission: false,
-          permissionScope: "config.city",
-        },
-        {
-          type: "single",
-          title: "ui.navigation.country",
-          name: "CountryPage",
-          icon: "mdi-earth",
-          permission: false,
-          permissionScope: "config.country",
-        },
-        {
-          type: "single",
-          title: "ui.navigation.state",
-          name: "StatePage",
-          icon: "mdi-map-marker",
-          permission: false,
-          permissionScope: "config.state",
-        },
-      ],
     },
     {
-      type: "expansive",
+      type: "single",
       title: "ui.navigation.partners",
-      icon: "mdi-handshake-outline",
+      name: "ParceirosConveniosPage",
+      icon: "mdi-heart-outline",
       permission: false,
       permissionScope: "parceiro",
       allowedTypes: ["administrador"],
-      childrens: [
-        {
-          type: "single",
-          title: "ui.navigation.partner_agreements",
-          name: "PartnerAgreementsPage",
-          icon: "mdi-office-building-outline",
-          permission: false,
-          permissionScope: "parceiro.convenio",
-        },
-        {
-          type: "single",
-          title: "ui.navigation.partner_services",
-          name: "PartnerServicesPage",
-          icon: "mdi-briefcase-outline",
-          permission: false,
-          permissionScope: "parceiro.servico",
-        },
-      ],
     },
     {
-      type: "expansive",
+      type: "single",
       title: "ui.navigation.store",
-      icon: "mdi-store-outline",
+      name: "LojaPage",
+      icon: "mdi-cart-outline",
       permission: false,
       permissionScope: "loja",
       allowedTypes: ["administrador"],
-      childrens: [
-        {
-          type: "single",
-          title: "ui.navigation.store_items",
-          name: "StoreItemsPage",
-          icon: "mdi-package-variant-closed",
-          permission: false,
-          permissionScope: "loja.item",
-        },
-        {
-          type: "single",
-          title: "ui.navigation.store_orders",
-          name: "StoreOrdersPage",
-          icon: "mdi-cart-outline",
-          permission: false,
-          permissionScope: "loja.pedido",
-        },
-      ],
     },
     {
       type: "single",
@@ -139,57 +61,62 @@ export const navigationStore = defineStore("navigation", () => {
     },
     {
       type: "single",
-      title: "ui.navigation.categories",
-      name: "CategoriesPage",
-      icon: "mdi-tag-multiple-outline",
+      title: "ui.navigation.relatorios",
+      name: "relatorios",
+      icon: "mdi-chart-bar",
       permission: false,
-      permissionScope: "categoria",
+      permissionScope: "relatorio",
       allowedTypes: ["administrador"],
     },
-
-    // ─── ASSOCIADO ───────────────────────────────────────────────────
     {
       type: "single",
-      title: "ui.navigation.dashboard",
-      name: "DashboardPage",
-      icon: "mdi-view-dashboard-outline",
+      title: "ui.navigation.meu_perfil",
+      name: "MeuPerfilPage",
+      icon: "mdi-account-outline",
       permission: false,
-      permissionScope: "dashboard",
+      permissionScope: "associado.perfil",
       allowedTypes: ["associado"],
     },
     {
       type: "single",
-      title: "ui.navigation.partner_agreements",
-      name: "PartnerAgreementsPage",
-      icon: "mdi-handshake-outline",
+      title: "ui.navigation.carteirinha",
+      name: "CarteirinhaPage",
+      icon: "mdi-card-account-details-outline",
       permission: false,
-      permissionScope: "parceiro.convenio",
+      permissionScope: "associado.carteirinha",
       allowedTypes: ["associado"],
     },
     {
       type: "single",
-      title: "ui.navigation.store",
-      name: "StoreItemsPage",
-      icon: "mdi-store-outline",
+      title: "ui.navigation.convenios",
+      name: "ConveniosPage",
+      icon: "mdi-heart-outline",
       permission: false,
-      permissionScope: "loja.item",
+      permissionScope: "associado.convenio",
       allowedTypes: ["associado"],
     },
     {
       type: "single",
-      title: "ui.navigation.my_appointments",
-      name: "AppointmentsPage",
-      icon: "mdi-calendar-check-outline",
+      title: "ui.navigation.notifications",
+      name: "NotificacoesPage",
+      icon: "mdi-bell-outline",
       permission: false,
-      permissionScope: "agendamento",
+      permissionScope: "notificacao",
+      allowedTypes: ["associado"],
+    },
+    {
+      type: "single",
+      title: "ui.navigation.dependentes",
+      name: "DependentesPage",
+      icon: "mdi-account-multiple-outline",
+      permission: false,
+      permissionScope: "associado.dependente",
       allowedTypes: ["associado"],
     },
-
-    // ─── PARCEIRO ────────────────────────────────────────────────────
     {
       type: "single",
       title: "ui.navigation.dashboard",
-      name: "DashboardPage",
+      name: "HomePage",
       icon: "mdi-view-dashboard-outline",
       permission: false,
       permissionScope: "dashboard",
@@ -197,40 +124,39 @@ export const navigationStore = defineStore("navigation", () => {
     },
     {
       type: "single",
-      title: "ui.navigation.my_profile",
-      name: "PartnerAgreementsPage",
-      icon: "mdi-office-building-outline",
+      title: "ui.navigation.validar_carteirinha",
+      name: "ValidarCarteirinhaPage",
+      icon: "mdi-check",
       permission: false,
-      permissionScope: "parceiro.convenio",
+      permissionScope: "parceiro.carteirinha",
       allowedTypes: ["parceiro"],
     },
     {
       type: "single",
-      title: "ui.navigation.my_services",
-      name: "PartnerServicesPage",
-      icon: "mdi-briefcase-outline",
+      title: "ui.navigation.appointments",
+      name: "AgendamentosPage",
+      icon: "mdi-calendar-clock-outline",
       permission: false,
-      permissionScope: "parceiro.servico",
+      permissionScope: "parceiro.agendamento",
       allowedTypes: ["parceiro"],
     },
     {
       type: "single",
-      title: "ui.navigation.received_appointments",
-      name: "AppointmentsPage",
-      icon: "mdi-calendar-clock-outline",
+      title: "ui.navigation.meus_dados",
+      name: "MeusDadosPage",
+      icon: "mdi-account-box-outline",
       permission: false,
-      permissionScope: "agendamento",
+      permissionScope: "parceiro.dados",
       allowedTypes: ["parceiro"],
     },
-
-    // ─── Comum (rodapé) ───────────────────────────────────────
     {
       type: "single",
-      title: "ui.navigation.versions",
-      name: "SystemVersionsPage",
-      icon: "mdi-information-outline",
-      permission: true,
-      allowedTypes: [],
+      title: "ui.navigation.notifications",
+      name: "NotificacoesPage",
+      icon: "mdi-bell-outline",
+      permission: false,
+      permissionScope: "parceiro.notificacao",
+      allowedTypes: ["parceiro"],
     },
   ]);
 

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio