Files
C4IT-F4SD-Client/FasdDesktopUi/Pages/AdvancedSearchPage/AdvancedSearchPageView.xaml.cs
2025-11-11 11:03:42 +01:00

564 lines
22 KiB
C#

using C4IT.FASD.Base;
using C4IT.FASD.Cockpit.Communication;
using C4IT.Logging;
using C4IT.MultiLanguage;
using FasdDesktopUi.Basics;
using FasdDesktopUi.Basics.CustomEvents;
using FasdDesktopUi.Basics.Enums;
using FasdDesktopUi.Basics.Helper;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.UiActions;
using FasdDesktopUi.Basics.UserControls;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using FasdDesktopUi.Basics.Services.SupportCaseSearchService;
using FasdDesktopUi.Basics.Services.RelationService;
using static C4IT.Logging.cLogManager;
using static FasdDesktopUi.Basics.UserControls.SearchBar;
namespace FasdDesktopUi.Pages.AdvancedSearchPage
{
public partial class AdvancedSearchPage : Window, ISearchUiProvider, INotifyPropertyChanged
{
public SupportCaseSearchService SearchService { get; } = new SupportCaseSearchService(new RelationService());
// New Advanced-Search-Page IN PROGRESS
// Next steps:
// 1. Open F4SD Cockpit when user is clicked
// 2. Display filter name next to filter icon when filter is selected
// 3. When F4SD Tray-Icon is clicked, open the new advanced search page
SearchBar.ChangedSearchValueDelegate ChangedSearchValue { get; set; }
private cF4sdApiSearchResultRelation preSelectedRelation = null;
private static readonly object _currentSearchTaskLock = new object();
private static Guid? CurrentSearchTaskId = null;
private static CancellationTokenSource CurrentSearchTaskToken = null;
private readonly DispatcherTimer _searchTimer = new DispatcherTimer();
private string searchValue = null;
public static AdvancedSearchPage advancedSearchPage;
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
// Mapping zwischen den Enum-Classes, da Integer-Values nicht alle identisch sind. Evtl. im nachgang anpassen, aber vorher alle Abhängigkeiten prüfen
private enumFasdInformationClass MapSearchResultClassToInformationClass(enumF4sdSearchResultClass searchResultClass)
{
switch (searchResultClass)
{
case enumF4sdSearchResultClass.Computer: return enumFasdInformationClass.Computer;
case enumF4sdSearchResultClass.User: return enumFasdInformationClass.User;
case enumF4sdSearchResultClass.Ticket: return enumFasdInformationClass.Ticket;
case enumF4sdSearchResultClass.VirtualSession: return enumFasdInformationClass.VirtualSession;
case enumF4sdSearchResultClass.MobileDevice: return enumFasdInformationClass.MobileDevice;
default: return enumFasdInformationClass.Unknown;
}
}
// Alle Suchergebnisse (ungefilterte Sammlung)
public cFasdApiSearchResultCollection SearchResults { get; set; }
// Gefilterte Suchergebnisse (für die Anzeige in der UI)
private cFasdApiSearchResultCollection _filteredSearchResults;
public cFasdApiSearchResultCollection FilteredSearchResults
{
get => _filteredSearchResults;
set
{
if (_filteredSearchResults != value)
{
_filteredSearchResults = value;
OnPropertyChanged();
}
}
}
// Aktuell gesetzte Filterklassen
public HashSet<enumFasdInformationClass> StoredFilterInformationClasses { get; set; }
public static readonly DependencyProperty FilterInformationClassesProperty =
DependencyProperty.Register("FilterInformationClasses", typeof(HashSet<enumFasdInformationClass>), typeof(AdvancedSearchPage), new PropertyMetadata(new HashSet<enumFasdInformationClass>()));
// Ermittelte Filter bei der Suche
public HashSet<enumFasdInformationClass> FilterInformationClasses
{
get { return (HashSet<enumFasdInformationClass>)GetValue(FilterInformationClassesProperty); }
set { SetValue(FilterInformationClassesProperty, value); }
}
public eSearchStatus searchStatus { get; private set; } = eSearchStatus.message;
public AdvancedSearchPage()
{
InitializeComponent();
AddCustomEventHandlers();
SearchBarUc.ActivateManualSearch();
advancedSearchPage = this;
}
private void AddCustomEventHandlers()
{
AddHandler(cUiActionBase.UiActionClickedEvent, new cUiActionBase.UiActionEventHandlerDelegate(UiActionWasTriggered));
SearchBarUc.ChangedSearchValue = ChangedSearchValueActionAsync;
SearchBarUc.CancledSearchAction = CancledSearchAction;
}
private Task ChangedSearchValueActionAsync(cFilteredResults filteredResults)
{
this.Dispatcher.Invoke(new Action(() =>
{
try
{
if (filteredResults?.Results is null)
{
SetSearchResultVisibility(false);
return;
}
// Speichern der Kompletten Suchergebnisse
SearchResults = filteredResults.Results;
// Verfügbare FilterIcons dynamisch ermitteln
UpdateFilterInformationClasses();
// Filter anwenden
ApplyFilters();
// Menü mit den gefilterten Ergebnissen anzeigen
string menuHeaderText = filteredResults?.PreSelectedRelation?.DisplayName;
ResultMenu.ShowSearchResults(filteredResults, menuHeaderText, this);
preSelectedRelation = filteredResults.PreSelectedRelation;
SetSearchResultVisibility(true);
}
catch (Exception E)
{
cLogManager.LogException(E);
}
}));
return Task.CompletedTask;
}
private void UpdateFilterInformationClasses()
{
if (SearchResults == null)
{
FilterInformationClasses = new HashSet<enumFasdInformationClass>();
return;
}
var filter = new HashSet<enumFasdInformationClass>();
foreach (var group in SearchResults)
{
foreach (var entry in group.Value)
{
var mappedClass = MapSearchResultClassToInformationClass(entry.Type);
filter.Add(mappedClass);
}
}
FilterInformationClasses = filter;
}
private void SearchFilterBar_FilterChanged(object sender, SearchFilterChangedEventArgs e)
{
// Filter speichern
StoredFilterInformationClasses = new HashSet<enumFasdInformationClass>(e.InformationClassesToShow);
// Filter anwenden
ApplyFilters();
// cFilteredResults erzeugen
var filteredResultWrapper = new cFilteredResults()
{
Results = FilteredSearchResults,
PreSelectedRelation = preSelectedRelation
};
// Ergebnisse im Menü neu anzeigen
string menuHeaderText = preSelectedRelation?.DisplayName;
ResultMenu.ShowSearchResults(filteredResultWrapper, menuHeaderText, this);
}
private void ApplyFilters()
{
if (SearchResults == null)
return;
// Wenn keine Filter ausgewählt wurden, alles übernehmen
if (StoredFilterInformationClasses == null || StoredFilterInformationClasses.Count == 0)
{
FilteredSearchResults = SearchResults;
return;
}
// Wenn ein oder mehrere Filter gesetzt, dann Ergebnisse entsprechend gefiltert anzeigen
cFasdApiSearchResultCollection filtered = new cFasdApiSearchResultCollection();
foreach (var group in SearchResults)
{
var filteredEntries = group.Value
.Where(entry =>
StoredFilterInformationClasses.Contains(MapSearchResultClassToInformationClass(entry.Type))
)
.ToList();
if (filteredEntries.Any())
{
filtered.Add(group.Key, filteredEntries);
}
}
FilteredSearchResults = filtered;
}
public void ShowSearchRelations(cSearchHistorySearchResultEntry searchHistoryEntry, IRelationService relationService, ISearchUiProvider searchUiProvider)
{
try
{
ResultMenu.SetHeaderText(searchHistoryEntry.HeaderText, hideDetailsCheckbox: false);
cSearchManager.ResolveRelations(searchHistoryEntry.Relations);
ILookup<enumFasdInformationClass, cMenuDataBase> relationsLookup = searchHistoryEntry.Relations
.OrderBy(r => r.UsingLevel)
.ThenBy(r => r.LastUsed)
.ToLookup(GetInformationClass, GetMenuData);
ResultMenu.UpdateSearchRelations(relationsLookup);
ResultMenu.SetHeaderText(searchHistoryEntry.HeaderText, hideDetailsCheckbox: false);
}
catch (Exception ex)
{
LogException(ex);
}
cMenuDataBase GetMenuData(cF4sdApiSearchResultRelation relation)
{
var requiredInformationClasses = new List<enumFasdInformationClass>() { cF4sdIdentityEntry.GetFromSearchResult(relation.Type) };
bool isEnabled = cHealthCardDataHelper.HasAvailableHealthCard(requiredInformationClasses);
string disabledReason = null;
if (!isEnabled)
{
disabledReason = cMultiLanguageSupport.GetItem("Searchbar.NoValidHealthcard") ?? string.Empty;
}
string trailingText = null;
return new cMenuDataSearchRelation(relation)
{
MenuText = relation.DisplayName,
TrailingText = trailingText,
UiAction = new cUiProcessSearchRelationAction(searchHistoryEntry, relation, relationService, searchUiProvider)
{
DisplayType = isEnabled ? enumActionDisplayType.enabled : enumActionDisplayType.disabled,
Description = isEnabled ? string.Empty : disabledReason,
AlternativeDescription = isEnabled ? string.Empty : disabledReason
}
};
}
enumFasdInformationClass GetInformationClass(cF4sdApiSearchResultRelation relation) => cF4sdIdentityEntry.GetFromSearchResult(relation.Type);
}
public void SetPendingInformationClasses(HashSet<enumFasdInformationClass> informationClasses) => ResultMenu.SetPendingInformationClasses(informationClasses);
public void UpdatePendingInformationClasses(HashSet<enumFasdInformationClass> informationClasses) => ResultMenu.UpdatePendingInformationClasses(informationClasses);
private async void UiActionWasTriggered(object sender, UiActionEventArgs e)
{
try
{
if (e.UiAction == null)
return;
Mouse.OverrideCursor = Cursors.Wait;
switch (e.UiAction)
{
case cUiProcessSearchResultAction searchResultAction:
searchResultAction.PreSelectedSearchRelation = preSelectedRelation;
break;
case cUiProcessSearchRelationAction _:
case cUiProcessSearchHistoryEntry _:
break;
default:
var _t = e.UiAction?.GetType().Name;
Debug.Assert(true, $"The UI action '{_t}' is not supported in detailed page");
CancledSearchAction();
return;
}
SetSearchResultVisibility(true);
if (!await e.UiAction.RunUiActionAsync(sender, null, false, null))
CancledSearchAction();
}
catch (Exception E)
{
LogException(E);
}
finally
{
Mouse.OverrideCursor = null;
}
}
private void SetSearchResultVisibility(bool isVisible)
{
SearchResultBorder.Visibility = isVisible ? Visibility.Visible : Visibility.Collapsed;
BodyStack.Visibility = (isVisible || SearchResultBorder.IsVisible) ? Visibility.Visible : Visibility.Collapsed;
}
private void CancledSearchAction()
{
SearchBarUc.Clear();
ResultMenu.ShowSearchResults(new cFilteredResults(), null, this);
SetSearchResultVisibility(false);
SetSearchHistoryVisibility(false);
Visibility = Visibility.Hidden;
}
#region Cleanup when implementing search
public void SetSearchHistoryVisibility(bool isVisible)
{
SearchHistoryBorder.Visibility = isVisible && !SearchHistory.IsEmpty() ? Visibility.Visible : Visibility.Collapsed;
BodyStack.Visibility = (isVisible || SearchResultBorder.IsVisible) ? Visibility.Visible : Visibility.Collapsed;
}
private void ComputerDomainSearch(cComputerDomainSearchParameters searchInfo)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
var _h = Task.Run(async () =>
{
try
{
var _result = await cFasdCockpitCommunicationBase.Instance.GetComputerSearchResults(searchInfo.name, searchInfo.domain);
var strInfo = string.Format(cMultiLanguageSupport.GetItem("Searchbar.ComputerSearch.Info"), searchInfo.name);
ShowExternalSearchInfo(strInfo, _result, enumF4sdSearchResultClass.Computer);
}
catch (Exception E)
{
LogException(E);
}
});
}
catch (Exception E)
{
LogException(E);
}
finally
{
LogMethodEnd(CM);
}
}
private void ShowExternalSearchInfo(string strInfo, cFasdApiSearchResultCollection resultEntry, enumF4sdSearchResultClass Class)
{
var filteredResults = new cFilteredResults(resultEntry) { AutoContinue = true };
var _t = ShowExternalSearchInfoAsync(strInfo, filteredResults, Class);
}
private async Task ShowExternalSearchInfoAsync(string strInfo, cFilteredResults result, enumF4sdSearchResultClass Class)
{
try
{
await this.Dispatcher.InvokeAsync(async () =>
{
try
{
await SearchBarUc.SetFixedSearchResultAsync(Class, strInfo, result);
if (result.AutoContinue && result.Results?.Count == 1)
{
ResultMenu.IndexOfSelectedResultItem = 0;
ResultMenu.SelectCurrentResultItem();
}
Show();
}
catch (Exception E)
{
LogException(E);
}
});
}
catch (Exception E)
{
LogException(E);
}
}
public string SearchValue
{
get => searchValue;
set
{
if (value != searchValue)
{
searchValue = value;
if (searchStatus == eSearchStatus.active)
UpdateSearchResults();
}
}
}
private async Task UpdateSearchResultsAsync(string searchValue, CancellationToken cancellationToken)
{
try
{
if (string.IsNullOrEmpty(searchValue))
{
await ChangedSearchValue.Invoke(null);
}
else
{
cFasdApiSearchResultCollection filteredResultDictionary = null;
try
{
var taskId = await cFasdCockpitCommunicationBase.Instance.GetSearchResultsStart(searchValue, CancellationToken.None);
lock (_currentSearchTaskLock)
{
CurrentSearchTaskId = taskId;
}
var _canceled = false;
if (!cancellationToken.IsCancellationRequested && taskId != null)
try
{
filteredResultDictionary = await cFasdCockpitCommunicationBase.Instance.GetSearchResultsResult((Guid)taskId, cancellationToken);
}
catch (TaskCanceledException)
{
_canceled = true;
LogEntry("UpdateSearchResultsAsync was cancelled by token (4).", LogLevels.Debug);
}
_canceled |= cancellationToken.IsCancellationRequested;
taskId = null;
if (_canceled)
lock (_currentSearchTaskLock)
{
taskId = CurrentSearchTaskId;
CurrentSearchTaskId = null;
}
if (taskId != null)
await cFasdCockpitCommunicationBase.Instance.GetSearchResultsStop((Guid)taskId, CancellationToken.None);
}
catch (TaskCanceledException)
{
LogEntry("UpdateSearchResultsAsync was cancelled by token (3).", LogLevels.Debug);
}
catch (Exception E)
{
LogException(E);
}
if (cancellationToken.IsCancellationRequested)
{
LogEntry("UpdateSearchResultsAsync was cancelled by token (2).", LogLevels.Debug);
return;
}
cFilteredResults filteredResult = new cFilteredResults(filteredResultDictionary);
await ChangedSearchValue.Invoke(filteredResult);
}
LogEntry("UpdateSearchResultsAsync finished successful.", LogLevels.Warning);
}
catch (TaskCanceledException)
{
LogEntry("UpdateSearchResultsAsync was cancelled by token (1).", LogLevels.Warning);
}
catch (Exception E)
{
LogException(E);
}
}
private void UpdateSearchResults()
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
try
{
if (ChangedSearchValue == null)
return;
if (_searchTimer.IsEnabled)
_searchTimer.Stop();
_searchTimer.Start();
var stopTask = Task.Run(() =>
{
CancelRunningSearchTaskAsync();
});
}
catch (Exception E)
{
LogException(E);
}
finally
{
if (CM != null) LogMethodEnd(CM);
}
}
private void CancelRunningSearchTaskAsync()
{
// cancel the search task, in case of a running search task
lock (_currentSearchTaskLock)
{
if (CurrentSearchTaskToken != null)
{
LogEntry("Canceling current running search...");
CurrentSearchTaskToken.Cancel(true);
CurrentSearchTaskToken = null;
}
}
}
public void ShowLoadingTextItem(string itemText)
{
SetSearchHistoryVisibility(false);
ResultMenu.ShowLoadingTextItem(itemText);
}
#endregion
}
}