feat: enhance feedback handling and status checks in report management

This commit is contained in:
gabdevele
2026-06-18 18:05:49 +02:00 Unverified
parent 2c907044a3
commit fc47313b8a
2 changed files with 54 additions and 12 deletions
+2
View File
@@ -60,6 +60,7 @@ const handleResolve = async () => {
resolution_note: resolveModal.note || undefined resolution_note: resolveModal.note || undefined
}) })
resolveModal.pending = false
await refresh() await refresh()
closeResolveModal() closeResolveModal()
} catch (err) { } catch (err) {
@@ -90,6 +91,7 @@ const handleDismiss = async () => {
try { try {
await updateModerationFlag(dismissModal.flagId, { status: 'DISMISSED' }) await updateModerationFlag(dismissModal.flagId, { status: 'DISMISSED' })
dismissModal.pending = false
await refresh() await refresh()
closeDismissModal() closeDismissModal()
} catch (err) { } catch (err) {
+52 -12
View File
@@ -46,6 +46,10 @@ const isTakenByCurrentOperator = computed(() => report.value?.assigned_operator_
const isOpenAndUnassigned = computed(() => report.value?.status === 'OPEN' && !report.value?.assigned_operator_id) const isOpenAndUnassigned = computed(() => report.value?.status === 'OPEN' && !report.value?.assigned_operator_id)
const isReadOnlyPreview = computed(() => !isTakenByCurrentOperator.value) const isReadOnlyPreview = computed(() => !isTakenByCurrentOperator.value)
const canTakeReport = computed(() => isOpenAndUnassigned.value && !isTakenByCurrentOperator.value) const canTakeReport = computed(() => isOpenAndUnassigned.value && !isTakenByCurrentOperator.value)
const isFinalStatus = computed(() => ['REJECTED', 'RESOLVED', 'CLOSED'].includes(report.value?.status || ''))
const canAssignTeam = computed(() => !isReadOnlyPreview.value && !isFinalStatus.value && teams.value.length > 0)
const canSendMessage = computed(() => !isReadOnlyPreview.value && !isFinalStatus.value)
const canChangeStatus = computed(() => !isReadOnlyPreview.value && !isFinalStatus.value && selectableStatuses.value.length > 0)
const statusMeta: Record<string, string> = { const statusMeta: Record<string, string> = {
OPEN: 'Aperta', OPEN: 'Aperta',
@@ -108,6 +112,8 @@ const interventionLinkError = ref('')
const interventionLinkSuccess = ref('') const interventionLinkSuccess = ref('')
const isRevokeInterventionModalOpen = ref(false) const isRevokeInterventionModalOpen = ref(false)
const copyFeedback = ref('') const copyFeedback = ref('')
const actionError = ref('')
const actionSuccess = ref('')
const isSubmittingMessage = ref(false) const isSubmittingMessage = ref(false)
const flaggedMessageIds = ref<number[]>([]) const flaggedMessageIds = ref<number[]>([])
@@ -140,6 +146,11 @@ const resetReportFlagForm = () => {
reportFlagError.value = '' reportFlagError.value = ''
} }
const clearActionFeedback = () => {
actionError.value = ''
actionSuccess.value = ''
}
const setPublicInterventionLink = (url?: string) => { const setPublicInterventionLink = (url?: string) => {
if (!url) { if (!url) {
interventionPublicLink.value = '' interventionPublicLink.value = ''
@@ -158,21 +169,25 @@ const handleTakeReport = async () => {
if (!canTakeReport.value || !report.value) return if (!canTakeReport.value || !report.value) return
isTakingReport.value = true isTakingReport.value = true
clearActionFeedback()
try { try {
const res = await takeReport(report.value.id) const res = await takeReport(report.value.id)
if (res.ok) { if (res.ok) {
await Promise.all([refreshReport(), refreshSummary()]) await Promise.all([refreshReport(), refreshSummary()])
actionSuccess.value = 'Segnalazione presa in carico.'
} }
} catch (e) { } catch (e: any) {
console.error('Failed to take report', e) console.error('Failed to take report', e)
actionError.value = e?.data?.message || 'Presa in carico non riuscita.'
} finally { } finally {
isTakingReport.value = false isTakingReport.value = false
} }
} }
const handleStatusUpdate = async () => { const handleStatusUpdate = async () => {
if (isReadOnlyPreview.value) return if (!canChangeStatus.value) return
isUpdatingStatus.value = true isUpdatingStatus.value = true
clearActionFeedback()
try { try {
const res = await updateReportStatus(reportId, { const res = await updateReportStatus(reportId, {
status: newStatus.value, status: newStatus.value,
@@ -181,19 +196,22 @@ const handleStatusUpdate = async () => {
if (res.ok) { if (res.ok) {
statusNote.value = '' statusNote.value = ''
await Promise.all([refreshReport(), refreshSummary()]) await Promise.all([refreshReport(), refreshSummary()])
actionSuccess.value = 'Stato aggiornato con successo.'
} }
} catch (e) { } catch (e: any) {
console.error('Failed to update status', e) console.error('Failed to update status', e)
actionError.value = e?.data?.message || 'Aggiornamento stato non riuscito.'
} finally { } finally {
isUpdatingStatus.value = false isUpdatingStatus.value = false
} }
} }
const handleAssignTeam = async () => { const handleAssignTeam = async () => {
if (isReadOnlyPreview.value) return if (!canAssignTeam.value) return
if (!selectedTeamId.value) return if (!selectedTeamId.value) return
isAssigning.value = true isAssigning.value = true
clearActionFeedback()
try { try {
const res = await assignTeam(reportId, { const res = await assignTeam(reportId, {
team_id: selectedTeamId.value team_id: selectedTeamId.value
@@ -202,9 +220,11 @@ const handleAssignTeam = async () => {
isAssignModalOpen.value = false isAssignModalOpen.value = false
selectedTeamId.value = null selectedTeamId.value = null
await Promise.all([refreshReport(), refreshSummary()]) await Promise.all([refreshReport(), refreshSummary()])
actionSuccess.value = 'Team assegnato con successo.'
} }
} catch (e) { } catch (e: any) {
console.error('Failed to assign team', e) console.error('Failed to assign team', e)
actionError.value = e?.data?.message || 'Assegnazione team non riuscita.'
} finally { } finally {
isAssigning.value = false isAssigning.value = false
} }
@@ -231,6 +251,7 @@ const copyInterventionLink = async () => {
const handleCreateInterventionLink = async () => { const handleCreateInterventionLink = async () => {
if (!canCreateInterventionLink.value) return if (!canCreateInterventionLink.value) return
isGeneratingInterventionLink.value = true isGeneratingInterventionLink.value = true
clearActionFeedback()
interventionLinkError.value = '' interventionLinkError.value = ''
interventionLinkSuccess.value = '' interventionLinkSuccess.value = ''
@@ -264,6 +285,7 @@ const handleRevokeInterventionLink = async () => {
if (!interventionLink.value) return if (!interventionLink.value) return
isRevokingInterventionLink.value = true isRevokingInterventionLink.value = true
clearActionFeedback()
interventionLinkError.value = '' interventionLinkError.value = ''
interventionLinkSuccess.value = '' interventionLinkSuccess.value = ''
@@ -285,14 +307,17 @@ const handleRevokeInterventionLink = async () => {
} }
const handleSendMessage = async (msgText: string, files: File[]) => { const handleSendMessage = async (msgText: string, files: File[]) => {
if (isReadOnlyPreview.value) return if (!canSendMessage.value) return
if (!msgText.trim() && files.length === 0) return if (!msgText.trim() && files.length === 0) return
isSubmittingMessage.value = true isSubmittingMessage.value = true
clearActionFeedback()
try { try {
await addMessage(reportId, { body: msgText }) await addMessage(reportId, { body: msgText })
await refreshMessages() await refreshMessages()
} catch (e) { actionSuccess.value = 'Messaggio inviato.'
} catch (e: any) {
console.error('Failed to send message', e) console.error('Failed to send message', e)
actionError.value = e?.data?.message || 'Invio messaggio non riuscito.'
} finally { } finally {
isSubmittingMessage.value = false isSubmittingMessage.value = false
} }
@@ -325,8 +350,10 @@ const executeFlagMessage = async () => {
flaggedMessageIds.value.push(messageToFlag.value.id) flaggedMessageIds.value.push(messageToFlag.value.id)
isFlagMessageModalOpen.value = false isFlagMessageModalOpen.value = false
messageToFlag.value = null messageToFlag.value = null
} catch (e) { actionSuccess.value = 'Messaggio segnalato alla moderazione.'
} catch (e: any) {
console.error('Failed to flag message', e) console.error('Failed to flag message', e)
actionError.value = e?.data?.message || 'Segnalazione messaggio non riuscita.'
} finally { } finally {
isFlaggingMessage.value = false isFlaggingMessage.value = false
} }
@@ -419,6 +446,13 @@ const formattedInterventionLinkExpiry = computed(() =>
</div> </div>
<div class="container my-5"> <div class="container my-5">
<div v-if="actionError" class="alert alert-danger mb-4">
{{ actionError }}
</div>
<div v-if="actionSuccess" class="alert alert-success mb-4">
{{ actionSuccess }}
</div>
<div class="row g-4"> <div class="row g-4">
<div class="col-12 col-xl-7"> <div class="col-12 col-xl-7">
<div class="card card-bg border-0 shadow-sm mb-4"> <div class="card card-bg border-0 shadow-sm mb-4">
@@ -495,9 +529,12 @@ const formattedInterventionLinkExpiry = computed(() =>
@flag="handleFlagMessage" @flag="handleFlagMessage"
/> />
<div v-if="isReadOnlyPreview" class="alert alert-secondary mt-3 mb-0 small"> <div v-if="isReadOnlyPreview" class="alert alert-secondary mt-3 mb-0 small">
Messaggistica disponibile dopo la presa in carico. {{ isFinalStatus ? 'Messaggistica non disponibile per segnalazioni concluse o rifiutate.' : 'Messaggistica disponibile dopo la presa in carico.' }}
</div>
<MessageComposer v-else-if="canSendMessage" @send="handleSendMessage" />
<div v-else class="alert alert-secondary mt-3 mb-0 small">
Messaggistica non disponibile per segnalazioni concluse o rifiutate.
</div> </div>
<MessageComposer v-else @send="handleSendMessage" />
</div> </div>
</div> </div>
</div> </div>
@@ -529,9 +566,12 @@ const formattedInterventionLinkExpiry = computed(() =>
</div> </div>
</div> </div>
<div class="card-body pt-0" v-if="!isReadOnlyPreview"> <div class="card-body pt-0" v-if="!isReadOnlyPreview">
<button class="btn btn-outline-primary w-100" @click="isAssignModalOpen = true" :disabled="teams.length === 0"> <button class="btn btn-outline-primary w-100" @click="isAssignModalOpen = true" :disabled="!canAssignTeam">
Assegna team Assegna team
</button> </button>
<div v-if="isFinalStatus" class="small text-muted mt-2">
Assegnazione team non disponibile per segnalazioni concluse o rifiutate.
</div>
</div> </div>
</div> </div>
@@ -597,7 +637,7 @@ const formattedInterventionLinkExpiry = computed(() =>
</div> </div>
</template> </template>
<template v-else-if="selectableStatuses.length > 0"> <template v-else-if="canChangeStatus">
<form @submit.prevent="handleStatusUpdate"> <form @submit.prevent="handleStatusUpdate">
<div class="mb-3"> <div class="mb-3">
<label class="form-label">Nuovo Stato</label> <label class="form-label">Nuovo Stato</label>