- Implementazione TRASPORTI ! Passo 1
This commit is contained in:
275
src/modules/trasporti/pages/ChatListPage.vue
Normal file
275
src/modules/trasporti/pages/ChatListPage.vue
Normal file
@@ -0,0 +1,275 @@
|
||||
<!-- ChatListPage.vue -->
|
||||
<template>
|
||||
<q-page class="chat-list-page">
|
||||
<!-- Header -->
|
||||
<div class="chat-list-page__header">
|
||||
<div class="chat-list-page__title-section">
|
||||
<q-icon name="forum" class="chat-list-page__icon" />
|
||||
<div>
|
||||
<h1 class="chat-list-page__title">Messaggi</h1>
|
||||
<p class="chat-list-page__subtitle">Le tue conversazioni</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Search -->
|
||||
<q-input
|
||||
v-model="searchQuery"
|
||||
placeholder="Cerca conversazione..."
|
||||
outlined
|
||||
dense
|
||||
class="chat-list-page__search"
|
||||
>
|
||||
<template #prepend>
|
||||
<q-icon name="search" />
|
||||
</template>
|
||||
<template v-if="searchQuery" #append>
|
||||
<q-icon name="close" class="cursor-pointer" @click="searchQuery = ''" />
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
|
||||
<!-- Tabs -->
|
||||
<q-tabs
|
||||
v-model="activeTab"
|
||||
class="chat-list-page__tabs"
|
||||
active-color="primary"
|
||||
indicator-color="primary"
|
||||
align="justify"
|
||||
>
|
||||
<q-tab name="all" label="Tutte" icon="inbox" />
|
||||
<q-tab name="unread" icon="mark_email_unread">
|
||||
<template #default>
|
||||
<div class="chat-list-page__tab-content">
|
||||
<span>Non lette</span>
|
||||
<q-badge v-if="unreadCount > 0" color="negative" :label="unreadCount" />
|
||||
</div>
|
||||
</template>
|
||||
</q-tab>
|
||||
<q-tab name="rides" label="Viaggi" icon="directions_car" />
|
||||
<q-tab name="archived" label="Archiviate" icon="archive" />
|
||||
</q-tabs>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="chat-list-page__content">
|
||||
<!-- Loading -->
|
||||
<div v-if="loading" class="chat-list-page__loading">
|
||||
<q-spinner-dots size="50px" color="primary" />
|
||||
<p>Caricamento conversazioni...</p>
|
||||
</div>
|
||||
|
||||
<!-- Empty State -->
|
||||
<div v-else-if="filteredChats.length === 0" class="chat-list-page__empty">
|
||||
<q-icon :name="emptyStateIcon" size="80px" color="grey-4" />
|
||||
<h3>{{ emptyStateTitle }}</h3>
|
||||
<p>{{ emptyStateMessage }}</p>
|
||||
<q-btn
|
||||
v-if="activeTab === 'all' && !searchQuery"
|
||||
color="primary"
|
||||
icon="explore"
|
||||
label="Esplora viaggi"
|
||||
rounded
|
||||
unelevated
|
||||
to="/trasporti"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Chat List -->
|
||||
<q-list v-else class="chat-list-page__list" separator>
|
||||
<q-slide-item
|
||||
v-for="chat in filteredChats"
|
||||
:key="chat._id"
|
||||
class="chat-list-page__item"
|
||||
:class="{ 'chat-list-page__item--unread': chat.unreadCount > 0 }"
|
||||
@left="onArchiveChat(chat)"
|
||||
@right="onDeleteChat(chat)"
|
||||
>
|
||||
<template #left>
|
||||
<div class="chat-list-page__slide-action chat-list-page__slide-action--archive">
|
||||
<q-icon name="archive" />
|
||||
<span>Archivia</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #right>
|
||||
<div class="chat-list-page__slide-action chat-list-page__slide-action--delete">
|
||||
<q-icon name="delete" />
|
||||
<span>Elimina</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<q-item clickable @click="openChat(chat)">
|
||||
<!-- Avatar -->
|
||||
<q-item-section avatar>
|
||||
<div class="chat-list-page__avatar-wrapper">
|
||||
<q-avatar size="56px">
|
||||
<img
|
||||
v-if="getOtherParticipant(chat)?.profile?.img"
|
||||
:src="getOtherParticipant(chat).profile.img"
|
||||
:alt="getOtherParticipant(chat).name"
|
||||
/>
|
||||
<div v-else class="chat-list-page__avatar-placeholder">
|
||||
{{ getInitials(getOtherParticipant(chat)) }}
|
||||
</div>
|
||||
</q-avatar>
|
||||
|
||||
<!-- Online indicator -->
|
||||
<div
|
||||
v-if="isOnline(getOtherParticipant(chat)?._id)"
|
||||
class="chat-list-page__online-dot"
|
||||
/>
|
||||
|
||||
<!-- Ride type badge -->
|
||||
<q-badge
|
||||
v-if="chat.rideInfo"
|
||||
:color="chat.rideInfo.type === 'offer' ? 'positive' : 'negative'"
|
||||
floating
|
||||
rounded
|
||||
class="chat-list-page__ride-badge"
|
||||
>
|
||||
<q-icon :name="chat.rideInfo.type === 'offer' ? 'directions_car' : 'hail'" size="12px" />
|
||||
</q-badge>
|
||||
</div>
|
||||
</q-item-section>
|
||||
|
||||
<!-- Content -->
|
||||
<q-item-section>
|
||||
<q-item-label class="chat-list-page__name">
|
||||
{{ getOtherParticipant(chat)?.name }} {{ getOtherParticipant(chat)?.surname }}
|
||||
</q-item-label>
|
||||
|
||||
<!-- Ride info -->
|
||||
<q-item-label v-if="chat.rideInfo" caption class="chat-list-page__ride-info">
|
||||
<q-icon name="place" size="14px" />
|
||||
{{ chat.rideInfo.departure }} → {{ chat.rideInfo.destination }}
|
||||
</q-item-label>
|
||||
|
||||
<!-- Last message -->
|
||||
<q-item-label
|
||||
caption
|
||||
lines="1"
|
||||
class="chat-list-page__last-message"
|
||||
:class="{ 'chat-list-page__last-message--unread': chat.unreadCount > 0 }"
|
||||
>
|
||||
<q-icon
|
||||
v-if="chat.lastMessage?.senderId === currentUserId"
|
||||
:name="getMessageStatusIcon(chat.lastMessage)"
|
||||
size="14px"
|
||||
:color="chat.lastMessage?.read ? 'primary' : 'grey'"
|
||||
/>
|
||||
{{ getMessagePreview(chat.lastMessage) }}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
|
||||
<!-- Right side -->
|
||||
<q-item-section side>
|
||||
<div class="chat-list-page__meta">
|
||||
<q-item-label caption class="chat-list-page__time">
|
||||
{{ formatTime(chat.lastMessage?.createdAt || chat.updatedAt) }}
|
||||
</q-item-label>
|
||||
|
||||
<q-badge
|
||||
v-if="chat.unreadCount > 0"
|
||||
color="primary"
|
||||
:label="chat.unreadCount > 99 ? '99+' : chat.unreadCount"
|
||||
rounded
|
||||
class="chat-list-page__unread-badge"
|
||||
/>
|
||||
|
||||
<q-icon
|
||||
v-else-if="chat.pinned"
|
||||
name="push_pin"
|
||||
size="18px"
|
||||
color="grey"
|
||||
/>
|
||||
</div>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-slide-item>
|
||||
</q-list>
|
||||
|
||||
<!-- Load More -->
|
||||
<div v-if="hasMore && !loading" class="chat-list-page__load-more">
|
||||
<q-btn
|
||||
flat
|
||||
color="primary"
|
||||
label="Carica altre conversazioni"
|
||||
:loading="loadingMore"
|
||||
@click="loadMore"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FAB for new message -->
|
||||
<q-page-sticky position="bottom-right" :offset="[18, 18]">
|
||||
<q-fab
|
||||
icon="edit"
|
||||
direction="up"
|
||||
color="primary"
|
||||
:disable="loading"
|
||||
>
|
||||
<q-fab-action
|
||||
color="secondary"
|
||||
icon="person_search"
|
||||
label="Cerca utente"
|
||||
@click="showUserSearch = true"
|
||||
/>
|
||||
<q-fab-action
|
||||
color="accent"
|
||||
icon="group"
|
||||
label="Nuovo gruppo"
|
||||
@click="showGroupCreate = true"
|
||||
/>
|
||||
</q-fab>
|
||||
</q-page-sticky>
|
||||
|
||||
<!-- User Search Dialog -->
|
||||
<q-dialog v-model="showUserSearch" position="top">
|
||||
<q-card class="chat-list-page__search-dialog">
|
||||
<q-card-section>
|
||||
<div class="text-h6">Nuova conversazione</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section>
|
||||
<q-input
|
||||
v-model="userSearchQuery"
|
||||
placeholder="Cerca per nome o username..."
|
||||
outlined
|
||||
autofocus
|
||||
@update:model-value="searchUsers"
|
||||
>
|
||||
<template #prepend>
|
||||
<q-icon name="search" />
|
||||
</template>
|
||||
</q-input>
|
||||
|
||||
<q-list v-if="searchedUsers.length > 0" class="q-mt-md">
|
||||
<q-item
|
||||
v-for="user in searchedUsers"
|
||||
:key="user._id"
|
||||
clickable
|
||||
@click="startChatWith(user)"
|
||||
>
|
||||
<q-item-section avatar>
|
||||
<q-avatar>
|
||||
<img v-if="user.profile?.img" :src="user.profile.img" />
|
||||
<span v-else>{{ getInitials(user) }}</span>
|
||||
</q-avatar>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ user.name }} {{ user.surname }}</q-item-label>
|
||||
<q-item-label caption>@{{ user.username }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
|
||||
<div v-else-if="userSearchQuery && !searchingUsers" class="text-center q-pa-md text-grey">
|
||||
Nessun utente trovato
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" src="./ChatListPage.ts" />
|
||||
<style lang="scss" src="./ChatListPage.scss" />
|
||||
Reference in New Issue
Block a user