fix: update migration seeding condition to check for PrimaryColor instead of TenantColor
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user