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

@@ -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