From 6a07d3ede596d9dace6a0cab2a6c0cc17847c840 Mon Sep 17 00:00:00 2001 From: Babak Alizadeh Date: Sat, 20 Sep 2025 23:02:58 +0330 Subject: [PATCH] more bug fix in support tickets --- .../repositories/support/ticket_repository.py | 65 ++++++++++++++ .../lib/pages/profile/support_page.dart | 2 +- .../widgets/data_table/data_table_config.dart | 4 + .../widgets/data_table/data_table_widget.dart | 89 +++++++++++++------ 4 files changed, 133 insertions(+), 27 deletions(-) diff --git a/hesabixAPI/adapters/db/repositories/support/ticket_repository.py b/hesabixAPI/adapters/db/repositories/support/ticket_repository.py index 215a566..b00badc 100644 --- a/hesabixAPI/adapters/db/repositories/support/ticket_repository.py +++ b/hesabixAPI/adapters/db/repositories/support/ticket_repository.py @@ -60,6 +60,34 @@ class TicketRepository(BaseRepository[Ticket]): )\ .filter(Ticket.user_id == user_id) + # اعمال فیلترها + if query_info.filters: + for filter_item in query_info.filters: + if filter_item.property == "title" and hasattr(Ticket, "title"): + if filter_item.operator == "*": + query = query.filter(Ticket.title.ilike(f"%{filter_item.value}%")) + elif filter_item.operator == "*?": + query = query.filter(Ticket.title.ilike(f"{filter_item.value}%")) + elif filter_item.operator == "?*": + query = query.filter(Ticket.title.ilike(f"%{filter_item.value}")) + elif filter_item.operator == "=": + query = query.filter(Ticket.title == filter_item.value) + elif filter_item.property == "category.name": + query = query.join(Ticket.category).filter(Ticket.category.has(name=filter_item.value)) + elif filter_item.property == "priority.name": + query = query.join(Ticket.priority).filter(Ticket.priority.has(name=filter_item.value)) + elif filter_item.property == "status.name": + query = query.join(Ticket.status).filter(Ticket.status.has(name=filter_item.value)) + elif filter_item.property == "description" and hasattr(Ticket, "description"): + if filter_item.operator == "*": + query = query.filter(Ticket.description.ilike(f"%{filter_item.value}%")) + elif filter_item.operator == "*?": + query = query.filter(Ticket.description.ilike(f"{filter_item.value}%")) + elif filter_item.operator == "?*": + query = query.filter(Ticket.description.ilike(f"%{filter_item.value}")) + elif filter_item.operator == "=": + query = query.filter(Ticket.description == filter_item.value) + # اعمال جستجو if query_info.search and query_info.search_fields: search_conditions = [] @@ -98,6 +126,43 @@ class TicketRepository(BaseRepository[Ticket]): joinedload(Ticket.status) ) + # اعمال فیلترها + if query_info.filters: + for filter_item in query_info.filters: + if filter_item.property == "title" and hasattr(Ticket, "title"): + if filter_item.operator == "*": + query = query.filter(Ticket.title.ilike(f"%{filter_item.value}%")) + elif filter_item.operator == "*?": + query = query.filter(Ticket.title.ilike(f"{filter_item.value}%")) + elif filter_item.operator == "?*": + query = query.filter(Ticket.title.ilike(f"%{filter_item.value}")) + elif filter_item.operator == "=": + query = query.filter(Ticket.title == filter_item.value) + elif filter_item.property == "category.name": + query = query.join(Ticket.category).filter(Ticket.category.has(name=filter_item.value)) + elif filter_item.property == "priority.name": + query = query.join(Ticket.priority).filter(Ticket.priority.has(name=filter_item.value)) + elif filter_item.property == "status.name": + query = query.join(Ticket.status).filter(Ticket.status.has(name=filter_item.value)) + elif filter_item.property == "description" and hasattr(Ticket, "description"): + if filter_item.operator == "*": + query = query.filter(Ticket.description.ilike(f"%{filter_item.value}%")) + elif filter_item.operator == "*?": + query = query.filter(Ticket.description.ilike(f"{filter_item.value}%")) + elif filter_item.operator == "?*": + query = query.filter(Ticket.description.ilike(f"%{filter_item.value}")) + elif filter_item.operator == "=": + query = query.filter(Ticket.description == filter_item.value) + elif filter_item.property == "user_email": + query = query.join(Ticket.user).filter(Ticket.user.has(email=filter_item.value)) + elif filter_item.property == "user_name": + query = query.join(Ticket.user).filter( + or_( + Ticket.user.has(first_name=filter_item.value), + Ticket.user.has(last_name=filter_item.value) + ) + ) + # اعمال جستجو if query_info.search and query_info.search_fields: search_conditions = [] diff --git a/hesabixUI/hesabix_ui/lib/pages/profile/support_page.dart b/hesabixUI/hesabix_ui/lib/pages/profile/support_page.dart index 34bec0e..448f177 100644 --- a/hesabixUI/hesabix_ui/lib/pages/profile/support_page.dart +++ b/hesabixUI/hesabix_ui/lib/pages/profile/support_page.dart @@ -129,7 +129,7 @@ class _SupportPageState extends State { }, defaultPageSize: 20, pageSizeOptions: const [10, 20, 50, 100], - showRefreshButton: true, + showFiltersButton: false, showClearFiltersButton: true, emptyStateMessage: t.noTickets, loadingMessage: t.loadingTickets, diff --git a/hesabixUI/hesabix_ui/lib/widgets/data_table/data_table_config.dart b/hesabixUI/hesabix_ui/lib/widgets/data_table/data_table_config.dart index 71d71b0..21fbbe8 100644 --- a/hesabixUI/hesabix_ui/lib/widgets/data_table/data_table_config.dart +++ b/hesabixUI/hesabix_ui/lib/widgets/data_table/data_table_config.dart @@ -214,6 +214,9 @@ class DataTableConfig { // Custom header actions final List? customHeaderActions; + + // Show individual action buttons + final bool showFiltersButton; const DataTableConfig({ required this.endpoint, @@ -276,6 +279,7 @@ class DataTableConfig { this.initialColumnSettings, this.onColumnSettingsChanged, this.customHeaderActions, + this.showFiltersButton = true, }); /// Get column width as double diff --git a/hesabixUI/hesabix_ui/lib/widgets/data_table/data_table_widget.dart b/hesabixUI/hesabix_ui/lib/widgets/data_table/data_table_widget.dart index 39c5a02..24ffa5c 100644 --- a/hesabixUI/hesabix_ui/lib/widgets/data_table/data_table_widget.dart +++ b/hesabixUI/hesabix_ui/lib/widgets/data_table/data_table_widget.dart @@ -772,7 +772,7 @@ class _DataTableWidgetState extends State> { const Spacer(), // Filter buttons - if (widget.config.showFilters) ...[ + if (widget.config.showFilters && widget.config.showFiltersButton) ...[ Tooltip( message: _showFilters ? t.hideFilters : t.showFilters, child: IconButton( @@ -807,37 +807,74 @@ class _DataTableWidgetState extends State> { const SizedBox(width: 8), ], - if (widget.config.showRefreshButton) - IconButton( - onPressed: _fetchData, - icon: const Icon(Icons.refresh), - tooltip: t.refresh, - ), - // Custom header actions if (widget.config.customHeaderActions != null) ...[ const SizedBox(width: 8), ...widget.config.customHeaderActions!, ], - // Column settings button (moved after refresh button) - if (widget.config.showColumnSettingsButton && widget.config.enableColumnSettings) ...[ - const SizedBox(width: 4), - Tooltip( - message: t.columnSettings, - child: IconButton( - onPressed: _isLoadingColumnSettings ? null : _openColumnSettingsDialog, - icon: _isLoadingColumnSettings - ? const SizedBox( - width: 16, - height: 16, - child: CircularProgressIndicator(strokeWidth: 2), - ) - : const Icon(Icons.view_column), - tooltip: t.columnSettings, - ), - ), - ], + // Actions menu + PopupMenuButton( + icon: const Icon(Icons.more_vert), + tooltip: 'عملیات', + onSelected: (value) { + switch (value) { + case 'refresh': + _fetchData(); + break; + case 'filters': + setState(() { + _showFilters = !_showFilters; + }); + break; + case 'columnSettings': + _openColumnSettingsDialog(); + break; + } + }, + itemBuilder: (context) => [ + if (widget.config.showRefreshButton) + PopupMenuItem( + value: 'refresh', + child: Row( + children: [ + const Icon(Icons.refresh, size: 20), + const SizedBox(width: 8), + Text(t.refresh), + ], + ), + ), + if (widget.config.showFilters && widget.config.showFiltersButton) + PopupMenuItem( + value: 'filters', + child: Row( + children: [ + Icon(_showFilters ? Icons.filter_list_off : Icons.filter_list, size: 20), + const SizedBox(width: 8), + Text(_showFilters ? t.hideFilters : t.showFilters), + ], + ), + ), + if (widget.config.showColumnSettingsButton && widget.config.enableColumnSettings) + PopupMenuItem( + value: 'columnSettings', + enabled: !_isLoadingColumnSettings, + child: Row( + children: [ + _isLoadingColumnSettings + ? const SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator(strokeWidth: 2), + ) + : const Icon(Icons.view_column, size: 20), + const SizedBox(width: 8), + Text(t.columnSettings), + ], + ), + ), + ], + ), ], ); }