feat: expand cockpit integrations and support workflows

Add Phoenix remote desktop, action connector, documentation engine, and gamification support. Refactor support-case processing and search/action UI, and update installer assets and dependencies.
This commit is contained in:
Meik
2026-07-13 11:16:53 +02:00
parent bbedbf71c8
commit 0b52999b1d
303 changed files with 11235 additions and 3580 deletions

View File

@@ -1,7 +1,12 @@
using C4IT.FASD.Base;
using C4IT.FASD.Cockpit.Communication;
using FasdCockpitCommunication.RemoteDesktopCommunication;
using FasdDesktopUi.Basics.CustomEvents;
using FasdDesktopUi.Basics.Enums;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.Services.Models;
using FasdDesktopUi.Basics.Services.SupportCase.Processors;
using FasdDesktopUi.Basics.UiActions;
using FasdDesktopUi.Pages.DetailsPage.Models;
using System;
using System.Collections.Generic;
@@ -12,18 +17,18 @@ using static C4IT.Logging.cLogManager;
namespace FasdDesktopUi.Basics.Services.SupportCase.Controllers
{
/// <summary>
/// Used to manage the <see cref="ISupportCase"/> for the UI via the <see cref="SupportCaseProcessor"/>
/// Used to manage the <see cref="ISupportCase"/> for the UI via the <see cref="ISupportCaseProcessor"/>
/// </summary>
public class SupportCaseController
{
private SupportCaseProcessor _supportCaseProcessor;
private ISupportCaseProcessor _supportCaseProcessor;
private cF4sdApiSearchResultRelation _focusedRelation;
private readonly Dictionary<enumFasdInformationClass, cF4sdApiSearchResultRelation> _selectedRelations = new Dictionary<enumFasdInformationClass, cF4sdApiSearchResultRelation>();
private cHealthCard _selectedHealthcard = null;
private bool _hasDirectionConnection = false;
public cSupportCaseDataProvider SupportCaseDataProviderArtifact { get => _supportCaseProcessor?.SupportCaseDataProviderArtifact; }
internal void SetSupportCaseProcessor(SupportCaseProcessor supportCaseProcessor, IEnumerable<cF4sdIdentityEntry> preselectedIdentities)
internal void SetSupportCaseProcessor(ISupportCaseProcessor supportCaseProcessor, IEnumerable<cF4sdIdentityEntry> preselectedIdentities)
{
IEnumerable<cF4sdApiSearchResultRelation> preselectedRelations = GetPreselectedRelations(supportCaseProcessor.GetCaseRelations(), preselectedIdentities);
@@ -88,6 +93,9 @@ namespace FasdDesktopUi.Basics.Services.SupportCase.Controllers
{
CaseDataChanged?.Invoke(this, e);
if (e.DataTables.Any(t => !t.Name.StartsWith("Computation_")))
_supportCaseProcessor.ProcessComputations(e.Relation, _supportCaseProcessor.GetHealthcardFor(e.Relation).Prerequisites.Computations.Values);
_ = Task.Run(async () =>
{
await UpdateStatusOfSelectedRelations();
@@ -220,7 +228,7 @@ namespace FasdDesktopUi.Basics.Services.SupportCase.Controllers
relationEntry.Value.Infos[StatusString] = statusValue;
}
IEnumerable<cHeadingDataModel> newHeadingData = SupportCaseHeadingController.GetHeadingData(_selectedRelations);
IEnumerable<cHeadingDataModel> newHeadingData = GetHeadingData();
HeadingDataChanged?.Invoke(this, new HeadingDataEventArgs(newHeadingData));
}
catch (Exception ex)
@@ -234,18 +242,209 @@ namespace FasdDesktopUi.Basics.Services.SupportCase.Controllers
SupportCaseDataProviderArtifact.HealthCardDataHelper.LoadingHelper.LastDataRequest = DateTime.Now;
await _supportCaseProcessor.UpdateLatestCaseDataFor(_focusedRelation);
}
public cCopyTemplate GetCopyTemplate()
{
string defaultCopyTemplate = _selectedHealthcard?.DefaultCopyTemplate
?? cF4SDCockpitXmlConfig.Instance?.CopyTemplateConfig?.CopyTemplates.DefaultTemplate.Name
?? string.Empty;
public List<List<cWidgetValueModel>> GetWidgetData()
=> _supportCaseProcessor.GetWidgetData(_focusedRelation);
Dictionary<string, cCopyTemplate> copyTemplates = cF4SDCockpitXmlConfig.Instance?.CopyTemplateConfig?.CopyTemplates?.CopyTemplates;
if (copyTemplates?.TryGetValue(defaultCopyTemplate, out cCopyTemplate _defaultCopyTemplate) ?? false)
return _defaultCopyTemplate;
else
return null;
}
public List<List<cWidgetValueModel>> GetWidgetsData()
{
try
{
List<List<cWidgetValueModel>> widgetsData = new List<List<cWidgetValueModel>>();
cHealthCard currentHealthCard = _supportCaseProcessor.GetHealthcardFor(_focusedRelation);
IEnumerable<cHealthCardStateCategory> widgetsDefinition = currentHealthCard.CategoriesStatic.StateCategories;
foreach (var widgetDefinition in widgetsDefinition)
{
List<cWidgetValueModel> widgetData = new List<cWidgetValueModel>();
foreach (var widgetValueDefinition in widgetDefinition.States)
{
CockpitValueDisplayData displayData = _supportCaseProcessor.GetCockpitValueDisplayData(widgetValueDefinition, _focusedRelation, true);
cUiActionBase uiAction = _supportCaseProcessor.GetUiAction(widgetValueDefinition);
var widgetValue = new cWidgetValueModel(displayData, GetHighlightColor(displayData?.Levels?.FirstOrDefault() ?? enumHealthCardStateLevel.None), uiAction);
if (widgetValue?.UiActionValue != null && ShouldHideUiActionValue(widgetValue.Value))
widgetValue.UiActionValue.DisplayType = enumActionDisplayType.hidden;
widgetData.Add(widgetValue);
}
widgetsData.Add(widgetData);
}
return widgetsData;
}
catch (Exception ex)
{
LogException(ex);
return null;
}
bool ShouldHideUiActionValue(string widgetValue)
=> string.IsNullOrWhiteSpace(widgetValue) || widgetValue == "0";
}
public cDetailsPageDataHistoryDataModel GetHistoryData()
=> _supportCaseProcessor.GetHistoryData(_focusedRelation);
=> ((SupportCaseProcessor)_supportCaseProcessor).GetHistoryData(_focusedRelation);
public List<cContainerCollectionData> GetContainerData()
=> _supportCaseProcessor.GetContainerData(_focusedRelation);
=> ((SupportCaseProcessor)_supportCaseProcessor).GetContainerData(_focusedRelation);
public List<cMenuDataBase> GetMenuBarData()
=> _supportCaseProcessor.GetMenuBarData(_focusedRelation);
public IEnumerable<cMenuDataBase> GetMenuData()
{
var menuBarDatas = cF4SDCockpitXmlConfig.Instance.MenuItems.ToDictionary(config => config.Key, config => (Config: config.Value, MenuValue: MenuDataFactory.Create(config.Value, _supportCaseProcessor.SupportCaseDataProviderArtifact.NamedParameterEntries, _selectedRelations.Values.Select(r => cF4sdIdentityEntry.GetFromSearchResult(r.Type)).ToList())));
try
{
List<string> menuBarDatasAddedToSection = new List<string>();
foreach (var menuBarData in menuBarDatas.Values)
{
bool isPinned = cF4SDCockpitXmlConfig.Instance.HealthCardConfig.QuickActionsPinned.Contains(menuBarData.Config.Name);
menuBarData.MenuValue.IconPositionIndex = isPinned ? cF4SDCockpitXmlConfig.Instance.HealthCardConfig.QuickActionsPinned.IndexOf(menuBarData.Config.Name) : -1;
// Same Logic in GetFilteredMenuData()
bool hasBeenAddedToSection = TryAddMenuDataToSection(ref menuBarDatas, menuBarData.Config.Section, menuBarData.MenuValue);
foreach (var section in menuBarData.Config.Sections)
{
// todo add test for adding to multiple sections
hasBeenAddedToSection = TryAddMenuDataToSection(ref menuBarDatas, section, menuBarData.MenuValue) || hasBeenAddedToSection; // order is relevent, because data has to be added to section
}
if (hasBeenAddedToSection)
menuBarDatasAddedToSection.Add(menuBarData.Config.Name);
}
foreach (var menuBarData in menuBarDatasAddedToSection)
{
if (cF4SDCockpitXmlConfig.Instance.HealthCardConfig.QuickActionsPinned.Contains(menuBarData))
continue;
menuBarDatas.Remove(menuBarData);
}
return menuBarDatas.Values.Select(data => data.MenuValue);
}
catch (Exception ex)
{
LogException(ex);
return new List<cMenuDataBase>();
}
}
public IEnumerable<cMenuDataBase> GetFilteredMenuData(MenuDataFilter filterQuery)
{
Dictionary<string, (cFasdBaseConfigMenuItem Config, cMenuDataBase MenuValue)> menuBarDatas = cF4SDCockpitXmlConfig.Instance.MenuItems.ToDictionary(config => config.Key, config => (Config: config.Value, MenuValue: MenuDataFactory.Create(config.Value, _supportCaseProcessor.SupportCaseDataProviderArtifact.NamedParameterEntries, _selectedRelations.Values.Select(r => cF4sdIdentityEntry.GetFromSearchResult(r.Type)).ToList())));
try
{
foreach (var menuBarData in menuBarDatas.Values)
{
// Same Logic in GetMenuData()
bool hasBeenAddedToSection = TryAddMenuDataToSection(ref menuBarDatas, menuBarData.Config.Section, menuBarData.MenuValue);
foreach (var section in menuBarData.Config.Sections)
{
// todo add test for adding to multiple sections
hasBeenAddedToSection = TryAddMenuDataToSection(ref menuBarDatas, section, menuBarData.MenuValue) || hasBeenAddedToSection; // order is relevent, because data has to be added to section
}
}
return menuBarDatas.Values.Select(data => data.MenuValue)
.Where(md => md.MenuText.ToLower().Contains(filterQuery?.SearchString?.ToLower()));
}
catch (Exception ex)
{
LogException(ex);
return new List<cMenuDataBase>();
}
}
private bool TryAddMenuDataToSection(ref Dictionary<string, (cFasdBaseConfigMenuItem Config, cMenuDataBase MenuValue)> menuBarDatas, string section, cMenuDataBase menuData)
{
if (string.IsNullOrEmpty(section))
return false;
if (!menuBarDatas.TryGetValue(section, out var sectionMenuBar))
return false;
if (!(sectionMenuBar.MenuValue is cMenuDataContainer containerData))
return false;
if (containerData.UiAction is cSubMenuAction subMenuAction)
{
if (!subMenuAction.SubMenuData.Any(item => item.MenuText == menuData.MenuText)) // todo SubMenuData maybe better as Dictionary<Guid, cMenuDataBase>
subMenuAction.SubMenuData.Add(menuData);
if (!containerData.SubMenuData.Any(item => item.MenuText == menuData.MenuText))
containerData.SubMenuData.Add(menuData);
}
return true;
}
internal IEnumerable<cHeadingDataModel> GetHeadingData()
=> SupportCaseHeadingController.GetHeadingData(_selectedRelations);
private static enumHighlightColor GetHighlightColor(enumHealthCardStateLevel level)
{
switch (level)
{
case enumHealthCardStateLevel.None:
return enumHighlightColor.none;
case enumHealthCardStateLevel.Ok:
return enumHighlightColor.green;
case enumHealthCardStateLevel.Warning:
return enumHighlightColor.orange;
case enumHealthCardStateLevel.Error:
return enumHighlightColor.red;
case enumHealthCardStateLevel.Info:
return enumHighlightColor.blue;
default:
return enumHighlightColor.none;
}
}
internal IEnumerable<cF4sdApiSearchResultRelation> GetRelationsOf(enumFasdInformationClass informationClass)
{
return _supportCaseProcessor.GetCaseRelations().FirstOrDefault(r => r.Key == informationClass);
}
internal AgentRemoteClientInfo GetAgentClientInfo()
{
if (_focusedRelation.Type != enumF4sdSearchResultClass.Computer)
return null;
int deviceId = 0;
int userId = 0;
bool hasAllRequiredNamedParameters =
_supportCaseProcessor.TryGetNamedParameterValue(_focusedRelation, SupportCaseProcessor.AgentOrganisationCodeNamedParameterName, out int organisationId)
&& _supportCaseProcessor.TryGetNamedParameterValue(_focusedRelation, SupportCaseProcessor.AgentUserIdNamedParameterName, out userId)
&& _supportCaseProcessor.TryGetNamedParameterValue(_focusedRelation, SupportCaseProcessor.AgentDeviceIdNamedParameterName, out deviceId);
if (!hasAllRequiredNamedParameters)
return null;
return new AgentRemoteClientInfo()
{
DeviceCode = deviceId,
AccountCode = userId,
OrganisationCode = organisationId
};
}
/// <summary>
/// Raised when the currently for a support case relevant and shown relations have been updated.
@@ -264,4 +463,15 @@ namespace FasdDesktopUi.Basics.Services.SupportCase.Controllers
public event EventHandler<HeadingDataEventArgs> HeadingDataChanged;
}
public class MenuDataFilter
{
public string SearchString { get; set; }
public MenuDataFilter(string searchString = null)
{
SearchString = searchString;
}
}
}