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