tweaks before iamges
This commit is contained in:
parent
bd4085dfce
commit
7fe250a411
|
|
@ -10,6 +10,10 @@
|
||||||
Text="Clear"
|
Text="Clear"
|
||||||
Order="Secondary"
|
Order="Secondary"
|
||||||
Clicked="OnClearClicked" />
|
Clicked="OnClearClicked" />
|
||||||
|
<ToolbarItem x:Name="CopyForAIToolbarItem"
|
||||||
|
Text="Copy for AI"
|
||||||
|
Order="Primary"
|
||||||
|
Clicked="OnCopyForAIClicked" />
|
||||||
<ToolbarItem x:Name="ShareToolbarItem"
|
<ToolbarItem x:Name="ShareToolbarItem"
|
||||||
Text="Share"
|
Text="Share"
|
||||||
Order="Primary"
|
Order="Primary"
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,25 @@ public partial class AppLogView : ContentPage
|
||||||
set => SetValue(ShowClearButtonProperty, value);
|
set => SetValue(ShowClearButtonProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to show the Copy for AI toolbar item. Default: true.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly BindableProperty ShowCopyForAIButtonProperty = BindableProperty.Create(
|
||||||
|
nameof(ShowCopyForAIButton),
|
||||||
|
typeof(bool),
|
||||||
|
typeof(AppLogView),
|
||||||
|
true,
|
||||||
|
propertyChanged: (b, o, n) => ((AppLogView)b).CopyForAIToolbarItem.IsEnabled = (bool)n);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether the Copy for AI toolbar button is visible.
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowCopyForAIButton
|
||||||
|
{
|
||||||
|
get => (bool)GetValue(ShowCopyForAIButtonProperty);
|
||||||
|
set => SetValue(ShowCopyForAIButtonProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether to automatically refresh when new items are added to the TelemetryClient. Default: false.
|
/// Whether to automatically refresh when new items are added to the TelemetryClient. Default: false.
|
||||||
/// When enabled, polls for new items every 2 seconds.
|
/// When enabled, polls for new items every 2 seconds.
|
||||||
|
|
@ -186,6 +205,11 @@ public partial class AppLogView : ContentPage
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler? LogsRefreshed;
|
public event EventHandler? LogsRefreshed;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised when logs are copied for AI debugging.
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler? LogsCopiedForAI;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Public Methods
|
#region Public Methods
|
||||||
|
|
@ -313,6 +337,123 @@ public partial class AppLogView : ContentPage
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Export logs in a format optimized for AI debugging assistance.
|
||||||
|
/// Includes system context, environment info, and structured log data.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>AI-optimized log export string.</returns>
|
||||||
|
public string ExportLogsForAI()
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
// Header for AI
|
||||||
|
sb.AppendLine("# Application Debug Log for AI Analysis");
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("Please analyze the following application logs and help identify issues, root causes, and potential fixes.");
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
// System Context
|
||||||
|
sb.AppendLine("## Environment");
|
||||||
|
sb.AppendLine("```");
|
||||||
|
sb.AppendLine($"Platform: {DeviceInfo.Platform}");
|
||||||
|
sb.AppendLine($"OS Version: {DeviceInfo.VersionString}");
|
||||||
|
sb.AppendLine($"Device: {DeviceInfo.Model} ({DeviceInfo.Manufacturer})");
|
||||||
|
sb.AppendLine($"Device Type: {DeviceInfo.DeviceType}");
|
||||||
|
sb.AppendLine($"App: {AppInfo.Name} v{AppInfo.VersionString} (Build {AppInfo.BuildString})");
|
||||||
|
sb.AppendLine($"Framework: .NET MAUI");
|
||||||
|
sb.AppendLine($"Captured: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC");
|
||||||
|
sb.AppendLine($"Local Time: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
|
||||||
|
sb.AppendLine($"Timezone: {TimeZoneInfo.Local.DisplayName}");
|
||||||
|
sb.AppendLine("```");
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
// Summary
|
||||||
|
var exceptions = _logItems.Where(i => i.Type == "exception").ToList();
|
||||||
|
var warnings = _logItems.Where(i => i.Type == "warning").ToList();
|
||||||
|
var journeyEvents = _logItems.Where(i => i.Type.StartsWith("journey_") || i.Type.StartsWith("step_")).ToList();
|
||||||
|
|
||||||
|
sb.AppendLine("## Summary");
|
||||||
|
sb.AppendLine($"- **Total Entries:** {_logItems.Count}");
|
||||||
|
sb.AppendLine($"- **Exceptions:** {exceptions.Count}");
|
||||||
|
sb.AppendLine($"- **Warnings:** {warnings.Count}");
|
||||||
|
sb.AppendLine($"- **Journey Events:** {journeyEvents.Count}");
|
||||||
|
if (_logItems.Count > 0)
|
||||||
|
{
|
||||||
|
var oldest = _logItems.Min(i => i.Timestamp);
|
||||||
|
var newest = _logItems.Max(i => i.Timestamp);
|
||||||
|
sb.AppendLine($"- **Time Range:** {oldest:HH:mm:ss} to {newest:HH:mm:ss} UTC ({(newest - oldest).TotalSeconds:F1}s span)");
|
||||||
|
}
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
// Exceptions first (most important for debugging)
|
||||||
|
if (exceptions.Count > 0)
|
||||||
|
{
|
||||||
|
sb.AppendLine("## Exceptions");
|
||||||
|
sb.AppendLine();
|
||||||
|
foreach (var item in exceptions.OrderByDescending(i => i.Timestamp))
|
||||||
|
{
|
||||||
|
sb.AppendLine($"### {item.Title}");
|
||||||
|
sb.AppendLine($"**Time:** {item.Timestamp:yyyy-MM-dd HH:mm:ss} UTC");
|
||||||
|
if (!string.IsNullOrEmpty(item.JourneyId))
|
||||||
|
sb.AppendLine($"**Journey ID:** {item.JourneyId}");
|
||||||
|
if (!string.IsNullOrEmpty(item.UserId))
|
||||||
|
sb.AppendLine($"**User ID:** {item.UserId}");
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("**Message:**");
|
||||||
|
sb.AppendLine($"```");
|
||||||
|
sb.AppendLine(item.Message);
|
||||||
|
sb.AppendLine($"```");
|
||||||
|
if (!string.IsNullOrEmpty(item.StackTrace))
|
||||||
|
{
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("**Stack Trace:**");
|
||||||
|
sb.AppendLine("```");
|
||||||
|
sb.AppendLine(item.StackTrace);
|
||||||
|
sb.AppendLine("```");
|
||||||
|
}
|
||||||
|
sb.AppendLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All logs in chronological order
|
||||||
|
sb.AppendLine("## Full Log (Chronological)");
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("| Time (UTC) | Type | Title | Message |");
|
||||||
|
sb.AppendLine("|------------|------|-------|---------|");
|
||||||
|
foreach (var item in _logItems.OrderBy(i => i.Timestamp))
|
||||||
|
{
|
||||||
|
var msg = item.Message?.Replace("|", "\\|").Replace("\n", " ").Replace("\r", "") ?? "";
|
||||||
|
if (msg.Length > 100) msg = msg[..97] + "...";
|
||||||
|
var title = item.Title?.Replace("|", "\\|") ?? "";
|
||||||
|
if (title.Length > 50) title = title[..47] + "...";
|
||||||
|
sb.AppendLine($"| {item.Timestamp:HH:mm:ss.fff} | {item.TypeDisplay} | {title} | {msg} |");
|
||||||
|
}
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
// Detailed entries for non-exceptions
|
||||||
|
var otherItems = _logItems.Where(i => i.Type != "exception").OrderByDescending(i => i.Timestamp).ToList();
|
||||||
|
if (otherItems.Count > 0)
|
||||||
|
{
|
||||||
|
sb.AppendLine("## Other Log Details");
|
||||||
|
sb.AppendLine();
|
||||||
|
foreach (var item in otherItems)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"### [{item.TypeDisplay}] {item.Title}");
|
||||||
|
sb.AppendLine($"**Time:** {item.Timestamp:yyyy-MM-dd HH:mm:ss.fff} UTC");
|
||||||
|
if (!string.IsNullOrEmpty(item.JourneyId))
|
||||||
|
sb.AppendLine($"**Journey ID:** {item.JourneyId}");
|
||||||
|
if (!string.IsNullOrEmpty(item.Message))
|
||||||
|
{
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine(item.Message);
|
||||||
|
}
|
||||||
|
sb.AppendLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the current log items as a read-only list.
|
/// Get the current log items as a read-only list.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -356,6 +497,33 @@ public partial class AppLogView : ContentPage
|
||||||
LogsShared?.Invoke(this, EventArgs.Empty);
|
LogsShared?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void OnCopyForAIClicked(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (_logItems.Count == 0)
|
||||||
|
{
|
||||||
|
await DisplayAlert("No Logs", "There are no logs to copy.", "OK");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var content = ExportLogsForAI();
|
||||||
|
await Clipboard.Default.SetTextAsync(content);
|
||||||
|
|
||||||
|
// Show feedback
|
||||||
|
await MainThread.InvokeOnMainThreadAsync(async () =>
|
||||||
|
{
|
||||||
|
var originalText = CountLabel.Text;
|
||||||
|
CountLabel.Text = "Copied for AI!";
|
||||||
|
CountLabel.TextColor = Colors.Green;
|
||||||
|
await Task.Delay(1500);
|
||||||
|
CountLabel.Text = originalText;
|
||||||
|
CountLabel.TextColor = Application.Current?.RequestedTheme == AppTheme.Dark
|
||||||
|
? Color.FromArgb("#9CA3AF")
|
||||||
|
: Color.FromArgb("#6B7280");
|
||||||
|
});
|
||||||
|
|
||||||
|
LogsCopiedForAI?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
private async void OnLogSelected(object? sender, SelectionChangedEventArgs e)
|
private async void OnLogSelected(object? sender, SelectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.CurrentSelection.FirstOrDefault() is LogItem item)
|
if (e.CurrentSelection.FirstOrDefault() is LogItem item)
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,18 @@
|
||||||
|
|
||||||
<RootNamespace>IronServices.Maui</RootNamespace>
|
<RootNamespace>IronServices.Maui</RootNamespace>
|
||||||
<AssemblyName>IronServices.Maui</AssemblyName>
|
<AssemblyName>IronServices.Maui</AssemblyName>
|
||||||
<Description>Control library library for IronServices API (IronLicensing, IronTelemetry and IronNotify)</Description>
|
<Version>1.1.0</Version>
|
||||||
|
<Description>MAUI UI controls for IronServices - includes LoginView, LicenseActivationView, AppLogView for telemetry logs, and UserJourneyView for visualizing user journeys with Timeline, Tree, and Flow view modes.</Description>
|
||||||
<Authors>David H Friedel Jr</Authors>
|
<Authors>David H Friedel Jr</Authors>
|
||||||
<Company>MarketAlly</Company>
|
<Company>MarketAlly</Company>
|
||||||
<PackageId>IronServices.Maui</PackageId>
|
<PackageId>IronServices.Maui</PackageId>
|
||||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
||||||
<PackageIcon>nuget_im.png</PackageIcon>
|
<PackageIcon>nuget_im.png</PackageIcon>
|
||||||
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
<PackageTags>maui;licensing;telemetry;error-monitoring;user-journeys;ironservices;controls;ui</PackageTags>
|
||||||
|
<RepositoryType>git</RepositoryType>
|
||||||
|
<Copyright>Copyright (c) 2025 MarketAlly</Copyright>
|
||||||
|
|
||||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">15.0</SupportedOSPlatformVersion>
|
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">15.0</SupportedOSPlatformVersion>
|
||||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">15.0</SupportedOSPlatformVersion>
|
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">15.0</SupportedOSPlatformVersion>
|
||||||
|
|
|
||||||
217
README.md
217
README.md
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
MAUI controls and services for IronServices (IronLicensing, IronNotify, IronTelemetry).
|
MAUI controls and services for IronServices (IronLicensing, IronNotify, IronTelemetry).
|
||||||
|
|
||||||
|
[](https://www.nuget.org/packages/IronServices.Maui/)
|
||||||
|
[](https://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
|
|
@ -37,6 +40,15 @@ builder.Services.AddIronServices(options =>
|
||||||
|
|
||||||
## Controls
|
## Controls
|
||||||
|
|
||||||
|
| Control | Type | Description |
|
||||||
|
|---------|------|-------------|
|
||||||
|
| [`LoginView`](#loginview) | ContentView | Drop-in login form |
|
||||||
|
| [`LicenseActivationView`](#licenseactivationview) | ContentView | License key entry and activation |
|
||||||
|
| [`AppLogView`](#applogview) | ContentPage | Exception and telemetry log viewer |
|
||||||
|
| [`UserJourneyView`](#userjourneyview) | ContentPage | User journey visualization with Timeline/Tree/Flow modes |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### LoginView
|
### LoginView
|
||||||
|
|
||||||
A drop-in login form for IronServices authentication.
|
A drop-in login form for IronServices authentication.
|
||||||
|
|
@ -154,6 +166,7 @@ A full-page log viewer for exceptions and telemetry events. Shows queued items f
|
||||||
| `Title` | `string` | `"App Logs"` | Page title (shown in navigation bar). |
|
| `Title` | `string` | `"App Logs"` | Page title (shown in navigation bar). |
|
||||||
| `ShowShareButton` | `bool` | `true` | Enable Share toolbar button. |
|
| `ShowShareButton` | `bool` | `true` | Enable Share toolbar button. |
|
||||||
| `ShowClearButton` | `bool` | `true` | Enable Clear toolbar button. |
|
| `ShowClearButton` | `bool` | `true` | Enable Clear toolbar button. |
|
||||||
|
| `EnableLiveUpdates` | `bool` | `false` | Auto-refresh when new items are added (polls every 2s). |
|
||||||
|
|
||||||
#### Events
|
#### Events
|
||||||
|
|
||||||
|
|
@ -182,12 +195,15 @@ A full-page log viewer for exceptions and telemetry events. Shows queued items f
|
||||||
| Property | Type | Description |
|
| Property | Type | Description |
|
||||||
|----------|------|-------------|
|
|----------|------|-------------|
|
||||||
| `Timestamp` | `DateTime` | UTC timestamp. |
|
| `Timestamp` | `DateTime` | UTC timestamp. |
|
||||||
| `Type` | `string` | `"exception"`, `"message"`, `"info"`, `"warning"`, etc. |
|
| `Type` | `string` | `"exception"`, `"message"`, `"journey_start"`, `"step_start"`, etc. |
|
||||||
| `Title` | `string` | Exception type name or message title. |
|
| `Title` | `string` | Exception type name or message title. |
|
||||||
| `Message` | `string` | Log message. |
|
| `Message` | `string` | Log message. |
|
||||||
| `StackTrace` | `string?` | Stack trace for exceptions. |
|
| `StackTrace` | `string?` | Stack trace for exceptions. |
|
||||||
| `JourneyId` | `string?` | Associated user journey ID. |
|
| `JourneyId` | `string?` | Associated user journey ID. |
|
||||||
| `UserId` | `string?` | Associated user ID. |
|
| `UserId` | `string?` | Associated user ID. |
|
||||||
|
| `TypeDisplay` | `string` | Short type label: `"ERROR"`, `"MSG"`, `"START"`, etc. |
|
||||||
|
| `TypeColor` | `Color` | Color for type badge. |
|
||||||
|
| `TimestampDisplay` | `string` | Formatted local time (HH:mm:ss). |
|
||||||
|
|
||||||
#### Example
|
#### Example
|
||||||
|
|
||||||
|
|
@ -196,7 +212,8 @@ A full-page log viewer for exceptions and telemetry events. Shows queued items f
|
||||||
await Navigation.PushAsync(new AppLogView
|
await Navigation.PushAsync(new AppLogView
|
||||||
{
|
{
|
||||||
TelemetryClient = _telemetryClient,
|
TelemetryClient = _telemetryClient,
|
||||||
Title = "Error Logs"
|
Title = "Error Logs",
|
||||||
|
EnableLiveUpdates = true
|
||||||
});
|
});
|
||||||
|
|
||||||
// With event handling
|
// With event handling
|
||||||
|
|
@ -213,9 +230,176 @@ logView.AddLog("warning", "Network Issue", "Connection timeout after 30s");
|
||||||
|
|
||||||
#### Toolbar
|
#### Toolbar
|
||||||
|
|
||||||
|
- **Refresh** (in header): Reloads from queue.
|
||||||
- **Share** (Primary): Exports logs as text and opens system share sheet.
|
- **Share** (Primary): Exports logs as text and opens system share sheet.
|
||||||
- **Clear** (Secondary/overflow menu): Clears displayed logs.
|
- **Clear** (Secondary/overflow): Clears displayed logs.
|
||||||
- **Refresh** button in summary bar: Reloads from queue.
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### UserJourneyView
|
||||||
|
|
||||||
|
A full-page user journey viewer with multiple visualization modes. Reconstructs journeys from `TelemetryClient` log items and displays steps, breadcrumbs, exceptions, and metadata.
|
||||||
|
|
||||||
|
**Type:** `ContentPage` (standalone page, use with NavigationPage)
|
||||||
|
|
||||||
|
#### View Modes
|
||||||
|
|
||||||
|
| Mode | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| `Timeline` | Vertical list with nested steps shown with indentation (default). |
|
||||||
|
| `Tree` | Expandable tree hierarchy with collapsible journey/step nodes. |
|
||||||
|
| `Flow` | Horizontal flowchart-style diagram showing step progression. |
|
||||||
|
|
||||||
|
#### Properties
|
||||||
|
|
||||||
|
| Property | Type | Default | Description |
|
||||||
|
|----------|------|---------|-------------|
|
||||||
|
| `TelemetryClient` | `TelemetryClient` | `null` | Client to pull journey data from. Auto-refreshes on set. |
|
||||||
|
| `Title` | `string` | `"User Journeys"` | Page title. |
|
||||||
|
| `ViewMode` | `JourneyViewMode` | `Timeline` | Current visualization mode. |
|
||||||
|
| `EnableLiveUpdates` | `bool` | `false` | Auto-refresh when new items are added (polls every 2s). |
|
||||||
|
| `ShowShareButton` | `bool` | `true` | Enable Share toolbar button. |
|
||||||
|
| `ShowClearButton` | `bool` | `true` | Enable Clear toolbar button. |
|
||||||
|
| `UserIdFilter` | `string?` | `null` | Filter to show only journeys for a specific user. |
|
||||||
|
|
||||||
|
#### Events
|
||||||
|
|
||||||
|
| Event | Args | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `JourneySelected` | `JourneyItem` | User selected a journey from the list. |
|
||||||
|
| `StepSelected` | `StepItem` | User selected a step in the detail panel. |
|
||||||
|
| `JourneysRefreshed` | `EventArgs` | Journey list was refreshed. |
|
||||||
|
| `JourneysCleared` | `EventArgs` | Journeys were cleared. |
|
||||||
|
| `JourneysShared` | `EventArgs` | Journeys were exported/shared. |
|
||||||
|
|
||||||
|
#### Methods
|
||||||
|
|
||||||
|
| Method | Returns | Description |
|
||||||
|
|--------|---------|-------------|
|
||||||
|
| `RefreshJourneys()` | `void` | Reload journeys from TelemetryClient. |
|
||||||
|
| `ClearJourneys()` | `void` | Clear displayed journeys. |
|
||||||
|
| `ClearAllJourneys()` | `void` | Clear displayed journeys AND the TelemetryClient queue. |
|
||||||
|
| `ExportJourneys()` | `string` | Export all journeys as formatted text. |
|
||||||
|
| `GetJourneys()` | `IReadOnlyList<JourneyItem>` | Get current journey items. |
|
||||||
|
| `JourneyCount` | `int` | Number of journeys. |
|
||||||
|
|
||||||
|
#### JourneyItem Properties
|
||||||
|
|
||||||
|
| Property | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `JourneyId` | `string` | Unique journey identifier. |
|
||||||
|
| `Name` | `string` | Journey name (e.g., "Checkout Flow"). |
|
||||||
|
| `UserId` | `string?` | Associated user ID. |
|
||||||
|
| `UserEmail` | `string?` | Associated user email. |
|
||||||
|
| `Status` | `JourneyDisplayStatus` | `InProgress`, `Completed`, `Failed`, `Abandoned`. |
|
||||||
|
| `StartTime` | `DateTime` | When the journey started. |
|
||||||
|
| `EndTime` | `DateTime?` | When the journey ended (null if in progress). |
|
||||||
|
| `DurationMs` | `double?` | Duration in milliseconds. |
|
||||||
|
| `DurationDisplay` | `string` | Formatted duration: `"125ms"`, `"2.3s"`, `"1.5m"`. |
|
||||||
|
| `TimeAgo` | `string` | Relative time: `"just now"`, `"5m ago"`, `"2h ago"`. |
|
||||||
|
| `StatusDisplay` | `string` | Human-readable status text. |
|
||||||
|
| `StatusColor` | `Color` | Color for status badge (Blue/Green/Red/Gray). |
|
||||||
|
| `StatusIcon` | `string` | Icon text: `"..."`, `"OK"`, `"X"`, `"-"`. |
|
||||||
|
| `Steps` | `ObservableCollection<StepItem>` | Top-level steps in this journey. |
|
||||||
|
| `Breadcrumbs` | `List<BreadcrumbItem>` | Breadcrumbs captured during journey. |
|
||||||
|
| `Exceptions` | `List<ExceptionItem>` | Exceptions captured during journey. |
|
||||||
|
| `Metadata` | `Dictionary<string, object>` | Custom metadata. |
|
||||||
|
| `StepCount` | `int` | Total steps (including nested). |
|
||||||
|
| `FailedStepCount` | `int` | Number of failed steps. |
|
||||||
|
| `HasFailedSteps` | `bool` | Whether any steps failed. |
|
||||||
|
| `HasExceptions` | `bool` | Whether any exceptions occurred. |
|
||||||
|
| `HasUser` | `bool` | Whether a user is associated. |
|
||||||
|
|
||||||
|
#### StepItem Properties
|
||||||
|
|
||||||
|
| Property | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `StepId` | `string` | Unique step identifier. |
|
||||||
|
| `ParentStepId` | `string?` | Parent step ID for nested steps. |
|
||||||
|
| `Name` | `string` | Step name (e.g., "Process Payment"). |
|
||||||
|
| `Category` | `string?` | Category: `"business"`, `"technical"`, `"navigation"`. |
|
||||||
|
| `Status` | `StepDisplayStatus` | `InProgress`, `Completed`, `Failed`, `Skipped`. |
|
||||||
|
| `FailureReason` | `string?` | Reason for failure. |
|
||||||
|
| `StartTime` | `DateTime` | When the step started. |
|
||||||
|
| `EndTime` | `DateTime?` | When the step ended. |
|
||||||
|
| `DurationMs` | `double?` | Duration in milliseconds. |
|
||||||
|
| `DurationDisplay` | `string` | Formatted duration. |
|
||||||
|
| `StatusColor` | `Color` | Color for status indicator. |
|
||||||
|
| `StatusIcon` | `string` | Icon text. |
|
||||||
|
| `ChildSteps` | `ObservableCollection<StepItem>` | Nested child steps. |
|
||||||
|
| `NestingLevel` | `int` | Depth level for indentation. |
|
||||||
|
| `IndentMargin` | `Thickness` | Calculated margin for indentation. |
|
||||||
|
|
||||||
|
#### BreadcrumbItem Properties
|
||||||
|
|
||||||
|
| Property | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `Timestamp` | `DateTime` | When captured. |
|
||||||
|
| `Category` | `string` | Breadcrumb category. |
|
||||||
|
| `Message` | `string` | Breadcrumb message. |
|
||||||
|
| `Level` | `string` | Log level: `"info"`, `"warning"`, `"error"`, `"debug"`. |
|
||||||
|
| `LevelColor` | `Color` | Color based on level. |
|
||||||
|
| `Data` | `Dictionary<string, object>?` | Additional data. |
|
||||||
|
|
||||||
|
#### ExceptionItem Properties
|
||||||
|
|
||||||
|
| Property | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `Timestamp` | `DateTime` | When captured. |
|
||||||
|
| `ExceptionType` | `string` | Exception type name. |
|
||||||
|
| `Message` | `string` | Exception message. |
|
||||||
|
| `StackTrace` | `string?` | Stack trace. |
|
||||||
|
| `StepId` | `string?` | Step ID where exception occurred. |
|
||||||
|
| `HasStackTrace` | `bool` | Whether stack trace is available. |
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Basic usage
|
||||||
|
await Navigation.PushAsync(new UserJourneyView
|
||||||
|
{
|
||||||
|
TelemetryClient = _telemetryClient,
|
||||||
|
EnableLiveUpdates = true
|
||||||
|
});
|
||||||
|
|
||||||
|
// With view mode and filtering
|
||||||
|
var journeyView = new UserJourneyView
|
||||||
|
{
|
||||||
|
TelemetryClient = _telemetryClient,
|
||||||
|
ViewMode = JourneyViewMode.Tree,
|
||||||
|
UserIdFilter = currentUser.Id
|
||||||
|
};
|
||||||
|
await Navigation.PushAsync(journeyView);
|
||||||
|
|
||||||
|
// Event handling
|
||||||
|
var journeyView = new UserJourneyView { TelemetryClient = _telemetryClient };
|
||||||
|
journeyView.JourneySelected += (s, journey) =>
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Selected: {journey.Name} ({journey.StepCount} steps)");
|
||||||
|
};
|
||||||
|
journeyView.StepSelected += (s, step) =>
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Step: {step.Name} - {step.StatusDisplay}");
|
||||||
|
};
|
||||||
|
await Navigation.PushAsync(journeyView);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Detail Panel
|
||||||
|
|
||||||
|
When a journey is selected, a detail panel shows:
|
||||||
|
- **Status, Duration, User** - Summary info
|
||||||
|
- **Steps** - List of steps with status indicators and timing
|
||||||
|
- **Breadcrumbs** - Timestamped breadcrumb trail
|
||||||
|
- **Exceptions** - Exception details (tap to copy)
|
||||||
|
- **Metadata** - Key-value pairs
|
||||||
|
- **Copy to Clipboard** - Export journey details
|
||||||
|
|
||||||
|
#### Toolbar
|
||||||
|
|
||||||
|
- **Timeline/Tree/Flow** buttons - Switch view modes
|
||||||
|
- **Share** (Primary) - Export journeys as text
|
||||||
|
- **Clear** (Secondary) - Clear displayed journeys
|
||||||
|
- **Refresh** - Reload from TelemetryClient
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -310,7 +494,20 @@ private async void OnViewLogsClicked(object sender, EventArgs e)
|
||||||
var telemetry = Handler.MauiContext.Services.GetService<TelemetryClient>();
|
var telemetry = Handler.MauiContext.Services.GetService<TelemetryClient>();
|
||||||
await Navigation.PushAsync(new AppLogView
|
await Navigation.PushAsync(new AppLogView
|
||||||
{
|
{
|
||||||
TelemetryClient = telemetry
|
TelemetryClient = telemetry,
|
||||||
|
EnableLiveUpdates = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// SettingsPage.xaml.cs - Navigate to journeys
|
||||||
|
private async void OnViewJourneysClicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var telemetry = Handler.MauiContext.Services.GetService<TelemetryClient>();
|
||||||
|
await Navigation.PushAsync(new UserJourneyView
|
||||||
|
{
|
||||||
|
TelemetryClient = telemetry,
|
||||||
|
EnableLiveUpdates = true,
|
||||||
|
ViewMode = JourneyViewMode.Timeline
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -329,6 +526,8 @@ All controls use MAUI resource dictionaries. Override these colors in your `App.
|
||||||
<Color x:Key="Gray400">#919191</Color>
|
<Color x:Key="Gray400">#919191</Color>
|
||||||
<Color x:Key="Gray500">#6E6E6E</Color>
|
<Color x:Key="Gray500">#6E6E6E</Color>
|
||||||
<Color x:Key="Gray600">#404040</Color>
|
<Color x:Key="Gray600">#404040</Color>
|
||||||
|
<Color x:Key="Gray700">#374151</Color>
|
||||||
|
<Color x:Key="Gray800">#1F2937</Color>
|
||||||
<Color x:Key="Gray900">#212121</Color>
|
<Color x:Key="Gray900">#212121</Color>
|
||||||
<Color x:Key="White">#FFFFFF</Color>
|
<Color x:Key="White">#FFFFFF</Color>
|
||||||
```
|
```
|
||||||
|
|
@ -342,8 +541,8 @@ Controls support light/dark themes via `AppThemeBinding`.
|
||||||
| Platform | Minimum Version |
|
| Platform | Minimum Version |
|
||||||
|----------|-----------------|
|
|----------|-----------------|
|
||||||
| Android | API 21 (5.0) |
|
| Android | API 21 (5.0) |
|
||||||
| iOS | 14.0 |
|
| iOS | 15.0 |
|
||||||
| macOS | 11.0 |
|
| macOS | 15.0 |
|
||||||
| Windows | 10.0.17763.0 |
|
| Windows | 10.0.17763.0 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -352,11 +551,11 @@ Controls support light/dark themes via `AppThemeBinding`.
|
||||||
|
|
||||||
- `IronServices.Client` - Core API client
|
- `IronServices.Client` - Core API client
|
||||||
- `IronLicensing.Client` - License validation
|
- `IronLicensing.Client` - License validation
|
||||||
- `IronTelemetry.Client` - Telemetry and logging
|
- `IronTelemetry.Client` - Telemetry, journeys, and logging
|
||||||
- `Microsoft.Maui.Controls` - MAUI framework
|
- `Microsoft.Maui.Controls` - MAUI framework
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Proprietary. See LICENSE file.
|
MIT License - see [LICENSE](LICENSE) file.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue