fix: update migration seeding condition to check for PrimaryColor instead of TenantColor

This commit is contained in:
MarcWieland
2026-06-26 23:50:03 +02:00
parent e2e57143b8
commit f47b4d86ba
9 changed files with 1090 additions and 2 deletions
@@ -199,4 +199,144 @@ public class TimetrackerService(IDbContextFactory<TimetrackerDbContext> factory)
.OrderBy(w => w.Date)
.ToListAsync();
}
// ── Dashboard ─────────────────────────────────────────────────────────
public async Task<DashboardStatsVm> GetDashboardStatsAsync(int userId, int year, int month)
{
await using var db = await factory.CreateDbContextAsync();
var startDate = new DateOnly(year, month, 1);
var endDate = startDate.AddMonths(1).AddDays(-1);
var workDays = await db.WorkDays
.Include(w => w.Breaks)
.Where(w => w.UserId == userId && w.Date >= startDate && w.Date <= endDate)
.ToListAsync();
var stats = new DashboardStatsVm();
if (workDays.Any(w => w.StartTime.HasValue))
{
var earliest = workDays.Where(w => w.StartTime.HasValue).Min(w => w.StartTime!.Value);
stats.EarliestStart = earliest.ToTimeSpan();
}
if (workDays.Any(w => w.EndTime.HasValue))
{
var latest = workDays.Where(w => w.EndTime.HasValue).Max(w => w.EndTime!.Value);
stats.LatestEnd = latest.ToTimeSpan();
}
var daysWithBreaks = workDays.Where(w => w.Breaks.Any(b => b.StartTime.HasValue && b.EndTime.HasValue)).ToList();
if (daysWithBreaks.Any())
{
var totalBreakTicks = daysWithBreaks.Sum(w =>
w.Breaks.Where(b => b.StartTime.HasValue && b.EndTime.HasValue)
.Sum(b => (b.EndTime!.Value - b.StartTime!.Value).Ticks));
stats.AverageBreak = TimeSpan.FromTicks(totalBreakTicks / daysWithBreaks.Count);
}
var allUserDays = await db.WorkDays
.Where(w => w.UserId == userId && w.StartTime.HasValue && w.EndTime.HasValue)
.OrderBy(w => w.Date)
.Select(w => w.Date)
.ToListAsync();
if (allUserDays.Any())
{
int currentStreak = 1;
int maxStreak = 1;
int tempStreak = 1;
for (int i = 1; i < allUserDays.Count; i++)
{
if (allUserDays[i].DayNumber == allUserDays[i - 1].DayNumber + 1)
{
tempStreak++;
}
else if (allUserDays[i].DayNumber > allUserDays[i - 1].DayNumber + 1)
{
if (tempStreak > maxStreak) maxStreak = tempStreak;
tempStreak = 1;
}
}
if (tempStreak > maxStreak) maxStreak = tempStreak;
stats.LongestStreak = maxStreak;
var today = DateOnly.FromDateTime(DateTime.Today);
var yesterday = today.AddDays(-1);
if (allUserDays.Last() == today || allUserDays.Last() == yesterday)
{
stats.CurrentStreak = tempStreak;
}
else
{
stats.CurrentStreak = 0;
}
}
return stats;
}
public async Task<DashboardChartDataVm> GetDashboardChartDataAsync(int userId, int year)
{
await using var db = await factory.CreateDbContextAsync();
var chartData = new DashboardChartDataVm();
var settings = await GetSettingsAsync(userId);
var workDaysThisYear = await db.WorkDays
.Include(w => w.Breaks)
.Where(w => w.UserId == userId && w.Date.Year == year)
.ToListAsync();
var vacationsThisYear = await db.VacationDays
.Where(v => v.UserId == userId && v.Date.Year == year)
.ToListAsync();
var holidaysThisYear = await db.PublicHolidays
.Where(h => h.Date.Year == year && h.Counties.Contains(settings.GermanState))
.ToListAsync();
chartData.WorkDaysCount = workDaysThisYear.Count(w => w.StartTime.HasValue);
chartData.VacationDaysCount = vacationsThisYear.Count;
chartData.PublicHolidaysCount = holidaysThisYear.Count(h => settings.IsWorkDay(h.Date.DayOfWeek));
for (int month = 1; month <= 12; month++)
{
var daysInMonth = workDaysThisYear.Where(w => w.Date.Month == month).ToList();
if (!daysInMonth.Any(d => d.StartTime.HasValue)) continue;
double monthOvertime = 0;
foreach (var day in daysInMonth)
{
if (!day.StartTime.HasValue || !day.EndTime.HasValue) continue;
var gross = day.EndTime.Value - day.StartTime.Value;
var breakTicks = day.Breaks
.Where(b => b.StartTime.HasValue && b.EndTime.HasValue)
.Sum(b => (b.EndTime!.Value - b.StartTime!.Value).Ticks);
var enteredBreak = TimeSpan.FromTicks(breakTicks);
int minMinutes = gross.TotalHours >= 9.0 ? 45 : 30;
int finalMin = Math.Max(minMinutes, settings.MinimumBreakMinutes);
var minBreak = TimeSpan.FromMinutes(finalMin);
var effectiveBreak = enteredBreak > minBreak ? enteredBreak : minBreak;
var net = gross - effectiveBreak;
var target = TimeSpan.FromHours(settings.DailyTargetHours);
var isHoliday = holidaysThisYear.Any(h => h.Date == day.Date);
var isVacation = vacationsThisYear.Any(v => v.Date == day.Date);
var isWorkDay = settings.IsWorkDay(day.Date.DayOfWeek);
if (!isWorkDay || isHoliday || isVacation) target = TimeSpan.Zero;
monthOvertime += (net - target).TotalHours;
}
var monthName = new DateTime(year, month, 1).ToString("MMM", new System.Globalization.CultureInfo("de-DE"));
chartData.MonthlyOvertime.Add(new MonthlyOvertimeVm { MonthName = monthName, OvertimeHours = monthOvertime });
}
return chartData;
}
}