diff --git a/BeWo/View/Detail/Zeiterfassung/ServiceRecordView2.xaml b/BeWo/View/Detail/Zeiterfassung/ServiceRecordView2.xaml
index a5a05a606..cc4e8a470 100644
--- a/BeWo/View/Detail/Zeiterfassung/ServiceRecordView2.xaml
+++ b/BeWo/View/Detail/Zeiterfassung/ServiceRecordView2.xaml
@@ -1737,7 +1737,10 @@
-
+
-
-
+
+
diff --git a/Shared/Core/DiagnosticContext.cs b/Shared/Core/DiagnosticContext.cs
new file mode 100644
index 000000000..8f6e0bcb0
--- /dev/null
+++ b/Shared/Core/DiagnosticContext.cs
@@ -0,0 +1,160 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Shapes;
+
+namespace BS.Shared.Core
+{
+ ///
+ /// Sammelt Systemkontext-Informationen, die einer Exception-Mail
+ /// beigelegt werden können, um Support/Debugging zu erleichtern.
+ ///
+ public static class DiagnosticContext
+ {
+ // Ringpuffer für die letzten User-Aktionen (Thread-safe)
+ private static readonly ConcurrentQueue _actionLog = new ConcurrentQueue();
+ private const int MaxActionLogEntries = 10;
+
+ private static readonly DateTime _appStartTime = DateTime.UtcNow;
+
+ ///
+ /// Optionale Mapping-Funktion: MachineName -> "sprechender" Servername.
+ /// Vom Aufrufer beim Start gesetzt, z. B. aus einer Config.
+ ///
+ public static Func MachineNameResolver { get; set; }
+ = machineName => machineName; // Fallback: keine Übersetzung
+
+ ///
+ /// Registriert eine User-Aktion im Ringpuffer (z. B. Button-Klicks, Commands, Navigation).
+ /// Am besten zentral im Command-Handling oder Navigation-Service aufrufen.
+ ///
+ public static void LogAction(string action)
+ {
+ _actionLog.Enqueue($"{DateTime.UtcNow:HH:mm:ss.fff} - {action}");
+ while (_actionLog.Count > MaxActionLogEntries)
+ {
+ _actionLog.TryDequeue(out _);
+ }
+ }
+
+ ///
+ /// Baut den vollständigen Diagnose-Report als String, z. B. zum Anhängen an eine Exception-Mail.
+ ///
+ public static string BuildReport(Exception ex = null, string currentView = null)
+ {
+ var sb = new StringBuilder();
+
+ sb.AppendLine("=== SYSTEM ===");
+ var machineName = Environment.MachineName;
+ var friendlyName = MachineNameResolver(machineName);
+ sb.AppendLine($"MachineName: {machineName}" +
+ (friendlyName != machineName ? $" ({friendlyName})" : ""));
+ sb.AppendLine($"Systemzeit (UTC): {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss}");
+ sb.AppendLine($"Systemzeit (lokal): {DateTime.Now:yyyy-MM-dd HH:mm:ss} ({TimeZoneInfo.Local.Id})");
+ sb.AppendLine($"OS: {RuntimeInformation.OSDescription}");
+ sb.AppendLine($"Architektur: {RuntimeInformation.OSArchitecture} / Prozess: {RuntimeInformation.ProcessArchitecture}");
+ sb.AppendLine($"CPU Cores: {Environment.ProcessorCount}");
+ sb.AppendLine($"Angemeldeter User:{Environment.UserName}");
+
+ sb.AppendLine();
+ sb.AppendLine("=== RESSOURCEN ===");
+ try
+ {
+ using (var proc = Process.GetCurrentProcess())
+ {
+ sb.AppendLine($"Working Set: {proc.WorkingSet64 / 1024 / 1024} MB");
+ sb.AppendLine($"Prozess-Uptime: {DateTime.UtcNow - _appStartTime:hh\\:mm\\:ss}");
+ sb.AppendLine($"Prozess-ID: {proc.Id}");
+ }
+ }
+ catch (Exception e)
+ {
+ sb.AppendLine($"(Fehler beim Auslesen der Prozessdaten: {e.Message})");
+ }
+
+ try
+ {
+ var drive = new DriveInfo(System.IO.Path.GetPathRoot(AppDomain.CurrentDomain.BaseDirectory) ?? "C:\\");
+ sb.AppendLine($"Freier Speicher: {drive.AvailableFreeSpace / 1024 / 1024 / 1024} GB " +
+ $"von {drive.TotalSize / 1024 / 1024 / 1024} GB auf {drive.Name}");
+ }
+ catch (Exception e)
+ {
+ sb.AppendLine($"(Fehler beim Auslesen des Speicherplatzes: {e.Message})");
+ }
+
+ sb.AppendLine();
+ sb.AppendLine("=== ANWENDUNG ===");
+ var assembly = Assembly.GetEntryAssembly();
+ sb.AppendLine($"App-Version: {assembly?.GetName().Version}");
+ sb.AppendLine($".NET Runtime: {RuntimeInformation.FrameworkDescription}");
+ sb.AppendLine($"Environment: {AppEnvironmentInfo.CurrentEnvironmentName}"); // an deine bestehende Environment-Detection anpassen
+ //if (!string.IsNullOrEmpty(currentView))
+ // sb.AppendLine($"Aktuelle View: {currentView}");
+
+ //sb.AppendLine();
+ //sb.AppendLine("=== NETZWERK / BACKEND ===");
+ //sb.AppendLine($"Ollama erreichbar: {CheckHostReachable("localhost", 500)}"); // Host/Port anpassen
+
+ sb.AppendLine();
+ sb.AppendLine("=== LETZTE USER-AKTIONEN ===");
+ if (_actionLog.IsEmpty)
+ {
+ sb.AppendLine("(keine erfasst)");
+ }
+ else
+ {
+ foreach (var entry in _actionLog)
+ sb.AppendLine(entry);
+ }
+
+ if (ex != null)
+ {
+ sb.AppendLine();
+ sb.AppendLine("=== EXCEPTION ===");
+ AppendExceptionDetails(sb, ex);
+ }
+
+ return sb.ToString();
+ }
+
+ private static void AppendExceptionDetails(StringBuilder sb, Exception ex, int level = 0)
+ {
+ var indent = new string(' ', level * 2);
+ sb.AppendLine($"{indent}Typ: {ex.GetType().FullName}");
+ sb.AppendLine($"{indent}Message: {ex.Message}");
+ sb.AppendLine($"{indent}HResult: {ex.HResult}");
+ sb.AppendLine($"{indent}Source: {ex.Source}");
+
+ if (ex.Data.Count > 0)
+ {
+ sb.AppendLine($"{indent}Data:");
+ foreach (var key in ex.Data.Keys)
+ sb.AppendLine($"{indent} {key}: {ex.Data[key]}");
+ }
+
+ sb.AppendLine($"{indent}StackTrace:");
+ sb.AppendLine(ex.StackTrace);
+
+ if (ex.InnerException != null)
+ {
+ sb.AppendLine($"{indent}--- InnerException ---");
+ AppendExceptionDetails(sb, ex.InnerException, level + 1);
+ }
+ }
+ }
+
+ // Platzhalter — an deine bestehende Environment-Detection-Logik anbinden
+ internal static class AppEnvironmentInfo
+ {
+ public static string CurrentEnvironmentName { get; set; } = "Unknown";
+ }
+}
diff --git a/Shared/Core/MailUtils.cs b/Shared/Core/MailUtils.cs
index cb4857a77..de6aa6fd8 100644
--- a/Shared/Core/MailUtils.cs
+++ b/Shared/Core/MailUtils.cs
@@ -88,7 +88,6 @@ namespace BS.Shared.Core
message.ReplyToList.Add(new MailAddress(fromAddress, fromName));
}
-
message.BodyEncoding = Encoding.UTF8;
message.IsBodyHtml = false;
diff --git a/Shared/Shared.csproj b/Shared/Shared.csproj
index a47933167..eb714bc1e 100644
--- a/Shared/Shared.csproj
+++ b/Shared/Shared.csproj
@@ -119,6 +119,9 @@
+
+ ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll
+
3.0
@@ -146,6 +149,7 @@
+
diff --git a/Shared/packages.config b/Shared/packages.config
index 46471ce61..3de9df43e 100644
--- a/Shared/packages.config
+++ b/Shared/packages.config
@@ -1,4 +1,5 @@
+
\ No newline at end of file
diff --git a/TranslationUnitTest/app.config b/TranslationUnitTest/app.config
index 59e44e966..0a46500b7 100644
--- a/TranslationUnitTest/app.config
+++ b/TranslationUnitTest/app.config
@@ -8,43 +8,51 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
+
+
+
+
+
+
+
+
diff --git a/WohneinheitUnitTest/WohneinheitUnitTest.csproj b/WohneinheitUnitTest/WohneinheitUnitTest.csproj
index 100ce9518..4020ab601 100644
--- a/WohneinheitUnitTest/WohneinheitUnitTest.csproj
+++ b/WohneinheitUnitTest/WohneinheitUnitTest.csproj
@@ -62,6 +62,7 @@
+
diff --git a/WohneinheitUnitTest/app.config b/WohneinheitUnitTest/app.config
new file mode 100644
index 000000000..98f7d6cf3
--- /dev/null
+++ b/WohneinheitUnitTest/app.config
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file