User commited on
Commit
f6d34ff
·
1 Parent(s): 8f72634

fix: overall UI

Browse files
Files changed (29) hide show
  1. TaskTrackingSystem.WebApp/Components/App.razor +67 -55
  2. TaskTrackingSystem.WebApp/Components/Layout/MainLayout.razor +100 -27
  3. TaskTrackingSystem.WebApp/Components/Layout/NavMenu.razor +7 -6
  4. TaskTrackingSystem.WebApp/Components/Layout/NavMenu.razor.css +50 -67
  5. TaskTrackingSystem.WebApp/Components/Pages/Features/Auth/Login.razor +12 -9
  6. TaskTrackingSystem.WebApp/Components/Pages/Features/Auth/Register.razor +13 -12
  7. TaskTrackingSystem.WebApp/Components/Pages/Features/Auth/ResetPassword.razor +16 -8
  8. TaskTrackingSystem.WebApp/Components/Pages/Features/Dashboard/EmployeeDashboard.razor +18 -18
  9. TaskTrackingSystem.WebApp/Components/Pages/Features/Dashboard/Home.razor +26 -26
  10. TaskTrackingSystem.WebApp/Components/Pages/Features/Dashboard/ManagerDashboard.razor +24 -24
  11. TaskTrackingSystem.WebApp/Components/Pages/Features/Issues/AddIssue.razor +1 -1
  12. TaskTrackingSystem.WebApp/Components/Pages/Features/Issues/Issues.razor +1 -1
  13. TaskTrackingSystem.WebApp/Components/Pages/Features/Projects/ProjectAssign.razor +1 -1
  14. TaskTrackingSystem.WebApp/Components/Pages/Features/Projects/Projects.razor +3 -3
  15. TaskTrackingSystem.WebApp/Components/Pages/Features/Roles/Roles.razor +8 -8
  16. TaskTrackingSystem.WebApp/Components/Pages/Features/Tasks/KanbanBoard.razor +2 -2
  17. TaskTrackingSystem.WebApp/Components/Pages/Features/Tasks/TaskAssign.razor +2 -2
  18. TaskTrackingSystem.WebApp/Components/Pages/Features/Tasks/TaskDetails.razor +3 -3
  19. TaskTrackingSystem.WebApp/Components/Pages/Features/Tasks/Tasks.razor +3 -3
  20. TaskTrackingSystem.WebApp/Components/Pages/Features/Users/Users.razor +2 -2
  21. TaskTrackingSystem.WebApp/Components/Partial/ConfirmDialog.razor +9 -5
  22. TaskTrackingSystem.WebApp/Components/Partial/EmptyState.razor +5 -3
  23. TaskTrackingSystem.WebApp/Components/Partial/LanguageToggle.razor +1 -1
  24. TaskTrackingSystem.WebApp/Components/Partial/LoadingSpinner.razor +1 -1
  25. TaskTrackingSystem.WebApp/Components/Partial/NotificationBell.razor +6 -6
  26. TaskTrackingSystem.WebApp/Components/Partial/Pagination.razor +13 -21
  27. TaskTrackingSystem.WebApp/Components/Partial/SuccessAlert.razor +2 -2
  28. TaskTrackingSystem.WebApp/Components/Partial/ThemeToggle.razor +1 -1
  29. TaskTrackingSystem.WebApp/wwwroot/app.css +470 -29
TaskTrackingSystem.WebApp/Components/App.razor CHANGED
@@ -71,39 +71,40 @@
71
  };
72
  </script>
73
  <script>
74
- const brandDark = "#111827";
75
- const brandDarkAlt = "#1F2937";
76
- const brandPrimary = "#111827";
77
- const brandAccent = "#374151";
78
- const brandLight = "#E5E7EB";
79
- const brandMuted = "#6B7280";
 
80
 
81
  const semanticRedBg = "#FFE4E6";
82
- const semanticRedText = "#991B1B";
83
 
84
- const semanticGreenBg = "#D1FAE5";
85
- const semanticGreenText = "#065F46";
86
 
87
- const semanticNeutralBg = "#F3F4F6";
88
- const semanticNeutralText = "#374151";
89
 
90
- const neutralBg = "#F8F9FA";
91
- const neutralBorder = "#E5E7EB";
92
- const neutralMuted = "#6B7280";
93
- const neutralDark = "#111827";
94
 
95
  const customGray = {
96
  50: neutralBg,
97
- 100: "#F3F4F6",
98
  200: neutralBorder,
99
- 300: "#D1D5DB",
100
- 400: "#9CA3AF",
101
  500: neutralMuted,
102
- 600: "#4B5563",
103
- 700: "#374151",
104
- 800: "#1F2937",
105
  900: neutralDark,
106
- 950: neutralDark
107
  };
108
 
109
  tailwind.config = {
@@ -206,16 +207,16 @@
206
  900: "#0C4A6E"
207
  },
208
  blue: {
209
- 50: semanticNeutralBg,
210
- 100: semanticNeutralBg,
211
- 200: "#E5E7EB",
212
- 300: "#D1D5DB",
213
- 400: "#9CA3AF",
214
- 500: semanticNeutralText,
215
- 600: semanticNeutralText,
216
- 700: "#1F2937",
217
- 800: "#111827",
218
- 900: "#111827"
219
  },
220
  amber: {
221
  50: "#FEF3C7",
@@ -243,49 +244,49 @@
243
  },
244
  violet: {
245
  50: brandLight,
246
- 100: "#E8E8FF",
247
- 200: brandMuted,
248
- 300: brandMuted,
249
- 400: brandAccent,
250
- 500: brandPrimary,
251
  600: brandPrimary,
252
- 700: brandDarkAlt,
253
  800: brandDark,
254
  900: brandDark,
255
  950: brandDark
256
  },
257
  purple: {
258
  50: brandLight,
259
- 100: "#E8E8FF",
260
- 200: brandMuted,
261
- 300: brandMuted,
262
- 400: brandAccent,
263
- 500: brandPrimary,
264
  600: brandPrimary,
265
- 700: brandDarkAlt,
266
  800: brandDark,
267
  900: brandDark,
268
  950: brandDark
269
  },
270
  indigo: {
271
  50: brandLight,
272
- 100: "#E8E8FF",
273
- 200: brandMuted,
274
- 300: brandMuted,
275
- 400: brandAccent,
276
- 500: brandPrimary,
277
  600: brandPrimary,
278
- 700: brandDarkAlt,
279
  800: brandDark,
280
  900: brandDark,
281
  950: brandDark
282
  },
283
 
284
  chart: {
285
- purple: "#111827",
286
- teal: "#4B5563",
287
  green: "#10B981",
288
- neutral: "#E5E7EB"
289
  },
290
 
291
  background: neutralBg,
@@ -308,6 +309,17 @@
308
  "accent-foreground": "#FFFFFF",
309
 
310
  border: neutralBorder,
 
 
 
 
 
 
 
 
 
 
 
311
  }
312
  }
313
  }
@@ -457,13 +469,13 @@
457
  }
458
  },
459
  currentDateIndicator: {
460
- color: '#7c3aed',
461
  dashStyle: 'ShortDash',
462
  width: 2,
463
  label: {
464
  format: 'Today',
465
  style: {
466
- color: '#7c3aed',
467
  fontWeight: 'bold'
468
  }
469
  }
 
71
  };
72
  </script>
73
  <script>
74
+ const brandDark = "#172554";
75
+ const brandDarkAlt = "#1E3A8A";
76
+ const brandPrimary = "#2563EB";
77
+ const brandPrimaryHover = "#1D4ED8";
78
+ const brandAccent = "#0F766E";
79
+ const brandLight = "#EFF6FF";
80
+ const brandMuted = "#64748B";
81
 
82
  const semanticRedBg = "#FFE4E6";
83
+ const semanticRedText = "#BE123C";
84
 
85
+ const semanticGreenBg = "#CCFBF1";
86
+ const semanticGreenText = "#0F766E";
87
 
88
+ const semanticNeutralBg = "#F8FAFC";
89
+ const semanticNeutralText = "#475569";
90
 
91
+ const neutralBg = "#F6F8FB";
92
+ const neutralBorder = "#E2E8F0";
93
+ const neutralMuted = "#64748B";
94
+ const neutralDark = "#0F172A";
95
 
96
  const customGray = {
97
  50: neutralBg,
98
+ 100: "#F1F5F9",
99
  200: neutralBorder,
100
+ 300: "#CBD5E1",
101
+ 400: "#94A3B8",
102
  500: neutralMuted,
103
+ 600: "#475569",
104
+ 700: "#334155",
105
+ 800: "#1E293B",
106
  900: neutralDark,
107
+ 950: "#020617"
108
  };
109
 
110
  tailwind.config = {
 
207
  900: "#0C4A6E"
208
  },
209
  blue: {
210
+ 50: "#EFF6FF",
211
+ 100: "#DBEAFE",
212
+ 200: "#BFDBFE",
213
+ 300: "#93C5FD",
214
+ 400: "#60A5FA",
215
+ 500: "#3B82F6",
216
+ 600: brandPrimary,
217
+ 700: brandPrimaryHover,
218
+ 800: brandDarkAlt,
219
+ 900: brandDark
220
  },
221
  amber: {
222
  50: "#FEF3C7",
 
244
  },
245
  violet: {
246
  50: brandLight,
247
+ 100: "#DBEAFE",
248
+ 200: "#BFDBFE",
249
+ 300: "#93C5FD",
250
+ 400: "#60A5FA",
251
+ 500: "#3B82F6",
252
  600: brandPrimary,
253
+ 700: brandPrimaryHover,
254
  800: brandDark,
255
  900: brandDark,
256
  950: brandDark
257
  },
258
  purple: {
259
  50: brandLight,
260
+ 100: "#DBEAFE",
261
+ 200: "#BFDBFE",
262
+ 300: "#93C5FD",
263
+ 400: "#60A5FA",
264
+ 500: "#3B82F6",
265
  600: brandPrimary,
266
+ 700: brandPrimaryHover,
267
  800: brandDark,
268
  900: brandDark,
269
  950: brandDark
270
  },
271
  indigo: {
272
  50: brandLight,
273
+ 100: "#DBEAFE",
274
+ 200: "#BFDBFE",
275
+ 300: "#93C5FD",
276
+ 400: "#60A5FA",
277
+ 500: "#3B82F6",
278
  600: brandPrimary,
279
+ 700: brandPrimaryHover,
280
  800: brandDark,
281
  900: brandDark,
282
  950: brandDark
283
  },
284
 
285
  chart: {
286
+ purple: brandPrimary,
287
+ teal: "#14B8A6",
288
  green: "#10B981",
289
+ neutral: neutralBorder
290
  },
291
 
292
  background: neutralBg,
 
309
  "accent-foreground": "#FFFFFF",
310
 
311
  border: neutralBorder,
312
+ },
313
+ borderRadius: {
314
+ none: "0",
315
+ sm: "0.375rem",
316
+ DEFAULT: "0.5rem",
317
+ md: "0.5rem",
318
+ lg: "0.5rem",
319
+ xl: "0.5rem",
320
+ "2xl": "0.5rem",
321
+ "3xl": "0.5rem",
322
+ full: "9999px"
323
  }
324
  }
325
  }
 
469
  }
470
  },
471
  currentDateIndicator: {
472
+ color: '#2563eb',
473
  dashStyle: 'ShortDash',
474
  width: 2,
475
  label: {
476
  format: 'Today',
477
  style: {
478
+ color: '#2563eb',
479
  fontWeight: 'bold'
480
  }
481
  }
TaskTrackingSystem.WebApp/Components/Layout/MainLayout.razor CHANGED
@@ -3,29 +3,43 @@
3
  @using System.Security.Claims
4
  @inject TaskTrackingSystem.WebApp.Localization.UiLanguageService Language
5
 
6
- <div class="flex h-screen bg-background font-sans text-slate-800 antialiased overflow-hidden">
7
- <aside class="flex w-64 flex-col border-r border-slate-800 bg-slate-950 text-white shrink-0 overflow-hidden">
8
- <div class="flex h-20 items-center px-6">
9
- <span class="flex h-9 w-9 items-center justify-center rounded-xl bg-white/10 text-white">
 
 
 
 
 
10
  <i data-lucide="flame" class="h-5 w-5 fill-current"></i>
11
  </span>
12
- <span class="ml-3 font-bold text-xl tracking-tight text-white">Taskify</span>
 
 
 
 
 
 
 
 
 
13
  </div>
14
 
15
  <NavMenu @rendermode="new InteractiveServerRenderMode(prerender: false)" />
16
 
17
- <div class="border-t border-slate-800 p-4 shrink-0">
18
  <AuthorizeView>
19
  <Authorized>
20
- <div class="flex items-center rounded-xl p-2 hover:bg-white/10 transition-all">
21
- <span class="flex h-10 w-10 items-center justify-center rounded-full bg-slate-700 text-white font-semibold text-sm shrink-0">
22
  @(context.User.Identity?.Name?.Length > 0 ? context.User.Identity.Name[0].ToString().ToUpper() : "U")
23
  </span>
24
  <div class="ml-3 overflow-hidden flex flex-col">
25
  <span class="text-sm font-semibold truncate max-w-[130px] text-white">@context.User.Identity?.Name</span>
26
  <form id="logout-form" method="post" action="/account/logout" class="mt-0.5">
27
  <AntiforgeryToken />
28
- <button type="button" onclick="document.getElementById('logout-confirm-dialog').style.display='flex';" class="text-xs hover:opacity-80 font-semibold text-left flex items-center gap-1 transition-all text-slate-300">
29
  <i data-lucide="log-out" class="h-3 w-3"></i>
30
  @Language.Texts.SignOut
31
  </button>
@@ -34,7 +48,7 @@
34
  </div>
35
  </Authorized>
36
  <NotAuthorized>
37
- <a href="/login" class="flex items-center justify-center rounded-xl bg-slate-800 text-white px-4 py-2.5 text-sm font-medium hover:bg-slate-700 transition-all">
38
  @Language.Texts.SignIn
39
  </a>
40
  </NotAuthorized>
@@ -42,9 +56,9 @@
42
  </div>
43
 
44
  <div id="logout-confirm-dialog" class="fixed inset-0 z-[60] flex items-center justify-center bg-black/40 backdrop-blur-sm" style="display: none;">
45
- <div class="rounded-2xl shadow-xl w-full max-w-sm mx-4 overflow-hidden animate-in border border-slate-100 bg-white">
46
  <div class="p-6 text-center">
47
- <div class="mx-auto flex h-14 w-14 items-center justify-center rounded-full mb-4" style="background-color: var(--status-warning-bg); color: var(--brand-accent);">
48
  <i data-lucide="log-out" class="h-7 w-7"></i>
49
  </div>
50
  <h3 class="text-lg font-semibold text-slate-900">@Language.Texts.SignOutTitle</h3>
@@ -62,10 +76,21 @@
62
  </div>
63
  </aside>
64
 
65
- <main class="flex-1 flex flex-col overflow-y-auto">
66
- <header class="flex h-16 items-center justify-between border-b border-slate-200 bg-white px-8">
67
- <h1 class="text-lg font-semibold text-slate-900">@Language.Texts.Workspace</h1>
68
- <div class="flex items-center space-x-4">
 
 
 
 
 
 
 
 
 
 
 
69
  <AuthorizeView>
70
  <Authorized>
71
  <FirebaseTokenSync />
@@ -77,7 +102,7 @@
77
  </div>
78
  </header>
79
 
80
- <div class="p-8 flex-1">
81
  @if (isAuthorizedPage)
82
  {
83
  @Body
@@ -115,17 +140,65 @@
115
  string.IsNullOrEmpty(Navigation.ToBaseRelativePath(Navigation.Uri)) ||
116
  Navigation.ToBaseRelativePath(Navigation.Uri).Split('?')[0].Trim('/') is "dashboard" or "Home" or "";
117
 
118
- private string CurrentPageTitle => Navigation.ToBaseRelativePath(Navigation.Uri).Split('?')[0].Trim('/') switch
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  {
120
- "projects" => "Projects",
121
- "tasks" => "Tasks",
122
- "reports" => "Reports",
123
- "roles" => "Roles & Access",
124
- "users" => "Users",
125
- "dashboard" => "Dashboard",
126
- "dashboard/manager" => "Manager Dashboard",
127
- "dashboard/employee" => "Employee Dashboard",
128
- _ => Language.Texts.Workspace
 
 
 
 
 
 
129
  };
130
 
131
  protected override async Task OnInitializedAsync()
 
3
  @using System.Security.Claims
4
  @inject TaskTrackingSystem.WebApp.Localization.UiLanguageService Language
5
 
6
+ <div class="app-shell min-h-screen bg-slate-50 text-slate-950 lg:flex lg:overflow-hidden">
7
+ <button type="button"
8
+ class="sidebar-scrim"
9
+ onclick="document.body.classList.remove('tts-sidebar-open');"
10
+ aria-label="Close navigation"></button>
11
+
12
+ <aside class="app-sidebar fixed inset-y-0 left-0 z-[60] flex w-72 -translate-x-full flex-col overflow-hidden border-r border-slate-800 bg-slate-950 text-white transition-transform duration-200 lg:static lg:z-auto lg:min-h-screen lg:translate-x-0 lg:shrink-0">
13
+ <div class="app-sidebar-brand flex min-h-20 items-center gap-3 px-5 py-4">
14
+ <span class="app-sidebar-logo inline-flex h-10 w-10 items-center justify-center rounded-lg bg-blue-600 text-white shadow-sm">
15
  <i data-lucide="flame" class="h-5 w-5 fill-current"></i>
16
  </span>
17
+ <span class="app-sidebar-title flex flex-col leading-tight">
18
+ <strong class="text-lg font-bold tracking-normal text-white">Taskify</strong>
19
+ <span class="mt-0.5 text-[11px] font-semibold uppercase tracking-wide text-slate-400">Workspace</span>
20
+ </span>
21
+ <button type="button"
22
+ class="control-button mobile-sidebar-close ml-auto inline-flex h-9 w-9 items-center justify-center rounded-lg border border-white/10 bg-white/10 text-white hover:bg-white/15 hover:text-white lg:hidden"
23
+ onclick="document.body.classList.remove('tts-sidebar-open');"
24
+ aria-label="Close navigation">
25
+ <i data-lucide="x" class="h-4 w-4"></i>
26
+ </button>
27
  </div>
28
 
29
  <NavMenu @rendermode="new InteractiveServerRenderMode(prerender: false)" />
30
 
31
+ <div class="app-sidebar-user mt-auto shrink-0 border-t border-white/10 p-3">
32
  <AuthorizeView>
33
  <Authorized>
34
+ <div class="flex items-center rounded-lg border border-white/10 bg-white/[0.04] p-2 transition-colors hover:bg-white/[0.08]">
35
+ <span class="flex h-10 w-10 items-center justify-center rounded-full bg-white/10 text-white font-semibold text-sm shrink-0">
36
  @(context.User.Identity?.Name?.Length > 0 ? context.User.Identity.Name[0].ToString().ToUpper() : "U")
37
  </span>
38
  <div class="ml-3 overflow-hidden flex flex-col">
39
  <span class="text-sm font-semibold truncate max-w-[130px] text-white">@context.User.Identity?.Name</span>
40
  <form id="logout-form" method="post" action="/account/logout" class="mt-0.5">
41
  <AntiforgeryToken />
42
+ <button type="button" onclick="document.getElementById('logout-confirm-dialog').style.display='flex';" class="text-xs hover:opacity-90 font-semibold text-left flex items-center gap-1 transition-all text-slate-300">
43
  <i data-lucide="log-out" class="h-3 w-3"></i>
44
  @Language.Texts.SignOut
45
  </button>
 
48
  </div>
49
  </Authorized>
50
  <NotAuthorized>
51
+ <a href="/login" class="flex items-center justify-center rounded-lg bg-white/10 text-white px-4 py-2.5 text-sm font-medium hover:bg-white/15 transition-colors">
52
  @Language.Texts.SignIn
53
  </a>
54
  </NotAuthorized>
 
56
  </div>
57
 
58
  <div id="logout-confirm-dialog" class="fixed inset-0 z-[60] flex items-center justify-center bg-black/40 backdrop-blur-sm" style="display: none;">
59
+ <div class="rounded-lg shadow-lg w-full max-w-sm mx-4 overflow-hidden animate-in border border-slate-100 bg-white">
60
  <div class="p-6 text-center">
61
+ <div class="mx-auto flex h-14 w-14 items-center justify-center rounded-full mb-4" style="background-color: var(--status-warning-bg); color: var(--status-warning-text);">
62
  <i data-lucide="log-out" class="h-7 w-7"></i>
63
  </div>
64
  <h3 class="text-lg font-semibold text-slate-900">@Language.Texts.SignOutTitle</h3>
 
76
  </div>
77
  </aside>
78
 
79
+ <main class="app-main flex min-w-0 flex-1 flex-col lg:min-h-screen">
80
+ <header class="app-topbar flex min-h-16 items-center justify-between gap-3 border-b border-slate-200 bg-white/95 px-4 backdrop-blur lg:px-6">
81
+ <div class="flex min-w-0 items-center gap-3">
82
+ <button type="button"
83
+ class="control-button mobile-sidebar-toggle inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-lg border border-slate-200 bg-white text-slate-600 shadow-sm hover:bg-slate-50 hover:text-blue-600 lg:hidden"
84
+ onclick="document.body.classList.add('tts-sidebar-open');"
85
+ aria-label="Open navigation">
86
+ <i data-lucide="menu" class="h-4 w-4"></i>
87
+ </button>
88
+ <div class="app-topbar-title min-w-0">
89
+ <h1 class="truncate text-base font-bold tracking-normal text-slate-950">@CurrentPageTitle</h1>
90
+ <p class="hidden truncate text-xs font-medium text-slate-500 sm:block">@CurrentPageDescription</p>
91
+ </div>
92
+ </div>
93
+ <div class="flex shrink-0 items-center gap-2 sm:gap-3">
94
  <AuthorizeView>
95
  <Authorized>
96
  <FirebaseTokenSync />
 
102
  </div>
103
  </header>
104
 
105
+ <div class="app-content flex-1 overflow-y-auto p-4 lg:p-8">
106
  @if (isAuthorizedPage)
107
  {
108
  @Body
 
140
  string.IsNullOrEmpty(Navigation.ToBaseRelativePath(Navigation.Uri)) ||
141
  Navigation.ToBaseRelativePath(Navigation.Uri).Split('?')[0].Trim('/') is "dashboard" or "Home" or "";
142
 
143
+ private string CurrentRoute => Navigation.ToBaseRelativePath(Navigation.Uri).Split('?')[0].Trim('/');
144
+
145
+ private string CurrentPageTitle
146
+ {
147
+ get
148
+ {
149
+ if (CurrentRoute.StartsWith("projects/", StringComparison.OrdinalIgnoreCase) &&
150
+ CurrentRoute.EndsWith("/tasks", StringComparison.OrdinalIgnoreCase))
151
+ {
152
+ return "Kanban Board";
153
+ }
154
+
155
+ if (CurrentRoute.StartsWith("tasks/", StringComparison.OrdinalIgnoreCase))
156
+ {
157
+ return "Task Details";
158
+ }
159
+
160
+ return CurrentRoute switch
161
+ {
162
+ "" or "dashboard" or "Home" => "Dashboard",
163
+ "dashboard/manager" => "Manager Dashboard",
164
+ "dashboard/employee" => "Employee Dashboard",
165
+ "projects" or "projects/all" => "Projects",
166
+ "projects/assign" => "Project Assignment",
167
+ "tasks" or "tasks/all" => "Tasks",
168
+ "board" => "Kanban Board",
169
+ "tasks/assign" => "Task Assignment",
170
+ "issues" or "issues/all" => "Issues",
171
+ "issues/add" => "Add Issue",
172
+ "reports/tasks" => "Task Report",
173
+ "reports/time" => "Time Tracking",
174
+ "reports/projects" => "Project Progress",
175
+ "reports/overdue" => "Overdue Tasks",
176
+ "reports/employees" => "Employee Report",
177
+ "roles" => "Roles & Access",
178
+ "users" => "Users",
179
+ "audit-logs" => "Audit Logs",
180
+ _ => Language.Texts.Workspace
181
+ };
182
+ }
183
+ }
184
+
185
+ private string CurrentPageDescription => CurrentPageTitle switch
186
  {
187
+ "Dashboard" => "A quick overview of work, risk, and progress.",
188
+ "Manager Dashboard" => "Track projects, people, and delivery health.",
189
+ "Employee Dashboard" => "Focus on your tasks, due dates, and progress.",
190
+ "Projects" => "Manage project plans, timelines, and assignments.",
191
+ "Project Assignment" => "Assign team members to the right projects.",
192
+ "Tasks" => "Review, filter, and update work items.",
193
+ "Kanban Board" => "Move work through status columns.",
194
+ "Task Assignment" => "Assign tasks to project members.",
195
+ "Issues" => "Track blockers, notes, and daily work progress.",
196
+ "Add Issue" => "Record a new issue against a task.",
197
+ "Roles & Access" => "Control user roles, menus, and permissions.",
198
+ "Users" => "Manage people, status, and account details.",
199
+ "Audit Logs" => "Review system activity and changes.",
200
+ _ when CurrentPageTitle.Contains("Report", StringComparison.OrdinalIgnoreCase) => "Analyze work data and export reports.",
201
+ _ => "Workspace"
202
  };
203
 
204
  protected override async Task OnInitializedAsync()
TaskTrackingSystem.WebApp/Components/Layout/NavMenu.razor CHANGED
@@ -10,14 +10,15 @@
10
  @inject NavigationManager Navigation
11
  @inject IJSRuntime JS
12
 
13
- <nav class="flex-1 space-y-1 px-4 py-6 overflow-y-auto">
 
14
  @if (menus.Any())
15
  {
16
  @foreach (var menu in menus)
17
  {
18
  if (menu.SubMenus != null && menu.SubMenus.Count > 0)
19
  {
20
- <button class="nav-dropdown-toggle @(IsMenuGroupExpanded(menu.MenuCode) ? "expanded" : "")"
21
  @onclick="() => ToggleMenuGroup(menu.MenuCode)">
22
  @RenderIcon(menu.Icon)
23
  <span class="flex-1 text-left">@menu.MenuName</span>
@@ -28,7 +29,7 @@
28
  <div class="nav-dropdown-children">
29
  @foreach (var sub in menu.SubMenus)
30
  {
31
- <NavLink class="nav-link-item nav-child-item"
32
  href="@MenuAuthorizationService.NormalizeMenuHref(sub.MenuUrl)"
33
  Match="@GetNavLinkMatch(sub.MenuUrl)">
34
  @RenderSmallIcon(sub.Icon)
@@ -40,7 +41,7 @@
40
  }
41
  else
42
  {
43
- <NavLink class="nav-link-item"
44
  href="@MenuAuthorizationService.NormalizeMenuHref(menu.MenuUrl)"
45
  Match="@(string.IsNullOrWhiteSpace(menu.MenuUrl) || menu.MenuUrl.Trim('/') is "dashboard" or "" ? NavLinkMatch.All : NavLinkMatch.Prefix)">
46
  @RenderIcon(menu.Icon)
@@ -50,7 +51,7 @@
50
  }
51
  @if (isAdmin)
52
  {
53
- <NavLink class="nav-link-item mt-2 pt-2 border-t border-slate-100" href="/audit-logs" Match="NavLinkMatch.All">
54
  <i data-lucide="file-text" class="h-4 w-4 mr-3 text-slate-400 shrink-0"></i>
55
  <span>@Language.Texts.AuditLogs</span>
56
  </NavLink>
@@ -157,7 +158,7 @@
157
  }
158
  else
159
  {
160
- // Access items are the same just refresh expanded state for the new route.
161
  foreach (var menu in menus)
162
  {
163
  if (menu.SubMenus != null && menu.SubMenus.Count > 0 && IsMenuExpanded(menu.MenuCode, menu.SubMenus))
 
10
  @inject NavigationManager Navigation
11
  @inject IJSRuntime JS
12
 
13
+ <nav class="flex-1 space-y-1 overflow-y-auto px-3 pb-5 pt-2 text-slate-300"
14
+ onclick="if (event.target.closest('a')) document.body.classList.remove('tts-sidebar-open');">
15
  @if (menus.Any())
16
  {
17
  @foreach (var menu in menus)
18
  {
19
  if (menu.SubMenus != null && menu.SubMenus.Count > 0)
20
  {
21
+ <button class="nav-dropdown-toggle flex w-full items-center rounded-lg px-3 py-2.5 text-sm font-semibold text-slate-300 transition-colors hover:bg-white/10 hover:text-white @(IsMenuGroupExpanded(menu.MenuCode) ? "expanded bg-white/10 text-white" : "")"
22
  @onclick="() => ToggleMenuGroup(menu.MenuCode)">
23
  @RenderIcon(menu.Icon)
24
  <span class="flex-1 text-left">@menu.MenuName</span>
 
29
  <div class="nav-dropdown-children">
30
  @foreach (var sub in menu.SubMenus)
31
  {
32
+ <NavLink class="nav-link-item nav-child-item flex items-center rounded-lg px-3 py-2 text-sm font-semibold text-slate-300 transition-colors hover:bg-white/10 hover:text-white"
33
  href="@MenuAuthorizationService.NormalizeMenuHref(sub.MenuUrl)"
34
  Match="@GetNavLinkMatch(sub.MenuUrl)">
35
  @RenderSmallIcon(sub.Icon)
 
41
  }
42
  else
43
  {
44
+ <NavLink class="nav-link-item flex items-center rounded-lg px-3 py-2.5 text-sm font-semibold text-slate-300 transition-colors hover:bg-white/10 hover:text-white"
45
  href="@MenuAuthorizationService.NormalizeMenuHref(menu.MenuUrl)"
46
  Match="@(string.IsNullOrWhiteSpace(menu.MenuUrl) || menu.MenuUrl.Trim('/') is "dashboard" or "" ? NavLinkMatch.All : NavLinkMatch.Prefix)">
47
  @RenderIcon(menu.Icon)
 
51
  }
52
  @if (isAdmin)
53
  {
54
+ <NavLink class="nav-link-item mt-2 flex items-center rounded-lg border-t border-white/10 px-3 py-2.5 pt-3 text-sm font-semibold text-slate-300 transition-colors hover:bg-white/10 hover:text-white" href="/audit-logs" Match="NavLinkMatch.All">
55
  <i data-lucide="file-text" class="h-4 w-4 mr-3 text-slate-400 shrink-0"></i>
56
  <span>@Language.Texts.AuditLogs</span>
57
  </NavLink>
 
158
  }
159
  else
160
  {
161
+ // Access items are the same - just refresh expanded state for the new route.
162
  foreach (var menu in menus)
163
  {
164
  if (menu.SubMenus != null && menu.SubMenus.Count > 0 && IsMenuExpanded(menu.MenuCode, menu.SubMenus))
TaskTrackingSystem.WebApp/Components/Layout/NavMenu.razor.css CHANGED
@@ -1,99 +1,82 @@
1
- /* Modern Sidebar Navigation Styles */
2
- ::deep a.nav-link-item {
3
  display: flex;
 
4
  align-items: center;
5
- border-radius: 0.75rem;
6
- padding: 0.625rem 1rem;
7
- font-size: 0.875rem;
8
- font-weight: 500;
9
- color: #CBD5E1;
10
- transition: all 0.2s ease;
11
- border-left: 3px solid transparent;
12
- }
13
-
14
- ::deep a.nav-link-item:hover {
15
- background-color: rgba(255, 255, 255, 0.08); /* crisp highlight on hover */
16
- color: #FFFFFF;
17
- }
18
-
19
- ::deep a.nav-link-item.active {
20
- background-color: rgba(255, 255, 255, 0.15) !important;
21
- color: #FFFFFF !important;
22
- border-left: 3px solid #FFFFFF !important;
23
- font-weight: 600;
24
- }
25
-
26
- ::deep a.nav-link-item.active i {
27
- color: #FFFFFF !important;
28
- }
29
-
30
- ::deep a.nav-link-item i {
31
- color: #CBD5E1;
32
- transition: color 0.2s ease;
33
- }
34
-
35
- ::deep a.nav-link-item:hover i {
36
- color: #FFFFFF;
37
  }
38
 
39
- /* Dropdown toggle button (parent item) */
40
  ::deep button.nav-dropdown-toggle {
41
- display: flex;
42
- align-items: center;
43
- width: 100%;
44
- border-radius: 0.75rem;
45
- padding: 0.625rem 1rem;
46
- font-size: 0.875rem;
47
- font-weight: 500;
48
- color: #CBD5E1;
49
- background: none;
50
- border: none;
51
- border-left: 3px solid transparent;
52
  cursor: pointer;
53
- transition: all 0.2s ease;
54
  }
55
 
 
56
  ::deep button.nav-dropdown-toggle:hover {
57
- background-color: rgba(255, 255, 255, 0.08);
 
58
  color: #FFFFFF;
59
  }
60
 
61
- ::deep button.nav-dropdown-toggle.expanded {
62
- color: #FFFFFF;
63
- font-weight: 600;
 
 
64
  }
65
 
66
- ::deep button.nav-dropdown-toggle:hover i,
67
- ::deep button.nav-dropdown-toggle.expanded i {
68
  color: #FFFFFF;
69
  }
70
 
 
71
  ::deep button.nav-dropdown-toggle i {
72
- color: #CBD5E1;
 
 
 
 
 
 
 
 
 
73
  }
74
 
75
- /* Chevron rotation on expand */
76
  ::deep button.nav-dropdown-toggle .nav-chevron {
77
- transition: transform 0.25s ease;
 
 
78
  }
79
 
80
  ::deep button.nav-dropdown-toggle.expanded .nav-chevron {
81
  transform: rotate(180deg);
 
82
  }
83
 
84
- /* Dropdown children container */
85
  ::deep .nav-dropdown-children {
86
- margin-left: 0.75rem;
87
- padding-left: 0.75rem;
88
- border-left: 1.5px solid rgba(255, 255, 255, 0.15);
89
- margin-top: 0.125rem;
90
- margin-bottom: 0.25rem;
91
  }
92
 
93
- /* Child nav items — slightly smaller */
94
  ::deep a.nav-child-item {
95
- padding: 0.5rem 0.75rem;
96
- font-size: 0.8125rem;
97
- border-left: none !important;
98
- border-radius: 0.5rem;
 
 
 
99
  }
 
1
+ ::deep a.nav-link-item,
2
+ ::deep button.nav-dropdown-toggle {
3
  display: flex;
4
+ width: 100%;
5
  align-items: center;
6
+ border: 1px solid transparent;
7
+ border-radius: var(--radius-lg, 0.5rem);
8
+ padding: 0.65rem 0.85rem;
9
+ color: var(--sidebar-muted, #CBD5E1);
10
+ font-size: 0.9rem;
11
+ font-weight: 650;
12
+ line-height: 1.2;
13
+ transition:
14
+ background-color 0.16s ease,
15
+ border-color 0.16s ease,
16
+ color 0.16s ease,
17
+ transform 0.16s ease;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  }
19
 
 
20
  ::deep button.nav-dropdown-toggle {
21
+ background: transparent;
 
 
 
 
 
 
 
 
 
 
22
  cursor: pointer;
 
23
  }
24
 
25
+ ::deep a.nav-link-item:hover,
26
  ::deep button.nav-dropdown-toggle:hover {
27
+ border-color: rgba(255, 255, 255, 0.08);
28
+ background-color: rgba(255, 255, 255, 0.07);
29
  color: #FFFFFF;
30
  }
31
 
32
+ ::deep a.nav-link-item.active {
33
+ border-color: rgba(96, 165, 250, 0.28) !important;
34
+ background-color: var(--sidebar-active, rgba(37, 99, 235, 0.18)) !important;
35
+ color: #FFFFFF !important;
36
+ box-shadow: inset 3px 0 0 var(--brand-primary);
37
  }
38
 
39
+ ::deep button.nav-dropdown-toggle.expanded {
 
40
  color: #FFFFFF;
41
  }
42
 
43
+ ::deep a.nav-link-item i,
44
  ::deep button.nav-dropdown-toggle i {
45
+ color: currentColor !important;
46
+ opacity: 0.78;
47
+ transition: opacity 0.16s ease;
48
+ }
49
+
50
+ ::deep a.nav-link-item:hover i,
51
+ ::deep a.nav-link-item.active i,
52
+ ::deep button.nav-dropdown-toggle:hover i,
53
+ ::deep button.nav-dropdown-toggle.expanded i {
54
+ opacity: 1;
55
  }
56
 
 
57
  ::deep button.nav-dropdown-toggle .nav-chevron {
58
+ margin-left: auto;
59
+ opacity: 0.65;
60
+ transition: transform 0.18s ease, opacity 0.18s ease;
61
  }
62
 
63
  ::deep button.nav-dropdown-toggle.expanded .nav-chevron {
64
  transform: rotate(180deg);
65
+ opacity: 1;
66
  }
67
 
 
68
  ::deep .nav-dropdown-children {
69
+ margin: 0.2rem 0 0.45rem 1rem;
70
+ padding-left: 0.65rem;
71
+ border-left: 1px solid rgba(148, 163, 184, 0.22);
 
 
72
  }
73
 
 
74
  ::deep a.nav-child-item {
75
+ padding: 0.54rem 0.7rem;
76
+ font-size: 0.84rem;
77
+ font-weight: 620;
78
+ }
79
+
80
+ ::deep a.nav-link-item.border-t {
81
+ border-top-color: rgba(148, 163, 184, 0.18) !important;
82
  }
TaskTrackingSystem.WebApp/Components/Pages/Features/Auth/Login.razor CHANGED
@@ -7,22 +7,25 @@
7
  @inject MenuAuthorizationService MenuAuthorization
8
  @inject IJSRuntime JS
9
 
10
- <PageTitle>Sign In TaskFlow</PageTitle>
11
 
12
- <div class="flex min-h-screen items-center justify-center bg-slate-50 py-12 px-4 sm:px-6 lg:px-8">
13
- <div class="w-full max-w-md space-y-8 bg-white p-8 rounded-2xl border border-slate-200 shadow-xl">
14
  <div>
15
  <div class="flex justify-center">
16
- <span class="flex h-12 w-12 items-center justify-center rounded-xl bg-violet-600 text-white font-bold text-xl">T</span>
 
 
17
  </div>
18
- <h2 class="mt-6 text-center text-3xl font-extrabold text-slate-900">Sign in to your account</h2>
 
19
  </div>
20
 
21
- <form method="post" action="/account/login" class="mt-8 space-y-6">
22
  <AntiforgeryToken />
23
  <input type="hidden" name="ReturnUrl" value="@ReturnUrl" />
24
 
25
- <div class="space-y-4 rounded-md shadow-sm">
26
  <div>
27
  <label for="usernameOrEmail" class="block text-sm font-medium text-slate-700 mb-1">Username or Email</label>
28
  <input id="usernameOrEmail" name="UsernameOrEmail" required placeholder="Enter username or email"
@@ -30,7 +33,7 @@
30
  </div>
31
  <div class="relative">
32
  <label for="password" class="block text-sm font-medium text-slate-700 mb-1">Password</label>
33
- <input id="password" name="Password" type="@(showPassword ? "text" : "password")" required placeholder="••••••••"
34
  class="w-full rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-900 placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-slate-900 focus:border-transparent transition-all pr-10" />
35
  <button type="button" @onclick="TogglePassword" class="absolute right-3 top-[28px] text-slate-400 hover:text-slate-600 focus:outline-none">
36
  <i data-lucide="@(showPassword ? "eye-off" : "eye")" class="h-5 w-5"></i>
@@ -78,7 +81,7 @@
78
  </p>
79
 
80
  <div>
81
- <button type="submit" class="group relative flex w-full justify-center rounded-lg bg-violet-600 px-4 py-2.5 text-sm font-medium text-white hover:bg-violet-700 transition-colors shadow-sm">
82
  Sign In
83
  </button>
84
  </div>
 
7
  @inject MenuAuthorizationService MenuAuthorization
8
  @inject IJSRuntime JS
9
 
10
+ <PageTitle>Sign In - Taskify</PageTitle>
11
 
12
+ <div class="flex min-h-screen items-center justify-center bg-slate-50 px-4 py-10 sm:px-6 lg:px-8">
13
+ <div class="w-full max-w-md space-y-7 rounded-xl border border-slate-200 bg-white p-8 shadow-sm">
14
  <div>
15
  <div class="flex justify-center">
16
+ <span class="flex h-12 w-12 items-center justify-center rounded-xl bg-violet-600 text-white shadow-sm">
17
+ <i data-lucide="flame" class="h-6 w-6 fill-current"></i>
18
+ </span>
19
  </div>
20
+ <h2 class="mt-5 text-center text-2xl font-bold text-slate-900">Sign in to Taskify</h2>
21
+ <p class="mt-2 text-center text-sm text-slate-500">Continue to your work dashboard.</p>
22
  </div>
23
 
24
+ <form method="post" action="/account/login" class="space-y-5">
25
  <AntiforgeryToken />
26
  <input type="hidden" name="ReturnUrl" value="@ReturnUrl" />
27
 
28
+ <div class="space-y-4">
29
  <div>
30
  <label for="usernameOrEmail" class="block text-sm font-medium text-slate-700 mb-1">Username or Email</label>
31
  <input id="usernameOrEmail" name="UsernameOrEmail" required placeholder="Enter username or email"
 
33
  </div>
34
  <div class="relative">
35
  <label for="password" class="block text-sm font-medium text-slate-700 mb-1">Password</label>
36
+ <input id="password" name="Password" type="@(showPassword ? "text" : "password")" required placeholder="Password"
37
  class="w-full rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-900 placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-slate-900 focus:border-transparent transition-all pr-10" />
38
  <button type="button" @onclick="TogglePassword" class="absolute right-3 top-[28px] text-slate-400 hover:text-slate-600 focus:outline-none">
39
  <i data-lucide="@(showPassword ? "eye-off" : "eye")" class="h-5 w-5"></i>
 
81
  </p>
82
 
83
  <div>
84
+ <button type="submit" class="group relative flex w-full justify-center rounded-lg bg-violet-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm transition-colors hover:bg-violet-700">
85
  Sign In
86
  </button>
87
  </div>
TaskTrackingSystem.WebApp/Components/Pages/Features/Auth/Register.razor CHANGED
@@ -5,26 +5,27 @@
5
  @attribute [Microsoft.AspNetCore.Authorization.AllowAnonymous]
6
  @inject IJSRuntime JS
7
 
8
- <PageTitle>Register TaskFlow</PageTitle>
9
 
10
- <div class="flex min-h-screen items-center justify-center bg-slate-50 py-12 px-4 sm:px-6 lg:px-8">
11
- <div class="w-full max-w-md space-y-8 bg-white p-8 rounded-2xl border border-slate-200 shadow-xl">
12
  <div>
13
  <div class="flex justify-center">
14
- <span class="flex h-12 w-12 items-center justify-center rounded-xl bg-violet-600 text-white font-bold text-xl">T</span>
 
 
15
  </div>
16
- <h2 class="mt-6 text-center text-3xl font-extrabold text-slate-900">Create a new account</h2>
17
  <p class="mt-2 text-center text-sm text-slate-600">
18
- Or
19
- <a href="/login" class="font-medium text-slate-900 hover:underline">sign in to existing account</a>
20
-
21
  </p>
22
  </div>
23
 
24
- <form method="post" action="/account/register" class="mt-8 space-y-6">
25
  <AntiforgeryToken />
26
 
27
- <div class="space-y-4 rounded-md shadow-sm">
28
  <div class="grid grid-cols-2 gap-4">
29
  <div>
30
  <label for="firstName" class="block text-sm font-medium text-slate-700 mb-1">First Name</label>
@@ -54,7 +55,7 @@
54
  </div>
55
  <div class="relative">
56
  <label for="password" class="block text-sm font-medium text-slate-700 mb-1">Password</label>
57
- <input id="password" name="Password" type="@(showPassword ? "text" : "password")" required placeholder="Min 8 characters (A-Z, a-z, 0-9, special)"
58
  class="w-full rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-900 placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-slate-900 focus:border-transparent transition-all pr-10" />
59
  <button type="button" @onclick="TogglePassword" class="absolute right-3 top-[28px] text-slate-400 hover:text-slate-600 focus:outline-none">
60
  <i data-lucide="@(showPassword ? "eye-off" : "eye")" class="h-5 w-5"></i>
@@ -70,7 +71,7 @@
70
  }
71
 
72
  <div>
73
- <button type="submit" class="group relative flex w-full justify-center rounded-lg bg-violet-600 px-4 py-2.5 text-sm font-medium text-white hover:bg-violet-700 transition-colors shadow-sm">
74
  Register
75
  </button>
76
  </div>
 
5
  @attribute [Microsoft.AspNetCore.Authorization.AllowAnonymous]
6
  @inject IJSRuntime JS
7
 
8
+ <PageTitle>Register - Taskify</PageTitle>
9
 
10
+ <div class="flex min-h-screen items-center justify-center bg-slate-50 px-4 py-10 sm:px-6 lg:px-8">
11
+ <div class="w-full max-w-md space-y-7 rounded-xl border border-slate-200 bg-white p-8 shadow-sm">
12
  <div>
13
  <div class="flex justify-center">
14
+ <span class="flex h-12 w-12 items-center justify-center rounded-xl bg-violet-600 text-white shadow-sm">
15
+ <i data-lucide="flame" class="h-6 w-6 fill-current"></i>
16
+ </span>
17
  </div>
18
+ <h2 class="mt-5 text-center text-2xl font-bold text-slate-900">Create your Taskify account</h2>
19
  <p class="mt-2 text-center text-sm text-slate-600">
20
+ Already have access?
21
+ <a href="/login" class="font-semibold text-violet-600 hover:underline">Sign in</a>
 
22
  </p>
23
  </div>
24
 
25
+ <form method="post" action="/account/register" class="space-y-5">
26
  <AntiforgeryToken />
27
 
28
+ <div class="space-y-4">
29
  <div class="grid grid-cols-2 gap-4">
30
  <div>
31
  <label for="firstName" class="block text-sm font-medium text-slate-700 mb-1">First Name</label>
 
55
  </div>
56
  <div class="relative">
57
  <label for="password" class="block text-sm font-medium text-slate-700 mb-1">Password</label>
58
+ <input id="password" name="Password" type="@(showPassword ? "text" : "password")" required placeholder="Password"
59
  class="w-full rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-900 placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-slate-900 focus:border-transparent transition-all pr-10" />
60
  <button type="button" @onclick="TogglePassword" class="absolute right-3 top-[28px] text-slate-400 hover:text-slate-600 focus:outline-none">
61
  <i data-lucide="@(showPassword ? "eye-off" : "eye")" class="h-5 w-5"></i>
 
71
  }
72
 
73
  <div>
74
+ <button type="submit" class="group relative flex w-full justify-center rounded-lg bg-violet-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm transition-colors hover:bg-violet-700">
75
  Register
76
  </button>
77
  </div>
TaskTrackingSystem.WebApp/Components/Pages/Features/Auth/ResetPassword.razor CHANGED
@@ -3,25 +3,28 @@
3
  @rendermode @(new InteractiveServerRenderMode(prerender: false))
4
  @attribute [Microsoft.AspNetCore.Authorization.AllowAnonymous]
5
  @inject NavigationManager Navigation
 
6
 
7
- <PageTitle>Reset Password - TaskFlow</PageTitle>
8
 
9
- <div class="flex min-h-screen items-center justify-center bg-slate-50 py-12 px-4 sm:px-6 lg:px-8">
10
- <div class="w-full max-w-md space-y-8 bg-white p-8 rounded-2xl border border-slate-200 shadow-xl">
11
  <div>
12
  <div class="flex justify-center">
13
- <span class="flex h-12 w-12 items-center justify-center rounded-xl bg-violet-600 text-white font-bold text-xl">T</span>
 
 
14
  </div>
15
- <h2 class="mt-6 text-center text-3xl font-extrabold text-slate-900">Reset your password</h2>
16
  <p class="mt-2 text-center text-sm text-slate-600">
17
  Use the recovery code configured in the API to set a new password.
18
  </p>
19
  </div>
20
 
21
- <form method="post" action="/account/reset-password" class="mt-8 space-y-6">
22
  <AntiforgeryToken />
23
 
24
- <div class="space-y-4 rounded-md shadow-sm">
25
  <div>
26
  <label for="usernameOrEmail" class="block text-sm font-medium text-slate-700 mb-1">Username or Email</label>
27
  <input id="usernameOrEmail" name="UsernameOrEmail" required placeholder="Enter username or email"
@@ -48,7 +51,7 @@
48
 
49
  <div class="flex items-center justify-between">
50
  <a href="/login" class="text-sm font-medium text-violet-600 hover:underline">Back to sign in</a>
51
- <button type="submit" class="group relative flex justify-center rounded-lg bg-violet-600 px-4 py-2.5 text-sm font-medium text-white hover:bg-violet-700 transition-colors shadow-sm">
52
  Reset Password
53
  </button>
54
  </div>
@@ -62,4 +65,9 @@
62
 
63
  private bool ShowError => !string.IsNullOrWhiteSpace(Error);
64
  private string ErrorMessage => string.IsNullOrWhiteSpace(Error) ? "Unable to reset password." : Error;
 
 
 
 
 
65
  }
 
3
  @rendermode @(new InteractiveServerRenderMode(prerender: false))
4
  @attribute [Microsoft.AspNetCore.Authorization.AllowAnonymous]
5
  @inject NavigationManager Navigation
6
+ @inject IJSRuntime JS
7
 
8
+ <PageTitle>Reset Password - Taskify</PageTitle>
9
 
10
+ <div class="flex min-h-screen items-center justify-center bg-slate-50 px-4 py-10 sm:px-6 lg:px-8">
11
+ <div class="w-full max-w-md space-y-7 rounded-xl border border-slate-200 bg-white p-8 shadow-sm">
12
  <div>
13
  <div class="flex justify-center">
14
+ <span class="flex h-12 w-12 items-center justify-center rounded-xl bg-violet-600 text-white shadow-sm">
15
+ <i data-lucide="flame" class="h-6 w-6 fill-current"></i>
16
+ </span>
17
  </div>
18
+ <h2 class="mt-5 text-center text-2xl font-bold text-slate-900">Reset your password</h2>
19
  <p class="mt-2 text-center text-sm text-slate-600">
20
  Use the recovery code configured in the API to set a new password.
21
  </p>
22
  </div>
23
 
24
+ <form method="post" action="/account/reset-password" class="space-y-5">
25
  <AntiforgeryToken />
26
 
27
+ <div class="space-y-4">
28
  <div>
29
  <label for="usernameOrEmail" class="block text-sm font-medium text-slate-700 mb-1">Username or Email</label>
30
  <input id="usernameOrEmail" name="UsernameOrEmail" required placeholder="Enter username or email"
 
51
 
52
  <div class="flex items-center justify-between">
53
  <a href="/login" class="text-sm font-medium text-violet-600 hover:underline">Back to sign in</a>
54
+ <button type="submit" class="group relative flex justify-center rounded-lg bg-violet-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm transition-colors hover:bg-violet-700">
55
  Reset Password
56
  </button>
57
  </div>
 
65
 
66
  private bool ShowError => !string.IsNullOrWhiteSpace(Error);
67
  private string ErrorMessage => string.IsNullOrWhiteSpace(Error) ? "Unable to reset password." : Error;
68
+
69
+ protected override async Task OnAfterRenderAsync(bool firstRender)
70
+ {
71
+ await JS.InvokeVoidAsync("initIcons");
72
+ }
73
  }
TaskTrackingSystem.WebApp/Components/Pages/Features/Dashboard/EmployeeDashboard.razor CHANGED
@@ -12,18 +12,18 @@
12
 
13
  <div class="min-h-screen bg-slate-50 px-4 py-6 text-slate-900 sm:px-6 lg:px-8">
14
  <div class="mx-auto max-w-[1440px] space-y-6">
15
- <div class="rounded-[32px] border border-slate-200 bg-white px-8 py-8 shadow-[0_18px_50px_rgba(15,23,42,0.08)]">
16
  <div class="flex flex-col gap-6 lg:flex-row lg:items-end lg:justify-between">
17
  <div>
18
- <p class="text-sm font-semibold uppercase tracking-[0.22em] text-violet-700">Team Member</p>
19
- <h2 class="mt-2 text-4xl font-bold tracking-tight text-slate-950">My Work</h2>
20
  <p class="mt-3 max-w-2xl text-sm text-slate-600">
21
  Tasks, deadlines, and your progress in one place.
22
  </p>
23
  </div>
24
 
25
  <div class="flex flex-wrap gap-3">
26
- <a href="/tasks/all" class="inline-flex items-center rounded-2xl bg-violet-700 px-5 py-3 text-sm font-semibold text-white shadow-lg shadow-violet-900/20 transition-colors hover:bg-violet-600">
27
  <i data-lucide="check-square" class="mr-2 h-4 w-4"></i>
28
  View Tasks
29
  </a>
@@ -37,7 +37,7 @@
37
 
38
  @if (isLoading)
39
  {
40
- <div class="rounded-[28px] border border-slate-200 bg-white px-6 py-12 text-center shadow-[0_18px_50px_rgba(15,23,42,0.08)]">
41
  <div class="mx-auto flex items-center justify-center gap-3 text-slate-500">
42
  <svg class="h-5 w-5 animate-spin" viewBox="0 0 24 24">
43
  <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none"></circle>
@@ -49,7 +49,7 @@
49
  }
50
  else if (!string.IsNullOrWhiteSpace(errorMessage))
51
  {
52
- <div class="rounded-[28px] border border-rose-200 bg-rose-50 px-6 py-5 text-sm text-rose-700 shadow-[0_18px_50px_rgba(15,23,42,0.08)]">
53
  @errorMessage
54
  </div>
55
  }
@@ -58,7 +58,7 @@
58
  <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-5">
59
  @foreach (var metric in SummaryMetrics)
60
  {
61
- <div class="rounded-[24px] border border-slate-200 bg-white p-5 shadow-[0_18px_50px_rgba(15,23,42,0.08)] transition-transform duration-200 hover:-translate-y-1">
62
  <div class="flex items-center justify-between">
63
  <div class="@metric.IconWrapClass">
64
  <i data-lucide="@metric.Icon" class="h-5 w-5"></i>
@@ -73,14 +73,14 @@
73
 
74
  <div class="grid grid-cols-1 gap-5 xl:grid-cols-[minmax(0,1.7fr)_minmax(340px,1fr)]">
75
  <div class="space-y-5">
76
- <div class="overflow-hidden rounded-[28px] border border-slate-200 bg-white shadow-[0_18px_50px_rgba(15,23,42,0.08)]">
77
  <div class="flex items-center justify-between gap-4 border-b border-slate-100 px-6 py-5">
78
  <div class="flex items-center gap-3">
79
  <span class="inline-flex h-8 w-8 items-center justify-center rounded-full border border-violet-200 bg-violet-50 text-violet-700">
80
  <i data-lucide="timer-reset" class="h-4 w-4"></i>
81
  </span>
82
  <div>
83
- <h3 class="text-2xl font-bold text-slate-950">In Progress</h3>
84
  <p class="text-sm text-slate-500">Tasks currently being worked on</p>
85
  </div>
86
  </div>
@@ -112,14 +112,14 @@
112
  </div>
113
  </div>
114
 
115
- <div class="overflow-hidden rounded-[28px] border border-slate-200 bg-white shadow-[0_18px_50px_rgba(15,23,42,0.08)]">
116
  <div class="flex items-center justify-between gap-4 border-b border-slate-100 px-6 py-5">
117
  <div class="flex items-center gap-3">
118
  <span class="inline-flex h-8 w-8 items-center justify-center rounded-full border border-sky-200 bg-sky-50 text-sky-600">
119
  <i data-lucide="clock-3" class="h-4 w-4"></i>
120
  </span>
121
  <div>
122
- <h3 class="text-2xl font-bold text-slate-950">To Do & Upcoming</h3>
123
  <p class="text-sm text-slate-500">Tasks waiting for action or coming up soon</p>
124
  </div>
125
  </div>
@@ -151,14 +151,14 @@
151
  </div>
152
  </div>
153
 
154
- <div class="overflow-hidden rounded-[28px] border border-slate-200 bg-white shadow-[0_18px_50px_rgba(15,23,42,0.08)]">
155
  <div class="flex items-center justify-between gap-4 border-b border-rose-100 px-6 py-5">
156
  <div class="flex items-center gap-3">
157
  <span class="inline-flex h-8 w-8 items-center justify-center rounded-full border border-rose-200 bg-rose-50 text-rose-600">
158
  <i data-lucide="triangle-alert" class="h-4 w-4"></i>
159
  </span>
160
  <div>
161
- <h3 class="text-2xl font-bold text-slate-950">Overdue</h3>
162
  <p class="text-sm text-slate-500">Tasks past their due date</p>
163
  </div>
164
  </div>
@@ -194,14 +194,14 @@
194
  </div>
195
 
196
  <div class="space-y-5">
197
- <div class="rounded-[28px] border border-slate-200 bg-white p-6 shadow-[0_18px_50px_rgba(15,23,42,0.08)]">
198
  <div class="flex items-start justify-between gap-4">
199
  <div>
200
  <div class="flex items-center gap-3">
201
  <button type="button" @onclick="PrevMonth" class="inline-flex h-8 w-8 items-center justify-center rounded-xl border border-slate-200 bg-white text-slate-600 transition-colors hover:bg-slate-50">
202
  <i data-lucide="chevron-left" class="h-4 w-4"></i>
203
  </button>
204
- <h3 class="text-2xl font-bold text-slate-950 min-w-[120px]">@CalendarMonthLabel</h3>
205
  <button type="button" @onclick="NextMonth" class="inline-flex h-8 w-8 items-center justify-center rounded-xl border border-slate-200 bg-white text-slate-600 transition-colors hover:bg-slate-50">
206
  <i data-lucide="chevron-right" class="h-4 w-4"></i>
207
  </button>
@@ -239,9 +239,9 @@
239
  </div>
240
  </div>
241
 
242
- <div class="overflow-hidden rounded-[28px] border border-slate-200 bg-white shadow-[0_18px_50px_rgba(15,23,42,0.08)]">
243
  <div class="border-b border-slate-100 px-6 py-5">
244
- <h3 class="text-2xl font-bold text-slate-950">Related Tasks</h3>
245
  <p class="mt-1 text-sm text-slate-500">Tasks marked on @SelectedDateLabel</p>
246
  </div>
247
 
@@ -446,7 +446,7 @@
446
  private string GetStatusColor(AppTaskStatus statusId) => statusId switch
447
  {
448
  AppTaskStatus.Todo => "#22d3ee",
449
- AppTaskStatus.InProgress => "#8b5cf6",
450
  AppTaskStatus.Done => "#10b981",
451
  _ => "#94a3b8"
452
  };
 
12
 
13
  <div class="min-h-screen bg-slate-50 px-4 py-6 text-slate-900 sm:px-6 lg:px-8">
14
  <div class="mx-auto max-w-[1440px] space-y-6">
15
+ <div class="rounded-xl border border-slate-200 bg-white px-8 py-8 shadow-sm">
16
  <div class="flex flex-col gap-6 lg:flex-row lg:items-end lg:justify-between">
17
  <div>
18
+ <p class="text-sm font-semibold uppercase tracking-[0.08em] text-violet-700">Team Member</p>
19
+ <h2 class="mt-2 text-3xl font-bold tracking-tight text-slate-950">My Work</h2>
20
  <p class="mt-3 max-w-2xl text-sm text-slate-600">
21
  Tasks, deadlines, and your progress in one place.
22
  </p>
23
  </div>
24
 
25
  <div class="flex flex-wrap gap-3">
26
+ <a href="/tasks/all" class="inline-flex items-center rounded-2xl bg-violet-700 px-5 py-3 text-sm font-semibold text-white shadow-sm transition-colors hover:bg-violet-600">
27
  <i data-lucide="check-square" class="mr-2 h-4 w-4"></i>
28
  View Tasks
29
  </a>
 
37
 
38
  @if (isLoading)
39
  {
40
+ <div class="rounded-xl border border-slate-200 bg-white px-6 py-12 text-center shadow-sm">
41
  <div class="mx-auto flex items-center justify-center gap-3 text-slate-500">
42
  <svg class="h-5 w-5 animate-spin" viewBox="0 0 24 24">
43
  <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none"></circle>
 
49
  }
50
  else if (!string.IsNullOrWhiteSpace(errorMessage))
51
  {
52
+ <div class="rounded-xl border border-rose-200 bg-rose-50 px-6 py-5 text-sm text-rose-700 shadow-sm">
53
  @errorMessage
54
  </div>
55
  }
 
58
  <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-5">
59
  @foreach (var metric in SummaryMetrics)
60
  {
61
+ <div class="rounded-xl border border-slate-200 bg-white p-5 shadow-sm transition-transform duration-200 hover:-translate-y-1">
62
  <div class="flex items-center justify-between">
63
  <div class="@metric.IconWrapClass">
64
  <i data-lucide="@metric.Icon" class="h-5 w-5"></i>
 
73
 
74
  <div class="grid grid-cols-1 gap-5 xl:grid-cols-[minmax(0,1.7fr)_minmax(340px,1fr)]">
75
  <div class="space-y-5">
76
+ <div class="overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm">
77
  <div class="flex items-center justify-between gap-4 border-b border-slate-100 px-6 py-5">
78
  <div class="flex items-center gap-3">
79
  <span class="inline-flex h-8 w-8 items-center justify-center rounded-full border border-violet-200 bg-violet-50 text-violet-700">
80
  <i data-lucide="timer-reset" class="h-4 w-4"></i>
81
  </span>
82
  <div>
83
+ <h3 class="text-xl font-bold text-slate-950">In Progress</h3>
84
  <p class="text-sm text-slate-500">Tasks currently being worked on</p>
85
  </div>
86
  </div>
 
112
  </div>
113
  </div>
114
 
115
+ <div class="overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm">
116
  <div class="flex items-center justify-between gap-4 border-b border-slate-100 px-6 py-5">
117
  <div class="flex items-center gap-3">
118
  <span class="inline-flex h-8 w-8 items-center justify-center rounded-full border border-sky-200 bg-sky-50 text-sky-600">
119
  <i data-lucide="clock-3" class="h-4 w-4"></i>
120
  </span>
121
  <div>
122
+ <h3 class="text-xl font-bold text-slate-950">To Do & Upcoming</h3>
123
  <p class="text-sm text-slate-500">Tasks waiting for action or coming up soon</p>
124
  </div>
125
  </div>
 
151
  </div>
152
  </div>
153
 
154
+ <div class="overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm">
155
  <div class="flex items-center justify-between gap-4 border-b border-rose-100 px-6 py-5">
156
  <div class="flex items-center gap-3">
157
  <span class="inline-flex h-8 w-8 items-center justify-center rounded-full border border-rose-200 bg-rose-50 text-rose-600">
158
  <i data-lucide="triangle-alert" class="h-4 w-4"></i>
159
  </span>
160
  <div>
161
+ <h3 class="text-xl font-bold text-slate-950">Overdue</h3>
162
  <p class="text-sm text-slate-500">Tasks past their due date</p>
163
  </div>
164
  </div>
 
194
  </div>
195
 
196
  <div class="space-y-5">
197
+ <div class="rounded-xl border border-slate-200 bg-white p-6 shadow-sm">
198
  <div class="flex items-start justify-between gap-4">
199
  <div>
200
  <div class="flex items-center gap-3">
201
  <button type="button" @onclick="PrevMonth" class="inline-flex h-8 w-8 items-center justify-center rounded-xl border border-slate-200 bg-white text-slate-600 transition-colors hover:bg-slate-50">
202
  <i data-lucide="chevron-left" class="h-4 w-4"></i>
203
  </button>
204
+ <h3 class="text-xl font-bold text-slate-950 min-w-[120px]">@CalendarMonthLabel</h3>
205
  <button type="button" @onclick="NextMonth" class="inline-flex h-8 w-8 items-center justify-center rounded-xl border border-slate-200 bg-white text-slate-600 transition-colors hover:bg-slate-50">
206
  <i data-lucide="chevron-right" class="h-4 w-4"></i>
207
  </button>
 
239
  </div>
240
  </div>
241
 
242
+ <div class="overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm">
243
  <div class="border-b border-slate-100 px-6 py-5">
244
+ <h3 class="text-xl font-bold text-slate-950">Related Tasks</h3>
245
  <p class="mt-1 text-sm text-slate-500">Tasks marked on @SelectedDateLabel</p>
246
  </div>
247
 
 
446
  private string GetStatusColor(AppTaskStatus statusId) => statusId switch
447
  {
448
  AppTaskStatus.Todo => "#22d3ee",
449
+ AppTaskStatus.InProgress => "#3b82f6",
450
  AppTaskStatus.Done => "#10b981",
451
  _ => "#94a3b8"
452
  };
TaskTrackingSystem.WebApp/Components/Pages/Features/Dashboard/Home.razor CHANGED
@@ -19,11 +19,11 @@
19
  <PageTitle>Dashboard</PageTitle>
20
 
21
  <div class="space-y-8 text-slate-900">
22
- <div class="rounded-[32px] border border-slate-200 bg-white px-8 py-8 text-slate-900 shadow-sm shadow-slate-200/60">
23
  <div class="flex flex-col gap-6 lg:flex-row lg:items-end lg:justify-between">
24
  <div>
25
- <p class="text-sm font-semibold uppercase tracking-[0.22em] text-violet-600">Overview</p>
26
- <h2 class="mt-2 text-4xl font-bold tracking-tight text-slate-900">Welcome back, @currentUserFullName</h2>
27
  <p class="mt-3 max-w-2xl text-sm text-slate-600">
28
  Projects, users, and team progress at a glance.
29
  </p>
@@ -31,7 +31,7 @@
31
  <div class="flex flex-wrap gap-3">
32
  @if (canCreateTask)
33
  {
34
- <a href="/tasks/all" class="inline-flex items-center rounded-2xl bg-violet-600 px-5 py-3 text-sm font-semibold text-white shadow-lg shadow-violet-900/20 transition-colors hover:bg-violet-500">
35
  <i data-lucide="plus" class="mr-2 h-4 w-4"></i>
36
  New Task
37
  </a>
@@ -45,7 +45,7 @@
45
  </div>
46
 
47
  <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4">
48
- <div class="rounded-[24px] border border-slate-200 bg-white p-5 text-slate-900 shadow-sm shadow-slate-200/50">
49
  <div class="flex items-center justify-between">
50
  <div class="flex h-12 w-12 items-center justify-center rounded-2xl bg-violet-500 text-white">
51
  <i data-lucide="users" class="h-5 w-5"></i>
@@ -56,7 +56,7 @@
56
  <p class="mt-2 text-sm text-slate-500">Total users</p>
57
  </div>
58
 
59
- <div class="rounded-[24px] border border-slate-200 bg-white p-5 text-slate-900 shadow-sm shadow-slate-200/50">
60
  <div class="flex items-center justify-between">
61
  <div class="flex h-12 w-12 items-center justify-center rounded-2xl bg-sky-500 text-white">
62
  <i data-lucide="folder-kanban" class="h-5 w-5"></i>
@@ -67,7 +67,7 @@
67
  <p class="mt-2 text-sm text-slate-500">Total projects</p>
68
  </div>
69
 
70
- <div class="rounded-[24px] border border-slate-200 bg-white p-5 text-slate-900 shadow-sm shadow-slate-200/50">
71
  <div class="flex items-center justify-between">
72
  <div class="flex h-12 w-12 items-center justify-center rounded-2xl bg-emerald-500 text-white">
73
  <i data-lucide="check-square" class="h-5 w-5"></i>
@@ -78,7 +78,7 @@
78
  <p class="mt-2 text-sm text-slate-500">Active tasks</p>
79
  </div>
80
 
81
- <div class="rounded-[24px] border border-slate-200 bg-white p-5 text-slate-900 shadow-sm shadow-slate-200/50">
82
  <div class="flex items-center justify-between">
83
  <div class="flex h-12 w-12 items-center justify-center rounded-2xl bg-amber-500 text-white">
84
  <i data-lucide="trending-up" class="h-5 w-5"></i>
@@ -91,10 +91,10 @@
91
  </div>
92
 
93
  <div class="grid grid-cols-1 gap-5 xl:grid-cols-[minmax(0,1.7fr)_minmax(340px,1fr)]">
94
- <div class="rounded-[28px] border border-slate-200 bg-white p-6 text-slate-900 shadow-sm shadow-slate-200/60">
95
  <div class="flex items-start justify-between gap-4">
96
  <div>
97
- <h3 class="text-2xl font-bold text-slate-900">Project Activity</h3>
98
  <p class="mt-1 text-sm text-slate-500">Active vs completed tasks per month.</p>
99
  </div>
100
  <div class="text-sm text-slate-500">@ActivityRangeLabel</div>
@@ -105,8 +105,8 @@
105
  <svg viewBox="0 0 760 340" class="h-[340px] w-full">
106
  <defs>
107
  <linearGradient id="activityFill" x1="0" x2="0" y1="0" y2="1">
108
- <stop offset="0%" stop-color="#7c3aed" stop-opacity="0.18" />
109
- <stop offset="100%" stop-color="#7c3aed" stop-opacity="0" />
110
  </linearGradient>
111
  </defs>
112
 
@@ -123,14 +123,14 @@
123
  }
124
 
125
  <path d="@ActivityAreaPath" fill="url(#activityFill)" />
126
- <path d="@ActivityLinePath" fill="none" stroke="#7c3aed" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" />
127
 
128
  @for (var i = 0; i < ActivityMonths.Count; i++)
129
  {
130
  var index = i;
131
  var point = ActivityPoints[i];
132
  <circle cx="@point.X" cy="@point.Y" r="5"
133
- fill="#7c3aed"
134
  stroke="#ddd6fe"
135
  stroke-width="3"
136
  @onmouseenter="() => SetHoveredActivity(index)"
@@ -158,8 +158,8 @@
158
  </div>
159
  </div>
160
 
161
- <div class="rounded-[28px] border border-slate-200 bg-white p-6 text-slate-900 shadow-sm shadow-slate-200/60">
162
- <h3 class="text-2xl font-bold text-slate-900">Task Distribution</h3>
163
  <p class="mt-1 text-sm text-slate-500">@TotalTasksCount total tasks</p>
164
 
165
  <div class="mt-4 flex justify-center">
@@ -183,10 +183,10 @@
183
  </div>
184
 
185
  <div class="grid grid-cols-1 gap-5 xl:grid-cols-[minmax(0,1.25fr)_minmax(340px,0.75fr)]">
186
- <div class="rounded-[28px] border border-slate-200 bg-white p-6 shadow-sm shadow-slate-200/60">
187
  <div class="flex items-center justify-between gap-4">
188
  <div>
189
- <h3 class="text-2xl font-bold text-slate-900">All Projects</h3>
190
  <p class="mt-1 text-sm text-slate-500">A quick snapshot of the current project list.</p>
191
  </div>
192
  <a href="/projects/all" class="inline-flex items-center rounded-2xl border border-slate-200 bg-white px-4 py-2.5 text-sm font-semibold text-violet-700 shadow-sm transition-colors hover:bg-violet-50">
@@ -221,10 +221,10 @@
221
  </div>
222
  </div>
223
 
224
- <div class="rounded-[28px] border border-slate-200 bg-white p-6 text-slate-900 shadow-sm shadow-slate-200/60">
225
  <div class="flex items-start justify-between gap-4">
226
  <div>
227
- <h3 class="text-2xl font-bold text-slate-900">Calendar</h3>
228
  <p class="mt-1 text-sm text-slate-500">Today and upcoming project starts.</p>
229
  </div>
230
  <div class="text-right">
@@ -272,10 +272,10 @@
272
  </div>
273
  </div>
274
 
275
- <div class="rounded-[28px] border border-slate-200 bg-white p-5 text-slate-900 shadow-sm shadow-slate-200/60">
276
  <div class="flex flex-col gap-3 lg:flex-row lg:items-end lg:justify-between">
277
  <div>
278
- <h3 class="text-2xl font-bold text-slate-900">Project Timeline</h3>
279
  <p class="mt-1 text-sm text-slate-500">Browse every project in a gantt view and adjust the month window below.</p>
280
  </div>
281
  <a href="/projects/all" class="inline-flex items-center rounded-2xl border border-slate-200 bg-white px-4 py-2.5 text-sm font-semibold text-violet-700 shadow-sm transition-colors hover:bg-violet-50">
@@ -283,7 +283,7 @@
283
  </a>
284
  </div>
285
 
286
- <div class="mt-4 flex flex-wrap items-end gap-3 rounded-3xl border border-slate-200 bg-slate-50 px-3 py-3">
287
  <div class="flex flex-col gap-1">
288
  <label class="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400">From</label>
289
  <input type="month"
@@ -449,7 +449,7 @@
449
  var pieData = new[]
450
  {
451
  new { name = "Completed", y = completedCount, color = "#10b981" },
452
- new { name = "In Progress", y = inProgressCount, color = "#8b5cf6" },
453
  new { name = "To Do", y = todoCount, color = "#06b6d4" },
454
  new { name = "Overdue", y = overdueCount, color = "#ef4444" }
455
  };
@@ -624,7 +624,7 @@
624
  overdueCount
625
  };
626
 
627
- var colors = new[] { "#10b981", "#8b5cf6", "#06b6d4", "#ef4444" };
628
 
629
  // 2. Adjust total so it maps 100% cleanly to the math displayed
630
  var total = Math.Max(TotalTasksCount, 1);
@@ -907,7 +907,7 @@
907
  {
908
  var palette = new[]
909
  {
910
- "#7c3aed",
911
  "#0ea5e9",
912
  "#10b981",
913
  "#f59e0b",
 
19
  <PageTitle>Dashboard</PageTitle>
20
 
21
  <div class="space-y-8 text-slate-900">
22
+ <div class="rounded-xl border border-slate-200 bg-white px-8 py-8 text-slate-900 shadow-sm shadow-slate-200/60">
23
  <div class="flex flex-col gap-6 lg:flex-row lg:items-end lg:justify-between">
24
  <div>
25
+ <p class="text-sm font-semibold uppercase tracking-[0.08em] text-violet-600">Overview</p>
26
+ <h2 class="mt-2 text-3xl font-bold tracking-tight text-slate-900">Welcome back, @currentUserFullName</h2>
27
  <p class="mt-3 max-w-2xl text-sm text-slate-600">
28
  Projects, users, and team progress at a glance.
29
  </p>
 
31
  <div class="flex flex-wrap gap-3">
32
  @if (canCreateTask)
33
  {
34
+ <a href="/tasks/all" class="inline-flex items-center rounded-2xl bg-violet-600 px-5 py-3 text-sm font-semibold text-white shadow-sm transition-colors hover:bg-violet-500">
35
  <i data-lucide="plus" class="mr-2 h-4 w-4"></i>
36
  New Task
37
  </a>
 
45
  </div>
46
 
47
  <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4">
48
+ <div class="rounded-xl border border-slate-200 bg-white p-5 text-slate-900 shadow-sm shadow-slate-200/50">
49
  <div class="flex items-center justify-between">
50
  <div class="flex h-12 w-12 items-center justify-center rounded-2xl bg-violet-500 text-white">
51
  <i data-lucide="users" class="h-5 w-5"></i>
 
56
  <p class="mt-2 text-sm text-slate-500">Total users</p>
57
  </div>
58
 
59
+ <div class="rounded-xl border border-slate-200 bg-white p-5 text-slate-900 shadow-sm shadow-slate-200/50">
60
  <div class="flex items-center justify-between">
61
  <div class="flex h-12 w-12 items-center justify-center rounded-2xl bg-sky-500 text-white">
62
  <i data-lucide="folder-kanban" class="h-5 w-5"></i>
 
67
  <p class="mt-2 text-sm text-slate-500">Total projects</p>
68
  </div>
69
 
70
+ <div class="rounded-xl border border-slate-200 bg-white p-5 text-slate-900 shadow-sm shadow-slate-200/50">
71
  <div class="flex items-center justify-between">
72
  <div class="flex h-12 w-12 items-center justify-center rounded-2xl bg-emerald-500 text-white">
73
  <i data-lucide="check-square" class="h-5 w-5"></i>
 
78
  <p class="mt-2 text-sm text-slate-500">Active tasks</p>
79
  </div>
80
 
81
+ <div class="rounded-xl border border-slate-200 bg-white p-5 text-slate-900 shadow-sm shadow-slate-200/50">
82
  <div class="flex items-center justify-between">
83
  <div class="flex h-12 w-12 items-center justify-center rounded-2xl bg-amber-500 text-white">
84
  <i data-lucide="trending-up" class="h-5 w-5"></i>
 
91
  </div>
92
 
93
  <div class="grid grid-cols-1 gap-5 xl:grid-cols-[minmax(0,1.7fr)_minmax(340px,1fr)]">
94
+ <div class="rounded-xl border border-slate-200 bg-white p-6 text-slate-900 shadow-sm shadow-slate-200/60">
95
  <div class="flex items-start justify-between gap-4">
96
  <div>
97
+ <h3 class="text-xl font-bold text-slate-900">Project Activity</h3>
98
  <p class="mt-1 text-sm text-slate-500">Active vs completed tasks per month.</p>
99
  </div>
100
  <div class="text-sm text-slate-500">@ActivityRangeLabel</div>
 
105
  <svg viewBox="0 0 760 340" class="h-[340px] w-full">
106
  <defs>
107
  <linearGradient id="activityFill" x1="0" x2="0" y1="0" y2="1">
108
+ <stop offset="0%" stop-color="#2563eb" stop-opacity="0.18" />
109
+ <stop offset="100%" stop-color="#2563eb" stop-opacity="0" />
110
  </linearGradient>
111
  </defs>
112
 
 
123
  }
124
 
125
  <path d="@ActivityAreaPath" fill="url(#activityFill)" />
126
+ <path d="@ActivityLinePath" fill="none" stroke="#2563eb" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" />
127
 
128
  @for (var i = 0; i < ActivityMonths.Count; i++)
129
  {
130
  var index = i;
131
  var point = ActivityPoints[i];
132
  <circle cx="@point.X" cy="@point.Y" r="5"
133
+ fill="#2563eb"
134
  stroke="#ddd6fe"
135
  stroke-width="3"
136
  @onmouseenter="() => SetHoveredActivity(index)"
 
158
  </div>
159
  </div>
160
 
161
+ <div class="rounded-xl border border-slate-200 bg-white p-6 text-slate-900 shadow-sm shadow-slate-200/60">
162
+ <h3 class="text-xl font-bold text-slate-900">Task Distribution</h3>
163
  <p class="mt-1 text-sm text-slate-500">@TotalTasksCount total tasks</p>
164
 
165
  <div class="mt-4 flex justify-center">
 
183
  </div>
184
 
185
  <div class="grid grid-cols-1 gap-5 xl:grid-cols-[minmax(0,1.25fr)_minmax(340px,0.75fr)]">
186
+ <div class="rounded-xl border border-slate-200 bg-white p-6 shadow-sm shadow-slate-200/60">
187
  <div class="flex items-center justify-between gap-4">
188
  <div>
189
+ <h3 class="text-xl font-bold text-slate-900">All Projects</h3>
190
  <p class="mt-1 text-sm text-slate-500">A quick snapshot of the current project list.</p>
191
  </div>
192
  <a href="/projects/all" class="inline-flex items-center rounded-2xl border border-slate-200 bg-white px-4 py-2.5 text-sm font-semibold text-violet-700 shadow-sm transition-colors hover:bg-violet-50">
 
221
  </div>
222
  </div>
223
 
224
+ <div class="rounded-xl border border-slate-200 bg-white p-6 text-slate-900 shadow-sm shadow-slate-200/60">
225
  <div class="flex items-start justify-between gap-4">
226
  <div>
227
+ <h3 class="text-xl font-bold text-slate-900">Calendar</h3>
228
  <p class="mt-1 text-sm text-slate-500">Today and upcoming project starts.</p>
229
  </div>
230
  <div class="text-right">
 
272
  </div>
273
  </div>
274
 
275
+ <div class="rounded-xl border border-slate-200 bg-white p-5 text-slate-900 shadow-sm shadow-slate-200/60">
276
  <div class="flex flex-col gap-3 lg:flex-row lg:items-end lg:justify-between">
277
  <div>
278
+ <h3 class="text-xl font-bold text-slate-900">Project Timeline</h3>
279
  <p class="mt-1 text-sm text-slate-500">Browse every project in a gantt view and adjust the month window below.</p>
280
  </div>
281
  <a href="/projects/all" class="inline-flex items-center rounded-2xl border border-slate-200 bg-white px-4 py-2.5 text-sm font-semibold text-violet-700 shadow-sm transition-colors hover:bg-violet-50">
 
283
  </a>
284
  </div>
285
 
286
+ <div class="mt-4 flex flex-wrap items-end gap-3 rounded-xl border border-slate-200 bg-slate-50 px-3 py-3">
287
  <div class="flex flex-col gap-1">
288
  <label class="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400">From</label>
289
  <input type="month"
 
449
  var pieData = new[]
450
  {
451
  new { name = "Completed", y = completedCount, color = "#10b981" },
452
+ new { name = "In Progress", y = inProgressCount, color = "#3b82f6" },
453
  new { name = "To Do", y = todoCount, color = "#06b6d4" },
454
  new { name = "Overdue", y = overdueCount, color = "#ef4444" }
455
  };
 
624
  overdueCount
625
  };
626
 
627
+ var colors = new[] { "#10b981", "#3b82f6", "#06b6d4", "#ef4444" };
628
 
629
  // 2. Adjust total so it maps 100% cleanly to the math displayed
630
  var total = Math.Max(TotalTasksCount, 1);
 
907
  {
908
  var palette = new[]
909
  {
910
+ "#2563eb",
911
  "#0ea5e9",
912
  "#10b981",
913
  "#f59e0b",
TaskTrackingSystem.WebApp/Components/Pages/Features/Dashboard/ManagerDashboard.razor CHANGED
@@ -11,11 +11,11 @@
11
 
12
  <div class="min-h-screen bg-slate-50 px-4 py-6 text-slate-900 sm:px-6 lg:px-8">
13
  <div class="mx-auto max-w-[1440px] space-y-6">
14
- <div class="rounded-[32px] border border-slate-200 bg-white px-8 py-8 shadow-[0_18px_50px_rgba(15,23,42,0.08)]">
15
  <div class="flex flex-col gap-6 lg:flex-row lg:items-end lg:justify-between">
16
  <div>
17
- <p class="text-sm font-semibold uppercase tracking-[0.22em] text-violet-700">Team View</p>
18
- <h2 class="mt-2 text-4xl font-bold tracking-tight text-slate-950">My Projects</h2>
19
  <p class="mt-3 max-w-2xl text-sm text-slate-600">
20
  Track your team, project health, and weekly task progress in one place.
21
  @if (summary.PendingTasksCount > 0)
@@ -26,7 +26,7 @@
26
  </div>
27
 
28
  <div class="flex flex-wrap gap-3">
29
- <a href="/projects/all" class="inline-flex items-center rounded-2xl bg-violet-700 px-5 py-3 text-sm font-semibold text-white shadow-lg shadow-violet-900/20 transition-colors hover:bg-violet-600">
30
  <i data-lucide="folder-kanban" class="mr-2 h-4 w-4"></i>
31
  All Projects
32
  </a>
@@ -40,7 +40,7 @@
40
 
41
  @if (isLoading)
42
  {
43
- <div class="rounded-[28px] border border-slate-200 bg-white px-6 py-12 text-center shadow-[0_18px_50px_rgba(15,23,42,0.08)]">
44
  <div class="mx-auto flex items-center justify-center gap-3 text-slate-500">
45
  <svg class="h-5 w-5 animate-spin" viewBox="0 0 24 24">
46
  <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none"></circle>
@@ -52,7 +52,7 @@
52
  }
53
  else if (!string.IsNullOrWhiteSpace(errorMessage))
54
  {
55
- <div class="rounded-[28px] border border-rose-200 bg-rose-50 px-6 py-5 text-sm text-rose-700 shadow-[0_18px_50px_rgba(15,23,42,0.08)]">
56
  @errorMessage
57
  </div>
58
  }
@@ -61,7 +61,7 @@
61
  <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4">
62
  @foreach (var metric in SummaryMetrics)
63
  {
64
- <div class="group relative overflow-hidden rounded-[24px] border border-slate-200 bg-white p-5 shadow-[0_18px_50px_rgba(15,23,42,0.08)] transition-transform duration-200 hover:-translate-y-1">
65
  <div class="relative flex items-center justify-between">
66
  <div class="@metric.IconWrapClass">
67
  <i data-lucide="@metric.Icon" class="h-5 w-5"></i>
@@ -75,10 +75,10 @@
75
  </div>
76
 
77
  <div class="grid grid-cols-1 gap-5 xl:grid-cols-[minmax(0,1.7fr)_minmax(340px,1fr)]">
78
- <div class="rounded-[28px] border border-slate-200 bg-white p-6 shadow-[0_18px_50px_rgba(15,23,42,0.08)]">
79
  <div class="flex items-start justify-between gap-4">
80
  <div>
81
- <h3 class="text-2xl font-bold text-slate-950">Weekly Task Progress</h3>
82
  <p class="mt-1 text-sm text-slate-500">Completed vs pending tasks per week</p>
83
  </div>
84
  <div class="text-sm text-slate-500">@WeeklyRangeLabel</div>
@@ -103,7 +103,7 @@
103
  <g @onmouseenter="() => SetHoveredWeek(index)" @onmouseleave="ClearHoveredWeek">
104
  <rect x="@week.HoverX" y="40" width="@week.HoverWidth" height="160" rx="18" fill="@(hoveredWeekIndex == index ? "#f8fafc" : "transparent")" opacity="@(hoveredWeekIndex == index ? "1" : "0")" />
105
 
106
- <rect x="@week.CompletedX" y="@(week.BaseY - week.CompletedHeight)" width="@BarWidth" height="@week.CompletedHeight" rx="8" fill="#8b5cf6" />
107
  <rect x="@week.PendingX" y="@(week.BaseY - week.PendingHeight)" width="@BarWidth" height="@week.PendingHeight" rx="8" fill="#14b8a6" />
108
 
109
  <text x="@week.LabelX" y="246" text-anchor="middle" font-size="13" fill="#64748b">@week.Label</text>
@@ -113,7 +113,7 @@
113
 
114
  @if (hoveredWeek is not null)
115
  {
116
- <div class="pointer-events-none absolute z-20 rounded-2xl border border-slate-200 bg-white px-4 py-3 shadow-[0_18px_50px_rgba(15,23,42,0.12)]"
117
  style="@HoveredWeekTooltipStyle">
118
  <div class="text-sm font-semibold text-slate-950">@hoveredWeek.Label</div>
119
  <div class="mt-2 text-sm text-violet-700">Completed: @hoveredWeek.Completed</div>
@@ -139,8 +139,8 @@
139
  </div>
140
  </div>
141
 
142
- <div class="rounded-[28px] border border-slate-200 bg-white p-6 shadow-[0_18px_50px_rgba(15,23,42,0.08)]">
143
- <h3 class="text-2xl font-bold text-slate-950">Team Progress</h3>
144
  <p class="mt-1 text-sm text-slate-500">Members with active workload</p>
145
 
146
  <div class="mt-6 max-h-[460px] space-y-4 overflow-y-auto pr-1">
@@ -181,10 +181,10 @@
181
  </div>
182
 
183
  <div class="grid grid-cols-1 gap-5 xl:grid-cols-[minmax(0,1.05fr)_minmax(0,0.95fr)]">
184
- <div class="rounded-[28px] border border-slate-200 bg-white p-6 shadow-[0_18px_50px_rgba(15,23,42,0.08)]">
185
  <div class="flex items-start justify-between gap-4">
186
  <div>
187
- <h3 class="text-2xl font-bold text-slate-950">@CalendarMonthLabel</h3>
188
  <p class="mt-1 text-sm text-slate-500">@CalendarTodayLabel</p>
189
  </div>
190
  </div>
@@ -214,8 +214,8 @@
214
  </div>
215
  </div>
216
 
217
- <div class="rounded-[28px] border border-slate-200 bg-white p-6 shadow-[0_18px_50px_rgba(15,23,42,0.08)]">
218
- <h3 class="text-2xl font-bold text-slate-950">Upcoming Deadlines</h3>
219
  <p class="mt-1 text-sm text-slate-500">Projects ending soon</p>
220
 
221
  <div class="mt-6 max-h-[460px] space-y-3 overflow-y-auto pr-1">
@@ -243,10 +243,10 @@
243
  </div>
244
  </div>
245
 
246
- <div class="overflow-hidden rounded-[28px] border border-slate-200 bg-white shadow-[0_18px_50px_rgba(15,23,42,0.08)]">
247
  <div class="flex items-center justify-between gap-4 border-b border-slate-100 px-6 py-5">
248
  <div>
249
- <h3 class="text-2xl font-bold text-slate-950">My Projects</h3>
250
  <p class="mt-1 text-sm text-slate-500">Click "View Tasks" to drill in</p>
251
  </div>
252
  <a href="/projects/all" class="inline-flex items-center rounded-2xl border border-slate-200 bg-white px-4 py-2.5 text-sm font-semibold text-violet-700 shadow-sm transition-colors hover:bg-slate-50">
@@ -281,8 +281,8 @@
281
 
282
  <div class="flex flex-col items-start gap-3 lg:items-end">
283
  <div class="text-right">
284
- <div class="text-2xl font-bold text-slate-950">@project.ProgressPercentage.ToString("F0")%</div>
285
- <div class="text-xs uppercase tracking-[0.22em] text-slate-500">Complete</div>
286
  </div>
287
  <button @onclick="() => ViewProjectTasks(project.Id)" class="inline-flex items-center rounded-2xl bg-violet-700 px-4 py-2.5 text-sm font-semibold text-white shadow-sm transition-colors hover:bg-violet-600">
288
  <i data-lucide="eye" class="mr-2 h-4 w-4"></i>
@@ -449,7 +449,7 @@
449
  private List<ProjectCardViewModel> BuildProjectCards()
450
  {
451
  var progressByProject = projectProgress.ToDictionary(item => item.ProjectId, item => item);
452
- var palette = new[] { "#7c3aed", "#0ea5e9", "#10b981", "#f59e0b", "#f97316" };
453
 
454
  return projects
455
  .OrderByDescending(project => progressByProject.TryGetValue(project.Id, out var progress) ? progress.CompletionPercentage : 0)
@@ -494,7 +494,7 @@
494
 
495
  private List<TeamProgressViewModel> BuildTeamProgress()
496
  {
497
- var palette = new[] { "#7c3aed", "#0ea5e9", "#10b981", "#f59e0b", "#f97316", "#ec4899" };
498
 
499
  return users
500
  .Select((user, index) =>
@@ -695,7 +695,7 @@
695
  {
696
  var palette = new[]
697
  {
698
- "#7c3aed",
699
  "#0ea5e9",
700
  "#10b981",
701
  "#f59e0b",
 
11
 
12
  <div class="min-h-screen bg-slate-50 px-4 py-6 text-slate-900 sm:px-6 lg:px-8">
13
  <div class="mx-auto max-w-[1440px] space-y-6">
14
+ <div class="rounded-xl border border-slate-200 bg-white px-8 py-8 shadow-sm">
15
  <div class="flex flex-col gap-6 lg:flex-row lg:items-end lg:justify-between">
16
  <div>
17
+ <p class="text-sm font-semibold uppercase tracking-[0.08em] text-violet-700">Team View</p>
18
+ <h2 class="mt-2 text-3xl font-bold tracking-tight text-slate-950">My Projects</h2>
19
  <p class="mt-3 max-w-2xl text-sm text-slate-600">
20
  Track your team, project health, and weekly task progress in one place.
21
  @if (summary.PendingTasksCount > 0)
 
26
  </div>
27
 
28
  <div class="flex flex-wrap gap-3">
29
+ <a href="/projects/all" class="inline-flex items-center rounded-2xl bg-violet-700 px-5 py-3 text-sm font-semibold text-white shadow-sm transition-colors hover:bg-violet-600">
30
  <i data-lucide="folder-kanban" class="mr-2 h-4 w-4"></i>
31
  All Projects
32
  </a>
 
40
 
41
  @if (isLoading)
42
  {
43
+ <div class="rounded-xl border border-slate-200 bg-white px-6 py-12 text-center shadow-sm">
44
  <div class="mx-auto flex items-center justify-center gap-3 text-slate-500">
45
  <svg class="h-5 w-5 animate-spin" viewBox="0 0 24 24">
46
  <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none"></circle>
 
52
  }
53
  else if (!string.IsNullOrWhiteSpace(errorMessage))
54
  {
55
+ <div class="rounded-xl border border-rose-200 bg-rose-50 px-6 py-5 text-sm text-rose-700 shadow-sm">
56
  @errorMessage
57
  </div>
58
  }
 
61
  <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4">
62
  @foreach (var metric in SummaryMetrics)
63
  {
64
+ <div class="group relative overflow-hidden rounded-xl border border-slate-200 bg-white p-5 shadow-sm transition-transform duration-200 hover:-translate-y-1">
65
  <div class="relative flex items-center justify-between">
66
  <div class="@metric.IconWrapClass">
67
  <i data-lucide="@metric.Icon" class="h-5 w-5"></i>
 
75
  </div>
76
 
77
  <div class="grid grid-cols-1 gap-5 xl:grid-cols-[minmax(0,1.7fr)_minmax(340px,1fr)]">
78
+ <div class="rounded-xl border border-slate-200 bg-white p-6 shadow-sm">
79
  <div class="flex items-start justify-between gap-4">
80
  <div>
81
+ <h3 class="text-xl font-bold text-slate-950">Weekly Task Progress</h3>
82
  <p class="mt-1 text-sm text-slate-500">Completed vs pending tasks per week</p>
83
  </div>
84
  <div class="text-sm text-slate-500">@WeeklyRangeLabel</div>
 
103
  <g @onmouseenter="() => SetHoveredWeek(index)" @onmouseleave="ClearHoveredWeek">
104
  <rect x="@week.HoverX" y="40" width="@week.HoverWidth" height="160" rx="18" fill="@(hoveredWeekIndex == index ? "#f8fafc" : "transparent")" opacity="@(hoveredWeekIndex == index ? "1" : "0")" />
105
 
106
+ <rect x="@week.CompletedX" y="@(week.BaseY - week.CompletedHeight)" width="@BarWidth" height="@week.CompletedHeight" rx="8" fill="#3b82f6" />
107
  <rect x="@week.PendingX" y="@(week.BaseY - week.PendingHeight)" width="@BarWidth" height="@week.PendingHeight" rx="8" fill="#14b8a6" />
108
 
109
  <text x="@week.LabelX" y="246" text-anchor="middle" font-size="13" fill="#64748b">@week.Label</text>
 
113
 
114
  @if (hoveredWeek is not null)
115
  {
116
+ <div class="pointer-events-none absolute z-20 rounded-2xl border border-slate-200 bg-white px-4 py-3 shadow-lg"
117
  style="@HoveredWeekTooltipStyle">
118
  <div class="text-sm font-semibold text-slate-950">@hoveredWeek.Label</div>
119
  <div class="mt-2 text-sm text-violet-700">Completed: @hoveredWeek.Completed</div>
 
139
  </div>
140
  </div>
141
 
142
+ <div class="rounded-xl border border-slate-200 bg-white p-6 shadow-sm">
143
+ <h3 class="text-xl font-bold text-slate-950">Team Progress</h3>
144
  <p class="mt-1 text-sm text-slate-500">Members with active workload</p>
145
 
146
  <div class="mt-6 max-h-[460px] space-y-4 overflow-y-auto pr-1">
 
181
  </div>
182
 
183
  <div class="grid grid-cols-1 gap-5 xl:grid-cols-[minmax(0,1.05fr)_minmax(0,0.95fr)]">
184
+ <div class="rounded-xl border border-slate-200 bg-white p-6 shadow-sm">
185
  <div class="flex items-start justify-between gap-4">
186
  <div>
187
+ <h3 class="text-xl font-bold text-slate-950">@CalendarMonthLabel</h3>
188
  <p class="mt-1 text-sm text-slate-500">@CalendarTodayLabel</p>
189
  </div>
190
  </div>
 
214
  </div>
215
  </div>
216
 
217
+ <div class="rounded-xl border border-slate-200 bg-white p-6 shadow-sm">
218
+ <h3 class="text-xl font-bold text-slate-950">Upcoming Deadlines</h3>
219
  <p class="mt-1 text-sm text-slate-500">Projects ending soon</p>
220
 
221
  <div class="mt-6 max-h-[460px] space-y-3 overflow-y-auto pr-1">
 
243
  </div>
244
  </div>
245
 
246
+ <div class="overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm">
247
  <div class="flex items-center justify-between gap-4 border-b border-slate-100 px-6 py-5">
248
  <div>
249
+ <h3 class="text-xl font-bold text-slate-950">My Projects</h3>
250
  <p class="mt-1 text-sm text-slate-500">Click "View Tasks" to drill in</p>
251
  </div>
252
  <a href="/projects/all" class="inline-flex items-center rounded-2xl border border-slate-200 bg-white px-4 py-2.5 text-sm font-semibold text-violet-700 shadow-sm transition-colors hover:bg-slate-50">
 
281
 
282
  <div class="flex flex-col items-start gap-3 lg:items-end">
283
  <div class="text-right">
284
+ <div class="text-xl font-bold text-slate-950">@project.ProgressPercentage.ToString("F0")%</div>
285
+ <div class="text-xs uppercase tracking-[0.08em] text-slate-500">Complete</div>
286
  </div>
287
  <button @onclick="() => ViewProjectTasks(project.Id)" class="inline-flex items-center rounded-2xl bg-violet-700 px-4 py-2.5 text-sm font-semibold text-white shadow-sm transition-colors hover:bg-violet-600">
288
  <i data-lucide="eye" class="mr-2 h-4 w-4"></i>
 
449
  private List<ProjectCardViewModel> BuildProjectCards()
450
  {
451
  var progressByProject = projectProgress.ToDictionary(item => item.ProjectId, item => item);
452
+ var palette = new[] { "#2563eb", "#0ea5e9", "#10b981", "#f59e0b", "#f97316" };
453
 
454
  return projects
455
  .OrderByDescending(project => progressByProject.TryGetValue(project.Id, out var progress) ? progress.CompletionPercentage : 0)
 
494
 
495
  private List<TeamProgressViewModel> BuildTeamProgress()
496
  {
497
+ var palette = new[] { "#2563eb", "#0ea5e9", "#10b981", "#f59e0b", "#f97316", "#ec4899" };
498
 
499
  return users
500
  .Select((user, index) =>
 
695
  {
696
  var palette = new[]
697
  {
698
+ "#2563eb",
699
  "#0ea5e9",
700
  "#10b981",
701
  "#f59e0b",
TaskTrackingSystem.WebApp/Components/Pages/Features/Issues/AddIssue.razor CHANGED
@@ -129,7 +129,7 @@
129
 
130
  @if (isAssigneeDropdownOpen)
131
  {
132
- <div class="absolute bottom-full left-0 z-30 mb-2 w-full rounded-2xl border border-slate-200 bg-white p-3 shadow-[0_18px_40px_rgba(15,23,42,0.16)]">
133
  <div class="flex items-center justify-end">
134
  <button type="button"
135
  @onclick="CloseAssigneeDropdown"
 
129
 
130
  @if (isAssigneeDropdownOpen)
131
  {
132
+ <div class="absolute bottom-full left-0 z-30 mb-2 w-full rounded-2xl border border-slate-200 bg-white p-3 shadow-lg">
133
  <div class="flex items-center justify-end">
134
  <button type="button"
135
  @onclick="CloseAssigneeDropdown"
TaskTrackingSystem.WebApp/Components/Pages/Features/Issues/Issues.razor CHANGED
@@ -200,7 +200,7 @@
200
  @if (showModal)
201
  {
202
  <div class="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
203
- <div class="bg-white rounded-2xl shadow-xl w-full max-w-2xl mx-4 overflow-hidden">
204
  <div class="flex items-center justify-between px-6 py-4 border-b border-slate-100">
205
  <div>
206
  <h3 class="text-lg font-semibold text-slate-900">Edit Issue</h3>
 
200
  @if (showModal)
201
  {
202
  <div class="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
203
+ <div class="bg-white rounded-2xl shadow-lg w-full max-w-2xl mx-4 overflow-hidden">
204
  <div class="flex items-center justify-between px-6 py-4 border-b border-slate-100">
205
  <div>
206
  <h3 class="text-lg font-semibold text-slate-900">Edit Issue</h3>
TaskTrackingSystem.WebApp/Components/Pages/Features/Projects/ProjectAssign.razor CHANGED
@@ -90,7 +90,7 @@
90
  else
91
  {
92
  <!-- Selected Project Header -->
93
- <div class="p-6 border-b border-slate-100 bg-gradient-to-r from-violet-50/40 via-white to-white flex flex-col md:flex-row md:items-center justify-between gap-4">
94
  <div>
95
  <span class="text-[10px] font-bold text-violet-600 uppercase tracking-wider">Currently Managing</span>
96
  <h3 class="text-xl font-bold text-slate-900">@SelectedProject.Name</h3>
 
90
  else
91
  {
92
  <!-- Selected Project Header -->
93
+ <div class="p-6 border-b border-slate-100 bg-slate-50 flex flex-col md:flex-row md:items-center justify-between gap-4">
94
  <div>
95
  <span class="text-[10px] font-bold text-violet-600 uppercase tracking-wider">Currently Managing</span>
96
  <h3 class="text-xl font-bold text-slate-900">@SelectedProject.Name</h3>
TaskTrackingSystem.WebApp/Components/Pages/Features/Projects/Projects.razor CHANGED
@@ -132,7 +132,7 @@
132
  @if (showModal)
133
  {
134
  <div class="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
135
- <div class="bg-white rounded-2xl shadow-xl w-full max-w-lg mx-4 overflow-hidden">
136
  <div class="flex items-center justify-between px-6 py-4 border-b border-slate-100">
137
  <h3 class="text-lg font-semibold text-slate-900">@(isEditing ? "Edit Project" : "New Project")</h3>
138
  <button @onclick="CloseModal" class="rounded-lg p-1 text-slate-400 hover:bg-slate-100 hover:text-slate-600 transition-all">
@@ -209,8 +209,8 @@
209
  @if (showAssignModal)
210
  {
211
  <div class="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
212
- <div class="bg-white rounded-2xl shadow-xl w-full max-w-lg mx-4 overflow-hidden">
213
- <div class="flex items-center justify-between px-6 py-4 border-b border-slate-100 bg-gradient-to-r from-violet-50 to-white">
214
  <div>
215
  <h3 class="text-lg font-semibold text-slate-900">Assign Members</h3>
216
  <p class="text-xs text-slate-500 mt-0.5">Project: <span class="font-semibold text-violet-700">@assigningProjectName</span></p>
 
132
  @if (showModal)
133
  {
134
  <div class="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
135
+ <div class="bg-white rounded-2xl shadow-lg w-full max-w-lg mx-4 overflow-hidden">
136
  <div class="flex items-center justify-between px-6 py-4 border-b border-slate-100">
137
  <h3 class="text-lg font-semibold text-slate-900">@(isEditing ? "Edit Project" : "New Project")</h3>
138
  <button @onclick="CloseModal" class="rounded-lg p-1 text-slate-400 hover:bg-slate-100 hover:text-slate-600 transition-all">
 
209
  @if (showAssignModal)
210
  {
211
  <div class="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
212
+ <div class="bg-white rounded-2xl shadow-lg w-full max-w-lg mx-4 overflow-hidden">
213
+ <div class="flex items-center justify-between px-6 py-4 border-b border-slate-100 bg-slate-50">
214
  <div>
215
  <h3 class="text-lg font-semibold text-slate-900">Assign Members</h3>
216
  <p class="text-xs text-slate-500 mt-0.5">Project: <span class="font-semibold text-violet-700">@assigningProjectName</span></p>
TaskTrackingSystem.WebApp/Components/Pages/Features/Roles/Roles.razor CHANGED
@@ -83,7 +83,7 @@
83
  <span class="text-sm font-semibold text-slate-900">@role.Name</span>
84
  </div>
85
  </td>
86
- <td class="px-6 py-4 text-sm text-slate-500 max-w-xs truncate">@(role.Description ?? "")</td>
87
  <td class="px-6 py-4 text-sm text-slate-400">@DisplayFormats.Date(role.CreatedAt)</td>
88
  <td class="px-6 py-4 text-right">
89
  <div class="flex items-center justify-end space-x-2">
@@ -135,11 +135,11 @@
135
  @if (showRoleModal)
136
  {
137
  <div class="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
138
- <div class="bg-white rounded-2xl shadow-2xl w-full mx-4 flex flex-col overflow-hidden"
139
  style="max-width: @(isEditing ? "30rem" : "58rem"); max-height: 88vh;">
140
 
141
  <!-- Header -->
142
- <div class="flex items-center justify-between px-6 py-4 border-b border-slate-100 bg-gradient-to-r from-slate-50 to-white shrink-0">
143
  <div class="flex items-center gap-3">
144
  <span class="flex h-8 w-8 items-center justify-center rounded-lg bg-violet-600 text-white">
145
  <i data-lucide="@(isEditing ? "pencil" : "shield-plus")" class="h-4 w-4"></i>
@@ -255,7 +255,7 @@
255
  var parentChecked = formSelectedAccessCodes.Contains(parent.MenuCode);
256
 
257
  <div class="rounded-2xl border border-slate-200 bg-white shadow-sm overflow-hidden">
258
- <label class="flex items-start gap-3 px-4 py-3 bg-gradient-to-r from-slate-50 to-white cursor-pointer hover:bg-slate-50 transition-colors">
259
  <input type="checkbox" checked="@parentChecked"
260
  @onchange="(e) => ToggleAccessSelection(parent, (bool)(e.Value ?? false), isForm: true)"
261
  class="mt-0.5 h-4 w-4 rounded border-slate-300 accent-violet-600" />
@@ -387,16 +387,16 @@
387
  @if (showAccessModal)
388
  {
389
  <div class="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
390
- <div class="bg-white rounded-2xl shadow-2xl w-full max-w-6xl mx-4 overflow-hidden flex flex-col" style="max-height: 88vh;">
391
 
392
  <!-- Header -->
393
- <div class="flex items-center justify-between px-6 py-4 border-b border-slate-100 bg-gradient-to-r from-violet-50 to-indigo-50">
394
  <div class="flex items-center gap-3">
395
  <div class="flex h-9 w-9 items-center justify-center rounded-lg bg-violet-100 text-violet-600">
396
  <i data-lucide="shield-check" class="h-4 w-4"></i>
397
  </div>
398
  <div>
399
- <h3 class="text-base font-semibold text-slate-900">Role Access <span class="text-violet-700">@selectedRoleName</span></h3>
400
  <p class="text-xs text-slate-500">Configure access and permissions</p>
401
  </div>
402
  </div>
@@ -459,7 +459,7 @@
459
  var parentChecked = selectedAccessCodes.Contains(parent.MenuCode);
460
 
461
  <div class="rounded-2xl border border-slate-200 bg-white shadow-sm overflow-hidden">
462
- <label class="flex items-start gap-3 px-4 py-3 bg-gradient-to-r from-slate-50 to-white cursor-pointer hover:bg-slate-50 transition-colors">
463
  <input type="checkbox" checked="@parentChecked"
464
  @onchange="(e) => ToggleAccessSelection(parent, (bool)(e.Value ?? false), isForm: false)"
465
  class="mt-0.5 h-4 w-4 rounded border-slate-300 accent-violet-600" />
 
83
  <span class="text-sm font-semibold text-slate-900">@role.Name</span>
84
  </div>
85
  </td>
86
+ <td class="px-6 py-4 text-sm text-slate-500 max-w-xs truncate">@(role.Description ?? "-")</td>
87
  <td class="px-6 py-4 text-sm text-slate-400">@DisplayFormats.Date(role.CreatedAt)</td>
88
  <td class="px-6 py-4 text-right">
89
  <div class="flex items-center justify-end space-x-2">
 
135
  @if (showRoleModal)
136
  {
137
  <div class="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
138
+ <div class="bg-white rounded-2xl shadow-lg w-full mx-4 flex flex-col overflow-hidden"
139
  style="max-width: @(isEditing ? "30rem" : "58rem"); max-height: 88vh;">
140
 
141
  <!-- Header -->
142
+ <div class="flex items-center justify-between px-6 py-4 border-b border-slate-100 bg-slate-50 shrink-0">
143
  <div class="flex items-center gap-3">
144
  <span class="flex h-8 w-8 items-center justify-center rounded-lg bg-violet-600 text-white">
145
  <i data-lucide="@(isEditing ? "pencil" : "shield-plus")" class="h-4 w-4"></i>
 
255
  var parentChecked = formSelectedAccessCodes.Contains(parent.MenuCode);
256
 
257
  <div class="rounded-2xl border border-slate-200 bg-white shadow-sm overflow-hidden">
258
+ <label class="flex items-start gap-3 px-4 py-3 bg-slate-50 cursor-pointer hover:bg-slate-50 transition-colors">
259
  <input type="checkbox" checked="@parentChecked"
260
  @onchange="(e) => ToggleAccessSelection(parent, (bool)(e.Value ?? false), isForm: true)"
261
  class="mt-0.5 h-4 w-4 rounded border-slate-300 accent-violet-600" />
 
387
  @if (showAccessModal)
388
  {
389
  <div class="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
390
+ <div class="bg-white rounded-2xl shadow-lg w-full max-w-6xl mx-4 overflow-hidden flex flex-col" style="max-height: 88vh;">
391
 
392
  <!-- Header -->
393
+ <div class="flex items-center justify-between px-6 py-4 border-b border-slate-100 bg-slate-50">
394
  <div class="flex items-center gap-3">
395
  <div class="flex h-9 w-9 items-center justify-center rounded-lg bg-violet-100 text-violet-600">
396
  <i data-lucide="shield-check" class="h-4 w-4"></i>
397
  </div>
398
  <div>
399
+ <h3 class="text-base font-semibold text-slate-900">Role Access - <span class="text-violet-700">@selectedRoleName</span></h3>
400
  <p class="text-xs text-slate-500">Configure access and permissions</p>
401
  </div>
402
  </div>
 
459
  var parentChecked = selectedAccessCodes.Contains(parent.MenuCode);
460
 
461
  <div class="rounded-2xl border border-slate-200 bg-white shadow-sm overflow-hidden">
462
+ <label class="flex items-start gap-3 px-4 py-3 bg-slate-50 cursor-pointer hover:bg-slate-50 transition-colors">
463
  <input type="checkbox" checked="@parentChecked"
464
  @onchange="(e) => ToggleAccessSelection(parent, (bool)(e.Value ?? false), isForm: false)"
465
  class="mt-0.5 h-4 w-4 rounded border-slate-300 accent-violet-600" />
TaskTrackingSystem.WebApp/Components/Pages/Features/Tasks/KanbanBoard.razor CHANGED
@@ -145,7 +145,7 @@
145
  @if (showTaskModal)
146
  {
147
  <div class="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
148
- <div class="bg-white rounded-2xl shadow-xl w-full max-w-lg mx-4 overflow-hidden">
149
  <div class="flex items-center justify-between px-6 py-4 border-b border-slate-100">
150
  <h3 class="text-lg font-semibold text-slate-900">New Task</h3>
151
  <button @onclick="CloseTaskModal" class="rounded-lg p-1 text-slate-400 hover:bg-slate-100 hover:text-slate-600 transition-all">
@@ -607,7 +607,7 @@
607
  TaskPriority.Low => "Low",
608
  TaskPriority.Medium => "Medium",
609
  TaskPriority.High => "High",
610
- _ => "—"
611
  };
612
 
613
  private record KanbanColumn(AppTaskStatus StatusId, string Name, string DotColor);
 
145
  @if (showTaskModal)
146
  {
147
  <div class="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
148
+ <div class="bg-white rounded-2xl shadow-lg w-full max-w-lg mx-4 overflow-hidden">
149
  <div class="flex items-center justify-between px-6 py-4 border-b border-slate-100">
150
  <h3 class="text-lg font-semibold text-slate-900">New Task</h3>
151
  <button @onclick="CloseTaskModal" class="rounded-lg p-1 text-slate-400 hover:bg-slate-100 hover:text-slate-600 transition-all">
 
607
  TaskPriority.Low => "Low",
608
  TaskPriority.Medium => "Medium",
609
  TaskPriority.High => "High",
610
+ _ => "-"
611
  };
612
 
613
  private record KanbanColumn(AppTaskStatus StatusId, string Name, string DotColor);
TaskTrackingSystem.WebApp/Components/Pages/Features/Tasks/TaskAssign.razor CHANGED
@@ -122,7 +122,7 @@
122
  else
123
  {
124
  <!-- Selected Task Details -->
125
- <div class="p-6 border-b border-slate-100 bg-gradient-to-r from-violet-50/40 via-white to-white">
126
  <div class="flex items-center gap-2 mb-2">
127
  <span class="text-[10px] font-bold text-violet-600 uppercase tracking-wider">Currently Assigning Task</span>
128
  <span class="text-[10px] font-mono font-semibold text-slate-400 bg-slate-100 px-1.5 py-0.5 rounded">TASK-#@SelectedTask.Id</span>
@@ -552,6 +552,6 @@
552
  TaskPriority.Low => "Low",
553
  TaskPriority.Medium => "Medium",
554
  TaskPriority.High => "High",
555
- _ => "—"
556
  };
557
  }
 
122
  else
123
  {
124
  <!-- Selected Task Details -->
125
+ <div class="p-6 border-b border-slate-100 bg-slate-50">
126
  <div class="flex items-center gap-2 mb-2">
127
  <span class="text-[10px] font-bold text-violet-600 uppercase tracking-wider">Currently Assigning Task</span>
128
  <span class="text-[10px] font-mono font-semibold text-slate-400 bg-slate-100 px-1.5 py-0.5 rounded">TASK-#@SelectedTask.Id</span>
 
552
  TaskPriority.Low => "Low",
553
  TaskPriority.Medium => "Medium",
554
  TaskPriority.High => "High",
555
+ _ => "-"
556
  };
557
  }
TaskTrackingSystem.WebApp/Components/Pages/Features/Tasks/TaskDetails.razor CHANGED
@@ -81,7 +81,7 @@
81
  }
82
  else
83
  {
84
- <h2 class="text-2xl font-bold text-slate-900 leading-tight flex items-center gap-2 group">
85
  @task.Title
86
  @if (hasTaskManagePermission)
87
  {
@@ -661,7 +661,7 @@
661
  }
662
  }
663
 
664
- // ——— CONFIRM DIALOG LOGIC —————————————————————————————————————————————————
665
 
666
  private async Task ExecuteConfirmAction()
667
  {
@@ -679,7 +679,7 @@
679
  confirmAction = null;
680
  }
681
 
682
- // ——— DISPLAY UTILITIES ————————————————————————————————————————————————————
683
 
684
  private string GetStatusLabel(AppTaskStatus statusId) => statusId switch
685
  {
 
81
  }
82
  else
83
  {
84
+ <h2 class="text-xl font-bold text-slate-900 leading-tight flex items-center gap-2 group">
85
  @task.Title
86
  @if (hasTaskManagePermission)
87
  {
 
661
  }
662
  }
663
 
664
+ // --- CONFIRM DIALOG LOGIC -------------------------------------------------
665
 
666
  private async Task ExecuteConfirmAction()
667
  {
 
679
  confirmAction = null;
680
  }
681
 
682
+ // --- DISPLAY UTILITIES ----------------------------------------------------
683
 
684
  private string GetStatusLabel(AppTaskStatus statusId) => statusId switch
685
  {
TaskTrackingSystem.WebApp/Components/Pages/Features/Tasks/Tasks.razor CHANGED
@@ -194,7 +194,7 @@
194
  @if (showModal)
195
  {
196
  <div class="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
197
- <div class="bg-white rounded-2xl shadow-xl w-full max-w-lg mx-4 overflow-hidden">
198
  <div class="flex items-center justify-between px-6 py-4 border-b border-slate-100">
199
  <h3 class="text-lg font-semibold text-slate-900">@(isEditing ? "Edit Task" : "New Task")</h3>
200
  <button @onclick="CloseModal" class="rounded-lg p-1 text-slate-400 hover:bg-slate-100 hover:text-slate-600 transition-all">
@@ -271,7 +271,7 @@
271
 
272
  @if (isAssigneeDropdownOpen)
273
  {
274
- <div class="absolute bottom-full left-0 z-30 mb-2 w-full rounded-2xl border border-slate-200 bg-white p-3 shadow-[0_18px_40px_rgba(15,23,42,0.16)]">
275
  <div class="flex items-center justify-end">
276
  <button type="button"
277
  @onclick="CloseAssigneeDropdown"
@@ -994,7 +994,7 @@
994
  TaskPriority.Low => "Low",
995
  TaskPriority.Medium => "Medium",
996
  TaskPriority.High => "High",
997
- _ => "—"
998
  };
999
 
1000
  }
 
194
  @if (showModal)
195
  {
196
  <div class="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
197
+ <div class="bg-white rounded-2xl shadow-lg w-full max-w-lg mx-4 overflow-hidden">
198
  <div class="flex items-center justify-between px-6 py-4 border-b border-slate-100">
199
  <h3 class="text-lg font-semibold text-slate-900">@(isEditing ? "Edit Task" : "New Task")</h3>
200
  <button @onclick="CloseModal" class="rounded-lg p-1 text-slate-400 hover:bg-slate-100 hover:text-slate-600 transition-all">
 
271
 
272
  @if (isAssigneeDropdownOpen)
273
  {
274
+ <div class="absolute bottom-full left-0 z-30 mb-2 w-full rounded-2xl border border-slate-200 bg-white p-3 shadow-lg">
275
  <div class="flex items-center justify-end">
276
  <button type="button"
277
  @onclick="CloseAssigneeDropdown"
 
994
  TaskPriority.Low => "Low",
995
  TaskPriority.Medium => "Medium",
996
  TaskPriority.High => "High",
997
+ _ => "-"
998
  };
999
 
1000
  }
TaskTrackingSystem.WebApp/Components/Pages/Features/Users/Users.razor CHANGED
@@ -104,7 +104,7 @@
104
  </div>
105
  </td>
106
  <td class="px-6 py-4 text-sm text-slate-600">@user.Email</td>
107
- <td class="px-6 py-4 text-sm text-slate-500">@(user.Phone ?? "")</td>
108
  <td class="px-6 py-4">
109
  @{ var role = roles.FirstOrDefault(r => r.Id == user.RoleId); }
110
  <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-indigo-50 text-indigo-700">
@@ -157,7 +157,7 @@
157
  @if (showModal)
158
  {
159
  <div class="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
160
- <div class="bg-white rounded-2xl shadow-xl w-full max-w-lg mx-4 overflow-hidden">
161
  <div class="flex items-center justify-between px-6 py-4 border-b border-slate-100">
162
  <h3 class="text-lg font-semibold text-slate-900">@(isEditing ? "Edit User" : "New User")</h3>
163
  <button @onclick="CloseModal" class="rounded-lg p-1 text-slate-400 hover:bg-slate-100 hover:text-slate-600 transition-all">
 
104
  </div>
105
  </td>
106
  <td class="px-6 py-4 text-sm text-slate-600">@user.Email</td>
107
+ <td class="px-6 py-4 text-sm text-slate-500">@(user.Phone ?? "-")</td>
108
  <td class="px-6 py-4">
109
  @{ var role = roles.FirstOrDefault(r => r.Id == user.RoleId); }
110
  <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-indigo-50 text-indigo-700">
 
157
  @if (showModal)
158
  {
159
  <div class="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
160
+ <div class="bg-white rounded-2xl shadow-lg w-full max-w-lg mx-4 overflow-hidden">
161
  <div class="flex items-center justify-between px-6 py-4 border-b border-slate-100">
162
  <h3 class="text-lg font-semibold text-slate-900">@(isEditing ? "Edit User" : "New User")</h3>
163
  <button @onclick="CloseModal" class="rounded-lg p-1 text-slate-400 hover:bg-slate-100 hover:text-slate-600 transition-all">
TaskTrackingSystem.WebApp/Components/Partial/ConfirmDialog.razor CHANGED
@@ -1,7 +1,7 @@
1
  @if (IsVisible)
2
  {
3
  <div class="fixed inset-0 z-[60] flex items-center justify-center bg-black/40 backdrop-blur-sm">
4
- <div class="rounded-2xl shadow-xl w-full max-w-sm mx-4 overflow-hidden animate-in border border-slate-100 bg-white">
5
  <div class="p-6 text-center">
6
  <div class="mx-auto flex h-14 w-14 items-center justify-center rounded-full mb-4" style="background-color: @GetIconBg(); color: @GetIconColor();">
7
  <i data-lucide="@Icon" class="h-7 w-7"></i>
@@ -16,7 +16,7 @@
16
  Cancel
17
  </button>
18
  }
19
- <button @onclick="OnConfirm" class="flex-1 rounded-lg px-4 py-2.5 text-sm font-medium text-white transition-colors" style="background-color: @GetButtonBg();">
20
  @ConfirmText
21
  </button>
22
  </div>
@@ -37,24 +37,28 @@
37
  [Parameter] public EventCallback OnConfirm { get; set; }
38
  [Parameter] public EventCallback OnCancel { get; set; }
39
 
 
 
 
 
40
  private string GetIconBg()
41
  {
42
  if (Icon.Contains("trash") || Icon.Contains("alert"))
43
- return "var(--status-warning-bg)";
44
  return "var(--status-success-bg)";
45
  }
46
 
47
  private string GetIconColor()
48
  {
49
  if (Icon.Contains("trash") || Icon.Contains("alert"))
50
- return "var(--brand-accent)";
51
  return "var(--brand-primary)";
52
  }
53
 
54
  private string GetButtonBg()
55
  {
56
  if (Icon.Contains("trash") || Icon.Contains("alert"))
57
- return "var(--brand-accent)";
58
  return "var(--brand-primary)";
59
  }
60
  }
 
1
  @if (IsVisible)
2
  {
3
  <div class="fixed inset-0 z-[60] flex items-center justify-center bg-black/40 backdrop-blur-sm">
4
+ <div class="w-full max-w-sm mx-4 overflow-hidden rounded-lg border border-slate-100 bg-white shadow-lg animate-in">
5
  <div class="p-6 text-center">
6
  <div class="mx-auto flex h-14 w-14 items-center justify-center rounded-full mb-4" style="background-color: @GetIconBg(); color: @GetIconColor();">
7
  <i data-lucide="@Icon" class="h-7 w-7"></i>
 
16
  Cancel
17
  </button>
18
  }
19
+ <button @onclick="OnConfirm" class="flex-1 rounded-lg px-4 py-2.5 text-sm font-medium text-white transition-colors @ButtonClass" style="@ConfirmButtonStyle">
20
  @ConfirmText
21
  </button>
22
  </div>
 
37
  [Parameter] public EventCallback OnConfirm { get; set; }
38
  [Parameter] public EventCallback OnCancel { get; set; }
39
 
40
+ private string ConfirmButtonStyle => string.IsNullOrWhiteSpace(ButtonClass)
41
+ ? $"background-color: {GetButtonBg()};"
42
+ : string.Empty;
43
+
44
  private string GetIconBg()
45
  {
46
  if (Icon.Contains("trash") || Icon.Contains("alert"))
47
+ return "var(--status-danger-bg)";
48
  return "var(--status-success-bg)";
49
  }
50
 
51
  private string GetIconColor()
52
  {
53
  if (Icon.Contains("trash") || Icon.Contains("alert"))
54
+ return "var(--status-danger-text)";
55
  return "var(--brand-primary)";
56
  }
57
 
58
  private string GetButtonBg()
59
  {
60
  if (Icon.Contains("trash") || Icon.Contains("alert"))
61
+ return "var(--status-danger-text)";
62
  return "var(--brand-primary)";
63
  }
64
  }
TaskTrackingSystem.WebApp/Components/Partial/EmptyState.razor CHANGED
@@ -2,9 +2,11 @@
2
 
3
  <tr>
4
  <td colspan="@ColSpan" class="px-6 py-12 text-center">
5
- <i data-lucide="@Icon" class="h-10 w-10 mx-auto text-slate-300 mb-3"></i>
6
- <p class="text-sm font-medium text-slate-500">@Title</p>
7
- <p class="text-xs text-slate-400 mt-1">@Subtitle</p>
 
 
8
  </td>
9
  </tr>
10
 
 
2
 
3
  <tr>
4
  <td colspan="@ColSpan" class="px-6 py-12 text-center">
5
+ <div class="mx-auto mb-3 flex h-12 w-12 items-center justify-center rounded-full bg-slate-100 text-slate-400">
6
+ <i data-lucide="@Icon" class="h-6 w-6"></i>
7
+ </div>
8
+ <p class="text-sm font-semibold text-slate-700">@Title</p>
9
+ <p class="mx-auto mt-1 max-w-sm text-xs leading-5 text-slate-500">@Subtitle</p>
10
  </td>
11
  </tr>
12
 
TaskTrackingSystem.WebApp/Components/Partial/LanguageToggle.razor CHANGED
@@ -4,7 +4,7 @@
4
 
5
  <button type="button"
6
  @onclick="ToggleLanguage"
7
- class="inline-flex h-9 items-center justify-center rounded-lg border border-slate-200 bg-white px-3 text-slate-600 shadow-sm transition-colors hover:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-slate-400"
8
  title="@ButtonTitle"
9
  aria-label="@ButtonTitle">
10
  <span class="text-xs font-semibold tracking-wide">@ButtonLabel</span>
 
4
 
5
  <button type="button"
6
  @onclick="ToggleLanguage"
7
+ class="control-button px-3"
8
  title="@ButtonTitle"
9
  aria-label="@ButtonTitle">
10
  <span class="text-xs font-semibold tracking-wide">@ButtonLabel</span>
TaskTrackingSystem.WebApp/Components/Partial/LoadingSpinner.razor CHANGED
@@ -1,6 +1,6 @@
1
  @* Partial/LoadingSpinner.razor - Reusable loading state component *@
2
 
3
- <div class="flex items-center justify-center py-20">
4
  <div class="flex items-center space-x-3 text-slate-500">
5
  <svg class="animate-spin h-5 w-5" viewBox="0 0 24 24">
6
  <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none"></circle>
 
1
  @* Partial/LoadingSpinner.razor - Reusable loading state component *@
2
 
3
+ <div class="surface-panel flex items-center justify-center py-14">
4
  <div class="flex items-center space-x-3 text-slate-500">
5
  <svg class="animate-spin h-5 w-5" viewBox="0 0 24 24">
6
  <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none"></circle>
TaskTrackingSystem.WebApp/Components/Partial/NotificationBell.razor CHANGED
@@ -8,11 +8,11 @@
8
  <div class="relative">
9
  <button type="button"
10
  @onclick="ToggleOpenAsync"
11
- class="relative rounded-full p-2 text-slate-400 hover:bg-slate-50 hover:text-violet-600 transition-all">
12
  <i data-lucide="bell" class="h-5 w-5"></i>
13
  @if (UnreadCount > 0)
14
  {
15
- <span class="absolute right-1 top-1 min-w-4 h-4 px-1 rounded-full bg-violet-600 text-white text-[10px] font-bold flex items-center justify-center">
16
  @(UnreadCount > 99 ? "99+" : UnreadCount.ToString())
17
  </span>
18
  }
@@ -20,8 +20,8 @@
20
 
21
  @if (IsOpen)
22
  {
23
- <div class="absolute right-0 mt-3 w-[380px] max-w-[calc(100vw-2rem)] rounded-2xl border border-slate-200 bg-white shadow-2xl z-50 overflow-hidden">
24
- <div class="flex items-center justify-between px-4 py-3 border-b border-slate-100 bg-slate-50">
25
  <div>
26
  <p class="text-sm font-semibold text-slate-900">Notifications</p>
27
  <p class="text-xs text-slate-500">@UnreadCount unread</p>
@@ -62,10 +62,10 @@
62
  @foreach (var item in Notifications)
63
  {
64
  <button type="button"
65
- class="w-full text-left px-4 py-3 border-b border-slate-100 hover:bg-slate-50 transition-colors @(item.IsRead ? "bg-white" : "bg-violet-50/40")"
66
  @onclick="() => OpenNotificationAsync(item)">
67
  <div class="flex items-start gap-3">
68
- <div class="mt-0.5 h-9 w-9 rounded-full flex items-center justify-center @(item.IsRead ? "bg-slate-100 text-slate-500" : "bg-violet-100 text-violet-700")">
69
  <i data-lucide="bell-ring" class="h-4 w-4"></i>
70
  </div>
71
  <div class="min-w-0 flex-1">
 
8
  <div class="relative">
9
  <button type="button"
10
  @onclick="ToggleOpenAsync"
11
+ class="control-button relative">
12
  <i data-lucide="bell" class="h-5 w-5"></i>
13
  @if (UnreadCount > 0)
14
  {
15
+ <span class="absolute -right-1 -top-1 flex h-4 min-w-4 items-center justify-center rounded-full bg-rose-600 px-1 text-[10px] font-bold text-white">
16
  @(UnreadCount > 99 ? "99+" : UnreadCount.ToString())
17
  </span>
18
  }
 
20
 
21
  @if (IsOpen)
22
  {
23
+ <div class="absolute right-0 z-50 mt-3 w-[380px] max-w-[calc(100vw-2rem)] overflow-hidden rounded-lg border border-slate-200 bg-white shadow-lg">
24
+ <div class="flex items-center justify-between px-4 py-3 border-b border-slate-100 bg-slate-50/80">
25
  <div>
26
  <p class="text-sm font-semibold text-slate-900">Notifications</p>
27
  <p class="text-xs text-slate-500">@UnreadCount unread</p>
 
62
  @foreach (var item in Notifications)
63
  {
64
  <button type="button"
65
+ class="w-full border-b border-slate-100 px-4 py-3 text-left transition-colors hover:bg-slate-50 @(item.IsRead ? "bg-white" : "bg-violet-50/70")"
66
  @onclick="() => OpenNotificationAsync(item)">
67
  <div class="flex items-start gap-3">
68
+ <div class="mt-0.5 flex h-9 w-9 items-center justify-center rounded-full @(item.IsRead ? "bg-slate-100 text-slate-500" : "bg-violet-100 text-violet-700")">
69
  <i data-lucide="bell-ring" class="h-4 w-4"></i>
70
  </div>
71
  <div class="min-w-0 flex-1">
TaskTrackingSystem.WebApp/Components/Partial/Pagination.razor CHANGED
@@ -1,6 +1,6 @@
1
  @* Partial/Pagination.razor - Reusable pagination component *@
2
 
3
- <div class="flex flex-wrap items-center justify-between gap-3 px-6 py-3 border-t border-slate-100 bg-slate-50/40">
4
  <div class="text-xs text-slate-500">
5
  @if (TotalCount == 0)
6
  {
@@ -18,7 +18,7 @@
18
  <div class="flex items-center gap-2 text-xs text-slate-500">
19
  <span>Rows per page</span>
20
  <select value="@PageSize" @onchange="HandlePageSizeChange"
21
- class="rounded-md border border-slate-200 bg-white px-2 py-1 text-xs text-slate-700 focus:outline-none focus:ring-2 focus:ring-slate-400 transition-all">
22
  <option value="5">5</option>
23
  <option value="10">10</option>
24
  <option value="20">20</option>
@@ -28,25 +28,21 @@
28
 
29
  <div class="flex items-center gap-1">
30
  <button @onclick="() => GoToPage(1)" disabled="@(CurrentPage <= 1)"
31
- class="inline-flex h-7 w-7 items-center justify-center rounded-md text-slate-400 transition-all hover:bg-slate-100 hover:text-slate-600 disabled:cursor-not-allowed disabled:opacity-40"
32
  title="First page">
33
- <svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
34
- <path stroke-linecap="round" stroke-linejoin="round" d="M11 19l-7-7 7-7M18 19l-7-7 7-7" />
35
- </svg>
36
  </button>
37
  <button @onclick="() => GoToPage(CurrentPage - 1)" disabled="@(CurrentPage <= 1)"
38
- class="inline-flex h-7 w-7 items-center justify-center rounded-md text-slate-400 transition-all hover:bg-slate-100 hover:text-slate-600 disabled:cursor-not-allowed disabled:opacity-40"
39
  title="Previous page">
40
- <svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
41
- <path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7" />
42
- </svg>
43
  </button>
44
 
45
  @foreach (var p in GetVisiblePages())
46
  {
47
  if (p == -1)
48
  {
49
- <span class="inline-flex h-7 w-7 items-center justify-center text-xs text-slate-400">...</span>
50
  }
51
  else
52
  {
@@ -58,18 +54,14 @@
58
  }
59
 
60
  <button @onclick="() => GoToPage(CurrentPage + 1)" disabled="@(CurrentPage >= TotalPages)"
61
- class="inline-flex h-7 w-7 items-center justify-center rounded-md text-slate-400 transition-all hover:bg-slate-100 hover:text-slate-600 disabled:cursor-not-allowed disabled:opacity-40"
62
  title="Next page">
63
- <svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
64
- <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7" />
65
- </svg>
66
  </button>
67
  <button @onclick="() => GoToPage(TotalPages)" disabled="@(CurrentPage >= TotalPages)"
68
- class="inline-flex h-7 w-7 items-center justify-center rounded-md text-slate-400 transition-all hover:bg-slate-100 hover:text-slate-600 disabled:cursor-not-allowed disabled:opacity-40"
69
  title="Last page">
70
- <svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
71
- <path stroke-linecap="round" stroke-linejoin="round" d="M13 5l7 7-7 7M6 5l7 7-7 7" />
72
- </svg>
73
  </button>
74
  </div>
75
  </div>
@@ -84,8 +76,8 @@
84
  [Parameter] public EventCallback<int> OnPageSizeChanged { get; set; }
85
 
86
  private string GetPageButtonClass(int p) => p == CurrentPage
87
- ? "inline-flex h-7 w-7 items-center justify-center rounded-md text-xs font-medium transition-all bg-violet-600 text-white shadow-sm"
88
- : "inline-flex h-7 w-7 items-center justify-center rounded-md text-xs font-medium transition-all text-slate-600 hover:bg-slate-100";
89
 
90
  private async Task GoToPage(int p)
91
  {
 
1
  @* Partial/Pagination.razor - Reusable pagination component *@
2
 
3
+ <div class="flex flex-wrap items-center justify-between gap-3 border-t border-slate-100 bg-slate-50/60 px-5 py-3">
4
  <div class="text-xs text-slate-500">
5
  @if (TotalCount == 0)
6
  {
 
18
  <div class="flex items-center gap-2 text-xs text-slate-500">
19
  <span>Rows per page</span>
20
  <select value="@PageSize" @onchange="HandlePageSizeChange"
21
+ class="rounded-lg border border-slate-200 bg-white px-2 py-1 text-xs text-slate-700 focus:outline-none focus:ring-2 focus:ring-violet-600 transition-all">
22
  <option value="5">5</option>
23
  <option value="10">10</option>
24
  <option value="20">20</option>
 
28
 
29
  <div class="flex items-center gap-1">
30
  <button @onclick="() => GoToPage(1)" disabled="@(CurrentPage <= 1)"
31
+ class="inline-flex h-8 w-8 items-center justify-center rounded-lg text-slate-400 transition-all hover:bg-white hover:text-slate-700 disabled:cursor-not-allowed disabled:opacity-40"
32
  title="First page">
33
+ <i data-lucide="chevrons-left" class="h-3.5 w-3.5"></i>
 
 
34
  </button>
35
  <button @onclick="() => GoToPage(CurrentPage - 1)" disabled="@(CurrentPage <= 1)"
36
+ class="inline-flex h-8 w-8 items-center justify-center rounded-lg text-slate-400 transition-all hover:bg-white hover:text-slate-700 disabled:cursor-not-allowed disabled:opacity-40"
37
  title="Previous page">
38
+ <i data-lucide="chevron-left" class="h-3.5 w-3.5"></i>
 
 
39
  </button>
40
 
41
  @foreach (var p in GetVisiblePages())
42
  {
43
  if (p == -1)
44
  {
45
+ <span class="inline-flex h-8 w-8 items-center justify-center text-xs text-slate-400">...</span>
46
  }
47
  else
48
  {
 
54
  }
55
 
56
  <button @onclick="() => GoToPage(CurrentPage + 1)" disabled="@(CurrentPage >= TotalPages)"
57
+ class="inline-flex h-8 w-8 items-center justify-center rounded-lg text-slate-400 transition-all hover:bg-white hover:text-slate-700 disabled:cursor-not-allowed disabled:opacity-40"
58
  title="Next page">
59
+ <i data-lucide="chevron-right" class="h-3.5 w-3.5"></i>
 
 
60
  </button>
61
  <button @onclick="() => GoToPage(TotalPages)" disabled="@(CurrentPage >= TotalPages)"
62
+ class="inline-flex h-8 w-8 items-center justify-center rounded-lg text-slate-400 transition-all hover:bg-white hover:text-slate-700 disabled:cursor-not-allowed disabled:opacity-40"
63
  title="Last page">
64
+ <i data-lucide="chevrons-right" class="h-3.5 w-3.5"></i>
 
 
65
  </button>
66
  </div>
67
  </div>
 
76
  [Parameter] public EventCallback<int> OnPageSizeChanged { get; set; }
77
 
78
  private string GetPageButtonClass(int p) => p == CurrentPage
79
+ ? "inline-flex h-8 w-8 items-center justify-center rounded-lg text-xs font-semibold transition-all bg-violet-600 text-white shadow-sm"
80
+ : "inline-flex h-8 w-8 items-center justify-center rounded-lg text-xs font-semibold transition-all text-slate-600 hover:bg-white";
81
 
82
  private async Task GoToPage(int p)
83
  {
TaskTrackingSystem.WebApp/Components/Partial/SuccessAlert.razor CHANGED
@@ -3,8 +3,8 @@
3
 
4
  @if (!string.IsNullOrEmpty(displayMessage))
5
  {
6
- <div class="fixed top-6 right-6 z-[100] pointer-events-none">
7
- <div class="flex items-center gap-3 rounded-xl bg-violet-600 text-white px-5 py-3 shadow-lg border border-violet-500/30 animate-pulse-once">
8
  <i data-lucide="check-circle" class="h-5 w-5 shrink-0"></i>
9
  <span class="text-sm font-semibold">@displayMessage</span>
10
  </div>
 
3
 
4
  @if (!string.IsNullOrEmpty(displayMessage))
5
  {
6
+ <div class="fixed right-4 top-4 z-[100] pointer-events-none sm:right-6 sm:top-6">
7
+ <div class="flex items-center gap-3 rounded-lg border border-teal-500/20 bg-teal-600 px-4 py-3 text-white shadow-lg animate-pulse-once">
8
  <i data-lucide="check-circle" class="h-5 w-5 shrink-0"></i>
9
  <span class="text-sm font-semibold">@displayMessage</span>
10
  </div>
TaskTrackingSystem.WebApp/Components/Partial/ThemeToggle.razor CHANGED
@@ -3,7 +3,7 @@
3
 
4
  <button type="button"
5
  @onclick="ToggleTheme"
6
- class="inline-flex h-9 w-9 items-center justify-center rounded-lg border border-slate-200 bg-white text-slate-600 shadow-sm transition-colors hover:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-slate-400"
7
  title="@ButtonTitle"
8
  aria-label="@ButtonTitle"
9
  aria-pressed="@isDarkMode.ToString().ToLowerInvariant()">
 
3
 
4
  <button type="button"
5
  @onclick="ToggleTheme"
6
+ class="control-button"
7
  title="@ButtonTitle"
8
  aria-label="@ButtonTitle"
9
  aria-pressed="@isDarkMode.ToString().ToLowerInvariant()">
TaskTrackingSystem.WebApp/wwwroot/app.css CHANGED
@@ -1,29 +1,76 @@
1
  :root {
2
- --brand-primary: #111827;
3
- --brand-accent: #374151;
4
- --app-bg: #F8F9FA;
5
- --text-primary: #111827;
6
- --text-muted: #6B7280;
7
- --status-success-bg: #D1FAE5;
8
- --status-warning-bg: #FFE4E6;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  }
10
 
11
  html.dark {
12
- --brand-primary: #4F46E5;
13
- --brand-accent: #6366F1;
14
- --app-bg: #0F172A;
 
 
 
 
 
 
15
  --text-primary: #F8FAFC;
16
  --text-muted: #CBD5E1;
17
- --status-success-bg: rgba(16, 185, 129, 0.18);
18
- --status-warning-bg: rgba(244, 63, 94, 0.18);
 
 
 
 
 
 
 
 
 
 
 
 
19
  color-scheme: dark;
20
  }
21
 
22
  html, body {
23
- font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
24
  background-color: var(--app-bg);
25
  color: var(--text-primary);
26
  scrollbar-gutter: stable;
 
 
 
 
 
 
 
 
27
  }
28
 
29
  * {
@@ -58,17 +105,17 @@ html, body {
58
  }
59
 
60
  a, .btn-link {
61
- color: #111827;
62
  }
63
 
64
  .btn-primary {
65
  color: #fff;
66
- background-color: #111827;
67
- border-color: #374151;
68
  }
69
 
70
  .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
71
- box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #6B7280;
72
  }
73
 
74
  .content {
@@ -107,7 +154,7 @@ h1:focus {
107
 
108
  html.dark a,
109
  html.dark .btn-link {
110
- color: #C4B5FD;
111
  }
112
 
113
  html.dark,
@@ -204,7 +251,7 @@ html.dark .border-violet-500,
204
  html.dark .border-violet-600,
205
  html.dark .border-indigo-500,
206
  html.dark .border-indigo-600 {
207
- border-color: #818CF8 !important;
208
  }
209
 
210
  html.dark input,
@@ -224,7 +271,7 @@ html.dark textarea::placeholder {
224
 
225
  html.dark input[type="checkbox"],
226
  html.dark input[type="radio"] {
227
- accent-color: #6366F1;
228
  }
229
 
230
  html.dark .btn-primary,
@@ -235,7 +282,7 @@ html.dark .bg-purple-500,
235
  html.dark .bg-purple-600,
236
  html.dark .bg-indigo-500,
237
  html.dark .bg-indigo-600 {
238
- background-color: #4F46E5 !important;
239
  border-color: #4338CA !important;
240
  color: #FFFFFF !important;
241
  }
@@ -250,7 +297,7 @@ html.dark .bg-indigo-700 {
250
  html.dark .bg-violet-400,
251
  html.dark .bg-purple-400,
252
  html.dark .bg-indigo-400 {
253
- background-color: #818CF8 !important;
254
  }
255
 
256
  html.dark .bg-violet-50,
@@ -271,7 +318,7 @@ html.dark .text-purple-600,
271
  html.dark .text-purple-700,
272
  html.dark .text-indigo-600,
273
  html.dark .text-indigo-700 {
274
- color: #C4B5FD !important;
275
  }
276
 
277
  html.dark .bg-rose-50,
@@ -386,7 +433,7 @@ html.dark .hover\:text-violet-500:hover,
386
  html.dark .hover\:text-violet-600:hover,
387
  html.dark .hover\:text-violet-700:hover,
388
  html.dark .hover\:text-violet-800:hover {
389
- color: #C4B5FD !important;
390
  }
391
 
392
  html.dark .hover\:text-amber-600:hover {
@@ -403,15 +450,15 @@ html.dark .focus\:ring-slate-400:focus,
403
  html.dark .focus\:ring-slate-900:focus,
404
  html.dark .focus\:ring-violet-600:focus,
405
  html.dark .focus\:ring-red-500:focus {
406
- --tw-ring-color: #818CF8 !important;
407
  }
408
 
409
  html.dark .shadow-sm {
410
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35) !important;
411
  }
412
 
413
- html.dark .shadow-xl,
414
- html.dark .shadow-2xl {
415
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.28) !important;
416
  }
417
 
@@ -438,7 +485,401 @@ html.dark .flatpickr-day:focus {
438
  html.dark .flatpickr-day.selected,
439
  html.dark .flatpickr-day.startRange,
440
  html.dark .flatpickr-day.endRange {
441
- background: #4F46E5;
442
- border-color: #4F46E5;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
443
  color: #FFFFFF;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
444
  }
 
1
  :root {
2
+ --brand-primary: #2563EB;
3
+ --brand-primary-hover: #1D4ED8;
4
+ --brand-accent: #0F766E;
5
+ --app-bg: #F6F8FB;
6
+ --surface: #FFFFFF;
7
+ --surface-muted: #F8FAFC;
8
+ --surface-raised: #FFFFFF;
9
+ --border: #E2E8F0;
10
+ --border-strong: #CBD5E1;
11
+ --text-primary: #0F172A;
12
+ --text-muted: #64748B;
13
+ --text-soft: #94A3B8;
14
+ --focus-ring: rgba(37, 99, 235, 0.24);
15
+ --status-success-bg: #CCFBF1;
16
+ --status-success-text: #0F766E;
17
+ --status-warning-bg: #FEF3C7;
18
+ --status-warning-text: #B45309;
19
+ --status-danger-bg: #FFE4E6;
20
+ --status-danger-text: #BE123C;
21
+ --sidebar-bg: #0B1220;
22
+ --sidebar-muted: #9AA6B8;
23
+ --sidebar-active: rgba(37, 99, 235, 0.18);
24
+ --sidebar-border: rgba(148, 163, 184, 0.16);
25
+ --radius-sm: 0.375rem;
26
+ --radius-md: 0.5rem;
27
+ --radius-lg: 0.5rem;
28
+ --shadow-card: 0 1px 2px rgba(15, 23, 42, 0.06);
29
+ --shadow-popover: 0 18px 40px rgba(15, 23, 42, 0.16);
30
  }
31
 
32
  html.dark {
33
+ --brand-primary: #60A5FA;
34
+ --brand-primary-hover: #93C5FD;
35
+ --brand-accent: #2DD4BF;
36
+ --app-bg: #0B1120;
37
+ --surface: #111827;
38
+ --surface-muted: #182235;
39
+ --surface-raised: #0F172A;
40
+ --border: #263449;
41
+ --border-strong: #334155;
42
  --text-primary: #F8FAFC;
43
  --text-muted: #CBD5E1;
44
+ --text-soft: #94A3B8;
45
+ --focus-ring: rgba(96, 165, 250, 0.30);
46
+ --status-success-bg: rgba(20, 184, 166, 0.18);
47
+ --status-success-text: #5EEAD4;
48
+ --status-warning-bg: rgba(245, 158, 11, 0.18);
49
+ --status-warning-text: #FCD34D;
50
+ --status-danger-bg: rgba(244, 63, 94, 0.18);
51
+ --status-danger-text: #FDA4AF;
52
+ --sidebar-bg: #060B16;
53
+ --sidebar-muted: #94A3B8;
54
+ --sidebar-active: rgba(96, 165, 250, 0.18);
55
+ --sidebar-border: rgba(148, 163, 184, 0.18);
56
+ --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.28);
57
+ --shadow-popover: 0 18px 50px rgba(0, 0, 0, 0.35);
58
  color-scheme: dark;
59
  }
60
 
61
  html, body {
62
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
63
  background-color: var(--app-bg);
64
  color: var(--text-primary);
65
  scrollbar-gutter: stable;
66
+ font-size: 15px;
67
+ letter-spacing: 0;
68
+ -webkit-font-smoothing: antialiased;
69
+ text-rendering: optimizeLegibility;
70
+ }
71
+
72
+ body.tts-sidebar-open {
73
+ overflow: hidden;
74
  }
75
 
76
  * {
 
105
  }
106
 
107
  a, .btn-link {
108
+ color: var(--brand-primary);
109
  }
110
 
111
  .btn-primary {
112
  color: #fff;
113
+ background-color: var(--brand-primary);
114
+ border-color: var(--brand-primary);
115
  }
116
 
117
  .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
118
+ box-shadow: 0 0 0 0.1rem var(--surface), 0 0 0 0.25rem var(--focus-ring);
119
  }
120
 
121
  .content {
 
154
 
155
  html.dark a,
156
  html.dark .btn-link {
157
+ color: #93C5FD;
158
  }
159
 
160
  html.dark,
 
251
  html.dark .border-violet-600,
252
  html.dark .border-indigo-500,
253
  html.dark .border-indigo-600 {
254
+ border-color: #93C5FD !important;
255
  }
256
 
257
  html.dark input,
 
271
 
272
  html.dark input[type="checkbox"],
273
  html.dark input[type="radio"] {
274
+ accent-color: #2563EB;
275
  }
276
 
277
  html.dark .btn-primary,
 
282
  html.dark .bg-purple-600,
283
  html.dark .bg-indigo-500,
284
  html.dark .bg-indigo-600 {
285
+ background-color: #2563EB !important;
286
  border-color: #4338CA !important;
287
  color: #FFFFFF !important;
288
  }
 
297
  html.dark .bg-violet-400,
298
  html.dark .bg-purple-400,
299
  html.dark .bg-indigo-400 {
300
+ background-color: #93C5FD !important;
301
  }
302
 
303
  html.dark .bg-violet-50,
 
318
  html.dark .text-purple-700,
319
  html.dark .text-indigo-600,
320
  html.dark .text-indigo-700 {
321
+ color: #93C5FD !important;
322
  }
323
 
324
  html.dark .bg-rose-50,
 
433
  html.dark .hover\:text-violet-600:hover,
434
  html.dark .hover\:text-violet-700:hover,
435
  html.dark .hover\:text-violet-800:hover {
436
+ color: #93C5FD !important;
437
  }
438
 
439
  html.dark .hover\:text-amber-600:hover {
 
450
  html.dark .focus\:ring-slate-900:focus,
451
  html.dark .focus\:ring-violet-600:focus,
452
  html.dark .focus\:ring-red-500:focus {
453
+ --tw-ring-color: #93C5FD !important;
454
  }
455
 
456
  html.dark .shadow-sm {
457
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35) !important;
458
  }
459
 
460
+ html.dark .shadow-lg,
461
+ html.dark .shadow-lg {
462
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.28) !important;
463
  }
464
 
 
485
  html.dark .flatpickr-day.selected,
486
  html.dark .flatpickr-day.startRange,
487
  html.dark .flatpickr-day.endRange {
488
+ background: var(--brand-primary);
489
+ border-color: var(--brand-primary);
490
+ color: #FFFFFF;
491
+ }
492
+
493
+ /* Taskify UI system */
494
+ .app-shell {
495
+ display: flex;
496
+ min-height: 100vh;
497
+ background:
498
+ linear-gradient(180deg, rgba(37, 99, 235, 0.035), transparent 18rem),
499
+ var(--app-bg);
500
+ color: var(--text-primary);
501
+ overflow: hidden;
502
+ }
503
+
504
+ .app-sidebar {
505
+ position: relative;
506
+ display: flex;
507
+ width: 17rem;
508
+ min-height: 100vh;
509
+ flex-shrink: 0;
510
+ flex-direction: column;
511
+ overflow: hidden;
512
+ border-right: 1px solid var(--sidebar-border);
513
+ background: var(--sidebar-bg);
514
+ color: #FFFFFF;
515
+ }
516
+
517
+ .app-sidebar::before {
518
+ position: absolute;
519
+ inset: 0;
520
+ pointer-events: none;
521
+ content: "";
522
+ background:
523
+ radial-gradient(circle at top left, rgba(37, 99, 235, 0.22), transparent 18rem),
524
+ linear-gradient(180deg, rgba(255, 255, 255, 0.04), transparent 42%);
525
+ }
526
+
527
+ .app-sidebar > * {
528
+ position: relative;
529
+ z-index: 1;
530
+ }
531
+
532
+ .app-sidebar-brand {
533
+ display: flex;
534
+ min-height: 4.75rem;
535
+ align-items: center;
536
+ gap: 0.75rem;
537
+ padding: 1rem 1.25rem;
538
+ }
539
+
540
+ .app-sidebar-logo {
541
+ display: inline-flex;
542
+ height: 2.4rem;
543
+ width: 2.4rem;
544
+ align-items: center;
545
+ justify-content: center;
546
+ border-radius: var(--radius-lg);
547
+ background: linear-gradient(135deg, var(--brand-primary), var(--brand-accent));
548
  color: #FFFFFF;
549
+ box-shadow: 0 10px 28px rgba(37, 99, 235, 0.28);
550
+ }
551
+
552
+ .app-sidebar-title {
553
+ display: flex;
554
+ flex-direction: column;
555
+ line-height: 1.15;
556
+ }
557
+
558
+ .app-sidebar-title strong {
559
+ font-size: 1.1rem;
560
+ letter-spacing: 0;
561
+ }
562
+
563
+ .app-sidebar-title span {
564
+ margin-top: 0.15rem;
565
+ color: var(--sidebar-muted);
566
+ font-size: 0.72rem;
567
+ font-weight: 600;
568
+ letter-spacing: 0.08em;
569
+ text-transform: uppercase;
570
+ }
571
+
572
+ .app-sidebar-user {
573
+ flex-shrink: 0;
574
+ border-top: 1px solid var(--sidebar-border);
575
+ padding: 0.875rem;
576
+ }
577
+
578
+ .app-main {
579
+ display: flex;
580
+ min-width: 0;
581
+ flex: 1;
582
+ flex-direction: column;
583
+ }
584
+
585
+ .app-topbar {
586
+ display: flex;
587
+ min-height: 4rem;
588
+ align-items: center;
589
+ justify-content: space-between;
590
+ gap: 1rem;
591
+ border-bottom: 1px solid var(--border);
592
+ background: color-mix(in srgb, var(--surface) 92%, transparent);
593
+ padding: 0 1.5rem;
594
+ backdrop-filter: blur(12px);
595
+ }
596
+
597
+ .app-topbar-title {
598
+ min-width: 0;
599
+ }
600
+
601
+ .app-topbar-title h1 {
602
+ margin: 0;
603
+ color: var(--text-primary);
604
+ font-size: 1.05rem;
605
+ font-weight: 750;
606
+ letter-spacing: 0;
607
+ }
608
+
609
+ .app-topbar-title p {
610
+ margin: 0.1rem 0 0;
611
+ color: var(--text-muted);
612
+ font-size: 0.78rem;
613
+ font-weight: 500;
614
+ }
615
+
616
+ .app-content {
617
+ flex: 1;
618
+ overflow-y: auto;
619
+ padding: clamp(1rem, 2vw, 2rem);
620
+ }
621
+
622
+ .mobile-sidebar-toggle,
623
+ .mobile-sidebar-close {
624
+ display: none;
625
+ }
626
+
627
+ .sidebar-scrim {
628
+ display: none;
629
+ }
630
+
631
+ .control-button {
632
+ display: inline-flex;
633
+ height: 2.35rem;
634
+ min-width: 2.35rem;
635
+ align-items: center;
636
+ justify-content: center;
637
+ border: 1px solid var(--border);
638
+ border-radius: var(--radius-lg);
639
+ background: var(--surface);
640
+ color: var(--text-muted);
641
+ box-shadow: var(--shadow-card);
642
+ transition: background-color 0.16s ease, border-color 0.16s ease, color 0.16s ease, box-shadow 0.16s ease;
643
+ }
644
+
645
+ .control-button:hover {
646
+ border-color: var(--border-strong);
647
+ background: var(--surface-muted);
648
+ color: var(--brand-primary);
649
+ }
650
+
651
+ .control-button:focus-visible {
652
+ outline: none;
653
+ box-shadow: 0 0 0 4px var(--focus-ring);
654
+ }
655
+
656
+ .surface-panel {
657
+ border: 1px solid var(--border);
658
+ border-radius: var(--radius-lg);
659
+ background: var(--surface);
660
+ box-shadow: var(--shadow-card);
661
+ }
662
+
663
+ .page-kicker {
664
+ color: var(--brand-primary);
665
+ font-size: 0.74rem;
666
+ font-weight: 750;
667
+ letter-spacing: 0.08em;
668
+ text-transform: uppercase;
669
+ }
670
+
671
+ .text-balance {
672
+ text-wrap: balance;
673
+ }
674
+
675
+ main :is(input, select, textarea, .form-control, .form-select) {
676
+ border-color: var(--border) !important;
677
+ border-radius: var(--radius-lg) !important;
678
+ background-color: var(--surface) !important;
679
+ color: var(--text-primary) !important;
680
+ }
681
+
682
+ main :is(input, select, textarea, .form-control, .form-select):focus {
683
+ border-color: var(--brand-primary) !important;
684
+ outline: none !important;
685
+ box-shadow: 0 0 0 4px var(--focus-ring) !important;
686
+ }
687
+
688
+ main :is(input, textarea)::placeholder {
689
+ color: var(--text-soft) !important;
690
+ }
691
+
692
+ main table {
693
+ width: 100%;
694
+ border-collapse: separate;
695
+ border-spacing: 0;
696
+ }
697
+
698
+ main thead tr {
699
+ background: var(--surface-muted) !important;
700
+ }
701
+
702
+ main th {
703
+ color: var(--text-muted) !important;
704
+ white-space: nowrap;
705
+ }
706
+
707
+ main td {
708
+ vertical-align: middle;
709
+ }
710
+
711
+ main tbody tr {
712
+ transition: background-color 0.14s ease;
713
+ }
714
+
715
+ main tbody tr:hover {
716
+ background: var(--surface-muted) !important;
717
+ }
718
+
719
+ main .shadow-\[0_18px_50px_rgba\(15\,23\,42\,0\.08\)\],
720
+ main .shadow-\[0_18px_50px_rgba\(15\,23\,42\,0\.12\)\] {
721
+ box-shadow: var(--shadow-card) !important;
722
+ }
723
+
724
+ main .rounded-\[32px\],
725
+ main .rounded-\[28px\],
726
+ main .rounded-\[24px\] {
727
+ border-radius: var(--radius-lg) !important;
728
+ }
729
+
730
+ .flatpickr-calendar {
731
+ border-color: var(--border);
732
+ border-radius: var(--radius-lg);
733
+ box-shadow: var(--shadow-popover);
734
+ }
735
+
736
+ html.dark a,
737
+ html.dark .btn-link,
738
+ html.dark .text-violet-500,
739
+ html.dark .text-violet-600,
740
+ html.dark .text-violet-700,
741
+ html.dark .text-purple-600,
742
+ html.dark .text-purple-700,
743
+ html.dark .text-indigo-600,
744
+ html.dark .text-indigo-700 {
745
+ color: var(--brand-primary) !important;
746
+ }
747
+
748
+ html.dark .bg-white,
749
+ html.dark .bg-card,
750
+ html.dark .bg-popover {
751
+ background-color: var(--surface) !important;
752
+ }
753
+
754
+ html.dark .bg-slate-50,
755
+ html.dark .bg-slate-50\/40,
756
+ html.dark .bg-slate-50\/50,
757
+ html.dark .bg-slate-50\/60,
758
+ html.dark .bg-slate-50\/70,
759
+ html.dark .bg-muted {
760
+ background-color: var(--surface-muted) !important;
761
+ }
762
+
763
+ html.dark .border-slate-100,
764
+ html.dark .border-slate-200,
765
+ html.dark .border-slate-300,
766
+ html.dark .border-border,
767
+ html.dark .divide-slate-100,
768
+ html.dark .divide-slate-200 {
769
+ border-color: var(--border) !important;
770
+ }
771
+
772
+ html.dark .text-slate-950,
773
+ html.dark .text-slate-900,
774
+ html.dark .text-slate-800,
775
+ html.dark .text-slate-700,
776
+ html.dark .text-foreground,
777
+ html.dark .text-card-foreground,
778
+ html.dark .text-popover-foreground {
779
+ color: var(--text-primary) !important;
780
+ }
781
+
782
+ html.dark .text-slate-600,
783
+ html.dark .text-slate-500,
784
+ html.dark .text-muted,
785
+ html.dark .text-muted-foreground {
786
+ color: var(--text-muted) !important;
787
+ }
788
+
789
+ html.dark .text-slate-400,
790
+ html.dark .text-slate-300 {
791
+ color: var(--text-soft) !important;
792
+ }
793
+
794
+ html.dark .btn-primary,
795
+ html.dark .bg-primary,
796
+ html.dark .bg-violet-500,
797
+ html.dark .bg-violet-600,
798
+ html.dark .bg-violet-700,
799
+ html.dark .bg-purple-500,
800
+ html.dark .bg-purple-600,
801
+ html.dark .bg-indigo-500,
802
+ html.dark .bg-indigo-600 {
803
+ background-color: var(--brand-primary) !important;
804
+ border-color: var(--brand-primary) !important;
805
+ color: #FFFFFF !important;
806
+ }
807
+
808
+ html.dark .hover\:bg-violet-600:hover,
809
+ html.dark .hover\:bg-violet-700:hover,
810
+ html.dark .hover\:bg-purple-700:hover,
811
+ html.dark .hover\:bg-indigo-100:hover {
812
+ background-color: var(--brand-primary-hover) !important;
813
+ color: #FFFFFF !important;
814
+ }
815
+
816
+ @media (max-width: 1023px) {
817
+ .app-shell {
818
+ display: block;
819
+ min-height: 100vh;
820
+ overflow: auto;
821
+ }
822
+
823
+ .app-sidebar {
824
+ position: fixed;
825
+ inset: 0 auto 0 0;
826
+ z-index: 60;
827
+ width: min(18rem, calc(100vw - 3rem));
828
+ transform: translateX(-100%);
829
+ transition: transform 0.22s ease;
830
+ }
831
+
832
+ body.tts-sidebar-open .app-sidebar {
833
+ transform: translateX(0);
834
+ }
835
+
836
+ .sidebar-scrim {
837
+ position: fixed;
838
+ inset: 0;
839
+ z-index: 50;
840
+ display: none;
841
+ border: 0;
842
+ background: rgba(15, 23, 42, 0.48);
843
+ }
844
+
845
+ body.tts-sidebar-open .sidebar-scrim {
846
+ display: block;
847
+ }
848
+
849
+ .mobile-sidebar-toggle,
850
+ .mobile-sidebar-close {
851
+ display: inline-flex;
852
+ }
853
+
854
+ .app-main {
855
+ min-height: 100vh;
856
+ }
857
+
858
+ .app-topbar {
859
+ min-height: 3.75rem;
860
+ padding: 0 1rem;
861
+ }
862
+
863
+ .app-content {
864
+ padding: 1rem;
865
+ }
866
+
867
+ .app-topbar-title p {
868
+ display: none;
869
+ }
870
+ }
871
+
872
+ @media (max-width: 640px) {
873
+ html,
874
+ body {
875
+ font-size: 14px;
876
+ }
877
+
878
+ .app-content {
879
+ padding: 0.875rem;
880
+ }
881
+
882
+ main table {
883
+ min-width: 760px;
884
+ }
885
  }