diff --git a/FasdCockpitBase/FasdCockpitCommunicationBase.cs b/FasdCockpitBase/FasdCockpitCommunicationBase.cs index eb4a7c9..704c463 100644 --- a/FasdCockpitBase/FasdCockpitCommunicationBase.cs +++ b/FasdCockpitBase/FasdCockpitCommunicationBase.cs @@ -90,6 +90,29 @@ namespace C4IT.FASD.Cockpit.Communication public abstract Task UpdateHealthcardTableData(cF4SDWriteParameters dataParameter); public abstract Task Matrix42TicketFinalization(cApiM42Ticket ticketData); + public virtual async Task FinalizeTicketAsync(cApiM42Ticket ticketData) + { + var success = await Matrix42TicketFinalization(ticketData); + var ticketId = ticketData?.Ticket; + if (ticketId == Guid.Empty) + ticketId = null; + + return new cTicketFinalizationResult + { + Success = success, + TicketId = ticketId + }; + } + + public virtual Task GetTicketExternalLinkAsync(Guid ticketId, enumTicketExternalOpenMode mode) + { + return Task.FromResult(new cTicketExternalLinkResult + { + Success = false, + Mode = enumTicketExternalOpenMode.None + }); + } + public abstract Task>> GetQuickActionHistory(string QuickActionName, int OrgId, int DeviceId, int? UserId); public abstract Task GetAgentApiAccessInfo(); diff --git a/FasdCockpitCommunication/FasdCockpitCommunicationWeb.cs b/FasdCockpitCommunication/FasdCockpitCommunicationWeb.cs index aa53cff..15c7078 100644 --- a/FasdCockpitCommunication/FasdCockpitCommunicationWeb.cs +++ b/FasdCockpitCommunication/FasdCockpitCommunicationWeb.cs @@ -1515,6 +1515,12 @@ namespace C4IT.FASD.Cockpit.Communication return output; } public override async Task Matrix42TicketFinalization(cApiM42Ticket ticketData) + { + var result = await FinalizeTicketAsync(ticketData); + return result?.Success == true; + } + + public override async Task FinalizeTicketAsync(cApiM42Ticket ticketData) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); @@ -1522,7 +1528,6 @@ namespace C4IT.FASD.Cockpit.Communication var apiError = 0; var timeStart = DateTime.UtcNow; - bool output = false; try { var http = GetHttpHelper(true); @@ -1532,7 +1537,7 @@ namespace C4IT.FASD.Cockpit.Communication var result = await http.PostJsonAsync(url, payload, 20000, CancellationToken.None); if (result.IsOk) - return true; + return ParseTicketFinalizationResult(result.Result, ticketData?.Ticket); else apiError = (int)result.Status; } @@ -1547,7 +1552,102 @@ namespace C4IT.FASD.Cockpit.Communication LogMethodEnd(CM); } - return output; + return new cTicketFinalizationResult { Success = false }; + } + + internal static cTicketFinalizationResult ParseTicketFinalizationResult(string response, Guid? fallbackTicketId) + { + if (fallbackTicketId == Guid.Empty) + fallbackTicketId = null; + + if (!string.IsNullOrWhiteSpace(response)) + { + try + { + var token = JToken.Parse(response); + if (token.Type == JTokenType.Object) + { + var result = token.ToObject(); + if (result != null) + { + if (result.Success && (!result.TicketId.HasValue || result.TicketId.Value == Guid.Empty)) + result.TicketId = fallbackTicketId; + + return result; + } + } + else if (token.Type == JTokenType.String && + Guid.TryParse(token.Value(), out var createdTicketId)) + { + return new cTicketFinalizationResult + { + Success = true, + TicketId = createdTicketId + }; + } + } + catch (JsonException) + { + if (Guid.TryParse(response.Trim().Trim('"'), out var createdTicketId)) + { + return new cTicketFinalizationResult + { + Success = true, + TicketId = createdTicketId + }; + } + } + } + + // Older providers returned an empty JSON value for successful updates. + return new cTicketFinalizationResult + { + Success = true, + TicketId = fallbackTicketId + }; + } + + public override async Task GetTicketExternalLinkAsync(Guid ticketId, enumTicketExternalOpenMode mode) + { + var CM = MethodBase.GetCurrentMethod(); + LogMethodBegin(CM); + + var apiError = 0; + var timeStart = DateTime.UtcNow; + + try + { + if (ticketId == Guid.Empty || mode == enumTicketExternalOpenMode.None) + return new cTicketExternalLinkResult { Success = false, Mode = enumTicketExternalOpenMode.None }; + + var http = GetHttpHelper(true); + var url = $"api/Ticketing/GetExternalLink?ticketId={ticketId:D}&mode={Uri.EscapeDataString(mode.ToString())}"; + var result = await http.GetHttpJson(url, 20000, CancellationToken.None); + + if (result.IsOk) + { + return JsonConvert.DeserializeObject(result.Result) + ?? new cTicketExternalLinkResult { Success = false, Mode = enumTicketExternalOpenMode.None }; + } + + apiError = (int)result.Status; + } + catch (Exception E) + { + apiError = E.HResult; + LogException(E); + } + finally + { + if (Debug_apiTiming) SaveApiTimingEntry("Ticketing-GetExternalLink", timeStart, apiError); + LogMethodEnd(CM); + } + + return new cTicketExternalLinkResult + { + Success = false, + Mode = enumTicketExternalOpenMode.None + }; } public override async Task GetAgentApiAccessInfo() diff --git a/FasdCockpitCommunicationDemo/Config/F4SD-Global-Configuration.xml b/FasdCockpitCommunicationDemo/Config/F4SD-Global-Configuration.xml index 58bbaec..3b02f8a 100644 --- a/FasdCockpitCommunicationDemo/Config/F4SD-Global-Configuration.xml +++ b/FasdCockpitCommunicationDemo/Config/F4SD-Global-Configuration.xml @@ -12,6 +12,8 @@ + + diff --git a/FasdDesktopUi/Basics/Helper/TicketDeepLinkHelper.cs b/FasdDesktopUi/Basics/Helper/TicketDeepLinkHelper.cs index e1daa0e..b6f5191 100644 --- a/FasdDesktopUi/Basics/Helper/TicketDeepLinkHelper.cs +++ b/FasdDesktopUi/Basics/Helper/TicketDeepLinkHelper.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using C4IT.FASD.Base; +using C4IT.FASD.Cockpit.Communication; using C4IT.Logging; using FasdDesktopUi.Basics; @@ -15,6 +17,33 @@ namespace FasdDesktopUi.Basics.Helper { private const string DefaultTicketActivityType = "SPSActivityTypeTicket"; + internal static async Task TryOpenFinalizedTicketExternallyAsync(Guid ticketId, enumTicketExternalOpenMode mode) + { + try + { + if (ticketId == Guid.Empty || mode == enumTicketExternalOpenMode.None) + return false; + + var linkResult = await cFasdCockpitCommunicationBase.Instance.GetTicketExternalLinkAsync(ticketId, mode); + if (linkResult?.Success != true || + !Uri.TryCreate(linkResult.Url, UriKind.Absolute, out var ticketUri) || + (ticketUri.Scheme != Uri.UriSchemeHttp && ticketUri.Scheme != Uri.UriSchemeHttps)) + { + LogEntry($"Could not resolve an external link for ticket '{ticketId:D}'.", LogLevels.Warning); + return false; + } + + new cBrowsers().Start("default", ticketUri.AbsoluteUri); + return true; + } + catch (Exception ex) + { + LogException(ex); + } + + return false; + } + internal static bool TryOpenTicketRelationExternally(cF4sdApiSearchResultRelation relation, bool forceExternal = false) { try diff --git a/FasdDesktopUi/Basics/UserControls/Ticket/CloseCaseDialogWithTicket.xaml.cs b/FasdDesktopUi/Basics/UserControls/Ticket/CloseCaseDialogWithTicket.xaml.cs index 4454da0..fe59cb0 100644 --- a/FasdDesktopUi/Basics/UserControls/Ticket/CloseCaseDialogWithTicket.xaml.cs +++ b/FasdDesktopUi/Basics/UserControls/Ticket/CloseCaseDialogWithTicket.xaml.cs @@ -104,6 +104,13 @@ namespace FasdDesktopUi.Basics.UserControls public bool AreCaseNotesMandatory => cFasdCockpitConfig.Instance?.Global?.TicketConfiguration?.NotesMandatory ?? false; + private bool UseSimplifiedCaseCompletionDialog => + cFasdCockpitConfig.Instance?.Global?.TicketConfiguration?.UseSimplifiedCaseCompletionDialog == true; + + private enumTicketExternalOpenMode SimplifiedCaseCompletionTicketOpenMode => + cFasdCockpitConfig.Instance?.Global?.TicketConfiguration?.SimplifiedCaseCompletionTicketOpenMode + ?? enumTicketExternalOpenMode.Preview; + private static class TicketInfoKeys { public const string ActivityType = "ActivityType"; @@ -531,6 +538,8 @@ namespace FasdDesktopUi.Basics.UserControls private void CloseCaseDialogWithTicket_Loaded(object sender, RoutedEventArgs e) { + UpdateTicketComponentVisibility(); + Window _parentWindow = Window.GetWindow(this); if (!(_parentWindow is TicketCompletion _msgBox)) @@ -623,10 +632,17 @@ namespace FasdDesktopUi.Basics.UserControls return; var _isVisible = checkTicketActive() && !_skipTicketPillSelected; + var useSimplifiedDialog = _isVisible && UseSimplifiedCaseCompletionDialog; + + if (useSimplifiedDialog) + SelectSaveTicketAction(); TicketSelectionCategory.Visibility = _isVisible ? Visibility.Visible : Visibility.Collapsed; - StatusSelectionBorder.Visibility = _isVisible ? Visibility.Visible : Visibility.Collapsed; + StatusSelectionBorder.Visibility = _isVisible && !useSimplifiedDialog ? Visibility.Visible : Visibility.Collapsed; CopyDisclaimerTextBox.Visibility = _isVisible ? Visibility.Collapsed : Visibility.Visible; + + if (!_isVisible || useSimplifiedDialog) + DynamicStatusAdditionBorder.Visibility = Visibility.Collapsed; } catch (Exception E) { @@ -634,6 +650,21 @@ namespace FasdDesktopUi.Basics.UserControls } } + private void SelectSaveTicketAction() + { + var saveItem = TicketStatusCombobox.Items + .OfType() + .FirstOrDefault(item => + Enum.TryParse(item.Tag?.ToString(), out enumCloseCaseTicketStatus action) && + action == enumCloseCaseTicketStatus.Save); + + if (saveItem != null && !ReferenceEquals(TicketStatusCombobox.SelectedItem, saveItem)) + TicketStatusCombobox.SelectedItem = saveItem; + + DynamicStatusAdditionBorder.Child = null; + DynamicStatusAdditionBorder.Visibility = Visibility.Collapsed; + } + private UIElement GetTicketUiElement(cF4sdApiSearchResultRelation ticketRelation) { try @@ -2011,7 +2042,20 @@ namespace FasdDesktopUi.Basics.UserControls ticketData.AdditionalValues.Add("Dialog.CloseCase.AssetNotFoundInMatrix42", cMultiLanguageSupport.GetItem("Dialog.CloseCase.AssetNotFoundInMatrix42")); ticketData.AdditionalValues.Add("Dialog.CloseCase.ReopenReasonText", cMultiLanguageSupport.GetItem("Dialog.CloseCase.ReopenReasonText")); ticketData.AdditionalValues.Add("Dialog.CloseCase.ReopenReasonTextHtml", cMultiLanguageSupport.GetItem("Dialog.CloseCase.ReopenReasonTextHtml")); - var _res = await cFasdCockpitCommunicationBase.Instance.Matrix42TicketFinalization(ticketData); + var finalizationResult = await cFasdCockpitCommunicationBase.Instance.FinalizeTicketAsync(ticketData); + if (finalizationResult?.Success != true) + return false; + + if (UseSimplifiedCaseCompletionDialog && + SimplifiedCaseCompletionTicketOpenMode != enumTicketExternalOpenMode.None && + finalizationResult.TicketId is Guid finalizedTicketId && + finalizedTicketId != Guid.Empty) + { + await TicketExternalLinkHelper.TryOpenFinalizedTicketExternallyAsync( + finalizedTicketId, + SimplifiedCaseCompletionTicketOpenMode); + } + return true; } catch (Exception e)