Overview
The useReportesStore manages citizen reports (reportes) with support for filtering, image uploads, real-time subscriptions, and status tracking. It handles the full lifecycle of community reports from creation to resolution.
State Properties
Array of reports
id: string - Unique identifier
usuario_id: string - ID of citizen who created report
titulo: string - Report title
descripcion: string - Detailed description
categoria: string - Report category (e.g., “infraestructura”, “seguridad”)
ubicacion: string - Location description
latitud: number | null - GPS latitude
longitud: number | null - GPS longitude
imagen_url: string | null - Photo evidence URL
estado: “pendiente” | “en_proceso” | “resuelto” | “rechazado” - Report status
prioridad: “baja” | “media” | “alta” | null - Priority level
respuesta_admin: string | null - Admin response/notes
created_at: string - Creation timestamp
updated_at: string - Last update timestamp
Currently selected/viewed report
Loading state for async operations
Error message from last failed operation
Actions
fetchReportes
Fetches reports with optional filters.
const result = await reportesStore . fetchReportes ( filtros )
Optional filters for reports Filter by user ID (view user’s own reports)
estado
'pendiente' | 'en_proceso' | 'resuelto' | 'rechazado'
Filter by report status
return
Promise<{ success: boolean; data?: Reporte[]; error?: string }>
Result object with reports array
Example:
// Get user's pending reports
await reportesStore . fetchReportes ({
usuario_id: authStore . user . id ,
estado: 'pendiente'
})
fetchReporte
Fetches a single report by ID.
const result = await reportesStore . fetchReporte ( id )
return
Promise<{ success: boolean; data?: Reporte; error?: string }>
Result object with report data
crearReporte
Creates a new citizen report.
const result = await reportesStore . crearReporte ( reporte )
reporte
Omit<InsertReporte, 'usuario_id'>
required
Report data (usuario_id is added automatically) Detailed description of the issue
Category (e.g., “infraestructura”, “seguridad”, “servicios”)
Photo evidence URL (use subirImagen first)
estado
'pendiente'
default: "'pendiente'"
Initial status (always starts as pendiente)
return
Promise<{ success: boolean; data?: Reporte; error?: string }>
Result object with created report data
Behavior:
Requires authenticated user
Adds usuario_id automatically from auth store
Prepends new report to local reportes array
Logs detailed information for debugging
actualizarReporte
Updates an existing report (typically used by admins).
const result = await reportesStore . actualizarReporte ( id , updates )
updates
Omit<UpdateReporte, 'id' | 'usuario_id'>
required
Fields to update Show Common update fields
estado
'pendiente' | 'en_proceso' | 'resuelto' | 'rechazado'
Change report status
prioridad
'baja' | 'media' | 'alta'
Set priority level
return
Promise<{ success: boolean; data?: Reporte; error?: string }>
Result object with updated report data
Behavior:
Updates report in local reportes array if present
Updates reporteActual if it’s the currently viewed report
eliminarReporte
Deletes a report.
const result = await reportesStore . eliminarReporte ( id )
return
Promise<{ success: boolean; error?: string }>
Result object indicating success or failure
subirImagen
Uploads an image to Supabase storage for a report.
const result = await reportesStore . subirImagen ( file , reporteId )
Report ID (used in filename)
return
Promise<{ success: boolean; url?: string; error?: string }>
Result object with public URL of uploaded image
Behavior:
Generates unique filename: {reporteId}-{timestamp}.{ext}
Uploads to imagenes/reportes/ bucket
Returns public URL for use in report imagen_url field
subscribeToReportes
Subscribes to real-time updates for reports.
const subscription = reportesStore . subscribeToReportes ()
// Later, to unsubscribe:
subscription . unsubscribe ()
Supabase realtime channel subscription
Features:
Automatically filters updates based on user role:
Citizens : Only see updates to their own reports
Admins : See all report updates
Handles INSERT, UPDATE, and DELETE events
Updates local state automatically
Logs changes to console
Usage Examples
Create Report with Image
View User Reports
Admin Update Status
Real-time Updates
Filter by Status
< script setup >
import { useReportesStore } from '@/stores/reportes.store'
import { ref } from 'vue'
const reportesStore = useReportesStore ()
const imageFile = ref < File | null >( null )
async function enviarReporte () {
let imageUrl = null
// First, upload image if provided
if ( imageFile . value ) {
const uploadResult = await reportesStore . subirImagen (
imageFile . value ,
'temp-' + Date . now () // Temporary ID
)
if ( uploadResult . success ) {
imageUrl = uploadResult . url
}
}
// Then create report
const result = await reportesStore . crearReporte ({
titulo: 'Bache en la vía principal' ,
descripcion: 'Hay un bache grande que necesita reparación' ,
categoria: 'infraestructura' ,
ubicacion: 'Av. Principal, frente al parque' ,
latitud: - 0.95 ,
longitud: - 80.73 ,
imagen_url: imageUrl ,
estado: 'pendiente'
})
if ( result . success ) {
console . log ( 'Reporte creado:' , result . data ?. id )
}
}
</ script >
< script setup >
import { useReportesStore } from '@/stores/reportes.store'
import { useAuthStore } from '@/stores/auth.store'
import { onMounted , computed } from 'vue'
const reportesStore = useReportesStore ()
const authStore = useAuthStore ()
const misReportes = computed (() => reportesStore . reportes )
onMounted ( async () => {
// Fetch current user's reports
await reportesStore . fetchReportes ({
usuario_id: authStore . user ?. id
})
})
</ script >
< template >
< div >
< h2 > Mis Reportes ({{ misReportes . length }}) </ h2 >
< div v-for = " reporte in misReportes " : key = " reporte . id " >
< h3 > {{ reporte . titulo }} </ h3 >
< span > Estado: {{ reporte . estado }} </ span >
</ div >
</ div >
</ template >
< script setup >
import { useReportesStore } from '@/stores/reportes.store'
const reportesStore = useReportesStore ()
async function marcarEnProceso ( reporteId : string ) {
const result = await reportesStore . actualizarReporte ( reporteId , {
estado: 'en_proceso' ,
prioridad: 'alta' ,
respuesta_admin: 'Hemos recibido tu reporte y estamos trabajando en ello'
})
if ( result . success ) {
console . log ( 'Estado actualizado' )
}
}
async function resolverReporte ( reporteId : string ) {
const result = await reportesStore . actualizarReporte ( reporteId , {
estado: 'resuelto' ,
respuesta_admin: 'El problema ha sido resuelto exitosamente'
})
}
</ script >
< script setup >
import { useReportesStore } from '@/stores/reportes.store'
import { onMounted , onUnmounted } from 'vue'
const reportesStore = useReportesStore ()
let subscription : any = null
onMounted ( async () => {
// Fetch initial data
await reportesStore . fetchReportes ()
// Subscribe to real-time updates
subscription = reportesStore . subscribeToReportes ()
console . log ( 'Suscrito a cambios en reportes' )
})
onUnmounted (() => {
// Clean up subscription
if ( subscription ) {
subscription . unsubscribe ()
console . log ( 'Desuscrito de reportes' )
}
})
</ script >
< script setup >
import { useReportesStore } from '@/stores/reportes.store'
import { ref } from 'vue'
const reportesStore = useReportesStore ()
const estadoFiltro = ref < 'pendiente' | 'en_proceso' | 'resuelto' >( 'pendiente' )
async function cargarReportes () {
await reportesStore . fetchReportes ({
estado: estadoFiltro . value
})
}
async function cambiarFiltro ( nuevoEstado : typeof estadoFiltro . value ) {
estadoFiltro . value = nuevoEstado
await cargarReportes ()
}
</ script >
< template >
< div >
< button @ click = " cambiarFiltro ( 'pendiente' ) " > Pendientes </ button >
< button @ click = " cambiarFiltro ( 'en_proceso' ) " > En Proceso </ button >
< button @ click = " cambiarFiltro ( 'resuelto' ) " > Resueltos </ button >
< div v-for = " reporte in reportesStore . reportes " : key = " reporte . id " >
{{ reporte . titulo }}
</ div >
</ div >
</ template >
Report Status Flow
Typical Report Lifecycle:
pendiente (Pending): Initial state when citizen creates report
en_proceso (In Progress): Admin has acknowledged and is working on it
resuelto (Resolved): Issue has been fixed
rechazado (Rejected): Report was invalid or duplicate
Real-time Updates
The subscribeToReportes() action creates a persistent connection. Always unsubscribe when the component unmounts to prevent memory leaks.
Real-time filtering by user role:
Citizens automatically see only updates to their own reports
Admins see all report changes across the system
Image Upload
Upload images BEFORE creating the report to get the URL, then include it in the report data. The filename includes the report ID and timestamp for uniqueness.
Notes
All reports are ordered by created_at in descending order (newest first) by default.