From 0b52999b1dcc969e8cc71c3076f54af329c20366 Mon Sep 17 00:00:00 2001 From: Meik Date: Mon, 13 Jul 2026 11:16:53 +0200 Subject: [PATCH] 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. --- .../Enumerations/ActionResultStatus.cs | 9 + .../Enumerations/ActionType.cs | 10 + .../Enumerations/TriggerEvent.cs | 10 + .../Events/ActionCompletedEventArgs.cs | 21 + .../Events/ActionConnectorEvents.cs | 40 + .../Events/TriggerEventArgs.cs | 15 + .../F4SD-ActionConnector.csproj | 16 + F4SD-ActionConnector/Models/ActionContext.cs | 12 + .../Models/ActionDefinitionBase.cs | 38 + F4SD-ActionConnector/Models/ActionResult.cs | 41 + .../ExternalCommunicationConfiguration.cs | 74 + .../Models/HttpCallDefinition.cs | 30 + .../Models/QuickActionDefinition.cs | 105 + F4SD-ActionConnector/Models/Trigger.cs | 101 + .../Payloads/ApplicationStartupPayload.cs | 9 + .../Payloads/CaseClosedPayload.cs | 17 + .../Payloads/CaseCreatedPayload.cs | 14 + F4SD-ActionConnector/Payloads/PayloadBase.cs | 9 + F4SD-AdaptableIcon/Enums/InternIcon.cs | 1 + F4SD-AdaptableIcon/IconPainter.cs | 7 + F4SD-Docu-Engine/DocuEngine.cs | 41 + .../DocuEngineDataProvider.cs | 93 + .../IDocuEngineDataToken.cs | 31 + .../ContentBlock/ContentBlock.cs | 227 +++ .../ContentBlockForeachCommand.cs | 52 + .../ContentBlock/ContentBlockHelperMethods.cs | 291 +++ .../ContentBlock/ContentBlockIfCommand.cs | 175 ++ .../ContentBlock/ContentBlockSwitchCommand.cs | 127 ++ .../DocuEngineParserException.cs | 15 + .../Exceptions/DocuEngineParserException.cs | 15 + .../Exceptions/SyntaxErrorException.cs | 26 + .../Exceptions/VariableErrorException.cs | 25 + .../DocuEngineParser/ParsingContext.cs | 13 + .../DocuEngineParser/SyntaxErrorException.cs | 26 + .../VariableErrorException.cs | 25 + F4SD-Docu-Engine/F4SD-Docu-Engine.csproj | 12 + F4SD-Gamification/CockpitAction.cs | 13 + F4SD-Gamification/F4SD-Gamification.csproj | 12 + F4SD-Gamification/LevelEventArgs.cs | 10 + .../Services/GamificationService.cs | 25 + F4SD-Gamification/Services/LevelService.cs | 178 ++ .../Services/PersistenceService.cs | 70 + F4SD-PhoneMonitor/F4SD-PhoneMonitor.csproj | 2 +- F4SD-PhoneMonitor/packages.config | 2 +- .../Controllers/MenuDataFactoryTest.cs | 44 + .../SupportCase/SupportCaseProcessorTest.cs | 224 +++ .../Sevices/SupportCase/SupportCaseTest.cs | 4 +- .../F4SD.Cockpit.Client.Test.csproj | 8 +- .../F4SD-Cockpit-Client-Base.csproj | 30 +- .../FasdCockpitCommunicationBase.cs | 28 +- .../IRemoteDesktopClientInfo.cs | 6 + .../IRemoteDesktopConnectionDetails.cs | 6 + .../RemoteDesktopConnectionStatus.cs | 13 + .../RemoteDesktopConnectionStatusResult.cs | 16 + .../RemoteDesktopCommunicationBase.cs | 29 + FasdCockpitBase/app.config | 2 +- FasdCockpitBase/packages.config | 12 +- .../F4SD-Cockpit-Client-Communication.csproj | 17 +- .../FasdCockpitCommunicationWeb.cs | 372 ++-- .../AgentRemoteDesktopCommunication.cs | 116 ++ .../AgentRemoteDesktopConnecitonDetails.cs | 18 + .../RemoteDesktopCommunication/ApiResult.cs | 20 + FasdCockpitCommunication/app.config | 2 +- FasdCockpitCommunication/packages.config | 2 +- .../Config/F4SD-QuickAction-Configuration.xml | 22 +- .../Config/F4SD-QuickAction-Configuration.xsd | 14 +- .../F4SD-Cockpit-Client-Demo.csproj | 36 +- .../F4SDCockpitCommunicationDemo.cs | 811 ++++---- FasdCockpitCommunicationDemo/TicketModel.cs | 24 +- FasdCockpitCommunicationDemo/app.config | 2 +- .../cF4SDHealthCardJsonRawData.cs | 2 +- FasdCockpitCommunicationDemo/packages.config | 2 +- FasdDesktopUi/App.config | 2 +- FasdDesktopUi/App.xaml.cs | 5 +- FasdDesktopUi/AppStartUp.cs | 30 +- .../Converter/LanguageCultureConverter.cs | 27 + .../Basics/ExternalToolExecutorEnh.cs | 2 +- .../Helper/ActionDisplayTypeInspector.cs | 126 ++ .../Basics/Helper/HealthCardDataHelper.cs | 40 +- .../Basics/Helper/MenuItemDataProvider.cs | 19 +- .../Basics/Helper/TicketDeepLinkHelper.cs | 148 +- .../Basics/Models/ConnectionStatusHelper.cs | 78 +- .../Basics/Models/DTOs/MenuDataBaseDto.cs | 175 ++ .../Basics/Models/DataCanvasDataModel.cs | 3 + FasdDesktopUi/Basics/Models/MenuDataModel.cs | 491 +++-- .../Basics/Models/NamedParameterEntry.cs | 2 +- .../Basics/Models/WidgetValueModel.cs | 15 +- FasdDesktopUi/Basics/SearchHistoryManager.cs | 8 +- .../Models/CockpitValueDisplayData.cs | 15 + .../QuickActionProtocollEntry.cs | 49 +- .../RelationService/IRelationService.cs | 4 +- .../RelationService/RelationService.cs | 76 +- .../AgentRemoteDesktopService.cs | 136 ++ .../Controllers/MenuDataFactory.cs | 48 + .../Controllers/SupportCaseController.cs | 230 ++- .../SupportCaseHeadingController.cs | 1 + .../Services/SupportCase/ISupportCase.cs | 2 +- .../Processors/ISupportCaseProcessor.cs | 29 + .../SupportCaseDetailsValueProcessesor.cs | 145 ++ .../Processors/SupportCaseProcessor.cs | 643 +++++++ .../SupportCaseProcessorFactory.cs | 6 +- .../Services/SupportCase/SupportCase.cs | 59 +- .../SupportCase/SupportCaseProcessor.cs | 281 --- .../Services/TicketOverviewUpdateService.cs | 1168 +++++------ .../Basics/SupportCaseDataProvider.cs | 8 +- .../NativeActions/RemoteConnectionAction.cs | 171 ++ .../UiActions/ShowDetailedDataAction.cs | 2 +- .../Basics/UiActions/SubMenuAction.cs | 8 +- .../Basics/UiActions/UiActionBase.cs | 2 + .../Basics/UiActions/UiCopyAction.cs | 4 + .../Basics/UiActions/UiDemoQuickAction.cs | 11 +- .../Basics/UiActions/UiLocalQuickAction.cs | 8 +- .../UiActions/UiLocalWebRequestQuickAction.cs | 12 +- .../Basics/UiActions/UiNativeQuickAction.cs | 49 + .../UiActions/UiProcessSearchHistoryEntry.cs | 7 +- .../UiProcessSearchRelationAction.cs | 43 +- .../UiActions/UiProcessSearchResultAction.cs | 2 +- .../Basics/UiActions/UiQuickAction.cs | 2 +- .../Basics/UiActions/UiRemoteQuickAction.cs | 15 +- .../Basics/UiActions/UiServerQuickAction.cs | 8 +- .../Basics/UserControls/CustomMenuItem.xaml | 11 +- .../UserControls/CustomMenuItem.xaml.cs | 556 +++--- .../CustomMenuItemToolTipTemplate.xaml | 23 + .../CustomMenuItemToolTipTemplate.xaml.cs | 36 + .../AdjustableParameterSection.xaml | 4 +- .../AdjustableParameterSection.xaml.cs | 62 +- .../UserControls/DataCanvas/DataCanvas.xaml | 6 +- .../DataCanvas/DataCanvas.xaml.cs | 19 +- .../DataCanvas/DetailedRecommendation.xaml.cs | 29 +- .../QuickActionStatusMonitor.xaml.cs | 19 +- .../Basics/UserControls/FooterButton.xaml | 40 + .../Basics/UserControls/FooterButton.xaml.cs | 84 + .../Gamification/LevelTracker.xaml | 74 + .../Gamification/LevelTracker.xaml.cs | 39 + .../InformationClassSearchBar.xaml | 72 + .../InformationClassSearchBar.xaml.cs | 287 +++ .../Basics/UserControls/MenuBar.xaml.cs | 32 +- .../Basics/UserControls/Notepad.xaml | 1 + .../Basics/UserControls/Notepad.xaml.cs | 34 +- .../UserControls/QuickActionSelector.xaml | 18 +- .../UserControls/QuickActionSelector.xaml.cs | 46 +- .../QuickTip/QuickTipStatusMonitor.xaml.cs | 67 +- .../Basics/UserControls/SearchBar.xaml | 57 +- .../Basics/UserControls/SearchBar.xaml.cs | 404 ++-- .../CustomSearchResultCollection.xaml.cs | 4 +- .../Basics/UserControls/TimerView.xaml | 71 +- .../Basics/UserControls/TimerView.xaml.cs | 106 +- FasdDesktopUi/Basics/cUtility.cs | 12 +- FasdDesktopUi/Config/LanguageDefinitions.xml | 290 +-- FasdDesktopUi/F4SD-Cockpit-Client.csproj | 103 +- .../AdvancedSearchPageView.xaml | 18 +- .../AdvancedSearchPageView.xaml.cs | 4 +- .../CustomMessageBox/CustomMessageBox.xaml.cs | 20 +- .../Pages/DetailsPage/DetailsPageView.xaml | 201 +- .../Pages/DetailsPage/DetailsPageView.xaml.cs | 195 +- .../DetailsPageDataHistoryValueColumn.xaml.cs | 25 +- .../DetailsPageNavigationHeading.xaml.cs | 23 +- .../UserControls/DetailsPageWidget.xaml.cs | 2 +- .../Pages/LevelUpPage/LevelUpPage.xaml | 112 ++ .../Pages/LevelUpPage/LevelUpPage.xaml.cs | 91 + .../Pages/SearchPage/SearchPageView.xaml | 12 +- .../Pages/SearchPage/SearchPageView.xaml.cs | 13 +- .../SettingsPage/M42SettingsPageView.xaml | 16 +- .../Pages/SettingsPage/SettingsPageView.xaml | 20 + .../SettingsPage/SettingsPageView.xaml.cs | 143 +- .../Pages/SlimPage/SlimPageView.xaml | 6 +- .../Pages/SlimPage/SlimPageView.xaml.cs | 8 +- .../SplashScreenView/SplashScreenView.xaml | 55 +- .../SplashScreenView/SplashScreenView.xaml.cs | 14 +- .../TicketCompletion/TicketCompletion.xaml.cs | 38 +- .../DarkModeResources.xaml | 8 +- .../CustomizableResources.xaml | 57 +- .../LightModeResources.xaml | 8 +- FasdDesktopUi/cFasdCockpitConfig.cs | 98 +- FasdDesktopUi/packages.config | 14 +- FasdExcelToJsonConverter/App.config | 2 +- .../ExcelHealthcardParser.cs | 18 +- .../F4SD-ExcelToJson-Converter.csproj | 2 +- FasdExcelToJsonConverter/packages.config | 2 +- FasdTestSolution.sln | 259 ++- PhoenixViewer/Assets/Themes/DarkTheme.xaml | 63 + PhoenixViewer/Assets/Themes/LightTheme.xaml | 63 + PhoenixViewer/GenerativeAI.Live.dll | Bin 0 -> 61952 bytes PhoenixViewer/GenerativeAI.dll | Bin 0 -> 2164736 bytes PhoenixViewer/Markdig.Wpf.dll | Bin 0 -> 37376 bytes PhoenixViewer/Markdig.dll | Bin 0 -> 406528 bytes PhoenixViewer/MessagePack.Annotations.dll | Bin 0 -> 11776 bytes PhoenixViewer/MessagePack.dll | Bin 0 -> 326144 bytes .../Microsoft.AspNetCore.Authorization.dll | Bin 0 -> 41016 bytes ...ft.AspNetCore.Connections.Abstractions.dll | Bin 0 -> 40208 bytes ...Microsoft.AspNetCore.Http.Abstractions.dll | Bin 0 -> 76832 bytes ...oft.AspNetCore.Http.Connections.Client.dll | Bin 0 -> 119456 bytes ...oft.AspNetCore.Http.Connections.Common.dll | Bin 0 -> 28952 bytes .../Microsoft.AspNetCore.Http.Features.dll | Bin 0 -> 33344 bytes PhoenixViewer/Microsoft.AspNetCore.Http.dll | Bin 0 -> 84512 bytes ...crosoft.AspNetCore.SignalR.Client.Core.dll | Bin 0 -> 197896 bytes .../Microsoft.AspNetCore.SignalR.Client.dll | Bin 0 -> 20752 bytes .../Microsoft.AspNetCore.SignalR.Common.dll | Bin 0 -> 42656 bytes .../Microsoft.AspNetCore.SignalR.Core.dll | Bin 0 -> 124984 bytes ...soft.AspNetCore.SignalR.Protocols.Json.dll | Bin 0 -> 37536 bytes ...pNetCore.SignalR.Protocols.MessagePack.dll | Bin 0 -> 39176 bytes .../Microsoft.AspNetCore.WebUtilities.dll | Bin 0 -> 67128 bytes ...rosoft.Extensions.Caching.Abstractions.dll | Bin 0 -> 37176 bytes .../Microsoft.Extensions.Caching.Memory.dll | Bin 0 -> 46864 bytes ....Extensions.Configuration.Abstractions.dll | Bin 0 -> 27936 bytes ...rosoft.Extensions.Configuration.Binder.dll | Bin 0 -> 42784 bytes ...t.Extensions.Configuration.CommandLine.dll | Bin 0 -> 24736 bytes ...ons.Configuration.EnvironmentVariables.dll | Bin 0 -> 21280 bytes ...xtensions.Configuration.FileExtensions.dll | Bin 0 -> 27936 bytes ...icrosoft.Extensions.Configuration.Json.dll | Bin 0 -> 26920 bytes ...t.Extensions.Configuration.UserSecrets.dll | Bin 0 -> 25384 bytes .../Microsoft.Extensions.Configuration.dll | Bin 0 -> 43800 bytes ...sions.DependencyInjection.Abstractions.dll | Bin 0 -> 65296 bytes ...crosoft.Extensions.DependencyInjection.dll | Bin 0 -> 92448 bytes ...ft.Extensions.Diagnostics.Abstractions.dll | Bin 0 -> 30480 bytes .../Microsoft.Extensions.Diagnostics.dll | Bin 0 -> 35592 bytes .../Microsoft.Extensions.Features.dll | Bin 0 -> 26392 bytes ....Extensions.FileProviders.Abstractions.dll | Bin 0 -> 22176 bytes ...soft.Extensions.FileProviders.Physical.dll | Bin 0 -> 44808 bytes ...icrosoft.Extensions.FileSystemGlobbing.dll | Bin 0 -> 45848 bytes ...rosoft.Extensions.Hosting.Abstractions.dll | Bin 0 -> 51472 bytes .../Microsoft.Extensions.Hosting.dll | Bin 0 -> 72488 bytes PhoenixViewer/Microsoft.Extensions.Http.dll | Bin 0 -> 88736 bytes ...rosoft.Extensions.Logging.Abstractions.dll | Bin 0 -> 66312 bytes ...osoft.Extensions.Logging.Configuration.dll | Bin 0 -> 27912 bytes .../Microsoft.Extensions.Logging.Console.dll | Bin 0 -> 71464 bytes .../Microsoft.Extensions.Logging.Debug.dll | Bin 0 -> 20248 bytes .../Microsoft.Extensions.Logging.EventLog.dll | Bin 0 -> 25760 bytes ...crosoft.Extensions.Logging.EventSource.dll | Bin 0 -> 34568 bytes .../Microsoft.Extensions.Logging.dll | Bin 0 -> 51464 bytes .../Microsoft.Extensions.ObjectPool.dll | Bin 0 -> 22840 bytes ...nsions.Options.ConfigurationExtensions.dll | Bin 0 -> 22688 bytes .../Microsoft.Extensions.Options.dll | Bin 0 -> 64776 bytes .../Microsoft.Extensions.Primitives.dll | Bin 0 -> 44304 bytes .../Microsoft.IO.RecyclableMemoryStream.dll | Bin 0 -> 65968 bytes PhoenixViewer/Microsoft.NET.StringTools.dll | Bin 0 -> 30352 bytes PhoenixViewer/Microsoft.Net.Http.Headers.dll | Bin 0 -> 76360 bytes PhoenixViewer/NLog.Extensions.Logging.dll | Bin 0 -> 74240 bytes PhoenixViewer/NLog.dll | Bin 0 -> 863232 bytes PhoenixViewer/Newtonsoft.Json.dll | Bin 0 -> 712464 bytes PhoenixViewer/OpenAI.dll | Bin 0 -> 4353488 bytes PhoenixViewer/Phoenix.Core.dll | Bin 0 -> 145408 bytes PhoenixViewer/Phoenix.UI.dll | Bin 0 -> 645632 bytes PhoenixViewer/Phoenix.Viewer.deps.json | 1703 +++++++++++++++++ PhoenixViewer/Phoenix.Viewer.dll | Bin 0 -> 297472 bytes PhoenixViewer/Phoenix.Viewer.exe | Bin 0 -> 440832 bytes .../Phoenix.Viewer.runtimeconfig.json | 19 + PhoenixViewer/SharpVectors.Converters.Wpf.dll | Bin 0 -> 148480 bytes PhoenixViewer/SharpVectors.Core.dll | Bin 0 -> 187392 bytes PhoenixViewer/SharpVectors.Css.dll | Bin 0 -> 93696 bytes PhoenixViewer/SharpVectors.Dom.dll | Bin 0 -> 34304 bytes PhoenixViewer/SharpVectors.Model.dll | Bin 0 -> 1073152 bytes PhoenixViewer/SharpVectors.Rendering.Wpf.dll | Bin 0 -> 231424 bytes PhoenixViewer/SharpVectors.Runtime.Wpf.dll | Bin 0 -> 73216 bytes .../SkiaSharp.Views.Desktop.Common.dll | Bin 0 -> 26144 bytes PhoenixViewer/SkiaSharp.Views.WPF.dll | Bin 0 -> 22560 bytes PhoenixViewer/SkiaSharp.dll | Bin 0 -> 437288 bytes PhoenixViewer/System.ClientModel.dll | Bin 0 -> 232480 bytes PhoenixViewer/System.CodeDom.dll | Bin 0 -> 184072 bytes PhoenixViewer/System.CommandLine.Hosting.dll | Bin 0 -> 28216 bytes ...tem.CommandLine.NamingConventionBinder.dll | Bin 0 -> 54856 bytes PhoenixViewer/System.CommandLine.dll | Bin 0 -> 149040 bytes .../System.Diagnostics.DiagnosticSource.dll | Bin 0 -> 192272 bytes PhoenixViewer/System.Diagnostics.EventLog.dll | Bin 0 -> 51992 bytes PhoenixViewer/System.IO.Pipelines.dll | Bin 0 -> 77608 bytes PhoenixViewer/System.Management.dll | Bin 0 -> 73488 bytes PhoenixViewer/System.Memory.Data.dll | Bin 0 -> 30472 bytes PhoenixViewer/System.Net.ServerSentEvents.dll | Bin 0 -> 34088 bytes PhoenixViewer/System.Reactive.dll | Bin 0 -> 1344080 bytes ...em.Security.Cryptography.ProtectedData.dll | Bin 0 -> 38696 bytes PhoenixViewer/System.Text.Encodings.Web.dll | Bin 0 -> 70968 bytes PhoenixViewer/System.Text.Json.dll | Bin 0 -> 643880 bytes PhoenixViewer/Websocket.Client.dll | Bin 0 -> 43520 bytes PhoenixViewer/appSettings.Installer.json | 8 + PhoenixViewer/appSettings.json | 11 + .../cs/System.CommandLine.resources.dll | Bin 0 -> 19008 bytes PhoenixViewer/de-DE/Phoenix.UI.resources.dll | Bin 0 -> 5632 bytes .../de/System.CommandLine.resources.dll | Bin 0 -> 18992 bytes PhoenixViewer/en-US/Phoenix.UI.resources.dll | Bin 0 -> 3584 bytes .../es/System.CommandLine.resources.dll | Bin 0 -> 19016 bytes .../fr/System.CommandLine.resources.dll | Bin 0 -> 18992 bytes .../it/System.CommandLine.resources.dll | Bin 0 -> 19016 bytes .../ja/System.CommandLine.resources.dll | Bin 0 -> 19016 bytes .../ko/System.CommandLine.resources.dll | Bin 0 -> 19016 bytes .../pl/System.CommandLine.resources.dll | Bin 0 -> 18992 bytes .../pt-BR/System.CommandLine.resources.dll | Bin 0 -> 18992 bytes .../ru/System.CommandLine.resources.dll | Bin 0 -> 19504 bytes .../lib/net8.0/System.Text.Encodings.Web.dll | Bin 0 -> 70952 bytes .../runtimes/osx/native/libSkiaSharp.dylib | Bin 0 -> 15084640 bytes .../win-arm64/native/libSkiaSharp.dll | Bin 0 -> 7948320 bytes .../runtimes/win-x64/native/libSkiaSharp.dll | Bin 0 -> 9414184 bytes .../runtimes/win-x86/native/libSkiaSharp.dll | Bin 0 -> 7980592 bytes .../net8.0/System.Diagnostics.EventLog.dll | Bin 0 -> 172704 bytes .../win/lib/net8.0/System.Management.dll | Bin 0 -> 313648 bytes .../tr/System.CommandLine.resources.dll | Bin 0 -> 18984 bytes .../zh-Hans/System.CommandLine.resources.dll | Bin 0 -> 18984 bytes .../zh-Hant/System.CommandLine.resources.dll | Bin 0 -> 18984 bytes Setup_Client/CopySourceFiles.cmd | 4 + Setup_Client/PhoenixFiles.wxs | 391 ++++ Setup_Client/Product.wxs | 53 +- Setup_Client/Setup_Client.wixproj | 21 +- Setup_Client/SignSourceFiles.cmd | 5 +- Shared/SharedAssemblyInfo.cs | 2 +- 303 files changed, 11235 insertions(+), 3580 deletions(-) create mode 100644 F4SD-ActionConnector/Enumerations/ActionResultStatus.cs create mode 100644 F4SD-ActionConnector/Enumerations/ActionType.cs create mode 100644 F4SD-ActionConnector/Enumerations/TriggerEvent.cs create mode 100644 F4SD-ActionConnector/Events/ActionCompletedEventArgs.cs create mode 100644 F4SD-ActionConnector/Events/ActionConnectorEvents.cs create mode 100644 F4SD-ActionConnector/Events/TriggerEventArgs.cs create mode 100644 F4SD-ActionConnector/F4SD-ActionConnector.csproj create mode 100644 F4SD-ActionConnector/Models/ActionContext.cs create mode 100644 F4SD-ActionConnector/Models/ActionDefinitionBase.cs create mode 100644 F4SD-ActionConnector/Models/ActionResult.cs create mode 100644 F4SD-ActionConnector/Models/ExternalCommunicationConfiguration.cs create mode 100644 F4SD-ActionConnector/Models/HttpCallDefinition.cs create mode 100644 F4SD-ActionConnector/Models/QuickActionDefinition.cs create mode 100644 F4SD-ActionConnector/Models/Trigger.cs create mode 100644 F4SD-ActionConnector/Payloads/ApplicationStartupPayload.cs create mode 100644 F4SD-ActionConnector/Payloads/CaseClosedPayload.cs create mode 100644 F4SD-ActionConnector/Payloads/CaseCreatedPayload.cs create mode 100644 F4SD-ActionConnector/Payloads/PayloadBase.cs create mode 100644 F4SD-Docu-Engine/DocuEngine.cs create mode 100644 F4SD-Docu-Engine/DocuEngineDataProvider/DocuEngineDataProvider.cs create mode 100644 F4SD-Docu-Engine/DocuEngineDataProvider/IDocuEngineDataToken.cs create mode 100644 F4SD-Docu-Engine/DocuEngineParser/ContentBlock/ContentBlock.cs create mode 100644 F4SD-Docu-Engine/DocuEngineParser/ContentBlock/ContentBlockForeachCommand.cs create mode 100644 F4SD-Docu-Engine/DocuEngineParser/ContentBlock/ContentBlockHelperMethods.cs create mode 100644 F4SD-Docu-Engine/DocuEngineParser/ContentBlock/ContentBlockIfCommand.cs create mode 100644 F4SD-Docu-Engine/DocuEngineParser/ContentBlock/ContentBlockSwitchCommand.cs create mode 100644 F4SD-Docu-Engine/DocuEngineParser/DocuEngineParserException.cs create mode 100644 F4SD-Docu-Engine/DocuEngineParser/Exceptions/DocuEngineParserException.cs create mode 100644 F4SD-Docu-Engine/DocuEngineParser/Exceptions/SyntaxErrorException.cs create mode 100644 F4SD-Docu-Engine/DocuEngineParser/Exceptions/VariableErrorException.cs create mode 100644 F4SD-Docu-Engine/DocuEngineParser/ParsingContext.cs create mode 100644 F4SD-Docu-Engine/DocuEngineParser/SyntaxErrorException.cs create mode 100644 F4SD-Docu-Engine/DocuEngineParser/VariableErrorException.cs create mode 100644 F4SD-Docu-Engine/F4SD-Docu-Engine.csproj create mode 100644 F4SD-Gamification/CockpitAction.cs create mode 100644 F4SD-Gamification/F4SD-Gamification.csproj create mode 100644 F4SD-Gamification/LevelEventArgs.cs create mode 100644 F4SD-Gamification/Services/GamificationService.cs create mode 100644 F4SD-Gamification/Services/LevelService.cs create mode 100644 F4SD-Gamification/Services/PersistenceService.cs create mode 100644 F4SD.Cockpit.Client.Test/Basics/Sevices/SupportCase/Controllers/MenuDataFactoryTest.cs create mode 100644 F4SD.Cockpit.Client.Test/Basics/Sevices/SupportCase/SupportCaseProcessorTest.cs create mode 100644 FasdCockpitBase/Models/RemoteDesktopConnection/IRemoteDesktopClientInfo.cs create mode 100644 FasdCockpitBase/Models/RemoteDesktopConnection/IRemoteDesktopConnectionDetails.cs create mode 100644 FasdCockpitBase/Models/RemoteDesktopConnection/RemoteDesktopConnectionStatus.cs create mode 100644 FasdCockpitBase/Models/RemoteDesktopConnection/RemoteDesktopConnectionStatusResult.cs create mode 100644 FasdCockpitBase/RemoteDesktopCommunicationBase.cs create mode 100644 FasdCockpitCommunication/RemoteDesktopCommunication/AgentRemoteDesktopCommunication.cs create mode 100644 FasdCockpitCommunication/RemoteDesktopCommunication/AgentRemoteDesktopConnecitonDetails.cs create mode 100644 FasdCockpitCommunication/RemoteDesktopCommunication/ApiResult.cs create mode 100644 FasdDesktopUi/Basics/Converter/LanguageCultureConverter.cs create mode 100644 FasdDesktopUi/Basics/Helper/ActionDisplayTypeInspector.cs create mode 100644 FasdDesktopUi/Basics/Models/DTOs/MenuDataBaseDto.cs create mode 100644 FasdDesktopUi/Basics/Services/Models/CockpitValueDisplayData.cs create mode 100644 FasdDesktopUi/Basics/Services/RemoteDesktop/AgentRemoteDesktopService.cs create mode 100644 FasdDesktopUi/Basics/Services/SupportCase/Controllers/MenuDataFactory.cs create mode 100644 FasdDesktopUi/Basics/Services/SupportCase/Processors/ISupportCaseProcessor.cs create mode 100644 FasdDesktopUi/Basics/Services/SupportCase/Processors/SupportCaseDetailsValueProcessesor.cs create mode 100644 FasdDesktopUi/Basics/Services/SupportCase/Processors/SupportCaseProcessor.cs rename FasdDesktopUi/Basics/Services/SupportCase/{ => Processors}/SupportCaseProcessorFactory.cs (52%) delete mode 100644 FasdDesktopUi/Basics/Services/SupportCase/SupportCaseProcessor.cs create mode 100644 FasdDesktopUi/Basics/UiActions/NativeActions/RemoteConnectionAction.cs create mode 100644 FasdDesktopUi/Basics/UiActions/UiNativeQuickAction.cs create mode 100644 FasdDesktopUi/Basics/UserControls/CustomMenuItemToolTipTemplate.xaml create mode 100644 FasdDesktopUi/Basics/UserControls/CustomMenuItemToolTipTemplate.xaml.cs create mode 100644 FasdDesktopUi/Basics/UserControls/FooterButton.xaml create mode 100644 FasdDesktopUi/Basics/UserControls/FooterButton.xaml.cs create mode 100644 FasdDesktopUi/Basics/UserControls/Gamification/LevelTracker.xaml create mode 100644 FasdDesktopUi/Basics/UserControls/Gamification/LevelTracker.xaml.cs create mode 100644 FasdDesktopUi/Basics/UserControls/InformationClassSearchBar.xaml create mode 100644 FasdDesktopUi/Basics/UserControls/InformationClassSearchBar.xaml.cs create mode 100644 FasdDesktopUi/Pages/LevelUpPage/LevelUpPage.xaml create mode 100644 FasdDesktopUi/Pages/LevelUpPage/LevelUpPage.xaml.cs create mode 100644 PhoenixViewer/Assets/Themes/DarkTheme.xaml create mode 100644 PhoenixViewer/Assets/Themes/LightTheme.xaml create mode 100644 PhoenixViewer/GenerativeAI.Live.dll create mode 100644 PhoenixViewer/GenerativeAI.dll create mode 100644 PhoenixViewer/Markdig.Wpf.dll create mode 100644 PhoenixViewer/Markdig.dll create mode 100644 PhoenixViewer/MessagePack.Annotations.dll create mode 100644 PhoenixViewer/MessagePack.dll create mode 100644 PhoenixViewer/Microsoft.AspNetCore.Authorization.dll create mode 100644 PhoenixViewer/Microsoft.AspNetCore.Connections.Abstractions.dll create mode 100644 PhoenixViewer/Microsoft.AspNetCore.Http.Abstractions.dll create mode 100644 PhoenixViewer/Microsoft.AspNetCore.Http.Connections.Client.dll create mode 100644 PhoenixViewer/Microsoft.AspNetCore.Http.Connections.Common.dll create mode 100644 PhoenixViewer/Microsoft.AspNetCore.Http.Features.dll create mode 100644 PhoenixViewer/Microsoft.AspNetCore.Http.dll create mode 100644 PhoenixViewer/Microsoft.AspNetCore.SignalR.Client.Core.dll create mode 100644 PhoenixViewer/Microsoft.AspNetCore.SignalR.Client.dll create mode 100644 PhoenixViewer/Microsoft.AspNetCore.SignalR.Common.dll create mode 100644 PhoenixViewer/Microsoft.AspNetCore.SignalR.Core.dll create mode 100644 PhoenixViewer/Microsoft.AspNetCore.SignalR.Protocols.Json.dll create mode 100644 PhoenixViewer/Microsoft.AspNetCore.SignalR.Protocols.MessagePack.dll create mode 100644 PhoenixViewer/Microsoft.AspNetCore.WebUtilities.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Caching.Abstractions.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Caching.Memory.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Configuration.Abstractions.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Configuration.Binder.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Configuration.CommandLine.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Configuration.EnvironmentVariables.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Configuration.FileExtensions.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Configuration.Json.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Configuration.UserSecrets.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Configuration.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.DependencyInjection.Abstractions.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.DependencyInjection.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Diagnostics.Abstractions.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Diagnostics.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Features.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.FileProviders.Abstractions.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.FileProviders.Physical.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.FileSystemGlobbing.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Hosting.Abstractions.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Hosting.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Http.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Logging.Abstractions.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Logging.Configuration.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Logging.Console.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Logging.Debug.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Logging.EventLog.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Logging.EventSource.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Logging.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.ObjectPool.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Options.ConfigurationExtensions.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Options.dll create mode 100644 PhoenixViewer/Microsoft.Extensions.Primitives.dll create mode 100644 PhoenixViewer/Microsoft.IO.RecyclableMemoryStream.dll create mode 100644 PhoenixViewer/Microsoft.NET.StringTools.dll create mode 100644 PhoenixViewer/Microsoft.Net.Http.Headers.dll create mode 100644 PhoenixViewer/NLog.Extensions.Logging.dll create mode 100644 PhoenixViewer/NLog.dll create mode 100644 PhoenixViewer/Newtonsoft.Json.dll create mode 100644 PhoenixViewer/OpenAI.dll create mode 100644 PhoenixViewer/Phoenix.Core.dll create mode 100644 PhoenixViewer/Phoenix.UI.dll create mode 100644 PhoenixViewer/Phoenix.Viewer.deps.json create mode 100644 PhoenixViewer/Phoenix.Viewer.dll create mode 100644 PhoenixViewer/Phoenix.Viewer.exe create mode 100644 PhoenixViewer/Phoenix.Viewer.runtimeconfig.json create mode 100644 PhoenixViewer/SharpVectors.Converters.Wpf.dll create mode 100644 PhoenixViewer/SharpVectors.Core.dll create mode 100644 PhoenixViewer/SharpVectors.Css.dll create mode 100644 PhoenixViewer/SharpVectors.Dom.dll create mode 100644 PhoenixViewer/SharpVectors.Model.dll create mode 100644 PhoenixViewer/SharpVectors.Rendering.Wpf.dll create mode 100644 PhoenixViewer/SharpVectors.Runtime.Wpf.dll create mode 100644 PhoenixViewer/SkiaSharp.Views.Desktop.Common.dll create mode 100644 PhoenixViewer/SkiaSharp.Views.WPF.dll create mode 100644 PhoenixViewer/SkiaSharp.dll create mode 100644 PhoenixViewer/System.ClientModel.dll create mode 100644 PhoenixViewer/System.CodeDom.dll create mode 100644 PhoenixViewer/System.CommandLine.Hosting.dll create mode 100644 PhoenixViewer/System.CommandLine.NamingConventionBinder.dll create mode 100644 PhoenixViewer/System.CommandLine.dll create mode 100644 PhoenixViewer/System.Diagnostics.DiagnosticSource.dll create mode 100644 PhoenixViewer/System.Diagnostics.EventLog.dll create mode 100644 PhoenixViewer/System.IO.Pipelines.dll create mode 100644 PhoenixViewer/System.Management.dll create mode 100644 PhoenixViewer/System.Memory.Data.dll create mode 100644 PhoenixViewer/System.Net.ServerSentEvents.dll create mode 100644 PhoenixViewer/System.Reactive.dll create mode 100644 PhoenixViewer/System.Security.Cryptography.ProtectedData.dll create mode 100644 PhoenixViewer/System.Text.Encodings.Web.dll create mode 100644 PhoenixViewer/System.Text.Json.dll create mode 100644 PhoenixViewer/Websocket.Client.dll create mode 100644 PhoenixViewer/appSettings.Installer.json create mode 100644 PhoenixViewer/appSettings.json create mode 100644 PhoenixViewer/cs/System.CommandLine.resources.dll create mode 100644 PhoenixViewer/de-DE/Phoenix.UI.resources.dll create mode 100644 PhoenixViewer/de/System.CommandLine.resources.dll create mode 100644 PhoenixViewer/en-US/Phoenix.UI.resources.dll create mode 100644 PhoenixViewer/es/System.CommandLine.resources.dll create mode 100644 PhoenixViewer/fr/System.CommandLine.resources.dll create mode 100644 PhoenixViewer/it/System.CommandLine.resources.dll create mode 100644 PhoenixViewer/ja/System.CommandLine.resources.dll create mode 100644 PhoenixViewer/ko/System.CommandLine.resources.dll create mode 100644 PhoenixViewer/pl/System.CommandLine.resources.dll create mode 100644 PhoenixViewer/pt-BR/System.CommandLine.resources.dll create mode 100644 PhoenixViewer/ru/System.CommandLine.resources.dll create mode 100644 PhoenixViewer/runtimes/browser/lib/net8.0/System.Text.Encodings.Web.dll create mode 100644 PhoenixViewer/runtimes/osx/native/libSkiaSharp.dylib create mode 100644 PhoenixViewer/runtimes/win-arm64/native/libSkiaSharp.dll create mode 100644 PhoenixViewer/runtimes/win-x64/native/libSkiaSharp.dll create mode 100644 PhoenixViewer/runtimes/win-x86/native/libSkiaSharp.dll create mode 100644 PhoenixViewer/runtimes/win/lib/net8.0/System.Diagnostics.EventLog.dll create mode 100644 PhoenixViewer/runtimes/win/lib/net8.0/System.Management.dll create mode 100644 PhoenixViewer/tr/System.CommandLine.resources.dll create mode 100644 PhoenixViewer/zh-Hans/System.CommandLine.resources.dll create mode 100644 PhoenixViewer/zh-Hant/System.CommandLine.resources.dll create mode 100644 Setup_Client/PhoenixFiles.wxs diff --git a/F4SD-ActionConnector/Enumerations/ActionResultStatus.cs b/F4SD-ActionConnector/Enumerations/ActionResultStatus.cs new file mode 100644 index 0000000..38353a3 --- /dev/null +++ b/F4SD-ActionConnector/Enumerations/ActionResultStatus.cs @@ -0,0 +1,9 @@ +namespace F4SD.ActionConnector.Enumerations +{ + public enum ActionResultStatus + { + Success, + Failure, + Skipped + } +} diff --git a/F4SD-ActionConnector/Enumerations/ActionType.cs b/F4SD-ActionConnector/Enumerations/ActionType.cs new file mode 100644 index 0000000..380c4df --- /dev/null +++ b/F4SD-ActionConnector/Enumerations/ActionType.cs @@ -0,0 +1,10 @@ +namespace F4SD.ActionConnector.Enumerations +{ + public enum ActionType + { + Unknown, + QuickAction, + HttpCall, + Webhook, + } +} diff --git a/F4SD-ActionConnector/Enumerations/TriggerEvent.cs b/F4SD-ActionConnector/Enumerations/TriggerEvent.cs new file mode 100644 index 0000000..e4d1069 --- /dev/null +++ b/F4SD-ActionConnector/Enumerations/TriggerEvent.cs @@ -0,0 +1,10 @@ +namespace F4SD.ActionConnector.Enumerations +{ + public enum TriggerEvent + { + Unknown, + ApplicationStartup, + CaseClosed, + CaseCreated, + } +} diff --git a/F4SD-ActionConnector/Events/ActionCompletedEventArgs.cs b/F4SD-ActionConnector/Events/ActionCompletedEventArgs.cs new file mode 100644 index 0000000..2c87c3d --- /dev/null +++ b/F4SD-ActionConnector/Events/ActionCompletedEventArgs.cs @@ -0,0 +1,21 @@ +using System; +using F4SD.ActionConnector.Models; +using F4SD.ActionConnector.Payloads; + +namespace F4SD.ActionConnector.Events +{ + /// + /// Raised after a sync action finishes. Carries the result for Cockpit integration. + /// + public sealed class ActionCompletedEventArgs : EventArgs + { + public ActionResult Result { get; } + public ActionContext Context { get; } + + public ActionCompletedEventArgs(ActionResult result, ActionContext context) + { + Result = result; + Context = context; + } + } +} diff --git a/F4SD-ActionConnector/Events/ActionConnectorEvents.cs b/F4SD-ActionConnector/Events/ActionConnectorEvents.cs new file mode 100644 index 0000000..3938321 --- /dev/null +++ b/F4SD-ActionConnector/Events/ActionConnectorEvents.cs @@ -0,0 +1,40 @@ +using System; +using F4SD.ActionConnector.Payloads; + +namespace F4SD.ActionConnector.Events +{ + /// + /// Static event bus. F4SD calls the methods after a business event occurs. + /// The connector engine subscribes to the corresponding events and dispatches configured actions. + /// + public static class ActionConnectorEvents + { + /// Raised when a support case is closed. + public static event EventHandler> CaseClosed; + + /// Raised when a new support case is created. + public static event EventHandler> CaseCreated; + + /// Raised once during application startup, after configuration is loaded. + public static event EventHandler> ApplicationStartup; + + /// + /// Raised after every sync action completes. The Cockpit Client subscribes here + /// to display results without coupling to specific trigger types. + /// + public static event EventHandler ActionCompleted; + + + public static void RaiseCaseClosed(object sender, CaseClosedPayload payload) + => CaseClosed?.Invoke(sender, new TriggerEventArgs(payload)); + + public static void RaiseCaseCreated(object sender, CaseCreatedPayload payload) + => CaseCreated?.Invoke(sender, new TriggerEventArgs(payload)); + + public static void RaiseApplicationStartup(object sender, ApplicationStartupPayload payload) + => ApplicationStartup?.Invoke(sender, new TriggerEventArgs(payload)); + + public static void RaiseActionCompleted(object sender, ActionCompletedEventArgs args) + => ActionCompleted?.Invoke(sender, args); + } +} diff --git a/F4SD-ActionConnector/Events/TriggerEventArgs.cs b/F4SD-ActionConnector/Events/TriggerEventArgs.cs new file mode 100644 index 0000000..6a0482c --- /dev/null +++ b/F4SD-ActionConnector/Events/TriggerEventArgs.cs @@ -0,0 +1,15 @@ +using System; +using F4SD.ActionConnector.Payloads; + +namespace F4SD.ActionConnector.Events +{ + public sealed class TriggerEventArgs : EventArgs where TPayload : PayloadBase + { + public TPayload Payload { get; } + + public TriggerEventArgs(TPayload payload) + { + Payload = payload; + } + } +} diff --git a/F4SD-ActionConnector/F4SD-ActionConnector.csproj b/F4SD-ActionConnector/F4SD-ActionConnector.csproj new file mode 100644 index 0000000..c316ba7 --- /dev/null +++ b/F4SD-ActionConnector/F4SD-ActionConnector.csproj @@ -0,0 +1,16 @@ + + + + netstandard2.0 + F4SD.ActionConnector + F4SD-ActionConnector + disable + 7.3 + + + + + + + + diff --git a/F4SD-ActionConnector/Models/ActionContext.cs b/F4SD-ActionConnector/Models/ActionContext.cs new file mode 100644 index 0000000..5487625 --- /dev/null +++ b/F4SD-ActionConnector/Models/ActionContext.cs @@ -0,0 +1,12 @@ +using F4SD.ActionConnector.Enumerations; +using F4SD.ActionConnector.Payloads; + +namespace F4SD.ActionConnector.Models +{ + public sealed class ActionContext + { + public TriggerEvent TriggerEvent { get; set; } + public bool AwaitResult { get; set; } + public PayloadBase Payload { get; set; } + } +} diff --git a/F4SD-ActionConnector/Models/ActionDefinitionBase.cs b/F4SD-ActionConnector/Models/ActionDefinitionBase.cs new file mode 100644 index 0000000..48e7f09 --- /dev/null +++ b/F4SD-ActionConnector/Models/ActionDefinitionBase.cs @@ -0,0 +1,38 @@ +using C4IT.XML; +using F4SD.ActionConnector.Enumerations; +using System; +using System.Xml; +using static C4IT.Logging.cLogManager; + +namespace F4SD.ActionConnector.Models +{ + public abstract class ActionDefinitionBase + { + public string Id { get; set; } + public abstract ActionType Type { get; } + public bool AwaitResult { get; set; } + public bool IsEnabled { get; set; } + public string Description { get; set; } + public bool IsValid { get; private protected set; } + + protected ActionDefinitionBase(XmlElement xNode, cXmlParser parser) + { + try + { + Id = cXmlParser.GetStringFromXmlAttribute(xNode, "Id"); + AwaitResult = cXmlParser.GetBoolFromXmlAttribute(xNode, "AwaitResult"); + IsEnabled = cXmlParser.GetBoolFromXmlAttribute(xNode, "IsEnabled"); + + XmlNode descriptionNode = xNode.SelectSingleNode("Description"); + if (descriptionNode != null) + Description = cXmlParser.GetInnerTextFromXmlElement(descriptionNode); + + IsValid = true; + } + catch (Exception ex) + { + LogException(ex); + } + } + } +} diff --git a/F4SD-ActionConnector/Models/ActionResult.cs b/F4SD-ActionConnector/Models/ActionResult.cs new file mode 100644 index 0000000..3b9559d --- /dev/null +++ b/F4SD-ActionConnector/Models/ActionResult.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using F4SD.ActionConnector.Enumerations; + +namespace F4SD.ActionConnector.Models +{ + public sealed class ActionResult + { + public string ActionId { get; set; } + public ActionResultStatus Status { get; set; } + + /// + /// HTTP status code for HttpCall actions; null for other types. + /// + public int? HttpStatusCode { get; set; } + + /// + /// Raw response body (HttpCall) or serialized output (QuickAction). + /// + public string RawResponse { get; set; } + + /// + /// Named fields extracted from the response via jsonPath mappings. + /// Populated when DisplayInCockpit is true. + /// + public IDictionary MappedFields { get; set; } = new Dictionary(); + + /// + /// Human-readable error message; set when Status is Failure. + /// + public string ErrorMessage { get; set; } + + public static ActionResult Ok(string actionId, string rawResponse = null) + => new ActionResult { ActionId = actionId, Status = ActionResultStatus.Success, RawResponse = rawResponse }; + + public static ActionResult Fail(string actionId, string errorMessage) + => new ActionResult { ActionId = actionId, Status = ActionResultStatus.Failure, ErrorMessage = errorMessage }; + + public static ActionResult Skip(string actionId) + => new ActionResult { ActionId = actionId, Status = ActionResultStatus.Skipped }; + } +} diff --git a/F4SD-ActionConnector/Models/ExternalCommunicationConfiguration.cs b/F4SD-ActionConnector/Models/ExternalCommunicationConfiguration.cs new file mode 100644 index 0000000..87d5708 --- /dev/null +++ b/F4SD-ActionConnector/Models/ExternalCommunicationConfiguration.cs @@ -0,0 +1,74 @@ +using C4IT.XML; +using F4SD.ActionConnector.Enumerations; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using System.Xml; +using static C4IT.Logging.cLogManager; + +namespace F4SD.ActionConnector.Models +{ + internal class ExternalCommunicationConfiguration + { + private const string FileNameConfig = "F4SD-ExternalCommunication-Configuration.xml"; + private const string FileNameConfigSchema = "F4SD-ExternalCommunication-Configuration.xsd"; + private const string ConfigRootElement = "F4SD-ExternalCommunication-Configuration"; + + public IDictionary Triggers { get; } = new Dictionary(); + + + private const string TriggersNodeName = "Triggers"; + private const string TriggerNodeName = "Trigger"; + + public void Initiate(XmlElement rootElement, cXmlParser parser) + { + var CM = MethodBase.GetCurrentMethod(); + LogMethodBegin(CM); + try + { + var triggersNode = rootElement.SelectSingleNode(TriggersNodeName); + parser.EnterElement(TriggersNodeName); + + var triggerNodes = triggersNode.SelectNodes(TriggerNodeName); + + if (triggerNodes is null || triggerNodes.Count == 0) + return; + + parser.EnterElement(TriggerNodeName); + + foreach (XmlElement triggerNodeElement in triggerNodes) + { + try + { + Trigger trigger = new Trigger(triggerNodeElement, parser); + + if (trigger is null || !Triggers.ContainsKey(trigger.Event)) + continue; + + Triggers.Add(trigger.Event, trigger); + } + catch (Exception ex) + { + LogException(ex); + } + finally + { + parser.SelectElementNext(); + } + } + + parser.LeaveElement(TriggerNodeName); + } + catch (Exception ex) + { + LogException(ex); + } + finally + { + parser.LeaveElement(TriggersNodeName); + LogMethodEnd(CM); + } + } + } +} diff --git a/F4SD-ActionConnector/Models/HttpCallDefinition.cs b/F4SD-ActionConnector/Models/HttpCallDefinition.cs new file mode 100644 index 0000000..2d0e4d2 --- /dev/null +++ b/F4SD-ActionConnector/Models/HttpCallDefinition.cs @@ -0,0 +1,30 @@ +using C4IT.XML; +using F4SD.ActionConnector.Enumerations; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Xml; + +namespace F4SD.ActionConnector.Models +{ + public sealed class HttpCallDefinition : ActionDefinitionBase + { + public override ActionType Type => ActionType.HttpCall; + + public string Url { get; set; } + public HttpMethod Method { get; set; } = HttpMethod.Post; + public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(30); + public IDictionary Headers { get; set; } = new Dictionary(); + public string Body { get; set; } + + /// + /// jsonPath expressions keyed by field name, used to extract values from the response. + /// + public IDictionary MappedFieldJsonPaths { get; set; } = new Dictionary(); + + internal HttpCallDefinition(XmlElement xNode, cXmlParser parser) : base(xNode, parser) + { + + } + } +} diff --git a/F4SD-ActionConnector/Models/QuickActionDefinition.cs b/F4SD-ActionConnector/Models/QuickActionDefinition.cs new file mode 100644 index 0000000..33df28f --- /dev/null +++ b/F4SD-ActionConnector/Models/QuickActionDefinition.cs @@ -0,0 +1,105 @@ +using C4IT.XML; +using F4SD.ActionConnector.Enumerations; +using System; +using System.Collections.Generic; +using System.Xml; +using static C4IT.Logging.cLogManager; + +namespace F4SD.ActionConnector.Models +{ + public sealed class QuickActionDefinition : ActionDefinitionBase + { + private const string ParametersNodeName = "Parameters"; + private const string ParameterNodeName = "Parameter"; + + public override ActionType Type => ActionType.QuickAction; + + /// + /// Reference to the Quick Action to invoke, e.g. "Protocol case in JIRA". + /// + public string QuickActionRef { get; set; } + + public IDictionary Parameters { get; private set; } = new Dictionary(); + + internal QuickActionDefinition(XmlElement xNode, cXmlParser parser) : base(xNode, parser) + { + try + { + if (!IsValid) return; else IsValid = false; + + XmlNode quickActionRefNode = xNode.SelectSingleNode("QuickActionRef"); + if (quickActionRefNode is null) + return; + + QuickActionRef = cXmlParser.GetInnerTextFromXmlElement(quickActionRefNode); + + if (QuickActionRef is null) + return; + + XmlNode parametersNode = xNode.SelectSingleNode(ParametersNodeName); + if (parametersNode != null && parametersNode is XmlElement paramtersNodeElement) + ParseParametersElement(paramtersNodeElement, parser); + + IsValid = true; + } + catch (Exception ex) + { + LogException(ex); + } + } + + private void ParseParametersElement(XmlElement parametersNodeElement, cXmlParser parser) + { + try + { + parser.EnterElement(ParametersNodeName); + + XmlNodeList parameterNodes = parametersNodeElement.SelectNodes(ParameterNodeName); + + if (parameterNodes?.Count > 0) + parser.EnterElement(ParameterNodeName); + + foreach (XmlElement parameterNode in parameterNodes) + { + ParseParameterElement(parameterNode, parser); + } + + if (parameterNodes?.Count > 0) + parser.LeaveElement(ParameterNodeName); + } + catch (Exception ex) + { + LogException(ex); + } + finally + { + parser.LeaveElement(ParametersNodeName); + } + } + + private void ParseParameterElement(XmlElement parameterNode, cXmlParser parser) + { + try + { + string parameterName = cXmlParser.GetStringFromXmlAttribute(parameterNode, "name"); + string parameterVariable = cXmlParser.GetInnerTextFromXmlElement(parameterNode); + + if (string.IsNullOrEmpty(parameterName) || string.IsNullOrEmpty(parameterVariable)) + return; + + if (Parameters.ContainsKey(parameterName)) + LogEntry($"A paramter with name '{parameterName}' does allready exist.", C4IT.Logging.LogLevels.Warning); + else + Parameters.Add(parameterName, parameterVariable); + } + catch (Exception ex) + { + LogException(ex); + } + finally + { + parser.SelectElementNext(); + } + } + } +} diff --git a/F4SD-ActionConnector/Models/Trigger.cs b/F4SD-ActionConnector/Models/Trigger.cs new file mode 100644 index 0000000..8f54af1 --- /dev/null +++ b/F4SD-ActionConnector/Models/Trigger.cs @@ -0,0 +1,101 @@ +using C4IT.XML; +using F4SD.ActionConnector.Enumerations; +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; +using static C4IT.Logging.cLogManager; + +namespace F4SD.ActionConnector.Models +{ + internal class Trigger + { + public TriggerEvent Event { get; set; } + + public IDictionary Actions { get; } = new Dictionary(); + + public bool IsValid { get; private set; } + + private const string ActionsNodeName = "Actions"; + private const string ActionNodeName = "Action"; + + internal Trigger(XmlElement xNode, cXmlParser parser) + { + try + { + Event = cXmlParser.GetEnumFromAttribute(xNode, "action", TriggerEvent.Unknown); + + XmlNode actionsNode = xNode.SelectSingleNode(ActionsNodeName); + + if (actionsNode is null || !(actionsNode is XmlElement actionsNodeElement)) + return; + + ParseActionsNode(actionsNodeElement, parser); + + IsValid = true; ; + } + catch (Exception ex) + { + LogException(ex); + } + } + + private void ParseActionsNode(XmlElement actionsNode, cXmlParser parser) + { + try + { + parser.EnterElement(ActionsNodeName); + + var actionNodes = actionsNode.SelectNodes(ActionNodeName); + if (actionNodes is null || actionNodes.Count == 0) + return; + + parser.EnterElement(ActionNodeName); + + foreach (XmlElement actionNode in actionNodes) + { + try + { + ActionType type = cXmlParser.GetEnumFromAttribute(actionNode, "type", ActionType.Unknown); + + ActionDefinitionBase actionDefinition = null; + + switch (type) + { + case ActionType.QuickAction: + actionDefinition = new QuickActionDefinition(actionNode, parser); + break; + case ActionType.HttpCall: + break; + case ActionType.Unknown: + case ActionType.Webhook: + default: + break; + } + + if (actionDefinition?.Id != null && !Actions.ContainsKey(actionDefinition.Id)) + Actions.Add(actionDefinition.Id, actionDefinition); + } + catch (Exception ex) + { + LogException(ex); + } + finally + { + parser.SelectElementNext(); + } + } + + parser.LeaveElement(ActionNodeName); + } + catch (Exception ex) + { + LogException(ex); + } + finally + { + parser.LeaveElement(ActionsNodeName); + } + } + } +} diff --git a/F4SD-ActionConnector/Payloads/ApplicationStartupPayload.cs b/F4SD-ActionConnector/Payloads/ApplicationStartupPayload.cs new file mode 100644 index 0000000..a4e4cc6 --- /dev/null +++ b/F4SD-ActionConnector/Payloads/ApplicationStartupPayload.cs @@ -0,0 +1,9 @@ +using F4SD.ActionConnector.Enumerations; + +namespace F4SD.ActionConnector.Payloads +{ + public sealed class ApplicationStartupPayload : PayloadBase + { + public override TriggerEvent Event => TriggerEvent.ApplicationStartup; + } +} diff --git a/F4SD-ActionConnector/Payloads/CaseClosedPayload.cs b/F4SD-ActionConnector/Payloads/CaseClosedPayload.cs new file mode 100644 index 0000000..95c6c07 --- /dev/null +++ b/F4SD-ActionConnector/Payloads/CaseClosedPayload.cs @@ -0,0 +1,17 @@ +using System; +using F4SD.ActionConnector.Enumerations; + +namespace F4SD.ActionConnector.Payloads +{ + public sealed class CaseClosedPayload : PayloadBase + { + public override TriggerEvent Event => TriggerEvent.CaseClosed; + + public string Id { get; set; } + public string Subject { get; set; } + public string Priority { get; set; } + public Guid UserId { get; set; } + public string ClosedByUserName { get; set; } + public DateTime ClosedAt { get; set; } + } +} diff --git a/F4SD-ActionConnector/Payloads/CaseCreatedPayload.cs b/F4SD-ActionConnector/Payloads/CaseCreatedPayload.cs new file mode 100644 index 0000000..fe6b8da --- /dev/null +++ b/F4SD-ActionConnector/Payloads/CaseCreatedPayload.cs @@ -0,0 +1,14 @@ +using System; +using F4SD.ActionConnector.Enumerations; + +namespace F4SD.ActionConnector.Payloads +{ + public sealed class CaseCreatedPayload : PayloadBase + { + public override TriggerEvent Event => TriggerEvent.CaseCreated; + + public string Id { get; set; } + public string UserId { get; set; } + public DateTime CreatedAt { get; set; } + } +} diff --git a/F4SD-ActionConnector/Payloads/PayloadBase.cs b/F4SD-ActionConnector/Payloads/PayloadBase.cs new file mode 100644 index 0000000..202f702 --- /dev/null +++ b/F4SD-ActionConnector/Payloads/PayloadBase.cs @@ -0,0 +1,9 @@ +using F4SD.ActionConnector.Enumerations; + +namespace F4SD.ActionConnector.Payloads +{ + public abstract class PayloadBase + { + public abstract TriggerEvent Event { get; } + } +} diff --git a/F4SD-AdaptableIcon/Enums/InternIcon.cs b/F4SD-AdaptableIcon/Enums/InternIcon.cs index bcc180b..8e00db7 100644 --- a/F4SD-AdaptableIcon/Enums/InternIcon.cs +++ b/F4SD-AdaptableIcon/Enums/InternIcon.cs @@ -59,6 +59,7 @@ namespace F4SD_AdaptableIcon.Enums misc_tool, misc_user, misc_user_disabled, + misc_disabledOverlay, //StatusIcons status_bad, diff --git a/F4SD-AdaptableIcon/IconPainter.cs b/F4SD-AdaptableIcon/IconPainter.cs index 53f4efc..cac0d69 100644 --- a/F4SD-AdaptableIcon/IconPainter.cs +++ b/F4SD-AdaptableIcon/IconPainter.cs @@ -519,6 +519,13 @@ namespace FasdDesktopUi.Basics.UserControls.AdaptableIcon null } }, + { enumInternIcons.misc_disabledOverlay, + new PathGeometry[] + { + PathGeometry.CreateFromGeometry(Geometry.Parse("F1 M7.8133 5.364l-.844-.62L1.1307.4627c-.5307-.4014-1.1307.4013-.5987.8026L6.1253 5.364l.844.62 5.8387 4.2813c.536.396 1.1307-.412.5933-.808 Z")), + null + } + }, #endregion diff --git a/F4SD-Docu-Engine/DocuEngine.cs b/F4SD-Docu-Engine/DocuEngine.cs new file mode 100644 index 0000000..1c12042 --- /dev/null +++ b/F4SD-Docu-Engine/DocuEngine.cs @@ -0,0 +1,41 @@ +using F4SD.DocuEngine.DocuEngineDataProvider; +using F4SD.DocuEngine.DocuEngineParser; +using F4SD.DocuEngine.DocuEngineParser.ContentBlock; +using F4SD.DocuEngine.DocuEngineParser.Exceptions; +using System; +using System.Collections.Generic; +using System.Text; + +namespace F4SD.DocuEngine +{ + public class DocuEngine + { + public string DocuTemplate { get; private set; } + public IDocuEngineDataContainer Container { get; private set; } + private ParsingContext parsingContext; + + public DocuEngine(string docuTemplate, IDocuEngineDataContainer container) + { + DocuTemplate = docuTemplate; + Container = container; + } + + public string ParseTemplate() + { + var index = 0; + parsingContext = new ParsingContext(); + ContentBlock contentBlock = new ContentBlock(DocuTemplate, Container); + try + { + parsingContext.Append(contentBlock.ParseContentBlock(ref index)); + } + catch (DocuEngineParser.Exceptions.DocuEngineParserException parserException) + { + parsingContext.Append(parserException.CurrentText); + parsingContext.Append(parserException.PrintedErrorMessage); + return parsingContext.GetFinalText(); + } + return parsingContext.GetFinalText(); + } + } +} diff --git a/F4SD-Docu-Engine/DocuEngineDataProvider/DocuEngineDataProvider.cs b/F4SD-Docu-Engine/DocuEngineDataProvider/DocuEngineDataProvider.cs new file mode 100644 index 0000000..acffa61 --- /dev/null +++ b/F4SD-Docu-Engine/DocuEngineDataProvider/DocuEngineDataProvider.cs @@ -0,0 +1,93 @@ +using C4IT.F4SD.DisplayFormatting; +using System.Collections.Generic; +using System.Linq; + +namespace F4SD.DocuEngine.DocuEngineDataProvider +{ + public class DocuEngineDataProperty : IDocuEngineDataProperty + { + public object Value { get; set; } + public RawValueType? ValueType { get; set; } + } + + public class DocuEngineDataContainer : Dictionary, IDocuEngineDataContainer + { + public IDocuEngineDataToken GetByName(string tokenName) + { + var tokenTree = tokenName.Split('.'); + if (this.TryGetValue(tokenTree[0], out IDocuEngineDataToken token)) + { + if (tokenTree.Length > 1) + { + switch (token) + { + case IDocuEngineDataContainer container: + return container.GetByName(string.Join(".", tokenTree.Skip(1))); + case IDocuEngineDataProperty property: + return property; + case IDocuEngineDataEnumeration enumeration: + return new DocuEngineDataProperty() { Value = tokenTree[0] }; + default: + //Syntax Error + break; + } + } + else + { + switch (token) + { + case IDocuEngineDataContainer container: + return container; + case IDocuEngineDataProperty property: + return property; + case IDocuEngineDataEnumeration enumeration: + return enumeration; + default: + //Syntax Error + break; + } + } + return null; + } + else + { + //Syntax Error + return null; + } + } + + public IDocuEngineDataContainer GetContainerByName(string containerName) + { + var token = GetByName(containerName); + if (token is IDocuEngineDataContainer container) + { + return container; + } + return null; + } + + public IDocuEngineDataProperty GetPropertyByName(string propertyName) + { + var token = GetByName(propertyName); + if (token is IDocuEngineDataProperty property) + { + return property; + } + return null; + } + + public IDocuEngineDataEnumeration GetEnumerationByName(string enumName) + { + var token = GetByName(enumName); + if (token is IDocuEngineDataEnumeration enumeration) + { + return enumeration; + } + return null; + } + } + + public class DocuEngineDataEnumeration : List, IDocuEngineDataEnumeration + { + } +} \ No newline at end of file diff --git a/F4SD-Docu-Engine/DocuEngineDataProvider/IDocuEngineDataToken.cs b/F4SD-Docu-Engine/DocuEngineDataProvider/IDocuEngineDataToken.cs new file mode 100644 index 0000000..3d8dae8 --- /dev/null +++ b/F4SD-Docu-Engine/DocuEngineDataProvider/IDocuEngineDataToken.cs @@ -0,0 +1,31 @@ +using C4IT.F4SD.DisplayFormatting; +using System.Collections.Generic; + +namespace F4SD.DocuEngine.DocuEngineDataProvider +{ + public interface IDocuEngineDataToken + { + } + + public interface IDocuEngineDataIllegal : IDocuEngineDataToken + { + } + + public interface IDocuEngineDataProperty : IDocuEngineDataToken + { + object Value { get; set; } + RawValueType? ValueType { get; set; } + } + + public interface IDocuEngineDataContainer : IDocuEngineDataToken + { + IDocuEngineDataToken GetByName(string name); + IDocuEngineDataContainer GetContainerByName(string containerName); + IDocuEngineDataEnumeration GetEnumerationByName(string enumName); + IDocuEngineDataProperty GetPropertyByName(string propertyName); + } + + public interface IDocuEngineDataEnumeration : IEnumerable, IDocuEngineDataToken + { + } +} \ No newline at end of file diff --git a/F4SD-Docu-Engine/DocuEngineParser/ContentBlock/ContentBlock.cs b/F4SD-Docu-Engine/DocuEngineParser/ContentBlock/ContentBlock.cs new file mode 100644 index 0000000..96bfbc5 --- /dev/null +++ b/F4SD-Docu-Engine/DocuEngineParser/ContentBlock/ContentBlock.cs @@ -0,0 +1,227 @@ +using C4IT.F4SD.DisplayFormatting; +using F4SD.DocuEngine.DocuEngineDataProvider; +using F4SD.DocuEngine.DocuEngineParser.Exceptions; +using System; +using System.Linq; +using System.Text.RegularExpressions; + +namespace F4SD.DocuEngine.DocuEngineParser.ContentBlock +{ + internal partial class ContentBlock + { + public string DocuTemplate { get; private set; } + public IDocuEngineDataContainer Container { get; private set; } + private ParsingContext parsingContext; + public ContentBlock(string DocuTemplate, IDocuEngineDataContainer Container) + { + this.DocuTemplate = DocuTemplate; + this.Container = Container; + parsingContext = new ParsingContext(); + } + + public string ParseContentBlock(ref int index) + { + bool isContentBlockFinished = false; + try + { + while (!isContentBlockFinished) + { + ParseTextBlock(ref index, out var finished); + if (finished) + { + isContentBlockFinished = true; + } + else + { + ParseCommandBlock(ref index); + } + } + } + catch (DocuEngineParserException parserException) + { + parsingContext.Append(parserException.CurrentText); + parserException.AddToCurrentText(parsingContext.GetFinalText()); + throw; + } + return parsingContext.GetFinalText(); + } + + private void ParseTextBlock(ref int index, out bool isContextBlockFinished) + { + bool foundTrigger = false; + string[] keywords = { "@@", "[[", "]]", "{{", "}}" }; + string pattern = string.Join("|", keywords.Select(Regex.Escape)); + Regex regex = new Regex(pattern); + isContextBlockFinished = false; + + while (!foundTrigger) + { + Match match = regex.Match(DocuTemplate, index); + if (match.Success) + { + parsingContext.Append(DocuTemplate.Substring(index, match.Index - index)); + index = match.Index; + switch (match.Value) + { + case "@@": + if (DocuTemplate.Substring(index, 3).Equals("@@@")) + { + parsingContext.Append("@@"); + index += 3; + } + else + { + foundTrigger = true; + index += 2; + } + break; + case "[[": + if (DocuTemplate.Substring(index, 3).Equals("[[[")) + { + parsingContext.Append("[["); + index += 3; + } + else + { + index += 2; + SkipWhiteSpace(ref index); + string variableName = ParseVariableName(ref index); + SkipWhiteSpace(ref index); + if (DocuTemplate.ElementAtOrDefault(index).Equals(';')) + { + index++; + SkipWhiteSpace(ref index); + string displayTypeString = ParseDisplayType(ref index); + RawValueType? displayType = null; + if (Enum.TryParse(displayTypeString, true, out RawValueType parseDisplayType)) + { + displayType = parseDisplayType; + } + parsingContext.Append(SubstituteVariable(variableName, displayType)); + SkipWhiteSpace(ref index); + if (DocuTemplate.Substring(index, 2).Equals("]]") && !(DocuTemplate.Substring(index, 3).Equals("]]]"))) + { + index += 2; + continue; + } + else + { + throw new SyntaxErrorException("]]", DocuTemplate.Substring(index, 3)); + } + } + else if (DocuTemplate.Substring(index, 2).Equals("]]")) + { + parsingContext.Append(SubstituteVariable(variableName, null)); + index += 2; + continue; + } + else + { + throw new SyntaxErrorException("]]\" / \";", DocuTemplate.Substring(index, 3)); + } + } + break; + case "]]": + if (DocuTemplate.Substring(index, 3).Equals("]]]")) + { + parsingContext.Append("]]"); + index += 3; + } + else + { + throw new SyntaxErrorException("]]]", "]]"); + } + break; + case "{{": + if (DocuTemplate.Substring(index, 3).Equals("{{{")) + { + parsingContext.Append("{{"); + index += 3; + } + else + { + throw new SyntaxErrorException("{{{", "{{"); + } + break; + case "}}": + if (DocuTemplate.Substring(index, 3).Equals("}}}")) + { + parsingContext.Append("}}"); + index += 3; + } + else if (DocuTemplate.Substring(index, 3).Equals("}};")) + { + foundTrigger = true; + isContextBlockFinished = true; + index += 3; + } + else + { + foundTrigger = true; + isContextBlockFinished = true; + index += 2; + } + break; + } + } + else + { + int length = DocuTemplate.Length - index; + if (length < 0) + { + length = 0; + } + else if (length > 15) + { + length = 15; + } + throw new SyntaxErrorException("}};", DocuTemplate.Substring(index, length)); + } + } + } + + private void ParseCommandBlock(ref int index) + { + string[] keywords = { "(" }; + string pattern = string.Join("|", keywords.Select(Regex.Escape)); + Regex regex = new Regex(pattern); + + Match match = regex.Match(DocuTemplate, index); + if (match.Success) + { + string commandName = ""; + commandName = DocuTemplate.Substring(index, match.Index - index); + index = match.Index + 1; + + switch (commandName) + { + case "if": + ParseIfCommand(ref index); + break; + case "switch": + ParseSwitchCommand(ref index); + break; + case "foreach": + ParseForeachCommand(ref index); + break; + case "include": + //TODO + break; + } + } + else + { + int length = DocuTemplate.Length - index; + if (length < 0) + { + length = 0; + } + else if (length > 15) + { + length = 15; + } + throw new SyntaxErrorException("if\" / \"switch\" / \"foreach", DocuTemplate.Substring(index, length)); + } + } + } +} diff --git a/F4SD-Docu-Engine/DocuEngineParser/ContentBlock/ContentBlockForeachCommand.cs b/F4SD-Docu-Engine/DocuEngineParser/ContentBlock/ContentBlockForeachCommand.cs new file mode 100644 index 0000000..51a6eda --- /dev/null +++ b/F4SD-Docu-Engine/DocuEngineParser/ContentBlock/ContentBlockForeachCommand.cs @@ -0,0 +1,52 @@ +using F4SD.DocuEngine.DocuEngineDataProvider; +using F4SD.DocuEngine.DocuEngineParser.Exceptions; +using System.ComponentModel; +using System.Linq; + +namespace F4SD.DocuEngine.DocuEngineParser.ContentBlock +{ + internal partial class ContentBlock + { + private void ParseForeachCommand(ref int index) + { + string variableName = ""; + string enumName = ""; + int tempIndex = 0; + + SkipWhiteSpace(ref index); + variableName += ParseVariableName(ref index); + SkipWhiteSpace(ref index); + if (!(DocuTemplate.ElementAtOrDefault(index).Equals(';'))) + { + throw new SyntaxErrorException(";", DocuTemplate.ElementAtOrDefault(index).ToString()); + } + index++; + SkipWhiteSpace(ref index); + enumName += ParseVariableName(ref index); + SkipWhiteSpace(ref index); + if (!(DocuTemplate.ElementAtOrDefault(index).Equals(')'))) + { + throw new SyntaxErrorException(")", DocuTemplate.ElementAtOrDefault(index).ToString()); + } + index++; + SkipWhiteSpace(ref index); + if (!DocuTemplate.Substring(index, 2).Equals("{{")) + { + throw new SyntaxErrorException("{{", DocuTemplate.Substring(index, 2)); + } + index += 2; + IDocuEngineDataEnumeration enumeration = Container.GetEnumerationByName(enumName); + if (enumeration == null) + { + //Syntax Error + } + tempIndex = index; + foreach (var token in enumeration) + { + index = tempIndex; + ContentBlock foreachBlock = new ContentBlock(DocuTemplate, new DocuEngineDataContainer() { { variableName, token } }); + parsingContext.Append(foreachBlock.ParseContentBlock(ref index)); + } + } + } +} diff --git a/F4SD-Docu-Engine/DocuEngineParser/ContentBlock/ContentBlockHelperMethods.cs b/F4SD-Docu-Engine/DocuEngineParser/ContentBlock/ContentBlockHelperMethods.cs new file mode 100644 index 0000000..e2ec143 --- /dev/null +++ b/F4SD-Docu-Engine/DocuEngineParser/ContentBlock/ContentBlockHelperMethods.cs @@ -0,0 +1,291 @@ +using C4IT.F4SD.DisplayFormatting; +using F4SD.DocuEngine.DocuEngineParser.Exceptions; +using System.ComponentModel; +using System.Linq; +using System.Text.RegularExpressions; + +namespace F4SD.DocuEngine.DocuEngineParser.ContentBlock +{ + internal partial class ContentBlock + { + private string ParseVariableType(ref int index) + { + bool foundTypeEnd = false; + string variableType = ""; + while (!foundTypeEnd) + { + char c = DocuTemplate.ElementAt(index); + if (char.IsLetter(c)) + { + variableType += c; + index++; + } + else + { + foundTypeEnd = true; + } + } + return variableType; + } + + private string ParseVariableName(ref int index) + { + bool foundVariableEnd = false; + string variableName = ""; + while (!foundVariableEnd) + { + char c = DocuTemplate.ElementAt(index); + if (char.IsLetterOrDigit(c) || c == '.') + { + variableName += c; + index++; + } + else + { + foundVariableEnd = true; + } + } + return variableName; + } + + private void SkipWhiteSpace(ref int index) + { + bool foundNonWhiteSpace = false; + while (!foundNonWhiteSpace) + { + char c = DocuTemplate.ElementAt(index); + if (char.IsWhiteSpace(c)) + { + index++; + } + else + { + foundNonWhiteSpace = true; + } + } + } + + private string ParseDisplayType(ref int index) + { + bool foundDisplayTypeEnd = false; + string displayType = ""; + while (!foundDisplayTypeEnd) + { + char c = DocuTemplate.ElementAt(index); + if (char.IsLetterOrDigit(c)) + { + displayType += c; + index++; + } + else + { + foundDisplayTypeEnd = true; + } + } + return displayType; + } + + private string SubstituteVariable(string variableName, RawValueType? displayType) + { + IRawValueFormatter rawValueFormatter = new RawValueFormatter(); + + var property = Container.GetPropertyByName(variableName); + if (property == null) + { + //Syntax Error + } + if (displayType != null) + { + return rawValueFormatter.GetDisplayValue(property.Value, (RawValueType)displayType, null); + } + else if (property.ValueType != null) + { + return rawValueFormatter.GetDisplayValue(property.Value, (RawValueType)property.ValueType, null); + } + else + { + return property.Value.ToString(); + } + } + + private char ParseComparator(ref int index) + { + char comparator = DocuTemplate.ElementAtOrDefault(index); + if (!(comparator == '<' || comparator == '>' || comparator == '=' || comparator == '!')) + { + throw new SyntaxErrorException("<\" / \">\" / \"=\" / \"!", comparator.ToString()); + } + index++; + return comparator; + } + + private string ParseValue(ref int index) + { + string value = ""; + string pattern = @"("")(.*?)(""\s*\))"; + Regex regex = new Regex(pattern); + Match match = regex.Match(DocuTemplate, index); + if (match.Success) + { + value = match.Groups[2].Value; + string wholeMatch = match.Value.ToString(); + index += wholeMatch.Length; + } + else + { + throw new SyntaxErrorException("\"VariableName\")", ""); + } + return value; + } + + private bool CheckCondition(string variableType, string variableName, char comparator, string value) + { + string stringVariable = ""; + var property = Container.GetPropertyByName(variableName); + if (property == null) + { + //Syntax Error + } + stringVariable = property.Value.ToString(); + switch (variableType) + { + case "string": + if (!(comparator == '=' || comparator == '!')) + { + throw new SyntaxErrorException("=\" / \"!", comparator.ToString()); + } + return CompareStrings(stringVariable, comparator, value); + case "int": + if (!(comparator == '<' || comparator == '>' || comparator == '=' || comparator == '!')) + { + throw new SyntaxErrorException("<\" / \">\" / \"=\" / \"!", comparator.ToString()); + } + if (!(int.TryParse(value, out var intValue))) + { + throw new VariableErrorException(value, "int"); + } + stringVariable = stringVariable.Replace("\"", ""); + if (!(int.TryParse(stringVariable, out var intVariable))) + { + throw new VariableErrorException(stringVariable, "int"); + } + return CompareIntegers(intVariable, comparator, intValue); + case "bool": + if (!(comparator == '=' || comparator == '!')) + { + throw new SyntaxErrorException("=\" / \"!", comparator.ToString()); + } + if (!(bool.TryParse(value, out var boolValue))) + { + throw new VariableErrorException(value, "bool"); + } + if (!(bool.TryParse(stringVariable, out var boolVariable))) + { + throw new VariableErrorException(stringVariable, "bool"); + } + return CompareBooleans(boolVariable, comparator, boolValue); + default: + throw new SyntaxErrorException("string\" / \"int\" / \"bool", variableType); + } + } + + private bool CompareStrings(string variable, char comparator, string value) + { + switch (comparator) + { + case '=': + if (variable == value) + { + return true; + } + else + { + return false; + } + case '!': + if (variable != value) + { + return true; + } + else + { + return false; + } + default: + return false; + } + } + + private bool CompareIntegers(int variable, char comparator, int value) + { + switch (comparator) + { + case '=': + if (variable == value) + { + return true; + } + else + { + return false; + } + case '!': + if (variable != value) + { + return true; + } + else + { + return false; + } + case '>': + if (variable > value) + { + return true; + } + else + { + return false; + } + case '<': + if (variable < value) + { + return true; + } + else + { + return false; + } + default: + return false; + } + } + + private bool CompareBooleans(bool variable, char comparator, bool value) + { + switch (comparator) + { + case '=': + if (variable == value) + { + return true; + } + else + { + return false; + } + case '!': + if (variable != value) + { + return true; + } + else + { + return false; + } + default: + return false; + } + } + } +} diff --git a/F4SD-Docu-Engine/DocuEngineParser/ContentBlock/ContentBlockIfCommand.cs b/F4SD-Docu-Engine/DocuEngineParser/ContentBlock/ContentBlockIfCommand.cs new file mode 100644 index 0000000..6fa459e --- /dev/null +++ b/F4SD-Docu-Engine/DocuEngineParser/ContentBlock/ContentBlockIfCommand.cs @@ -0,0 +1,175 @@ +using F4SD.DocuEngine.DocuEngineParser.Exceptions; +using System.ComponentModel; +using System.Linq; + +namespace F4SD.DocuEngine.DocuEngineParser.ContentBlock +{ + internal partial class ContentBlock + { + private void ParseIfCommand(ref int index) + { + string tempString = ""; + bool foundTrueCondition = false; + bool foundAllElseIf = false; + int tempIndex = 0; + + tempString = ParseIfBlock(ref index, out var isIfConditionTrue); + if (isIfConditionTrue) + { + foundTrueCondition = true; + parsingContext.Append(tempString); + } + tempString = ""; + while (!foundAllElseIf) + { + tempIndex = index; + SkipWhiteSpace(ref index); + if (DocuTemplate.Substring(index, 7).Equals("else-if")) + { + index += 7; + tempString += ParseElseIfBlock(ref index, out var isElseIfConditionTrue); + if (isElseIfConditionTrue && !foundTrueCondition) + { + foundTrueCondition = true; + parsingContext.Append(tempString); + } + tempString = ""; + } + else + { + foundAllElseIf = true; + index = tempIndex; + } + } + tempIndex = index; + SkipWhiteSpace(ref index); + if (DocuTemplate.Substring(index, 4).Equals("else")) + { + index += 4; + tempString += ParseElseBlock(ref index); + if (!foundTrueCondition) + { + foundTrueCondition = true; + parsingContext.Append(tempString); + tempString = ""; + } + } + else + { + index = tempIndex; + } + } + + private string ParseIfBlock(ref int index, out bool isConditionTrue) + { + string returnString = ""; + string variableType = ""; + string variableName = ""; + char comparator; + string value = ""; + SkipWhiteSpace(ref index); + variableType += ParseVariableType(ref index); + SkipWhiteSpace(ref index); + if (!(DocuTemplate.ElementAtOrDefault(index).Equals(';'))) + { + throw new SyntaxErrorException(";", DocuTemplate.ElementAtOrDefault(index).ToString()); + } + index++; + SkipWhiteSpace(ref index); + variableName += ParseVariableName(ref index); + SkipWhiteSpace(ref index); + if (!(DocuTemplate.ElementAtOrDefault(index).Equals(';'))) + { + throw new SyntaxErrorException(";", DocuTemplate.ElementAtOrDefault(index).ToString()); + } + index++; + SkipWhiteSpace(ref index); + comparator = ParseComparator(ref index); + SkipWhiteSpace(ref index); + if (!(DocuTemplate.ElementAtOrDefault(index).Equals(';'))) + { + throw new SyntaxErrorException(";", DocuTemplate.ElementAtOrDefault(index).ToString()); + } + index++; + SkipWhiteSpace(ref index); + value += ParseValue(ref index); + SkipWhiteSpace(ref index); + isConditionTrue = CheckCondition(variableType, variableName, comparator, value); + SkipWhiteSpace(ref index); + if (!DocuTemplate.Substring(index, 2).Equals("{{")) + { + throw new SyntaxErrorException("{{", DocuTemplate.Substring(index, 2)); + } + index += 2; + ContentBlock contentBlock = new ContentBlock(DocuTemplate, Container); + returnString += contentBlock.ParseContentBlock(ref index); + return returnString; + } + + private string ParseElseIfBlock(ref int index, out bool isConditionTrue) + { + string returnString = ""; + string variableType = ""; + string variableName = ""; + char comparator; + string value = ""; + SkipWhiteSpace(ref index); + if (!(DocuTemplate.ElementAt(index).Equals('('))) + { + throw new SyntaxErrorException("(", DocuTemplate.ElementAtOrDefault(index).ToString()); + } + index++; + SkipWhiteSpace(ref index); + variableType += ParseVariableType(ref index); + SkipWhiteSpace(ref index); + if (!(DocuTemplate.ElementAtOrDefault(index).Equals(';'))) + { + throw new SyntaxErrorException(";", DocuTemplate.ElementAtOrDefault(index).ToString()); + } + index++; + SkipWhiteSpace(ref index); + variableName += ParseVariableName(ref index); + SkipWhiteSpace(ref index); + if (!(DocuTemplate.ElementAtOrDefault(index).Equals(';'))) + { + throw new SyntaxErrorException(";", DocuTemplate.ElementAtOrDefault(index).ToString()); + } + index++; + SkipWhiteSpace(ref index); + comparator = ParseComparator(ref index); + SkipWhiteSpace(ref index); + if (!(DocuTemplate.ElementAtOrDefault(index).Equals(';'))) + { + throw new SyntaxErrorException(";", DocuTemplate.ElementAtOrDefault(index).ToString()); + } + index++; + SkipWhiteSpace(ref index); + value += ParseValue(ref index); + SkipWhiteSpace(ref index); + isConditionTrue = CheckCondition(variableType, variableName, comparator, value); + SkipWhiteSpace(ref index); + if (!DocuTemplate.Substring(index, 2).Equals("{{")) + { + throw new SyntaxErrorException("{{", DocuTemplate.Substring(index, 2)); + } + index += 2; + ContentBlock contentBlock = new ContentBlock(DocuTemplate, Container); + returnString += contentBlock.ParseContentBlock(ref index); + return returnString; + } + + private string ParseElseBlock(ref int index) + { + string returnString = ""; + SkipWhiteSpace(ref index); + if (!DocuTemplate.Substring(index, 2).Equals("{{")) + { + throw new SyntaxErrorException("{{", DocuTemplate.Substring(index, 2)); + } + index += 2; + ContentBlock contentBlock = new ContentBlock(DocuTemplate, Container); + returnString += contentBlock.ParseContentBlock(ref index); + return returnString; + } + } +} diff --git a/F4SD-Docu-Engine/DocuEngineParser/ContentBlock/ContentBlockSwitchCommand.cs b/F4SD-Docu-Engine/DocuEngineParser/ContentBlock/ContentBlockSwitchCommand.cs new file mode 100644 index 0000000..ec4b07b --- /dev/null +++ b/F4SD-Docu-Engine/DocuEngineParser/ContentBlock/ContentBlockSwitchCommand.cs @@ -0,0 +1,127 @@ +using F4SD.DocuEngine.DocuEngineParser.Exceptions; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; + +namespace F4SD.DocuEngine.DocuEngineParser.ContentBlock +{ + internal partial class ContentBlock + { + private void ParseSwitchCommand(ref int index) + { + string tempString = ""; + bool foundTrueCase = false; + bool foundAllCases = false; + int tempIndex = 0; + (string VariableType, string VariableName) switchVariable = ParseSwitchBlock(ref index); + while (!foundAllCases) + { + tempIndex = index; + SkipWhiteSpace(ref index); + if (DocuTemplate.Substring(index, 4).Equals("case")) + { + index += 4; + tempString += ParseCaseBlock(ref index, switchVariable, out var isCaseTrue); + if (isCaseTrue && !foundTrueCase) + { + foundTrueCase = true; + parsingContext.Append(tempString); + } + tempString = ""; + } + else + { + foundAllCases = true; + index = tempIndex; + } + } + tempIndex = index; + SkipWhiteSpace(ref index); + if (DocuTemplate.Substring(index, 7).Equals("default")) + { + index += 7; + tempString += ParseDefaultBlock(ref index); + if (!foundTrueCase) + { + foundTrueCase = true; + parsingContext.Append(tempString); + tempString = ""; + } + } + else + { + index = tempIndex; + } + } + + private (string VariableType, string VariableName) ParseSwitchBlock(ref int index) + { + string variableName = ""; + string variableType = ""; + SkipWhiteSpace(ref index); + variableType = ParseVariableType(ref index); + SkipWhiteSpace(ref index); + if (!(DocuTemplate.ElementAtOrDefault(index).Equals(';'))) + { + throw new SyntaxErrorException(";", DocuTemplate.ElementAtOrDefault(index).ToString()); + } + index++; + SkipWhiteSpace(ref index); + variableName = ParseVariableName(ref index); + SkipWhiteSpace(ref index); + if (!(DocuTemplate.ElementAtOrDefault(index).Equals(')'))) + { + throw new SyntaxErrorException(")", DocuTemplate.ElementAtOrDefault(index).ToString()); + } + index++; + SkipWhiteSpace(ref index); + if (!DocuTemplate.Substring(index, 2).Equals("{{")) + { + throw new SyntaxErrorException("{{", DocuTemplate.Substring(index, 2)); + } + index += 2; + return (variableType, variableName); + } + + private string ParseCaseBlock(ref int index, (string VariableType, string VariableName) switchVariable, out bool isCaseTrue) + { + string returnString = ""; + isCaseTrue = false; + string value = ""; + SkipWhiteSpace(ref index); + if (!(DocuTemplate.ElementAtOrDefault(index).Equals('('))) + { + throw new SyntaxErrorException("(", DocuTemplate.ElementAtOrDefault(index).ToString()); + } + index++; + SkipWhiteSpace(ref index); + value = ParseValue(ref index); + isCaseTrue = CheckCondition(switchVariable.VariableType, switchVariable.VariableName, '=', value); + SkipWhiteSpace(ref index); + if (!DocuTemplate.Substring(index, 2).Equals("{{")) + { + throw new SyntaxErrorException("{{", DocuTemplate.Substring(index, 2)); + } + index += 2; + ContentBlock contentBlock = new ContentBlock(DocuTemplate, Container); + returnString += contentBlock.ParseContentBlock(ref index); + return returnString; + } + + private string ParseDefaultBlock(ref int index) + { + string returnString = ""; + SkipWhiteSpace(ref index); + if (!DocuTemplate.Substring(index, 2).Equals("{{")) + { + throw new SyntaxErrorException("{{", DocuTemplate.Substring(index, 2)); + } + index += 2; + ContentBlock contentBlock = new ContentBlock(DocuTemplate, Container); + returnString += contentBlock.ParseContentBlock(ref index); + return returnString; + } + } +} diff --git a/F4SD-Docu-Engine/DocuEngineParser/DocuEngineParserException.cs b/F4SD-Docu-Engine/DocuEngineParser/DocuEngineParserException.cs new file mode 100644 index 0000000..3c294a8 --- /dev/null +++ b/F4SD-Docu-Engine/DocuEngineParser/DocuEngineParserException.cs @@ -0,0 +1,15 @@ +using System; + +namespace F4SD.DocuEngine.DocuEngineParser +{ + internal abstract class DocuEngineParserException : Exception + { + public string CurrentText { get; protected set; } + public string PrintedErrorMessage { get; protected set; } + public DocuEngineParserException(string message) : base(message) + { + + } + public abstract void AddToCurrentText(string text); + } +} \ No newline at end of file diff --git a/F4SD-Docu-Engine/DocuEngineParser/Exceptions/DocuEngineParserException.cs b/F4SD-Docu-Engine/DocuEngineParser/Exceptions/DocuEngineParserException.cs new file mode 100644 index 0000000..f7068d0 --- /dev/null +++ b/F4SD-Docu-Engine/DocuEngineParser/Exceptions/DocuEngineParserException.cs @@ -0,0 +1,15 @@ +using System; + +namespace F4SD.DocuEngine.DocuEngineParser.Exceptions +{ + internal abstract class DocuEngineParserException : Exception + { + public string CurrentText { get; protected set; } + public string PrintedErrorMessage { get; protected set; } + public DocuEngineParserException(string message) : base(message) + { + + } + public abstract void AddToCurrentText(string text); + } +} \ No newline at end of file diff --git a/F4SD-Docu-Engine/DocuEngineParser/Exceptions/SyntaxErrorException.cs b/F4SD-Docu-Engine/DocuEngineParser/Exceptions/SyntaxErrorException.cs new file mode 100644 index 0000000..b1d740c --- /dev/null +++ b/F4SD-Docu-Engine/DocuEngineParser/Exceptions/SyntaxErrorException.cs @@ -0,0 +1,26 @@ +namespace F4SD.DocuEngine.DocuEngineParser.Exceptions +{ + internal class SyntaxErrorException : DocuEngineParserException + { + private const string errorMessageTemplate = "<>"; + private const string defaultMessage = "You're syntax is wrong. Get good."; + + public SyntaxErrorException(string currentText) : base(defaultMessage) + { + CurrentText = currentText; + } + public SyntaxErrorException(string expected, string actual) : base(defaultMessage) + { + PrintedErrorMessage = string.Format(errorMessageTemplate, expected, actual); + } + public SyntaxErrorException(string currentText, string expected, string actual) : base(defaultMessage) + { + CurrentText = currentText; + PrintedErrorMessage = string.Format(errorMessageTemplate, expected, actual); + } + public override void AddToCurrentText(string text) + { + CurrentText = text + CurrentText; + } + } +} \ No newline at end of file diff --git a/F4SD-Docu-Engine/DocuEngineParser/Exceptions/VariableErrorException.cs b/F4SD-Docu-Engine/DocuEngineParser/Exceptions/VariableErrorException.cs new file mode 100644 index 0000000..840723a --- /dev/null +++ b/F4SD-Docu-Engine/DocuEngineParser/Exceptions/VariableErrorException.cs @@ -0,0 +1,25 @@ +namespace F4SD.DocuEngine.DocuEngineParser.Exceptions +{ + internal class VariableErrorException : DocuEngineParserException + { + private const string errorMessageTemplate = "<>"; + private const string defaultMessage = "Your variable is the wrong type. Get good."; + public VariableErrorException(string currentText) : base(defaultMessage) + { + CurrentText = currentText; + } + public VariableErrorException(string expected, string actual) : base(defaultMessage) + { + PrintedErrorMessage = string.Format(errorMessageTemplate, expected, actual); + } + public VariableErrorException(string currentText, string expected, string actual) : base(defaultMessage) + { + CurrentText = currentText; + PrintedErrorMessage = string.Format(errorMessageTemplate, expected, actual); + } + public override void AddToCurrentText(string text) + { + CurrentText = text + CurrentText; + } + } +} \ No newline at end of file diff --git a/F4SD-Docu-Engine/DocuEngineParser/ParsingContext.cs b/F4SD-Docu-Engine/DocuEngineParser/ParsingContext.cs new file mode 100644 index 0000000..d02ab7f --- /dev/null +++ b/F4SD-Docu-Engine/DocuEngineParser/ParsingContext.cs @@ -0,0 +1,13 @@ +using System.Text; + +namespace F4SD.DocuEngine.DocuEngineParser +{ + internal class ParsingContext + { + private readonly StringBuilder _stringBuilder = new StringBuilder(); + + public void Append(string text) => _stringBuilder.Append(text); + + public string GetFinalText() => _stringBuilder.ToString(); + } +} diff --git a/F4SD-Docu-Engine/DocuEngineParser/SyntaxErrorException.cs b/F4SD-Docu-Engine/DocuEngineParser/SyntaxErrorException.cs new file mode 100644 index 0000000..d6ac138 --- /dev/null +++ b/F4SD-Docu-Engine/DocuEngineParser/SyntaxErrorException.cs @@ -0,0 +1,26 @@ +namespace F4SD.DocuEngine.DocuEngineParser +{ + internal class SyntaxErrorException : DocuEngineParserException + { + private const string errorMessageTemplate = "<>"; + private const string defaultMessage = "You're syntax is wrong. Get good."; + + public SyntaxErrorException(string currentText) : base(defaultMessage) + { + CurrentText = currentText; + } + public SyntaxErrorException(string expected, string actual) : base(defaultMessage) + { + PrintedErrorMessage = string.Format(errorMessageTemplate, expected, actual); + } + public SyntaxErrorException(string currentText, string expected, string actual) : base(defaultMessage) + { + CurrentText = currentText; + PrintedErrorMessage = string.Format(errorMessageTemplate, expected, actual); + } + public override void AddToCurrentText(string text) + { + CurrentText = text + CurrentText; + } + } +} \ No newline at end of file diff --git a/F4SD-Docu-Engine/DocuEngineParser/VariableErrorException.cs b/F4SD-Docu-Engine/DocuEngineParser/VariableErrorException.cs new file mode 100644 index 0000000..2e1a403 --- /dev/null +++ b/F4SD-Docu-Engine/DocuEngineParser/VariableErrorException.cs @@ -0,0 +1,25 @@ +namespace F4SD.DocuEngine.DocuEngineParser +{ + internal class VariableErrorException : DocuEngineParserException + { + private const string errorMessageTemplate = "<>"; + private const string defaultMessage = "Your variable is the wrong type. Get good."; + public VariableErrorException(string currentText) : base(defaultMessage) + { + CurrentText = currentText; + } + public VariableErrorException(string expected, string actual) : base(defaultMessage) + { + PrintedErrorMessage = string.Format(errorMessageTemplate, expected, actual); + } + public VariableErrorException(string currentText, string expected, string actual) : base(defaultMessage) + { + CurrentText = currentText; + PrintedErrorMessage = string.Format(errorMessageTemplate, expected, actual); + } + public override void AddToCurrentText(string text) + { + CurrentText = text + CurrentText; + } + } +} \ No newline at end of file diff --git a/F4SD-Docu-Engine/F4SD-Docu-Engine.csproj b/F4SD-Docu-Engine/F4SD-Docu-Engine.csproj new file mode 100644 index 0000000..f4ab581 --- /dev/null +++ b/F4SD-Docu-Engine/F4SD-Docu-Engine.csproj @@ -0,0 +1,12 @@ + + + + netstandard2.0 + F4SD.DocuEngine + + + + + + + diff --git a/F4SD-Gamification/CockpitAction.cs b/F4SD-Gamification/CockpitAction.cs new file mode 100644 index 0000000..0103274 --- /dev/null +++ b/F4SD-Gamification/CockpitAction.cs @@ -0,0 +1,13 @@ +namespace F4SD.Gamification +{ + public enum CockpitAction + { + CaseOpened, + CaseClosed, + QuickActionExecuted, + CopyTemplateClicked, + BuiltDirectConnection, + AddNotes, + StartRemoteConnection, + } +} diff --git a/F4SD-Gamification/F4SD-Gamification.csproj b/F4SD-Gamification/F4SD-Gamification.csproj new file mode 100644 index 0000000..8641537 --- /dev/null +++ b/F4SD-Gamification/F4SD-Gamification.csproj @@ -0,0 +1,12 @@ + + + + netstandard2.0 + F4SD.Gamification + + + + 9.0 + + + diff --git a/F4SD-Gamification/LevelEventArgs.cs b/F4SD-Gamification/LevelEventArgs.cs new file mode 100644 index 0000000..c54fbe3 --- /dev/null +++ b/F4SD-Gamification/LevelEventArgs.cs @@ -0,0 +1,10 @@ +namespace F4SD.Gamification +{ + public class LevelEventArgs + { + public int CurrentLevel { get; set; } + public string LevelTitle { get; set; } + public int CurrentXp { get; set; } + public int XpRequiredForLevelUp { get; set; } + } +} diff --git a/F4SD-Gamification/Services/GamificationService.cs b/F4SD-Gamification/Services/GamificationService.cs new file mode 100644 index 0000000..46692d2 --- /dev/null +++ b/F4SD-Gamification/Services/GamificationService.cs @@ -0,0 +1,25 @@ +using System; + +namespace F4SD.Gamification.Services +{ + public static class GamificationService + { + private static readonly LevelService _levelService = new(); + + public static void Initialize() + { + PersistenceService.Initialize(); + + foreach (var action in PersistenceService.GetActions()) + { + _levelService.InitializeAction(action.Action); + } + } + + public static void TrackAction(CockpitAction action, params object[] args) + { + PersistenceService.Persist(action); + _levelService.TrackAction(action); + } + } +} diff --git a/F4SD-Gamification/Services/LevelService.cs b/F4SD-Gamification/Services/LevelService.cs new file mode 100644 index 0000000..13536ad --- /dev/null +++ b/F4SD-Gamification/Services/LevelService.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Numerics; +using System.Security.Cryptography; + +namespace F4SD.Gamification.Services +{ + public class LevelService + { + private static readonly List<(int MinimumTotalExperiencePoints, int Level)> _experienceToLevelMap = new() + { + (0,1), + (1128,2), + (2400,3), + (3832,4), + (5440,5), + (7240,6), + (9248,7), + (11480,8), + (13952,9), + (16680,10), + (19680,11), + (22968,12), + (26560,13), + (30472,14), + (34720,15), + (39320,16), + (44288,17), + (49640,18), + (55392,19), + (61560,20), + (68160,21), + (75208,22), + (82720,23), + (90712,24), + (99200,25), + (108200,26), + (117728,27), + (127800,28), + (138432,29), + (149640,30), + (161440,31), + (173848,32), + (186880,33), + (200552,34), + (214880,35), + (229880,36), + (245568,37), + (261960,38), + (279072,39), + (296920,40), + (315520,41), + (334888,42), + (355040,43), + (375992,44), + (397760,45), + (420360,46), + (443808,47), + (468120,48), + (493312,49), + (519400,50), + }; + + private int _totalExperiencePoints = 0; + private int _currentExperiencePoints; + + private int _level; + + internal void InitializeAction(CockpitAction action) + { + AddExperiencePoints(GetExperiencePointsFor(action), true); + } + + internal void TrackAction(CockpitAction type, params object[] args) + { + AddExperiencePoints(GetExperiencePointsFor(type)); + } + + private void AddExperiencePoints(int points, bool isInitialization = false) + { + _totalExperiencePoints += points; + UpdateLevel(isInitialization); + UpdateCurrentExperiencePoints(); + + if (isInitialization) + return; + + ExperiencePointsChanged?.Invoke(null, new LevelEventArgs() + { + CurrentLevel = _level, + LevelTitle = GetTitle(_level), + CurrentXp = _currentExperiencePoints, + XpRequiredForLevelUp = GetXpRequiredForNextLevel() + }); + } + + private void UpdateCurrentExperiencePoints() + { + int xpForCurrentLevel = _experienceToLevelMap.FirstOrDefault(mapEntry => mapEntry.Level == _level).MinimumTotalExperiencePoints; + _currentExperiencePoints = _totalExperiencePoints - xpForCurrentLevel; + } + + private void UpdateLevel(bool isInitialization) + { + int previousLevel = _level; + _level = _experienceToLevelMap.FirstOrDefault(mapEntry => _totalExperiencePoints < mapEntry.MinimumTotalExperiencePoints).Level - 1; + + if (!isInitialization && previousLevel < _level) + LevelChanged?.Invoke(null, new LevelEventArgs() { CurrentLevel = _level, LevelTitle = GetTitle(_level) }); + } + + private int GetXpRequiredForNextLevel() + { + int totalXpForNextLevel = _experienceToLevelMap.FirstOrDefault(mapEntry => mapEntry.Level == _level + 1).MinimumTotalExperiencePoints; + int totalXpForCurrentLevel = _experienceToLevelMap.FirstOrDefault(mapEntry => mapEntry.Level == _level).MinimumTotalExperiencePoints; + return totalXpForNextLevel - totalXpForCurrentLevel; + } + + private int GetExperiencePointsFor(CockpitAction action) + { + switch (action) + { + case CockpitAction.CaseOpened: + break; + case CockpitAction.CaseClosed: + return 10; + case CockpitAction.QuickActionExecuted: + return 20; + case CockpitAction.CopyTemplateClicked: + return 5; + case CockpitAction.BuiltDirectConnection: + break; + case CockpitAction.AddNotes: + break; + case CockpitAction.StartRemoteConnection: + return 50; + default: + break; + } + + return 1; + } + + private static string GetTitle(int level) + { + switch (level) + { + case <= 5: + return "Support Trainee"; + case <= 10: + return "Response Assistant"; + case <= 15: + return "Service Aider"; + case <= 20: + return "Ticket Resolver"; + case <= 25: + return "Customer Navigator"; + case <= 30: + return "Incident Coordinator"; + case <= 35: + return "IT Guy"; + case <= 40: + return "Support Specialist"; + case <= 45: + return "Service Mentor"; + case <= 49: + return "Operations Lead"; + case > 49: + return "Ticket Titan"; + } + } + + public static event EventHandler ExperiencePointsChanged; + public static event EventHandler LevelChanged; + } +} diff --git a/F4SD-Gamification/Services/PersistenceService.cs b/F4SD-Gamification/Services/PersistenceService.cs new file mode 100644 index 0000000..aa9e182 --- /dev/null +++ b/F4SD-Gamification/Services/PersistenceService.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace F4SD.Gamification.Services +{ + internal static class PersistenceService + { + private static readonly string _databaseName = "F4SD-gmfc.txt"; + private static readonly string _directory = $@"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}\Consulting4IT GmbH\C4IT First Aid Service Desk"; + + private static string GetConnectionString() + { + return Path.Combine(_directory, _databaseName); + } + + internal static void Persist(CockpitAction action) + { + Initialize(); + + using var writer = File.AppendText(GetConnectionString()); + string actionLine = $"{DateTime.UtcNow},{action}"; + string base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(actionLine)); + writer.WriteLine(base64); + } + + internal static void Initialize() + { + Directory.CreateDirectory(_directory); + + if (!File.Exists(GetConnectionString())) + { + FileStream createdFile = File.Create(GetConnectionString()); + File.SetAttributes(GetConnectionString(), FileAttributes.Hidden); + createdFile.Close(); + } + } + + internal static IEnumerable<(DateTime Time, CockpitAction Action)> GetActions() + { + using StreamReader reader = new(GetConnectionString()); + + string line; + while ((line = reader.ReadLine()) != null) + { + string encodedLine = string.Empty; + + try { encodedLine = Encoding.UTF8.GetString(Convert.FromBase64String(line)); } + catch { continue; } + + if (string.IsNullOrWhiteSpace(encodedLine)) + continue; + + string[] splittedLine = encodedLine.Split(','); + + if (splittedLine.Length < 2) + continue; + + if (!DateTime.TryParse(splittedLine[0], out var date)) + continue; + + if (!(Enum.TryParse(splittedLine[1], out CockpitAction action))) + continue; + + yield return new(date, action); + } + } + } +} diff --git a/F4SD-PhoneMonitor/F4SD-PhoneMonitor.csproj b/F4SD-PhoneMonitor/F4SD-PhoneMonitor.csproj index 15eac1a..78f9499 100644 --- a/F4SD-PhoneMonitor/F4SD-PhoneMonitor.csproj +++ b/F4SD-PhoneMonitor/F4SD-PhoneMonitor.csproj @@ -67,7 +67,7 @@ .\Interop.CLMgr.dll - ..\packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll + ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll diff --git a/F4SD-PhoneMonitor/packages.config b/F4SD-PhoneMonitor/packages.config index 17ab3be..fef83be 100644 --- a/F4SD-PhoneMonitor/packages.config +++ b/F4SD-PhoneMonitor/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/F4SD.Cockpit.Client.Test/Basics/Sevices/SupportCase/Controllers/MenuDataFactoryTest.cs b/F4SD.Cockpit.Client.Test/Basics/Sevices/SupportCase/Controllers/MenuDataFactoryTest.cs new file mode 100644 index 0000000..ed3a6bb --- /dev/null +++ b/F4SD.Cockpit.Client.Test/Basics/Sevices/SupportCase/Controllers/MenuDataFactoryTest.cs @@ -0,0 +1,44 @@ +using C4IT.FASD.Base; +using C4IT.MultiLanguage; +using FasdDesktopUi.Basics; +using FasdDesktopUi.Basics.Models; +using FasdDesktopUi.Basics.Services.SupportCase.Controllers; +using FasdDesktopUi.Basics.UiActions; + +namespace F4SD.Cockpit.Client.Test.Basics.Sevices.SupportCase.Controllers; + +public class MenuDataFactoryTest +{ + public MenuDataFactoryTest() + { + cMultiLanguageSupport.CurrentLanguage = "EN"; + } + + [Fact] + public async Task Create_From_QuickActionDefinition() + { + cF4sdQuickActionRemoteComputer menuDataDefinition = new() + { + Id = Guid.NewGuid(), + Names = new() { ["EN"] = "Foo EN", ["DE"] = "Foo DE" }, + Icon = { IconType = enumIconType.intern, Name = nameof(enumInternIcon.misc_computer) }, + Descriptions = new() { ["EN"] = "Bar EN", ["DE"] = "Bar DE" }, + AlternativeDescriptions = new() { ["EN"] = "FooBar EN", ["DE"] = "FooBar DE" }, + Section = "Section 1", + Sections = ["Section 2", "Section 3"], + IsHidden = false + }; + + cMenuDataBase expected = new() + { + MenuText = "Foo EN", + MenuIcon = new cMenuDataBase.MenuIconInfo(new(F4SD_AdaptableIcon.Enums.enumInternIcons.misc_computer), null, false, null), + MenuSections = ["Section 1", "Section 2", "Section 3"], + UiAction = new cUiRemoteQuickAction(menuDataDefinition) { DisplayType = FasdDesktopUi.Basics.Enums.enumActionDisplayType.enabled } + }; + + cMenuDataBase actual = MenuDataFactory.Create(menuDataDefinition, [], []); + + Assert.Equivalent(expected, actual); + } +} diff --git a/F4SD.Cockpit.Client.Test/Basics/Sevices/SupportCase/SupportCaseProcessorTest.cs b/F4SD.Cockpit.Client.Test/Basics/Sevices/SupportCase/SupportCaseProcessorTest.cs new file mode 100644 index 0000000..d1e49d0 --- /dev/null +++ b/F4SD.Cockpit.Client.Test/Basics/Sevices/SupportCase/SupportCaseProcessorTest.cs @@ -0,0 +1,224 @@ +using C4IT.FASD.Base; +using C4IT.MultiLanguage; +using FasdDesktopUi.Basics.Services.Models; +using FasdDesktopUi.Basics.Services.SupportCase; +using FasdDesktopUi.Basics.Services.SupportCase.Processors; +using FasdDesktopUi.Basics.UiActions; +using NSubstitute; +using System.Globalization; + +namespace F4SD.Cockpit.Client.Test.Basics.Sevices.SupportCase; + +public class SupportCaseProcessorTest +{ + private readonly SupportCaseProcessor _supportCaseProcessor; + private readonly ISupportCase _mockSupportCase = Substitute.For(); + + private static readonly cMultiLanguageDictionary _stateTitle = new() { ["EN"] = "Bar", ["DE"] = "Bar (DE)" }; + + public SupportCaseProcessorTest() + { + _supportCaseProcessor = new SupportCaseProcessor(); + _supportCaseProcessor.SetSupportCase(_mockSupportCase); + cMultiLanguageSupport.CurrentLanguage = "EN"; + cF4SDCockpitXmlConfig.Instance = new() { HealthCardConfig = new() { SearchResultAge = 14 } }; + } + + [Theory] + [MemberData(nameof(GetCockpitValueDisplayDataTestData_StateInfo))] + [MemberData(nameof(GetCockpitValueDisplayDataTestData_StateLevel))] + [MemberData(nameof(GetCockpitValueDisplayDataTestData_StateTranslation))] + [MemberData(nameof(GetCockpitValueDisplayDataTestData_StateDateTime))] + [MemberData(nameof(GetCockpitValueDisplayDataTestData_StateVersion))] + [MemberData(nameof(GetCockpitValueDisplayDataTestData_StateDetails))] + public void Test_CockpitValueDisplayData(MockState mockState, CockpitValueDisplayData expected) + { + _mockSupportCase.GetSupportCaseHealthcardData(default, default, Arg.Any()).ReturnsForAnyArgs([mockState.RawData]); + + CockpitValueDisplayData actual = _supportCaseProcessor.GetCockpitValueDisplayData(mockState.StateDefinition, default, default); + + Assert.Equivalent(expected, actual); + } + + public static IEnumerable> GetCockpitValueDisplayDataTestData_StateInfo() + { + yield return (new(new cHealthCardStateInfo() { Names = _stateTitle, IsNotTransparent = false }, "Foo"), new() { Title = "Bar", Values = ["Foo"], Levels = [enumHealthCardStateLevel.None] }); + yield return (new(new cHealthCardStateInfo() { Names = _stateTitle, IsNotTransparent = true }, "Foo"), new() { Title = "Bar", Values = ["Foo"], Levels = [enumHealthCardStateLevel.Info] }); + } + + public static IEnumerable> GetCockpitValueDisplayDataTestData_StateLevel() + { + yield return (new(new cHealthCardStateLevel() { Names = _stateTitle, Warning = 6, Error = 7, IsDirectionUp = true, IsNotTransparent = false }, 5), new() { Title = "Bar", Values = ["5"], Levels = [enumHealthCardStateLevel.None] }); + yield return (new(new cHealthCardStateLevel() { Names = _stateTitle, Warning = 6, Error = 7, IsDirectionUp = true, IsNotTransparent = true }, 5), new() { Title = "Bar", Values = ["5"], Levels = [enumHealthCardStateLevel.Ok] }); + yield return (new(new cHealthCardStateLevel() { Names = _stateTitle, Warning = 6, Error = 7, IsDirectionUp = true, IsNotTransparent = true }, 6), new() { Title = "Bar", Values = ["6"], Levels = [enumHealthCardStateLevel.Warning] }); + yield return (new(new cHealthCardStateLevel() { Names = _stateTitle, Warning = 6, Error = 7, IsDirectionUp = true, IsNotTransparent = true }, 7), new() { Title = "Bar", Values = ["7"], Levels = [enumHealthCardStateLevel.Error] }); + yield return (new(new cHealthCardStateLevel() { Names = _stateTitle, Warning = 4, Error = 2, IsDirectionUp = false, IsNotTransparent = false }, 5), new() { Title = "Bar", Values = ["5"], Levels = [enumHealthCardStateLevel.None] }); + yield return (new(new cHealthCardStateLevel() { Names = _stateTitle, Warning = 4, Error = 2, IsDirectionUp = false, IsNotTransparent = true }, 5), new() { Title = "Bar", Values = ["5"], Levels = [enumHealthCardStateLevel.Ok] }); + yield return (new(new cHealthCardStateLevel() { Names = _stateTitle, Warning = 4, Error = 2, IsDirectionUp = false, IsNotTransparent = true }, 4), new() { Title = "Bar", Values = ["4"], Levels = [enumHealthCardStateLevel.Warning] }); + yield return (new(new cHealthCardStateLevel() { Names = _stateTitle, Warning = 4, Error = 2, IsDirectionUp = false, IsNotTransparent = true }, 2), new() { Title = "Bar", Values = ["2"], Levels = [enumHealthCardStateLevel.Error] }); + } + + public static IEnumerable> GetCockpitValueDisplayDataTestData_StateTranslation() + { + cF4SDCockpitXmlConfig translationConfigNode = new() { HealthCardConfig = new cF4SDHealthCardConfig() }; + translationConfigNode.Translations.Add("MyTranslation", new cHealthCardTranslator() + { + DefaultTranslation = new() { Translation = new() { ["EN"] = "Foo translated" }, StateLevel = enumHealthCardStateLevel.Error }, + Translations = [ + new() { Translation = new() { ["EN"] = "Foo Info translated"}, Values = ["Foo Info"], StateLevel = enumHealthCardStateLevel.Info}, + new() { Translation = new() { ["EN"] = "Foo Ok translated"}, Values = ["Foo Ok"], StateLevel = enumHealthCardStateLevel.Ok}, + new() { Translation = new() { ["EN"] = "Foo Warning translated"}, Values = ["Foo Warning"], StateLevel = enumHealthCardStateLevel.Warning}, + new() { Translation = new() { ["EN"] = "Foo Error translated"}, Values = ["Foo Error"], StateLevel = enumHealthCardStateLevel.Error}, + ] + }); + + yield return (new(new cHealthCardStateTranslation(translationConfigNode) { Names = _stateTitle, Translation = "MyTranslation", IsNotTransparent = false }, "Foo"), new() { Title = "Bar", Values = ["Foo translated"], Levels = [enumHealthCardStateLevel.Error] }); + yield return (new(new cHealthCardStateTranslation(translationConfigNode) { Names = _stateTitle, Translation = "MyTranslation", IsNotTransparent = false }, "Foo Ok"), new() { Title = "Bar", Values = ["Foo Ok translated"], Levels = [enumHealthCardStateLevel.None] }); + yield return (new(new cHealthCardStateTranslation(translationConfigNode) { Names = _stateTitle, Translation = "MyTranslation", IsNotTransparent = true }, "Foo Ok"), new() { Title = "Bar", Values = ["Foo Ok translated"], Levels = [enumHealthCardStateLevel.Ok] }); + yield return (new(new cHealthCardStateTranslation(translationConfigNode) { Names = _stateTitle, Translation = "MyTranslation", IsNotTransparent = false }, "Foo Info"), new() { Title = "Bar", Values = ["Foo Info translated"], Levels = [enumHealthCardStateLevel.None] }); + yield return (new(new cHealthCardStateTranslation(translationConfigNode) { Names = _stateTitle, Translation = "MyTranslation", IsNotTransparent = true }, "Foo Info"), new() { Title = "Bar", Values = ["Foo Info translated"], Levels = [enumHealthCardStateLevel.Info] }); + yield return (new(new cHealthCardStateTranslation(translationConfigNode) { Names = _stateTitle, Translation = "MyTranslation", IsNotTransparent = true }, "Foo Warning"), new() { Title = "Bar", Values = ["Foo Warning translated"], Levels = [enumHealthCardStateLevel.Warning] }); + yield return (new(new cHealthCardStateTranslation(translationConfigNode) { Names = _stateTitle, Translation = "MyTranslation", IsNotTransparent = true }, "Foo Error"), new() { Title = "Bar", Values = ["Foo Error translated"], Levels = [enumHealthCardStateLevel.Error] }); + + yield return (new(new cHealthCardStateTranslation(translationConfigNode) { Names = _stateTitle, Translation = "NoTranslation", IsNotTransparent = false }, "Foo Ok"), new() { Title = "Bar", Values = [null], Levels = [enumHealthCardStateLevel.None] }); + } + + public static IEnumerable> GetCockpitValueDisplayDataTestData_StateDateTime() + { + yield return (new(new cHealthCardStateDateTime() { Names = _stateTitle, WarningHours = 6, ErrorHours = 7, IsDirectionUp = true, IsNotTransparent = false }, DateTime.UtcNow.ToString(CultureInfo.CurrentCulture)), new() { Title = "Bar", Values = [DateTime.UtcNow.ToString(CultureInfo.CurrentCulture)], Levels = [enumHealthCardStateLevel.None] }); + yield return (new(new cHealthCardStateDateTime() { Names = _stateTitle, WarningHours = 6, ErrorHours = 7, IsDirectionUp = true, IsNotTransparent = true }, DateTime.UtcNow.ToString(CultureInfo.CurrentCulture)), new() { Title = "Bar", Values = [DateTime.UtcNow.ToString(CultureInfo.CurrentCulture)], Levels = [enumHealthCardStateLevel.Ok] }); + yield return (new(new cHealthCardStateDateTime() { Names = _stateTitle, WarningHours = 6, ErrorHours = 7, IsDirectionUp = true, IsNotTransparent = true }, DateTime.UtcNow.AddHours(-6).ToString(CultureInfo.CurrentCulture)), new() { Title = "Bar", Values = [DateTime.UtcNow.AddHours(-6).ToString(CultureInfo.CurrentCulture)], Levels = [enumHealthCardStateLevel.Warning] }); + yield return (new(new cHealthCardStateDateTime() { Names = _stateTitle, WarningHours = 6, ErrorHours = 7, IsDirectionUp = true, IsNotTransparent = true }, DateTime.UtcNow.AddHours(-7).ToString(CultureInfo.CurrentCulture)), new() { Title = "Bar", Values = [DateTime.UtcNow.AddHours(-7).ToString(CultureInfo.CurrentCulture)], Levels = [enumHealthCardStateLevel.Error] }); + yield return (new(new cHealthCardStateDateTime() { Names = _stateTitle, WarningHours = 4, ErrorHours = 2, IsDirectionUp = false, IsNotTransparent = false }, DateTime.UtcNow.AddHours(-5).ToString(CultureInfo.CurrentCulture)), new() { Title = "Bar", Values = [DateTime.UtcNow.AddHours(-5).ToString(CultureInfo.CurrentCulture)], Levels = [enumHealthCardStateLevel.None] }); + yield return (new(new cHealthCardStateDateTime() { Names = _stateTitle, WarningHours = 4, ErrorHours = 2, IsDirectionUp = false, IsNotTransparent = true }, DateTime.UtcNow.AddHours(-5).ToString(CultureInfo.CurrentCulture)), new() { Title = "Bar", Values = [DateTime.UtcNow.AddHours(-5).ToString(CultureInfo.CurrentCulture)], Levels = [enumHealthCardStateLevel.Ok] }); + yield return (new(new cHealthCardStateDateTime() { Names = _stateTitle, WarningHours = 4, ErrorHours = 2, IsDirectionUp = false, IsNotTransparent = true }, DateTime.UtcNow.AddHours(-4).ToString(CultureInfo.CurrentCulture)), new() { Title = "Bar", Values = [DateTime.UtcNow.AddHours(-4).ToString(CultureInfo.CurrentCulture)], Levels = [enumHealthCardStateLevel.Warning] }); + yield return (new(new cHealthCardStateDateTime() { Names = _stateTitle, WarningHours = 4, ErrorHours = 2, IsDirectionUp = false, IsNotTransparent = true }, DateTime.UtcNow.AddHours(-2).ToString(CultureInfo.CurrentCulture)), new() { Title = "Bar", Values = [DateTime.UtcNow.AddHours(-2).ToString(CultureInfo.CurrentCulture)], Levels = [enumHealthCardStateLevel.Error] }); + } + + public static IEnumerable> GetCockpitValueDisplayDataTestData_StateVersion() + { + yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("4.2.4.2"), Error = Version.Parse("6.7.6.7"), IsDirectionUp = true, IsNotTransparent = false }, "4.2.4.1"), new() { Title = "Bar", Values = ["4.2.4.1"], Levels = [enumHealthCardStateLevel.None] }); + yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("4.2.4.2"), Error = Version.Parse("6.7.6.7"), IsDirectionUp = true, IsNotTransparent = true }, "4.2.4.1"), new() { Title = "Bar", Values = ["4.2.4.1"], Levels = [enumHealthCardStateLevel.Ok] }); + yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("4.2.4.2"), Error = Version.Parse("6.7.6.7"), IsDirectionUp = true, IsNotTransparent = true }, "4.2.4.2"), new() { Title = "Bar", Values = ["4.2.4.2"], Levels = [enumHealthCardStateLevel.Warning] }); + yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("4.2.4.2"), Error = Version.Parse("6.7.6.7"), IsDirectionUp = true, IsNotTransparent = true }, "6.7.6.7"), new() { Title = "Bar", Values = ["6.7.6.7"], Levels = [enumHealthCardStateLevel.Error] }); + yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("6.7.6.7"), Error = Version.Parse("4.2.4.2"), IsDirectionUp = false, IsNotTransparent = false }, "6.7.6.8"), new() { Title = "Bar", Values = ["6.7.6.8"], Levels = [enumHealthCardStateLevel.None] }); + yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("6.7.6.7"), Error = Version.Parse("4.2.4.2"), IsDirectionUp = false, IsNotTransparent = true }, "6.7.6.8"), new() { Title = "Bar", Values = ["6.7.6.8"], Levels = [enumHealthCardStateLevel.Ok] }); + yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("6.7.6.7"), Error = Version.Parse("4.2.4.2"), IsDirectionUp = false, IsNotTransparent = true }, "6.7.6.7"), new() { Title = "Bar", Values = ["6.7.6.7"], Levels = [enumHealthCardStateLevel.Warning] }); + yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("6.7.6.7"), Error = Version.Parse("4.2.4.2"), IsDirectionUp = false, IsNotTransparent = true }, "4.2.4.2"), new() { Title = "Bar", Values = ["4.2.4.2"], Levels = [enumHealthCardStateLevel.Error] }); + + yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("4.2.4.2"), Error = Version.Parse("6.7.6.7"), IsDirectionUp = true, IsNotTransparent = false }, Version.Parse("4.2.4.1")), new() { Title = "Bar", Values = ["4.2.4.1"], Levels = [enumHealthCardStateLevel.None] }); + yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("4.2.4.2"), Error = Version.Parse("6.7.6.7"), IsDirectionUp = true, IsNotTransparent = true }, Version.Parse("4.2.4.1")), new() { Title = "Bar", Values = ["4.2.4.1"], Levels = [enumHealthCardStateLevel.Ok] }); + yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("4.2.4.2"), Error = Version.Parse("6.7.6.7"), IsDirectionUp = true, IsNotTransparent = true }, Version.Parse("4.2.4.2")), new() { Title = "Bar", Values = ["4.2.4.2"], Levels = [enumHealthCardStateLevel.Warning] }); + yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("4.2.4.2"), Error = Version.Parse("6.7.6.7"), IsDirectionUp = true, IsNotTransparent = true }, Version.Parse("6.7.6.7")), new() { Title = "Bar", Values = ["6.7.6.7"], Levels = [enumHealthCardStateLevel.Error] }); + yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("6.7.6.7"), Error = Version.Parse("4.2.4.2"), IsDirectionUp = false, IsNotTransparent = false }, Version.Parse("6.7.6.8")), new() { Title = "Bar", Values = ["6.7.6.8"], Levels = [enumHealthCardStateLevel.None] }); + yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("6.7.6.7"), Error = Version.Parse("4.2.4.2"), IsDirectionUp = false, IsNotTransparent = true }, Version.Parse("6.7.6.8")), new() { Title = "Bar", Values = ["6.7.6.8"], Levels = [enumHealthCardStateLevel.Ok] }); + yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("6.7.6.7"), Error = Version.Parse("4.2.4.2"), IsDirectionUp = false, IsNotTransparent = true }, Version.Parse("6.7.6.7")), new() { Title = "Bar", Values = ["6.7.6.7"], Levels = [enumHealthCardStateLevel.Warning] }); + yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("6.7.6.7"), Error = Version.Parse("4.2.4.2"), IsDirectionUp = false, IsNotTransparent = true }, Version.Parse("4.2.4.2")), new() { Title = "Bar", Values = ["4.2.4.2"], Levels = [enumHealthCardStateLevel.Error] }); + } + + public static IEnumerable> GetCockpitValueDisplayDataTestData_StateDetails() + { + cMultiLanguageSupport.CurrentLanguage = "EN"; + + cHealthCardStateInfo stateDefinitionWithDetails = new() { Names = _stateTitle }; + stateDefinitionWithDetails.Details = new cHealthCardDetailsValued(stateDefinitionWithDetails) { Format = cHealthCardDetailsValued.ValuedFormat.csv, RowSeparator = ',', ColSeparator = null }; + yield return (new(stateDefinitionWithDetails, "42:42:42:42:42:42,67:67:67:67:67:67,69:69:69:69:69:69,,C0:FE:C0:FE:C0:FE"), new() { Title = "Bar", Values = ["4"], Levels = [enumHealthCardStateLevel.None], UiActions = [new cShowDetailedDataAction(stateDefinitionWithDetails, 0, null) { DisplayType = FasdDesktopUi.Basics.Enums.enumActionDisplayType.enabled }] }); + + stateDefinitionWithDetails = new() { Names = _stateTitle }; + stateDefinitionWithDetails.Details = new cHealthCardDetailsValued(stateDefinitionWithDetails) { Format = cHealthCardDetailsValued.ValuedFormat.json, RowSeparator = ',', ColSeparator = null }; + stateDefinitionWithDetails.Details.Add(new cHealthCardDetailsColumn() { Names = _stateTitle, Column = "Status" }); + stateDefinitionWithDetails.Details.Add(new cHealthCardDetailsColumn() { Names = new() { ["EN"] = "Hello" }, Column = "Name" }); + yield return (new(stateDefinitionWithDetails, "[{\"Availability\":2,\"BatteryStatus\":2,\"Caption\":\"Interner Akku\",\"Description\":\"Interner Akku\",\"EstimatedRunTime\":71582788,\"Name\":\"DELL 803W64C7\",\"Status\":\"OK\"}]"), new() { Title = "Bar", Values = ["OK"], Levels = [enumHealthCardStateLevel.None], UiActions = [new cShowDetailedDataAction(stateDefinitionWithDetails, 0, null) { DisplayType = FasdDesktopUi.Basics.Enums.enumActionDisplayType.enabled }] }); + } + + [Theory] + [MemberData(nameof(GetCockpitValueDisplayDataTestData_StateRefLink))] + public void Test_CockpitValueDisplayData_RefLink(MockState mockState, MockState referencedMockState, CockpitValueDisplayData expected) + { + _mockSupportCase.GetSupportCaseHealthcardData(default, Arg.Is(referencedMockState.StateDefinition.DatabaseInfo), Arg.Any()).Returns([referencedMockState.RawData]); + + if (referencedMockState.StateDefinition is cHealthCardStateAggregation aggregation) + { + foreach (var state in aggregation.States) + { + _mockSupportCase.GetSupportCaseHealthcardData(default, Arg.Is(state.DatabaseInfo), Arg.Any()).Returns([42]); + } + } + + _mockSupportCase.GetSupportCaseHealthcardData(default, Arg.Is(mockState.StateDefinition.DatabaseInfo), Arg.Any()).Returns([mockState.RawData]); + + CockpitValueDisplayData actual = _supportCaseProcessor.GetCockpitValueDisplayData(mockState.StateDefinition, default, default); + + Assert.Equivalent(expected, actual); + } + + public static IEnumerable> GetCockpitValueDisplayDataTestData_StateRefLink() + { + const int referenceValue = 42; + + var levelNoneStateDefinition = new cHealthCardStateLevel() { Name = "MyReferenceNone", DatabaseInfo = new() { ValueTable = "Foo", ValueColumn = "BarNone" }, Warning = referenceValue + 1, Error = referenceValue + 2, IsDirectionUp = true, IsNotTransparent = false }; + var levelInfoStateDefinition = new cHealthCardStateInfo() { Name = "MyReferenceInfo", DatabaseInfo = new() { ValueTable = "Foo", ValueColumn = "BarInfo" }, IsNotTransparent = true }; + var levelOkStateDefinition = new cHealthCardStateLevel() { Name = "MyReferenceOk", DatabaseInfo = new() { ValueTable = "Foo", ValueColumn = "BarOk" }, Warning = referenceValue + 1, Error = referenceValue + 2, IsDirectionUp = true, IsNotTransparent = true }; + var levelWarningStateDefinition = new cHealthCardStateLevel() { Name = "MyReferenceWarning", DatabaseInfo = new() { ValueTable = "Foo", ValueColumn = "BarWarning" }, Warning = referenceValue, Error = referenceValue + 1, IsDirectionUp = true, IsNotTransparent = true }; + var levelErrorStateDefinition = new cHealthCardStateLevel() { Name = "MyReferenceError", DatabaseInfo = new() { ValueTable = "Foo", ValueColumn = "BarError" }, Warning = referenceValue, Error = referenceValue, IsDirectionUp = true, IsNotTransparent = true }; + var levelWarningAggregationDefinition = new cHealthCardStateAggregation() { Name = "MyReferenceAggregation", States = [levelOkStateDefinition, levelWarningStateDefinition] }; + + var stateConfigNode = new cF4SDHealthCardConfig(); + stateConfigNode.Prerequisites.ReferencableStates.Add(levelNoneStateDefinition.Name, levelNoneStateDefinition); + stateConfigNode.Prerequisites.ReferencableStates.Add(levelInfoStateDefinition.Name, levelInfoStateDefinition); + stateConfigNode.Prerequisites.ReferencableStates.Add(levelOkStateDefinition.Name, levelOkStateDefinition); + stateConfigNode.Prerequisites.ReferencableStates.Add(levelWarningStateDefinition.Name, levelWarningStateDefinition); + stateConfigNode.Prerequisites.ReferencableStates.Add(levelErrorStateDefinition.Name, levelErrorStateDefinition); + stateConfigNode.Prerequisites.ReferencableStates.Add(levelWarningAggregationDefinition.Name, levelWarningAggregationDefinition); + + yield return (new(new cHealthCardStateRefLink(stateConfigNode) { Names = _stateTitle, DatabaseInfo = new() { ValueTable = "Bar", ValueColumn = "Foo" }, Reference = levelNoneStateDefinition.Name }, "Foo"), new(levelNoneStateDefinition, referenceValue), new() { Title = "Bar", Values = ["Foo"], Levels = [enumHealthCardStateLevel.None] }); + yield return (new(new cHealthCardStateRefLink(stateConfigNode) { Names = _stateTitle, DatabaseInfo = new() { ValueTable = "Bar", ValueColumn = "Foo" }, Reference = levelInfoStateDefinition.Name }, "Foo"), new(levelInfoStateDefinition, referenceValue), new() { Title = "Bar", Values = ["Foo"], Levels = [enumHealthCardStateLevel.Info] }); + yield return (new(new cHealthCardStateRefLink(stateConfigNode) { Names = _stateTitle, DatabaseInfo = new() { ValueTable = "Bar", ValueColumn = "Foo" }, Reference = levelOkStateDefinition.Name, IsNotTransparent = true }, "Foo"), new(levelOkStateDefinition, referenceValue), new() { Title = "Bar", Values = ["Foo"], Levels = [enumHealthCardStateLevel.Ok] }); + yield return (new(new cHealthCardStateRefLink(stateConfigNode) { Names = _stateTitle, DatabaseInfo = new() { ValueTable = "Bar", ValueColumn = "Foo" }, Reference = levelWarningStateDefinition.Name }, "Foo"), new(levelWarningStateDefinition, referenceValue), new() { Title = "Bar", Values = ["Foo"], Levels = [enumHealthCardStateLevel.Warning] }); + yield return (new(new cHealthCardStateRefLink(stateConfigNode) { Names = _stateTitle, DatabaseInfo = new() { ValueTable = "Bar", ValueColumn = "Foo" }, Reference = levelErrorStateDefinition.Name }, "Foo"), new(levelErrorStateDefinition, referenceValue), new() { Title = "Bar", Values = ["Foo"], Levels = [enumHealthCardStateLevel.Error] }); + yield return (new(new cHealthCardStateRefLink(stateConfigNode) { Names = _stateTitle, DatabaseInfo = new() { ValueTable = "Bar", ValueColumn = "Foo" }, Reference = levelWarningAggregationDefinition.Name }, "Foo"), new(levelWarningAggregationDefinition, referenceValue), new() { Title = "Bar", Values = ["Foo"], Levels = [enumHealthCardStateLevel.Warning] }); + } + + [Theory] + [MemberData(nameof(GetCockpitValueDisplayDataTestData_StateAggregation))] + public void Test_CockpitValueDisplayData_Aggregation(MockState mockState, CockpitValueDisplayData expected, params MockState[] aggregatedStates) + { + foreach (var aggregatedState in aggregatedStates) + { + _mockSupportCase.GetSupportCaseHealthcardData(default, Arg.Is(aggregatedState.StateDefinition.DatabaseInfo), Arg.Any()).Returns([aggregatedState.RawData]); + + if (mockState.StateDefinition is cHealthCardStateAggregation stateAggreagtion) + stateAggreagtion.States.Add(aggregatedState.StateDefinition); + } + + _mockSupportCase.GetSupportCaseHealthcardData(default, Arg.Is(mockState.StateDefinition.DatabaseInfo), Arg.Any()).Returns([mockState.RawData]); + + CockpitValueDisplayData actual = _supportCaseProcessor.GetCockpitValueDisplayData(mockState.StateDefinition, default, default); + + Assert.Equivalent(expected, actual); + } + + public static IEnumerable> GetCockpitValueDisplayDataTestData_StateAggregation() + { + MockState stateOk = new(new cHealthCardStateLevel() { DatabaseInfo = new() { ValueTable = "MyAggregated", ValueColumn = "Ok" }, Warning = 43, Error = 44, IsDirectionUp = true }, 42); + MockState stateWarning = new(new cHealthCardStateLevel() { DatabaseInfo = new() { ValueTable = "MyAggregated", ValueColumn = "Warning" }, Warning = 42, Error = 43, IsDirectionUp = true }, 42); + MockState stateError = new(new cHealthCardStateLevel() { DatabaseInfo = new() { ValueTable = "MyAggregated", ValueColumn = "Error" }, Warning = 41, Error = 42, IsDirectionUp = true }, 42); + + yield return (new(new cHealthCardStateAggregation() { Names = _stateTitle, IsNotTransparent = false }, null), new() { Title = "Bar", Values = ["Ø"], Levels = [enumHealthCardStateLevel.None] }, [stateOk]); + yield return (new(new cHealthCardStateAggregation() { Names = _stateTitle, IsNotTransparent = true }, null), new() { Title = "Bar", Values = ["Ø"], Levels = [enumHealthCardStateLevel.Ok] }, [stateOk]); + yield return (new(new cHealthCardStateAggregation() { Names = _stateTitle, IsNotTransparent = true }, null), new() { Title = "Bar", Values = ["Ø"], Levels = [enumHealthCardStateLevel.Warning] }, [stateOk, stateWarning]); + yield return (new(new cHealthCardStateAggregation() { Names = _stateTitle, IsNotTransparent = true }, null), new() { Title = "Bar", Values = ["Ø"], Levels = [enumHealthCardStateLevel.Error] }, [stateOk, stateWarning, stateError]); + } + + public record MockState + { + public cHealthCardStateBase StateDefinition { get; set; } + public object? RawData { get; set; } + + public MockState(cHealthCardStateBase stateDefinition, object? rawData) + { + StateDefinition = stateDefinition; + RawData = rawData; + } + } +} diff --git a/F4SD.Cockpit.Client.Test/Basics/Sevices/SupportCase/SupportCaseTest.cs b/F4SD.Cockpit.Client.Test/Basics/Sevices/SupportCase/SupportCaseTest.cs index f7a23d3..b4e0c52 100644 --- a/F4SD.Cockpit.Client.Test/Basics/Sevices/SupportCase/SupportCaseTest.cs +++ b/F4SD.Cockpit.Client.Test/Basics/Sevices/SupportCase/SupportCaseTest.cs @@ -126,7 +126,7 @@ public class SupportCaseTest _supportCase.UpdateSupportCaseDataCache(relation, [dataTable]); // Assert - var actual = _supportCase.GetSupportCaseHealthcardData(relation, new cValueAddress() { ValueTable = tableName, ValueColumn = columnName }); + var actual = _supportCase.GetSupportCaseHealthcardData(relation, new cValueAddress() { ValueTable = tableName, ValueColumn = columnName }, default); Assert.Equal(expected, actual); } @@ -145,7 +145,7 @@ public class SupportCaseTest ] }; // Act - var actual = _supportCase.GetSupportCaseHealthcardData(relation, new cValueAddress() { ValueTable = "NonExistentTable", ValueColumn = "NonExistentColumn" }); + var actual = _supportCase.GetSupportCaseHealthcardData(relation, new cValueAddress() { ValueTable = "NonExistentTable", ValueColumn = "NonExistentColumn" }, default); // Assert Assert.Null(actual); } diff --git a/F4SD.Cockpit.Client.Test/F4SD.Cockpit.Client.Test.csproj b/F4SD.Cockpit.Client.Test/F4SD.Cockpit.Client.Test.csproj index 4d0cc30..8bd807d 100644 --- a/F4SD.Cockpit.Client.Test/F4SD.Cockpit.Client.Test.csproj +++ b/F4SD.Cockpit.Client.Test/F4SD.Cockpit.Client.Test.csproj @@ -5,7 +5,8 @@ enable Exe F4SD.Cockpit.Client.Test - net10.0 + net10.0-windows + true - + - - + + - - + MouseLeftButtonUp="Border_Click" + TouchDown="Border_Click"> - - - + diff --git a/FasdDesktopUi/Basics/UserControls/TimerView.xaml.cs b/FasdDesktopUi/Basics/UserControls/TimerView.xaml.cs index 94e221a..6e79f42 100644 --- a/FasdDesktopUi/Basics/UserControls/TimerView.xaml.cs +++ b/FasdDesktopUi/Basics/UserControls/TimerView.xaml.cs @@ -15,18 +15,18 @@ namespace FasdDesktopUi.Basics.UserControls public event EventHandler OnPauseStarted; - private static List startTimes = new List(); - private static List endTimes = new List(); - private static List pausedTimesStart = new List(); - private static List pausedTimesEnd = new List(); + private static readonly List _startTimes = new List(); + private static readonly List _endTimes = new List(); + private static readonly List _pausedTimesStart = new List(); + private static readonly List _pausedTimesEnd = new List(); - public static List caseTimes = new List(); - private static Dictionary finalWorkingTimes = new Dictionary(); + public static List CaseTimes = new List(); + private static Dictionary _finalWorkingTimes = new Dictionary(); - private static DispatcherTimer timer; - private static TimeSpan elapsedTime; - private static TimeSpan totalPausedTime = TimeSpan.Zero; - private static TimeSpan pauseDuration; + private static DispatcherTimer _timer; + private static TimeSpan _elapsedTime; + private static TimeSpan _totalPausedTime = TimeSpan.Zero; + private static TimeSpan _pauseDuration; #endregion @@ -39,13 +39,13 @@ namespace FasdDesktopUi.Basics.UserControls public static void ResetTimer() { - startTimes.Clear(); - endTimes.Clear(); - pausedTimesEnd.Clear(); - pausedTimesStart.Clear(); - finalWorkingTimes.Clear(); - elapsedTime = TimeSpan.Zero; - caseTimes.Clear(); + _startTimes.Clear(); + _endTimes.Clear(); + _pausedTimesEnd.Clear(); + _pausedTimesStart.Clear(); + _finalWorkingTimes.Clear(); + _elapsedTime = TimeSpan.Zero; + CaseTimes.Clear(); StartTimer(); } @@ -54,8 +54,8 @@ namespace FasdDesktopUi.Basics.UserControls { try { - startTimes?.Add(DateTime.UtcNow); - timer?.Start(); + _startTimes?.Add(DateTime.UtcNow); + _timer?.Start(); } catch (Exception E) { @@ -67,8 +67,8 @@ namespace FasdDesktopUi.Basics.UserControls { try { - endTimes?.Add(DateTime.UtcNow); - timer?.Stop(); + _endTimes?.Add(DateTime.UtcNow); + _timer?.Stop(); UpdateTimerControl(); } catch (Exception E) @@ -83,7 +83,7 @@ namespace FasdDesktopUi.Basics.UserControls { ResetTimer(); - timer = new DispatcherTimer(TimeSpan.FromSeconds(1.0), DispatcherPriority.Loaded, new EventHandler((s, args) => + _timer = new DispatcherTimer(TimeSpan.FromSeconds(1.0), DispatcherPriority.Loaded, new EventHandler((s, args) => { try { UpdateTimerControl(); } catch { } @@ -100,8 +100,8 @@ namespace FasdDesktopUi.Basics.UserControls { try { - TimerControl.Text = elapsedTime.ToString(@"hh\:mm\:ss"); - elapsedTime = elapsedTime.Add(TimeSpan.FromSeconds(1.0)); + TimerControl.Text = _elapsedTime.ToString(@"hh\:mm\:ss"); + _elapsedTime = _elapsedTime.Add(TimeSpan.FromSeconds(1.0)); } catch (Exception E) { @@ -113,44 +113,44 @@ namespace FasdDesktopUi.Basics.UserControls { try { - var currentDate = DateTime.Now.Date.ToString("d"); - var startDateTime = startTimes.First(); + var currentDate = DateTime.UtcNow.Date.ToString("d"); + var startDateTime = _startTimes.First(); var endDateTime = DateTime.UtcNow; var bruttoWorkingTime = TimeSpan.Zero; var nettoWorkingTime = TimeSpan.Zero; - if (pausedTimesStart.Count == 0) + if (_pausedTimesStart.Count == 0) { - totalPausedTime = TimeSpan.Zero; + _totalPausedTime = TimeSpan.Zero; } - if (pausedTimesEnd.Count > 0) + if (_pausedTimesEnd.Count > 0) { - totalPausedTime = TimeSpan.Zero; + _totalPausedTime = TimeSpan.Zero; - for (int i = 0; i < pausedTimesStart.Count; i++) + for (int i = 0; i < _pausedTimesStart.Count; i++) { - var start = pausedTimesStart[i]; - var end = pausedTimesEnd[i]; + var start = _pausedTimesStart[i]; + var end = _pausedTimesEnd[i]; - pauseDuration = end - start; + _pauseDuration = end - start; - totalPausedTime += pauseDuration; + _totalPausedTime += _pauseDuration; } } bruttoWorkingTime = endDateTime - startDateTime; - nettoWorkingTime = bruttoWorkingTime - totalPausedTime; + nettoWorkingTime = bruttoWorkingTime - _totalPausedTime; - finalWorkingTimes = new Dictionary() + _finalWorkingTimes = new Dictionary() { {"CurrentDate", currentDate}, - {"StartTime", startTimes.First()}, + {"StartTime", _startTimes.First()}, {"EndTime", endDateTime }, {"BruttoWorkingTime", bruttoWorkingTime }, {"NettoWorkingTime", nettoWorkingTime.TotalSeconds }, - {"TotalPausedTime", totalPausedTime } + {"TotalPausedTime", _totalPausedTime } }; } catch (Exception E) @@ -158,7 +158,7 @@ namespace FasdDesktopUi.Basics.UserControls LogException(E); } - return finalWorkingTimes; + return _finalWorkingTimes; } #endregion @@ -169,13 +169,13 @@ namespace FasdDesktopUi.Basics.UserControls { try { - if (timer?.IsEnabled == true) + if (_timer?.IsEnabled == true) return; - timer?.Start(); - pausedTimesEnd?.Add(DateTime.UtcNow); + _timer?.Start(); + _pausedTimesEnd?.Add(DateTime.UtcNow); - caseTimes.Add(new cF4SDCaseTime() + CaseTimes.Add(new cF4SDCaseTime() { StatusId = CaseStatus.InProgress, CaseTime = DateTime.UtcNow @@ -191,10 +191,10 @@ namespace FasdDesktopUi.Basics.UserControls { try { - timer?.Stop(); - pausedTimesStart?.Add(DateTime.UtcNow); + _timer?.Stop(); + _pausedTimesStart?.Add(DateTime.UtcNow); - caseTimes.Add(new cF4SDCaseTime() + CaseTimes.Add(new cF4SDCaseTime() { StatusId = CaseStatus.OnHold, CaseTime = DateTime.UtcNow @@ -209,17 +209,9 @@ namespace FasdDesktopUi.Basics.UserControls } } - - private void Border_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) - { - PauseButton_Click(); - } + private void Border_Click(object sender, InputEventArgs e) + => PauseButton_Click(); #endregion - - private void Border_TouchDown(object sender, TouchEventArgs e) - { - PauseButton_Click(); - } } } diff --git a/FasdDesktopUi/Basics/cUtility.cs b/FasdDesktopUi/Basics/cUtility.cs index 2252060..d1d55e3 100644 --- a/FasdDesktopUi/Basics/cUtility.cs +++ b/FasdDesktopUi/Basics/cUtility.cs @@ -1,15 +1,13 @@ using C4IT.F4SD.DisplayFormatting; using C4IT.FASD.Base; -using C4IT.MultiLanguage; using F4SD_AdaptableIcon.Enums; using FasdDesktopUi.Basics.Models; using FasdDesktopUi.Basics.UserControls; using FasdDesktopUi.Basics.UserControls.AdaptableIcon; using System; -using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Runtime.InteropServices; +using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; @@ -362,7 +360,7 @@ namespace FasdDesktopUi.Basics try { FormattingOptions options = new FormattingOptions() { ReferenceDate = DateTime.UtcNow.AddDays(_v.ReferenceDays), TimeZone = TimeZoneInfo.Local }; - RawValueFormatter.SetDefaultCulture(new System.Globalization.CultureInfo(cFasdCockpitConfig.Instance.SelectedLanguage)); + RawValueFormatter.SetDefaultCulture(cFasdCockpitConfig.Instance.SelectedCulture); RawValueFormatter.SetDefaultTimeZone(TimeZoneInfo.Local); var _strVal = RawValueFormatter.GetDisplayValue(_v.Value, _v.StateDefinition.DisplayType, options); @@ -468,5 +466,11 @@ namespace FasdDesktopUi.Basics #endregion + public static string GetShortDatePattern() + { + string datePattern = cFasdCockpitConfig.Instance.SelectedCulture.DateTimeFormat.ShortDatePattern; + datePattern = Regex.Replace(datePattern, @"(^|[/.\-\s])y{1,4}($|[/.\-\s]?)", ""); + return datePattern.Trim('/', '.', '-', ' '); + } } } diff --git a/FasdDesktopUi/Config/LanguageDefinitions.xml b/FasdDesktopUi/Config/LanguageDefinitions.xml index 3159dfb..739ae53 100644 --- a/FasdDesktopUi/Config/LanguageDefinitions.xml +++ b/FasdDesktopUi/Config/LanguageDefinitions.xml @@ -192,15 +192,15 @@ Auswählen - - Select - Auswählen - + + Select + Auswählen + - - Select - Auswählen - + + Select + Auswählen + Create ticket @@ -237,15 +237,15 @@ Es wurden keine Session für diesen Fall gefunden. - - There were no mobile device found for this case. - Es wurden keine mobilen Geräte für diesen Fall gefunden. - + + There were no mobile device found for this case. + Es wurden keine mobilen Geräte für diesen Fall gefunden. + - - There were no tickets found for this case. - Es wurden keine Tickets für diesen Fall gefunden. - + + There were no tickets found for this case. + Es wurden keine Tickets für diesen Fall gefunden. + @@ -334,14 +334,10 @@ - - You currently have an open support case. - How would you like to proceed? - - - Sie haben aktuell einen noch nicht abgeschlossenen Support Fall. - Wie möchten Sie fortfahren? - + You currently have an open support case. +How would you like to proceed? + Sie haben aktuell einen noch nicht abgeschlossenen Support Fall. +Wie möchten Sie fortfahren? @@ -473,7 +469,48 @@ Geschlossen - + + Ticket + Ticket + + + + Closed ticket + geschlossenes Ticket + + + + Ticket (not classified) + Ticket (nicht klassifiziert) + + + + Closed ticket (not classified) + geschlossenes Ticket (nicht klassifiziert) + + + + Incident + Störung + + + + Closed incident + geschlossene Störung + + + + Service request + Serviceanfrage + + + + Closed service request + geschlossene Serviceanfrage + + + + Ticket overview updated Ticketübersicht aktualisiert @@ -503,30 +540,30 @@ Eigene Tickets - - My incidents - Eigene Störungen - - - - My unassigned - Eigener Eingang - - - - Role tickets - Rollentickets - + + My incidents + Eigene Störungen + - - Role incidents - Rollenstörungen - - - - Role unassigned - Rolleneingang - + + My unassigned + Eigener Eingang + + + + Role tickets + Rollentickets + + + + Role incidents + Rollenstörungen + + + + Role unassigned + Rolleneingang + @@ -555,14 +592,10 @@ - - Do you really want to restart the F4SD Cockpit? - All open cases will be closed. - - - Wollen Sie das F4SD Cockpit wirklich schließen? - Alle offenen Fälle werden dabei geschlossen. - + Do you really want to restart the F4SD Cockpit? +All open cases will be closed. + Wollen Sie das F4SD Cockpit wirklich schließen? +Alle offenen Fälle werden dabei geschlossen. @@ -581,16 +614,12 @@ - - Changes to the language settings will not take effect until the environment is restarted. All open sessions will be lost. + Changes to the language settings will not take effect until the environment is restarted. All open sessions will be lost. - Do you want to restart the F4SD Cockpit now? - - - Änderungen an den Spracheinstellungen treten erst in Kraft, nachdem die Umgebung neu gestartet wurde. Alle offenen Fälle gehen dabei verloren. +Do you want to restart the F4SD Cockpit now? + Änderungen an den Spracheinstellungen treten erst in Kraft, nachdem die Umgebung neu gestartet wurde. Alle offenen Fälle gehen dabei verloren. - Wollen Sie das F4SD Cockpit jetzt neu starten? - +Wollen Sie das F4SD Cockpit jetzt neu starten? @@ -613,15 +642,16 @@ Position der Favoriten Leiste + + Use level progression system + Levelfortschritts-System verwenden + + - - F4SD® is a registered word mark of Consulting4IT GmbH. - All rights, including but not limited to, ownership, intellectual property, and exclusive usage of the trademark, are fully held by Consulting4IT GmbH. Any unauthorized use or imitation of the mark is legally prohibited and may lead to civil and criminal penalties. - - - F4SD® ist eine eingetragene Wortmarke der Consulting4IT GmbH. - Alle Rechte, einschließlich, aber nicht beschränkt auf, das Eigentum, das geistige Eigentum und die exklusive Nutzung der Marke, liegen vollständig bei der Consulting4IT GmbH. Jegliche unautorisierte Nutzung oder Nachahmung der Marke ist gesetzlich verboten und kann zu zivil- und strafrechtlichen Sanktionen führen. - + F4SD® is a registered word mark of Consulting4IT GmbH. +All rights, including but not limited to, ownership, intellectual property, and exclusive usage of the trademark, are fully held by Consulting4IT GmbH. Any unauthorized use or imitation of the mark is legally prohibited and may lead to civil and criminal penalties. + F4SD® ist eine eingetragene Wortmarke der Consulting4IT GmbH. +Alle Rechte, einschließlich, aber nicht beschränkt auf, das Eigentum, das geistige Eigentum und die exklusive Nutzung der Marke, liegen vollständig bei der Consulting4IT GmbH. Jegliche unautorisierte Nutzung oder Nachahmung der Marke ist gesetzlich verboten und kann zu zivil- und strafrechtlichen Sanktionen führen. @@ -718,6 +748,11 @@ Führe Quick Action aus. + + Start F4SD phoenix viewer. + Starte F4SD Phoenix Viewer. + + Load and run local script. Lade und führe lokales Skript aus. @@ -889,15 +924,15 @@ Die Quick Action <b>"{0}"</b> wurde durch F4SD remote auf dem Gerät <b>"{1}"</b> am {2} UTC <b>{3}</b>ausgeführt. - - The Quick Action '{0}' was {3}executed remotely by F4SD for the session '{1}' at {2} UTC. - Die Quick Action "{0}" wurde durch F4SD remote für die Session "{1}" am {2} UTC {3}ausgeführt. - + + The Quick Action '{0}' was {3}executed remotely by F4SD for the session '{1}' at {2} UTC. + Die Quick Action "{0}" wurde durch F4SD remote für die Session "{1}" am {2} UTC {3}ausgeführt. + - - The Quick Action <b>'{0}'</b> was <b>{3}</b>executed remotely by F4SD on the session <b>'{1}'</b> at {2} UTC. - Die Quick Action <b>"{0}"</b> wurde durch F4SD remote für die Session <b>"{1}"</b> am {2} UTC <b>{3}</b>ausgeführt. - + + The Quick Action <b>'{0}'</b> was <b>{3}</b>executed remotely by F4SD on the session <b>'{1}'</b> at {2} UTC. + Die Quick Action <b>"{0}"</b> wurde durch F4SD remote für die Session <b>"{1}"</b> am {2} UTC <b>{3}</b>ausgeführt. + The Quick Action '{0}' was {3}executed local by F4SD for the device '{1}' at {2} UTC. @@ -1131,25 +1166,17 @@ - - Phone support was disabled. - Enable phone support first. - - - Telefonie-Unterstützung wurde deaktiviert. - Aktivieren Sie zuerst die Telefon-Unterstützung. - + Phone support was disabled. +Enable phone support first. + Telefonie-Unterstützung wurde deaktiviert. +Aktivieren Sie zuerst die Telefon-Unterstützung. - - SwyxIt! native was enabled. - Disable SwyxIt! native first. - - - Natives SwyxIt! wurde aktiviert. - Deaktivieren Sie zuerst natives SwyxIt!. - + SwyxIt! native was enabled. +Disable SwyxIt! native first. + Natives SwyxIt! wurde aktiviert. +Deaktivieren Sie zuerst natives SwyxIt!. @@ -1163,16 +1190,12 @@ - - Changes to the phone support will not take effect until the environment is restarted. All open sessions will be lost. + Changes to the phone support will not take effect until the environment is restarted. All open sessions will be lost. - Do you want to restart the F4SD Cockpit now? - - - Änderungen an Telefonie-Unterstützung treten erst in Kraft, nachdem die Umgebung neu gestartet wurde. Alle offenen Fälle gehen dabei verloren. +Do you want to restart the F4SD Cockpit now? + Änderungen an Telefonie-Unterstützung treten erst in Kraft, nachdem die Umgebung neu gestartet wurde. Alle offenen Fälle gehen dabei verloren. - Wollen Sie das F4SD Cockpit jetzt neu starten? - +Wollen Sie das F4SD Cockpit jetzt neu starten? @@ -1223,16 +1246,12 @@ - - Changes in the Matrix42 authentification will not take effect until the environment is restarted. All open sessions will be lost. + Changes in the Matrix42 authentification will not take effect until the environment is restarted. All open sessions will be lost. - Do you want to restart the F4SD Cockpit now? - - - Die Änderungen bei der Matrix42 Anmeldung treten erst in Kraft, nachdem die Umgebung neu gestartet wurde. Alle offenen Fälle gehen dabei verloren. +Do you want to restart the F4SD Cockpit now? + Die Änderungen bei der Matrix42 Anmeldung treten erst in Kraft, nachdem die Umgebung neu gestartet wurde. Alle offenen Fälle gehen dabei verloren. - Wollen Sie das F4SD Cockpit jetzt neu starten? - +Wollen Sie das F4SD Cockpit jetzt neu starten? @@ -1359,8 +1378,8 @@ - Close case - Fall abschließen + Document case + Fall dokumentieren @@ -1715,13 +1734,36 @@ Tickets - - Incidents - Störungen - - - - Unassigned - Eingang - - + + Incidents + Störungen + + + + Unassigned + Eingang + + + + Let's continue! + Weiter geht's! + + + + Remote connection is currenlty only available for computers + Remote Verbindung wird derzeit nur für Computer unterstützt + + + + Remote connection services are currently unavailable + Die Dienste für Remote Verbindung sind derzeit gestört + + + Logoff Pending + Abmeldung bevorstehend + + + Urgent maintenance required. You will be logged out shortly. Please save your data. + Eine dringende Wartung muss durchgeführt werden. Sie werden in Kürze abgemeldet. Bitte speichern Sie Ihre Daten. + + diff --git a/FasdDesktopUi/F4SD-Cockpit-Client.csproj b/FasdDesktopUi/F4SD-Cockpit-Client.csproj index d4079b7..d184b92 100644 --- a/FasdDesktopUi/F4SD-Cockpit-Client.csproj +++ b/FasdDesktopUi/F4SD-Cockpit-Client.csproj @@ -94,40 +94,40 @@ ..\packages\C4IT.F4SD.DisplayFormatting.1.0.0\lib\netstandard2.0\C4IT.F4SD.DisplayFormatting.dll - - ..\packages\C4IT.F4SD.SupportCaseProtocoll.1.0.0\lib\netstandard2.0\C4IT.F4SD.SupportCaseProtocoll.dll + + ..\packages\C4IT.F4SD.SupportCaseProtocoll.1.0.1\lib\netstandard2.0\C4IT.F4SD.SupportCaseProtocoll.dll ..\packages\MaterialIcons.1.0.3\lib\MaterialIcons.dll - - ..\packages\Microsoft.Bcl.AsyncInterfaces.10.0.2\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll + + ..\packages\Microsoft.Bcl.AsyncInterfaces.10.0.3\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll - - ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.10.0.2\lib\net462\Microsoft.Extensions.DependencyInjection.Abstractions.dll + + ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.10.0.3\lib\net462\Microsoft.Extensions.DependencyInjection.Abstractions.dll - - ..\packages\Microsoft.Extensions.Logging.Abstractions.10.0.2\lib\net462\Microsoft.Extensions.Logging.Abstractions.dll + + ..\packages\Microsoft.Extensions.Logging.Abstractions.10.0.3\lib\net462\Microsoft.Extensions.Logging.Abstractions.dll - - ..\packages\Microsoft.Web.WebView2.1.0.3650.58\lib\net462\Microsoft.Web.WebView2.Core.dll + + ..\packages\Microsoft.Web.WebView2.1.0.3800.47\lib\net462\Microsoft.Web.WebView2.Core.dll - - ..\packages\Microsoft.Web.WebView2.1.0.3650.58\lib\net462\Microsoft.Web.WebView2.WinForms.dll + + ..\packages\Microsoft.Web.WebView2.1.0.3800.47\lib\net462\Microsoft.Web.WebView2.WinForms.dll - - ..\packages\Microsoft.Web.WebView2.1.0.3650.58\lib\net462\Microsoft.Web.WebView2.Wpf.dll + + ..\packages\Microsoft.Web.WebView2.1.0.3800.47\lib\net462\Microsoft.Web.WebView2.Wpf.dll - ..\packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll + ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll ..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll - - ..\packages\System.Diagnostics.DiagnosticSource.10.0.2\lib\net462\System.Diagnostics.DiagnosticSource.dll + + ..\packages\System.Diagnostics.DiagnosticSource.10.0.3\lib\net462\System.Diagnostics.DiagnosticSource.dll @@ -177,7 +177,12 @@ + + + + + @@ -209,12 +214,15 @@ + + + - - + + @@ -244,6 +252,7 @@ + @@ -252,6 +261,7 @@ + @@ -281,6 +291,18 @@ ComboBoxPageAble.xaml + + CustomMenuItemToolTipTemplate.xaml + + + LevelTracker.xaml + + + FooterButton.xaml + + + InformationClassSearchBar.xaml + DesktopWidgetPageView.xaml @@ -383,6 +405,9 @@ CustomMessageBox.xaml + + LevelUpPage.xaml + RawHealthCardValuesPage.xaml @@ -501,10 +526,26 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile @@ -751,6 +792,10 @@ FunctionMarker.xaml + + Designer + MSBuild:Compile + MSBuild:Compile Designer @@ -971,6 +1016,10 @@ {bab63a6a-1524-435d-9f96-7a30b6ee0624} F4SD-AdaptableIcon + + {b59e7dfd-81c8-4d98-ace5-a8f1fc51f7a8} + F4SD-Gamification + {7793f281-b226-4e20-b6f6-5d53d70f1dc1} F4SD-Logging @@ -1058,11 +1107,21 @@ taskkill -im "F4SD-Cockpit-Client.exe" -f -FI "STATUS eq RUNNING" copy "$(ProjectDir)..\..\C4IT FASD\_Common\XmlSchemas\LanguageDefinitions.xsd" "$(ProjectDir)Config" - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + - + + + + + + + + \ No newline at end of file diff --git a/FasdDesktopUi/Pages/AdvancedSearchPage/AdvancedSearchPageView.xaml b/FasdDesktopUi/Pages/AdvancedSearchPage/AdvancedSearchPageView.xaml index c7c50b5..df8f12b 100644 --- a/FasdDesktopUi/Pages/AdvancedSearchPage/AdvancedSearchPageView.xaml +++ b/FasdDesktopUi/Pages/AdvancedSearchPage/AdvancedSearchPageView.xaml @@ -28,15 +28,15 @@ - + MultiButtonText, string Caption = null, enumHealthCardStateLevel Level = enumHealthCardStateLevel.None, Window Owner = null, bool TopMost = false, bool CenterScreen = false, int MaxWidth = -1) - { - var messageBox = InitializeMessageBox(Message, Caption, Level, Owner, false, false, MultiButtonText, TopMost, CenterScreen); - if (messageBox == null) - return -1; - if (MaxWidth > 0) - messageBox.MaxWidth = MaxWidth; - messageBox.ShowDialog(); - return messageBox.ResultIndex; - } + public static int Show(string Message, List MultiButtonText, string Caption = null, enumHealthCardStateLevel Level = enumHealthCardStateLevel.None, Window Owner = null, bool TopMost = false, bool CenterScreen = false, int MaxWidth = -1) + { + var messageBox = InitializeMessageBox(Message, Caption, Level, Owner, false, false, MultiButtonText, TopMost, CenterScreen); + if (messageBox == null) + return -1; + if (MaxWidth > 0) + messageBox.MaxWidth = MaxWidth; + messageBox.ShowDialog(); + return messageBox.ResultIndex; + } public static bool? Show(string Message, string Caption = null, enumHealthCardStateLevel Level = enumHealthCardStateLevel.None, Window Owner = null, bool HasYesNoButtons = false, bool HasYesNoText = false, bool TopMost = false, bool CenterScreen = false) { diff --git a/FasdDesktopUi/Pages/DetailsPage/DetailsPageView.xaml b/FasdDesktopUi/Pages/DetailsPage/DetailsPageView.xaml index d585357..4a6190f 100644 --- a/FasdDesktopUi/Pages/DetailsPage/DetailsPageView.xaml +++ b/FasdDesktopUi/Pages/DetailsPage/DetailsPageView.xaml @@ -8,6 +8,7 @@ xmlns:ico="clr-namespace:FasdDesktopUi.Basics.UserControls.AdaptableIcon;assembly=F4SD-AdaptableIcon" xmlns:uc="clr-namespace:FasdDesktopUi.Pages.DetailsPage.UserControls" xmlns:buc="clr-namespace:FasdDesktopUi.Basics.UserControls" + xmlns:bucg="clr-namespace:FasdDesktopUi.Basics.UserControls.Gamification" xmlns:quc="clr-namespace:FasdDesktopUi.Basics.UserControls.QuickTip" xmlns:vm="clr-namespace:FasdDesktopUi.Pages.DetailsPage.ViewModels" xmlns:vc="clr-namespace:FasdDesktopUi.Basics.Converter" @@ -84,93 +85,99 @@ Grid.Row="0" Panel.ZIndex="2" PreviewMouseLeftButtonDown="Header_PreviewMouseLeftButtonDown"> - + + - - - - - - - + + + + + + + + - - - - - - - - - - - - + + + + + + + + + - - + - + + + + + + + + + + + Panel.ZIndex="1" /> + IsVisibleChanged="DynamicElement_IsVisibleChanged" + SearchValueChanged="QuickActionSelectorUc_SearchValueChanged" /> @@ -246,13 +254,6 @@ Visibility="Collapsed" IsVisibleChanged="DynamicElement_IsVisibleChanged" /> - - + + - + + + - diff --git a/FasdDesktopUi/Pages/DetailsPage/DetailsPageView.xaml.cs b/FasdDesktopUi/Pages/DetailsPage/DetailsPageView.xaml.cs index 72e53ec..eeff376 100644 --- a/FasdDesktopUi/Pages/DetailsPage/DetailsPageView.xaml.cs +++ b/FasdDesktopUi/Pages/DetailsPage/DetailsPageView.xaml.cs @@ -1,37 +1,37 @@ -using System; +using C4IT.FASD.Base; +using C4IT.FASD.Cockpit.Communication; +using C4IT.MultiLanguage; +using FasdDesktopUi.Basics; +using FasdDesktopUi.Basics.Converter; +using FasdDesktopUi.Basics.CustomEvents; +using FasdDesktopUi.Basics.Helper; +using FasdDesktopUi.Basics.Models; +using FasdDesktopUi.Basics.Services.SupportCase.Controllers; +using FasdDesktopUi.Basics.UiActions; +using FasdDesktopUi.Basics.UserControls; +using FasdDesktopUi.Basics.UserControls.QuickTip; +using FasdDesktopUi.Pages.DetailsPage.Models; +using FasdDesktopUi.Pages.DetailsPage.UserControls; +using FasdDesktopUi.Pages.DetailsPage.ViewModels; +using FasdDesktopUi.Pages.SettingsPage; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Threading; +using System.Threading.Tasks; using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; using System.Windows.Input; using System.Windows.Interop; -using System.Windows.Media.Effects; -using System.Windows.Controls; -using System.Threading.Tasks; -using System.Windows.Media.Animation; -using System.Windows.Threading; -using System.Windows.Documents; using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Media.Effects; using System.Windows.Shell; - -using FasdDesktopUi.Basics; -using FasdDesktopUi.Basics.UiActions; -using FasdDesktopUi.Basics.UserControls; -using FasdDesktopUi.Basics.Models; -using FasdDesktopUi.Basics.Helper; -using FasdDesktopUi.Pages.SettingsPage; -using FasdDesktopUi.Pages.DetailsPage.Models; -using FasdDesktopUi.Pages.DetailsPage.ViewModels; -using FasdDesktopUi.Pages.DetailsPage.UserControls; - -using C4IT.FASD.Base; -using C4IT.MultiLanguage; - +using System.Windows.Threading; using static C4IT.Logging.cLogManager; -using F4SD_AdaptableIcon.Enums; -using FasdDesktopUi.Basics.CustomEvents; -using FasdDesktopUi.Basics.Converter; -using FasdDesktopUi.Basics.Services.SupportCase.Controllers; +using static System.Windows.Forms.AxHost; namespace FasdDesktopUi.Pages.DetailsPage { @@ -107,7 +107,7 @@ namespace FasdDesktopUi.Pages.DetailsPage case SettingsPageBase _: case CustomMessageBox.CustomMessageBox _: case TicketCompletion.TicketCompletion _: - case SearchBar _: + case InformationClassSearchBar _: case SuccessPage.SuccessPage _: case BlurInvokerContainer _: return true; @@ -201,8 +201,8 @@ namespace FasdDesktopUi.Pages.DetailsPage } private void HandleAvailableRelationsAdded(object sender, RelationEventArgs e) - { - } + => Dispatcher.Invoke(() => NavigationHeadingUc.HeadingData = _supportCaseController.GetHeadingData().ToList()); + private void HandleFocusedRelationsChanged(object sender, RelationEventArgs e) { @@ -234,15 +234,15 @@ namespace FasdDesktopUi.Pages.DetailsPage isDataChangedEventRunning = true; - Dispatcher.Invoke(() => + Dispatcher.Invoke(async () => { if (QuickActionDecorator.Child is DataCanvas dataCanvas) - Dispatcher.Invoke(async () => await dataCanvas.UpdateDataAsync()); + _ = Dispatcher.Invoke(async () => await dataCanvas.UpdateDataAsync()); if (WidgetCollection.WidgetDataList is null || WidgetCollection.WidgetDataList.Count == 0) - WidgetCollection.WidgetDataList = _supportCaseController?.GetWidgetData(); + WidgetCollection.WidgetDataList = _supportCaseController?.GetWidgetsData(); - WidgetCollection.UpdateWidgetData(_supportCaseController?.GetWidgetData()); + WidgetCollection.UpdateWidgetData(_supportCaseController?.GetWidgetsData()); if (DataHistoryCollectionUserControl.HistoryDataList is null || DataHistoryCollectionUserControl.HistoryDataList.Count == 0) DataHistoryCollectionUserControl.HistoryDataList = _supportCaseController?.GetHistoryData(); @@ -255,7 +255,9 @@ namespace FasdDesktopUi.Pages.DetailsPage CustomizableSectionUc.UpdateContainerCollection(_supportCaseController?.GetContainerData()); if (this.DataContext is DetailsPageViewModel viewModel) - viewModel.MenuBarData = _supportCaseController?.GetMenuBarData(); + viewModel.MenuBarData = _supportCaseController?.GetMenuData().ToList(); + + UpdateQuickActionSelectorVisibility(); if (_lastDesiredHeightOfWidgetCollection != WidgetCollection.DesiredSize.Height) { @@ -263,6 +265,10 @@ namespace FasdDesktopUi.Pages.DetailsPage MainGrid.InvalidateMeasure(); MainGrid.UpdateLayout(); } + + var footerMenuData = _supportCaseController.GetMenuData().FirstOrDefault(data => data.UiAction is UiNativeQuickAction); + FooterBtn.QuickAction = footerMenuData?.UiAction as cUiQuickAction; + FooterBtn.LabelText = footerMenuData?.MenuText; }); if (shouldReRunDataChangedEvent) @@ -275,6 +281,41 @@ namespace FasdDesktopUi.Pages.DetailsPage } } + private void UpdateQuickActionSelectorVisibility() + { + try + { + cHealthCard selectedHealthcard = _supportCaseController.SupportCaseDataProviderArtifact.HealthCardDataHelper.SelectedHealthCard; + bool showHistorySection = selectedHealthcard.CategoriesHistory?.StateCategories != null && selectedHealthcard.CategoriesHistory.StateCategories.Count > 0; + + if (showHistorySection && cFasdCockpitConfig.Instance.IsHistoryQuickActionSelectorVisible) + { + QuickActionSelectorUc.IsLocked = cFasdCockpitConfig.Instance.IsHistoryQuickActionSelectorVisible; + MoreButtonClickedAction(); + } + else if (!showHistorySection && cFasdCockpitConfig.Instance.IsCustomizableQuickActionSelectorVisible) + { + var ticketMenuData = _supportCaseController.GetMenuData().FirstOrDefault(menuData => menuData.MenuText == "Ticket"); + + if (ticketMenuData != null) + if (ticketMenuData is cMenuDataContainer containerMenuData) + { + QuickActionSelectorUc.QuickActionSelectorHeading = containerMenuData.MenuText; + QuickActionSelectorUc.QuickActionList = containerMenuData.SubMenuData; + } + + QuickActionSelectorUc.IsLocked = cFasdCockpitConfig.Instance.IsCustomizableQuickActionSelectorVisible; + + if (cFasdCockpitConfig.Instance.IsCustomizableQuickActionSelectorVisible) + QuickActionSelectorUc.Visibility = Visibility.Visible; + } + } + catch (Exception ex) + { + LogException(ex); + } + } + /// /// Sets the visibility of History and Customizable Section based on the currently selected Healthcard. /// @@ -597,6 +638,7 @@ namespace FasdDesktopUi.Pages.DetailsPage // define drawing area UIElement drawingArea = this; + switch (e.UiAction) { case cChangeHealthCardAction _: @@ -604,6 +646,11 @@ namespace FasdDesktopUi.Pages.DetailsPage case UiShowRawHealthcardValues _: break; case cUiQuickAction _: + if (!(e.OriginalSource is QuickTipStep) && QuickTipStatusMonitorUc.IsVisible && !QuickTipStatusMonitorUc.TryCancelQuickTip()) + return; + drawingArea = QuickActionDecorator; + ToggleHorizontalCollapse(true, true); + break; case cShowRecommendationAction _: case cShowDetailedDataAction _: drawingArea = QuickActionDecorator; @@ -678,7 +725,7 @@ namespace FasdDesktopUi.Pages.DetailsPage { IconTimerPanel.HorizontalAlignment = HorizontalAlignment.Right; IconTimerPanel.Children.Remove(F4SDIcon); - IconTimerPanel.Children.Insert(1, F4SDIcon); + IconTimerPanel.Children.Insert(IconTimerPanel.Children.Count, F4SDIcon); } else { @@ -687,6 +734,7 @@ namespace FasdDesktopUi.Pages.DetailsPage IconTimerPanel.Children.Insert(0, F4SDIcon); } + FooterBtn.HorizontalAlignment = cFasdCockpitConfig.Instance.Global.FavouriteBarAlignment == enumF4sdHorizontalAlignment.Center ? HorizontalAlignment.Right : HorizontalAlignment.Center; MenuBarUserControl.HorizontalAlignment = InternalEnumConverter.GetHorizontalAlignment(cFasdCockpitConfig.Instance.Global.FavouriteBarAlignment); } catch (Exception E) @@ -699,6 +747,7 @@ namespace FasdDesktopUi.Pages.DetailsPage { UpdateHistoryWidth(); UpdateFooterPositions(); + LevelTrackerUc.Visibility = cFasdCockpitConfig.Instance.Global.UseGamification ? Visibility.Visible : Visibility.Collapsed; } private void ApiConnectionStatusChanged(cConnectionStatusHelper.enumOnlineStatus? Status) @@ -923,13 +972,27 @@ namespace FasdDesktopUi.Pages.DetailsPage break; } + return; + } + + // Close Quick Action Section + if (e.Key == Key.Escape && QuickActionSelectorUc.Visibility == Visibility.Visible) + { + CloseQuickActionSelector(); return; } - if (FocusManager.GetFocusedElement(this) is TextBox) - return; + bool isTextInputFocused = FocusManager.GetFocusedElement(this) is TextBox || FocusManager.GetFocusedElement(this) is RichTextBox; - if (FocusManager.GetFocusedElement(this) is RichTextBox) + // Open Quick Action Section + if (e.Key == Key.Q && !isTextInputFocused) + { + MoreButtonClickedAction(); + e.Handled = true; + return; + } + + if (isTextInputFocused) return; List unpinnedDataHistories = DataHistoryCollectionUserControl.HistorySectionControls.Where(x => !x.IsVerticalExpandLocked).ToList(); @@ -970,9 +1033,6 @@ namespace FasdDesktopUi.Pages.DetailsPage if (Keyboard.Modifiers == ModifierKeys.Control) SearchButtonClickedAction(); break; - case Key.Q: - MoreButtonClickedAction(); - break; case Key.NumPad0: case Key.D0: if (Keyboard.Modifiers != ModifierKeys.Control) @@ -1177,8 +1237,6 @@ namespace FasdDesktopUi.Pages.DetailsPage { tempSubMenuList.Add(new cMenuDataBase(copyTemplate.Value)); } - - tempMoreQuickActionList.Insert(0, new cMenuDataContainer() { MenuText = cMultiLanguageSupport.GetItem("QuickAction.CopyContent"), MenuIcon = new F4SD_AdaptableIcon.IconData(enumInternIcons.menuBar_copy), UiAction = new cSubMenuAction(true) { Name = cMultiLanguageSupport.GetItem("QuickAction.CopyContent"), SubMenuData = tempSubMenuList }, SubMenuData = tempSubMenuList }); } QuickActionSelectorUc.QuickActionList = tempMoreQuickActionList; @@ -1187,6 +1245,10 @@ namespace FasdDesktopUi.Pages.DetailsPage ToggleHorizontalCollapse(true); QuickActionSelectorUc.Visibility = Visibility.Visible; QuickActionSelectorUc.CloseButtonClickedAction = BlurBorder_Click; + QuickActionSelectorUc.Search.ActivateManualSearch(); + QuickActionSelectorUc.Search.FocusInput(); + QuickActionSelectorUc.Search.Clear(); + } catch (Exception E) { @@ -1256,6 +1318,7 @@ namespace FasdDesktopUi.Pages.DetailsPage notepad.Visibility = Visibility.Collapsed; NotepadVisibility = false; + ResetWindowFocus(); ChangeNotepadNotification(); } else @@ -1451,6 +1514,29 @@ namespace FasdDesktopUi.Pages.DetailsPage ReinitializeNotepad(); DataHistoryCollectionUserControl.ToggleVerticalCollapseDetails(true); UpdateHistoryWidth(); + + Task.Run(async () => + { + if (await cFasdCockpitCommunicationBase.Instance.RemoteDesktopManager.IsRemoteDesktopCommunicationAvailable()) + Dispatcher.Invoke(() => FooterBtn.Visibility = Visibility.Visible); + }); + } + catch (Exception E) + { + LogException(E); + } + } + + private void ResetWindowFocus() + { + try + { + Window window = Window.GetWindow(this); + if (window == null) + return; + + FocusManager.SetFocusedElement(window, window); + Keyboard.Focus(window); } catch (Exception E) { @@ -1701,5 +1787,30 @@ namespace FasdDesktopUi.Pages.DetailsPage QuickActionDecorator.Visibility = Visibility.Collapsed; } + private void QuickActionSelectorUc_SearchValueChanged(object sender, string e) + { + if (string.IsNullOrWhiteSpace(e)) + { + QuickActionSelectorUc.QuickActionList = _supportCaseController.GetMenuData().ToList(); + QuickActionSelectorUc.QuickActionSelectorHeading = "Quick Actions"; + return; + } + + QuickActionSelectorUc.QuickActionList = _supportCaseController.GetFilteredMenuData(new MenuDataFilter(e)).ToList(); + + } + + private void CloseQuickActionSelector() + { + try + { + QuickActionSelectorUc.CloseButton_Click(); + QuickActionSelectorUc.Search.Clear(); + } + catch (Exception E) + { + LogException(E); + } + } } } diff --git a/FasdDesktopUi/Pages/DetailsPage/UserControls/DataHistory/DetailsPageDataHistoryValueColumn.xaml.cs b/FasdDesktopUi/Pages/DetailsPage/UserControls/DataHistory/DetailsPageDataHistoryValueColumn.xaml.cs index 544c234..87c7626 100644 --- a/FasdDesktopUi/Pages/DetailsPage/UserControls/DataHistory/DetailsPageDataHistoryValueColumn.xaml.cs +++ b/FasdDesktopUi/Pages/DetailsPage/UserControls/DataHistory/DetailsPageDataHistoryValueColumn.xaml.cs @@ -72,7 +72,7 @@ namespace FasdDesktopUi.Pages.DetailsPage.UserControls var updatedColumnValues = (DetailsPageDataHistoryColumnModel)e.NewValue; _me.UpdateColumnSize(updatedColumnValues.ColumnValues.Count - _me.MainGrid.RowDefinitions.Count); - _me.RefreshColumnHeader(updatedColumnValues.Content); + _me.ColumnHeaderTextBlock.Text = updatedColumnValues.Content; _me.RefreshColumnStatusIcon(updatedColumnValues.HighlightColor); _me.RefreshValueSection(updatedColumnValues); } @@ -155,7 +155,8 @@ namespace FasdDesktopUi.Pages.DetailsPage.UserControls border.ClearValue(TagProperty); border.SetResourceReference(StyleProperty, "DetailsPage.DataHistory.ValueBorder"); break; - }; + } + ; border.Tag = valueInfo?.ThresholdValues; } @@ -163,7 +164,7 @@ namespace FasdDesktopUi.Pages.DetailsPage.UserControls { LogException(E); } - + } #region Initialize Controls @@ -276,22 +277,6 @@ namespace FasdDesktopUi.Pages.DetailsPage.UserControls #region Refresh Values - private void RefreshColumnHeader(string columnHeader) - { - string headerContent; - - if (int.TryParse(columnHeader, out int dayIndex)) - { - CultureInfo culture = new CultureInfo(cMultiLanguageSupport.CurrentLanguage); - string customDateFormat = cMultiLanguageSupport.GetItem("Global.Date.Format.ShortDateWithDay", "ddd. dd.MM."); - headerContent = dayIndex == 0 ? cMultiLanguageSupport.GetItem("Global.Date.Today", DateTime.Now.ToString(customDateFormat, culture)) : DateTime.Today.AddDays(-dayIndex).ToString(customDateFormat, culture); - } - else - headerContent = columnHeader; - - ColumnHeaderTextBlock.Text = headerContent; - } - private void RefreshColumnStatusIcon(enumHighlightColor? statusColor) { ColumnStatusIcon.IconWidth = 25; @@ -386,7 +371,7 @@ namespace FasdDesktopUi.Pages.DetailsPage.UserControls default: valueRowTextBlock.SetResourceReference(ForegroundProperty, "FontColor.DetailsPage.DataHistory.Value"); break; - }; + } valueRow.Visibility = Visibility.Visible; valueRow.Opacity = 1; diff --git a/FasdDesktopUi/Pages/DetailsPage/UserControls/DetailsPageNavigationHeading.xaml.cs b/FasdDesktopUi/Pages/DetailsPage/UserControls/DetailsPageNavigationHeading.xaml.cs index 4da9f4f..7fba7dc 100644 --- a/FasdDesktopUi/Pages/DetailsPage/UserControls/DetailsPageNavigationHeading.xaml.cs +++ b/FasdDesktopUi/Pages/DetailsPage/UserControls/DetailsPageNavigationHeading.xaml.cs @@ -18,7 +18,6 @@ using C4IT.MultiLanguage; using C4IT.FASD.Base; using static C4IT.Logging.cLogManager; -using C4IT.FASD.Cockpit.Communication; using FasdDesktopUi.Basics.Services.SupportCase.Controllers; namespace FasdDesktopUi.Pages.DetailsPage.UserControls @@ -37,7 +36,7 @@ namespace FasdDesktopUi.Pages.DetailsPage.UserControls if (value != null) { UpdateHeaderHighlights(); - SetHeadingVisibility(); + _ = SetHeadingVisibilityAsync(); } } } @@ -110,7 +109,7 @@ namespace FasdDesktopUi.Pages.DetailsPage.UserControls } } - private async Task SetHeadingVisibility() + private async Task SetHeadingVisibilityAsync() { try { @@ -667,19 +666,17 @@ namespace FasdDesktopUi.Pages.DetailsPage.UserControls List quickActionList = new List(); - if (dataProvider.CaseRelations != null && dataProvider.CaseRelations.TryGetValue(swapCaseData.SelectedCaseInformationClass, out var storedRelations)) + foreach (var storedRelation in SupportCaseController.GetRelationsOf(swapCaseData.SelectedCaseInformationClass)) { - foreach (var storedRelation in storedRelations) + bool isMatchingRelation = IsMatchingRelation(storedRelation, swapCaseData.HeadingDatas); + quickActionList.Add(new cMenuDataSearchRelation(storedRelation) { - bool isMatchingRelation = IsMatchingRelation(storedRelation, swapCaseData.HeadingDatas); - quickActionList.Add(new cMenuDataSearchRelation(storedRelation) - { - IsMatchingRelation = isMatchingRelation, - IsUsedForCaseEnrichment = true, - UiAction = new cChangeHealthCardAction(storedRelation, supportCaseController) - }); - } + IsMatchingRelation = isMatchingRelation, + IsUsedForCaseEnrichment = true, + UiAction = new cChangeHealthCardAction(storedRelation, supportCaseController) + }); } + if (quickActionList.Count > 0) customMenu.MenuDataList = quickActionList; } diff --git a/FasdDesktopUi/Pages/DetailsPage/UserControls/DetailsPageWidget.xaml.cs b/FasdDesktopUi/Pages/DetailsPage/UserControls/DetailsPageWidget.xaml.cs index 4791ec0..8c30c04 100644 --- a/FasdDesktopUi/Pages/DetailsPage/UserControls/DetailsPageWidget.xaml.cs +++ b/FasdDesktopUi/Pages/DetailsPage/UserControls/DetailsPageWidget.xaml.cs @@ -414,7 +414,7 @@ namespace FasdDesktopUi.Pages.DetailsPage.UserControls if (Data.UiActionTitle is cShowDetailedDataAction) functionMarkerEntry.SelectedIcon = enumInternIcons.misc_dot; - else if (Data.UiActionTitle is cUiQuickAction || Data.UiActionTitle is cSubMenuAction) + else if (Data.UiActionTitle is cUiQuickAction || Data.UiActionTitle is cSubMenuAction || Data.UiActionTitle is cUiQuickTipAction) functionMarkerEntry.SelectedIcon = enumInternIcons.misc_functionBolt; } else diff --git a/FasdDesktopUi/Pages/LevelUpPage/LevelUpPage.xaml b/FasdDesktopUi/Pages/LevelUpPage/LevelUpPage.xaml new file mode 100644 index 0000000..034b6d8 --- /dev/null +++ b/FasdDesktopUi/Pages/LevelUpPage/LevelUpPage.xaml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + Level Up! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FasdDesktopUi/Pages/LevelUpPage/LevelUpPage.xaml.cs b/FasdDesktopUi/Pages/LevelUpPage/LevelUpPage.xaml.cs new file mode 100644 index 0000000..2173f6e --- /dev/null +++ b/FasdDesktopUi/Pages/LevelUpPage/LevelUpPage.xaml.cs @@ -0,0 +1,91 @@ +using System; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Animation; + +namespace FasdDesktopUi.Pages.LevelUpPage +{ + public partial class LevelUpPage : Window + { + public int NewLevel + { + get { return (int)GetValue(NewLevelProperty); } + set { SetValue(NewLevelProperty, value); } + } + + public static readonly DependencyProperty NewLevelProperty = + DependencyProperty.Register(nameof(NewLevel), typeof(int), typeof(LevelUpPage), new PropertyMetadata(0, new PropertyChangedCallback(HandleLevelChanged))); + + private static void HandleLevelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (!(d is LevelUpPage levelupPage)) + return; + + levelupPage.LastLevelTextBlock.Text = (levelupPage.NewLevel - 1).ToString(); + levelupPage.NewLevelTextBlock.Text = levelupPage.NewLevel.ToString(); + } + + public string LevelTitle + { + get { return (string)GetValue(LevelTitleProperty); } + set { SetValue(LevelTitleProperty, value); } + } + + public static readonly DependencyProperty LevelTitleProperty = + DependencyProperty.Register(nameof(LevelTitle), typeof(string), typeof(LevelUpPage), new PropertyMetadata("string.Empty")); + + + public LevelUpPage() + { + InitializeComponent(); + } + + private async Task AnimateText() + { + await Task.Delay(900); + + double animationDistance = NewLevelTextBlock.ActualHeight + NewLevelTextBlock.Margin.Top + NewLevelTextBlock.Margin.Bottom; + + Duration duration = TimeSpan.FromMilliseconds(500); + + DoubleAnimation lowerAnimation = new DoubleAnimation + { + From = 0, + To = -animationDistance, + Duration = duration, + EasingFunction = new CubicEase + { + EasingMode = EasingMode.EaseInOut + } + }; + + DoubleAnimation upperAnimation = new DoubleAnimation + { + From = 0, + To = -animationDistance, + Duration = duration, + EasingFunction = new CubicEase + { + EasingMode = EasingMode.EaseInOut + } + }; + + LowerTransform.BeginAnimation(TranslateTransform.YProperty, lowerAnimation); + UpperTransform.BeginAnimation(TranslateTransform.YProperty, upperAnimation); + } + + private async void HandleVisibilityChanged(object sender, DependencyPropertyChangedEventArgs e) + { + if (Visibility != Visibility.Visible) + return; + + await AnimateText(); + } + + private void Continue_Click(object sender, RoutedEventArgs e) + { + Close(); + } + } +} diff --git a/FasdDesktopUi/Pages/SearchPage/SearchPageView.xaml b/FasdDesktopUi/Pages/SearchPage/SearchPageView.xaml index 5dfda04..d8ab02a 100644 --- a/FasdDesktopUi/Pages/SearchPage/SearchPageView.xaml +++ b/FasdDesktopUi/Pages/SearchPage/SearchPageView.xaml @@ -183,12 +183,12 @@ - + diff --git a/FasdDesktopUi/Pages/SearchPage/SearchPageView.xaml.cs b/FasdDesktopUi/Pages/SearchPage/SearchPageView.xaml.cs index 7fd337f..2f9df92 100644 --- a/FasdDesktopUi/Pages/SearchPage/SearchPageView.xaml.cs +++ b/FasdDesktopUi/Pages/SearchPage/SearchPageView.xaml.cs @@ -197,8 +197,8 @@ namespace FasdDesktopUi.Pages.SearchPage cSearchManager.ResolveRelations(searchHistoryEntry.Relations); ILookup relationsLookup = searchHistoryEntry.Relations - .OrderBy(r => r.UsingLevel) - .ThenBy(r => r.LastUsed) + .OrderBy(r => r.LastUsed) + .ThenBy(r => r.UsingLevel) .ToLookup(GetInformationClass, r => GetMenuData(r, relationService)); Dispatcher.Invoke(() => @@ -799,7 +799,7 @@ namespace FasdDesktopUi.Pages.SearchPage Dispatcher.Invoke(() => { var first = e.RelatedTo.FirstOrDefault(); - var relationSearchResult = new cSearchHistorySearchResultEntry(first.DisplayName, first.DisplayName, e.RelatedTo.ToList(), e.StagedResultRelations.Relations.ToList(), this); + var relationSearchResult = new cSearchHistorySearchResultEntry(first.DisplayName, first.Name, e.RelatedTo.ToList(), e.StagedResultRelations.Relations.ToList(), this, _relationService); ShowSearchRelations(relationSearchResult, e.RelationService, this); UpdatePendingInformationClasses(e.StagedResultRelations.PendingInformationClasses); @@ -1033,7 +1033,7 @@ namespace FasdDesktopUi.Pages.SearchPage private bool TryOpenTicketOverviewRelationExternally(cF4sdApiSearchResultRelation relation) { - return TicketDeepLinkHelper.TryOpenTicketRelationExternally(relation); + return TicketExternalLinkHelper.TryOpenTicketRelationExternally(relation); } private Task RunTicketSearchAsync(string ticketName, Guid ticketId, string userName, string sids, bool suppressUi = false) @@ -1461,7 +1461,8 @@ namespace FasdDesktopUi.Pages.SearchPage header, new List(), relations, - this + this, + _relationService ) { isSeen = true }; _ticketOverviewHistoryEntries.Add(entry); @@ -1695,7 +1696,7 @@ namespace FasdDesktopUi.Pages.SearchPage if (filteredResults?.PreSelectedRelation != null) { List selectedResult = filteredResults.Results.Values.FirstOrDefault(); - var processSearchResult = new cUiProcessSearchResultAction(selectedResult.FirstOrDefault()?.DisplayName, this, selectedResult) { PreSelectedSearchRelation = filteredResults.PreSelectedRelation }; + var processSearchResult = new cUiProcessSearchResultAction(selectedResult.FirstOrDefault()?.Name, this, selectedResult) { PreSelectedSearchRelation = filteredResults.PreSelectedRelation }; Dispatcher.Invoke(async () => { bool isSearchOngoing = await processSearchResult.RunUiActionAsync(this, this, false, null); diff --git a/FasdDesktopUi/Pages/SettingsPage/M42SettingsPageView.xaml b/FasdDesktopUi/Pages/SettingsPage/M42SettingsPageView.xaml index 770ae8d..0916b2c 100644 --- a/FasdDesktopUi/Pages/SettingsPage/M42SettingsPageView.xaml +++ b/FasdDesktopUi/Pages/SettingsPage/M42SettingsPageView.xaml @@ -103,13 +103,13 @@ - - - - - - - + + + + + + + @@ -604,7 +604,7 @@ + + + + + + + + diff --git a/FasdDesktopUi/Pages/SettingsPage/SettingsPageView.xaml.cs b/FasdDesktopUi/Pages/SettingsPage/SettingsPageView.xaml.cs index 011ea21..da2bc21 100644 --- a/FasdDesktopUi/Pages/SettingsPage/SettingsPageView.xaml.cs +++ b/FasdDesktopUi/Pages/SettingsPage/SettingsPageView.xaml.cs @@ -1,34 +1,19 @@ -using C4IT.MultiLanguage; +using C4IT.Configuration; using C4IT.FASD.Base; +using C4IT.MultiLanguage; +using F4SD_AdaptableIcon.Enums; +using FasdDesktopUi.Basics; using FasdDesktopUi.Basics.Enums; -using FasdDesktopUi.Basics.Models; using FasdDesktopUi.Basics.UserControls.AdaptableIcon; -using FasdDesktopUi.Pages.DetailsPage; -using FasdDesktopUi.Pages.SlimPage; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.ComponentModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Shapes; - using static C4IT.Logging.cLogManager; -using System.Reflection; -using C4IT.Logging; -using System.Windows.Navigation; -using F4SD_AdaptableIcon.Enums; -using FasdDesktopUi.Basics; -using System.Diagnostics; -using C4IT.Configuration; namespace FasdDesktopUi.Pages.SettingsPage { @@ -36,11 +21,10 @@ namespace FasdDesktopUi.Pages.SettingsPage { #region Properties - private static SettingsPageView _Instance = null; - public static SettingsPageView Instance { get - { - return _Instance ?? (_Instance = new SettingsPageView()); - } + private static SettingsPageView _instance = null; + public static SettingsPageView Instance + { + get { return _instance ?? (_instance = new SettingsPageView()); } } private Dictionary highlightColorActivationStatus; @@ -104,7 +88,8 @@ namespace FasdDesktopUi.Pages.SettingsPage public int PositionOfSmallViews { - get { + get + { return cF4sdGlobalConfig.ConvertHorizontalAlignmentToPosition(cFasdCockpitConfig.Instance.Global.SmallViewAlignment, 0); } set @@ -152,6 +137,18 @@ namespace FasdDesktopUi.Pages.SettingsPage } } + public bool UseGamification + { + get => cFasdCockpitConfig.Instance.Global.UseGamification; + set + { + cFasdCockpitConfig.Instance.Global.UseGamification = value; + cFasdCockpitConfig.Instance.Global.Save(nameof(cFasdCockpitConfig.Instance.Global.UseGamification)); + cFasdCockpitConfig.Instance.OnUiSettingsChanged(); + OnPropertyChanged(nameof(UseGamification)); + } + } + #endregion public static SettingsPageView Create() @@ -184,7 +181,7 @@ namespace FasdDesktopUi.Pages.SettingsPage FavouritePositionRightTextBlock.ClearValue(ForegroundProperty); FavouritePositionSlider.SetResourceReference(BackgroundProperty, "Color.FunctionMarker"); break; - case 2: + case 2: FavouritePositionRightTextBlock.SetResourceReference(ForegroundProperty, "Color.FunctionMarker"); FavouritePositionLeftTextBlock.ClearValue(ForegroundProperty); break; @@ -233,86 +230,30 @@ namespace FasdDesktopUi.Pages.SettingsPage ZoomDetailsPageInPecent = cFasdCockpitConfig.Instance.DetailsPageZoom; ZoomSlimPageInPercent = cFasdCockpitConfig.Instance.SlimPageZoom; - // hide or deactivate ShouldSkipSlimView options due to the policies - var _policy = cFasdCockpitConfig.Instance.Global.GetPropertyPolicy("ShouldSkipSlimView"); - if (_policy == enumConfigPolicy.Hidden) - { - ShouldSkipSlimViewLabel.Visibility = Visibility.Collapsed; - ShouldSkipSlimViewCheckBox.Visibility = Visibility.Collapsed; - } - else - { - ShouldSkipSlimViewLabel.Visibility = Visibility.Visible; - ShouldSkipSlimViewCheckBox.Visibility = Visibility.Visible; - } - if (_policy == enumConfigPolicy.Default) - { - ShouldSkipSlimViewCheckBox.IsEnabled = true; - ShouldSkipSlimViewPolicy.Visibility = Visibility.Collapsed; - ShouldSkipSlimViewCheckBox.ToolTip = null; - ShouldSkipSlimViewPolicy.ToolTip = null; - } - else - { - ShouldSkipSlimViewCheckBox.IsEnabled = false; - ShouldSkipSlimViewPolicy.Visibility = Visibility.Visible; - ShouldSkipSlimViewCheckBox.ToolTip = cMultiLanguageSupport.GetItem("Settings.Global.PolicyTooltip"); - ShouldSkipSlimViewPolicy.ToolTip = cMultiLanguageSupport.GetItem("Settings.Global.PolicyTooltip"); - } + UpdatePolicyElements("ShouldSkipSlimView", ShouldSkipSlimViewLabel, ShouldSkipSlimViewCheckBox, ShouldSkipSlimViewPolicy); OnPropertyChanged(nameof(ShouldSkipSlimView)); - // hide or deactivate SmallViewAlignment options due to the policies - _policy = cFasdCockpitConfig.Instance.Global.GetPropertyPolicy("SmallViewAlignment"); - if (_policy == enumConfigPolicy.Hidden) - { - PositionOfSmallViewsLabel.Visibility = Visibility.Collapsed; - PositionOfSmallViewsInput.Visibility = Visibility.Collapsed; - } - else - { - PositionOfSmallViewsLabel.Visibility = Visibility.Visible; - PositionOfSmallViewsInput.Visibility = Visibility.Visible; - } - if (_policy == enumConfigPolicy.Default) - { - PositionOfSmallViewsInput.IsEnabled = true; - PositionOfSmallViewsPolicy.Visibility = Visibility.Collapsed; - PositionOfSmallViewsPolicy.ToolTip = null; - } - else - { - PositionOfSmallViewsInput.IsEnabled = false; - PositionOfSmallViewsPolicy.Visibility = Visibility.Visible; - PositionOfSmallViewsPolicy.ToolTip = cMultiLanguageSupport.GetItem("Settings.Global.PolicyTooltip"); - } + UpdatePolicyElements("SmallViewAlignment", PositionOfSmallViewsLabel, PositionOfSmallViewsInput, PositionOfSmallViewsPolicy); OnPropertyChanged(nameof(PositionOfSmallViews)); - // hide or deactivate FavouriteBarAlignment options due to the policies - _policy = cFasdCockpitConfig.Instance.Global.GetPropertyPolicy("FavouriteBarAlignment"); - if (_policy == enumConfigPolicy.Hidden) - { - PositionOfFavouriteBarLabel.Visibility = Visibility.Collapsed; - PositionOfFavouriteBarInput.Visibility = Visibility.Collapsed; - } - else - { - PositionOfFavouriteBarLabel.Visibility = Visibility.Visible; - PositionOfFavouriteBarInput.Visibility = Visibility.Visible; - } - if (_policy == enumConfigPolicy.Default) - { - PositionOfFavouriteBarInput.IsEnabled = true; - PositionOfFavouriteBarPolicy.Visibility = Visibility.Collapsed; - PositionOfFavouriteBarPolicy.ToolTip = null; - } - else - { - PositionOfFavouriteBarInput.IsEnabled = false; - PositionOfFavouriteBarPolicy.Visibility = Visibility.Visible; - PositionOfFavouriteBarPolicy.ToolTip = cMultiLanguageSupport.GetItem("Settings.Global.PolicyTooltip"); - } + UpdatePolicyElements("FavouriteBarAlignment", PositionOfFavouriteBarLabel, PositionOfFavouriteBarInput, PositionOfFavouriteBarPolicy); OnPropertyChanged(nameof(PositionOfFavouriteBar)); + UpdatePolicyElements(nameof(cFasdCockpitConfig.Instance.Global.UseGamification), UseGamificationLabel, UseGamificationCheckBox, UseGamifictationPolicy); + OnPropertyChanged(nameof(cFasdCockpitConfig.Instance.Global.UseGamification)); + + void UpdatePolicyElements(string policyName, FrameworkElement label, FrameworkElement control, FrameworkElement policyElement) + { + enumConfigPolicy policy = cFasdCockpitConfig.Instance.Global.GetPropertyPolicy(policyName); + + label.Visibility = policy == enumConfigPolicy.Hidden ? Visibility.Collapsed : Visibility.Visible; + control.Visibility = policy == enumConfigPolicy.Hidden ? Visibility.Collapsed : Visibility.Visible; + control.IsEnabled = policy == enumConfigPolicy.Default; + + + policyElement.Visibility = policy == enumConfigPolicy.Default ? Visibility.Collapsed : Visibility.Visible; + policyElement.ToolTip = policy == enumConfigPolicy.Default ? null : cMultiLanguageSupport.GetItem("Settings.Global.PolicyTooltip"); + } } public void SetUpSettingsControls() @@ -872,7 +813,7 @@ namespace FasdDesktopUi.Pages.SettingsPage { try { - _Instance = null; + _instance = null; if (cSupportCaseDataProvider.detailsPage?.Visibility == Visibility.Visible) await cSupportCaseDataProvider.detailsPage.AdjustWindowSizeAsync(); } diff --git a/FasdDesktopUi/Pages/SlimPage/SlimPageView.xaml b/FasdDesktopUi/Pages/SlimPage/SlimPageView.xaml index aeae58b..f490ec0 100644 --- a/FasdDesktopUi/Pages/SlimPage/SlimPageView.xaml +++ b/FasdDesktopUi/Pages/SlimPage/SlimPageView.xaml @@ -101,9 +101,9 @@ Padding="10"> - + diff --git a/FasdDesktopUi/Pages/SlimPage/SlimPageView.xaml.cs b/FasdDesktopUi/Pages/SlimPage/SlimPageView.xaml.cs index 7b307c4..2f4d02e 100644 --- a/FasdDesktopUi/Pages/SlimPage/SlimPageView.xaml.cs +++ b/FasdDesktopUi/Pages/SlimPage/SlimPageView.xaml.cs @@ -45,8 +45,8 @@ namespace FasdDesktopUi.Pages.SlimPage { switch (invoker) { - case SearchBar searchBar: - if (!searchBar.IsVisible || searchBar.SearchStatus != SearchBar.eSearchStatus.message) + case InformationClassSearchBar searchBar: + if (!searchBar.IsVisible || searchBar.SearchStatus != InformationClassSearchBar.eSearchStatus.message) return true; break; case QuickActionSelector _: @@ -256,7 +256,7 @@ namespace FasdDesktopUi.Pages.SlimPage tempSubMenuList.Add(new cMenuDataBase(copyTemplate.Value)); } - tempMoreQuickActionList.Insert(0, new cMenuDataContainer() { MenuText = cMultiLanguageSupport.GetItem("QuickAction.CopyContent"), MenuIcon = new F4SD_AdaptableIcon.IconData(enumInternIcons.menuBar_copy), UiAction = new cSubMenuAction(true) { Name = cMultiLanguageSupport.GetItem("QuickAction.CopyContent"), SubMenuData = tempSubMenuList }, SubMenuData = tempSubMenuList }); + tempMoreQuickActionList.Insert(0, new cMenuDataContainer() { MenuText = cMultiLanguageSupport.GetItem("QuickAction.CopyContent"), MenuIcon = new cMenuDataBase.MenuIconInfo(new F4SD_AdaptableIcon.IconData(enumInternIcons.menuBar_copy)), UiAction = new cSubMenuAction(true) { Name = cMultiLanguageSupport.GetItem("QuickAction.CopyContent"), SubMenuData = tempSubMenuList }, SubMenuData = tempSubMenuList }); } var quickActionSelector = new QuickActionSelector() @@ -403,7 +403,7 @@ namespace FasdDesktopUi.Pages.SlimPage if (!(DataContext is SlimPageViewModel viewModel)) return; - if (SearchBarUserControl.SearchStatus != SearchBar.eSearchStatus.message) + if (SearchBarUserControl.SearchStatus != InformationClassSearchBar.eSearchStatus.message) { SearchBarUserControl.Visibility = Visibility.Collapsed; MenuBarUc.Visibility = Visibility.Visible; diff --git a/FasdDesktopUi/Pages/SplashScreenView/SplashScreenView.xaml b/FasdDesktopUi/Pages/SplashScreenView/SplashScreenView.xaml index c3cd852..a72768e 100644 --- a/FasdDesktopUi/Pages/SplashScreenView/SplashScreenView.xaml +++ b/FasdDesktopUi/Pages/SplashScreenView/SplashScreenView.xaml @@ -15,6 +15,20 @@ WindowStyle="None" IsVisibleChanged="Window_IsVisibleChanged"> + + + @@ -30,31 +44,26 @@ - + - - - - - window_minimize - - + TouchDown="MinimizeButton_TouchDown" + SelectedInternIcon="window_minimize" + > + + + + MouseLeftButtonDown="F4SDLogo_MouseLeftButtonDown" + TouchDown="F4SDLogo_TouchDown" + /> identity.Class == enumFasdInformationClass.User).Id); - if (closedSuccessfull) - { - SuccessPage.SuccessPage successPage = new SuccessPage.SuccessPage(); - successPage.Show(); - await _dataProvider?.CloseCaseAsync(); - TrySetDialogResult(true); - Close(); - } - } + if (closedSuccessfull) + { + SuccessPage.SuccessPage successPage = new SuccessPage.SuccessPage(); + successPage.Show(); + GamificationService.TrackAction(F4SD.Gamification.CockpitAction.CaseClosed); + await _dataProvider?.CloseCaseAsync(); + TrySetDialogResult(true); + Close(); + } + } catch (Exception E) { LogException(E); diff --git a/FasdDesktopUi/ResourceDictionaries/DarkModeResources.xaml b/FasdDesktopUi/ResourceDictionaries/DarkModeResources.xaml index d94acf9..20344fb 100644 --- a/FasdDesktopUi/ResourceDictionaries/DarkModeResources.xaml +++ b/FasdDesktopUi/ResourceDictionaries/DarkModeResources.xaml @@ -74,13 +74,13 @@ #414141 - #303030 - #303030 - #303030 + #5A5A5A + #5A5A5A + #5A5A5A #404040 - \ No newline at end of file + \ No newline at end of file diff --git a/FasdDesktopUi/ResourceDictionaries/DetailsPageResources/CustomizableResources.xaml b/FasdDesktopUi/ResourceDictionaries/DetailsPageResources/CustomizableResources.xaml index 6335400..dd96013 100644 --- a/FasdDesktopUi/ResourceDictionaries/DetailsPageResources/CustomizableResources.xaml +++ b/FasdDesktopUi/ResourceDictionaries/DetailsPageResources/CustomizableResources.xaml @@ -1,5 +1,10 @@  + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:base="clr-namespace:C4IT.FASD.Base;assembly=F4SD-Cockpit-Client-Base" + xmlns:vc="clr-namespace:FasdDesktopUi.Basics.Converter"> + + + - -