|
|
@@ -2,108 +2,138 @@
|
|
|
<div>
|
|
|
<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
|
|
|
- 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>
|
|
|
|
|
|
- <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>
|
|
|
</template>
|
|
|
|
|
|
<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";
|
|
|
|
|
|
+const { t } = useI18n();
|
|
|
+
|
|
|
const loading = ref(true);
|
|
|
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) => {
|
|
|
if (item.read) return;
|
|
|
-
|
|
|
try {
|
|
|
await markNotificationAsRead(item.id);
|
|
|
-
|
|
|
item.read = true;
|
|
|
-
|
|
|
} catch (e) {
|
|
|
console.error(e);
|
|
|
}
|
|
|
@@ -112,10 +142,8 @@ const onRead = async (item) => {
|
|
|
onMounted(async () => {
|
|
|
try {
|
|
|
notifications.value = await getMyNotifications();
|
|
|
-
|
|
|
} catch (e) {
|
|
|
console.error(e);
|
|
|
-
|
|
|
} finally {
|
|
|
loading.value = false;
|
|
|
}
|
|
|
@@ -123,33 +151,52 @@ onMounted(async () => {
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
-.notificacoes-card {
|
|
|
- border-radius: 12px;
|
|
|
- cursor: pointer;
|
|
|
- transition: all 0.2s;
|
|
|
+.notif-card {
|
|
|
+ border-radius: 8px;
|
|
|
overflow: hidden;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: box-shadow 0.2s, transform 0.15s;
|
|
|
|
|
|
&: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>
|