diff --git a/backend/app/api/v1/endpoints/tickets.py b/backend/app/api/v1/endpoints/tickets.py index 1ed5df4..53d60f3 100644 --- a/backend/app/api/v1/endpoints/tickets.py +++ b/backend/app/api/v1/endpoints/tickets.py @@ -217,12 +217,18 @@ async def get_all_tickets_admin( status_filter: Optional[str] = None, priority_filter: Optional[str] = None, tenant_id_filter: Optional[str] = None, + category_filter: Optional[str] = None, + assigned_to_filter: Optional[str] = None, + search: Optional[str] = None, + date_from: Optional[str] = None, + date_to: Optional[str] = None, db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user) ): """ Obtener todos los tickets de todos los tenants (solo para administradores) Incluye información del tenant y usuario que creó el ticket + Filtros: estado, prioridad, tenant, categoría, asignado a, búsqueda de texto y fechas """ # Verificar que el usuario sea administrador if current_user.role not in ["ADMIN", "SUPPORT_MANAGER"]: @@ -269,6 +275,57 @@ async def get_all_tickets_admin( detail="Invalid tenant ID format" ) + if category_filter: + try: + category_uuid = uuid.UUID(category_filter) + query = query.where(Ticket.category_id == category_uuid) + except ValueError: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Invalid category ID format" + ) + + if assigned_to_filter: + try: + assigned_uuid = uuid.UUID(assigned_to_filter) + query = query.where(Ticket.assigned_to == assigned_uuid) + except ValueError: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Invalid assigned user ID format" + ) + + if search: + # Búsqueda de texto en subject y description + search_pattern = f"%{search}%" + query = query.where( + (Ticket.subject.ilike(search_pattern)) | + (Ticket.description.ilike(search_pattern)) + ) + + if date_from: + try: + from datetime import datetime + date_from_parsed = datetime.fromisoformat(date_from) + query = query.where(Ticket.created_at >= date_from_parsed) + except ValueError: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Invalid date_from format. Use YYYY-MM-DD" + ) + + if date_to: + try: + from datetime import datetime, timedelta + # Agregar 1 día para incluir todo el día final + date_to_parsed = datetime.fromisoformat(date_to) + timedelta(days=1) + query = query.where(Ticket.created_at < date_to_parsed) + except ValueError: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Invalid date_to format. Use YYYY-MM-DD" + ) + query = query.order_by(Ticket.created_at.desc()).offset(skip).limit(limit) result = await db.execute(query) diff --git a/frontend-internal/src/routes/tickets/+page.svelte b/frontend-internal/src/routes/tickets/+page.svelte index 55a38a8..d115088 100644 --- a/frontend-internal/src/routes/tickets/+page.svelte +++ b/frontend-internal/src/routes/tickets/+page.svelte @@ -20,6 +20,14 @@ let filterStatus = ''; let filterPriority = ''; let filterTenant = ''; // Nuevo filtro por cliente/tenant + let filterCategory = ''; // Filtro por categoría + let filterAssignedTo = ''; // Filtro por asignado a + let searchText = ''; // Búsqueda por texto + let filterDateFrom = ''; // Fecha desde + let filterDateTo = ''; // Fecha hasta + + // Estado de filtros + $: activeFiltersCount = [filterTenant, filterStatus, filterPriority, filterCategory, filterAssignedTo, searchText, filterDateFrom, filterDateTo].filter(f => f && f.trim()).length; // Form para editar let editFormData = { @@ -61,6 +69,11 @@ if (filterStatus) ticketParams.status_filter = filterStatus; if (filterPriority) ticketParams.priority_filter = filterPriority; if (filterTenant) ticketParams.tenant_id_filter = filterTenant; + if (filterCategory) ticketParams.category_filter = filterCategory; + if (filterAssignedTo) ticketParams.assigned_to_filter = filterAssignedTo; + if (searchText) ticketParams.search = searchText; + if (filterDateFrom) ticketParams.date_from = filterDateFrom; + if (filterDateTo) ticketParams.date_to = filterDateTo; const [ticketsData, categoriesData, systemsData, usersData, tenantsData] = await Promise.all([ // Usar el nuevo endpoint administrativo @@ -86,6 +99,28 @@ function applyFilters() { loadData(); } + + // Limpiar todos los filtros + function clearFilters() { + filterStatus = ''; + filterPriority = ''; + filterTenant = ''; + filterCategory = ''; + filterAssignedTo = ''; + searchText = ''; + filterDateFrom = ''; + filterDateTo = ''; + applyFilters(); + } + + // Búsqueda en tiempo real (debounced) + let searchTimeout; + function handleSearchInput() { + clearTimeout(searchTimeout); + searchTimeout = setTimeout(() => { + applyFilters(); + }, 500); + } function openCreateModal() { selectedTicket = null; @@ -225,12 +260,31 @@
Gestión de tickets del sistema de mesa de ayuda.
++ Gestión de tickets del sistema de mesa de ayuda. + {#if activeFiltersCount > 0} + + {activeFiltersCount} filtro{activeFiltersCount !== 1 ? 's' : ''} activo{activeFiltersCount !== 1 ? 's' : ''} + + {/if} +