@page "/dashboard/employee" @using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Components.Authorization @rendermode @(new InteractiveServerRenderMode(prerender: false)) @attribute [Authorize] @inject ApiClientService ApiClient @inject NavigationManager Navigation @inject IJSRuntime JS @inject AuthenticationStateProvider AuthStateProvider @AppLocalization.Text("dashboard.employeeTitle", "Employee Dashboard")

@AppLocalization.Text("dashboard.teamMember", "Team Member")

@AppLocalization.Text("dashboard.myWork", "My Work")

@AppLocalization.Text("dashboard.employeeIntro", "Tasks, deadlines, and your progress in one place.")

@if (isLoading) {
@AppLocalization.Text("dashboard.loadingDashboard", "Loading dashboard...")
} else if (!string.IsNullOrWhiteSpace(errorMessage)) {
@errorMessage
} else {

@AppLocalization.Text("status.inprogress", "In Progress")

@AppLocalization.Text("dashboard.tasksInProgressDesc", "Tasks currently being worked on")

@AppLocalization.Number(InProgressTasks.Count)
@if (InProgressTasks.Any()) { @foreach (var task in InProgressTasks) {
@Content.GetTaskTitle(task)
@GetProjectName(task.ProjectId)
@DisplayFormats.Date(task.DueDate)
@GetDueLabel(task)
} } else {
@AppLocalization.Text("dashboard.noTasksInProgress", "No tasks in progress right now.")
}

@AppLocalization.Text("dashboard.todoUpcoming", "To Do & Upcoming")

@AppLocalization.Text("dashboard.todoUpcomingDesc", "Tasks waiting for action or coming up soon")

@AppLocalization.Number(TodoUpcomingTasks.Count)
@if (TodoUpcomingTasks.Any()) { @foreach (var task in TodoUpcomingTasks) {
@Content.GetTaskTitle(task)
@GetProjectName(task.ProjectId)
@DisplayFormats.Date(task.DueDate)
@GetDueLabel(task)
} } else {
@AppLocalization.Text("dashboard.noUpcomingTasks", "No upcoming tasks found.")
}

@AppLocalization.Text("status.overdue", "Overdue")

@AppLocalization.Text("dashboard.overdueDesc", "Tasks past their due date")

@AppLocalization.Number(OverdueTasks.Count)
@if (OverdueTasks.Any()) { @foreach (var task in OverdueTasks) {
@Content.GetTaskTitle(task)
@GetProjectName(task.ProjectId)
@DisplayFormats.Date(task.DueDate)
@DaysOverdue(task)
} } else {
@AppLocalization.Text("dashboard.noOverdueTasks", "No overdue tasks. Nice work.")
}

@CalendarMonthLabel

@CalendarTodayLabel

@foreach (var day in WeekdayLabels) { @day }
@foreach (var cell in CalendarCells) { }

@AppLocalization.Text("dashboard.relatedTasks", "Related Tasks")

@AppLocalization.Text("dashboard.tasksMarkedOn", "Tasks marked on {date}").Replace("{date}", SelectedDateLabel)

@if (SelectedDateTasks.Any()) { @foreach (var task in SelectedDateTasks) {
@Content.GetTaskTitle(task)
@GetProjectName(task.ProjectId)
@DisplayFormats.Date(task.DueDate)
@GetDueLabel(task)
} } else {
@AppLocalization.Text("dashboard.noTasksForDay", "No tasks are scheduled for this day.")
}
}
@code { private bool isLoading = true; private string? errorMessage; private long currentUserId; private List tasks = new(); private List projects = new(); private DateTime selectedDate = DateTime.Today; private string[] WeekdayLabels => AppLocalization.IsBurmeseCulture ? ["တနင်္ဂနွေ", "တနင်္လာ", "အင်္ဂါ", "ဗုဒ္ဓဟူး", "ကြာသပတေး", "သောကြာ", "စနေ"] : ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; private List CalendarCells => BuildCalendarCells(); private List SelectedDateTasks => tasks .Where(task => task.DueDate.Date == selectedDate.Date) .OrderBy(task => task.StatusId) .ThenBy(task => task.DueDate) .ToList(); private string SelectedDateLabel => GetDisplayDateLabel(selectedDate); private string CalendarMonthLabel => GetMonthYearLabel(selectedDate); private string CalendarTodayLabel => GetDisplayDateLabel(DateTime.Today); private string CurrentTimeLabel => DateTime.Now.ToString("h:mm tt"); private string TimeZoneLabel { get { var offset = TimeZoneInfo.Local.GetUtcOffset(DateTime.Now); var sign = offset >= TimeSpan.Zero ? "+" : "-"; var hours = Math.Abs(offset.Hours); var minutes = Math.Abs(offset.Minutes); return minutes == 0 ? $"GMT{sign}{hours}" : $"GMT{sign}{hours}:{minutes:00}"; } } private List InProgressTasks => tasks .Where(task => task.StatusId == AppTaskStatus.InProgress) .OrderBy(task => task.DueDate) .ToList(); private List TodoUpcomingTasks => tasks .Where(task => task.StatusId == AppTaskStatus.Todo || (task.StatusId != AppTaskStatus.Done && task.DueDate.Date >= DateTime.Today)) .OrderBy(task => task.DueDate) .ToList(); private List OverdueTasks => tasks .Where(task => task.DueDate.Date < DateTime.Today && task.StatusId != AppTaskStatus.Done) .OrderBy(task => task.DueDate) .ToList(); private List SummaryMetrics => BuildSummaryMetrics(); protected override async Task OnInitializedAsync() { await LoadDashboardData(); } protected override async Task OnAfterRenderAsync(bool firstRender) { await JS.InvokeVoidAsync("initIcons"); } private async Task LoadDashboardData() { isLoading = true; errorMessage = null; try { var authState = await AuthStateProvider.GetAuthenticationStateAsync(); var user = authState.User; var idStr = user.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value; if (idStr != null) { long.TryParse(idStr, out currentUserId); } var client = ApiClient.CreateClient(); var tasksResult = await client.GetFromJsonAsync>>($"User/{currentUserId}/tasks", Serialization.CaseInsensitive); var projectsResult = await client.GetFromJsonAsync>("Project", Serialization.CaseInsensitive); if (tasksResult?.IsSuccess == true && tasksResult.Value != null) { tasks = tasksResult.Value.ToList(); } else { tasks = new List(); } selectedDate = DateTime.Today; projects = projectsResult ?? new List(); } catch (Exception ex) { errorMessage = $"{AppLocalization.Text("common.error", "Error")}: {ex.Message}"; } finally { isLoading = false; } } private void SelectDate(DateTime date) { selectedDate = date.Date; } private void PrevMonth() { selectedDate = selectedDate.AddMonths(-1); } private void NextMonth() { selectedDate = selectedDate.AddMonths(1); } private List BuildSummaryMetrics() { var inProgress = tasks.Count(task => task.StatusId == AppTaskStatus.InProgress); var todo = tasks.Count(task => task.StatusId == AppTaskStatus.Todo); var dueSoon = tasks.Count(task => task.StatusId != AppTaskStatus.Done && task.DueDate.Date >= DateTime.Today && task.DueDate.Date <= DateTime.Today.AddDays(3)); var completed = tasks.Count(task => task.StatusId == AppTaskStatus.Done); var overdue = tasks.Count(task => task.DueDate.Date < DateTime.Today && task.StatusId != AppTaskStatus.Done); return new List { new("timer-reset", AppLocalization.Text("dashboard.active", "active"), AppLocalization.Number(inProgress), AppLocalization.Text("status.inprogress", "In Progress"), "rounded-2xl bg-violet-700 p-3 text-white", "text-sm font-semibold text-violet-700"), new("list-checks", AppLocalization.Text("dashboard.queued", "queued"), AppLocalization.Number(todo), AppLocalization.Text("status.todo", "To Do"), "rounded-2xl bg-cyan-600 p-3 text-white", "text-sm font-semibold text-cyan-600"), new("calendar-clock", AppLocalization.Text("dashboard.soon", "soon"), AppLocalization.Number(dueSoon), AppLocalization.Text("dashboard.dueSoon", "Due Soon"), "rounded-2xl bg-amber-500 p-3 text-white", "text-sm font-semibold text-amber-600"), new("circle-check-big", AppLocalization.Text("dashboard.done", "done"), AppLocalization.Number(completed), AppLocalization.Text("status.done", "Completed"), "rounded-2xl bg-emerald-600 p-3 text-white", "text-sm font-semibold text-emerald-600"), new("triangle-alert", AppLocalization.Text("dashboard.late", "late"), AppLocalization.Number(overdue), AppLocalization.Text("status.overdue", "Overdue"), "rounded-2xl bg-rose-600 p-3 text-white", "text-sm font-semibold text-rose-600") }; } private List BuildCalendarCells() { var firstOfMonth = new DateTime(selectedDate.Year, selectedDate.Month, 1); var start = firstOfMonth.AddDays(-(int)firstOfMonth.DayOfWeek); var end = start.AddDays(42); var dateMap = tasks .Where(task => task.DueDate.Date >= start.Date && task.DueDate.Date < end.Date) .GroupBy(task => task.DueDate.Date) .ToDictionary(group => group.Key, group => group.ToList()); var cells = new List(); for (var i = 0; i < 42; i++) { var date = start.AddDays(i); dateMap.TryGetValue(date.Date, out var dayTasks); dayTasks ??= new List(); cells.Add(new CalendarCellViewModel( date, date.Month == selectedDate.Month, date.Date == selectedDate.Date, dayTasks.Select(task => GetStatusColor(task.StatusId)).ToList(), dayTasks.Count)); } return cells; } private string GetProjectName(long projectId) { var project = projects.FirstOrDefault(project => project.Id == projectId); return project is null ? $"Project #{projectId}" : Content.GetProjectName(project); } private string GetStatusColor(AppTaskStatus statusId) => statusId switch { AppTaskStatus.Todo => "#22d3ee", AppTaskStatus.InProgress => "#3b82f6", AppTaskStatus.Done => "#10b981", _ => "#94a3b8" }; private string GetCalendarCellClasses(CalendarCellViewModel cell) { var classes = new List { "min-h-[52px] rounded-2xl border p-2 text-left transition-colors" }; if (cell.IsSelected) { classes.Add("border-violet-500 bg-violet-600"); } else if (cell.IsCurrentMonth) { classes.Add("border-slate-200 bg-white hover:border-violet-200 hover:bg-slate-50"); } else { classes.Add("border-slate-100 bg-slate-50 text-slate-400"); } return string.Join(" ", classes); } private string GetDueLabel(TaskDto task) { var days = (task.DueDate.Date - DateTime.Today).Days; if (task.StatusId == AppTaskStatus.Done) { return AppLocalization.Text("dashboard.done", "Done"); } if (days < 0) { return AppLocalization.Text("dashboard.daysAgo", "{count}d ago").Replace("{count}", AppLocalization.Number(Math.Abs(days))); } if (days == 0) { return AppLocalization.Text("dashboard.today", "Today"); } return AppLocalization.Text("dashboard.daysLeft", "{count}d left").Replace("{count}", AppLocalization.Number(days)); } private string GetDueTextClass(TaskDto task) { var days = (task.DueDate.Date - DateTime.Today).Days; if (task.StatusId == AppTaskStatus.Done) { return "text-emerald-600"; } if (days < 0) { return "text-rose-600"; } if (days <= 3) { return "text-amber-500"; } return "text-sky-600"; } private string DaysOverdue(TaskDto task) { var days = (DateTime.Today - task.DueDate.Date).Days; return days <= 0 ? AppLocalization.Text("dashboard.dueToday", "Due today") : AppLocalization.Text("dashboard.daysAgo", "{count}d ago").Replace("{count}", AppLocalization.Number(days)); } private string GetDisplayDateLabel(DateTime date) => AppLocalization.IsBurmeseCulture ? $"{AppLocalization.MonthName(date, true)} {AppLocalization.Number(date.Day)}" : date.ToString("dddd, MMM d"); private string GetMonthYearLabel(DateTime date) => AppLocalization.IsBurmeseCulture ? $"{AppLocalization.Number(date.Year)} {AppLocalization.MonthName(date)}" : date.ToString("MMMM yyyy"); private sealed record CalendarCellViewModel( DateTime Date, bool IsCurrentMonth, bool IsSelected, List TaskDots, int TaskCount); }