feat(dashboard): add loading state for report assignment and truncate addresses

This commit is contained in:
2026-06-15 23:12:40 +02:00 Unverified
parent b6e7cc5b71
commit 107cb61ea4
2 changed files with 50 additions and 38 deletions
+39 -34
View File
@@ -9,6 +9,7 @@ interface Props {
actionVariant?: ActionVariant actionVariant?: ActionVariant
actionLabel?: string actionLabel?: string
showAssignee?: boolean showAssignee?: boolean
processingIds?: number[]
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
@@ -16,6 +17,7 @@ const props = withDefaults(defineProps<Props>(), {
actionVariant: 'open', actionVariant: 'open',
actionLabel: 'Open report', actionLabel: 'Open report',
showAssignee: false, showAssignee: false,
processingIds: () => [],
}) })
const emit = defineEmits<{ const emit = defineEmits<{
@@ -38,52 +40,55 @@ const reportHref = (report: Report) => `${props.detailBasePath}/${report.id}`
<div class="table-responsive report-table-wrapper"> <div class="table-responsive report-table-wrapper">
<table class="table table-hover report-table mb-0"> <table class="table table-hover report-table mb-0">
<thead> <thead>
<tr> <tr>
<th scope="col">ID</th> <th scope="col">ID</th>
<th scope="col">Categoria</th> <th scope="col">Categoria</th>
<th scope="col">Indirizzo</th> <th scope="col">Indirizzo</th>
<th scope="col">Stato</th> <th scope="col">Stato</th>
<th v-if="showAssignee" scope="col">Operatore</th> <th v-if="showAssignee" scope="col">Operatore</th>
<th scope="col">Creazione</th> <th scope="col">Creazione</th>
<th scope="col" class="text-end">Azioni</th> <th scope="col" class="text-end">Azioni</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="report in reports" :key="report.id" class="align-middle"> <tr v-for="report in reports" :key="report.id" class="align-middle">
<td class="report-table-id">#{{ report.id }}</td> <td class="report-table-id">#{{ report.id }}</td>
<td class="text-nowrap">{{ report.category_name || '-' }}</td> <td class="text-nowrap">{{ report.category_name || '-' }}</td>
<td> <td>
<span class="report-address" :title="report.address"> <!-- Added inline-block, text-truncate (for ellipsis), and max-width 10ch -->
<span class="report-address d-inline-block text-truncate align-middle" style="max-width: 10ch;" :title="report.address">
{{ report.address || '-' }} {{ report.address || '-' }}
</span> </span>
</td> </td>
<td> <td>
<StatusBadge :status="report.status" /> <StatusBadge :status="report.status" />
</td> </td>
<td v-if="showAssignee" class="text-muted"> <td v-if="showAssignee" class="text-muted">
{{ report.assigned_operator_id ? `#${report.assigned_operator_id}` : '-' }} {{ report.assigned_operator_id ? `#${report.assigned_operator_id}` : '-' }}
</td> </td>
<td class="text-muted text-nowrap">{{ formatDate(report.created_at) }}</td> <td class="text-muted text-nowrap">{{ formatDate(report.created_at) }}</td>
<td class="text-end"> <td class="text-end">
<button <button
v-if="actionVariant === 'manage'" v-if="actionVariant === 'manage'"
type="button" type="button"
class="btn btn-primary btn-xs" class="btn btn-primary btn-xs"
:disabled="processingIds.includes(report.id)"
@click="emit('take', report.id)" @click="emit('take', report.id)"
> >
Prendi in carico <span v-if="processingIds.includes(report.id)" class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>
</button> Prendi in carico
<NuxtLink </button>
<NuxtLink
v-else v-else
:to="reportHref(report)" :to="reportHref(report)"
class="btn btn-outline-primary btn-xs btn-icon" class="btn btn-outline-primary btn-xs btn-icon d-inline-flex align-items-center justify-content-center"
:aria-label="actionLabel" :aria-label="actionLabel"
:title="actionLabel" :title="actionLabel"
> >
<BIIcon name="it-arrow-right" size="xs" /> <BIIcon name="it-arrow-right" size="xs" />
</NuxtLink> </NuxtLink>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
+10 -3
View File
@@ -4,10 +4,17 @@ const { getOpenReports, getAssignedReports, takeReport } = useOperator()
const { data: openReportsRes, refresh: refreshOpen } = useAsyncData('open-reports', () => getOpenReports({ per_page: 5 })) const { data: openReportsRes, refresh: refreshOpen } = useAsyncData('open-reports', () => getOpenReports({ per_page: 5 }))
const { data: assignedReportsRes, refresh: refreshAssigned } = useAsyncData('assigned-reports', () => getAssignedReports({ per_page: 5 })) const { data: assignedReportsRes, refresh: refreshAssigned } = useAsyncData('assigned-reports', () => getAssignedReports({ per_page: 5 }))
const processingIds = ref<number[]>([])
const handleTakeReport = async (id: number) => { const handleTakeReport = async (id: number) => {
const res = await takeReport(id) processingIds.value.push(id)
if (res.ok) { try {
await Promise.all([refreshOpen(), refreshAssigned()]) const res = await takeReport(id)
if (res.ok) {
await Promise.all([refreshOpen(), refreshAssigned()])
}
} finally {
processingIds.value = processingIds.value.filter(pid => pid !== id)
} }
} }
</script> </script>