Kategorie

This commit is contained in:
Meik
2025-11-11 19:43:15 +01:00
parent dc3e8a2e4c
commit 05fb34815a
27 changed files with 1832 additions and 1109 deletions

View File

@@ -0,0 +1,160 @@
<UserControl x:Class="FasdDesktopUi.Basics.UserControls.HierarchicalSelectionControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ico="clr-namespace:FasdDesktopUi.Basics.UserControls.AdaptableIcon;assembly=F4SD-AdaptableIcon"
xmlns:vc="clr-namespace:FasdDesktopUi.Basics.Converter"
xmlns:models="clr-namespace:FasdDesktopUi.Basics.Models"
mc:Ignorable="d"
x:Name="HierarchySelector"
Focusable="True"
d:DesignHeight="60"
d:DesignWidth="350">
<UserControl.Resources>
<vc:LanguageDefinitionsConverter x:Key="LanguageConverter" />
<vc:NullValueToVisibilityConverter x:Key="NullToVisibility" />
</UserControl.Resources>
<Grid>
<Border x:Name="DisplayBorder"
Height="28"
Background="{Binding ElementName=HierarchySelector, Path=ComboBoxBackground}"
BorderBrush="{Binding ElementName=HierarchySelector, Path=BorderBrush}"
BorderThickness="1"
CornerRadius="7.5"
Padding="6 0"
MouseLeftButtonUp="DisplayBorder_MouseLeftButtonUp">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Opacity" Value="1" />
<Setter Property="Cursor" Value="Hand" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsOpen, ElementName=DropDownPopup}"
Value="True">
<Setter Property="BorderBrush" Value="{DynamicResource BackgroundColor.Menu.SubCategory.Hover}" />
<Setter Property="Background" Value="{DynamicResource BackgroundColor.DetailsPage.DataHistory.ValueColumn}" />
</DataTrigger>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="BorderBrush" Value="{DynamicResource BackgroundColor.Menu.SubCategory.Hover}" />
</Trigger>
<DataTrigger Binding="{Binding ElementName=HierarchySelector, Path=IsEnabled}"
Value="False">
<Setter Property="Opacity" Value="0.5" />
<Setter Property="Cursor" Value="Arrow" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0"
Margin="0 0 10 0"
VerticalAlignment="Center">
<TextBlock Text="{Binding ElementName=HierarchySelector, Path=SelectedItem.FullPath}"
Foreground="{DynamicResource FontColor.Menu.Categories}"
VerticalAlignment="Center"
TextTrimming="CharacterEllipsis"
ToolTip="{Binding ElementName=HierarchySelector, Path=SelectedItem.FullPath}" />
</Grid>
<Path Grid.Column="1"
Margin="0 0 6 0"
Data="F1 M 2.89067,4.99467C 2.89067,4.22933 3.724,3.74533 4.39067,4.13067L 10.8227,7.844L 17.26,4.13067C 18.412,3.48 19.4013,5.19333 18.26,5.86533L 11.3227,9.86533C 11.016,10.0413 10.636,10.0413 10.3227,9.86533L 3.39067,5.86533C 3.07867,5.688 2.89067,5.35467 2.89067,4.99467 Z"
Fill="{DynamicResource Color.Menu.Icon}"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Width="10"
Height="6"
Stretch="Uniform"
IsHitTestVisible="False" />
</Grid>
</Border>
<Popup x:Name="DropDownPopup"
PlacementTarget="{Binding ElementName=DisplayBorder}"
Placement="Bottom"
StaysOpen="False"
AllowsTransparency="True"
Width="{Binding ElementName=DisplayBorder, Path=ActualWidth}"
Opened="DropDownPopup_Opened"
Closed="DropDownPopup_Closed">
<Border Background="{DynamicResource BackgroundColor.Menu.SubCategory}"
CornerRadius="7.5"
Padding="10">
<Border.Effect>
<DropShadowEffect BlurRadius="10"
Color="{DynamicResource DropShadowColor.Menu}"
Direction="55"
ShadowDepth="10"
Opacity="0.35" />
</Border.Effect>
<StackPanel>
<Grid Margin="0 0 10 0">
<TextBox x:Name="SearchTextBox"
Style="{DynamicResource Customizable.Editable.TextBox.EditOnly}"
Background="{Binding ElementName=HierarchySelector, Path=ComboBoxBackground}"
Padding="27 5 5 5"
VerticalContentAlignment="Center"
TextChanged="SearchTextBox_TextChanged" />
<ico:AdaptableIcon SelectedInternIcon="menuBar_search"
Style="{DynamicResource Menu.MenuBar.PinnedIcon.Base}"
BorderPadding="5 5 5 7"
Margin="0"
IconBackgroundColor="Transparent"
HorizontalAlignment="Left"
VerticalAlignment="Center" />
<TextBlock Margin="30 0 0 0"
Foreground="{DynamicResource FontColor.DetailsPage.DataHistory.Date}"
IsHitTestVisible="False"
VerticalAlignment="Center">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding Text, ElementName=SearchTextBox}"
Value="">
<Setter Property="Text"
Value="{Binding ElementName=HierarchySelector, Path=SearchPlaceholderText}" />
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
<Border Margin="0 10 0 0"
Background="{DynamicResource BackgroundColor.Menu.SubCategory}"
BorderThickness="0"
CornerRadius="5">
<ScrollViewer MaxHeight="300"
VerticalScrollBarVisibility="Auto">
<TreeView x:Name="TreeViewControl"
Background="{Binding ElementName=HierarchySelector, Path=ComboBoxBackground}"
ItemsSource="{Binding VisibleItems, ElementName=HierarchySelector}"
SelectedItemChanged="TreeViewControl_SelectedItemChanged">
<TreeView.Resources>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded"
Value="{Binding IsExpanded, Mode=TwoWay}" />
</Style>
<HierarchicalDataTemplate DataType="{x:Type models:HierarchicalSelectionItem}"
ItemsSource="{Binding Children}">
<TextBlock Text="{Binding DisplayName}"
Foreground="{DynamicResource FontColor.Menu.Categories}"
Margin="0 2 0 2" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</ScrollViewer>
</Border>
</StackPanel>
</Border>
</Popup>
</Grid>
</UserControl>

View File

@@ -0,0 +1,256 @@
using FasdDesktopUi.Basics.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
namespace FasdDesktopUi.Basics.UserControls
{
public partial class HierarchicalSelectionControl : UserControl, IFocusInvoker
{
private readonly ObservableCollection<HierarchicalSelectionItem> visibleItems = new ObservableCollection<HierarchicalSelectionItem>();
private readonly Dictionary<string, HierarchicalSelectionItem> itemLookup = new Dictionary<string, HierarchicalSelectionItem>();
private readonly DispatcherTimer searchDelayTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(250) };
private string lastSearchText = string.Empty;
public ObservableCollection<HierarchicalSelectionItem> VisibleItems => visibleItems;
public int? ParentIndex { get; set; }
public UIElement ParentElement { get; set; }
public HierarchicalSelectionControl()
{
InitializeComponent();
TreeViewControl.ItemsSource = visibleItems;
searchDelayTimer.Tick += SearchDelayTimer_Tick;
}
#region DependencyProperties
public ObservableCollection<HierarchicalSelectionItem> ItemsSource
{
get => (ObservableCollection<HierarchicalSelectionItem>)GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(
nameof(ItemsSource),
typeof(ObservableCollection<HierarchicalSelectionItem>),
typeof(HierarchicalSelectionControl),
new PropertyMetadata(null, OnItemsSourceChanged));
private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is HierarchicalSelectionControl control)
{
control.RebuildLookup();
control.ApplyFilter(control.lastSearchText);
control.TryExpandToSelectedItem();
}
}
public HierarchicalSelectionItem SelectedItem
{
get => (HierarchicalSelectionItem)GetValue(SelectedItemProperty);
set => SetValue(SelectedItemProperty, value);
}
public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register(
nameof(SelectedItem),
typeof(HierarchicalSelectionItem),
typeof(HierarchicalSelectionControl),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedItemChanged));
private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is HierarchicalSelectionControl control)
{
control.TryExpandToSelectedItem();
}
}
public Brush ComboBoxBackground
{
get => (Brush)GetValue(ComboBoxBackgroundProperty);
set => SetValue(ComboBoxBackgroundProperty, value);
}
public static readonly DependencyProperty ComboBoxBackgroundProperty =
DependencyProperty.Register(nameof(ComboBoxBackground), typeof(Brush), typeof(HierarchicalSelectionControl), new PropertyMetadata(Brushes.Transparent));
public string SearchPlaceholderText
{
get => (string)GetValue(SearchPlaceholderTextProperty);
set => SetValue(SearchPlaceholderTextProperty, value);
}
public static readonly DependencyProperty SearchPlaceholderTextProperty =
DependencyProperty.Register(nameof(SearchPlaceholderText), typeof(string), typeof(HierarchicalSelectionControl), new PropertyMetadata(string.Empty));
#endregion
private void DropDownPopup_Opened(object sender, EventArgs e)
{
SearchTextBox.Focus();
SearchTextBox.SelectAll();
EnsureParentPlacement();
cFocusInvoker.InvokeGotFocus(this, e);
}
private void DropDownPopup_Closed(object sender, EventArgs e)
{
searchDelayTimer.Stop();
cFocusInvoker.InvokeLostFocus(this, e);
}
private void EnsureParentPlacement()
{
if (ParentElement == null || ParentElement != this.Parent)
{
ParentElement = this.Parent as UIElement;
if (this.Parent is Panel panel)
ParentIndex = panel.Children.IndexOf(this);
else
ParentIndex = null;
}
}
private void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
searchDelayTimer.Stop();
searchDelayTimer.Start();
}
private void SearchDelayTimer_Tick(object sender, EventArgs e)
{
searchDelayTimer.Stop();
lastSearchText = SearchTextBox.Text ?? string.Empty;
ApplyFilter(lastSearchText);
}
private void TreeViewControl_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
if (e.NewValue is HierarchicalSelectionItem selected)
{
var original = ResolveOriginalItem(selected);
if (original != null)
SelectedItem = original;
DropDownPopup.IsOpen = false;
}
}
private HierarchicalSelectionItem ResolveOriginalItem(HierarchicalSelectionItem item)
{
if (item == null)
return null;
if (!string.IsNullOrWhiteSpace(item.Id) && itemLookup.TryGetValue(item.Id, out var original))
return original;
return item;
}
private void RebuildLookup()
{
itemLookup.Clear();
if (ItemsSource == null)
return;
foreach (var entry in ItemsSource.SelectMany(item => item.SelfAndDescendants()))
{
if (string.IsNullOrWhiteSpace(entry.Id))
continue;
itemLookup[entry.Id] = entry;
}
}
private void ApplyFilter(string searchText)
{
visibleItems.Clear();
if (ItemsSource == null)
return;
if (string.IsNullOrWhiteSpace(searchText))
{
foreach (var item in ItemsSource)
{
item.SetExpandedRecursive(false);
visibleItems.Add(item);
}
TryExpandToSelectedItem();
return;
}
var comparison = StringComparison.CurrentCultureIgnoreCase;
foreach (var root in ItemsSource)
{
var clone = root.CloneBranch(node =>
node.FullPath?.IndexOf(searchText, comparison) >= 0 ||
node.DisplayName?.IndexOf(searchText, comparison) >= 0);
if (clone == null)
continue;
clone.SetExpandedRecursive(true);
visibleItems.Add(clone);
}
}
private void TryExpandToSelectedItem()
{
if (SelectedItem == null || string.IsNullOrWhiteSpace(SelectedItem.Id))
return;
if (!string.IsNullOrWhiteSpace(lastSearchText))
return;
var chain = SelectedItem;
while (chain != null)
{
chain.IsExpanded = true;
chain = chain.Parent;
}
}
private void DisplayBorder_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (!IsEnabled)
return;
e.Handled = true;
DropDownPopup.IsOpen = !DropDownPopup.IsOpen;
}
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
base.OnPreviewKeyDown(e);
if (!IsEnabled)
return;
if (!DropDownPopup.IsOpen && (e.Key == Key.Enter || e.Key == Key.Down || e.Key == Key.Space))
{
DropDownPopup.IsOpen = true;
e.Handled = true;
return;
}
if (DropDownPopup.IsOpen && e.Key == Key.Escape)
{
DropDownPopup.IsOpen = false;
e.Handled = true;
}
}
}
}

View File

@@ -160,13 +160,13 @@
Margin="0 10 0 0"
Foreground="{DynamicResource FontColor.Menu.Categories}" />
</StackPanel>
<local:ComboBoxPageable x:Name="CategorySelectionControl"
Margin="0 5 0 0"
SelectedItem="{Binding SelectedCategory, ElementName=CloseCaseDialogUc, Mode=TwoWay}"
ComboBoxBackground="{DynamicResource BackgroundColor.DetailsPage.DataHistory.ValueColumn}"
BorderBrush="{DynamicResource BackgroundColor.Menu.SubCategory.Hover}"
PreviewKeyDown="Combobox_PreviewKeyDown"
/>
<local:HierarchicalSelectionControl x:Name="CategorySelectionControl"
Margin="0 5 0 0"
SelectedItem="{Binding SelectedCategory, ElementName=CloseCaseDialogUc, Mode=TwoWay}"
ComboBoxBackground="{DynamicResource BackgroundColor.DetailsPage.DataHistory.ValueColumn}"
BorderBrush="{DynamicResource BackgroundColor.Menu.SubCategory.Hover}"
SearchPlaceholderText="{Binding Converter={StaticResource LanguageConverter}, ConverterParameter=Searchbar.Placeholder}"
PreviewKeyDown="Combobox_PreviewKeyDown" />
<StackPanel>
<TextBlock Text="{Binding Converter={StaticResource LanguageConverter}, ConverterParameter=Dialog.CloseCase.Template}"
FontWeight="Bold"

View File

@@ -59,13 +59,21 @@ namespace FasdDesktopUi.Basics.UserControls
Infos = new Dictionary<string, string> { ["Special"] = "NoTicket" }
};
private readonly cF4sdApiSearchResultRelation _removeAffectedAssetEntry = new cF4sdApiSearchResultRelation()
{
DisplayName = cMultiLanguageSupport.GetItem("Dialog.CloseCase.RemoveAffectedAssetEntry"),
Type = enumF4sdSearchResultClass.Computer,
Identities = null,
Infos = new Dictionary<string, string> { ["Special"] = "RemoveAffectedAssetEntry" }
};
private readonly cF4sdApiSearchResultRelation _removeAffectedAssetEntry = new cF4sdApiSearchResultRelation()
{
DisplayName = cMultiLanguageSupport.GetItem("Dialog.CloseCase.RemoveAffectedAssetEntry"),
Type = enumF4sdSearchResultClass.Computer,
Identities = null,
Infos = new Dictionary<string, string> { ["Special"] = "RemoveAffectedAssetEntry" }
};
private const string CategoryTableNamePrimary = "M42Wpm-Ticket-Categories";
private const string CategoryTableNameLegacy = "M42Wpm-Ticket-CloseCase-Categories";
private string activeCategoryTableName = CategoryTableNamePrimary;
private ObservableCollection<HierarchicalSelectionItem> categoryHierarchy = new ObservableCollection<HierarchicalSelectionItem>();
private readonly Dictionary<string, HierarchicalSelectionItem> categoryLookup = new Dictionary<string, HierarchicalSelectionItem>(StringComparer.OrdinalIgnoreCase);
private bool isCategoryLoading;
public bool IsTicket
{
@@ -139,17 +147,17 @@ namespace FasdDesktopUi.Basics.UserControls
ShowAssetWarningTicketAction = IsTicket;
ValidateProperty("SelectedTicket");
EnableHoldTicketAction = _selectedTicket == null || SelectedTicket == _selectTicketRelation || selectedTicketIsNewTicket;
if (_selectedTicket != null && _selectedTicket.Infos.TryGetValue("StatusId", out var statusIdString))
{
object parsedStatusId = Enum.Parse(typeof(enumTicketStatus), statusIdString);
if (parsedStatusId is enumTicketStatus statusId)
{
EnableHoldTicketAction = statusId != enumTicketStatus.OnHold;
}
}
}
}
if (_selectedTicket != null && _selectedTicket.Infos.TryGetValue("StatusId", out var statusIdString))
{
object parsedStatusId = Enum.Parse(typeof(enumTicketStatus), statusIdString);
if (parsedStatusId is enumTicketStatus statusId)
{
EnableHoldTicketAction = statusId != enumTicketStatus.OnHold;
}
}
TrySelectTicketCategoryFromTicketInfos();
}
}
private bool selectedTicketIsNewTicket
{
@@ -207,26 +215,22 @@ namespace FasdDesktopUi.Basics.UserControls
#region SelectedCategory DependencyProperty
public static readonly DependencyProperty SelectedCategoryProperty = DependencyProperty.Register(
"SelectedCategory",
typeof(KeyValuePair<string, object>),
typeof(CloseCaseDialogWithTicket),
new PropertyMetadata(default(KeyValuePair<string, object>), OnSelectedCategoryChanged));
public KeyValuePair<string, object> SelectedCategory
{
get { return (KeyValuePair<string, object>)GetValue(SelectedCategoryProperty); }
set
{
SetValue(SelectedCategoryProperty, value);
}
}
private static void OnSelectedCategoryChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (CloseCaseDialogWithTicket)d;
var newValue = (KeyValuePair<string, object>)e.NewValue;
}
public static readonly DependencyProperty SelectedCategoryProperty = DependencyProperty.Register(
"SelectedCategory",
typeof(HierarchicalSelectionItem),
typeof(CloseCaseDialogWithTicket),
new PropertyMetadata(null, OnSelectedCategoryChanged));
public HierarchicalSelectionItem SelectedCategory
{
get { return (HierarchicalSelectionItem)GetValue(SelectedCategoryProperty); }
set { SetValue(SelectedCategoryProperty, value); }
}
private static void OnSelectedCategoryChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Reserved for future use when additional reactions are required.
}
#endregion
@@ -373,19 +377,15 @@ namespace FasdDesktopUi.Basics.UserControls
me.UpdateAffectedServiceLabel();
me.UpdateAffectedAssetComboBox();
me.ServiceSelectionControl.SearchDataChanged = me.HandleServiceSearchDataChanged;
me.ServiceSelectionControl.ParentElement = me.MainStack;
me.ServiceSelectionControl.ParentIndex = me.MainStack.Children.IndexOf(me.ServiceSelectionControl);
me.CategorySelectionControl.ParentElement = me.MainStack;
me.CategorySelectionControl.ParentIndex = me.MainStack.Children.IndexOf(me.CategorySelectionControl);
me.CategorySelectionControl.SearchDataChanged = me.HandleCategorySearchDataChanged;
me.AddNewOrExistingOrNoneTicketSelection();
me.UpdateCaseNotesPreview();
me.UpdateTicketComponentVisibility();
await me.UpdateQuickCallsComboBoxAsync();
me.ServiceSelectionControl.SearchDataChanged = me.HandleServiceSearchDataChanged;
me.ServiceSelectionControl.ParentElement = me.MainStack;
me.ServiceSelectionControl.ParentIndex = me.MainStack.Children.IndexOf(me.ServiceSelectionControl);
me.AddNewOrExistingOrNoneTicketSelection();
me.UpdateCaseNotesPreview();
me.UpdateTicketComponentVisibility();
await me.InitializeCategorySelectionAsync();
await me.UpdateQuickCallsComboBoxAsync();
}
catch (Exception E)
{
@@ -412,13 +412,8 @@ namespace FasdDesktopUi.Basics.UserControls
dpd?.AddValueChanged(CaseNotesPreview, TicketCaseNotesTextBlock_TextChanged);
#if !IsNewFeature
CategoryLabelPanel.Visibility = Visibility.Collapsed;
CategorySelectionControl.Visibility = Visibility.Collapsed;
#endif
SupportNotepad = true;
}
SupportNotepad = true;
}
private void CloseCaseDialogWithTicket_Loaded(object sender, RoutedEventArgs e)
{
@@ -886,95 +881,193 @@ namespace FasdDesktopUi.Basics.UserControls
return null;
}
private string lastCategorySearch;
private async void HandleCategorySearchDataChanged(object sender, cF4sdHealthSelectionDataRequest e)
{
await UpdateCategorySelectionControlAsync(e);
}
private async Task UpdateCategorySelectionControlAsync(cF4sdHealthSelectionDataRequest requestData)
{
try
{
requestData.Identities = DataProvider.Identities;
requestData.Table = "M42Wpm-Ticket-CloseCase-Categories";
requestData.ResetFilter = CategorySelectionControl.ResetFilter;
if (string.IsNullOrEmpty(lastCategorySearch) ||
!lastCategorySearch.Equals(requestData.Search, StringComparison.CurrentCultureIgnoreCase))
{
var resCategoryCount = await cFasdCockpitCommunicationBase.Instance.GetPagedDataCount(requestData);
CategorySelectionControl.TotalItemCount = resCategoryCount;
lastCategorySearch = requestData.Search;
}
var categoryData = await cFasdCockpitCommunicationBase.Instance.GetPagedData(requestData);
var categoriesList = GetCategorySelectionData(categoryData) ?? new ObservableCollection<KeyValuePair<string, object>>();
// Aktuell am Ticket gesetzte Kategorie ggf. sicherstellen
string categoryId = null;
string categoryName = null;
if (SelectedTicket != null && SelectedTicket.Infos != null)
{
var catIdPair = SelectedTicket.Infos.FirstOrDefault(x => x.Key == "CategoryId");
categoryId = catIdPair.Value;
var catNamePair = SelectedTicket.Infos.FirstOrDefault(x => x.Key == "CategoryName");
categoryName = catNamePair.Value;
}
if (!string.IsNullOrEmpty(categoryId) && Guid.Empty.ToString() != categoryId)
{
bool exists = categoriesList.Any(kvp => kvp.Value != null && kvp.Value.ToString() == categoryId);
if (!exists)
{
categoriesList.Add(new KeyValuePair<string, object>(
string.IsNullOrEmpty(categoryName) ? categoryId : categoryName,
categoryId));
}
}
CategorySelectionControl.ItemData = categoriesList;
}
catch (Exception E)
{
LogException(E);
}
}
private ObservableCollection<KeyValuePair<string, object>> GetCategorySelectionData(cF4SDHealthCardRawData.cHealthCardTable dataTable)
{
try
{
if (dataTable == null || dataTable.Name != "M42Wpm-Ticket-CloseCase-Categories")
return null;
if (!dataTable.Columns.TryGetValue("id", out var idColumn))
return null;
if (!dataTable.Columns.TryGetValue("Name", out var nameColumn))
return null;
var zippedKeyValuePairs =
idColumn.Values.Zip(nameColumn.Values, (id, display) =>
new KeyValuePair<string, object>(display != null ? display.ToString() : string.Empty, id));
return new ObservableCollection<KeyValuePair<string, object>>(zippedKeyValuePairs);
}
catch (Exception E)
{
LogException(E);
}
return null;
}
private async Task UpdateQuickCallsComboBoxAsync()
{
private async Task InitializeCategorySelectionAsync()
{
if (isCategoryLoading)
return;
try
{
if (DataProvider == null || CategorySelectionControl == null)
return;
isCategoryLoading = true;
var tableCandidates = new[] { CategoryTableNamePrimary, CategoryTableNameLegacy };
bool initialized = false;
foreach (var tableName in tableCandidates)
{
if (await TryPopulateCategoryHierarchyAsync(tableName))
{
activeCategoryTableName = tableName;
initialized = true;
break;
}
}
if (initialized)
{
CategorySelectionControl.ItemsSource = categoryHierarchy;
TrySelectTicketCategoryFromTicketInfos();
}
}
catch (Exception E)
{
LogException(E);
}
finally
{
isCategoryLoading = false;
}
}
private async Task<bool> TryPopulateCategoryHierarchyAsync(string tableName)
{
try
{
const int defaultPageSize = 250;
var requestData = new cF4sdHealthSelectionDataRequest
{
Identities = DataProvider.Identities,
Table = tableName,
Page = 0,
PageSize = defaultPageSize,
Search = string.Empty
};
var totalCount = await cFasdCockpitCommunicationBase.Instance.GetPagedDataCount(requestData);
var effectivePageSize = requestData.PageSize > 0 ? requestData.PageSize : defaultPageSize;
var pageCount = Math.Max(1, (int)Math.Ceiling((double)totalCount / Math.Max(1, effectivePageSize)));
var flatItems = new Dictionary<string, HierarchicalSelectionItem>(StringComparer.OrdinalIgnoreCase);
for (int pageIndex = 0; pageIndex < pageCount; pageIndex++)
{
requestData.Page = pageIndex;
requestData.PageSize = effectivePageSize;
var categoryTable = await cFasdCockpitCommunicationBase.Instance.GetPagedData(requestData);
bool rowAdded = false;
foreach (var item in ConvertCategoryTable(categoryTable))
{
if (string.IsNullOrWhiteSpace(item.Id))
continue;
flatItems[item.Id] = item;
rowAdded = true;
}
if (!rowAdded && categoryTable == null)
return false;
}
var hierarchy = HierarchicalSelectionItem.BuildTree(flatItems.Values);
if (hierarchy == null || hierarchy.Count == 0)
return false;
categoryHierarchy = hierarchy;
UpdateCategoryLookup();
return true;
}
catch (Exception E)
{
LogException(E);
return false;
}
}
private IEnumerable<HierarchicalSelectionItem> ConvertCategoryTable(cF4SDHealthCardRawData.cHealthCardTable dataTable)
{
if (dataTable == null || dataTable.Columns == null)
yield break;
if (!dataTable.Columns.TryGetValue("id", out var idColumn))
yield break;
if (!dataTable.Columns.TryGetValue("Name", out var nameColumn))
yield break;
cF4SDHealthCardRawData.cHealthCardTableColumn parentIdColumn = null;
cF4SDHealthCardRawData.cHealthCardTableColumn parentNameColumn = null;
if (!dataTable.Columns.TryGetValue("parentValue", out parentIdColumn))
dataTable.Columns.TryGetValue("Parent_Value", out parentIdColumn);
if (!dataTable.Columns.TryGetValue("parent", out parentNameColumn))
dataTable.Columns.TryGetValue("Parent", out parentNameColumn);
for (int index = 0; index < idColumn.Values.Count; index++)
{
var id = idColumn.Values[index]?.ToString();
if (string.IsNullOrWhiteSpace(id))
continue;
var displayName = index < nameColumn.Values.Count ? nameColumn.Values[index]?.ToString() : string.Empty;
string parentId = null;
if (parentIdColumn != null && index < parentIdColumn.Values.Count)
parentId = parentIdColumn.Values[index]?.ToString();
if (Guid.TryParse(parentId, out var parsedParentId) && parsedParentId == Guid.Empty)
parentId = null;
var parentDisplayName = parentNameColumn != null && index < parentNameColumn.Values.Count
? parentNameColumn.Values[index]?.ToString()
: null;
yield return new HierarchicalSelectionItem
{
Id = id,
DisplayName = string.IsNullOrWhiteSpace(displayName) ? parentDisplayName ?? id : displayName,
ParentId = parentId,
ParentDisplayName = parentDisplayName
};
}
}
private void UpdateCategoryLookup()
{
categoryLookup.Clear();
if (categoryHierarchy == null)
return;
foreach (var node in categoryHierarchy.SelectMany(item => item.SelfAndDescendants()))
{
if (!string.IsNullOrWhiteSpace(node.Id))
categoryLookup[node.Id] = node;
}
}
private void TrySelectTicketCategoryFromTicketInfos()
{
try
{
if (SelectedTicket?.Infos == null)
{
SelectedCategory = null;
return;
}
if (!SelectedTicket.Infos.TryGetValue("CategoryId", out var categoryId) || string.IsNullOrWhiteSpace(categoryId))
{
SelectedCategory = null;
return;
}
if (categoryLookup.TryGetValue(categoryId, out var categoryNode))
SelectedCategory = categoryNode;
}
catch (Exception E)
{
LogException(E);
}
}
private async Task UpdateQuickCallsComboBoxAsync()
{
try
{
cF4sdHealthSelectionDataRequest requestData = new cF4sdHealthSelectionDataRequest