217 lines
5.1 KiB
Vue
217 lines
5.1 KiB
Vue
<script setup lang="ts">
|
|
import type { Message } from '~/types/api'
|
|
|
|
interface Props {
|
|
messages: Message[]
|
|
isLoading?: boolean
|
|
canFlagMessage?: (message: Message) => boolean
|
|
isFlaggingMessageId?: number | null
|
|
}
|
|
|
|
withDefaults(defineProps<Props>(), {
|
|
isLoading: false,
|
|
canFlagMessage: () => false,
|
|
isFlaggingMessageId: null,
|
|
})
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'flag', message: Message): void
|
|
}>()
|
|
|
|
const formatDate = (dateString: string): string => {
|
|
const date = new Date(dateString)
|
|
const today = new Date()
|
|
const yesterday = new Date(today)
|
|
yesterday.setDate(yesterday.getDate() - 1)
|
|
|
|
if (date.toDateString() === today.toDateString()) {
|
|
return date.toLocaleTimeString('it-IT', { hour: '2-digit', minute: '2-digit' })
|
|
} else if (date.toDateString() === yesterday.toDateString()) {
|
|
return 'Ieri'
|
|
} else {
|
|
return date.toLocaleDateString('it-IT', { day: '2-digit', month: '2-digit', year: '2-digit' })
|
|
}
|
|
}
|
|
|
|
const auth = useAuth()
|
|
|
|
const isCurrentUser = (userId: number): boolean => auth.user.value?.id === userId
|
|
|
|
const senderInitials = (msg: Message): string =>
|
|
`${msg.first_name?.[0] || ''}${msg.last_name?.[0] || ''}`.toUpperCase() || '?'
|
|
</script>
|
|
|
|
<template>
|
|
<div class="message-list">
|
|
<div v-if="isLoading" class="text-center py-5">
|
|
<div class="spinner-border text-primary" role="status">
|
|
<span class="visually-hidden">Caricamento...</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-else-if="messages.length === 0" class="message-empty-state" role="alert">
|
|
<BIIcon name="it-comment" size="lg" color="primary" class="mb-2" />
|
|
<strong class="d-block mb-1">Nessun messaggio</strong>
|
|
<span>Usa chat per aggiornamenti rapidi tra cittadino e operatori.</span>
|
|
</div>
|
|
|
|
<div v-else class="messages-container">
|
|
<div
|
|
v-for="msg in messages"
|
|
:key="msg.id"
|
|
class="message-row"
|
|
:class="{ 'is-current-user': isCurrentUser(msg.user_id) }"
|
|
>
|
|
<div v-if="!isCurrentUser(msg.user_id)" class="message-avatar" aria-hidden="true">
|
|
{{ senderInitials(msg) }}
|
|
</div>
|
|
<div
|
|
class="message-bubble-content"
|
|
:class="{
|
|
'message-bubble-outgoing': isCurrentUser(msg.user_id),
|
|
'message-bubble-incoming': !isCurrentUser(msg.user_id)
|
|
}"
|
|
>
|
|
<div class="message-meta">
|
|
<span v-if="!isCurrentUser(msg.user_id)" class="message-author">
|
|
{{ msg.first_name }} {{ msg.last_name }}
|
|
</span>
|
|
<span v-else class="message-author">Tu</span>
|
|
<span class="message-time">
|
|
{{ formatDate(msg.created_at) }}
|
|
</span>
|
|
</div>
|
|
|
|
<div v-if="!isCurrentUser(msg.user_id)" class="small font-weight-bold mb-2 text-primary d-none">
|
|
{{ msg.first_name }} {{ msg.last_name }}
|
|
</div>
|
|
|
|
<div class="message-body">
|
|
{{ msg.body }}
|
|
</div>
|
|
|
|
<div v-if="canFlagMessage(msg)" class="message-actions">
|
|
<button
|
|
type="button"
|
|
class="btn btn-link btn-xs px-0"
|
|
:disabled="isFlaggingMessageId === msg.id"
|
|
@click="emit('flag', msg)"
|
|
>
|
|
<span v-if="isFlaggingMessageId === msg.id" class="spinner-border spinner-border-sm me-1" aria-hidden="true"></span>
|
|
<BIIcon v-else name="it-flag" size="xs" class="me-1" />
|
|
Segnala messaggio
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.message-list {
|
|
border: 1px solid #d9e3ee;
|
|
border-radius: 4px;
|
|
max-height: 34rem;
|
|
overflow-y: auto;
|
|
padding: 1rem;
|
|
}
|
|
|
|
.messages-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.875rem;
|
|
}
|
|
|
|
.message-empty-state {
|
|
align-items: center;
|
|
color: #5c6f82;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
min-height: 14rem;
|
|
padding: 2rem 1rem;
|
|
text-align: center;
|
|
}
|
|
|
|
.message-row {
|
|
align-items: flex-end;
|
|
display: flex;
|
|
gap: 0.75rem;
|
|
justify-content: flex-start;
|
|
}
|
|
|
|
.message-row.is-current-user {
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
.message-avatar {
|
|
align-items: center;
|
|
background: #e6f0fa;
|
|
border-radius: 999px;
|
|
color: #17324d;
|
|
display: inline-flex;
|
|
flex: 0 0 2.25rem;
|
|
font-size: 0.75rem;
|
|
font-weight: 700;
|
|
height: 2.25rem;
|
|
justify-content: center;
|
|
}
|
|
|
|
.message-bubble-content {
|
|
border-radius: 4px;
|
|
max-width: min(100%, 40rem);
|
|
padding: 0.875rem 1rem;
|
|
word-break: break-word;
|
|
}
|
|
|
|
.message-bubble-incoming {
|
|
background: #f5f7f9;
|
|
border: 1px solid #d9e3ee;
|
|
color: #17324d;
|
|
}
|
|
|
|
.message-bubble-outgoing {
|
|
background: #06c;
|
|
color: #fff;
|
|
}
|
|
|
|
.message-meta {
|
|
align-items: center;
|
|
display: flex;
|
|
font-size: 0.8125rem;
|
|
gap: 0.75rem;
|
|
justify-content: space-between;
|
|
margin-bottom: 0.375rem;
|
|
}
|
|
|
|
.message-author {
|
|
font-weight: 700;
|
|
}
|
|
|
|
.message-time {
|
|
opacity: 0.78;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.message-body {
|
|
line-height: 1.4;
|
|
white-space: pre-wrap;
|
|
}
|
|
|
|
.message-actions {
|
|
margin-top: 0.5rem;
|
|
text-align: right;
|
|
}
|
|
|
|
.message-actions .btn {
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
@media (max-width: 767.98px) {
|
|
.message-bubble-content {
|
|
max-width: calc(100% - 3rem);
|
|
}
|
|
}
|
|
</style>
|