feat(admin): add delete functionality for areas, categories, and teams in admin panel
This commit is contained in:
@@ -17,7 +17,7 @@ if (!reportId) {
|
||||
}
|
||||
|
||||
const { getReportDetail, getMessages, addMessage, flagMessage } = useReports()
|
||||
const { getInterventionSummary, updateReportStatus, assignTeam, getTeams } = useOperator()
|
||||
const { getInterventionSummary, updateReportStatus, assignTeam, getTeams, createInterventionLink, revokeInterventionLink } = useOperator()
|
||||
const { user } = useAuth()
|
||||
|
||||
// Dati principali
|
||||
@@ -52,6 +52,13 @@ const isUpdatingStatus = ref(false)
|
||||
const isAssignModalOpen = ref(false)
|
||||
const selectedTeamId = ref<number | null>(null)
|
||||
const isAssigning = ref(false)
|
||||
const interventionLink = ref('')
|
||||
const interventionLinkExpiresAt = ref('')
|
||||
const isGeneratingInterventionLink = ref(false)
|
||||
const isRevokingInterventionLink = ref(false)
|
||||
const interventionLinkError = ref('')
|
||||
const interventionLinkSuccess = ref('')
|
||||
const isRevokeInterventionModalOpen = ref(false)
|
||||
|
||||
// Messaggi
|
||||
const isSubmittingMessage = ref(false)
|
||||
@@ -109,6 +116,72 @@ const handleAssignTeam = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const copyInterventionLink = async () => {
|
||||
if (!interventionLink.value || !navigator?.clipboard) return
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(interventionLink.value)
|
||||
interventionLinkSuccess.value = 'Link copiato negli appunti.'
|
||||
interventionLinkError.value = ''
|
||||
} catch {
|
||||
interventionLinkError.value = 'Copia automatica non riuscita.'
|
||||
interventionLinkSuccess.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
const handleCreateInterventionLink = async () => {
|
||||
isGeneratingInterventionLink.value = true
|
||||
interventionLinkError.value = ''
|
||||
interventionLinkSuccess.value = ''
|
||||
|
||||
try {
|
||||
const res = await createInterventionLink(reportId)
|
||||
if (res.ok) {
|
||||
interventionLink.value = res.url
|
||||
interventionLinkExpiresAt.value = res.expires_at
|
||||
interventionLinkSuccess.value = 'Link intervento generato.'
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.error('Failed to create intervention link', e)
|
||||
interventionLinkError.value = e?.data?.message || 'Generazione link non riuscita.'
|
||||
} finally {
|
||||
isGeneratingInterventionLink.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const openRevokeInterventionLinkModal = () => {
|
||||
if (!interventionLink.value) return
|
||||
isRevokeInterventionModalOpen.value = true
|
||||
}
|
||||
|
||||
const closeRevokeInterventionLinkModal = () => {
|
||||
if (isRevokingInterventionLink.value) return
|
||||
isRevokeInterventionModalOpen.value = false
|
||||
}
|
||||
|
||||
const handleRevokeInterventionLink = async () => {
|
||||
if (!interventionLink.value) return
|
||||
|
||||
isRevokingInterventionLink.value = true
|
||||
interventionLinkError.value = ''
|
||||
interventionLinkSuccess.value = ''
|
||||
|
||||
try {
|
||||
const res = await revokeInterventionLink(reportId)
|
||||
if (res.ok) {
|
||||
interventionLink.value = ''
|
||||
interventionLinkExpiresAt.value = ''
|
||||
interventionLinkSuccess.value = 'Link intervento revocato.'
|
||||
isRevokeInterventionModalOpen.value = false
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.error('Failed to revoke intervention link', e)
|
||||
interventionLinkError.value = e?.data?.message || 'Revoca link non riuscita.'
|
||||
} finally {
|
||||
isRevokingInterventionLink.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleSendMessage = async (msgText: string, files: File[]) => {
|
||||
if (!msgText.trim() && files.length === 0) return
|
||||
isSubmittingMessage.value = true
|
||||
@@ -158,6 +231,10 @@ const latestCitizenMessage = computed(() => {
|
||||
.reverse()
|
||||
.find((message) => message.user_id === reportUserId) || null
|
||||
})
|
||||
|
||||
const formattedInterventionLinkExpiry = computed(() =>
|
||||
interventionLinkExpiresAt.value ? formatDate(interventionLinkExpiresAt.value) : ''
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -292,6 +369,54 @@ const latestCitizenMessage = computed(() => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-bg border-0 shadow-sm mb-4">
|
||||
<div class="card-body">
|
||||
<h2 class="h6 mb-3">Link intervento pubblico</h2>
|
||||
<p class="text-muted small mb-3">Genera link temporaneo per team esterno. Nuova generazione ruota link esistente.</p>
|
||||
|
||||
<div v-if="interventionLinkError" class="alert alert-danger py-2" role="alert">
|
||||
{{ interventionLinkError }}
|
||||
</div>
|
||||
<div v-if="interventionLinkSuccess" class="alert alert-success py-2" role="alert">
|
||||
{{ interventionLinkSuccess }}
|
||||
</div>
|
||||
|
||||
<template v-if="interventionLink">
|
||||
<label class="form-label">URL attivo</label>
|
||||
<div class="input-group mb-2">
|
||||
<input type="text" class="form-control" :value="interventionLink" readonly />
|
||||
<button type="button" class="btn btn-outline-primary" @click="copyInterventionLink">
|
||||
Copia
|
||||
</button>
|
||||
</div>
|
||||
<p class="small text-muted mb-3">
|
||||
Scade: {{ formattedInterventionLinkExpiry || '-' }}
|
||||
</p>
|
||||
<div class="d-grid gap-2">
|
||||
<button type="button" class="btn btn-outline-primary" :disabled="isGeneratingInterventionLink" @click="handleCreateInterventionLink">
|
||||
<span v-if="isGeneratingInterventionLink" class="spinner-border spinner-border-sm me-2"></span>
|
||||
Rigenera link
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-danger" :disabled="isRevokingInterventionLink" @click="openRevokeInterventionLinkModal">
|
||||
<span v-if="isRevokingInterventionLink" class="spinner-border spinner-border-sm me-2"></span>
|
||||
Revoca link
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<button
|
||||
v-else
|
||||
type="button"
|
||||
class="btn btn-outline-primary w-100"
|
||||
:disabled="isGeneratingInterventionLink"
|
||||
@click="handleCreateInterventionLink"
|
||||
>
|
||||
<span v-if="isGeneratingInterventionLink" class="spinner-border spinner-border-sm me-2"></span>
|
||||
Genera link
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-bg border-0 shadow-sm mb-4">
|
||||
<div class="card-body">
|
||||
<h2 class="h6 mb-3">Aggiorna stato</h2>
|
||||
@@ -375,6 +500,16 @@ const latestCitizenMessage = computed(() => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ConfirmModal
|
||||
:open="isRevokeInterventionModalOpen"
|
||||
title="Revoca link intervento"
|
||||
message="Vuoi revocare link pubblico per team esterno? Link non sara piu utilizzabile."
|
||||
confirm-label="Revoca link"
|
||||
confirm-class="btn-danger"
|
||||
:pending="isRevokingInterventionLink"
|
||||
@cancel="closeRevokeInterventionLinkModal"
|
||||
@confirm="handleRevokeInterventionLink"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="text-center py-5">
|
||||
<div class="spinner-border text-primary" role="status"></div>
|
||||
|
||||
Reference in New Issue
Block a user