Refactor AppSettingsDocument and CoeffSnapshotDocument: Remove LunchBreakHours property
Add CalendarEventDocument and CalendarEventType enum for event management Update WorkDayDocument to include WorkUnitDocument and CalendarEventDocument lists Enhance CouchbaseLiteWorkDayService with methods for managing WorkUnit and CalendarEvent Revise MonthlySummaryModel to track preview worked hours and counted work units Improve CSS for calendar view, including responsive design and new item styles
This commit is contained in:
parent
08e573d63c
commit
cab549ab3a
22 changed files with 1725 additions and 356 deletions
|
|
@ -29,12 +29,10 @@ else
|
|||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Day</th>
|
||||
<th>Type</th>
|
||||
<th>Start</th>
|
||||
<th>Projected</th>
|
||||
<th>Actual</th>
|
||||
<th class="text-end">Worked</th>
|
||||
<th class="text-end">Extra</th>
|
||||
<th>Work Units</th>
|
||||
<th>Calendar Events</th>
|
||||
<th class="text-end">Counted</th>
|
||||
<th class="text-end">Preview</th>
|
||||
<th class="text-end">Off</th>
|
||||
<th class="text-end">Gross €</th>
|
||||
<th class="text-end">Net €</th>
|
||||
|
|
@ -47,24 +45,42 @@ else
|
|||
<tr class="@GetRowClass(row)">
|
||||
<td>@row.Date.ToString("dd")</td>
|
||||
<td>@row.Date.ToString("ddd")</td>
|
||||
@if (row.Entry is not null)
|
||||
{
|
||||
<td>@row.Entry.DayType</td>
|
||||
<td>@(row.Entry.StartTime?.ToString("HH:mm") ?? "")</td>
|
||||
<td>@(row.Entry.ProjectedExitTime?.ToString("HH:mm") ?? "")</td>
|
||||
<td>@(row.Entry.ActualExitTime?.ToString("HH:mm") ?? "")</td>
|
||||
<td class="text-end">@row.Entry.WorkedHoursFinal.ToString("N2")</td>
|
||||
<td class="text-end">@FormatDelta(row.Entry.ExtraHoursDelta)</td>
|
||||
<td class="text-end">@row.Entry.HoursOff.ToString("N2")</td>
|
||||
<td class="text-end">@row.Entry.GrossIncome.ToString("N2")</td>
|
||||
<td class="text-end">@row.Entry.NetIncome.ToString("N2")</td>
|
||||
}
|
||||
else
|
||||
{
|
||||
<td colspan="9" class="text-muted">—</td>
|
||||
}
|
||||
<td>
|
||||
<a href="workday/@row.Date.ToString("yyyy-MM-dd")" class="btn btn-sm btn-outline-primary">Edit</a>
|
||||
@if (row.Entry?.WorkUnits.Count > 0)
|
||||
{
|
||||
@foreach (var unit in row.Entry.WorkUnits)
|
||||
{
|
||||
<div class="small mb-1">@unit.Label: @FormatTimeRange(unit.StartTime, unit.EndTime) (@FormatHours(unit.ManualWorkedHours)@(unit.IsPreview ? ", preview" : ""))</div>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="text-muted">—</span>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (row.Entry?.CalendarEvents.Count > 0)
|
||||
{
|
||||
@foreach (var calendarEvent in row.Entry.CalendarEvents)
|
||||
{
|
||||
<div class="small mb-1">@calendarEvent.EventType: @calendarEvent.Description</div>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="text-muted">—</span>
|
||||
}
|
||||
</td>
|
||||
<td class="text-end">@FormatHours(GetCountedHours(row))</td>
|
||||
<td class="text-end">@FormatHours(GetPreviewHours(row))</td>
|
||||
<td class="text-end">@FormatHours(GetHoursOff(row))</td>
|
||||
<td class="text-end">@GetGrossIncome(row).ToString("N2")</td>
|
||||
<td class="text-end">@GetNetIncome(row).ToString("N2")</td>
|
||||
<td>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="work-unit/@row.Date.ToString("yyyy-MM-dd")" class="btn btn-sm btn-outline-primary">Unit</a>
|
||||
<a href="calendar-event/@row.Date.ToString("yyyy-MM-dd")" class="btn btn-sm btn-outline-secondary">Event</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
|
|
@ -136,23 +152,93 @@ else
|
|||
{
|
||||
if (row.IsWeekend || row.IsFestivity) return "table-danger";
|
||||
if (row.Entry is null) return "";
|
||||
return row.Entry.DayType switch
|
||||
if (row.Entry.CalendarEvents.Any(entry => entry.EventType == CalendarEventType.Holiday))
|
||||
{
|
||||
DayType.Closure => "table-warning",
|
||||
DayType.Illness => "table-info",
|
||||
DayType.DayOff => "table-secondary",
|
||||
DayType.Holiday => "table-success",
|
||||
DayType.Home => "table-light",
|
||||
_ => ""
|
||||
};
|
||||
return "table-success";
|
||||
}
|
||||
|
||||
if (row.Entry.CalendarEvents.Any(entry => entry.EventType == CalendarEventType.Closure))
|
||||
{
|
||||
return "table-warning";
|
||||
}
|
||||
|
||||
if (row.Entry.CalendarEvents.Any(entry => entry.EventType == CalendarEventType.Illness))
|
||||
{
|
||||
return "table-info";
|
||||
}
|
||||
|
||||
if (row.Entry.CalendarEvents.Any(entry => entry.EventType == CalendarEventType.DayOff))
|
||||
{
|
||||
return "table-secondary";
|
||||
}
|
||||
|
||||
if (row.Entry.WorkUnits.Any(entry => entry.Location == WorkUnitLocation.Home))
|
||||
{
|
||||
return "table-light";
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private static string FormatDelta(decimal d) => d switch
|
||||
private static decimal GetCountedHours(CalendarDayRow row)
|
||||
{
|
||||
> 0 => $"+{d:N2}",
|
||||
< 0 => d.ToString("N2"),
|
||||
_ => "—"
|
||||
};
|
||||
return row.Entry?.WorkUnits.Where(unit => !unit.IsPreview).Sum(unit => unit.ManualWorkedHours) ?? 0m;
|
||||
}
|
||||
|
||||
private static decimal GetPreviewHours(CalendarDayRow row)
|
||||
{
|
||||
return row.Entry?.WorkUnits.Where(unit => unit.IsPreview).Sum(unit => unit.ManualWorkedHours) ?? 0m;
|
||||
}
|
||||
|
||||
private static decimal GetHoursOff(CalendarDayRow row)
|
||||
{
|
||||
if (row.Entry is null || row.Entry.WorkUnits.Count == 0)
|
||||
{
|
||||
return 0m;
|
||||
}
|
||||
|
||||
var countedUnits = row.Entry.WorkUnits.Where(unit => !unit.IsPreview).ToList();
|
||||
if (countedUnits.Count == 0)
|
||||
{
|
||||
return 0m;
|
||||
}
|
||||
|
||||
var standardHours = countedUnits[0].CoeffSnapshot.StandardWorkHoursPerDay;
|
||||
return Math.Max(0m, standardHours - countedUnits.Sum(unit => unit.ManualWorkedHours));
|
||||
}
|
||||
|
||||
private static decimal GetGrossIncome(CalendarDayRow row)
|
||||
{
|
||||
return row.Entry?.WorkUnits.Where(unit => !unit.IsPreview).Sum(unit => unit.GrossIncome) ?? 0m;
|
||||
}
|
||||
|
||||
private static decimal GetNetIncome(CalendarDayRow row)
|
||||
{
|
||||
return row.Entry?.WorkUnits.Where(unit => !unit.IsPreview).Sum(unit => unit.NetIncome) ?? 0m;
|
||||
}
|
||||
|
||||
private static string FormatTimeRange(TimeOnly? startTime, TimeOnly? endTime)
|
||||
{
|
||||
if (startTime.HasValue && endTime.HasValue)
|
||||
{
|
||||
return $"{startTime:HH:mm}-{endTime:HH:mm}";
|
||||
}
|
||||
|
||||
if (startTime.HasValue)
|
||||
{
|
||||
return startTime.Value.ToString("HH:mm");
|
||||
}
|
||||
|
||||
return "No time range";
|
||||
}
|
||||
|
||||
private static string FormatHours(decimal value)
|
||||
{
|
||||
var totalMinutes = (int)Math.Round(value * 60m, MidpointRounding.AwayFromZero);
|
||||
var hours = totalMinutes / 60;
|
||||
var minutes = totalMinutes % 60;
|
||||
return $"{hours:00}:{minutes:00}";
|
||||
}
|
||||
|
||||
private sealed class CalendarDayRow
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue