Преглед изворни кода

Merge branch 'feature/SERPRATI-GUS-plataforma-v1' of gogs.softpar.inf.br:Softpar/sfp_front_vue_serprati_digital into feature/SERPRATI-GUS-plataforma-v1

Gustavo Zanatta пре 3 недеља
родитељ
комит
12069a8730

+ 17 - 1
package-lock.json

@@ -2618,6 +2618,7 @@
       "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==",
       "devOptional": true,
       "license": "MIT",
+      "peer": true,
       "dependencies": {
         "@types/body-parser": "*",
         "@types/express-serve-static-core": "^4.17.33",
@@ -2938,6 +2939,7 @@
       "integrity": "sha512-TlGPkLFLVOY3T7fZrwdvKpjprR3s4fxRln0ORDo1VQ7HHyxJwTlrjKU3kpVWTlaAjIEuCTokmjkZnr8Tpc925w==",
       "dev": true,
       "license": "MIT",
+      "peer": true,
       "dependencies": {
         "@rolldown/pluginutils": "1.0.0-beta.53"
       },
@@ -3138,6 +3140,7 @@
       "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
       "dev": true,
       "license": "MIT",
+      "peer": true,
       "bin": {
         "acorn": "bin/acorn"
       },
@@ -3725,6 +3728,7 @@
         }
       ],
       "license": "MIT",
+      "peer": true,
       "dependencies": {
         "baseline-browser-mapping": "^2.9.0",
         "caniuse-lite": "^1.0.30001759",
@@ -3952,6 +3956,7 @@
       "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz",
       "integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
       "license": "MIT",
+      "peer": true,
       "dependencies": {
         "@kurkle/color": "^0.3.0"
       },
@@ -4983,6 +4988,7 @@
       "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
       "dev": true,
       "license": "MIT",
+      "peer": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.8.0",
         "@eslint-community/regexpp": "^4.12.1",
@@ -5059,6 +5065,7 @@
       "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
       "dev": true,
       "license": "MIT",
+      "peer": true,
       "bin": {
         "eslint-config-prettier": "bin/cli.js"
       },
@@ -6708,6 +6715,7 @@
       "integrity": "sha512-1e4qoRgnn448pRuMvKGsFFymUCquZV0mpGgOyIKNgD3JVDTsVJyRBGH/Fm0tBb8WsWGgmB1mDe6/yJMQM37DUA==",
       "dev": true,
       "license": "MIT",
+      "peer": true,
       "dependencies": {
         "acorn": "^8.5.0",
         "eslint-visitor-keys": "^3.0.0",
@@ -7625,6 +7633,7 @@
       "resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.4.tgz",
       "integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==",
       "license": "MIT",
+      "peer": true,
       "dependencies": {
         "@vue/devtools-api": "^7.7.7"
       },
@@ -7688,6 +7697,7 @@
         }
       ],
       "license": "MIT",
+      "peer": true,
       "dependencies": {
         "nanoid": "^3.3.11",
         "picocolors": "^1.1.1",
@@ -7734,6 +7744,7 @@
       "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==",
       "dev": true,
       "license": "MIT",
+      "peer": true,
       "bin": {
         "prettier": "bin/prettier.cjs"
       },
@@ -7980,6 +7991,7 @@
       "resolved": "https://registry.npmjs.org/quasar/-/quasar-2.18.6.tgz",
       "integrity": "sha512-ZlK+vJXOBPSFDCNQDBDNwSI+AHoqaFPxK8ve6mhsYLhMKWI5b8zsGY9VU1xYjngO2aBvU4fvGWXy4tTbzrBk8Q==",
       "license": "MIT",
+      "peer": true,
       "engines": {
         "node": ">= 10.18.1",
         "npm": ">= 6.13.4",
@@ -8321,6 +8333,7 @@
       "integrity": "sha512-iTNAbFSlRpcHeeWu73ywU/8KuU/LZmNCSxp6fjQkJBD3ivUb8tpDrXhIxEzA05HlYMEwmtaUnb3RP+YNv162OQ==",
       "dev": true,
       "license": "MIT",
+      "peer": true,
       "dependencies": {
         "@types/estree": "1.0.8"
       },
@@ -9716,6 +9729,7 @@
       "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
       "devOptional": true,
       "license": "Apache-2.0",
+      "peer": true,
       "bin": {
         "tsc": "bin/tsc",
         "tsserver": "bin/tsserver"
@@ -9925,6 +9939,7 @@
       "integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
       "dev": true,
       "license": "MIT",
+      "peer": true,
       "dependencies": {
         "esbuild": "^0.27.0",
         "fdir": "^6.5.0",
@@ -10611,6 +10626,7 @@
       "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.25.tgz",
       "integrity": "sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==",
       "license": "MIT",
+      "peer": true,
       "dependencies": {
         "@vue/compiler-dom": "3.5.25",
         "@vue/compiler-sfc": "3.5.25",
@@ -10677,7 +10693,6 @@
       "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
       "dev": true,
       "license": "Apache-2.0",
-      "peer": true,
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
@@ -11023,6 +11038,7 @@
       "integrity": "sha512-odxVsHAkZYYglR30aPYRY4nUGJnoJ2y1ww2HDvZALo0BDETv9kWbi16J52eHs+PWRNmF4ub6nZqfVOeesOvntg==",
       "dev": true,
       "license": "MIT",
+      "peer": true,
       "dependencies": {
         "eslint-visitor-keys": "^3.0.0",
         "yaml": "^2.0.0"

+ 5 - 0
src/api/notification.js

@@ -5,6 +5,11 @@ export const getMyUnreadNotifications = async () => {
   return data.payload;
 };
 
+export const getMyNotifications = async () => {
+  const { data } = await api.get("/notification/my");
+  return data.payload;
+};
+
 export const markNotificationAsRead = async (sendId) => {
   const { data } = await api.patch(`/notification/${sendId}/read`);
   return data.payload;

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

@@ -43,6 +43,8 @@
                 >{{ $t(item.title) }}</q-tooltip>
               </q-item>
 
+              
+
               <div v-else>
                 <template v-if="!navigation_store.miniState">
                   <q-expansion-item

+ 339 - 0
src/pages/associado/loja/AssociadoLojaPage.vue

@@ -0,0 +1,339 @@
+<template>
+  <div class="q-pa-md">
+
+    <div v-if="loading" class="flex flex-center q-py-xl">
+      <q-spinner color="primary" size="48px" />
+    </div>
+
+    <div
+      v-else-if="filteredItems.length === 0"
+      class="text-center text-grey q-py-xl"
+    >
+      Nenhum produto encontrado
+    </div>
+
+    <div v-else class="row q-col-gutter-md items-stretch">
+
+      <div
+        v-for="item in filteredItems"
+        :key="item.id"
+        class="col-12 col-sm-6 col-md-4"
+      >
+
+        <q-card flat bordered class="store-card">
+
+          <q-card-section class="q-pa-sm">
+
+            <div class="row no-wrap store-card-inner">
+
+              <!-- LEFT -->
+              <div class="col column justify-between q-pr-sm">
+
+                <div>
+
+                  <div
+                    class="text-subtitle1 text-weight-bold text-violet-medium q-mb-xs"
+                  >
+                    {{ item.name }}
+                  </div>
+
+                  <div
+                    v-if="item.description"
+                    class="text-caption text-grey-7 q-mb-sm ellipsis-2-lines"
+                  >
+                    {{ item.description }}
+                  </div>
+
+                  <!-- VARIACOES -->
+                  <template v-if="item.variations?.length">
+
+                    <div class="text-caption text-grey-6 q-mb-xs">
+                      {{ variationTypeLabel(item) }}
+                    </div>
+
+                    <div class="row q-gutter-xs q-mb-sm">
+
+                      <div
+                        v-for="v in item.variations"
+                        :key="v.id"
+                        :class="[
+                          'variation-tag',
+                          activeVariation(item)?.id === v.id
+                            ? 'variation-tag--selected'
+                            : 'variation-tag--default'
+                        ]"
+                        @click="selectVariation(item, v)"
+                      >
+                        {{ v.variation_label }}
+                      </div>
+
+                    </div>
+
+                  </template>
+
+                </div>
+
+                <!-- PRECO -->
+                <div class="column items-start q-mt-sm">
+
+                  <span
+                    v-if="item.price"
+                    class="text-caption text-grey-5 text-strike"
+                  >
+                    R$ {{ formatPrice(item.price) }}
+                  </span>
+
+                  <span
+                    class="text-subtitle1 text-weight-bold text-violet-dark"
+                  >
+                    R$ {{ formatPrice(displayPrice(item)) }}
+                  </span>
+
+                </div>
+
+              </div>
+
+              <!-- RIGHT -->
+              <div class="store-card-right column items-stretch">
+
+                <div class="store-card-image">
+
+                  <img
+                    v-if="item.media?.length"
+                    :src="item.media[0].url"
+                    :alt="item.name"
+                    class="store-card-img"
+                  />
+
+                  <q-icon
+                    v-else
+                    name="mdi-image-off-outline"
+                    size="32px"
+                    color="grey-4"
+                    class="absolute-center"
+                  />
+
+                </div>
+
+                <!-- BOTAO -->
+                <q-btn
+                  unelevated
+                  size="sm"
+                  class="btn-gradient q-mt-xs"
+                  :color="item.user_interested ? 'negative' : 'primary'"
+                  :icon="
+                    item.user_interested
+                      ? 'mdi-heart-remove'
+                      : 'mdi-heart-outline'
+                  "
+                  :label="
+                    item.user_interested
+                      ? 'Remover Interesse'
+                      : 'Demonstrar Interesse'
+                  "
+                  @click="toggleInterest(item)"
+                />
+
+              </div>
+
+            </div>
+
+          </q-card-section>
+
+        </q-card>
+
+      </div>
+
+    </div>
+
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, onMounted } from "vue";
+import { useQuasar } from "quasar";
+
+import {
+  getStoreItems,
+  toggleInterest as apiToggleInterest,
+} from "src/api/storeItem";
+
+const $q = useQuasar();
+
+const items = ref([]);
+const loading = ref(true);
+
+const selectedVariations = ref({});
+
+const filteredItems = computed(() => items.value);
+
+const selectVariation = (item, variation) => {
+  selectedVariations.value[item.id] = variation;
+};
+
+const activeVariation = (item) =>
+  selectedVariations.value[item.id] ?? item.variations?.[0] ?? null;
+
+const displayPrice = (item) => {
+  const v = activeVariation(item);
+
+  if (v?.variation_value != null) {
+    return v.variation_value;
+  }
+
+  return item.associate_price ?? item.price;
+};
+
+const variationTypeLabel = (item) => {
+  const type = item.variations?.[0]?.variation_type;
+
+  if (type === "tamanho") return "Tamanho";
+  if (type === "cor") return "Cor";
+  if (type === "modelo") return "Modelo";
+
+  return "";
+};
+
+const formatPrice = (price) =>
+  Number(price).toLocaleString("pt-BR", {
+    minimumFractionDigits: 2,
+  });
+
+const toggleInterest = async (item) => {
+  try {
+    const result = await apiToggleInterest(item.id);
+
+    item.user_interested = result.interested;
+
+    item.interests_count += result.interested ? 1 : -1;
+
+    $q.notify({
+      type: "positive",
+      message: result.interested
+        ? "Interesse demonstrado!"
+        : "Interesse removido!",
+    });
+
+  } catch {
+    $q.notify({
+      type: "negative",
+      message: "Erro ao processar interesse",
+    });
+  }
+};
+
+onMounted(async () => {
+  try {
+    const response = await getStoreItems();
+
+    items.value = response;
+
+    response.forEach((item) => {
+      if (item.variations?.length) {
+        selectedVariations.value[item.id] = item.variations[0];
+      }
+    });
+
+  } catch (e) {
+    console.error(e);
+
+  } finally {
+    loading.value = false;
+  }
+});
+</script>
+
+<style scoped lang="scss">
+.store-card {
+  transition: 0.2s;
+  border-radius: 12px;
+  height: 100%;
+
+  &:hover {
+    box-shadow: 0 4px 16px rgba(102, 29, 117, 0.12);
+  }
+}
+
+.store-card-inner {
+  min-height: 140px;
+}
+
+.store-card-right {
+  width: 110px;
+  min-width: 110px;
+  flex-shrink: 0;
+}
+
+.store-card-image {
+  position: relative;
+  height: 110px;
+  border-radius: 8px;
+  overflow: hidden;
+  background: #f5f0f7;
+  border: 1px solid rgba(102, 29, 117, 0.15);
+}
+
+.store-card-img {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+}
+
+.variation-tag {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+
+  min-width: 28px;
+  height: 26px;
+
+  padding: 0 7px;
+
+  border-radius: 4px;
+
+  font-size: 11px;
+  font-weight: 500;
+
+  cursor: pointer;
+
+  transition: 0.15s;
+
+  &--default {
+    background: #ede0f5;
+    color: #4d1658;
+  }
+
+  &--selected {
+    background: #4d1658;
+    color: #fff;
+  }
+}
+
+.btn-gradient {
+  background: linear-gradient(
+    90deg,
+    #4d1658 0%,
+    #8b30a5 100%
+  ) !important;
+
+  color: white !important;
+
+  border-radius: 8px !important;
+}
+
+.text-violet-dark {
+  color: #4d1658;
+}
+
+.text-violet-medium {
+  color: #7b2d97;
+}
+
+.ellipsis-2-lines {
+  display: -webkit-box;
+  -webkit-line-clamp: 2;
+  line-clamp: 2;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+}
+</style>

+ 0 - 158
src/pages/associado/loja/LojaPage.vue

@@ -1,158 +0,0 @@
-<template>
-  <div>
-    <DefaultHeaderPage />
-
-    <div class="q-pa-md">
-      <div class="row q-col-gutter-sm q-mb-md">
-        <div class="col-12 col-md-5">
-          <q-input
-            v-model="search"
-            :placeholder="$t('common.actions.search')"
-            outlined
-            dense
-            clearable
-          >
-            <template #prepend>
-              <q-icon name="mdi-magnify" />
-            </template>
-          </q-input>
-        </div>
-        <div class="col-12 col-md-4">
-          <CategorySelect
-            v-model="selectedCategory"
-            :label="$t('associado.filter_by_category')"
-          />
-        </div>
-      </div>
-
-      <div v-if="loading" class="flex flex-center q-py-xl">
-        <q-spinner color="primary" size="48px" />
-      </div>
-
-      <div v-else-if="filteredItems.length === 0" class="text-center text-grey q-py-xl">
-        {{ $t("http.errors.no_records_found") }}
-      </div>
-
-      <div v-else class="row q-col-gutter-md">
-        <div
-          v-for="item in filteredItems"
-          :key="item.id"
-          class="col-12 col-sm-6 col-md-4 col-lg-3"
-        >
-          <q-card flat bordered class="store-card full-height column">
-            <q-card-section class="col">
-              <div class="flex items-start justify-between q-mb-sm">
-                <div class="text-subtitle1 text-weight-medium">{{ item.name }}</div>
-                <q-badge v-if="item.category?.name" color="primary" :label="item.category.name" />
-              </div>
-              <div v-if="item.description" class="text-body2 text-grey-7 q-mb-sm ellipsis-3-lines">
-                {{ item.description }}
-              </div>
-              <div class="text-body2 q-mb-xs">
-                <span class="text-weight-medium">{{ $t('associado.price') }}:</span>
-                R$ {{ formatPrice(item.price) }}
-              </div>
-              <div v-if="item.size_s || item.size_m || item.size_l || item.size_xl" class="q-mt-xs">
-                <div class="text-caption text-grey-6">{{ $t('associado.available_sizes') }}:</div>
-                <div class="row q-gutter-xs q-mt-xs">
-                  <q-chip v-if="item.size_s && item.stock_s > 0" size="sm" outline color="primary">P</q-chip>
-                  <q-chip v-if="item.size_m && item.stock_m > 0" size="sm" outline color="primary">M</q-chip>
-                  <q-chip v-if="item.size_l && item.stock_l > 0" size="sm" outline color="primary">G</q-chip>
-                  <q-chip v-if="item.size_xl && item.stock_xl > 0" size="sm" outline color="primary">GG</q-chip>
-                </div>
-              </div>
-            </q-card-section>
-            <q-card-actions>
-              <q-btn
-                :color="item.user_interested ? 'negative' : 'primary'"
-                :icon="item.user_interested ? 'mdi-heart' : 'mdi-heart-outline'"
-                :label="item.user_interested ? $t('associado.remove_interest') : $t('associado.i_want')"
-                :loading="togglingId === item.id"
-                flat
-                @click="toggleInterest(item)"
-              />
-            </q-card-actions>
-          </q-card>
-        </div>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup>
-import { ref, computed, onMounted } from "vue";
-import { useQuasar } from "quasar";
-import { useI18n } from "vue-i18n";
-import { getStoreItems, toggleInterest as apiToggleInterest } from "src/api/storeItem";
-import { normalizeString } from "src/helpers/utils";
-
-import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
-import CategorySelect from "src/components/selects/CategorySelect.vue";
-
-const $q = useQuasar();
-const { t } = useI18n();
-
-const items = ref([]);
-const loading = ref(true);
-const search = ref("");
-const selectedCategory = ref(null);
-const togglingId = ref(null);
-
-const filteredItems = computed(() => {
-  let list = items.value;
-  if (selectedCategory.value) {
-    list = list.filter((i) => i.category_id === selectedCategory.value.value);
-  }
-  if (search.value) {
-    const needle = normalizeString(search.value);
-    list = list.filter((i) =>
-      normalizeString(i.name || "").includes(needle),
-    );
-  }
-  return list;
-});
-
-const formatPrice = (price) => {
-  if (!price) return "0,00";
-  return Number(price).toLocaleString("pt-BR", { minimumFractionDigits: 2 });
-};
-
-const toggleInterest = async (item) => {
-  togglingId.value = item.id;
-  try {
-    const result = await apiToggleInterest(item.id);
-    item.user_interested = result.interested;
-    $q.notify({ type: "positive", message: t("http.success") });
-  } catch {
-    $q.notify({ type: "negative", message: t("http.errors.failed") });
-  } finally {
-    togglingId.value = null;
-  }
-};
-
-onMounted(async () => {
-  try {
-    items.value = await getStoreItems();
-  } catch (e) {
-    console.error(e);
-  } finally {
-    loading.value = false;
-  }
-});
-</script>
-
-<style lang="scss" scoped>
-.store-card {
-  transition: box-shadow 0.2s;
-  &:hover {
-    box-shadow: 0 4px 16px rgba(102, 29, 117, 0.12);
-  }
-}
-.ellipsis-3-lines {
-  display: -webkit-box;
-  -webkit-line-clamp: 3;
-  line-clamp: 3; /* For Firefox */
-  -webkit-box-orient: vertical;
-  overflow: hidden;
-}
-</style>

+ 89 - 16
src/pages/associado/notificacoes/NotificacoesAssociadoPage.vue

@@ -6,21 +6,42 @@
       <q-spinner color="violet-normal" size="50px" />
     </div>
 
-    <div v-else-if="notifications.length === 0" class="flex flex-center q-pa-xl text-grey-6">
-      {{ $t('notification.empty') }}
+    <div
+      v-else-if="notifications.length === 0"
+      class="flex flex-center q-pa-xl text-grey-6"
+    >
+      {{ $t("notification.empty") }}
     </div>
 
     <div v-else class="q-gutter-md">
+
       <q-card
         v-for="item in notifications"
         :key="item.id"
         flat
         bordered
         class="notificacoes-card"
+        :class="{ 'not-read': !item.read }"
         @click="onRead(item)"
       >
         <q-card-section horizontal>
-          <q-card-section class="flex items-center q-pa-md">
+
+          <!-- IMAGEM -->
+          <div
+            v-if="item.notification?.media?.length"
+            class="notification-image-container"
+          >
+            <img
+              :src="item.notification.media[0].url"
+              class="notification-image"
+            />
+          </div>
+
+          <!-- ICONE -->
+          <q-card-section
+            v-else
+            class="flex items-center q-pa-md"
+          >
             <q-icon
               name="mdi-bell-outline"
               size="28px"
@@ -28,27 +49,48 @@
             />
           </q-card-section>
 
+          <!-- CONTEUDO -->
           <q-card-section class="flex-grow q-pa-md">
-            <div class="text-weight-bold" :class="item.read ? 'text-grey-7' : 'text-violet-normal'">
-              {{ item.notification?.title }}
+
+            <div class="row items-start justify-between">
+
+              <div
+                class="text-weight-bold"
+                :class="item.read ? 'text-grey-7' : 'text-violet-normal'"
+              >
+                {{ item.notification?.title }}
+              </div>
+
+              <q-badge
+                v-if="!item.read"
+                color="violet-normal"
+                rounded
+              />
+
             </div>
+
             <div class="text-caption text-grey-7 q-mt-xs">
               {{ item.notification?.message }}
             </div>
-          </q-card-section>
 
-          <q-card-section class="flex items-center q-pa-md">
-            <q-badge v-if="!item.read" color="violet-normal" rounded />
+            <div class="text-caption text-grey-5 q-mt-sm">
+              {{ item.created_at }}
+            </div>
+
           </q-card-section>
+
         </q-card-section>
       </q-card>
+
     </div>
   </div>
 </template>
 
 <script setup>
 import { ref, onMounted } from "vue";
-import { getMyUnreadNotifications, markNotificationAsRead } from "src/api/notification";
+
+import {getMyNotifications,markNotificationAsRead} from "src/api/notification";
+
 import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
 
 const loading = ref(true);
@@ -56,13 +98,24 @@ const notifications = ref([]);
 
 const onRead = async (item) => {
   if (item.read) return;
-  await markNotificationAsRead(item.id);
-  item.read = true;
+
+  try {
+    await markNotificationAsRead(item.id);
+
+    item.read = true;
+
+  } catch (e) {
+    console.error(e);
+  }
 };
 
 onMounted(async () => {
   try {
-    notifications.value = await getMyUnreadNotifications();
+    notifications.value = await getMyNotifications();
+
+  } catch (e) {
+    console.error(e);
+
   } finally {
     loading.value = false;
   }
@@ -71,12 +124,32 @@ onMounted(async () => {
 
 <style scoped lang="scss">
 .notificacoes-card {
-  border-radius: 8px;
+  border-radius: 12px;
   cursor: pointer;
-  transition: background-color 0.2s;
+  transition: all 0.2s;
+  overflow: hidden;
 
   &:hover {
-    background: #f0e8f1;
+    background: #f5eef7;
+    transform: translateY(-1px);
+  }
+
+  &.not-read {
+    border-left: 4px solid #7b2d97;
   }
 }
-</style>
+
+.notification-image-container {
+  width: 140px;
+  min-width: 140px;
+  height: 140px;
+  overflow: hidden;
+  background: #f3edf5;
+}
+
+.notification-image {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+}
+</style>

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

@@ -35,7 +35,7 @@ export default [
   {
     path: "/associado/loja",
     name: "AssociadoLojaPage",
-    component: () => import("pages/associado/loja/LojaPage.vue"),
+    component: () => import("pages/associado/loja/AssociadoLojaPage.vue"),
     meta: {
       title: { value: "ui.navigation.loja", translate: true },
       description: { value: "page.associado.loja.description", translate: true },

+ 1 - 1
src/stores/navigation.js

@@ -139,7 +139,7 @@ export const navigationStore = defineStore("navigation", () => {
     {
       type: "single",
       title: "ui.navigation.notifications",
-      name: "NotificacoesPage",
+      name: "NotificacoesAssociadoPage",
       icon: "mdi-bell-outline",
       permission: false,
       permissionScope: "notificacao",