feat: enhance feedback handling and status checks in report management
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user