Compare commits

...

19 Commits

Author SHA1 Message Date
Drechsler, Meik
ffcfc85a4e Rename demo activity filters to ticket filters 2026-07-23 12:47:01 +02:00
Drechsler, Meik
2d3460574e Add activity filter configuration examples 2026-07-23 11:21:26 +02:00
Drechsler, Meik
bdff275da9 Use generic ticket processing demo defaults 2026-07-23 09:27:00 +02:00
Drechsler, Meik
bb5d7c1d2a Align simplified completion demo defaults 2026-07-22 11:59:50 +02:00
Drechsler, Meik
9bd3896ba0 Add event-driven quick action dispatch 2026-07-22 11:57:32 +02:00
Drechsler, Meik
fb45f1c42b Add simplified ticket completion flow 2026-07-20 10:48:40 +02:00
Meik
1a3bef9eeb Consume ticket overview capabilities separately 2026-07-20 09:47:17 +02:00
Meik
21e9b85be7 Enable ticket activity rows by default 2026-07-20 09:01:52 +02:00
Meik
30cc7a0482 Apply shared ticket activity capability to overview rows 2026-07-20 08:55:15 +02:00
Meik
4ed75c6e82 Avoid marquee for marginal ticket heading overflow 2026-07-19 00:09:53 +02:00
Meik
791e53062e Document ticket overview marquee viewport 2026-07-19 00:01:34 +02:00
Meik
b88b325d02 Fix ticket overview marquee viewport width 2026-07-19 00:00:44 +02:00
Meik
2fbf2e2d2d Add marquee for truncated ticket overview headings 2026-07-18 23:45:34 +02:00
Meik
62a5c97cbd Add conditional service request row to ticket overview 2026-07-17 18:48:48 +02:00
Meik
0fef3ad49c Refactor quick action output handling and extend case note support 2026-07-17 18:34:59 +02:00
Meik
1db0da9a40 fix(search): avoid ticket list flicker on fallback 2026-07-13 12:44:41 +02:00
Meik
7ea13f60a7 fix(search): avoid duplicate external ticket tabs 2026-07-13 12:08:04 +02:00
Meik
b3520f8865 fix(search): preserve configured ticket links 2026-07-13 11:55:00 +02:00
Meik
997dcee78f fix(search): open unassigned tickets in Matrix42 2026-07-13 11:51:11 +02:00
66 changed files with 2292 additions and 992 deletions

View File

@@ -0,0 +1,159 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
using F4SD.ActionConnector.Enumerations;
using F4SD.ActionConnector.Events;
using F4SD.ActionConnector.Execution;
using F4SD.ActionConnector.Models;
using F4SD.ActionConnector.Payloads;
using static C4IT.Logging.cLogManager;
namespace F4SD.ActionConnector.Bus
{
/// <summary>
/// Entry point for the ActionConnector.
/// Subscribes to <see cref="ActionConnectorEvents"/>, reads the configuration once,
/// and dispatches actions whenever a trigger event fires.
///
/// Usage from the host application:
/// <code>
/// var bus = new ActionBus(new ActionBusOptions
/// {
/// ConfigurationFilePath = @"C:\Config\F4SD-ExternalCommunication-Configuration.xml",
/// QuickActionExecutor = async request =>
/// {
/// await myJiraClient.CreateTicketAsync(request.QuickActionRef, request.Parameters);
/// return ActionResult.Ok(request.ActionId);
/// }
/// });
///
/// bus.Start();
/// // later:
/// bus.Stop();
/// </code>
/// </summary>
public sealed class ActionBus : IDisposable
{
private readonly ActionBusOptions _options;
private readonly ActionDispatcher _dispatcher;
private ExternalCommunicationConfiguration _configuration;
private bool _running;
public ActionBus(ActionBusOptions options)
{
_options = options ?? throw new ArgumentNullException(nameof(options));
_dispatcher = new ActionDispatcher(options.QuickActionExecutor);
}
/// <summary>
/// Loads the configuration and subscribes to all trigger events.
/// Call once during application startup.
/// </summary>
public void Start()
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
_configuration = new ExternalCommunicationConfiguration();
_configuration.Initiate(_options.ConfigRootElement, _options.Parser);
ActionConnectorEvents.CaseClosed += OnCaseClosed;
ActionConnectorEvents.CaseCreated += OnCaseCreated;
ActionConnectorEvents.ApplicationStartup += OnApplicationStartup;
_running = true;
LogEntry("ActionBus started.", C4IT.Logging.LogLevels.Info);
}
catch (Exception ex)
{
LogException(ex);
}
finally
{
LogMethodEnd(CM);
}
}
/// <summary>
/// Unsubscribes from all trigger events.
/// Call during application shutdown.
/// </summary>
public void Stop()
{
ActionConnectorEvents.CaseClosed -= OnCaseClosed;
ActionConnectorEvents.CaseCreated -= OnCaseCreated;
ActionConnectorEvents.ApplicationStartup -= OnApplicationStartup;
_running = false;
LogEntry("ActionBus stopped.", C4IT.Logging.LogLevels.Info);
}
public void Dispose() => Stop();
// ──────────────────────────────────────────────────────────────────────────
// Event handlers — each one creates a typed ActionContext and fires dispatch
// ──────────────────────────────────────────────────────────────────────────
private void OnCaseClosed(object sender, TriggerEventArgs<CaseClosedPayload> args)
=> DispatchTrigger(TriggerEvent.CaseClosed, args.Payload, sender);
private void OnCaseCreated(object sender, TriggerEventArgs<CaseCreatedPayload> args)
=> DispatchTrigger(TriggerEvent.CaseCreated, args.Payload, sender);
private void OnApplicationStartup(object sender, TriggerEventArgs<ApplicationStartupPayload> args)
=> DispatchTrigger(TriggerEvent.ApplicationStartup, args.Payload, sender);
// ──────────────────────────────────────────────────────────────────────────
// Core dispatch — fire-and-forget wrapper that surfaces results via events
// ──────────────────────────────────────────────────────────────────────────
private void DispatchTrigger(TriggerEvent triggerEvent, PayloadBase payload, object sender)
{
if (!_running) return;
if (!_configuration.Triggers.TryGetValue(triggerEvent, out Trigger trigger)) return;
// Run async dispatch on the thread pool; do not block the caller.
Task.Run(() => DispatchActionsAsync(trigger.Actions, triggerEvent, payload, sender));
}
private async Task DispatchActionsAsync(
IDictionary<string, ActionDefinitionBase> actions,
TriggerEvent triggerEvent,
PayloadBase payload,
object sender)
{
foreach (var pair in actions)
{
ActionDefinitionBase action = pair.Value;
var context = new ActionContext
{
TriggerEvent = triggerEvent,
AwaitResult = action.AwaitResult,
Payload = payload,
};
ActionResult result = await ExecuteSafeAsync(action, context);
ActionConnectorEvents.RaiseActionCompleted(
sender,
new ActionCompletedEventArgs(result, context));
}
}
private async Task<ActionResult> ExecuteSafeAsync(ActionDefinitionBase action, ActionContext context)
{
try
{
return await _dispatcher.DispatchAsync(action, context);
}
catch (Exception ex)
{
LogException(ex);
return ActionResult.Fail(action.Id, ex.Message);
}
}
}
}

View File

@@ -0,0 +1,23 @@
using System;
using System.Threading.Tasks;
using System.Xml;
using C4IT.XML;
using F4SD.ActionConnector.Execution;
using F4SD.ActionConnector.Models;
namespace F4SD.ActionConnector.Bus
{
public sealed class ActionBusOptions
{
public XmlElement ConfigRootElement { get; set; }
public cXmlParser Parser { get; set; }
/// <summary>
/// Called for every QuickAction that fires.
/// Template variables in <see cref="QuickActionRequest.Parameters"/> are already
/// resolved against the event payload before this delegate is invoked.
/// Must be set when any QuickAction is configured.
/// </summary>
public Func<QuickActionRequest, Task<ActionResult>> QuickActionExecutor { get; set; }
}
}

View File

@@ -0,0 +1,62 @@
using System;
using System.Threading.Tasks;
using F4SD.ActionConnector.Enumerations;
using F4SD.ActionConnector.Models;
using static C4IT.Logging.cLogManager;
namespace F4SD.ActionConnector.Execution
{
/// <summary>
/// Routes a single action definition to the correct executor based on its <see cref="ActionType"/>.
/// </summary>
internal sealed class ActionDispatcher
{
private readonly Func<QuickActionRequest, Task<ActionResult>> _quickActionExecutor;
internal ActionDispatcher(Func<QuickActionRequest, Task<ActionResult>> quickActionExecutor)
{
_quickActionExecutor = quickActionExecutor;
}
internal async Task<ActionResult> DispatchAsync(ActionDefinitionBase action, ActionContext context)
{
if (!action.IsEnabled)
return ActionResult.Skip(action.Id);
switch (action.Type)
{
case ActionType.QuickAction:
return await ExecuteQuickActionAsync((QuickActionDefinition)action, context);
case ActionType.HttpCall:
case ActionType.Webhook:
throw new NotImplementedException(
$"ActionType '{action.Type}' is not yet implemented.");
default:
LogEntry($"ActionDispatcher: unknown ActionType '{action.Type}' for action '{action.Id}'.",
C4IT.Logging.LogLevels.Warning);
return ActionResult.Skip(action.Id);
}
}
private async Task<ActionResult> ExecuteQuickActionAsync(QuickActionDefinition definition, ActionContext context)
{
if (_quickActionExecutor == null)
throw new InvalidOperationException(
$"A QuickAction ('{definition.Id}') was triggered but no {nameof(Bus.ActionBusOptions.QuickActionExecutor)} was registered.");
var resolvedParameters = PayloadVariableResolver.Resolve(definition.Parameters, context.Payload);
var request = new QuickActionRequest
{
ActionId = definition.Id,
QuickActionRef = definition.QuickActionRef,
Parameters = resolvedParameters,
Context = context,
};
return await _quickActionExecutor(request);
}
}
}

View File

@@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Text.RegularExpressions;
using F4SD.ActionConnector.Payloads;
namespace F4SD.ActionConnector.Execution
{
/// <summary>
/// Resolves template variables such as {{Case.Id}} or {{Case.ClosedAt:yyyy-MM-ddTHH:mm:ssZ}}
/// against a payload object.
///
/// Variable format: {{ObjectName.PropertyName[:FormatSpecifier]}}
/// </summary>
internal static class PayloadVariableResolver
{
private static readonly Regex VariablePattern =
new Regex(@"\{\{(?<obj>[^.}]+)\.(?<prop>[^:}]+)(?::(?<fmt>[^}]+))?\}\}",
RegexOptions.Compiled);
/// <summary>
/// Returns a new dictionary with every value in <paramref name="rawParameters"/> having
/// its template variables replaced by the corresponding payload property values.
/// Pure single-variable templates (e.g. <c>{{Case.ClosedAt}}</c>) yield the raw typed
/// object; mixed or formatted templates yield a resolved string.
/// </summary>
internal static IDictionary<string, object> Resolve(
IDictionary<string, string> rawParameters,
PayloadBase payload)
{
var resolved = new Dictionary<string, object>(rawParameters.Count);
foreach (var pair in rawParameters)
resolved[pair.Key] = ResolveValue(pair.Value, payload);
return resolved;
}
private static object ResolveValue(string template, PayloadBase payload)
{
var match = VariablePattern.Match(template);
// Pure single variable with no format specifier → return raw object
if (match.Success && match.Value == template && !match.Groups["fmt"].Success)
return ResolveRawProperty(match, payload);
// Mixed template, literal, or explicit format specifier → return resolved string
return ResolveString(template, payload);
}
private static object ResolveRawProperty(Match match, PayloadBase payload)
{
string propertyName = match.Groups["prop"].Value;
PropertyInfo property = payload.GetType().GetProperty(
propertyName,
BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
if (property == null)
return match.Value;
return property.GetValue(payload);
}
private static string ResolveString(string template, PayloadBase payload)
{
return VariablePattern.Replace(template, match => ResolveMatch(match, payload));
}
private static string ResolveMatch(Match match, PayloadBase payload)
{
string propertyName = match.Groups["prop"].Value;
string formatSpecifier = match.Groups["fmt"].Success ? match.Groups["fmt"].Value : null;
PropertyInfo property = payload.GetType().GetProperty(
propertyName,
BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
if (property == null)
return match.Value;
object value = property.GetValue(payload);
if (value == null)
return string.Empty;
if (formatSpecifier != null && value is IFormattable formattable)
return formattable.ToString(formatSpecifier, CultureInfo.InvariantCulture);
return value.ToString();
}
}
}

View File

@@ -0,0 +1,29 @@
using System.Collections.Generic;
using F4SD.ActionConnector.Models;
namespace F4SD.ActionConnector.Execution
{
public sealed class QuickActionRequest
{
/// <summary>
/// Matches <see cref="QuickActionDefinition.Id"/> from the configuration.
/// </summary>
public string ActionId { get; internal set; }
/// <summary>
/// The name of the Quick Action to invoke, e.g. "Document case in JIRA".
/// </summary>
public string QuickActionRef { get; internal set; }
/// <summary>
/// Template variables resolved against the event payload.
/// Values are the raw payload values; callers are responsible for casting.
/// </summary>
public IDictionary<string, object> Parameters { get; internal set; }
/// <summary>
/// Full context of the triggering event.
/// </summary>
public ActionContext Context { get; internal set; }
}
}

View File

@@ -5,8 +5,8 @@ namespace F4SD.ActionConnector.Models
{
public sealed class ActionContext
{
public TriggerEvent TriggerEvent { get; set; }
public bool AwaitResult { get; set; }
public PayloadBase Payload { get; set; }
public TriggerEvent TriggerEvent { get; internal set; }
public bool AwaitResult { get; internal set; }
public PayloadBase Payload { get; internal set; }
}
}

View File

@@ -8,11 +8,11 @@ namespace F4SD.ActionConnector.Models
{
public abstract class ActionDefinitionBase
{
public string Id { get; set; }
public string Id { get; internal set; }
public abstract ActionType Type { get; }
public bool AwaitResult { get; set; }
public bool IsEnabled { get; set; }
public string Description { get; set; }
public bool AwaitResult { get; internal set; }
public bool IsEnabled { get; internal set; }
public string Description { get; internal set; }
public bool IsValid { get; private protected set; }
protected ActionDefinitionBase(XmlElement xNode, cXmlParser parser)

View File

@@ -5,29 +5,29 @@ namespace F4SD.ActionConnector.Models
{
public sealed class ActionResult
{
public string ActionId { get; set; }
public ActionResultStatus Status { get; set; }
public string ActionId { get; private set; }
public ActionResultStatus Status { get; private set; }
/// <summary>
/// HTTP status code for HttpCall actions; null for other types.
/// </summary>
public int? HttpStatusCode { get; set; }
public int? HttpStatusCode { get; private set; }
/// <summary>
/// Raw response body (HttpCall) or serialized output (QuickAction).
/// </summary>
public string RawResponse { get; set; }
public string RawResponse { get; private set; }
/// <summary>
/// Named fields extracted from the response via jsonPath mappings.
/// Populated when DisplayInCockpit is true.
/// </summary>
public IDictionary<string, string> MappedFields { get; set; } = new Dictionary<string, string>();
public IDictionary<string, string> MappedFields { get; private set; } = new Dictionary<string, string>();
/// <summary>
/// Human-readable error message; set when Status is Failure.
/// </summary>
public string ErrorMessage { get; set; }
public string ErrorMessage { get; private set; }
public static ActionResult Ok(string actionId, string rawResponse = null)
=> new ActionResult { ActionId = actionId, Status = ActionResultStatus.Success, RawResponse = rawResponse };

View File

@@ -3,17 +3,16 @@ 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
public 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 const string ConfigRootElement = "F4SD-ExternalCommunication-Configuration";
public IDictionary<TriggerEvent, Trigger> Triggers { get; } = new Dictionary<TriggerEvent, Trigger>();
@@ -43,7 +42,7 @@ namespace F4SD.ActionConnector.Models
{
Trigger trigger = new Trigger(triggerNodeElement, parser);
if (trigger is null || !Triggers.ContainsKey(trigger.Event))
if (trigger is null || !trigger.IsValid || Triggers.ContainsKey(trigger.Event))
continue;
Triggers.Add(trigger.Event, trigger);

View File

@@ -11,16 +11,16 @@ namespace F4SD.ActionConnector.Models
{
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<string, string> Headers { get; set; } = new Dictionary<string, string>();
public string Body { get; set; }
public string Url { get; internal set; }
public HttpMethod Method { get; internal set; } = HttpMethod.Post;
public TimeSpan Timeout { get; internal set; } = TimeSpan.FromSeconds(30);
public IDictionary<string, string> Headers { get; internal set; } = new Dictionary<string, string>();
public string Body { get; internal set; }
/// <summary>
/// jsonPath expressions keyed by field name, used to extract values from the response.
/// </summary>
public IDictionary<string, string> MappedFieldJsonPaths { get; set; } = new Dictionary<string, string>();
public IDictionary<string, string> MappedFieldJsonPaths { get; internal set; } = new Dictionary<string, string>();
internal HttpCallDefinition(XmlElement xNode, cXmlParser parser) : base(xNode, parser)
{

View File

@@ -17,7 +17,7 @@ namespace F4SD.ActionConnector.Models
/// <summary>
/// Reference to the Quick Action to invoke, e.g. "Protocol case in JIRA".
/// </summary>
public string QuickActionRef { get; set; }
public string QuickActionRef { get; internal set; }
public IDictionary<string, string> Parameters { get; private set; } = new Dictionary<string, string>();
@@ -81,7 +81,7 @@ namespace F4SD.ActionConnector.Models
{
try
{
string parameterName = cXmlParser.GetStringFromXmlAttribute(parameterNode, "name");
string parameterName = cXmlParser.GetStringFromXmlAttribute(parameterNode, "Name");
string parameterVariable = cXmlParser.GetInnerTextFromXmlElement(parameterNode);
if (string.IsNullOrEmpty(parameterName) || string.IsNullOrEmpty(parameterVariable))

View File

@@ -8,7 +8,7 @@ using static C4IT.Logging.cLogManager;
namespace F4SD.ActionConnector.Models
{
internal class Trigger
public class Trigger
{
public TriggerEvent Event { get; set; }
@@ -23,7 +23,7 @@ namespace F4SD.ActionConnector.Models
{
try
{
Event = cXmlParser.GetEnumFromAttribute(xNode, "action", TriggerEvent.Unknown);
Event = cXmlParser.GetEnumFromAttribute(xNode, "event", TriggerEvent.Unknown);
XmlNode actionsNode = xNode.SelectSingleNode(ActionsNodeName);
@@ -56,7 +56,7 @@ namespace F4SD.ActionConnector.Models
{
try
{
ActionType type = cXmlParser.GetEnumFromAttribute(actionNode, "type", ActionType.Unknown);
ActionType type = cXmlParser.GetEnumFromAttribute(actionNode, "Type", ActionType.Unknown);
ActionDefinitionBase actionDefinition = null;

View File

@@ -1,15 +0,0 @@
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);
}
}

View File

@@ -1,26 +0,0 @@
namespace F4SD.DocuEngine.DocuEngineParser
{
internal class SyntaxErrorException : DocuEngineParserException
{
private const string errorMessageTemplate = "<<Syntax Error | Expected Characters: \"{0}\" | Actual Characters: \"{1}\">>";
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;
}
}
}

View File

@@ -1,25 +0,0 @@
namespace F4SD.DocuEngine.DocuEngineParser
{
internal class VariableErrorException : DocuEngineParserException
{
private const string errorMessageTemplate = "<<Variable Error | \"{0}\" can't be converted to type \"{1}\">>";
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;
}
}
}

View File

@@ -0,0 +1,121 @@
using C4IT.FASD.Base;
using FasdDesktopUi.Basics.Helper;
namespace F4SD.Cockpit.Client.Test.Basics.Helper;
public class TicketExternalLinkHelperTest
{
[Fact]
public void HasValidUserIdentity_NullIdentities_ReturnsFalse()
{
var relation = CreateTicketRelation();
Assert.False(TicketExternalLinkHelper.HasValidUserIdentity(relation));
}
[Fact]
public void HasValidUserIdentity_EmptyUserId_ReturnsFalse()
{
var relation = CreateTicketRelation();
relation.Identities =
[
new cF4sdIdentityEntry { Class = enumFasdInformationClass.Ticket, Id = relation.id },
new cF4sdIdentityEntry { Class = enumFasdInformationClass.User, Id = Guid.Empty }
];
Assert.False(TicketExternalLinkHelper.HasValidUserIdentity(relation));
}
[Fact]
public void HasValidUserIdentity_ValidUserId_ReturnsTrue()
{
var relation = CreateTicketRelation();
relation.Identities =
[
new cF4sdIdentityEntry { Class = enumFasdInformationClass.User, Id = Guid.NewGuid() }
];
Assert.True(TicketExternalLinkHelper.HasValidUserIdentity(relation));
}
[Fact]
public void TryGetTicketLink_UsesProvidedPreviewLink()
{
var relation = CreateTicketRelation();
relation.Infos = new Dictionary<string, string>
{
["DirectLinkPreview"] = "https://m42.example/wm/ticket/preview"
};
var result = TicketExternalLinkHelper.TryGetTicketLink(relation, null, out var ticketLink);
Assert.True(result);
Assert.Equal("https://m42.example/wm/ticket/preview", ticketLink.TrimEnd('/'));
}
[Fact]
public void TryGetTicketLink_PreservesConfiguredTicketLink()
{
var relation = CreateTicketRelation();
relation.Infos = new Dictionary<string, string>
{
["TicketLink"] = "matrix42-client://ticket/TCK00001"
};
var result = TicketExternalLinkHelper.TryGetTicketLink(relation, null, out var ticketLink);
Assert.True(result);
Assert.Equal("matrix42-client://ticket/TCK00001", ticketLink);
}
[Fact]
public void TryGetTicketLink_BuildsPreviewLinkForOverviewRelation()
{
var relation = CreateTicketRelation();
relation.Infos = new Dictionary<string, string>
{
["ActivityType"] = "SPSActivityTypeIncident"
};
var result = TicketExternalLinkHelper.TryGetTicketLink(relation, "m42.example", out var ticketLink);
Assert.True(result);
Assert.Equal(
$"https://m42.example/wm/app-ServiceDesk/notSet/preview-object/SPSActivityTypeIncident/{relation.id:D}/0/",
ticketLink);
}
[Fact]
public void TryGetTicketLink_UsesDefaultTicketActivityType()
{
var relation = CreateTicketRelation();
var result = TicketExternalLinkHelper.TryGetTicketLink(relation, "https://m42.example/", out var ticketLink);
Assert.True(result);
Assert.Contains("/SPSActivityTypeTicket/", ticketLink);
}
[Fact]
public void TryGetTicketLink_MissingTicketId_ReturnsFalse()
{
var relation = CreateTicketRelation();
relation.id = Guid.Empty;
var result = TicketExternalLinkHelper.TryGetTicketLink(relation, "https://m42.example", out var ticketLink);
Assert.False(result);
Assert.Null(ticketLink);
}
private static cF4sdApiSearchResultRelation CreateTicketRelation()
{
return new cF4sdApiSearchResultRelation
{
Type = enumF4sdSearchResultClass.Ticket,
Name = "TCK00001",
DisplayName = "TCK00001",
id = Guid.NewGuid()
};
}
}

View File

@@ -1,3 +1,4 @@
using C4IT.FASD.Cockpit.Communication;
using FasdDesktopUi.Basics.Services;
using FasdDesktopUi.Basics.Services.Models;
@@ -110,6 +111,52 @@ public class TicketOverviewUpdateServiceTest
});
}
[Fact]
public async Task FetchAsync_TicketAndServiceRequestsCapability_UsesEnabledDefaultAndIsSeparatedFromCounts()
{
// Arrange
var communication = new FakeCommunication();
communication.SetCounts(TileScope.Personal, new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
{
["ServiceRequestsNew"] = 2
});
communication.SetCounts(TileScope.Role, new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
{
["ServiceRequestsNew"] = 4
});
var service = CreateService(communication, out _);
var availabilityChanges = 0;
service.TicketAndServiceRequestsAvailabilityChanged += (_, _) => availabilityChanges++;
Assert.True(service.TicketAndServiceRequestsEnabled);
// Act
service.UpdateAvailability(true);
await WaitUntilAsync(() => service.AreAllScopesInitialized);
// Assert
Assert.True(service.TicketAndServiceRequestsEnabled);
Assert.Equal(0, availabilityChanges);
Assert.Equal(2, service.CurrentCounts["ServiceRequestsNew"].Personal);
Assert.Equal(4, service.CurrentCounts["ServiceRequestsNew"].Role);
Assert.DoesNotContain("TicketAndServiceRequestsEnabled", service.CurrentCounts.Keys);
communication.SetCounts(TileScope.Personal, new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
{
["ServiceRequestsNew"] = 3
});
await service.FetchAsync(TileScope.Personal);
Assert.True(service.TicketAndServiceRequestsEnabled);
Assert.Equal(0, availabilityChanges);
communication.SetCapability(TileScope.Personal, false);
await service.FetchAsync(TileScope.Personal);
Assert.False(service.TicketAndServiceRequestsEnabled);
Assert.Equal(1, availabilityChanges);
}
private static TicketOverviewUpdateService CreateService(FakeCommunication communication, out FakeDispatcher dispatcher)
{
dispatcher = new FakeDispatcher();
@@ -149,18 +196,27 @@ public class TicketOverviewUpdateServiceTest
private sealed class FakeCommunication : ITicketOverviewCommunication
{
private Dictionary<string, int> _personalCounts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
private Dictionary<string, int> _roleCounts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
private cF4sdTicketOverviewCountsResult _personalResult = new cF4sdTicketOverviewCountsResult();
private cF4sdTicketOverviewCountsResult _roleResult = new cF4sdTicketOverviewCountsResult();
public bool IsDemo()
{
return false;
}
public Task<Dictionary<string, int>> GetTicketOverviewCounts(string[] overviewKeys, bool useRoleScope)
public Task<cF4sdTicketOverviewCountsResult> GetTicketOverviewCounts(string[] overviewKeys, bool useRoleScope)
{
var source = useRoleScope ? _roleCounts : _personalCounts;
return Task.FromResult(new Dictionary<string, int>(source, StringComparer.OrdinalIgnoreCase));
var source = useRoleScope ? _roleResult : _personalResult;
return Task.FromResult(new cF4sdTicketOverviewCountsResult
{
Counts = new Dictionary<string, int>(source.Counts, StringComparer.OrdinalIgnoreCase),
Capabilities = source.Capabilities == null
? null
: new cF4sdTicketOverviewCapabilities
{
TicketAndServiceRequestsEnabled = source.Capabilities.TicketAndServiceRequestsEnabled
}
});
}
public void RegisterGeneratedTicket(FasdCockpitCommunicationDemo.DemoTicketRecord record)
@@ -175,13 +231,22 @@ public class TicketOverviewUpdateServiceTest
if (scope == TileScope.Role)
{
_roleCounts = copy;
_roleResult.Counts = copy;
}
else
{
_personalCounts = copy;
_personalResult.Counts = copy;
}
}
public void SetCapability(TileScope scope, bool enabled)
{
var result = scope == TileScope.Role ? _roleResult : _personalResult;
result.Capabilities = new cF4sdTicketOverviewCapabilities
{
TicketAndServiceRequestsEnabled = enabled
};
}
}
private sealed class FakeDispatcher : ITicketOverviewDispatcher

View File

@@ -163,6 +163,7 @@
</Compile>
<Compile Include="Models\RemoteDesktopConnection\IRemoteDesktopClientInfo.cs" />
<Compile Include="Models\RemoteDesktopConnection\RemoteDesktopConnectionStatusResult.cs" />
<Compile Include="Models\TicketOverview\TicketOverviewCountsResult.cs" />
<Compile Include="RemoteDesktopCommunicationBase.cs" />
<Compile Include="ExternalToolExecutor.cs" />
<Compile Include="F4sdCockpitCommunicationM42Base.cs" />

View File

@@ -73,7 +73,7 @@ namespace C4IT.FASD.Cockpit.Communication
#region Ticketübersicht
public abstract Task<List<cF4sdApiSearchResultRelation>> GetTicketOverviewRelations(string key, bool useRoleScope, int count);
public abstract Task<Dictionary<string, int>> GetTicketOverviewCounts(IEnumerable<string> keys, bool useRoleScope);
public abstract Task<cF4sdTicketOverviewCountsResult> GetTicketOverviewCounts(IEnumerable<string> keys, bool useRoleScope);
#endregion
@@ -90,6 +90,29 @@ namespace C4IT.FASD.Cockpit.Communication
public abstract Task<bool> UpdateHealthcardTableData(cF4SDWriteParameters dataParameter);
public abstract Task<bool> Matrix42TicketFinalization(cApiM42Ticket ticketData);
public virtual async Task<cTicketFinalizationResult> 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<cTicketExternalLinkResult> GetTicketExternalLinkAsync(Guid ticketId, enumTicketExternalOpenMode mode)
{
return Task.FromResult(new cTicketExternalLinkResult
{
Success = false,
Mode = enumTicketExternalOpenMode.None
});
}
public abstract Task<List<List<object>>> GetQuickActionHistory(string QuickActionName, int OrgId, int DeviceId, int? UserId);
public abstract Task<bool> GetAgentApiAccessInfo();

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
namespace C4IT.FASD.Cockpit.Communication
{
public sealed class cF4sdTicketOverviewCountsResult
{
public Dictionary<string, int> Counts { get; set; } = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
public cF4sdTicketOverviewCapabilities Capabilities { get; set; } = new cF4sdTicketOverviewCapabilities();
}
public sealed class cF4sdTicketOverviewCapabilities
{
public bool TicketAndServiceRequestsEnabled { get; set; } = true;
}
}

View File

@@ -1147,7 +1147,7 @@ namespace C4IT.FASD.Cockpit.Communication
#region Ticketübersicht
public override async Task<Dictionary<string, int>> GetTicketOverviewCounts(IEnumerable<string> keys, bool useRoleScope)
public override async Task<cF4sdTicketOverviewCountsResult> GetTicketOverviewCounts(IEnumerable<string> keys, bool useRoleScope)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
@@ -1182,13 +1182,13 @@ namespace C4IT.FASD.Cockpit.Communication
await CheckConnectionStatus.Invoke();
LogEntry($"Error on requesting ticket overview counts ({scope}). Status: {result.Status}", LogLevels.Warning);
return new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
return new cF4sdTicketOverviewCountsResult();
}
if (Debug_apiValues) SaveApiResultValueJson("TicketOverview.GetCounts", result.Result, url);
var response = JsonConvert.DeserializeObject<TicketOverviewCountsResponse>(result.Result);
return response?.ToDictionary(normalizedKeys) ?? new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
return response?.ToResult(normalizedKeys) ?? new cF4sdTicketOverviewCountsResult();
}
catch (Exception E)
{
@@ -1204,7 +1204,7 @@ namespace C4IT.FASD.Cockpit.Communication
LogMethodEnd(CM);
}
return new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
return new cF4sdTicketOverviewCountsResult();
}
public override async Task<List<cF4sdApiSearchResultRelation>> GetTicketOverviewRelations(string key, bool useRoleScope, int count)
@@ -1515,6 +1515,12 @@ namespace C4IT.FASD.Cockpit.Communication
return output;
}
public override async Task<bool> Matrix42TicketFinalization(cApiM42Ticket ticketData)
{
var result = await FinalizeTicketAsync(ticketData);
return result?.Success == true;
}
public override async Task<cTicketFinalizationResult> 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<cTicketFinalizationResult>();
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<string>(), 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<cTicketExternalLinkResult> 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<cTicketExternalLinkResult>(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<bool> GetAgentApiAccessInfo()

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using C4IT.FASD.Cockpit.Communication;
using Newtonsoft.Json;
namespace FasdCockpitCommunication.TicketOverview
@@ -10,7 +11,10 @@ namespace FasdCockpitCommunication.TicketOverview
[JsonProperty("counts")]
public Dictionary<string, int> Counts { get; set; } = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
public Dictionary<string, int> ToDictionary(IEnumerable<string> expectedKeys)
[JsonProperty("capabilities")]
public TicketOverviewCapabilitiesResponse Capabilities { get; set; } = new TicketOverviewCapabilitiesResponse();
public cF4sdTicketOverviewCountsResult ToResult(IEnumerable<string> expectedKeys)
{
var comparer = StringComparer.OrdinalIgnoreCase;
var output = new Dictionary<string, int>(comparer);
@@ -28,7 +32,7 @@ namespace FasdCockpitCommunication.TicketOverview
output[key] = 0;
}
return output;
return CreateResult(output);
}
if (Counts != null)
@@ -42,7 +46,25 @@ namespace FasdCockpitCommunication.TicketOverview
}
}
return output;
return CreateResult(output);
}
private cF4sdTicketOverviewCountsResult CreateResult(Dictionary<string, int> counts)
{
return new cF4sdTicketOverviewCountsResult
{
Counts = counts,
Capabilities = new cF4sdTicketOverviewCapabilities
{
TicketAndServiceRequestsEnabled = Capabilities?.TicketAndServiceRequestsEnabled ?? true
}
};
}
}
internal sealed class TicketOverviewCapabilitiesResponse
{
[JsonProperty("ticketAndServiceRequestsEnabled")]
public bool TicketAndServiceRequestsEnabled { get; set; } = true;
}
}

View File

@@ -12,12 +12,31 @@
<TicketConfiguration>
<DisableAutomaticTimeTracking Policy="Mandatory" Value="true" />
<CompletitionPolicy Policy="Mandatory" Value="IfRequired" />
<UseSimplifiedCaseCompletionDialog Policy="Mandatory" Value="false" />
<SimplifiedCaseCompletionTicketOpenMode Policy="Default" Value="Preview" />
<NotesMandatory Policy="Mandatory" Value="true" />
<ShowOverview Policy="Mandatory" Value="true" />
<OpenActivitiesExternally Policy="Mandatory" Value="false">
<OpenActivityOverride ActivityType="SPSActivityTypeTicket" Value="false" />
<OpenActivityOverride ActivityType="SPSActivityTypeServiceRequest" Value="true" />
</OpenActivitiesExternally>
<TicketProcessing Policy="Mandatory" Value="both">
<TicketTypeProcessing Type="Ticket" Value="intern" />
<TicketTypeProcessing Type="UnclassifiedTicket" Value="intern" />
<TicketTypeProcessing Type="Incident" Value="intern" />
</TicketProcessing>
<TicketFilters>
<Filter Provider="Matrix42" Field="Queue" Enabled="false" Match="include" EmptyHandling="include">
<Value ID="2a7e8099-3d57-f011-1988-00155d320605" Name="HR" />
<Value ID="2a7a8099-3d47-f011-1988-00155d320505" Name="FM" />
</Filter>
<Filter Field="AssignmentGroup" Enabled="false" Match="include" EmptyHandling="exclude">
<Value ID="0a76de08-136c-764a-b410-5610e8076712" Name="HR Service Desk Agent" />
<Value ID="0b76de08-8668-a026-b410-5610e8076f95" Name="HR-Manager" />
<Value ID="0a76de08-d589-4c94-b410-5610e8076919" Name="HR-Spezialist" />
<Value ID="e87dde08-235c-04e5-b410-562b64035575" Name="Facility-Koordinator" />
</Filter>
<Filter Provider="Matrix42" Field="Workspace" Enabled="false" Match="include" EmptyHandling="exclude">
<Value ID="1676de08-27f3-550c-b410-5610e807aa02" Name="HR Service Management" />
<Value ID="ed7dde08-199e-c27b-b410-562b64037aa1" Name="Gebäudemanagement" />
</Filter>
</TicketFilters>
<OverviewPollingPersonal Policy="Mandatory" Value="10" />
<OverviewPollingRole Policy="Mandatory" Value="5" />
</TicketConfiguration>

View File

@@ -268,7 +268,7 @@ namespace C4IT.FASD.Cockpit.Communication
return ticket;
}
public override Task<Dictionary<string, int>> GetTicketOverviewCounts(IEnumerable<string> keys, bool useRoleScope)
public override Task<cF4sdTicketOverviewCountsResult> GetTicketOverviewCounts(IEnumerable<string> keys, bool useRoleScope)
{
var scopeKey = useRoleScope ? "Role" : "Personal";
var comparer = StringComparer.OrdinalIgnoreCase;
@@ -299,7 +299,10 @@ namespace C4IT.FASD.Cockpit.Communication
}
}
return Task.FromResult(result);
return Task.FromResult(new cF4sdTicketOverviewCountsResult
{
Counts = result
});
}
public override async Task<List<cF4sdApiSearchResultRelation>> GetTicketOverviewRelations(string key, bool useRoleScope, int count)

View File

@@ -308,6 +308,58 @@
}
],
"Tickets": [
{
"TicketId": "8f3ef61d-6f59-44ee-ae2e-38e7dd9d6f61",
"UserId": "a2164ecd-791f-482c-bea3-f089f14bec8a",
"TileKey": "ServiceRequestsNew",
"UseRoleScope": false,
"ActivityType": "SPSActivityTypeServiceRequest",
"DisplayName": "SRQ00001",
"Summary": "Neue Software fuer den Arbeitsplatz bereitstellen",
"StatusId": "New",
"UserDisplayName": "Busch, Andrea",
"UserAccount": "AB014",
"UserDomain": "CONTOSO"
},
{
"TicketId": "8f3ef61d-6f59-44ee-ae2e-38e7dd9d6f61",
"UserId": "a2164ecd-791f-482c-bea3-f089f14bec8a",
"TileKey": "ServiceRequestsActive",
"UseRoleScope": false,
"ActivityType": "SPSActivityTypeServiceRequest",
"DisplayName": "SRQ00001",
"Summary": "Neue Software fuer den Arbeitsplatz bereitstellen",
"StatusId": "InProgress",
"UserDisplayName": "Busch, Andrea",
"UserAccount": "AB014",
"UserDomain": "CONTOSO"
},
{
"TicketId": "8f3ef61d-6f59-44ee-ae2e-38e7dd9d6f61",
"UserId": "a2164ecd-791f-482c-bea3-f089f14bec8a",
"TileKey": "ServiceRequestsCritical",
"UseRoleScope": false,
"ActivityType": "SPSActivityTypeServiceRequest",
"DisplayName": "SRQ00001",
"Summary": "Neue Software fuer den Arbeitsplatz bereitstellen",
"StatusId": "InProgress",
"UserDisplayName": "Busch, Andrea",
"UserAccount": "AB014",
"UserDomain": "CONTOSO"
},
{
"TicketId": "8f3ef61d-6f59-44ee-ae2e-38e7dd9d6f61",
"UserId": "a2164ecd-791f-482c-bea3-f089f14bec8a",
"TileKey": "ServiceRequestsNewInfo",
"UseRoleScope": false,
"ActivityType": "SPSActivityTypeServiceRequest",
"DisplayName": "SRQ00001",
"Summary": "Neue Software fuer den Arbeitsplatz bereitstellen",
"StatusId": "OnHold",
"UserDisplayName": "Busch, Andrea",
"UserAccount": "AB014",
"UserDomain": "CONTOSO"
},
{
"TicketId": "7e852bb9-420b-4caa-b79a-9178d793fc06",
"UserId": "a2c35ad1-7cc7-4b2b-9aa5-d03fdaecd155",

View File

@@ -4,10 +4,14 @@ using C4IT.FASD.Security;
using C4IT.Graphics;
using C4IT.Logging;
using C4IT.MultiLanguage;
using C4IT.XML;
using F4SD.ActionConnector.Bus;
using F4SD.ActionConnector.Models;
using FasdDesktopUi.Basics;
using FasdDesktopUi.Basics.Helper;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.Services.Models;
using FasdDesktopUi.Basics.UiActions.NativeActions;
using FasdDesktopUi.Pages.CustomMessageBox;
using FasdDesktopUi.Pages.PhoneSettingsPage;
using FasdDesktopUi.Pages.SearchPage;
@@ -46,6 +50,8 @@ namespace FasdDesktopUi
#endregion
private ActionBus _actionBus;
private async void Application_Startup(object sender, StartupEventArgs e)
{
var CM = MethodBase.GetCurrentMethod();
@@ -127,7 +133,24 @@ namespace FasdDesktopUi
InitializeNotifyIcon();
//CustomMessageBox.ShowCustomContent("Close case", enumHealthCardStateLevel.None, null, true, new CloseCaseDialogWithTicket());
try
{
//_actionBus = new ActionBus(new ActionBusOptions()
//{
// ConfigRootElement = cXmlParser.OpenXmlDocument(new List<cXmlFileLocation>() { new cXmlFileLocation()
// {
// LocationType = cXmlFileLocation.enumXmlLocationType.StringContent,
// StringContent = string.Empty
// } }, out cXmlParser parser, ExternalCommunicationConfiguration.ConfigRootElement),
// Parser = parser,
// QuickActionExecutor = QuickActionExecutor.RunAction
//});
//_actionBus.Start();
}
catch (Exception ex)
{
LogException(ex);
}
await cAppStartUp.StartAsync(e.Args);
notifyIcon.Visible = true;
@@ -491,7 +514,7 @@ namespace FasdDesktopUi
}
}
#endregion
#endregion
private async void Application_Exit(object sender, ExitEventArgs e)
{
@@ -545,6 +568,8 @@ namespace FasdDesktopUi
}
catch { }
_actionBus?.Stop();
if (closeUserSessionTask is ConfiguredTaskAwaitable _t)
await _t;
}

View File

@@ -1,18 +1,11 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using static C4IT.Logging.cLogManager;
using static System.Net.Mime.MediaTypeNames;
namespace FasdDesktopUi.Basics.Helper
{
@@ -20,7 +13,7 @@ namespace FasdDesktopUi.Basics.Helper
{
#region Unicode
public static void TraverseBlockAsUnicode(BlockCollection blocks, StringBuilder stringBuilder, bool isBlockFromList = false)
public static void TraverseBlockAsUnicode(BlockCollection blocks, StringBuilder stringBuilder)
{
try
{
@@ -54,15 +47,11 @@ namespace FasdDesktopUi.Basics.Helper
private static void TraverseParagraphAsUnicode(Paragraph paragraph, StringBuilder stringBuilder)
{
try
{
foreach (var inline in paragraph.Inlines)
foreach (Run run in paragraph.Inlines.OfType<Run>())
{
if (inline is Run run)
{
stringBuilder.Append(run.Text);
}
stringBuilder.Append(run.Text);
}
}
catch (Exception E)
@@ -73,32 +62,24 @@ namespace FasdDesktopUi.Basics.Helper
private static void TraverseListAsUnicode(List list, StringBuilder stringBuilder)
{
try
{
if (list.MarkerStyle == TextMarkerStyle.Decimal)
{
for (int i = 0; i < list.ListItems.Count; i++)
{
stringBuilder.Append(i + 1 + ". ");
TraverseBlockAsUnicode(list.ListItems.ElementAt(i).Blocks, stringBuilder, true);
TraverseBlockAsUnicode(list.ListItems.ElementAt(i).Blocks, stringBuilder);
}
}
else
{
foreach (var item in list.ListItems)
{
stringBuilder.Append("- ");
TraverseBlockAsUnicode(item.Blocks, stringBuilder, true);
TraverseBlockAsUnicode(item.Blocks, stringBuilder);
}
}
}
catch (Exception E)
{
@@ -108,18 +89,14 @@ namespace FasdDesktopUi.Basics.Helper
private static void TraverseImageAsUnicode(System.Windows.Controls.Image image, StringBuilder stringBuilder)
{
try
{
stringBuilder.Append("[Image]");
stringBuilder.Append($"[Image:\"{image.Name}\"]");
}
catch (Exception E)
{
LogException(E);
}
}
#endregion
@@ -151,12 +128,7 @@ namespace FasdDesktopUi.Basics.Helper
else if (block is BlockUIContainer container)
{
if (container.Child is System.Windows.Controls.Image image)
{
TraverseImageAsHtml(image, stringBuilder);
}
}
}
}
@@ -223,7 +195,6 @@ namespace FasdDesktopUi.Basics.Helper
try
{
byte[] arr;
using (MemoryStream ms = new MemoryStream())
{
@@ -242,7 +213,6 @@ namespace FasdDesktopUi.Basics.Helper
{
LogException(E);
}
}
public static void TraverseRunAsHtml(Run run, StringBuilder stringBuilder)

View File

@@ -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;
@@ -13,7 +15,36 @@ namespace FasdDesktopUi.Basics.Helper
{
internal static class TicketExternalLinkHelper
{
internal static bool TryOpenTicketRelationExternally(cF4sdApiSearchResultRelation relation)
private const string DefaultTicketActivityType = "SPSActivityTypeTicket";
internal static async Task<bool> 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
{
@@ -25,15 +56,21 @@ namespace FasdDesktopUi.Basics.Helper
var processing = ShouldOpenExternally(ticketType);
// check if we have a valid user id in the id list => if not we could open this ticket only extern.
var hasUser = relation.Identities.Any(e => (e.Class == enumFasdInformationClass.User && e.Id != null && e.Id != Guid.Empty));
if (!hasUser)
var hasUser = HasValidUserIdentity(relation);
if (forceExternal || !hasUser)
processing = enumTicketProcessing.Extern;
if (processing == enumTicketProcessing.Intern)
return false;
if (relation?.Infos?.TryGetValue("TicketLink", out var ticketLink) == true && !string.IsNullOrWhiteSpace(ticketLink))
new cBrowsers().Start("default", ticketLink);
var m42Server = cCockpitConfiguration.Instance?.m42ServerConfiguration?.Server;
if (!TryGetTicketLink(relation, m42Server, out var ticketLink))
{
LogEntry($"Could not resolve an external Matrix42 link for ticket '{relation.DisplayName ?? relation.Name}'.", LogLevels.Warning);
return false;
}
new cBrowsers().Start("default", ticketLink);
return processing == enumTicketProcessing.Extern;
}
@@ -45,6 +82,73 @@ namespace FasdDesktopUi.Basics.Helper
return false;
}
internal static bool HasValidUserIdentity(cF4sdApiSearchResultRelation relation)
{
return relation?.Identities?.Any(identity =>
identity.Class == enumFasdInformationClass.User && identity.Id != Guid.Empty) == true;
}
internal static bool TryGetTicketLink(cF4sdApiSearchResultRelation relation, string m42Server, out string ticketLink)
{
ticketLink = null;
if (relation == null || relation.Type != enumF4sdSearchResultClass.Ticket)
return false;
if (TryGetConfiguredLink(relation, "TicketLink", out ticketLink) ||
TryGetConfiguredLink(relation, "DirectLinkPreview", out ticketLink))
{
return true;
}
if (relation.id == Guid.Empty || string.IsNullOrWhiteSpace(m42Server))
return false;
var server = m42Server.Trim();
if (!server.Contains("://"))
server = $"https://{server}";
if (!Uri.TryCreate(server, UriKind.Absolute, out var serverUri) ||
(serverUri.Scheme != Uri.UriSchemeHttp && serverUri.Scheme != Uri.UriSchemeHttps))
{
return false;
}
var activityType = GetActivityType(relation);
var encodedActivityType = Uri.EscapeDataString(activityType);
ticketLink = $"{serverUri.Scheme}://{serverUri.Authority}/wm/app-ServiceDesk/notSet/preview-object/{encodedActivityType}/{relation.id:D}/0/";
return true;
}
private static bool TryGetConfiguredLink(cF4sdApiSearchResultRelation relation, string key, out string ticketLink)
{
ticketLink = null;
if (relation?.Infos?.TryGetValue(key, out var configuredLink) != true || string.IsNullOrWhiteSpace(configuredLink))
return false;
ticketLink = configuredLink;
return true;
}
private static string GetActivityType(cF4sdApiSearchResultRelation relation)
{
if (relation?.Infos?.TryGetValue("ActivityType", out var activityType) == true && !string.IsNullOrWhiteSpace(activityType))
return activityType.Trim();
if (relation?.Infos?.TryGetValue("TicketType", out var ticketTypeValue) == true &&
Enum.TryParse(ticketTypeValue, true, out enumTicketType ticketType))
{
switch (ticketType)
{
case enumTicketType.Incident:
return "SPSActivityTypeIncident";
case enumTicketType.ServiceRequest:
return "SPSActivityTypeServiceRequest";
}
}
return DefaultTicketActivityType;
}
private static enumTicketType GetTicketType(cF4sdApiSearchResultRelation relation)
{
if (relation?.Infos != null && relation.Infos.TryGetValue("TicketType", out var ticketTypeValue))

View File

@@ -1,13 +1,15 @@
using System;
using System.Collections.Generic;
using System.Windows;
using C4IT.F4SD.DisplayFormatting;
using C4IT.F4SD.DisplayFormatting;
using C4IT.F4SD.SupportCaseProtocoll.Models;
using C4IT.FASD.Base;
using FasdCockpitBase;
using FasdDesktopUi.Basics.Helper;
using FasdDesktopUi.Basics.Services.ProtocollService;
using FasdDesktopUi.Basics.Services.SupportCase.Processors;
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Media.Imaging;
using static C4IT.Logging.cLogManager;
namespace FasdDesktopUi.Basics.Models
@@ -193,6 +195,33 @@ namespace FasdDesktopUi.Basics.Models
}
}
public class cNamedParameterEntryCaseNotes : cNamedParameterEntryBase
{
private readonly bool _getHtmlAsDefaultValue;
public cNamedParameterEntryCaseNotes(cSupportCaseDataProvider dataProvider, bool getHtmlAsDefaultValue) : base(dataProvider)
{
_getHtmlAsDefaultValue = getHtmlAsDefaultValue;
}
public override string GetValue()
{
if (_getHtmlAsDefaultValue)
return GetHtmlValue();
StringBuilder caseNotesStringBuilder = new StringBuilder();
cRichTextBoxHelper.TraverseBlockAsUnicode(dataProvider.CaseNotes.Blocks, caseNotesStringBuilder);
return caseNotesStringBuilder.ToString();
}
public override string GetHtmlValue()
{
StringBuilder caseNotesHtmlStringBuilder = new StringBuilder();
cRichTextBoxHelper.TraverseBlockAsHtml(dataProvider.CaseNotes.Blocks, caseNotesHtmlStringBuilder);
return caseNotesHtmlStringBuilder.ToString();
}
}
public class cNamedParameterList : Dictionary<string, cNamedParameterEntryBase>
{
public cNamedParameterList()
@@ -204,8 +233,11 @@ namespace FasdDesktopUi.Basics.Models
{
try
{
Add("F4SD_QuickActionProtocolLast", new cNamedParameterEntryQuickActionResult(dataProvider));
Add("F4SD_QuickActionProtocol", new cNamedParameterEntryQuickActionResultProtocol(dataProvider));
Add(SupportCaseProcessor.QuickActionProtocolLastNamedParameterName, new cNamedParameterEntryQuickActionResult(dataProvider));
Add(SupportCaseProcessor.QuickActionProtocolNamedParameterName, new cNamedParameterEntryQuickActionResultProtocol(dataProvider));
Add(SupportCaseProcessor.CaseNotesNamedParameterName, new cNamedParameterEntryCaseNotes(dataProvider, false));
Add(SupportCaseProcessor.CaseNotesHtmlNamedParameterName, new cNamedParameterEntryCaseNotes(dataProvider, true));
}
catch (Exception E)
{

View File

@@ -1,9 +1,6 @@
using FasdDesktopUi.Basics.UserControls;
using FasdDesktopUi.Basics.Models.QuickActionOutput;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FasdDesktopUi.Basics.Models
{
@@ -13,7 +10,7 @@ namespace FasdDesktopUi.Basics.Models
public string Name { get; set; }
public string AffectedDeviceName { get; set; }
public bool WasRunningOnAffectedDevice { get; set; }
public QuickActionStatusMonitor.cQuickActionOutput QuickActionOutput { get; set; }
public List<QuickActionStatusMonitor.cQuickActionMeasureValue> MeasureValues { get; set; }
public cQuickActionOutput QuickActionOutput { get; set; }
public List<cQuickActionMeasureValue> MeasureValues { get; set; }
}
}

View File

@@ -0,0 +1,39 @@
using C4IT.F4SD.DisplayFormatting;
using C4IT.FASD.Base;
using C4IT.MultiLanguage;
namespace FasdDesktopUi.Basics.Models.QuickActionOutput
{
public class cQuickActionMeasureValue
{
public int Id { get; set; }
public cMultiLanguageDictionary Names { get; set; }
public object Value { get; set; }
public object PostValue { get; set; }
public object Difference
{
get
{
switch (Display)
{
case RawValueType.INTEGER:
case RawValueType.PERCENT:
case RawValueType.PERCENT100:
case RawValueType.PERCENT1000:
case RawValueType.BYTES:
var valueDouble = cF4SDHealthCardRawData.GetDouble(Value);
var postValueDouble = cF4SDHealthCardRawData.GetDouble(PostValue);
if (valueDouble != null && postValueDouble != null)
return postValueDouble - valueDouble;
break;
}
return null;
}
}
public RawValueType Display { get; set; }
}
}

View File

@@ -0,0 +1,137 @@
using C4IT.F4SD.DisplayFormatting;
using C4IT.FASD.Base;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using static C4IT.Logging.cLogManager;
namespace FasdDesktopUi.Basics.Models.QuickActionOutput
{
public abstract class cQuickActionOutput
{
protected const string veiledText = "********";
protected readonly IRawValueFormatter _rawValueFormatter = new RawValueFormatter();
public cQuickActionOutput(cF4sdQuickActionRevision.cOutput scriptOutput)
{
_rawValueFormatter.SetDefaultCulture(cFasdCockpitConfig.Instance.SelectedCulture);
_rawValueFormatter.SetDefaultTimeZone(TimeZoneInfo.Local);
ErrorCode = scriptOutput.ErrorCode;
ErrorDescription = scriptOutput.ErrorDescription;
ResultCode = scriptOutput.ResultCode.GetValueOrDefault();
IsError = HasError(scriptOutput);
}
public cSupportCaseDataProvider DataProvider { get; private set; }
public enumQuickActionSuccess? ResultCode { get; set; }
public int? ErrorCode { get; set; }
public string ErrorDescription { get; set; }
public bool IsError { get; private set; }
internal static KeyValuePair<string, RawValueType> GetDisplayType(dynamic columnTypeObject)
{
var output = new KeyValuePair<string, RawValueType>();
try
{
var columnType = columnTypeObject.ToObject<dynamic>();
string name = string.Empty;
RawValueType type = RawValueType.STRING;
if (columnType.Name != null)
if (columnType.Name is Newtonsoft.Json.Linq.JValue nameJValue)
name = nameJValue.Value.ToString();
if (columnType.Type != null)
if (columnType.Type is Newtonsoft.Json.Linq.JValue typeJValue)
if (Enum.TryParse(typeJValue.Value.ToString(), out RawValueType typeJValueType))
type = typeJValueType;
output = new KeyValuePair<string, RawValueType>(name, type);
}
catch (Exception E)
{
LogException(E);
}
return output;
}
public static cQuickActionOutput GetQuickActionOutput(cF4sdQuickActionRevision.cOutput scriptOutput, cSupportCaseDataProvider dataProvider)
{
if (scriptOutput == null)
return null;
try
{
if (scriptOutput?.Values == null)
{
var output = new cQuickActionOutputSingle(scriptOutput) { DataProvider = dataProvider };
return output;
}
else if (scriptOutput.Values is JArray scriptValueJArray)
{
var scriptValues = scriptValueJArray.ToObject<List<dynamic>>();
if (scriptValues != null && scriptValues.Count > 0)
{
var output = new cQuickActionOutputList(scriptOutput) { DataProvider = dataProvider };
foreach (var scriptValue in scriptValues)
{
if (scriptValue is JObject scriptValueJObject)
{
try
{
var scriptValueHelperDictionary = scriptValueJObject.ToObject<Dictionary<string, object>>();
output.Values.Add(scriptValueHelperDictionary.ToList());
}
catch (Exception E)
{
LogException(E);
}
}
}
return output;
}
}
else if (scriptOutput.Values is JObject scriptValueJObject)
{
try
{
var output = new cQuickActionOutputObject(scriptOutput) { DataProvider = dataProvider };
return output;
}
catch (Exception E)
{
LogException(E);
}
}
else
{
return new cQuickActionOutputSingle(scriptOutput) { DataProvider = dataProvider };
}
}
catch (Exception E)
{
LogException(E);
}
return null;
}
public static bool HasError(cF4sdQuickActionRevision.cOutput quickActionOutput)
{
if ((quickActionOutput?.ResultCode == null || quickActionOutput?.ResultCode == enumQuickActionSuccess.finished || quickActionOutput?.ResultCode == enumQuickActionSuccess.successfull)
&& (quickActionOutput?.ErrorCode == null || quickActionOutput?.ErrorCode == 0)
&& string.IsNullOrEmpty(quickActionOutput?.ErrorDescription))
return false;
return true;
}
}
}

View File

@@ -0,0 +1,101 @@
using C4IT.F4SD.DisplayFormatting;
using C4IT.FASD.Base;
using System;
using System.Collections.Generic;
using System.Linq;
using static C4IT.Logging.cLogManager;
namespace FasdDesktopUi.Basics.Models.QuickActionOutput
{
public class cQuickActionOutputList : cQuickActionOutput
{
public cQuickActionOutputList(cF4sdQuickActionRevision.cOutput scriptOutput) : base(scriptOutput)
{
InstantiateDisplayTypes(scriptOutput);
}
public Dictionary<string, RawValueType> DisplayTypes { get; set; } = new Dictionary<string, RawValueType>();
public List<List<KeyValuePair<string, object>>> Values { get; set; } = new List<List<KeyValuePair<string, object>>>();
private void InstantiateDisplayTypes(cF4sdQuickActionRevision.cOutput scriptOutput)
{
try
{
if (scriptOutput.ColumnTypes is Newtonsoft.Json.Linq.JArray columnTypeJArray)
{
var columnTypes = columnTypeJArray.ToObject<List<dynamic>>();
if (columnTypes != null && columnTypes.Count > 0)
{
foreach (var columnTypeObject in columnTypes)
{
var displayType = GetDisplayType(columnTypeObject);
DisplayTypes[displayType.Key] = displayType.Value;
}
}
}
else if (scriptOutput.ColumnTypes is Newtonsoft.Json.Linq.JObject columnTypeJObject)
{
try
{
var displayType = GetDisplayType(columnTypeJObject);
DisplayTypes[displayType.Key] = displayType.Value;
}
catch (Exception E)
{
LogException(E);
}
}
}
catch (Exception E)
{
LogException(E);
}
}
public string GetDisplayValue(int row, int column, Dictionary<string, cColumnOutputFormatting> columnOutputFormattings, bool getSecretValue = false)
{
string output = null;
try
{
RawValueType displayType = RawValueType.STRING;
DisplayTypes.TryGetValue(Values[row][column].Key, out displayType);
output = _rawValueFormatter.GetDisplayValue(Values[row][column].Value, displayType);
if (columnOutputFormattings != null && columnOutputFormattings.TryGetValue(Values[row][column].Key, out var outputFormatting))
{
if (outputFormatting.Hidden)
return string.Empty;
if (outputFormatting.IsSecret && !getSecretValue)
return veiledText;
if (outputFormatting.DisplayType != null)
output = _rawValueFormatter.GetDisplayValue(Values[row][column].Value, outputFormatting.DisplayType.Value);
if (outputFormatting.Translation != null)
{
var abstractTranslation = cF4SDHealthCardConfig.GetTranslationsWithName(DataProvider?.HealthCardDataHelper?.SelectedHealthCard, outputFormatting.Translation);
if (abstractTranslation != null && abstractTranslation is cHealthCardTranslator translator)
{
output = translator.DefaultTranslation?.Translation.GetValue() ?? Values[row].ToList()[column].Value.ToString();
foreach (var translation in translator.Translations)
{
if (translation.Values.Any(value => value.Equals(Values[row].ToList()[column].Value.ToString(), StringComparison.InvariantCultureIgnoreCase)))
output = translation.Translation.GetValue();
}
}
}
}
}
catch (Exception E)
{
LogException(E);
}
return output;
}
}
}

View File

@@ -0,0 +1,136 @@
using C4IT.F4SD.DisplayFormatting;
using C4IT.FASD.Base;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using static C4IT.Logging.cLogManager;
namespace FasdDesktopUi.Basics.Models.QuickActionOutput
{
public class cQuickActionOutputObject : cQuickActionOutput
{
public Dictionary<string, RawValueType> DisplayTypes { get; set; } = new Dictionary<string, RawValueType>();
public List<KeyValuePair<string, object>> Values { get; set; } = new List<KeyValuePair<string, object>>();
public cQuickActionOutputObject(cF4sdQuickActionRevision.cOutput scriptOutput) : base(scriptOutput)
{
try
{
InstantiateDisplayTypes(scriptOutput);
InstantiateValues(scriptOutput);
}
catch (Exception E)
{
LogException(E);
}
}
private void InstantiateDisplayTypes(cF4sdQuickActionRevision.cOutput scriptOutput)
{
try
{
if (scriptOutput.ColumnTypes is Newtonsoft.Json.Linq.JArray columnTypeJArray)
{
var columnTypes = columnTypeJArray.ToObject<List<dynamic>>();
if (columnTypes != null && columnTypes.Count > 0)
{
foreach (var columnTypeObject in columnTypes)
{
var displayType = GetDisplayType(columnTypeObject);
DisplayTypes[displayType.Key] = displayType.Value;
}
}
}
else if (scriptOutput.ColumnTypes is Newtonsoft.Json.Linq.JObject columnTypeJObject)
{
try
{
var displayType = GetDisplayType(columnTypeJObject);
DisplayTypes[displayType.Key] = displayType.Value;
}
catch (Exception E)
{
LogException(E);
}
}
}
catch (Exception E)
{
LogException(E);
}
}
private void InstantiateValues(cF4sdQuickActionRevision.cOutput scriptOutput)
{
try
{
if (!(scriptOutput.Values is JObject valuesJObject))
return;
if (valuesJObject.Properties() == null)
return;
foreach (var property in valuesJObject.Properties())
{
Values.Add(new KeyValuePair<string, object>(property.Name, property.Value));
}
}
catch (Exception E)
{
LogException(E);
}
}
public string GetDisplayValue(int index, Dictionary<string, cColumnOutputFormatting> columnOutputFormattings, bool getSecretValue = false)
{
string output = null;
try
{
if (Values.Count <= index)
return null;
var selectedItem = Values[index];
RawValueType displayType = RawValueType.STRING;
DisplayTypes.TryGetValue(selectedItem.Key, out displayType);
output = _rawValueFormatter.GetDisplayValue(selectedItem.Value, displayType);
if (columnOutputFormattings != null && columnOutputFormattings.TryGetValue(selectedItem.Key, out var outputFormatting))
{
if (outputFormatting.Hidden)
return string.Empty;
if (outputFormatting.IsSecret && !getSecretValue)
return veiledText;
if (outputFormatting.DisplayType != null)
output = _rawValueFormatter.GetDisplayValue(selectedItem.Value, outputFormatting.DisplayType.Value);
if (outputFormatting.Translation != null)
{
var abstractTranslation = cF4SDHealthCardConfig.GetTranslationsWithName(DataProvider?.HealthCardDataHelper?.SelectedHealthCard, outputFormatting.Translation);
if (abstractTranslation != null && abstractTranslation is cHealthCardTranslator translator)
{
output = translator.DefaultTranslation?.Translation.GetValue() ?? selectedItem.Value.ToString();
foreach (var translation in translator.Translations)
{
if (translation.Values.Any(value => value.Equals(selectedItem.Value.ToString(), StringComparison.InvariantCultureIgnoreCase)))
output = translation.Translation.GetValue();
}
}
}
}
}
catch (Exception E)
{
LogException(E);
}
return output;
}
}
}

View File

@@ -0,0 +1,128 @@
using C4IT.F4SD.DisplayFormatting;
using C4IT.FASD.Base;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using static C4IT.Logging.cLogManager;
namespace FasdDesktopUi.Basics.Models.QuickActionOutput
{
public class cQuickActionOutputSingle : cQuickActionOutput
{
public cQuickActionOutputSingle(cF4sdQuickActionRevision.cOutput scriptOutput) : base(scriptOutput)
{
Instantiate(scriptOutput);
}
public RawValueType DisplayType { get; set; }
public string Key { get; set; }
public object Value { get; set; }
private void Instantiate(cF4sdQuickActionRevision.cOutput scriptOutput)
{
try
{
Value = scriptOutput.Values;
if (scriptOutput.Values is JObject scriptValueJObject)
{
string scriptValueKey = null;
try
{
var scriptValueHelperDictionary = scriptValueJObject.ToObject<Dictionary<string, object>>();
scriptValueKey = scriptValueHelperDictionary.Keys.ToList()[0];
Key = scriptValueKey;
Value = scriptValueHelperDictionary[scriptValueKey];
}
catch (Exception E)
{
LogException(E);
}
if (scriptOutput.ColumnTypes is JArray columnTypeJArray)
{
var columnTypes = columnTypeJArray.ToObject<List<dynamic>>();
if (columnTypes != null && columnTypes.Count > 0)
{
foreach (var columnTypeObject in columnTypes)
{
var displayType = cQuickActionOutput.GetDisplayType(columnTypeObject);
if (displayType.Key == scriptValueKey)
DisplayType = displayType.Value;
}
}
}
else if (scriptOutput.ColumnTypes is JObject columnTypeJObject)
{
try
{
var dispayType = cQuickActionOutput.GetDisplayType(columnTypeJObject);
DisplayType = dispayType.Value;
}
catch (Exception E)
{
LogException(E);
}
}
}
}
catch (Exception E)
{
LogException(E);
}
}
public string GetDisplayValue(Dictionary<string, cColumnOutputFormatting> columnOutputFormattings, bool getSecretValue = false)
{
string output = null;
try
{
output = _rawValueFormatter.GetDisplayValue(Value, DisplayType);
cColumnOutputFormatting outputFormatting = null;
if (string.IsNullOrEmpty(Key))
outputFormatting = columnOutputFormattings?.Values.FirstOrDefault();
else
columnOutputFormattings?.TryGetValue(Key, out outputFormatting);
if (outputFormatting != null)
{
if (outputFormatting.IsSecret && !getSecretValue)
return veiledText;
if (outputFormatting.DisplayType != null)
output = _rawValueFormatter.GetDisplayValue(Value, outputFormatting.DisplayType.Value);
if (outputFormatting.Translation != null)
{
var abstractTranslation = cF4SDHealthCardConfig.GetTranslationsWithName(DataProvider?.HealthCardDataHelper?.SelectedHealthCard, outputFormatting.Translation);
if (abstractTranslation != null && abstractTranslation is cHealthCardTranslator translator)
{
output = translator.DefaultTranslation?.Translation.GetValue() ?? Value.ToString();
foreach (var translation in translator.Translations)
{
if (translation.Values.Any(value => value.Equals(Value.ToString(), StringComparison.InvariantCultureIgnoreCase)))
output = translation.Translation.GetValue();
}
}
}
}
}
catch (Exception E)
{
LogException(E);
}
return output;
}
}
}

View File

@@ -62,6 +62,34 @@ namespace FasdDesktopUi.Basics.Models
set { if (_incidentNewInfoSelected != value) { _incidentNewInfoSelected = value; OnPropertyChanged(nameof(IncidentNewInfoSelected)); } }
}
private bool _serviceRequestsNewSelected;
public bool ServiceRequestsNewSelected
{
get => _serviceRequestsNewSelected;
set { if (_serviceRequestsNewSelected != value) { _serviceRequestsNewSelected = value; OnPropertyChanged(nameof(ServiceRequestsNewSelected)); } }
}
private bool _serviceRequestsActiveSelected;
public bool ServiceRequestsActiveSelected
{
get => _serviceRequestsActiveSelected;
set { if (_serviceRequestsActiveSelected != value) { _serviceRequestsActiveSelected = value; OnPropertyChanged(nameof(ServiceRequestsActiveSelected)); } }
}
private bool _serviceRequestsCriticalSelected;
public bool ServiceRequestsCriticalSelected
{
get => _serviceRequestsCriticalSelected;
set { if (_serviceRequestsCriticalSelected != value) { _serviceRequestsCriticalSelected = value; OnPropertyChanged(nameof(ServiceRequestsCriticalSelected)); } }
}
private bool _serviceRequestsNewInfoSelected;
public bool ServiceRequestsNewInfoSelected
{
get => _serviceRequestsNewInfoSelected;
set { if (_serviceRequestsNewInfoSelected != value) { _serviceRequestsNewInfoSelected = value; OnPropertyChanged(nameof(ServiceRequestsNewInfoSelected)); } }
}
private bool _unassignedTicketsSelected;
public bool UnassignedTicketsSelected
{
@@ -102,6 +130,18 @@ namespace FasdDesktopUi.Basics.Models
private bool _incidentNewInfoHighlighted;
public bool IncidentNewInfoHighlighted { get => _incidentNewInfoHighlighted; set { if (_incidentNewInfoHighlighted != value) { _incidentNewInfoHighlighted = value; OnPropertyChanged(nameof(IncidentNewInfoHighlighted)); } } }
private bool _serviceRequestsNewHighlighted;
public bool ServiceRequestsNewHighlighted { get => _serviceRequestsNewHighlighted; set { if (_serviceRequestsNewHighlighted != value) { _serviceRequestsNewHighlighted = value; OnPropertyChanged(nameof(ServiceRequestsNewHighlighted)); } } }
private bool _serviceRequestsActiveHighlighted;
public bool ServiceRequestsActiveHighlighted { get => _serviceRequestsActiveHighlighted; set { if (_serviceRequestsActiveHighlighted != value) { _serviceRequestsActiveHighlighted = value; OnPropertyChanged(nameof(ServiceRequestsActiveHighlighted)); } } }
private bool _serviceRequestsCriticalHighlighted;
public bool ServiceRequestsCriticalHighlighted { get => _serviceRequestsCriticalHighlighted; set { if (_serviceRequestsCriticalHighlighted != value) { _serviceRequestsCriticalHighlighted = value; OnPropertyChanged(nameof(ServiceRequestsCriticalHighlighted)); } } }
private bool _serviceRequestsNewInfoHighlighted;
public bool ServiceRequestsNewInfoHighlighted { get => _serviceRequestsNewInfoHighlighted; set { if (_serviceRequestsNewInfoHighlighted != value) { _serviceRequestsNewInfoHighlighted = value; OnPropertyChanged(nameof(ServiceRequestsNewInfoHighlighted)); } } }
private bool _unassignedTicketsHighlighted;
public bool UnassignedTicketsHighlighted { get => _unassignedTicketsHighlighted; set { if (_unassignedTicketsHighlighted != value) { _unassignedTicketsHighlighted = value; OnPropertyChanged(nameof(UnassignedTicketsHighlighted)); } } }
@@ -134,6 +174,18 @@ namespace FasdDesktopUi.Basics.Models
private string _incidentNewInfoChangeHint;
public string IncidentNewInfoChangeHint { get => _incidentNewInfoChangeHint; set { if (_incidentNewInfoChangeHint != value) { _incidentNewInfoChangeHint = value; OnPropertyChanged(nameof(IncidentNewInfoChangeHint)); } } }
private string _serviceRequestsNewChangeHint;
public string ServiceRequestsNewChangeHint { get => _serviceRequestsNewChangeHint; set { if (_serviceRequestsNewChangeHint != value) { _serviceRequestsNewChangeHint = value; OnPropertyChanged(nameof(ServiceRequestsNewChangeHint)); } } }
private string _serviceRequestsActiveChangeHint;
public string ServiceRequestsActiveChangeHint { get => _serviceRequestsActiveChangeHint; set { if (_serviceRequestsActiveChangeHint != value) { _serviceRequestsActiveChangeHint = value; OnPropertyChanged(nameof(ServiceRequestsActiveChangeHint)); } } }
private string _serviceRequestsCriticalChangeHint;
public string ServiceRequestsCriticalChangeHint { get => _serviceRequestsCriticalChangeHint; set { if (_serviceRequestsCriticalChangeHint != value) { _serviceRequestsCriticalChangeHint = value; OnPropertyChanged(nameof(ServiceRequestsCriticalChangeHint)); } } }
private string _serviceRequestsNewInfoChangeHint;
public string ServiceRequestsNewInfoChangeHint { get => _serviceRequestsNewInfoChangeHint; set { if (_serviceRequestsNewInfoChangeHint != value) { _serviceRequestsNewInfoChangeHint = value; OnPropertyChanged(nameof(ServiceRequestsNewInfoChangeHint)); } } }
private string _unassignedTicketsChangeHint;
public string UnassignedTicketsChangeHint { get => _unassignedTicketsChangeHint; set { if (_unassignedTicketsChangeHint != value) { _unassignedTicketsChangeHint = value; OnPropertyChanged(nameof(UnassignedTicketsChangeHint)); } } }
@@ -169,6 +221,19 @@ namespace FasdDesktopUi.Basics.Models
private int _incidentNewInfo;
public int IncidentNewInfo { get => _incidentNewInfo; set { _incidentNewInfo = value; OnPropertyChanged(nameof(IncidentNewInfo)); } }
// Service Request Properties
private int _serviceRequestsNew;
public int ServiceRequestsNew { get => _serviceRequestsNew; set { _serviceRequestsNew = value; OnPropertyChanged(nameof(ServiceRequestsNew)); } }
private int _serviceRequestsActive;
public int ServiceRequestsActive { get => _serviceRequestsActive; set { _serviceRequestsActive = value; OnPropertyChanged(nameof(ServiceRequestsActive)); } }
private int _serviceRequestsCritical;
public int ServiceRequestsCritical { get => _serviceRequestsCritical; set { _serviceRequestsCritical = value; OnPropertyChanged(nameof(ServiceRequestsCritical)); } }
private int _serviceRequestsNewInfo;
public int ServiceRequestsNewInfo { get => _serviceRequestsNewInfo; set { _serviceRequestsNewInfo = value; OnPropertyChanged(nameof(ServiceRequestsNewInfo)); } }
// Unassigned Ticket Properties
private int _unassignedTickets;
public int UnassignedTickets { get => _unassignedTickets; set { _unassignedTickets = value; OnPropertyChanged(nameof(UnassignedTickets)); } }
@@ -196,6 +261,11 @@ namespace FasdDesktopUi.Basics.Models
IncidentCriticalSelected = false;
IncidentNewInfoSelected = false;
ServiceRequestsNewSelected = false;
ServiceRequestsActiveSelected = false;
ServiceRequestsCriticalSelected = false;
ServiceRequestsNewInfoSelected = false;
UnassignedTicketsSelected = false;
UnassignedTicketsCriticalSelected = false;
}
@@ -212,6 +282,11 @@ namespace FasdDesktopUi.Basics.Models
IncidentCriticalHighlighted = false;
IncidentNewInfoHighlighted = false;
ServiceRequestsNewHighlighted = false;
ServiceRequestsActiveHighlighted = false;
ServiceRequestsCriticalHighlighted = false;
ServiceRequestsNewInfoHighlighted = false;
UnassignedTicketsHighlighted = false;
UnassignedTicketsCriticalHighlighted = false;
@@ -225,6 +300,11 @@ namespace FasdDesktopUi.Basics.Models
IncidentCriticalChangeHint = null;
IncidentNewInfoChangeHint = null;
ServiceRequestsNewChangeHint = null;
ServiceRequestsActiveChangeHint = null;
ServiceRequestsCriticalChangeHint = null;
ServiceRequestsNewInfoChangeHint = null;
UnassignedTicketsChangeHint = null;
UnassignedTicketsCriticalChangeHint = null;
}

View File

@@ -12,7 +12,7 @@ namespace FasdDesktopUi.Basics.Services.Models
{
bool IsDemo();
Task<Dictionary<string, int>> GetTicketOverviewCounts(string[] overviewKeys, bool useRoleScope);
Task<cF4sdTicketOverviewCountsResult> GetTicketOverviewCounts(string[] overviewKeys, bool useRoleScope);
#if isDemo
void RegisterGeneratedTicket(DemoTicketRecord record);
@@ -47,12 +47,15 @@ namespace FasdDesktopUi.Basics.Services.Models
return _communication.IsDemo();
}
public async Task<Dictionary<string, int>> GetTicketOverviewCounts(string[] overviewKeys, bool useRoleScope)
public async Task<cF4sdTicketOverviewCountsResult> GetTicketOverviewCounts(string[] overviewKeys, bool useRoleScope)
{
var rawCounts = await _communication.GetTicketOverviewCounts(overviewKeys, useRoleScope).ConfigureAwait(false);
return rawCounts == null
var result = await _communication.GetTicketOverviewCounts(overviewKeys, useRoleScope).ConfigureAwait(false)
?? new cF4sdTicketOverviewCountsResult();
result.Counts = result.Counts == null
? new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
: new Dictionary<string, int>(rawCounts, StringComparer.OrdinalIgnoreCase);
: new Dictionary<string, int>(result.Counts, StringComparer.OrdinalIgnoreCase);
result.Capabilities = result.Capabilities ?? new cF4sdTicketOverviewCapabilities();
return result;
}
#if isDemo

View File

@@ -3,13 +3,11 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using C4IT.F4SD.DisplayFormatting;
using C4IT.FASD.Base;
using C4IT.MultiLanguage;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.UserControls;
using static C4IT.Logging.cLogManager;
using static FasdDesktopUi.Basics.UserControls.QuickActionStatusMonitor;
using FasdDesktopUi.Basics.Models.QuickActionOutput;
namespace FasdDesktopUi.Basics.Services.ProtocollService
{
@@ -155,7 +153,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
return errorMessage;
}
private static string GetQuickActionAsciiOutput(cFasdQuickAction quickActionDefinition, QuickActionStatusMonitor.cQuickActionOutput quickActionOutput)
private static string GetQuickActionAsciiOutput(cFasdQuickAction quickActionDefinition, cQuickActionOutput quickActionOutput)
{
string output = string.Empty;
@@ -166,7 +164,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
switch (quickActionOutput)
{
case QuickActionStatusMonitor.cQuickActionOutputSingle singleOutput:
case cQuickActionOutputSingle singleOutput:
{
if (singleOutput.Value is null)
return output;
@@ -177,7 +175,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
break;
}
case QuickActionStatusMonitor.cQuickActionOutputList listOutput:
case cQuickActionOutputList listOutput:
{
output += AsciiSeperator;
output += cMultiLanguageSupport.GetItem("QuickAction.Copy.Output") + "\n";
@@ -219,7 +217,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
break;
}
case QuickActionStatusMonitor.cQuickActionOutputObject objectOutput:
case cQuickActionOutputObject objectOutput:
{
output += AsciiSeperator;
output += cMultiLanguageSupport.GetItem("QuickAction.Copy.Output") + "\n";
@@ -259,7 +257,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
return output;
}
private static string GetQuickActionAsciiValueComparisonString(List<QuickActionStatusMonitor.cQuickActionMeasureValue> measureValues)
private static string GetQuickActionAsciiValueComparisonString(List<cQuickActionMeasureValue> measureValues)
{
string output = string.Empty;
@@ -365,7 +363,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
return errorMessage;
}
private static string GetQuickActionHtmlOutput(cFasdQuickAction quickActionDefinition, QuickActionStatusMonitor.cQuickActionOutput quickActionOutput)
private static string GetQuickActionHtmlOutput(cFasdQuickAction quickActionDefinition, cQuickActionOutput quickActionOutput)
{
string output = string.Empty;
@@ -378,7 +376,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
switch (quickActionOutput)
{
case QuickActionStatusMonitor.cQuickActionOutputSingle singleOutput:
case cQuickActionOutputSingle singleOutput:
{
if (singleOutput.Value is null)
return output;
@@ -387,7 +385,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
output += "<p>" + cMultiLanguageSupport.GetItem("QuickAction.Copy.Output.Html") + " " + displayValue + "</p>";
break;
}
case QuickActionStatusMonitor.cQuickActionOutputList listOutput:
case cQuickActionOutputList listOutput:
{
output += "<p>" + cMultiLanguageSupport.GetItem("QuickAction.Copy.Output.Html") + "</p>";
@@ -436,7 +434,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
output += "</table>";
break;
}
case QuickActionStatusMonitor.cQuickActionOutputObject objectOutput:
case cQuickActionOutputObject objectOutput:
{
output += "<p>" + cMultiLanguageSupport.GetItem("QuickAction.Copy.Output.Html") + "</p>";
@@ -479,7 +477,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
return output;
}
private static string GetQuickActionHtmlValueComparison(List<QuickActionStatusMonitor.cQuickActionMeasureValue> measureValues)
private static string GetQuickActionHtmlValueComparison(List<cQuickActionMeasureValue> measureValues)
{
string output = string.Empty;

View File

@@ -105,7 +105,7 @@ namespace FasdDesktopUi.Basics.Services.RemoteDesktop
{
ProcessStartInfo info = new ProcessStartInfo(GetViewerPath())
{
Arguments = $"-connectionId {connectionDetails.ConnectionId} -phoenixServiceUrl {connectionDetails.PhoenixServiceUrl} -secret {connectionDetails.Secret}"
Arguments = $"-connectionId {connectionDetails.ConnectionId} -phoenixServiceUrl {connectionDetails.PhoenixServiceUrl} -secret {connectionDetails.Secret} -hideAi {!cFasdCockpitConfig.Instance.Global.UseAiAnalyzer}"
};
Process process = Process.Start(info);

View File

@@ -3,6 +3,7 @@ using C4IT.FASD.Cockpit.Communication;
using C4IT.Logging;
using FasdDesktopUi.Basics.CustomEvents;
using FasdDesktopUi.Basics.Enums;
using FasdDesktopUi.Basics.Helper;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.Services.Models;
using FasdDesktopUi.Basics.Services.SupportCase.Controllers;
@@ -11,6 +12,7 @@ using FasdDesktopUi.Pages.DetailsPage.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static C4IT.Logging.cLogManager;
@@ -24,6 +26,11 @@ namespace FasdDesktopUi.Basics.Services.SupportCase.Processors
internal const string AgentDeviceIdNamedParameterName = "F4SD_Agent_DeviceId";
internal const string AgentUserIdNamedParameterName = "F4SD_Agent_UserId";
internal const string AgentOrganisationCodeNamedParameterName = "F4SD_Agent_OrganisationId";
internal const string CaseNotesNamedParameterName = "F4SD_CaseNotes";
internal const string CaseNotesHtmlNamedParameterName = "F4SD_CaseNotesHtml";
internal const string QuickActionProtocolNamedParameterName = "F4SD_QuickActionProtocol";
internal const string QuickActionProtocolLastNamedParameterName = "F4SD_QuickActionProtocolLast";
private ISupportCase _supportCase;
public cSupportCaseDataProvider SupportCaseDataProviderArtifact { get => _supportCase?.SupportCaseDataProviderArtifact; }
@@ -134,6 +141,16 @@ namespace FasdDesktopUi.Basics.Services.SupportCase.Processors
}
_namedParameterCache[relation][AgentOrganisationCodeNamedParameterName] = cCockpitConfiguration.Instance.agentApiConfiguration.OrganizationCode;
// todo insert quick action protocoll
StringBuilder caseNotesStringBuilder = new StringBuilder();
cRichTextBoxHelper.TraverseBlockAsUnicode(SupportCaseDataProviderArtifact.CaseNotes.Blocks, caseNotesStringBuilder);
_namedParameterCache[relation][CaseNotesNamedParameterName] = caseNotesStringBuilder.ToString();
StringBuilder caseNotesHtmlStringBuilder = new StringBuilder();
cRichTextBoxHelper.TraverseBlockAsHtml(SupportCaseDataProviderArtifact.CaseNotes.Blocks, caseNotesHtmlStringBuilder);
_namedParameterCache[relation][CaseNotesHtmlNamedParameterName] = caseNotesHtmlStringBuilder.ToString();
}
}

View File

@@ -1,4 +1,5 @@
using System;
using FasdDesktopUi.Basics.UiActions.NativeActions;
using System;
using System.Collections.Generic;
namespace FasdDesktopUi.Basics.Services.SupportCase.Processors
@@ -12,6 +13,7 @@ namespace FasdDesktopUi.Basics.Services.SupportCase.Processors
if (!_supportCaseProccesors.ContainsKey(id))
_supportCaseProccesors.Add(id, new SupportCaseProcessor());
QuickActionExecutor.SetSupportCaseProcessor(_supportCaseProccesors[id]);
return _supportCaseProccesors[id];
}
}

View File

@@ -24,6 +24,7 @@ namespace FasdDesktopUi.Basics.Services
{
#region Fields
private const bool TicketAndServiceRequestsEnabledDefault = true;
private readonly ITicketOverviewCommunicationSource _communicationSource;
private static readonly string[] OverviewKeys = new[]
{
@@ -35,6 +36,10 @@ namespace FasdDesktopUi.Basics.Services
"IncidentActive",
"IncidentCritical",
"IncidentNewInfo",
"ServiceRequestsNew",
"ServiceRequestsActive",
"ServiceRequestsCritical",
"ServiceRequestsNewInfo",
"UnassignedTickets",
"UnassignedTicketsCritical"
};
@@ -53,6 +58,7 @@ namespace FasdDesktopUi.Basics.Services
private bool _isDemo;
private bool _initialized;
private bool _isEnabled;
private bool _ticketAndServiceRequestsEnabled = TicketAndServiceRequestsEnabledDefault;
private readonly Random _random = new Random();
#if isDemo
private readonly List<DemoTicketRecord> _persistedDemoTickets = new List<DemoTicketRecord>();
@@ -109,8 +115,10 @@ namespace FasdDesktopUi.Basics.Services
#region Public API
public event EventHandler<TicketOverviewCountsChangedEventArgs> OverviewCountsChanged;
public event EventHandler TicketAndServiceRequestsAvailabilityChanged;
public IReadOnlyDictionary<string, TileCounts> CurrentCounts => _currentCounts;
public bool TicketAndServiceRequestsEnabled => _ticketAndServiceRequestsEnabled;
public bool IsScopeInitialized(TileScope scope)
{
@@ -215,6 +223,8 @@ namespace FasdDesktopUi.Basics.Services
{
_currentCounts[key] = TileCounts.Empty;
}
UpdateTicketAndServiceRequestsAvailability(false);
});
}
@@ -328,8 +338,11 @@ namespace FasdDesktopUi.Basics.Services
_isDemo = communication.IsDemo();
var rawCounts = await communication.GetTicketOverviewCounts(OverviewKeys, scope == TileScope.Role).ConfigureAwait(false);
var result = await communication.GetTicketOverviewCounts(OverviewKeys, scope == TileScope.Role).ConfigureAwait(false);
var rawCounts = result?.Counts;
var counts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
var ticketAndServiceRequestsEnabled =
result?.Capabilities?.TicketAndServiceRequestsEnabled ?? TicketAndServiceRequestsEnabledDefault;
if (rawCounts != null)
{
@@ -345,7 +358,13 @@ namespace FasdDesktopUi.Basics.Services
if (!_isEnabled)
return;
await _dispatcher.InvokeAsync(() => ProcessScopeCounts(scope, counts));
await _dispatcher.InvokeAsync(() =>
{
if (scope == TileScope.Personal)
UpdateTicketAndServiceRequestsAvailability(ticketAndServiceRequestsEnabled);
ProcessScopeCounts(scope, counts);
});
}
catch (Exception ex)
{
@@ -362,6 +381,15 @@ namespace FasdDesktopUi.Basics.Services
#region Count Processing
private void UpdateTicketAndServiceRequestsAvailability(bool isEnabled)
{
if (_ticketAndServiceRequestsEnabled == isEnabled)
return;
_ticketAndServiceRequestsEnabled = isEnabled;
TicketAndServiceRequestsAvailabilityChanged?.Invoke(this, EventArgs.Empty);
}
private void RefreshTimerIntervals()
{
_ = _dispatcher.InvokeAsync(() =>

View File

@@ -1,37 +1,38 @@
using System;
using C4IT.F4SD.SupportCaseProtocoll.Models;
using C4IT.FASD.Base;
using C4IT.FASD.Cockpit.Communication;
using C4IT.Logging;
using C4IT.MultiLanguage;
using F4SD.ActionConnector.Bus;
using F4SD.ActionConnector.Events;
using F4SD.ActionConnector.Payloads;
using F4SD.Gamification;
using F4SD.Gamification.Services;
using FasdDesktopUi.Basics.Helper;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.Services.ProtocollService;
using FasdDesktopUi.Basics.Services.RelationService;
using FasdDesktopUi.Basics.Services.SupportCase;
using FasdDesktopUi.Basics.Services.SupportCase.Controllers;
using FasdDesktopUi.Basics.Services.SupportCase.Processors;
using FasdDesktopUi.Basics.UserControls;
using FasdDesktopUi.Config;
using FasdDesktopUi.Pages;
using FasdDesktopUi.Pages.CustomMessageBox;
using FasdDesktopUi.Pages.SearchPage;
using FasdDesktopUi.Pages.TicketCompletion;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Documents;
using System.Linq;
using System.Windows.Input;
using System.Diagnostics;
using C4IT.FASD.Base;
using C4IT.Logging;
using C4IT.FASD.Cockpit.Communication;
using C4IT.MultiLanguage;
using static C4IT.Logging.cLogManager;
using FasdDesktopUi.Basics.Helper;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.UserControls;
using FasdDesktopUi.Pages;
using FasdDesktopUi.Pages.CustomMessageBox;
using FasdDesktopUi.Config;
using FasdDesktopUi.Pages.SearchPage;
using FasdDesktopUi.Pages.TicketCompletion;
using Newtonsoft.Json;
using FasdDesktopUi.Basics.Services.ProtocollService;
using FasdDesktopUi.Basics.Services.SupportCase;
using FasdDesktopUi.Basics.Services.RelationService;
using FasdDesktopUi.Basics.Services.SupportCase.Controllers;
using C4IT.F4SD.SupportCaseProtocoll.Models;
using FasdDesktopUi.Basics.Services.SupportCase.Processors;
using F4SD.Gamification;
using F4SD.Gamification.Services;
namespace FasdDesktopUi.Basics
{
@@ -303,6 +304,8 @@ namespace FasdDesktopUi.Basics
detailsPage?.EndPause();
ActionConnectorEvents.RaiseCaseClosed(this, new CaseClosedPayload() { ClosedAt = DateTime.UtcNow });
List<Task> tasks = new List<Task>();
tasks.Add(DirectConnectionHelper.DirectConnectionStopAsync());

View File

@@ -0,0 +1,71 @@
using C4IT.FASD.Base;
using F4SD.ActionConnector.Execution;
using F4SD.ActionConnector.Models;
using F4SD.ActionConnector.Payloads;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.Services.SupportCase.Controllers;
using FasdDesktopUi.Basics.Services.SupportCase.Processors;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using static C4IT.Logging.cLogManager;
namespace FasdDesktopUi.Basics.UiActions.NativeActions
{
internal static class QuickActionExecutor
{
private static ISupportCaseProcessor _supportCaseProcessor;
internal static void SetSupportCaseProcessor(ISupportCaseProcessor supportCaseProcessor)
{
_supportCaseProcessor = supportCaseProcessor ?? throw new ArgumentNullException(nameof(supportCaseProcessor));
}
internal static async Task<ActionResult> RunAction(QuickActionRequest quickActionRequest)
{
try
{
if (_supportCaseProcessor is null)
return ActionResult.Fail(quickActionRequest.ActionId, "No valid SupportCase processor was found.");
if (!(quickActionRequest.Context.Payload is CaseClosedPayload payload))
return ActionResult.Fail(quickActionRequest.ActionId, $"Payload is not of type '{typeof(CaseClosedPayload)}'. It is of type '{quickActionRequest.Context.Payload.GetType()}'");
cMenuDataBase menuData = MenuDataFactory.GetByName(quickActionRequest.QuickActionRef, _supportCaseProcessor.SupportCaseDataProviderArtifact.NamedParameterEntries, _supportCaseProcessor.SupportCaseDataProviderArtifact.CaseRelations.Select(r => r.Key));
// Till there is a clear picture of what object belongs to a case and what doesn't
// we do not want to make remote Quick Actions available
if (menuData is null || !(menuData.UiAction is cUiLocalQuickAction quickAction))
{
LogEntry($"'{quickActionRequest.QuickActionRef}' is no valid Local QuickAction and was therefore skipped.", C4IT.Logging.LogLevels.Error);
return ActionResult.Skip(quickActionRequest.ActionId);
}
await quickAction.RunUiActionAsync(null, null, false, _supportCaseProcessor.SupportCaseDataProviderArtifact);
await quickAction.ProcessActionAsync(CancellationToken.None, GetParameterDictionaryFrom(quickActionRequest.Parameters));
return ActionResult.Ok(quickActionRequest.ActionId);
}
catch (Exception ex)
{
LogException(ex);
return ActionResult.Fail(quickActionRequest.ActionId, ex.Message);
}
}
private static Dictionary<cAdjustableParameter, object> GetParameterDictionaryFrom(IDictionary<string, object> requestParameters)
{
try
{
return requestParameters.ToDictionary(p => new cAdjustableParameterString() { ParameterName = p.Key } as cAdjustableParameter, p => p.Value);
}
catch (Exception ex)
{
LogException(ex);
return new Dictionary<cAdjustableParameter, object>();
}
}
}
}

View File

@@ -1,22 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using C4IT.F4SD.DisplayFormatting;
using C4IT.F4SD.SupportCaseProtocoll.Models;
using C4IT.FASD.Base;
using C4IT.Logging;
using C4IT.MultiLanguage;
using FasdCockpitBase.Models;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.Models.QuickActionOutput;
using FasdDesktopUi.Basics.Services.ProtocollService;
using FasdDesktopUi.Basics.UserControls;
using static FasdDesktopUi.Basics.UserControls.QuickActionStatusMonitor;
namespace FasdDesktopUi.Basics.UiActions
{
@@ -139,7 +132,7 @@ namespace FasdDesktopUi.Basics.UiActions
catch { }
StatusMonitor.QuickActionOutputs.Add(_ref.Output);
quickActionOutput = QuickActionStatusMonitor.cQuickActionOutput.GetQuickActionOutput(_ref.Output, DataProvider);
quickActionOutput = cQuickActionOutput.GetQuickActionOutput(_ref.Output, DataProvider);
cQuickActionCopyData copyData = QuickActionProtocollEntryOutput.GetCopyData(quickActionDemo, DataProvider, true, quickActionOutput, StatusMonitor.MeasureValues);
QuickActionProtocollEntry protocollEntry = QuickActionProtocollEntryOutput.GetQuickActionProtocollEntry(quickActionDemo, copyData);

View File

@@ -1,19 +1,12 @@
using C4IT.F4SD.DisplayFormatting;
using C4IT.FASD.Base;
using C4IT.Logging;
using C4IT.MultiLanguage;
using FasdCockpitBase.Models;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.UserControls;
using FasdDesktopUi.Basics.Models.QuickActionOutput;
using System;
using System.Collections.Generic;
using System.Diagnostics.Tracing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static C4IT.Logging.cLogManager;
namespace FasdDesktopUi.Basics.UiActions
{
@@ -39,7 +32,7 @@ namespace FasdDesktopUi.Basics.UiActions
return result;
}
private List<QuickActionStatusMonitor.cQuickActionMeasureValue> GetStatusMonitorMeasureValues(List<cF4sdQuickActionRevision.cMeasure> measures)
private List<cQuickActionMeasureValue> GetStatusMonitorMeasureValues(List<cF4sdQuickActionRevision.cMeasure> measures)
{
return null;
}

View File

@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using C4IT.F4SD.DisplayFormatting;
@@ -11,12 +9,11 @@ using C4IT.FASD.Base;
using C4IT.FASD.Cockpit.Communication;
using C4IT.Logging;
using C4IT.MultiLanguage;
using FasdCockpitBase.Models;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.Models.QuickActionOutput;
using FasdDesktopUi.Basics.Services.ProtocollService;
using Newtonsoft.Json;
using static C4IT.Logging.cLogManager;
using static FasdDesktopUi.Basics.UserControls.QuickActionStatusMonitor;
namespace FasdDesktopUi.Basics.UiActions
{
@@ -140,7 +137,7 @@ namespace FasdDesktopUi.Basics.UiActions
quickActionOutput = new cQuickActionOutputSingle(new cF4sdQuickActionRevision.cOutput() { ResultCode = enumQuickActionSuccess.finished });
else
{
StatusMonitor.QuickActionOutputs.Add(ResultRevision.Output);
StatusMonitor?.QuickActionOutputs?.Add(ResultRevision.Output);
quickActionOutput = cQuickActionOutput.GetQuickActionOutput(ResultRevision.Output, DataProvider);
}
@@ -155,7 +152,7 @@ namespace FasdDesktopUi.Basics.UiActions
cMultiLanguageSupport.CurrentLanguage = tempLang;
}
cQuickActionCopyData copyData = QuickActionProtocollEntryOutput.GetCopyData(LocalQuickAction, DataProvider, false, protocollOutput, StatusMonitor.MeasureValues);
cQuickActionCopyData copyData = QuickActionProtocollEntryOutput.GetCopyData(LocalQuickAction, DataProvider, false, protocollOutput, StatusMonitor?.MeasureValues);
QuickActionProtocollEntry protocollEntry = QuickActionProtocollEntryOutput.GetQuickActionProtocollEntry(LocalQuickAction, copyData);
F4SDProtocoll.Instance.Add(protocollEntry);
@@ -195,7 +192,7 @@ namespace FasdDesktopUi.Basics.UiActions
cMultiLanguageSupport.CurrentLanguage = tempLang;
}
cQuickActionCopyData copyData = QuickActionProtocollEntryOutput.GetCopyData(LocalQuickAction, DataProvider, false, protocollOutput, StatusMonitor.MeasureValues);
cQuickActionCopyData copyData = QuickActionProtocollEntryOutput.GetCopyData(LocalQuickAction, DataProvider, false, protocollOutput, StatusMonitor?.MeasureValues);
QuickActionProtocollEntry protocollEntry = QuickActionProtocollEntryOutput.GetQuickActionProtocollEntry(LocalQuickAction, copyData);
F4SDProtocoll.Instance.Add(protocollEntry);

View File

@@ -1,21 +1,15 @@
using C4IT.F4SD.DisplayFormatting;
using C4IT.F4SD.SupportCaseProtocoll.Models;
using C4IT.FASD.Base;
using C4IT.Logging;
using C4IT.MultiLanguage;
using FasdCockpitBase.Models;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.Models.QuickActionOutput;
using FasdDesktopUi.Basics.Services.ProtocollService;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static C4IT.Logging.cLogManager;
using static FasdDesktopUi.Basics.UserControls.QuickActionStatusMonitor;
namespace FasdDesktopUi.Basics.UiActions
{

View File

@@ -85,6 +85,9 @@ namespace FasdDesktopUi.Basics.UiActions
cF4sdStagedSearchResultRelationTaskId gatherRelationTask = await _searchUiProvider.SearchService.LoadRelationsAsync(_searchResults, token);
if (gatherRelationTask == null)
return false;
HashSet<enumFasdInformationClass> orderedPendingInfoClasses
= GetInformationClassOrderedByPriority(gatherRelationTask.PendingInformationClasses, cF4sdIdentityEntry.GetFromSearchResult(_searchResults.FirstOrDefault()?.Type ?? enumF4sdSearchResultClass.Unknown));
_searchUiProvider.SetPendingInformationClasses(orderedPendingInfoClasses);

View File

@@ -6,6 +6,7 @@ using C4IT.Logging;
using C4IT.MultiLanguage;
using FasdCockpitBase.Models;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.Models.QuickActionOutput;
using FasdDesktopUi.Basics.Services.ProtocollService;
using Newtonsoft.Json;
using System;

View File

@@ -6,6 +6,7 @@ using C4IT.Logging;
using C4IT.MultiLanguage;
using FasdCockpitBase.Models;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.Models.QuickActionOutput;
using FasdDesktopUi.Basics.Services.ProtocollService;
using FasdDesktopUi.Basics.UserControls;
using Newtonsoft.Json;

View File

@@ -1,14 +1,11 @@
using C4IT.F4SD.DisplayFormatting;
using C4IT.F4SD.SupportCaseProtocoll.Models;
using C4IT.FASD.Base;
using C4IT.MultiLanguage;
using F4SD_AdaptableIcon.Enums;
using F4SD.Gamification;
using FasdDesktopUi.Basics.Converter;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.Services.ProtocollService;
using FasdDesktopUi.Basics.UiActions;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -23,12 +20,12 @@ using System.Windows.Input;
using System.Windows.Media;
using static C4IT.Logging.cLogManager;
using F4SD.Gamification.Services;
using FasdDesktopUi.Basics.Models.QuickActionOutput;
namespace FasdDesktopUi.Basics.UserControls
{
public partial class QuickActionStatusMonitor : UserControl, IBlurInvoker
{
private const string veiledText = "********";
private bool secretsAreShown = false;
private readonly IRawValueFormatter _rawValueFormatter = new RawValueFormatter();
@@ -42,503 +39,6 @@ namespace FasdDesktopUi.Basics.UserControls
set { lock (_currentQuickActionLock) { _currentRunningQuickActionId = value; } }
}
#region Helperclasses QuickAction Output
public abstract class cQuickActionOutput
{
protected readonly IRawValueFormatter _rawValueFormatter = new RawValueFormatter();
public cQuickActionOutput(cF4sdQuickActionRevision.cOutput scriptOutput)
{
_rawValueFormatter.SetDefaultCulture(cFasdCockpitConfig.Instance.SelectedCulture);
_rawValueFormatter.SetDefaultTimeZone(TimeZoneInfo.Local);
ErrorCode = scriptOutput.ErrorCode;
ErrorDescription = scriptOutput.ErrorDescription;
ResultCode = scriptOutput.ResultCode.GetValueOrDefault();
IsError = HasError(scriptOutput);
}
public cSupportCaseDataProvider DataProvider { get; private set; }
public enumQuickActionSuccess? ResultCode { get; set; }
public int? ErrorCode { get; set; }
public string ErrorDescription { get; set; }
public bool IsError { get; private set; }
internal static KeyValuePair<string, RawValueType> GetDisplayType(dynamic columnTypeObject)
{
var output = new KeyValuePair<string, RawValueType>();
try
{
var columnType = columnTypeObject.ToObject<dynamic>();
string name = string.Empty;
RawValueType type = RawValueType.STRING;
if (columnType.Name != null)
if (columnType.Name is Newtonsoft.Json.Linq.JValue nameJValue)
name = nameJValue.Value.ToString();
if (columnType.Type != null)
if (columnType.Type is Newtonsoft.Json.Linq.JValue typeJValue)
if (Enum.TryParse(typeJValue.Value.ToString(), out RawValueType typeJValueType))
type = typeJValueType;
output = new KeyValuePair<string, RawValueType>(name, type);
}
catch (Exception E)
{
LogException(E);
}
return output;
}
public static cQuickActionOutput GetQuickActionOutput(cF4sdQuickActionRevision.cOutput scriptOutput, cSupportCaseDataProvider dataProvider)
{
if (scriptOutput == null)
return null;
try
{
if (scriptOutput?.Values == null)
{
var output = new cQuickActionOutputSingle(scriptOutput) { DataProvider = dataProvider };
return output;
}
else if (scriptOutput.Values is JArray scriptValueJArray)
{
var scriptValues = scriptValueJArray.ToObject<List<dynamic>>();
if (scriptValues != null && scriptValues.Count > 0)
{
var output = new cQuickActionOutputList(scriptOutput) { DataProvider = dataProvider };
foreach (var scriptValue in scriptValues)
{
if (scriptValue is JObject scriptValueJObject)
{
try
{
var scriptValueHelperDictionary = scriptValueJObject.ToObject<Dictionary<string, object>>();
output.Values.Add(scriptValueHelperDictionary.ToList());
}
catch (Exception E)
{
LogException(E);
}
}
}
return output;
}
}
else if (scriptOutput.Values is JObject scriptValueJObject)
{
try
{
var output = new cQuickActionOutputObject(scriptOutput) { DataProvider = dataProvider };
return output;
}
catch (Exception E)
{
LogException(E);
}
}
else
{
return new cQuickActionOutputSingle(scriptOutput) { DataProvider = dataProvider };
}
}
catch (Exception E)
{
LogException(E);
}
return null;
}
public static bool HasError(cF4sdQuickActionRevision.cOutput quickActionOutput)
{
if ((quickActionOutput?.ResultCode == null || quickActionOutput?.ResultCode == enumQuickActionSuccess.finished || quickActionOutput?.ResultCode == enumQuickActionSuccess.successfull)
&& (quickActionOutput?.ErrorCode == null || quickActionOutput?.ErrorCode == 0)
&& string.IsNullOrEmpty(quickActionOutput?.ErrorDescription))
return false;
return true;
}
}
public class cQuickActionOutputSingle : cQuickActionOutput
{
public cQuickActionOutputSingle(cF4sdQuickActionRevision.cOutput scriptOutput) : base(scriptOutput)
{
Instantiate(scriptOutput);
}
public RawValueType DisplayType { get; set; }
public string Key { get; set; }
public object Value { get; set; }
private void Instantiate(cF4sdQuickActionRevision.cOutput scriptOutput)
{
try
{
Value = scriptOutput.Values;
if (scriptOutput.Values is JObject scriptValueJObject)
{
string scriptValueKey = null;
try
{
var scriptValueHelperDictionary = scriptValueJObject.ToObject<Dictionary<string, object>>();
scriptValueKey = scriptValueHelperDictionary.Keys.ToList()[0];
Key = scriptValueKey;
Value = scriptValueHelperDictionary[scriptValueKey];
}
catch (Exception E)
{
LogException(E);
}
if (scriptOutput.ColumnTypes is JArray columnTypeJArray)
{
var columnTypes = columnTypeJArray.ToObject<List<dynamic>>();
if (columnTypes != null && columnTypes.Count > 0)
{
foreach (var columnTypeObject in columnTypes)
{
var displayType = cQuickActionOutput.GetDisplayType(columnTypeObject);
if (displayType.Key == scriptValueKey)
DisplayType = displayType.Value;
}
}
}
else if (scriptOutput.ColumnTypes is JObject columnTypeJObject)
{
try
{
var dispayType = cQuickActionOutput.GetDisplayType(columnTypeJObject);
DisplayType = dispayType.Value;
}
catch (Exception E)
{
LogException(E);
}
}
}
}
catch (Exception E)
{
LogException(E);
}
}
public string GetDisplayValue(Dictionary<string, cColumnOutputFormatting> columnOutputFormattings, bool getSecretValue = false)
{
string output = null;
try
{
output = _rawValueFormatter.GetDisplayValue(Value, DisplayType);
cColumnOutputFormatting outputFormatting = null;
if (string.IsNullOrEmpty(Key))
outputFormatting = columnOutputFormattings?.Values.FirstOrDefault();
else
columnOutputFormattings?.TryGetValue(Key, out outputFormatting);
if (outputFormatting != null)
{
if (outputFormatting.IsSecret && !getSecretValue)
return veiledText;
if (outputFormatting.DisplayType != null)
output = _rawValueFormatter.GetDisplayValue(Value, outputFormatting.DisplayType.Value);
if (outputFormatting.Translation != null)
{
var abstractTranslation = cF4SDHealthCardConfig.GetTranslationsWithName(DataProvider?.HealthCardDataHelper?.SelectedHealthCard, outputFormatting.Translation);
if (abstractTranslation != null && abstractTranslation is cHealthCardTranslator translator)
{
output = translator.DefaultTranslation?.Translation.GetValue() ?? Value.ToString();
foreach (var translation in translator.Translations)
{
if (translation.Values.Any(value => value.Equals(Value.ToString(), StringComparison.InvariantCultureIgnoreCase)))
output = translation.Translation.GetValue();
}
}
}
}
}
catch (Exception E)
{
LogException(E);
}
return output;
}
}
public class cQuickActionOutputObject : cQuickActionOutput
{
public Dictionary<string, RawValueType> DisplayTypes { get; set; } = new Dictionary<string, RawValueType>();
public List<KeyValuePair<string, object>> Values { get; set; } = new List<KeyValuePair<string, object>>();
public cQuickActionOutputObject(cF4sdQuickActionRevision.cOutput scriptOutput) : base(scriptOutput)
{
try
{
InstantiateDisplayTypes(scriptOutput);
InstantiateValues(scriptOutput);
}
catch (Exception E)
{
LogException(E);
}
}
private void InstantiateDisplayTypes(cF4sdQuickActionRevision.cOutput scriptOutput)
{
try
{
if (scriptOutput.ColumnTypes is Newtonsoft.Json.Linq.JArray columnTypeJArray)
{
var columnTypes = columnTypeJArray.ToObject<List<dynamic>>();
if (columnTypes != null && columnTypes.Count > 0)
{
foreach (var columnTypeObject in columnTypes)
{
var displayType = GetDisplayType(columnTypeObject);
DisplayTypes[displayType.Key] = displayType.Value;
}
}
}
else if (scriptOutput.ColumnTypes is Newtonsoft.Json.Linq.JObject columnTypeJObject)
{
try
{
var displayType = GetDisplayType(columnTypeJObject);
DisplayTypes[displayType.Key] = displayType.Value;
}
catch (Exception E)
{
LogException(E);
}
}
}
catch (Exception E)
{
LogException(E);
}
}
private void InstantiateValues(cF4sdQuickActionRevision.cOutput scriptOutput)
{
try
{
if (!(scriptOutput.Values is JObject valuesJObject))
return;
if (valuesJObject.Properties() == null)
return;
foreach (var property in valuesJObject.Properties())
{
Values.Add(new KeyValuePair<string, object>(property.Name, property.Value));
}
}
catch (Exception E)
{
LogException(E);
}
}
public string GetDisplayValue(int index, Dictionary<string, cColumnOutputFormatting> columnOutputFormattings, bool getSecretValue = false)
{
string output = null;
try
{
if (Values.Count <= index)
return null;
var selectedItem = Values[index];
RawValueType displayType = RawValueType.STRING;
DisplayTypes.TryGetValue(selectedItem.Key, out displayType);
output = _rawValueFormatter.GetDisplayValue(selectedItem.Value, displayType);
if (columnOutputFormattings != null && columnOutputFormattings.TryGetValue(selectedItem.Key, out var outputFormatting))
{
if (outputFormatting.Hidden)
return string.Empty;
if (outputFormatting.IsSecret && !getSecretValue)
return veiledText;
if (outputFormatting.DisplayType != null)
output = _rawValueFormatter.GetDisplayValue(selectedItem.Value, outputFormatting.DisplayType.Value);
if (outputFormatting.Translation != null)
{
var abstractTranslation = cF4SDHealthCardConfig.GetTranslationsWithName(DataProvider?.HealthCardDataHelper?.SelectedHealthCard, outputFormatting.Translation);
if (abstractTranslation != null && abstractTranslation is cHealthCardTranslator translator)
{
output = translator.DefaultTranslation?.Translation.GetValue() ?? selectedItem.Value.ToString();
foreach (var translation in translator.Translations)
{
if (translation.Values.Any(value => value.Equals(selectedItem.Value.ToString(), StringComparison.InvariantCultureIgnoreCase)))
output = translation.Translation.GetValue();
}
}
}
}
}
catch (Exception E)
{
LogException(E);
}
return output;
}
}
public class cQuickActionOutputList : cQuickActionOutput
{
public cQuickActionOutputList(cF4sdQuickActionRevision.cOutput scriptOutput) : base(scriptOutput)
{
InstantiateDisplayTypes(scriptOutput);
}
public Dictionary<string, RawValueType> DisplayTypes { get; set; } = new Dictionary<string, RawValueType>();
public List<List<KeyValuePair<string, object>>> Values { get; set; } = new List<List<KeyValuePair<string, object>>>();
private void InstantiateDisplayTypes(cF4sdQuickActionRevision.cOutput scriptOutput)
{
try
{
if (scriptOutput.ColumnTypes is Newtonsoft.Json.Linq.JArray columnTypeJArray)
{
var columnTypes = columnTypeJArray.ToObject<List<dynamic>>();
if (columnTypes != null && columnTypes.Count > 0)
{
foreach (var columnTypeObject in columnTypes)
{
var displayType = GetDisplayType(columnTypeObject);
DisplayTypes[displayType.Key] = displayType.Value;
}
}
}
else if (scriptOutput.ColumnTypes is Newtonsoft.Json.Linq.JObject columnTypeJObject)
{
try
{
var displayType = GetDisplayType(columnTypeJObject);
DisplayTypes[displayType.Key] = displayType.Value;
}
catch (Exception E)
{
LogException(E);
}
}
}
catch (Exception E)
{
LogException(E);
}
}
public string GetDisplayValue(int row, int column, Dictionary<string, cColumnOutputFormatting> columnOutputFormattings, bool getSecretValue = false)
{
string output = null;
try
{
RawValueType displayType = RawValueType.STRING;
DisplayTypes.TryGetValue(Values[row][column].Key, out displayType);
output = _rawValueFormatter.GetDisplayValue(Values[row][column].Value, displayType);
if (columnOutputFormattings != null && columnOutputFormattings.TryGetValue(Values[row][column].Key, out var outputFormatting))
{
if (outputFormatting.Hidden)
return string.Empty;
if (outputFormatting.IsSecret && !getSecretValue)
return veiledText;
if (outputFormatting.DisplayType != null)
output = _rawValueFormatter.GetDisplayValue(Values[row][column].Value, outputFormatting.DisplayType.Value);
if (outputFormatting.Translation != null)
{
var abstractTranslation = cF4SDHealthCardConfig.GetTranslationsWithName(DataProvider?.HealthCardDataHelper?.SelectedHealthCard, outputFormatting.Translation);
if (abstractTranslation != null && abstractTranslation is cHealthCardTranslator translator)
{
output = translator.DefaultTranslation?.Translation.GetValue() ?? Values[row].ToList()[column].Value.ToString();
foreach (var translation in translator.Translations)
{
if (translation.Values.Any(value => value.Equals(Values[row].ToList()[column].Value.ToString(), StringComparison.InvariantCultureIgnoreCase)))
output = translation.Translation.GetValue();
}
}
}
}
}
catch (Exception E)
{
LogException(E);
}
return output;
}
}
public class cQuickActionMeasureValue
{
public int Id { get; set; }
public cMultiLanguageDictionary Names { get; set; }
public object Value { get; set; }
public object PostValue { get; set; }
public object Difference
{
get
{
switch (Display)
{
case RawValueType.INTEGER:
case RawValueType.PERCENT:
case RawValueType.PERCENT100:
case RawValueType.PERCENT1000:
case RawValueType.BYTES:
var valueDouble = cF4SDHealthCardRawData.GetDouble(Value);
var postValueDouble = cF4SDHealthCardRawData.GetDouble(PostValue);
if (valueDouble != null && postValueDouble != null)
return postValueDouble - valueDouble;
break;
}
return null;
}
}
public RawValueType Display { get; set; }
}
#endregion
#region Properties
private CancellationTokenSource tokenSource;

View File

@@ -1,5 +1,5 @@
using C4IT.FASD.Base;
using FasdDesktopUi.Basics.Models.QuickActionOutput;
using System;
using System.Collections.Generic;
using System.Linq;

View File

@@ -38,52 +38,26 @@
</Grid.RowDefinitions>
<DockPanel>
<!--<DockPanel.Resources>
<Style TargetType="ico:AdaptableIcon">
<Setter Property="IconWidth"
Value="20" />
<Setter Property="IconHeight"
Value="20" />
<Setter Property="BorderPadding"
Value="5" />
<Setter Property="IconBackgroundColor"
Value="Transparent" />
<Setter Property="Margin"
Value="0, 0, 10, 0" />
<Setter Property="PrimaryIconColor"
Value="{DynamicResource Color.Menu.Icon}" />
<Setter Property="Cursor"
Value="Hand" />
<Setter Property="WindowChrome.IsHitTestVisibleInChrome"
Value="True" />
<Style.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="PrimaryIconColor"
Value="{DynamicResource Color.Menu.Icon.Hover}" />
<Setter Property="IconBackgroundColor"
Value="{DynamicResource Background.Menu.Icon.Hover}" />
</Trigger>
</Style.Triggers>
</Style>
</DockPanel.Resources>-->
<ico:AdaptableIcon x:Name="CloseNotepadIcon" x:FieldModifier="private"
<ico:AdaptableIcon x:Name="CloseNotepadIcon"
x:FieldModifier="private"
DockPanel.Dock="Right"
SelectedInternIcon="window_minimize"
Style="{DynamicResource SettingsPage.Close.Icon}"
WindowChrome.IsHitTestVisibleInChrome ="True"
WindowChrome.IsHitTestVisibleInChrome="True"
Margin="0 8 0 0"
Tag="close" MouseUp="CloseNotepadIcon_MouseUp" TouchDown="CloseNotepadIcon_TouchDown"/>
Tag="close"
MouseLeftButtonUp="CloseNotepadIcon_Click"
TouchDown="CloseNotepadIcon_Click" />
<ico:AdaptableIcon x:Name="LockNotepadIcon" x:FieldModifier="private"
<ico:AdaptableIcon x:Name="LockNotepadIcon"
x:FieldModifier="private"
DockPanel.Dock="Right"
Style="{DynamicResource Menu.Notepad.Icon.Lock}"
MouseLeave="LockNotepadIcon_MouseLeave"
WindowChrome.IsHitTestVisibleInChrome ="True"
Tag="lock" MouseUp="LockNotepadIcon_MouseUp" TouchDown="LockNotepadIcon_TouchDown"/>
WindowChrome.IsHitTestVisibleInChrome="True"
Tag="lock"
MouseLeftButtonUp="LockNotepadIcon_Click"
TouchDown="LockNotepadIcon_Click" />
<ico:AdaptableIcon DockPanel.Dock="Left"
VerticalAlignment="Center"
@@ -110,48 +84,56 @@
<DockPanel Grid.Row="2">
<Border DockPanel.Dock="Top"
Margin="0 0 0 10"
Background="{DynamicResource BackgroundColor.DetailsPage.Widget.Title}"
Padding="5"
CornerRadius="7.5">
Margin="0 0 0 10"
Background="{DynamicResource BackgroundColor.DetailsPage.Widget.Title}"
Padding="5"
CornerRadius="7.5">
<DockPanel>
<ico:AdaptableIcon x:Name="ToggleDockButton" x:FieldModifier="private"
<ico:AdaptableIcon x:Name="ToggleDockButton"
x:FieldModifier="private"
DockPanel.Dock="Right"
SelectedMaterialIcon="ic_zoom_out_map"
MouseLeftButtonUp="ToggleDockButton_MouseLeftButtonUp"
TouchDown="ToggleDockButton_TouchDown" />
<StackPanel Orientation="Horizontal">
<ico:AdaptableIcon x:Name="UndoIcon" x:FieldModifier="private"
<ico:AdaptableIcon x:Name="UndoIcon"
x:FieldModifier="private"
SelectedMaterialIcon="ic_undo"
Command="ApplicationCommands.Undo"
Style="{DynamicResource Menu.MenuBar.PinnedIcon.Disabled}"/>
<ico:AdaptableIcon x:Name="RedoIcon" x:FieldModifier="private"
Style="{DynamicResource Menu.MenuBar.PinnedIcon.Disabled}" />
<ico:AdaptableIcon x:Name="RedoIcon"
x:FieldModifier="private"
SelectedMaterialIcon="ic_redo"
Command="ApplicationCommands.Redo"
Style="{DynamicResource Menu.MenuBar.PinnedIcon.Disabled}"/>
Style="{DynamicResource Menu.MenuBar.PinnedIcon.Disabled}" />
<ico:AdaptableIcon x:Name="CopyIcon"
SelectedInternIcon="menuBar_copy"
TouchDown="CopyIcon_TouchDown"
MouseLeftButtonUp="CopyIcon_MouseLeftButtonUp" />
<ico:AdaptableIcon x:Name="BoldIcon" x:FieldModifier="private"
TouchDown="CopyIcon_Click"
MouseLeftButtonUp="CopyIcon_Click" />
<ico:AdaptableIcon x:Name="BoldIcon"
x:FieldModifier="private"
SelectedMaterialIcon="ic_format_bold"
Command="EditingCommands.ToggleBold"
MouseLeftButtonUp="TextBoxIcon_MouseLeftButtonUp" />
<ico:AdaptableIcon x:Name="ItalicIcon" x:FieldModifier="private"
<ico:AdaptableIcon x:Name="ItalicIcon"
x:FieldModifier="private"
SelectedMaterialIcon="ic_format_italic"
Command="EditingCommands.ToggleItalic"
MouseLeftButtonUp="TextBoxIcon_MouseLeftButtonUp" />
<ico:AdaptableIcon x:Name="UnderlineIcon" x:FieldModifier="private"
<ico:AdaptableIcon x:Name="UnderlineIcon"
x:FieldModifier="private"
SelectedMaterialIcon="ic_format_underlined"
Command="EditingCommands.ToggleUnderline"
MouseLeftButtonUp="TextBoxIcon_MouseLeftButtonUp" />
<ico:AdaptableIcon x:Name="BulletsIcon" x:FieldModifier="private"
<ico:AdaptableIcon x:Name="BulletsIcon"
x:FieldModifier="private"
SelectedMaterialIcon="ic_format_list_bulleted"
Command="EditingCommands.ToggleBullets"
MouseLeftButtonUp="TextBoxIcon_MouseLeftButtonUp" />
<ico:AdaptableIcon x:Name="NumberingIcon" x:FieldModifier="private"
<ico:AdaptableIcon x:Name="NumberingIcon"
x:FieldModifier="private"
SelectedMaterialIcon="ic_format_list_numbered"
Command="EditingCommands.ToggleNumbering"
MouseLeftButtonUp="TextBoxIcon_MouseLeftButtonUp" />
@@ -161,7 +143,8 @@
</Border>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<RichTextBox x:Name="NotepadRichTextBox" x:FieldModifier="private"
<RichTextBox x:Name="NotepadRichTextBox"
x:FieldModifier="private"
VerticalAlignment="Stretch"
Padding="2.5 5"
TextChanged="NotepadRichTextBox_TextChanged"
@@ -169,8 +152,7 @@
Background="{DynamicResource BackgroundColor.DetailsPage.Widget.Title}"
SpellCheck.IsEnabled="True"
SelectionChanged="NotepadRichTextBox_SelectionChanged"
IsVisibleChanged="NotepadRichTextBox_IsVisibleChanged"
GotKeyboardFocus="NotepadRichTextBox_GotKeyboardFocus">
IsVisibleChanged="NotepadRichTextBox_IsVisibleChanged">
<RichTextBox.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius"

View File

@@ -42,21 +42,7 @@ namespace FasdDesktopUi.Basics.UserControls
#region DataProvider
private cSupportCaseDataProvider DataProvider;
/*
{
get { return (cDataProviderBase)GetValue(DataProviderProperty); }
set {
SetValue(DataProviderProperty, value);
}
}
*/
/*
private static readonly DependencyProperty DataProviderProperty =
DependencyProperty.Register("DataProvider", typeof(cDataProviderBase), typeof(Notepad), new PropertyMetadata(null));
*/
private readonly cSupportCaseDataProvider _dataProvider;
#endregion
@@ -102,24 +88,18 @@ namespace FasdDesktopUi.Basics.UserControls
private static void HandleLockedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
try
{
if (!(d is Notepad _me))
{
if (!(d is Notepad notepad))
return;
}
_me.LockNotepadIcon.SelectedInternIcon = _me.IsLocked ? F4SD_AdaptableIcon.Enums.enumInternIcons.lock_closed : F4SD_AdaptableIcon.Enums.enumInternIcons.lock_open;
_me.CloseNotepadIcon.IsEnabled = _me.IsLocked ? false : true;
notepad.LockNotepadIcon.SelectedInternIcon = notepad.IsLocked ? enumInternIcons.lock_closed : enumInternIcons.lock_open;
notepad.CloseNotepadIcon.IsEnabled = !notepad.IsLocked;
}
catch (Exception E)
{
LogException(E);
}
}
#endregion
@@ -165,7 +145,7 @@ namespace FasdDesktopUi.Basics.UserControls
public Notepad(cSupportCaseDataProvider dataProvider)
{
DataProvider = dataProvider;
_dataProvider = dataProvider;
InitializeComponent();
saveTimer = new DispatcherTimer(TimeSpan.FromSeconds(0.75), DispatcherPriority.Render, SaveNotes, Dispatcher.CurrentDispatcher);
IsLocked = cFasdCockpitConfig.Instance.IsNotepadVisibleDocked;
@@ -175,29 +155,6 @@ namespace FasdDesktopUi.Basics.UserControls
#region Methods
private void NotepadRichTextBox_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
try
{
if (DataProvider is null)
return;
if (DataProvider?.CaseNotes?.Parent is RichTextBox parentBox)
parentBox.Document = new FlowDocument();
if (!(sender is RichTextBox senderElement))
return;
senderElement.Document = DataProvider.CaseNotes;
senderElement.CaretPosition = senderElement.CaretPosition.DocumentEnd;
}
catch (Exception E)
{
LogException(E);
}
}
private async void NotepadRichTextBox_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
try
@@ -205,11 +162,15 @@ namespace FasdDesktopUi.Basics.UserControls
if (!(e.NewValue is bool isVisible && isVisible))
return;
await Task.Delay(50);
NotepadRichTextBox.Focus();
Keyboard.Focus(NotepadRichTextBox);
if (!(sender is RichTextBox senderElement))
return;
NotepadRichTextBox_GotKeyboardFocus(sender, null);
senderElement.Document = _dataProvider.CaseNotes;
senderElement.CaretPosition = senderElement.CaretPosition.DocumentEnd;
await Task.Delay(50);
senderElement.Focus();
Keyboard.Focus(senderElement);
}
catch (Exception E)
{
@@ -221,7 +182,7 @@ namespace FasdDesktopUi.Basics.UserControls
#region CopyIcon Click
private async void CopyIcon_Click(object sender)
private async void CopyIcon_Click(object sender, InputEventArgs e)
{
try
{
@@ -254,16 +215,6 @@ namespace FasdDesktopUi.Basics.UserControls
}
}
private void CopyIcon_TouchDown(object sender, TouchEventArgs e)
{
CopyIcon_Click(sender);
}
private void CopyIcon_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
CopyIcon_Click(sender);
}
#endregion
#region Popout
@@ -308,13 +259,13 @@ namespace FasdDesktopUi.Basics.UserControls
{
saveTimer?.Stop();
if (DataProvider is null)
if (_dataProvider is null)
return;
Dispatcher.Invoke(() =>
{
SetLanguageOfDocument();
DataProvider.SaveCaseNotes();
_dataProvider.SaveCaseNotes();
});
}
catch (Exception E)
@@ -642,7 +593,7 @@ namespace FasdDesktopUi.Basics.UserControls
{
ToggleDockButton.Visibility = IsUndocked ? Visibility.Collapsed : Visibility.Visible;
LockNotepadIcon.Visibility = IsUndocked ? Visibility.Collapsed : Visibility.Visible;
CloseNotepadIcon.IsEnabled = IsUndocked ? true : !IsLocked;
CloseNotepadIcon.IsEnabled = IsUndocked || !IsLocked;
}
catch (Exception E)
@@ -680,23 +631,12 @@ namespace FasdDesktopUi.Basics.UserControls
}
}
private void CloseNotepadIcon_MouseUp(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
CloseNotepad();
}
private void CloseNotepadIcon_TouchDown(object sender, TouchEventArgs e)
{
CloseNotepad();
}
private void CloseNotepadIcon_Click(object sender, InputEventArgs e) => CloseNotepad();
private void LockNotepad()
{
try
{
if (IsLocked)
{
LockNotepadIcon.SetResourceReference(AdaptableIcon.AdaptableIcon.PrimaryIconColorProperty, "Color.SoftContrast");
@@ -717,19 +657,9 @@ namespace FasdDesktopUi.Basics.UserControls
{
LogException(E);
}
}
private void LockNotepadIcon_MouseUp(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
LockNotepad();
}
private void LockNotepadIcon_TouchDown(object sender, TouchEventArgs e)
{
LockNotepad();
}
private void LockNotepadIcon_Click(object sender, InputEventArgs e) => LockNotepad();
private void LockNotepadIcon_MouseLeave(object sender, MouseEventArgs e)
{

View File

@@ -78,14 +78,15 @@
<local:SearchBar x:Name="Search"
DockPanel.Dock="Top"
PlaceholderText="{Binding Converter={StaticResource LanguageConverter}, ConverterParameter=Searchbar.Placeholder}"
ShowCloseButton="False"
ShowCloseButton="True"
SearchButtonSize="26"
DebounceInterval="0"
SearchBackground="{DynamicResource BackgroundColor.DetailsPage.Widget.Title}"
SearchValueChanged="SearchBar_SearchValueChanged"
Margin="0 0 0 10"
Focusable="True"
Height="26" />
Height="26"
CancelledSearch="Search_CancelledSearch" />
<local:CustomMenu x:Name="SubMenuUc"
DockPanel.Dock="Top"

View File

@@ -238,5 +238,12 @@ namespace FasdDesktopUi.Basics.UserControls
private void SearchBar_SearchValueChanged(object sender, string e)
=> SearchValueChanged?.Invoke(this, e);
#region ClearSearch
private void Search_CancelledSearch(object sender, RoutedEventArgs e)
{
Search.Clear();
}
#endregion
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
@@ -9,7 +10,7 @@ namespace FasdDesktopUi.Basics.UserControls
{
public partial class SearchBar : UserControl
{
private readonly DispatcherTimer _searchTimer = new DispatcherTimer();
private readonly Timer _searchTimer = new Timer();
#region Dependency Properties
@@ -70,7 +71,8 @@ namespace FasdDesktopUi.Basics.UserControls
private static void OnDebounceIntervalChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((SearchBar)d)._searchTimer.Interval = (TimeSpan)e.NewValue;
TimeSpan interval = (TimeSpan)e.NewValue;
((SearchBar)d)._searchTimer.Interval = Math.Max(interval.TotalMilliseconds, 10);
}
public static readonly DependencyProperty IsInputEnabledProperty =
@@ -158,7 +160,7 @@ namespace FasdDesktopUi.Basics.UserControls
private void HandleSearchValueChanged()
{
if (_searchTimer.IsEnabled)
if (_searchTimer.Enabled)
_searchTimer.Stop();
_searchTimer.Start();
@@ -183,8 +185,8 @@ namespace FasdDesktopUi.Basics.UserControls
private void SearchBar_Loaded(object sender, RoutedEventArgs e)
{
_searchTimer.Stop();
_searchTimer.Interval = DebounceInterval;
_searchTimer.Tick += SearchTimerTick;
_searchTimer.Interval = Math.Max(DebounceInterval.TotalMilliseconds, 10);
_searchTimer.Elapsed += SearchTimerTick;
BackgroundBorder.CornerRadius = new CornerRadius(BackgroundBorder.ActualHeight / 2.0);
}

View File

@@ -422,9 +422,28 @@ namespace FasdDesktopUi.Basics.UserControls
foreach (var informationClass in informationClasses.Reverse())
{
string _title = string.Empty;
switch (informationClass)
{
case enumFasdInformationClass.Computer:
_title = "Searchbar.Relations.SearchFor.Computer";
break;
case enumFasdInformationClass.User:
_title = "Searchbar.Relations.SearchFor.User";
break;
case enumFasdInformationClass.Ticket:
_title = "Searchbar.Relations.SearchFor.Ticket";
break;
case enumFasdInformationClass.VirtualSession:
_title = "Searchbar.Relations.SearchFor.VirtualSession";
break;
}
if (string.IsNullOrEmpty(_title))
continue;
var searchResultCategory = new SearchResultCategory()
{
Title = string.Format(cMultiLanguageSupport.GetItem("Searchbar.Relations.SearchFor"), informationClass),
Title = cMultiLanguageSupport.GetItem(_title),
IsPending = true,
TitleIcon = GetIconFrom(informationClass),
Margin = new Thickness(0, 7.5, 0, 0)

View File

@@ -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<ComboBoxItem>()
.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)

View File

@@ -15,6 +15,44 @@
<UserControl.Resources>
<vc:LanguageDefinitionsConverter x:Key="LanguageConverter" />
<Style x:Key="TicketOverviewRowHeadingStyle"
TargetType="Label">
<Setter Property="HorizontalContentAlignment"
Value="Stretch" />
<Setter Property="VerticalContentAlignment"
Value="Center" />
<EventSetter Event="MouseEnter"
Handler="RowHeading_MouseEnter" />
<EventSetter Event="MouseLeave"
Handler="RowHeading_MouseLeave" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Label">
<!-- Padding stays outside the ticker viewport so the animation uses the actual visible text width.
The small right-side reserve prevents marginal overflows from triggering the marquee. -->
<Border Background="Transparent"
Padding="{TemplateBinding Padding}">
<Border x:Name="PART_TickerContainer"
Background="Transparent"
ClipToBounds="True"
Margin="0,0,-2,0">
<TextBlock x:Name="PART_TickerText"
Text="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}"
Foreground="{TemplateBinding Foreground}"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
FontStretch="{TemplateBinding FontStretch}"
FontStyle="{TemplateBinding FontStyle}"
FontWeight="{TemplateBinding FontWeight}"
TextTrimming="CharacterEllipsis"
VerticalAlignment="Center" />
</Border>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Style für Labels mit Auswahlzustand -->
<Style x:Key="RoundedSelectedLabelStyle"
TargetType="Label">
@@ -82,8 +120,11 @@
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition x:Name="TicketsRow"
Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition x:Name="ServiceRequestsRow"
Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
@@ -128,6 +169,7 @@
<!-- Tickets -->
<Label Grid.Row="2"
Grid.Column="0"
Style="{StaticResource TicketOverviewRowHeadingStyle}"
Foreground="{DynamicResource FontColor.DetailsPage.TitleSection.Header}"
Content="{Binding Converter={StaticResource LanguageConverter}, ConverterParameter=TicketOverview.Row.Heading.Tickets}"
FontWeight="Bold"
@@ -200,6 +242,7 @@
<!-- Incidents -->
<Label Grid.Row="3"
Grid.Column="0"
Style="{StaticResource TicketOverviewRowHeadingStyle}"
Foreground="{DynamicResource FontColor.DetailsPage.TitleSection.Header}"
Content="{Binding Converter={StaticResource LanguageConverter}, ConverterParameter=TicketOverview.Row.Heading.Incidents}"
FontWeight="Bold"
@@ -269,15 +312,89 @@
HorizontalAlignment="Center"
Cursor="Hand" />
<!-- Unassigned -->
<!-- Service Requests -->
<Label Grid.Row="4"
Grid.Column="0"
Style="{StaticResource TicketOverviewRowHeadingStyle}"
Foreground="{DynamicResource FontColor.DetailsPage.TitleSection.Header}"
Content="{Binding Converter={StaticResource LanguageConverter}, ConverterParameter=TicketOverview.Row.Heading.ServiceRequests}"
FontWeight="Bold"
FontSize="12" />
<Label Grid.Row="4"
Grid.Column="1"
Content="{Binding ServiceRequestsNew}"
Style="{StaticResource RoundedSelectedLabelStyle}"
local:TicketOverview.IsSelected="{Binding ServiceRequestsNewSelected, Mode=TwoWay}"
local:TicketOverview.IsHighlighted="{Binding ServiceRequestsNewHighlighted}"
ToolTip="{Binding ServiceRequestsNewChangeHint}"
Tag="ServiceRequestsNewSelected"
MouseLeftButtonUp="Label_MouseLeftButtonUp"
FontWeight="Medium"
Foreground="{DynamicResource Color.Green}"
FontSize="12"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Cursor="Hand" />
<Label Grid.Row="4"
Grid.Column="2"
Content="{Binding ServiceRequestsActive}"
Style="{StaticResource RoundedSelectedLabelStyle}"
local:TicketOverview.IsSelected="{Binding ServiceRequestsActiveSelected, Mode=TwoWay}"
local:TicketOverview.IsHighlighted="{Binding ServiceRequestsActiveHighlighted}"
ToolTip="{Binding ServiceRequestsActiveChangeHint}"
Tag="ServiceRequestsActiveSelected"
MouseLeftButtonUp="Label_MouseLeftButtonUp"
FontWeight="Medium"
Foreground="{DynamicResource Color.Green}"
FontSize="12"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Cursor="Hand" />
<Label Grid.Row="4"
Grid.Column="3"
Content="{Binding ServiceRequestsCritical}"
Style="{StaticResource RoundedSelectedLabelStyle}"
local:TicketOverview.IsSelected="{Binding ServiceRequestsCriticalSelected, Mode=TwoWay}"
local:TicketOverview.IsHighlighted="{Binding ServiceRequestsCriticalHighlighted}"
ToolTip="{Binding ServiceRequestsCriticalChangeHint}"
Tag="ServiceRequestsCriticalSelected"
MouseLeftButtonUp="Label_MouseLeftButtonUp"
FontWeight="Medium"
Foreground="{DynamicResource Color.Red}"
FontSize="12"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Cursor="Hand" />
<Label Grid.Row="4"
Grid.Column="4"
Content="{Binding ServiceRequestsNewInfo}"
Style="{StaticResource RoundedSelectedLabelStyle}"
local:TicketOverview.IsSelected="{Binding ServiceRequestsNewInfoSelected, Mode=TwoWay}"
local:TicketOverview.IsHighlighted="{Binding ServiceRequestsNewInfoHighlighted}"
ToolTip="{Binding ServiceRequestsNewInfoChangeHint}"
Tag="ServiceRequestsNewInfoSelected"
MouseLeftButtonUp="Label_MouseLeftButtonUp"
FontWeight="Medium"
Foreground="{DynamicResource Color.Orange}"
FontSize="12"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Cursor="Hand" />
<!-- Unassigned -->
<Label Grid.Row="5"
Grid.Column="0"
Style="{StaticResource TicketOverviewRowHeadingStyle}"
Foreground="{DynamicResource FontColor.DetailsPage.TitleSection.Header}"
Content="{Binding Converter={StaticResource LanguageConverter}, ConverterParameter=TicketOverview.Row.Heading.UnassignedTickets}"
FontWeight="Bold"
FontSize="12" />
<Label Grid.Row="4"
<Label Grid.Row="5"
Grid.Column="1"
Content="{Binding UnassignedTickets}"
Style="{StaticResource RoundedSelectedLabelStyle}"
@@ -293,7 +410,7 @@
HorizontalAlignment="Center"
Cursor="Hand" />
<Label Grid.Row="4"
<Label Grid.Row="5"
Grid.Column="3"
Content="{Binding UnassignedTicketsCritical}"
Style="{StaticResource RoundedSelectedLabelStyle}"

View File

@@ -10,6 +10,7 @@ using System.Windows.Threading;
using C4IT.FASD.Cockpit.Communication;
using C4IT.Logging;
using FasdDesktopUi.Basics;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.Services;
using FasdDesktopUi.Basics.Services.Models;
@@ -31,6 +32,10 @@ namespace FasdDesktopUi.Basics.UserControls
("IncidentActive", new Action<TicketOverviewModel, int>((vm, value) => { vm.IncidentActive = value; })),
("IncidentCritical", new Action<TicketOverviewModel, int>((vm, value) => { vm.IncidentCritical = value; })),
("IncidentNewInfo", new Action<TicketOverviewModel, int>((vm, value) => { vm.IncidentNewInfo = value; })),
("ServiceRequestsNew", new Action<TicketOverviewModel, int>((vm, value) => { vm.ServiceRequestsNew = value; })),
("ServiceRequestsActive", new Action<TicketOverviewModel, int>((vm, value) => { vm.ServiceRequestsActive = value; })),
("ServiceRequestsCritical", new Action<TicketOverviewModel, int>((vm, value) => { vm.ServiceRequestsCritical = value; })),
("ServiceRequestsNewInfo", new Action<TicketOverviewModel, int>((vm, value) => { vm.ServiceRequestsNewInfo = value; })),
("UnassignedTickets", new Action<TicketOverviewModel, int>((vm, value) => { vm.UnassignedTickets = value; })),
("UnassignedTicketsCritical", new Action<TicketOverviewModel, int>((vm, value) => { vm.UnassignedTicketsCritical = value; }))
};
@@ -44,6 +49,10 @@ namespace FasdDesktopUi.Basics.UserControls
("IncidentActive", new Action<TicketOverviewModel, bool>((vm, value) => { vm.IncidentActiveHighlighted = value; })),
("IncidentCritical", new Action<TicketOverviewModel, bool>((vm, value) => { vm.IncidentCriticalHighlighted = value; })),
("IncidentNewInfo", new Action<TicketOverviewModel, bool>((vm, value) => { vm.IncidentNewInfoHighlighted = value; })),
("ServiceRequestsNew", new Action<TicketOverviewModel, bool>((vm, value) => { vm.ServiceRequestsNewHighlighted = value; })),
("ServiceRequestsActive", new Action<TicketOverviewModel, bool>((vm, value) => { vm.ServiceRequestsActiveHighlighted = value; })),
("ServiceRequestsCritical", new Action<TicketOverviewModel, bool>((vm, value) => { vm.ServiceRequestsCriticalHighlighted = value; })),
("ServiceRequestsNewInfo", new Action<TicketOverviewModel, bool>((vm, value) => { vm.ServiceRequestsNewInfoHighlighted = value; })),
("UnassignedTickets", new Action<TicketOverviewModel, bool>((vm, value) => { vm.UnassignedTicketsHighlighted = value; })),
("UnassignedTicketsCritical", new Action<TicketOverviewModel, bool>((vm, value) => { vm.UnassignedTicketsCriticalHighlighted = value; }))
};
@@ -57,6 +66,10 @@ namespace FasdDesktopUi.Basics.UserControls
("IncidentActive", new Action<TicketOverviewModel, string>((vm, value) => { vm.IncidentActiveChangeHint = value; })),
("IncidentCritical", new Action<TicketOverviewModel, string>((vm, value) => { vm.IncidentCriticalChangeHint = value; })),
("IncidentNewInfo", new Action<TicketOverviewModel, string>((vm, value) => { vm.IncidentNewInfoChangeHint = value; })),
("ServiceRequestsNew", new Action<TicketOverviewModel, string>((vm, value) => { vm.ServiceRequestsNewChangeHint = value; })),
("ServiceRequestsActive", new Action<TicketOverviewModel, string>((vm, value) => { vm.ServiceRequestsActiveChangeHint = value; })),
("ServiceRequestsCritical", new Action<TicketOverviewModel, string>((vm, value) => { vm.ServiceRequestsCriticalChangeHint = value; })),
("ServiceRequestsNewInfo", new Action<TicketOverviewModel, string>((vm, value) => { vm.ServiceRequestsNewInfoChangeHint = value; })),
("UnassignedTickets", new Action<TicketOverviewModel, string>((vm, value) => { vm.UnassignedTicketsChangeHint = value; })),
("UnassignedTicketsCritical", new Action<TicketOverviewModel, string>((vm, value) => { vm.UnassignedTicketsCriticalChangeHint = value; }))
};
@@ -175,6 +188,41 @@ namespace FasdDesktopUi.Basics.UserControls
}
#endregion
#region Row Heading Ticker
private void RowHeading_MouseEnter(object sender, MouseEventArgs e)
{
if (!TryGetRowHeadingTickerParts(sender, out var textBlock, out var container))
return;
cUtility.SetTickerTextAnimation(textBlock, container);
}
private void RowHeading_MouseLeave(object sender, MouseEventArgs e)
{
if (!TryGetRowHeadingTickerParts(sender, out var textBlock, out var container))
return;
textBlock.TextTrimming = TextTrimming.CharacterEllipsis;
textBlock.BeginAnimation(MarginProperty, null);
container.ClearValue(WidthProperty);
}
private static bool TryGetRowHeadingTickerParts(object sender, out TextBlock textBlock, out Border container)
{
textBlock = null;
container = null;
if (!(sender is Label label) || label.Template == null)
return false;
textBlock = label.Template.FindName("PART_TickerText", label) as TextBlock;
container = label.Template.FindName("PART_TickerContainer", label) as Border;
return textBlock != null && container != null;
}
#endregion
#region Methods
private async void TicketOverview_Loaded(object sender, RoutedEventArgs e)
{
@@ -225,6 +273,15 @@ namespace FasdDesktopUi.Basics.UserControls
if (DataContext is TicketOverviewModel vm)
vm.ResetSelection();
}
public void SetTicketAndServiceRequestsVisibility(bool isVisible)
{
Dispatcher.Invoke(() =>
{
TicketsRow.Height = isVisible ? GridLength.Auto : new GridLength(0);
ServiceRequestsRow.Height = isVisible ? GridLength.Auto : new GridLength(0);
});
}
/// <summary>
/// Prüft welche Daten für die Ansicht geladen werden sollen
/// Wenn UseRoledTickets = true dann Rolebased Tickets und Incidents laden
@@ -268,6 +325,8 @@ namespace FasdDesktopUi.Basics.UserControls
if (counts == null || counts.Count == 0)
counts = await LoadCountsFallbackAsync(useRoleTickets).ConfigureAwait(false);
SetTicketAndServiceRequestsVisibility(TicketOverviewUpdateService.Instance?.TicketAndServiceRequestsEnabled == true);
await Dispatcher.InvokeAsync(() =>
{
foreach (var (key, setter) in CategorySetters)

View File

@@ -399,9 +399,24 @@ Wie möchten Sie fortfahren?</Language>
<Language Lang="DE">Letzte Suchanfragen</Language>
</UIItem>
<UIItem Name="Searchbar.Relations.SearchFor">
<Language Lang="EN">Search {0}</Language>
<Language Lang="DE">Suche {0}</Language>
<UIItem Name="Searchbar.Relations.SearchFor.Computer">
<Language Lang="EN">Search Computers</Language>
<Language Lang="DE">Suche Computer</Language>
</UIItem>
<UIItem Name="Searchbar.Relations.SearchFor.User">
<Language Lang="EN">Search users</Language>
<Language Lang="DE">Suche Anwender</Language>
</UIItem>
<UIItem Name="Searchbar.Relations.SearchFor.Ticket">
<Language Lang="EN">Search tickets</Language>
<Language Lang="DE">Suche Tickets</Language>
</UIItem>
<UIItem Name="Searchbar.Relations.SearchFor.VirtualSession">
<Language Lang="EN">Search virtual sessions</Language>
<Language Lang="DE">Suche virtuelle Sitzungen</Language>
</UIItem>
<UIItem Name="Searchbar.Header.Template">
@@ -545,6 +560,11 @@ Wie möchten Sie fortfahren?</Language>
<Language Lang="DE">Eigene Störungen</Language>
</UIItem>
<UIItem Name="TicketOverview.ScopeRow.Personal.ServiceRequests">
<Language Lang="EN">My service requests</Language>
<Language Lang="DE">Eigene Serviceanfragen</Language>
</UIItem>
<UIItem Name="TicketOverview.ScopeRow.Personal.UnassignedTickets">
<Language Lang="EN">My unassigned</Language>
<Language Lang="DE">Eigener Eingang</Language>
@@ -560,6 +580,11 @@ Wie möchten Sie fortfahren?</Language>
<Language Lang="DE">Rollenstörungen</Language>
</UIItem>
<UIItem Name="TicketOverview.ScopeRow.Role.ServiceRequests">
<Language Lang="EN">Role service requests</Language>
<Language Lang="DE">Rollen-Serviceanfragen</Language>
</UIItem>
<UIItem Name="TicketOverview.ScopeRow.Role.UnassignedTickets">
<Language Lang="EN">Role unassigned</Language>
<Language Lang="DE">Rolleneingang</Language>
@@ -604,8 +629,8 @@ Alle offenen Fälle werden dabei geschlossen.</Language>
</UIItem>
<UIItem Name="Menu.SelectLanguage">
<Language Lang="EN">Select language</Language>
<Language Lang="DE">Sprache festlegen</Language>
<Language Lang="EN">Select language &amp; Format</Language>
<Language Lang="DE">Sprache &amp; Format festlegen</Language>
</UIItem>
<UIItem Name="Menu.SelectLanguage.RestartDialog.Caption">
@@ -1739,6 +1764,11 @@ Wollen Sie das F4SD Cockpit jetzt neu starten?</Language>
<Language Lang="DE">Störungen</Language>
</UIItem>
<UIItem Name="TicketOverview.Row.Heading.ServiceRequests">
<Language Lang="EN">Service requests</Language>
<Language Lang="DE">Serviceanfragen</Language>
</UIItem>
<UIItem Name="TicketOverview.Row.Heading.UnassignedTickets">
<Language Lang="EN">Unassigned</Language>
<Language Lang="DE">Eingang</Language>

View File

@@ -252,6 +252,7 @@
<Compile Include="Basics\PrivateSecurePassword.cs" />
<Compile Include="Basics\UiActions\ChangeHealthCardAction.cs" />
<Compile Include="Basics\UiActions\CopyQuickActionProtocolAction.cs" />
<Compile Include="Basics\UiActions\NativeActions\QuickActionExecutor.cs" />
<Compile Include="Basics\UiActions\NativeActions\RemoteConnectionAction.cs" />
<Compile Include="Basics\UiActions\UiCopyDetailsTableContent.cs" />
<Compile Include="Basics\UiActions\UiDummyQuickAction.cs" />
@@ -294,6 +295,11 @@
<Compile Include="Basics\UserControls\CustomMenuItemToolTipTemplate.xaml.cs">
<DependentUpon>CustomMenuItemToolTipTemplate.xaml</DependentUpon>
</Compile>
<Compile Include="Basics\Models\QuickActionOutput\cQuickActionMeasureValue.cs" />
<Compile Include="Basics\Models\QuickActionOutput\cQuickActionOutput.cs" />
<Compile Include="Basics\Models\QuickActionOutput\cQuickActionOutputList.cs" />
<Compile Include="Basics\Models\QuickActionOutput\cQuickActionOutputObject.cs" />
<Compile Include="Basics\Models\QuickActionOutput\cQuickActionOutputSingle.cs" />
<Compile Include="Basics\UserControls\Gamification\LevelTracker.xaml.cs">
<DependentUpon>LevelTracker.xaml</DependentUpon>
</Compile>
@@ -1012,6 +1018,10 @@
<EmbeddedResource Include="Config\LanguageDefinitions.xml" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\F4SD-ActionConnector\F4SD-ActionConnector.csproj">
<Project>{887c6721-b9f3-c83a-3d55-5e684ca19592}</Project>
<Name>F4SD-ActionConnector</Name>
</ProjectReference>
<ProjectReference Include="..\F4SD-AdaptableIcon\F4SD-AdaptableIcon.csproj">
<Project>{bab63a6a-1524-435d-9f96-7a30b6ee0624}</Project>
<Name>F4SD-AdaptableIcon</Name>
@@ -1119,9 +1129,6 @@ taskkill -im "F4SD-Cockpit-Client.exe" -f -FI "STATUS eq RUNNING"
<PhoenixFiles Include="$(SolutionDir)PhoenixViewer\*" />
</ItemGroup>
<MakeDir Directories="$(TargetDir)Phoenix" />
<Copy
SourceFiles="@(PhoenixFiles)"
DestinationFolder="$(TargetDir)Phoenix"
SkipUnchangedFiles="true" />
<Copy SourceFiles="@(PhoenixFiles)" DestinationFolder="$(TargetDir)Phoenix" SkipUnchangedFiles="true" />
</Target>
</Project>

View File

@@ -105,6 +105,7 @@ namespace FasdDesktopUi.Pages.SearchPage
if (TicketOverviewUpdateService.Instance != null)
{
TicketOverviewUpdateService.Instance.OverviewCountsChanged += TicketOverviewUpdateService_OverviewCountsChanged;
TicketOverviewUpdateService.Instance.TicketAndServiceRequestsAvailabilityChanged += TicketOverviewUpdateService_TicketAndServiceRequestsAvailabilityChanged;
}
UiSettingsChanged(null, null);
@@ -732,7 +733,10 @@ namespace FasdDesktopUi.Pages.SearchPage
try
{
if (TicketOverviewUpdateService.Instance != null)
{
TicketOverviewUpdateService.Instance.OverviewCountsChanged -= TicketOverviewUpdateService_OverviewCountsChanged;
TicketOverviewUpdateService.Instance.TicketAndServiceRequestsAvailabilityChanged -= TicketOverviewUpdateService_TicketAndServiceRequestsAvailabilityChanged;
}
_pipeServer?.Dispose();
}
catch { }
@@ -1008,8 +1012,11 @@ namespace FasdDesktopUi.Pages.SearchPage
if (relation == null || relation.Type != enumF4sdSearchResultClass.Ticket)
return Task.CompletedTask;
if (TryOpenTicketOverviewRelationExternally(relation))
if (!TicketExternalLinkHelper.HasValidUserIdentity(relation))
{
TryOpenTicketOverviewRelationExternally(relation, forceExternal: true);
return Task.CompletedTask;
}
var ticketName = string.IsNullOrWhiteSpace(relation.DisplayName) ? relation.Name : relation.DisplayName;
var ticketId = relation.id;
@@ -1028,22 +1035,19 @@ namespace FasdDesktopUi.Pages.SearchPage
relation.Infos.TryGetValue("UserSid", out sids);
}
return RunTicketSearchAsync(ticketName, ticketId, userName, sids, suppressUi: true);
return RunTicketSearchAsync(ticketName, ticketId, userName, sids, suppressUi: true, ticketOverviewRelation: relation);
}
private bool TryOpenTicketOverviewRelationExternally(cF4sdApiSearchResultRelation relation)
private bool TryOpenTicketOverviewRelationExternally(cF4sdApiSearchResultRelation relation, bool forceExternal = false)
{
return TicketExternalLinkHelper.TryOpenTicketRelationExternally(relation);
return TicketExternalLinkHelper.TryOpenTicketRelationExternally(relation, forceExternal);
}
private Task RunTicketSearchAsync(string ticketName, Guid ticketId, string userName, string sids, bool suppressUi = false)
private Task RunTicketSearchAsync(string ticketName, Guid ticketId, string userName, string sids, bool suppressUi = false, cF4sdApiSearchResultRelation ticketOverviewRelation = null)
{
if (ticketId == Guid.Empty)
return Task.CompletedTask;
if (suppressUi)
BeginTicketOverviewAutoContinue(TimeSpan.FromSeconds(6));
return Task.Run(async () =>
{
try
@@ -1054,6 +1058,7 @@ namespace FasdDesktopUi.Pages.SearchPage
if (_result is null || _result.Count == 0 || _result.First().Value.Count == 0)
{
LogEntry($"No corresponding user could be found for ticket '{ticketName}'", LogLevels.Warning);
TryOpenTicketOverviewRelationExternally(ticketOverviewRelation, forceExternal: true);
if (suppressUi)
EndTicketOverviewAutoContinue(showSearch: true);
return;
@@ -1063,6 +1068,7 @@ namespace FasdDesktopUi.Pages.SearchPage
if (userId == Guid.Empty)
{
LogEntry($"No valid user id could be found for ticket '{ticketName}'", LogLevels.Warning);
TryOpenTicketOverviewRelationExternally(ticketOverviewRelation, forceExternal: true);
if (suppressUi)
EndTicketOverviewAutoContinue(showSearch: true);
return;
@@ -1074,6 +1080,9 @@ namespace FasdDesktopUi.Pages.SearchPage
DisplayName = ticketName,
id = ticketId,
Status = enumF4sdSearchResultStatus.Active,
Infos = ticketOverviewRelation?.Infos == null
? null
: new Dictionary<string, string>(ticketOverviewRelation.Infos),
Identities = new cF4sdIdentityList
{
new cF4sdIdentityEntry()
@@ -1092,6 +1101,9 @@ namespace FasdDesktopUi.Pages.SearchPage
var filteredResults = new cFilteredResults(_result) { AutoContinue = true, PreSelectedRelation = _ticketRelation };
var strInfo = string.Format(cMultiLanguageSupport.GetItem("Searchbar.TicketSearch.Info"), ticketName);
if (suppressUi)
BeginTicketOverviewAutoContinue(TimeSpan.FromSeconds(6));
await ShowExternalSearchInfoAsync(strInfo, filteredResults, enumF4sdSearchResultClass.Ticket, suppressUi);
}
catch (Exception E)
@@ -1291,6 +1303,8 @@ namespace FasdDesktopUi.Pages.SearchPage
var service = TicketOverviewUpdateService.Instance;
if (service == null)
return;
TicketOverviewUc?.SetTicketAndServiceRequestsVisibility(service.TicketAndServiceRequestsEnabled);
var counts = service.GetCountsForScope(IsFilterChecked);
if (counts == null || counts.Count == 0)
return;
@@ -1299,6 +1313,13 @@ namespace FasdDesktopUi.Pages.SearchPage
TicketOverviewUc?.RefreshHighlightState(IsFilterChecked);
}
private void TicketOverviewUpdateService_TicketAndServiceRequestsAvailabilityChanged(object sender, EventArgs e)
{
var isVisible = TicketOverviewUpdateService.Instance?.TicketAndServiceRequestsEnabled == true;
TicketOverviewUc?.SetTicketAndServiceRequestsVisibility(isVisible);
ScheduleSearchResultMaxHeightUpdate();
}
private string BuildNotificationMessage(IReadOnlyList<TileCountChange> changes)
{
if (changes == null || changes.Count == 0)
@@ -1362,6 +1383,8 @@ namespace FasdDesktopUi.Pages.SearchPage
rowKey = "TicketOverview.Row.Heading.Tickets";
else if (normalized.StartsWith("incident"))
rowKey = "TicketOverview.Row.Heading.Incidents";
else if (normalized.StartsWith("servicerequests"))
rowKey = "TicketOverview.Row.Heading.ServiceRequests";
else if (normalized.StartsWith("unassigned"))
rowKey = "TicketOverview.Row.Heading.UnassignedTickets";
else
@@ -1389,6 +1412,8 @@ namespace FasdDesktopUi.Pages.SearchPage
suffix = "Tickets";
else if (string.Equals(rowKey, "TicketOverview.Row.Heading.Incidents", StringComparison.OrdinalIgnoreCase))
suffix = "Incidents";
else if (string.Equals(rowKey, "TicketOverview.Row.Heading.ServiceRequests", StringComparison.OrdinalIgnoreCase))
suffix = "ServiceRequests";
else if (string.Equals(rowKey, "TicketOverview.Row.Heading.UnassignedTickets", StringComparison.OrdinalIgnoreCase))
suffix = "UnassignedTickets";

View File

@@ -173,10 +173,11 @@ namespace FasdDesktopUi.Pages.ShortCutPage
{
Names = new C4IT.MultiLanguage.cMultiLanguageDictionary()
{
["EN"] = "Open Quick Action menu",
["DE"] = "Quick Action Menü öffnen"
["EN"] = "Open Quick Action menu/close",
["DE"] = "Quick Action Menü öffnen/schließen"
},
HotKey = Key.Q,
AlternativeKey = Key.Escape
},
new cHotKeyInformation()
{