style(reports): simplify timeline ui and update composer integration

This commit is contained in:
2026-06-15 23:12:41 +02:00 Unverified
parent 7138123df5
commit c9f628391a
+55 -43
View File
@@ -31,7 +31,10 @@ const statusHistory = computed(() => (reportData.value as any)?.history || [])
const messages = computed(() => (messagesData.value as any)?.messages || []) const messages = computed(() => (messagesData.value as any)?.messages || [])
const newMessage = ref('') const newMessage = ref('')
const fileInput = ref<HTMLInputElement | null>(null)
const selectedFiles = ref<File[]>([])
const isSubmittingMessage = ref(false) const isSubmittingMessage = ref(false)
const isFeedbackOpen = ref(false) const isFeedbackOpen = ref(false)
const feedbackRating = ref(5) const feedbackRating = ref(5)
const feedbackComment = ref('') const feedbackComment = ref('')
@@ -39,14 +42,28 @@ const isSubmittingFeedback = ref(false)
const selectedMediaIndex = ref(0) const selectedMediaIndex = ref(0)
const isGalleryOpen = ref(false) const isGalleryOpen = ref(false)
// Gestione selezione file
const handleFileSelect = (event: Event) => {
const target = event.target as HTMLInputElement
if (target.files) {
selectedFiles.value = Array.from(target.files)
}
}
// Invia messaggio // Invia messaggio
const handleSendMessage = async (msgText: string, files: File[]) => { const handleSendMessage = async () => {
if (!msgText.trim() && files.length === 0) return if (!newMessage.value.trim() && selectedFiles.value.length === 0) return
isSubmittingMessage.value = true isSubmittingMessage.value = true
try { try {
// Gestione mock di file, nella realtà si usa FormData o simle // Gestione mock di file, nella realtà si usa FormData o simile
await addMessage(reportId, { body: msgText }) await addMessage(reportId, { body: newMessage.value })
// Pulisci i campi
newMessage.value = ''
selectedFiles.value = []
if (fileInput.value) fileInput.value.value = ''
await refreshMessages() await refreshMessages()
} catch (error) { } catch (error) {
console.error('Error sending message:', error) console.error('Error sending message:', error)
@@ -75,7 +92,10 @@ const handleSubmitFeedback = async () => {
} }
const isOwner = computed(() => report.value?.user_id === user.value?.id) const isOwner = computed(() => report.value?.user_id === user.value?.id)
const canFeedback = computed(() => report.value?.status === 'RESOLVED' || report.value?.status === 'CLOSED')
// Valutazione solo se lo stato è specificamente "RESOLVED"
const canFeedback = computed(() => report.value?.status === 'RESOLVED')
const statusMeta: Record<string, { label: string; tone: string }> = { const statusMeta: Record<string, { label: string; tone: string }> = {
OPEN: { label: 'Aperta', tone: 'info' }, OPEN: { label: 'Aperta', tone: 'info' },
UNDER_REVIEW: { label: 'In revisione', tone: 'warning' }, UNDER_REVIEW: { label: 'In revisione', tone: 'warning' },
@@ -180,30 +200,21 @@ const formatDate = (dateString: string): string => {
</div> </div>
</div> </div>
<div v-if="statusHistory.length > 0" class="card card-bg border-0 shadow-sm mb-4"> <div class="card card-bg border-0 shadow-sm mb-4">
<div class="card-body"> <div class="card-body">
<div class="report-section-head mb-4"> <h2 class="h6 mb-3">Cronologia eventi</h2>
<BIIcon name="it-history" size="sm" color="primary" class="me-2" /> <div v-if="statusHistory.length === 0" class="small text-muted">Nessun evento registrato.</div>
<h2 class="h6 mb-0">Cronologia</h2> <ul class="list-unstyled mb-0 ms-2">
</div> <li v-for="item in statusHistory" :key="item.id" class="mb-3 border-start border-primary border-3 ps-3">
<div class="timeline"> <small class="text-muted d-block">{{ formatDate(item.created_at) }}</small>
<div v-for="item in statusHistory" :key="item.id" class="timeline-item mb-3"> <div class="d-flex align-items-center gap-2 mt-1">
<div class="d-flex gap-3">
<div class="flex-shrink-0">
<div class="timeline-marker"></div>
</div>
<div class="flex-grow-1">
<small class="text-muted">{{ formatDate(item.created_at) }}</small>
<div class="small font-weight-semibold d-flex align-items-center gap-2 mt-1">
<StatusBadge :status="item.old_status" /> <StatusBadge :status="item.old_status" />
<BIIcon name="it-arrow-right" size="xs" /> <BIIcon name="it-arrow-right" size="xs" />
<StatusBadge :status="item.new_status" /> <StatusBadge :status="item.new_status" />
</div> </div>
<small v-if="item.note" class="text-muted d-block">{{ item.note }}</small> <div v-if="item.note" class="small mt-1 bg-light p-1 rounded">{{ item.note }}</div>
</div> </li>
</div> </ul>
</div>
</div>
</div> </div>
</div> </div>
@@ -219,8 +230,10 @@ const formatDate = (dateString: string): string => {
</div> </div>
<span class="badge bg-light text-dark border">{{ messages.length }} messaggi</span> <span class="badge bg-light text-dark border">{{ messages.length }} messaggi</span>
</div> </div>
<MessageList :messages="messages" /> <MessageList :messages="messages" />
<MessageComposer @send="handleSendMessage" />
<MessageComposer @send=handleSendMessage />
</div> </div>
</div> </div>
</div> </div>
@@ -408,23 +421,25 @@ const formatDate = (dateString: string): string => {
justify-content: space-between; justify-content: space-between;
} }
.timeline-marker { /* Stili specifici per l'input della Chat per gestire le tue richieste visive */
width: 12px; .chat-textarea {
height: 12px; resize: none !important;
background-color: #006f9d; height: 48px;
border-radius: 50%; min-height: 48px;
border: 3px solid #f0f0f0; overflow-y: auto;
padding-top: 12px;
padding-bottom: 12px;
border-radius: 6px;
} }
.timeline-item:not(:last-child) .timeline-marker::after { .btn-square {
content: ''; width: 48px;
position: absolute; height: 48px;
left: 50%; padding: 0;
top: 20px; display: flex;
width: 2px; align-items: center;
height: calc(100% + 20px); justify-content: center;
background-color: #006f9d; border-radius: 6px;
transform: translateX(-50%);
} }
.ratio { .ratio {
@@ -457,6 +472,3 @@ const formatDate = (dateString: string): string => {
} }
} }
</style> </style>