+
Wochenübersicht
Monatsübersicht
+ Statistiken
Feiertage
Urlaubs-Maximizer
Einstellungen
@@ -16,21 +17,25 @@
-
-
- @context.User.Identity?.Name
-
-
- Abmelden
-
+ @* Username Display - Styled to match NavLink layout exactly *@
+
+
+ @context.User.Identity?.Name
+
-
-
+
+
+
+
+ Abmelden
+
+
+
Version 1.4
-
-
+
+
\ No newline at end of file
diff --git a/timetracker.Client/Components/Pages/AdminUsers.razor b/timetracker.Client/Components/Pages/AdminUsers.razor
index 5039fbc..71cf2fd 100644
--- a/timetracker.Client/Components/Pages/AdminUsers.razor
+++ b/timetracker.Client/Components/Pages/AdminUsers.razor
@@ -22,7 +22,7 @@ else
@* ── Header ── *@
+ Style="background: #991B1B; color:white;">
diff --git a/timetracker.Client/Components/Pages/Changelog.razor b/timetracker.Client/Components/Pages/Changelog.razor
index 1a206ea..49d4603 100644
--- a/timetracker.Client/Components/Pages/Changelog.razor
+++ b/timetracker.Client/Components/Pages/Changelog.razor
@@ -8,7 +8,7 @@
@* ── Header ── *@
+ Style="background: #1E293B; color:white;">
@@ -71,7 +71,8 @@
new("1.4", "08.06.2026", true,
[
new("Neu", "Timebot implementiert"),
- new("Upgrade", "Security hardening")
+ new("Upgrade", "Security hardening"),
+ new("Upgrade", "Changed coloring")
]),
new("1.3", "08.06.2026", false,
[
diff --git a/timetracker.Client/Components/Pages/Feiertage.razor b/timetracker.Client/Components/Pages/Feiertage.razor
index 7e2debc..7f41223 100644
--- a/timetracker.Client/Components/Pages/Feiertage.razor
+++ b/timetracker.Client/Components/Pages/Feiertage.razor
@@ -20,7 +20,7 @@ else
@* ── Header ── *@
+ Style="background: #0F766E; color: white;">
@@ -140,7 +140,7 @@ else
@* ── Zusammenfassung ── *@
+ Style="background: linear-gradient(90deg, rgba(15,118,110,0.08) 0%, transparent 100%); border-left: 4px solid #0F766E;">
diff --git a/timetracker.Client/Components/Pages/Home.razor b/timetracker.Client/Components/Pages/Home.razor
index 21e8dd4..8dee457 100644
--- a/timetracker.Client/Components/Pages/Home.razor
+++ b/timetracker.Client/Components/Pages/Home.razor
@@ -21,7 +21,7 @@ else
@* ── Wochen-Header ── *@
+ Style="background: #1E293B; color: white;">
@@ -95,7 +95,7 @@ else
@* ── Arbeitstag: vollständige Karte ── *@
-
+
@@ -270,7 +270,7 @@ else
@* ── Wochensumme ── *@
+ Style="background: #0F172A; color:white;">
Wochensumme
diff --git a/timetracker.Client/Components/Pages/Login.razor b/timetracker.Client/Components/Pages/Login.razor
index 617d1bc..93d765b 100644
--- a/timetracker.Client/Components/Pages/Login.razor
+++ b/timetracker.Client/Components/Pages/Login.razor
@@ -12,8 +12,8 @@
@* ── Logo / Header ── *@
- Timetracker
+ Style="font-size:4rem; color:#0EA5E9" />
+ Timetracker
diff --git a/timetracker.Client/Components/Pages/Month.razor b/timetracker.Client/Components/Pages/Month.razor
index 4c9628c..41afa6d 100644
--- a/timetracker.Client/Components/Pages/Month.razor
+++ b/timetracker.Client/Components/Pages/Month.razor
@@ -20,7 +20,7 @@ else
@* ── Monats-Header ── *@
+ Style="background: #1E293B; color: white;">
@@ -51,7 +51,7 @@ else
-
+
| Tag |
Start |
Ende |
@@ -95,7 +95,7 @@ else
@* ── Monatszusammenfassung ── *@
+ Style="background: linear-gradient(135deg, rgba(14,165,233,0.08) 0%, rgba(14,165,233,0.02) 100%); border-left: 6px solid #0EA5E9;">
Monatszusammenfassung
diff --git a/timetracker.Client/Components/Pages/Settings.razor b/timetracker.Client/Components/Pages/Settings.razor
index 716e1f4..2da5397 100644
--- a/timetracker.Client/Components/Pages/Settings.razor
+++ b/timetracker.Client/Components/Pages/Settings.razor
@@ -20,7 +20,7 @@ else
@* ── Header ── *@
+ Style="background: #1E293B; color:white;">
diff --git a/timetracker.Client/Components/Pages/Stats.razor b/timetracker.Client/Components/Pages/Stats.razor
new file mode 100644
index 0000000..3650eef
--- /dev/null
+++ b/timetracker.Client/Components/Pages/Stats.razor
@@ -0,0 +1,382 @@
+@page "/stats"
+@rendermode InteractiveWebAssembly
+@attribute [Authorize]
+@inject ITimetrackerService TrackerService
+@inject AuthenticationStateProvider AuthStateProvider
+@using System.Security.Claims
+@using timetracker.Shared
+
+Statistiken – Timetracker
+
+@if (_loading)
+{
+
+
+ Lade Statistiken…
+
+}
+else
+{
+
+
+ @* ── Header ── *@
+
+
+
+
+ Statistiken
+ Auswertung deiner Arbeitsleistung und Gleitzeitkonto
+
+
+
+
+
+ @* ── Card 1: Wochen-Fortschritt ── *@
+
+
+
+
+ Wochenfortschritt
+
+ @{
+ var pct = _weekTargetHours > 0 ? (int)Math.Min((_weekWorkedHours / _weekTargetHours) * 100, 100) : 0;
+ var displayPct = _weekTargetHours > 0 ? (int)Math.Round((_weekWorkedHours / _weekTargetHours) * 100) : 0;
+ }
+
+
+
+
+ @displayPct%
+ Erreicht
+
+
+
+
+
+ Arbeitszeit (Ist):
+ @FormatHours(_weekWorkedHours) Std.
+
+
+ Wochensoll (Soll):
+ @FormatHours(_weekTargetHours) Std.
+
+
+
+
+
+
+
+ @* ── Card 2: Wochensaldo ── *@
+
+
+
+
+ Wochensaldo
+
+
+ = 0 ? "#10B981" : "#EF4444")}; font-size:3rem")" />
+
+ = 0 ? "#10B981" : "#EF4444")};")">
+ @FormatTs(TimeSpan.FromHours(_weekOvertimeHours), sign: true)
+
+ Überstunden-Veränderung diese Woche
+
+
+
+
+
+
+
+ @* ── Card 3: Gesamtsaldo ── *@
+
+ = TimeSpan.Zero ? "#10B981" : "#EF4444")};")">
+
+
+ Gleitzeitkonto
+
+
+ = TimeSpan.Zero ? "#10B981" : "#EF4444")}; font-size:3rem")" />
+
+ = TimeSpan.Zero ? "#10B981" : "#EF4444")};")">
+ @FormatTs(_totalOvertime, sign: true)
+
+ Gesamtsaldo aller erfassten Tage
+
+
+
+
+
+
+
+ @* ── Säulendiagramm (Tagesverteilung) ── *@
+
+
+
+
+ Arbeitszeitverteilung diese Woche
+ Geleistete Nettoarbeitsstunden pro Wochentag
+
+
+
+
+
+
+
+
+
+
+ @* ── Diagnostics Panel ── *@
+
+
+
+ Benutzer-ID: @_userId
+ Wochensoll: @_weekTargetHours Std. | Netto-Arbeitszeit: @_weekWorkedHours Std. | Wochensaldo: @_weekOvertimeHours Std.
+ Anzahl erfasste Tage aus DB: @_rawDbDaysCount
+
+
+
+
+ | Datum |
+ Tag |
+ Ist (Std) |
+ Soll (Std) |
+ Ist > 0 |
+ Arbeitstag (Einst.) |
+ Aus DB zugeordnet |
+
+
+
+ @foreach (var d in _days)
+ {
+
+ | @d.Date.ToString("yyyy-MM-dd") |
+ @d.DayName |
+ @d.WorkedHours |
+ @d.TargetHours |
+ @(d.WorkedHours > 0 ? "Ja" : "Nein") |
+ @(d.IsWorkDay ? "Ja" : "Nein") |
+ @(_dbMatchStatus.GetValueOrDefault(d.Date, "Nein")) |
+
+ }
+
+
+
+
+
+
+
+}
+
+@code {
+ private bool _loading = true;
+ private int _userId;
+ private AppSettings _settings = new();
+ private List _days = [];
+ private TimeSpan _totalOvertime;
+ private double _weekWorkedHours;
+ private double _weekTargetHours;
+ private double _weekOvertimeHours;
+ private double _maxHoursValue = 10.0;
+
+ // Debug variables
+ private int _rawDbDaysCount = 0;
+ private Dictionary _dbMatchStatus = new();
+
+ private static readonly System.Globalization.CultureInfo _deCulture = new("de-DE");
+
+ protected override async Task OnInitializedAsync()
+ {
+ var authState = await AuthStateProvider.GetAuthenticationStateAsync();
+ var claim = authState.User.FindFirst(ClaimTypes.NameIdentifier);
+ if (claim == null) return;
+ _userId = int.Parse(claim.Value);
+
+ _settings = await TrackerService.GetSettingsAsync(_userId);
+
+ var today = DateOnly.FromDateTime(DateTime.Today);
+ var monday = GetMonday(today);
+
+ var dbDays = await TrackerService.GetWeekAsync(_userId, monday);
+ _rawDbDaysCount = dbDays.Count;
+ _totalOvertime = await TrackerService.GetTotalOvertimeAsync(_userId, _settings);
+
+ _weekTargetHours = 0;
+ _weekWorkedHours = 0;
+ _dbMatchStatus.Clear();
+
+ _days = Enumerable.Range(0, 7).Select(i =>
+ {
+ var date = monday.AddDays(i);
+ bool isWorkDay = _settings.IsWorkDay(date.DayOfWeek);
+ double target = isWorkDay ? _settings.DailyTargetHours : 0.0;
+
+ if (isWorkDay)
+ {
+ _weekTargetHours += _settings.DailyTargetHours;
+ }
+
+ var wd = dbDays.FirstOrDefault(d => d.Date == date);
+ double worked = 0.0;
+
+ if (wd != null)
+ {
+ _dbMatchStatus[date] = $"Ja (Id={wd.Id}, Start={wd.StartTime}, Ende={wd.EndTime})";
+
+ if (wd.StartTime != null && wd.EndTime != null)
+ {
+ var gross = wd.EndTime.Value.ToTimeSpan() - wd.StartTime.Value.ToTimeSpan();
+ if (gross > TimeSpan.Zero)
+ {
+ var breakTotal = wd.Breaks
+ .Where(b => b.StartTime.HasValue && b.EndTime.HasValue && b.EndTime > b.StartTime)
+ .Aggregate(TimeSpan.Zero, (s, b) =>
+ s + (b.EndTime!.Value.ToTimeSpan() - b.StartTime!.Value.ToTimeSpan()));
+ worked = (gross - breakTotal).TotalHours;
+ if (worked < 0) worked = 0.0;
+ }
+ }
+ }
+ else
+ {
+ _dbMatchStatus[date] = "Nein";
+ }
+
+ _weekWorkedHours += worked;
+
+ return new DayStat
+ {
+ Date = date,
+ DayName = date.ToString("dddd", _deCulture),
+ DayShortName = date.ToString("ddd", _deCulture),
+ WorkedHours = worked,
+ TargetHours = target,
+ IsToday = date == today,
+ IsWorkDay = isWorkDay
+ };
+ }).ToList();
+
+ _weekOvertimeHours = _weekWorkedHours - _weekTargetHours;
+
+ var maxWorked = _days.Max(d => d.WorkedHours);
+ _maxHoursValue = Math.Max(10.0, Math.Max(maxWorked, _settings.DailyTargetHours));
+
+ _loading = false;
+ }
+
+ private static DateOnly GetMonday(DateOnly date)
+ {
+ int diff = ((int)date.DayOfWeek - (int)DayOfWeek.Monday + 7) % 7;
+ return date.AddDays(-diff);
+ }
+
+ private string FormatHours(double hours)
+ {
+ var ts = TimeSpan.FromHours(hours);
+ return $"{(int)ts.TotalHours}:{ts.Minutes:D2}";
+ }
+
+ private string FormatTs(TimeSpan ts, bool sign = false)
+ {
+ if (ts == TimeSpan.Zero && sign) return "±0:00";
+ var prefix = sign ? (ts >= TimeSpan.Zero ? "+" : "−") : (ts < TimeSpan.Zero ? "−" : "");
+ var abs = ts.Duration();
+ return $"{prefix}{(int)abs.TotalHours}:{abs.Minutes:D2}";
+ }
+
+ private sealed class DayStat
+ {
+ public DateOnly Date { get; set; }
+ public string DayName { get; set; } = "";
+ public string DayShortName { get; set; } = "";
+ public double WorkedHours { get; set; }
+ public double TargetHours { get; set; }
+ public bool IsToday { get; set; }
+ public bool IsWorkDay { get; set; }
+ }
+}
diff --git a/timetracker.Client/Components/Pages/UrlaubsMaximizer.razor b/timetracker.Client/Components/Pages/UrlaubsMaximizer.razor
index e6e095b..879743b 100644
--- a/timetracker.Client/Components/Pages/UrlaubsMaximizer.razor
+++ b/timetracker.Client/Components/Pages/UrlaubsMaximizer.razor
@@ -21,7 +21,7 @@ else
@* ── Header ── *@
+ Style="background: #C2410C; color:white;">