diff --git a/WorkManagementTool/Components/Calendar/Calendar.razor b/WorkManagementTool/Components/Calendar/Calendar.razor new file mode 100644 index 0000000..3ebe590 --- /dev/null +++ b/WorkManagementTool/Components/Calendar/Calendar.razor @@ -0,0 +1,315 @@ +@page "/calendar" + +@using Microsoft.AspNetCore.Authorization +@using WorkManagementTool.Services +@using WorkManagementTool.Data.Entities + +@attribute [Authorize] + +@inject SchoolSubjectService SchoolSubjectService +@inject HomeworkService HomeworkService + +

Calendar

+ +
+ + @currentMonth.ToString("MMMM yyyy") + + +
+ + +
+
+ +@if (Subjects == null || Subjects.Count == 0) +{ +

Chargement des matières...

+} +else +{ +
+ @foreach (var s in Subjects) + { +
+ + @s.Name +
+ } +
+ +
+ @foreach (var d in WeekDayHeaders) + { +
@d
+ } + + @foreach (var day in DaysForCurrentMonth) + { + var isOtherMonth = day.Month != currentMonth.Month; + var hwForDay = GetHomeworksForDate(day); +
+
@day.Day
+ @if (hwForDay != null && hwForDay.Count > 0) + { +
+ @foreach (var hw in hwForDay) + { + var color = GetSubjectColor(hw.SchoolSubject); +
+ @Shorten(hw.Title, 28) +
+ } +
+ } +
+ } +
+} + + + +@code { + [Parameter] + public List Subjects { get; set; } = new(); + + private DateTime currentMonth = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1); + private bool showCompleted { get; set; } = false; + + // mapping subjectId -> color + private readonly Dictionary subjectColors = new(); + + // mapping date -> list of homeworks + private readonly Dictionary> homeworksByDate = new(); + + private static readonly string[] WeekDayHeaders = new[] { "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam", "Dim" }; + + private IEnumerable DaysForCurrentMonth + { + get + { + var first = StartOfCalendar(currentMonth); + var last = EndOfCalendar(currentMonth); + for (var d = first; d <= last; d = d.AddDays(1)) + yield return d; + } + } + + protected override async Task OnInitializedAsync() + { + await LoadDataAsync(); + } + + private async Task LoadDataAsync() + { + // Load subjects if not provided by parent + if (Subjects == null || Subjects.Count == 0) + { + Subjects = await SchoolSubjectService.GetAllSchoolSubjectsAsync(); + } + + // Assign colors deterministically + foreach (var s in Subjects) + { + if (!subjectColors.ContainsKey(s.Id)) + subjectColors[s.Id] = GenerateColorFromId(s.Id); + } + + // Clear and load homeworks grouped by date + homeworksByDate.Clear(); + + foreach (var s in Subjects) + { + // services used elsewhere: GetUncompletedHomeworkAsync and GetCompletedHomeworksAsync + var list = new List(); + try + { + var uncompleted = await HomeworkService.GetUncompletedHomeworkAsync(s.Id); + if (uncompleted != null) + list.AddRange(uncompleted); + + if (showCompleted) + { + var completed = await HomeworkService.GetCompletedHomeworksAsync(s.Id); + if (completed != null) + list.AddRange(completed); + } + } + catch + { + // ignore per-subject errors to keep calendar robust + } + + foreach (var hw in list) + { + var date = hw.DueDate.Date; + if (!homeworksByDate.ContainsKey(date)) + homeworksByDate[date] = new List(); + homeworksByDate[date].Add(hw); + } + } + + StateHasChanged(); + } + + private List? GetHomeworksForDate(DateTime date) + { + return homeworksByDate.TryGetValue(date.Date, out var list) ? list : null; + } + + private string GetSubjectColor(SchoolSubject? subject) + { + if (subject is null) return "#6c757d"; // fallback + if (subjectColors.TryGetValue(subject.Id, out var c)) return c; + var gen = GenerateColorFromId(subject.Id); + subjectColors[subject.Id] = gen; + return gen; + } + + private static string GenerateColorFromId(int id) + { + // deterministic pastel-ish color from id using HSL -> RGB + var hue = (id * 47) % 360; // disperse hues + var s = 65; // saturation % + var l = 50; // lightness % + var (r, g, b) = HslToRgb(hue, s, l); + return $"rgb({r},{g},{b})"; + } + + private static (int r, int g, int b) HslToRgb(int h, int s, int l) + { + double hh = h / 360.0; + double ss = s / 100.0; + double ll = l / 100.0; + + double q = ll < 0.5 ? ll * (1 + ss) : ll + ss - ll * ss; + double p = 2 * ll - q; + + double[] t = new double[3] { hh + 1.0 / 3.0, hh, hh - 1.0 / 3.0 }; + for (int i = 0; i < 3; i++) + { + if (t[i] < 0) t[i] += 1; + if (t[i] > 1) t[i] -= 1; + } + + double[] c = new double[3]; + for (int i = 0; i < 3; i++) + { + if (t[i] < 1.0 / 6.0) c[i] = p + ((q - p) * 6 * t[i]); + else if (t[i] < 1.0 / 2.0) c[i] = q; + else if (t[i] < 2.0 / 3.0) c[i] = p + ((q - p) * (2.0 / 3.0 - t[i]) * 6); + else c[i] = p; + } + + return ((int)(c[0] * 255), (int)(c[1] * 255), (int)(c[2] * 255)); + } + + private static DateTime StartOfCalendar(DateTime month) + { + // Start on Monday + var firstOfMonth = new DateTime(month.Year, month.Month, 1); + var diff = ((int)firstOfMonth.DayOfWeek + 6) % 7; // convert Sun=0.. to Mon=0.. + return firstOfMonth.AddDays(-diff); + } + + private static DateTime EndOfCalendar(DateTime month) + { + var last = new DateTime(month.Year, month.Month, DateTime.DaysInMonth(month.Year, month.Month)); + var diff = (7 - (((int)last.DayOfWeek + 6) % 7) - 1); + return last.AddDays(diff); + } + + private void ChangeMonth(int offset) + { + currentMonth = currentMonth.AddMonths(offset); + StateHasChanged(); + } + + private static string Shorten(string? text, int max) + { + if (string.IsNullOrEmpty(text)) return string.Empty; + if (text.Length <= max) return text; + return text.Substring(0, max - 1) + "…"; + } +} \ No newline at end of file diff --git a/WorkManagementTool/Components/SchoolSubject/SchoolSubjectCreation.razor b/WorkManagementTool/Components/SchoolSubject/SchoolSubjectCreation.razor index 78f67c0..cdef475 100644 --- a/WorkManagementTool/Components/SchoolSubject/SchoolSubjectCreation.razor +++ b/WorkManagementTool/Components/SchoolSubject/SchoolSubjectCreation.razor @@ -1,48 +1,53 @@ @page "/school-subjects/creation" +@rendermode InteractiveServer + @attribute [Authorize] @using Microsoft.AspNetCore.Authorization @using WorkManagementTool.Services @using WorkManagementTool.Data.Entities; @inject SchoolSubjectService SchoolSubjectService @inject NavigationManager NavigationManager +@inject ILogger Logger

SchoolSubjectCreation

- +@if (!string.IsNullOrEmpty(errorMessage)) +{ +
@errorMessage
+} + +
- + +
- + +
@code { - [SupplyParameterFromForm] - private SchoolSubject? Model { get; set; } + private SchoolSubject Model { get; set; } = new(); - protected override void OnInitialized() - { - if (Model is null) - { - Model = new SchoolSubject - { - Name = null!, - Description = null, - }; - } - } + private string? errorMessage; private async Task Submit() { - await SchoolSubjectService.AddSchoolSubjectAsync(Model!); - NavigationManager.NavigateTo("/school-subjects"); + if (Model is not null) + { + await SchoolSubjectService.AddSchoolSubjectAsync(Model); + Logger.LogInformation("SchoolSubject created: {Name}", Model.Name); + NavigationManager.NavigateTo("/school-subjects"); + } } -} +} \ No newline at end of file diff --git a/WorkManagementTool/Data/Entities/SchoolSubject.cs b/WorkManagementTool/Data/Entities/SchoolSubject.cs index 2353464..ef53e8b 100644 --- a/WorkManagementTool/Data/Entities/SchoolSubject.cs +++ b/WorkManagementTool/Data/Entities/SchoolSubject.cs @@ -5,9 +5,10 @@ namespace WorkManagementTool.Data.Entities public class SchoolSubject { public int Id { get; set; } - [Required] - [MaxLength(100, ErrorMessage = "Name must be less than 100 caracters")] - public required string Name { get; set; } = null!; + //[Required] + //[MaxLength(100, ErrorMessage = "Name must be less than 100 caracters")] + //[MinLength(1, ErrorMessage = "Name must containes more than 1 caracters")] + public string Name { get; set; } = null!; [MaxLength(500, ErrorMessage = "Description must be less than 500 caracters")] public string? Description { get; set; } public DateTime CreatedAt { get; set; } = DateTime.UtcNow; diff --git a/WorkManagementTool/work_management.db b/WorkManagementTool/work_management.db index 2d14ccd..b4c2b83 100644 Binary files a/WorkManagementTool/work_management.db and b/WorkManagementTool/work_management.db differ diff --git a/WorkManagementTool/work_management.db-shm b/WorkManagementTool/work_management.db-shm index 021e342..94fe69d 100644 Binary files a/WorkManagementTool/work_management.db-shm and b/WorkManagementTool/work_management.db-shm differ diff --git a/WorkManagementTool/work_management.db-wal b/WorkManagementTool/work_management.db-wal index 14a41c9..b138805 100644 Binary files a/WorkManagementTool/work_management.db-wal and b/WorkManagementTool/work_management.db-wal differ