- Trasporti- Passo 2

This commit is contained in:
Surya Paolo
2025-12-22 23:39:42 +01:00
parent c9fc1a83d0
commit 11e946bfc6
34 changed files with 1682 additions and 1029 deletions

View File

@@ -4,7 +4,7 @@
<!-- Header -->
<div class="chat-list-page__header">
<div class="chat-list-page__title-section">
<q-icon name="forum" class="chat-list-page__icon" />
<q-icon name="forum" class="chat-list-page__icon" size="32px" color="primary" />
<div>
<h1 class="chat-list-page__title">Messaggi</h1>
<p class="chat-list-page__subtitle">Le tue conversazioni</p>
@@ -17,12 +17,12 @@
placeholder="Cerca conversazione..."
outlined
dense
class="chat-list-page__search"
class="chat-list-page__search q-mt-md"
>
<template #prepend>
<template v-slot:prepend>
<q-icon name="search" />
</template>
<template v-if="searchQuery" #append>
<template v-if="searchQuery" v-slot:append>
<q-icon name="close" class="cursor-pointer" @click="searchQuery = ''" />
</template>
</q-input>
@@ -35,13 +35,19 @@
active-color="primary"
indicator-color="primary"
align="justify"
dense
>
<q-tab name="all" label="Tutte" icon="inbox" />
<q-tab name="unread" icon="mark_email_unread">
<template #default>
<template v-slot:default>
<div class="chat-list-page__tab-content">
<span>Non lette</span>
<q-badge v-if="unreadCount > 0" color="negative" :label="unreadCount" />
<q-badge
v-if="unreadCount > 0"
color="negative"
:label="unreadCount > 99 ? '99+' : unreadCount"
class="q-ml-xs"
/>
</div>
</template>
</q-tab>
@@ -52,16 +58,16 @@
<!-- Content -->
<div class="chat-list-page__content">
<!-- Loading -->
<div v-if="loading" class="chat-list-page__loading">
<div v-if="loading && filteredChats.length === 0" class="chat-list-page__loading">
<q-spinner-dots size="50px" color="primary" />
<p>Caricamento conversazioni...</p>
<p class="text-grey q-mt-md">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>
<h3 class="q-mt-md q-mb-sm">{{ emptyStateTitle }}</h3>
<p class="text-grey">{{ emptyStateMessage }}</p>
<q-btn
v-if="activeTab === 'all' && !searchQuery"
color="primary"
@@ -69,6 +75,7 @@
label="Esplora viaggi"
rounded
unelevated
class="q-mt-md"
to="/trasporti"
/>
</div>
@@ -79,24 +86,27 @@
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)"
:class="{ 'chat-list-page__item--unread': (chat.unreadCount || 0) > 0 }"
@left="(details) => { details.reset(); onArchiveChat(chat); }"
@right="(details) => { details.reset(); onDeleteChat(chat); }"
>
<template #left>
<!-- Left Slide Action: Archive -->
<template v-slot:left>
<div class="chat-list-page__slide-action chat-list-page__slide-action--archive">
<q-icon name="archive" />
<span>Archivia</span>
<q-icon name="archive" size="24px" />
<span class="text-caption q-mt-xs">Archivia</span>
</div>
</template>
<template #right>
<!-- Right Slide Action: Delete -->
<template v-slot:right>
<div class="chat-list-page__slide-action chat-list-page__slide-action--delete">
<q-icon name="delete" />
<span>Elimina</span>
<q-icon name="delete" size="24px" />
<span class="text-caption q-mt-xs">Elimina</span>
</div>
</template>
<!-- Chat Item -->
<q-item clickable @click="openChat(chat)">
<!-- Avatar -->
<q-item-section avatar>
@@ -105,7 +115,7 @@
<img
v-if="getOtherParticipant(chat)?.profile?.img"
:src="getOtherParticipant(chat).profile.img"
:alt="getOtherParticipant(chat).name"
:alt="getOtherParticipant(chat)?.name"
/>
<div v-else class="chat-list-page__avatar-placeholder">
{{ getInitials(getOtherParticipant(chat)) }}
@@ -120,27 +130,53 @@
<!-- Ride type badge -->
<q-badge
v-if="chat.rideInfo"
:color="chat.rideInfo.type === 'offer' ? 'positive' : 'negative'"
v-if="chat.rideId && typeof chat.rideId === 'object' && chat.rideId.type"
:color="chat.rideId.type === 'offer' ? 'positive' : 'warning'"
floating
rounded
class="chat-list-page__ride-badge"
>
<q-icon :name="chat.rideInfo.type === 'offer' ? 'directions_car' : 'hail'" size="12px" />
<q-icon
:name="chat.rideId.type === 'offer' ? 'directions_car' : 'hail'"
size="12px"
/>
</q-badge>
</div>
</q-item-section>
<!-- Content -->
<q-item-section>
<!-- Name -->
<q-item-label class="chat-list-page__name">
{{ getOtherParticipant(chat)?.name }} {{ getOtherParticipant(chat)?.surname }}
<span class="text-weight-medium">
{{ getOtherParticipant(chat)?.name || 'Utente' }}
{{ getOtherParticipant(chat)?.surname || '' }}
</span>
<q-icon
v-if="chat.pinned"
name="push_pin"
size="16px"
color="grey"
class="q-ml-xs"
/>
</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
v-if="chat.rideId && typeof chat.rideId === 'object'"
caption
class="chat-list-page__ride-info"
>
<q-icon name="place" size="14px" class="q-mr-xs" />
<template v-if="chat.rideId.departure && chat.rideId.destination">
{{ typeof chat.rideId.departure === 'string'
? chat.rideId.departure
: chat.rideId.departure.city }}
{{ typeof chat.rideId.destination === 'string'
? chat.rideId.destination
: chat.rideId.destination.city }}
</template>
</q-item-label>
<!-- Last message -->
@@ -148,38 +184,46 @@
caption
lines="1"
class="chat-list-page__last-message"
:class="{ 'chat-list-page__last-message--unread': chat.unreadCount > 0 }"
:class="{
'chat-list-page__last-message--unread': (chat.unreadCount || 0) > 0,
'text-weight-medium': (chat.unreadCount || 0) > 0
}"
>
<q-icon
v-if="chat.lastMessage?.senderId === currentUserId"
v-if="chat.lastMessage && chat.lastMessage.senderId === currentUserId"
:name="getMessageStatusIcon(chat.lastMessage)"
size="14px"
:color="chat.lastMessage?.read ? 'primary' : 'grey'"
:color="chat.lastMessage.readBy && chat.lastMessage.readBy.length > 1 ? 'primary' : 'grey'"
class="q-mr-xs"
/>
{{ getMessagePreview(chat.lastMessage) }}
</q-item-label>
</q-item-section>
<!-- Right side -->
<q-item-section side>
<!-- Right side: Time & Badge -->
<q-item-section side top>
<div class="chat-list-page__meta">
<!-- Time -->
<q-item-label caption class="chat-list-page__time">
{{ formatTime(chat.lastMessage?.createdAt || chat.updatedAt) }}
{{ formatTime(chat.lastMessage?.timestamp || chat.updatedAt) }}
</q-item-label>
<!-- Unread badge -->
<q-badge
v-if="chat.unreadCount > 0"
v-if="(chat.unreadCount || 0) > 0"
color="primary"
:label="chat.unreadCount > 99 ? '99+' : chat.unreadCount"
rounded
class="chat-list-page__unread-badge"
class="chat-list-page__unread-badge q-mt-xs"
/>
<!-- Muted icon -->
<q-icon
v-else-if="chat.pinned"
name="push_pin"
v-else-if="chat.mutedBy && chat.mutedBy.includes(currentUserId)"
name="notifications_off"
size="18px"
color="grey"
class="q-mt-xs"
/>
</div>
</q-item-section>
@@ -188,11 +232,12 @@
</q-list>
<!-- Load More -->
<div v-if="hasMore && !loading" class="chat-list-page__load-more">
<div v-if="hasMore && filteredChats.length > 0" class="chat-list-page__load-more">
<q-btn
flat
color="primary"
label="Carica altre conversazioni"
icon="expand_more"
:loading="loadingMore"
@click="loadMore"
/>
@@ -211,12 +256,16 @@
color="secondary"
icon="person_search"
label="Cerca utente"
external-label
label-position="left"
@click="showUserSearch = true"
/>
<q-fab-action
color="accent"
icon="group"
label="Nuovo gruppo"
external-label
label-position="left"
@click="showGroupCreate = true"
/>
</q-fab>
@@ -224,9 +273,11 @@
<!-- User Search Dialog -->
<q-dialog v-model="showUserSearch" position="top">
<q-card class="chat-list-page__search-dialog">
<q-card-section>
<q-card class="chat-list-page__search-dialog" style="width: 100%; max-width: 500px;">
<q-card-section class="row items-center q-pb-none">
<div class="text-h6">Nuova conversazione</div>
<q-space />
<q-btn icon="close" flat round dense v-close-popup />
</q-card-section>
<q-card-section>
@@ -234,40 +285,91 @@
v-model="userSearchQuery"
placeholder="Cerca per nome o username..."
outlined
dense
autofocus
@update:model-value="searchUsers"
>
<template #prepend>
<template v-slot:prepend>
<q-icon name="search" />
</template>
<template v-if="userSearchQuery" v-slot:append>
<q-icon
name="close"
class="cursor-pointer"
@click="userSearchQuery = ''; searchedUsers = []"
/>
</template>
</q-input>
<q-list v-if="searchedUsers.length > 0" class="q-mt-md">
<!-- Searching spinner -->
<div v-if="searchingUsers" class="text-center q-pa-md">
<q-spinner color="primary" size="40px" />
</div>
<!-- Search results -->
<q-list v-else-if="searchedUsers.length > 0" class="q-mt-md">
<q-item
v-for="user in searchedUsers"
:key="user._id"
clickable
v-ripple
@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>
<div v-else class="chat-list-page__avatar-placeholder">
{{ getInitials(user) }}
</div>
</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-section side>
<q-icon name="chevron_right" color="grey" />
</q-item-section>
</q-item>
</q-list>
<div v-else-if="userSearchQuery && !searchingUsers" class="text-center q-pa-md text-grey">
Nessun utente trovato
<!-- No results -->
<div
v-else-if="userSearchQuery && userSearchQuery.length >= 2 && !searchingUsers"
class="text-center q-pa-md text-grey"
>
<q-icon name="person_off" size="48px" color="grey-4" />
<p class="q-mt-md">Nessun utente trovato</p>
</div>
<!-- Hint -->
<div
v-else-if="!userSearchQuery || userSearchQuery.length < 2"
class="text-center q-pa-md text-grey"
>
<q-icon name="search" size="48px" color="grey-4" />
<p class="q-mt-md">Digita almeno 2 caratteri per cercare</p>
</div>
</q-card-section>
</q-card>
</q-dialog>
<!-- Group Create Dialog (placeholder) -->
<q-dialog v-model="showGroupCreate">
<q-card style="min-width: 350px;">
<q-card-section>
<div class="text-h6">Crea gruppo</div>
</q-card-section>
<q-card-section class="q-pt-none">
<p class="text-grey">Funzionalità in arrivo...</p>
</q-card-section>
<q-card-actions align="right">
<q-btn flat label="Chiudi" color="primary" v-close-popup />
</q-card-actions>
</q-card>
</q-dialog>
</q-page>
</template>