AppHeader.vue 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. <template>
  2. <q-toolbar class="app-header">
  3. <div class="app-header__left row items-center no-wrap">
  4. <q-img :src="LogoSmall" fit="contain" class="app-header__logo" />
  5. <q-btn
  6. flat
  7. round
  8. dense
  9. class="app-header__toggle q-ml-xs"
  10. :icon="navStore.miniState ? 'mdi-chevron-right' : 'mdi-chevron-left'"
  11. @click="navStore.toggleMini()"
  12. />
  13. </div>
  14. <div class="app-header__type-label text-white text-weight-medium">
  15. {{ userTypeLabel }}
  16. </div>
  17. <q-space />
  18. <q-btn
  19. flat
  20. dense
  21. no-caps
  22. class="app-header__logout q-mr-sm"
  23. icon="logout"
  24. :label="$t('auth.logout')"
  25. @click="logoutFn"
  26. />
  27. <q-avatar size="36px" class="app-header__avatar">
  28. <img v-if="store.user?.photo_url" :src="store.user.photo_url" style="object-fit: cover; width: 100%; height: 100%;" />
  29. <span v-else class="app-header__avatar-initial">{{ userInitial }}</span>
  30. <q-menu anchor="bottom right" self="top right" class="app-header__lang-menu">
  31. <q-list dense style="min-width: 160px">
  32. <q-item
  33. v-for="lang in languages"
  34. :key="lang.code"
  35. v-close-popup
  36. clickable
  37. :active="currentLocaleBase === lang.code"
  38. active-class="app-header__lang-item--active"
  39. @click="changeLocale(lang.code)"
  40. >
  41. <q-item-section side class="q-pr-sm">
  42. <span class="app-header__lang-flag">{{ lang.flag }}</span>
  43. </q-item-section>
  44. <q-item-section>{{ lang.label }}</q-item-section>
  45. </q-item>
  46. </q-list>
  47. </q-menu>
  48. </q-avatar>
  49. </q-toolbar>
  50. </template>
  51. <script setup>
  52. import { computed } from "vue";
  53. import { useI18n } from "vue-i18n";
  54. import { useRouter } from "vue-router";
  55. import { Cookies } from "quasar";
  56. import { i18n } from "src/boot/i18n";
  57. import { useAuth } from "src/composables/useAuth";
  58. import { userStore } from "src/stores/user";
  59. import { navigationStore } from "src/stores/navigation";
  60. import LogoSmall from "src/assets/logo_serprati_small.png";
  61. const store = userStore();
  62. const navStore = navigationStore();
  63. const router = useRouter();
  64. const { logout } = useAuth();
  65. const { locale } = useI18n();
  66. const logoutFn = async () => {
  67. await logout();
  68. router.push({ name: "LoginPage" });
  69. };
  70. const languages = [
  71. { code: "pt", label: "Português", flag: "🇧🇷" },
  72. { code: "en", label: "English", flag: "🇺🇸" },
  73. { code: "es", label: "Español", flag: "🇪🇸" },
  74. ];
  75. const currentLocaleBase = computed(() =>
  76. locale.value?.split("-")[0].toLowerCase()
  77. );
  78. const changeLocale = (code) => {
  79. i18n.global.locale.value = code;
  80. Cookies.set("locale", code, { expires: 365, path: "/" });
  81. };
  82. const userTypeLabel = computed(() => {
  83. const t = store.userTipo;
  84. if (t === "administrador") return "Administração";
  85. if (t === "associado") return "Associado";
  86. if (t === "parceiro") return "Parceiro";
  87. return "";
  88. });
  89. const userInitial = computed(() => {
  90. const name = store.user?.name || store.user?.email || "";
  91. return name.charAt(0).toUpperCase();
  92. });
  93. </script>
  94. <style lang="scss" scoped>
  95. @use "src/css/quasar.variables.scss" as vars;
  96. .app-header {
  97. height: 52px;
  98. min-height: 52px;
  99. background: linear-gradient(90deg, vars.$violet-darker 0%, #0f0215 100%);
  100. padding: 0 16px;
  101. }
  102. .app-header__left {
  103. flex-shrink: 0;
  104. }
  105. .app-header__logo {
  106. width: 36px;
  107. height: 36px;
  108. }
  109. .app-header__toggle {
  110. color: rgba(white, 0.75) !important;
  111. &:hover {
  112. color: white !important;
  113. background: rgba(white, 0.1) !important;
  114. }
  115. }
  116. .app-header__type-label {
  117. font-size: 14px;
  118. letter-spacing: 0.3px;
  119. opacity: 0.9;
  120. margin-left: 16px;
  121. }
  122. .app-header__logout {
  123. color: white !important;
  124. font-size: 14px;
  125. :deep(.q-icon) {
  126. font-size: 20px;
  127. }
  128. &:hover {
  129. background: rgba(white, 0.1) !important;
  130. }
  131. }
  132. .app-header__avatar {
  133. background: rgba(white, 0.15);
  134. // border: 2px solid rgba(white, 0.3);
  135. cursor: pointer;
  136. flex-shrink: 0;
  137. }
  138. .app-header__avatar-initial {
  139. color: white;
  140. font-size: 15px;
  141. font-weight: 600;
  142. }
  143. .app-header__lang-flag {
  144. font-size: 16px;
  145. line-height: 1;
  146. }
  147. .app-header__lang-item--active {
  148. background: rgba(vars.$violet-normal, 0.1);
  149. color: vars.$violet-normal;
  150. font-weight: 600;
  151. }
  152. </style>