Bläddra i källkod

style: :lipstick: style (notificacoes-associado) foram realizados ajustes nas notificacoes associado

foi ajustado o layout para que seja grade com 3 por linha, exiba a imagem corretamente e tambem foi corrigida a opcao de marcar como lida

fase:dev | origin:escopo
Gustavo Zanatta 3 veckor sedan
förälder
incheckning
3774410de3
1 ändrade filer med 149 tillägg och 102 borttagningar
  1. 149 102
      src/pages/associado/notificacoes/NotificacoesAssociadoPage.vue

+ 149 - 102
src/pages/associado/notificacoes/NotificacoesAssociadoPage.vue

@@ -2,108 +2,138 @@
   <div>
   <div>
     <DefaultHeaderPage />
     <DefaultHeaderPage />
 
 
-    <div v-if="loading" class="flex flex-center q-pa-xl">
-      <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>
+    <div class="q-pt-sm">
 
 
-    <div v-else class="q-gutter-md">
+      <div v-if="loading" class="flex flex-center q-pa-xl">
+        <q-spinner color="violet-normal" size="50px" />
+      </div>
 
 
-      <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>
+      <div v-else-if="pagedItems.length === 0" class="flex flex-center q-pa-xl text-grey-6">
+        {{ $t('notification.empty') }}
+      </div>
 
 
-          <!-- IMAGEM -->
+      <div v-else>
+        <div class="row q-px-md q-col-gutter-md">
           <div
           <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"
+            v-for="item in pagedItems"
+            :key="item.id"
+            class="col-xl-4 col-lg-4 col-md-4 col-sm-6 col-12"
           >
           >
-            <q-icon
-              name="mdi-bell-outline"
-              size="28px"
-              :color="item.read ? 'grey-5' : 'violet-normal'"
-            />
-          </q-card-section>
-
-          <!-- CONTEUDO -->
-          <q-card-section class="flex-grow q-pa-md">
-
-            <div class="row items-start justify-between">
-
-              <div
-                class="text-weight-bold"
-                :class="item.read ? 'text-grey-7' : 'text-violet-normal'"
-              >
-                {{ item.notification?.title }}
+            <q-card
+              flat
+              bordered
+              class="notif-card"
+              :class="{ 'notif-card--unread': !item.read }"
+              @click="onRead(item)"
+            >
+              <div class="notif-card__image">
+                <img
+                  v-if="imageUrl(item)"
+                  :src="imageUrl(item)"
+                  alt=""
+                  class="notif-card__img"
+                />
+                <div v-else class="notif-card__placeholder flex flex-center">
+                  <q-icon
+                    name="mdi-bell-outline"
+                    size="40px"
+                    :color="item.read ? 'grey-4' : 'violet-normal'"
+                  />
+                </div>
               </div>
               </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>
-
-            <div class="text-caption text-grey-5 q-mt-sm">
-              {{ item.created_at }}
-            </div>
-
-          </q-card-section>
-
-        </q-card-section>
-      </q-card>
+              <q-card-section class="q-pt-sm q-pb-xs">
+                <div class="row items-start justify-between no-wrap q-mb-xs">
+                  <div
+                    class="notif-card__title text-weight-bold ellipsis"
+                    :class="item.read ? 'text-grey-7' : 'text-violet-normal'"
+                  >
+                    {{ item.notification?.title }}
+                  </div>
+                  <q-badge
+                    v-if="!item.read"
+                    color="violet-normal"
+                    rounded
+                    class="q-ml-xs"
+                    style="flex-shrink: 0"
+                  />
+                </div>
+                <div class="notif-card__message text-caption text-grey-7">
+                  {{ item.notification?.message }}
+                </div>
+              </q-card-section>
+
+              <q-card-actions class="q-pt-xs q-pb-sm q-px-md">
+                <div class="text-caption text-grey-6">
+                  {{ formatDate(item.created_at) }}
+                </div>
+              </q-card-actions>
+            </q-card>
+          </div>
+        </div>
+
+        <div v-if="totalPages > 1" class="flex flex-center q-mt-lg">
+          <q-pagination
+            v-model="currentPage"
+            :max="totalPages"
+            :max-pages="6"
+            boundary-numbers
+            color="violet-normal"
+            active-color="violet-normal"
+            direction-links
+          />
+        </div>
+      </div>
 
 
     </div>
     </div>
   </div>
   </div>
 </template>
 </template>
 
 
 <script setup>
 <script setup>
-import { ref, onMounted } from "vue";
-
-import {getMyNotifications,markNotificationAsRead} from "src/api/notification";
-
+import { ref, computed, onMounted } from "vue";
+import { useI18n } from "vue-i18n";
+import { getMyNotifications, markNotificationAsRead } from "src/api/notification";
 import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
 import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
 
 
+const { t } = useI18n();
+
 const loading = ref(true);
 const loading = ref(true);
 const notifications = ref([]);
 const notifications = ref([]);
+const currentPage = ref(1);
+const PER_PAGE = 12;
+
+const totalPages = computed(() => Math.max(1, Math.ceil(notifications.value.length / PER_PAGE)));
+
+const pagedItems = computed(() => {
+  const start = (currentPage.value - 1) * PER_PAGE;
+  return notifications.value.slice(start, start + PER_PAGE);
+});
+
+const imageUrl = (item) => {
+  const media = item.notification?.media?.[0]?.url;
+  if (media) return media;
+  const direct = item.notification?.image_url;
+  if (!direct) return null;
+  return direct.startsWith("http") ? direct : (process.env.API_URL + direct);
+};
+
+const formatDate = (dateStr) => {
+  if (!dateStr) return "";
+  const date = new Date(dateStr);
+  const today = new Date();
+  const isToday =
+    date.getDate() === today.getDate() &&
+    date.getMonth() === today.getMonth() &&
+    date.getFullYear() === today.getFullYear();
+  if (isToday) return t("common.terms.today");
+  return date.toLocaleDateString("pt-BR", { day: "2-digit", month: "2-digit", year: "numeric" });
+};
 
 
 const onRead = async (item) => {
 const onRead = async (item) => {
   if (item.read) return;
   if (item.read) return;
-
   try {
   try {
     await markNotificationAsRead(item.id);
     await markNotificationAsRead(item.id);
-
     item.read = true;
     item.read = true;
-
   } catch (e) {
   } catch (e) {
     console.error(e);
     console.error(e);
   }
   }
@@ -112,10 +142,8 @@ const onRead = async (item) => {
 onMounted(async () => {
 onMounted(async () => {
   try {
   try {
     notifications.value = await getMyNotifications();
     notifications.value = await getMyNotifications();
-
   } catch (e) {
   } catch (e) {
     console.error(e);
     console.error(e);
-
   } finally {
   } finally {
     loading.value = false;
     loading.value = false;
   }
   }
@@ -123,33 +151,52 @@ onMounted(async () => {
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
-.notificacoes-card {
-  border-radius: 12px;
-  cursor: pointer;
-  transition: all 0.2s;
+.notif-card {
+  border-radius: 8px;
   overflow: hidden;
   overflow: hidden;
+  cursor: pointer;
+  transition: box-shadow 0.2s, transform 0.15s;
 
 
   &:hover {
   &:hover {
-    background: #f5eef7;
-    transform: translateY(-1px);
+    box-shadow: 0 4px 16px rgba(102, 29, 117, 0.15);
+    transform: translateY(-2px);
   }
   }
 
 
-  &.not-read {
-    border-left: 4px solid #7b2d97;
+  &--unread {
+    border-top: 3px solid #7b2d97;
   }
   }
-}
 
 
-.notification-image-container {
-  width: 140px;
-  min-width: 140px;
-  height: 140px;
-  overflow: hidden;
-  background: #f3edf5;
-}
+  &__image {
+    width: 100%;
+    height: 120px;
+    overflow: hidden;
+  }
+
+  &__img {
+    width: 100%;
+    height: 100%;
+    object-fit: cover;
+    display: block;
+  }
 
 
-.notification-image {
-  width: 100%;
-  height: 100%;
-  object-fit: cover;
+  &__placeholder {
+    width: 100%;
+    height: 100%;
+    background: #f0e8f1;
+  }
+
+  &__title {
+    font-size: 14px;
+    line-height: 1.3;
+    min-width: 0;
+  }
+
+  &__message {
+    display: -webkit-box;
+    -webkit-line-clamp: 2;
+    -webkit-box-orient: vertical;
+    overflow: hidden;
+    line-height: 1.4;
+  }
 }
 }
-</style>
+</style>