Spaces:
Running
Running
User commited on
Commit ·
d594ffb
1
Parent(s): ecb51fa
update: issue form
Browse files
TaskTrackingSystem.WebApp/Components/Pages/Features/Issues/AddIssue.razor
CHANGED
|
@@ -33,205 +33,183 @@
|
|
| 33 |
}
|
| 34 |
else
|
| 35 |
{
|
| 36 |
-
<div class="
|
| 37 |
-
<div class="
|
| 38 |
-
<div class="
|
| 39 |
-
<
|
| 40 |
-
|
| 41 |
-
<
|
| 42 |
-
|
| 43 |
-
@foreach (var task in tasks)
|
| 44 |
-
{
|
| 45 |
-
<option value="@task.Id">@task.Title</option>
|
| 46 |
-
}
|
| 47 |
-
</select>
|
| 48 |
-
@if (isTaskError)
|
| 49 |
{
|
| 50 |
-
<
|
| 51 |
}
|
| 52 |
-
</
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
<
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
@if (isTitleError)
|
| 59 |
-
{
|
| 60 |
-
<p class="mt-1 text-xs text-red-500">@titleErrorMessage</p>
|
| 61 |
-
}
|
| 62 |
-
</div>
|
| 63 |
-
|
| 64 |
-
<div class="md:col-span-2">
|
| 65 |
-
<label class="block text-sm font-medium text-slate-700 mb-1.5">Description</label>
|
| 66 |
-
<textarea @bind="formDescription" rows="4" placeholder="Add a short note about the work you did..."
|
| 67 |
-
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-violet-600 focus:border-transparent transition-all"></textarea>
|
| 68 |
-
</div>
|
| 69 |
-
|
| 70 |
-
<div>
|
| 71 |
-
<label class="block text-sm font-medium text-slate-700 mb-1.5">Project</label>
|
| 72 |
-
<input value="@SelectedProjectLabel" readonly
|
| 73 |
-
class="w-full rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-sm text-slate-700 focus:outline-none" />
|
| 74 |
-
</div>
|
| 75 |
-
|
| 76 |
-
<div>
|
| 77 |
-
<label class="block text-sm font-medium text-slate-700 mb-1.5">Assignee</label>
|
| 78 |
-
<div class="relative">
|
| 79 |
-
<button type="button"
|
| 80 |
-
@onclick="ToggleAssigneeDropdown"
|
| 81 |
-
class="flex h-11 w-full items-center justify-between rounded-lg border border-slate-200 bg-white px-3 text-left text-sm text-slate-900 shadow-sm transition-colors hover:border-violet-300 hover:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-violet-600">
|
| 82 |
-
<span class="truncate @(formAssignedTo.HasValue ? "font-medium text-slate-900" : "text-slate-400")">@SelectedAssigneeLabel</span>
|
| 83 |
-
<i data-lucide="chevron-down" class="ml-3 h-4 w-4 shrink-0 text-slate-400"></i>
|
| 84 |
-
</button>
|
| 85 |
-
|
| 86 |
-
@if (isAssigneeDropdownOpen)
|
| 87 |
-
{
|
| 88 |
-
<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)]">
|
| 89 |
-
<div class="flex items-center justify-end">
|
| 90 |
-
<button type="button"
|
| 91 |
-
@onclick="CloseAssigneeDropdown"
|
| 92 |
-
class="inline-flex h-7 w-7 items-center justify-center rounded-lg text-slate-400 transition-colors hover:bg-slate-100 hover:text-slate-600"
|
| 93 |
-
aria-label="Close assignee dropdown">
|
| 94 |
-
<i data-lucide="x" class="h-4 w-4"></i>
|
| 95 |
-
</button>
|
| 96 |
-
</div>
|
| 97 |
-
<div class="relative">
|
| 98 |
-
<input @bind="assigneeSearchInput"
|
| 99 |
-
@onfocus="EnsureAssigneeDropdownOpen"
|
| 100 |
-
type="text"
|
| 101 |
-
placeholder="Search project members..."
|
| 102 |
-
class="w-full rounded-xl border border-slate-200 bg-white py-2 pl-9 pr-3 text-sm text-slate-900 placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-violet-600 focus:border-transparent" />
|
| 103 |
-
<i data-lucide="search" class="absolute left-3 top-2.5 h-4 w-4 text-slate-400"></i>
|
| 104 |
-
</div>
|
| 105 |
-
|
| 106 |
-
@if (isLoadingProjectMembers)
|
| 107 |
-
{
|
| 108 |
-
<div class="mt-3 rounded-xl border border-dashed border-slate-200 bg-slate-50 px-3 py-4 text-center text-xs text-slate-500">
|
| 109 |
-
Loading project members...
|
| 110 |
-
</div>
|
| 111 |
-
}
|
| 112 |
-
else
|
| 113 |
-
{
|
| 114 |
-
<div class="mt-3 max-h-56 overflow-y-auto space-y-2 pr-1">
|
| 115 |
-
<button type="button"
|
| 116 |
-
@onclick="() => SetFormAssignee(null)"
|
| 117 |
-
class="flex w-full items-center gap-3 rounded-xl border px-3 py-3 text-left transition-colors @(formAssignedTo.HasValue ? "border-slate-200 bg-white hover:bg-slate-50" : "border-violet-600 bg-violet-50/40")">
|
| 118 |
-
<span class="flex h-9 w-9 items-center justify-center rounded-full bg-slate-100 text-slate-500 text-xs font-bold shrink-0">
|
| 119 |
-
<i data-lucide="user-minus" class="h-4 w-4"></i>
|
| 120 |
-
</span>
|
| 121 |
-
<div class="min-w-0 flex-1">
|
| 122 |
-
<p class="text-sm font-semibold text-slate-900">Unassigned</p>
|
| 123 |
-
</div>
|
| 124 |
-
</button>
|
| 125 |
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
<div class="min-w-0 flex-1">
|
| 136 |
-
<p class="truncate text-sm font-semibold text-slate-900">@user.FirstName @user.LastName</p>
|
| 137 |
-
<p class="truncate text-xs text-slate-500">@@@user.Username</p>
|
| 138 |
-
</div>
|
| 139 |
-
</button>
|
| 140 |
-
}
|
| 141 |
-
|
| 142 |
-
@if (!FilteredProjectMembers.Any())
|
| 143 |
-
{
|
| 144 |
-
<div class="rounded-xl border border-dashed border-slate-200 bg-slate-50 px-3 py-4 text-center text-xs text-slate-500">
|
| 145 |
-
No members match the current search.
|
| 146 |
-
</div>
|
| 147 |
-
}
|
| 148 |
-
</div>
|
| 149 |
-
}
|
| 150 |
-
</div>
|
| 151 |
-
}
|
| 152 |
-
</div>
|
| 153 |
-
</div>
|
| 154 |
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
|
|
|
| 166 |
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
<option value="@AppTaskStatus.Done">Done</option>
|
| 181 |
-
</select>
|
| 182 |
-
</div>
|
| 183 |
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
<option value="@TaskPriority.Medium">Medium</option>
|
| 189 |
-
<option value="@TaskPriority.High">High</option>
|
| 190 |
-
</select>
|
| 191 |
-
</div>
|
| 192 |
</div>
|
| 193 |
|
| 194 |
-
<div
|
| 195 |
-
<
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
</
|
| 201 |
</div>
|
| 202 |
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
<
|
| 206 |
-
|
| 207 |
-
|
|
|
|
|
|
|
|
|
|
| 208 |
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
<
|
| 212 |
-
|
| 213 |
</div>
|
| 214 |
|
| 215 |
-
<div
|
| 216 |
-
<
|
| 217 |
-
|
| 218 |
-
<
|
| 219 |
-
|
| 220 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 221 |
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 227 |
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 232 |
</div>
|
| 233 |
</div>
|
| 234 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 235 |
</div>
|
| 236 |
}
|
| 237 |
</div>
|
|
@@ -267,6 +245,7 @@
|
|
| 267 |
private string formTitle = "";
|
| 268 |
private string? formDescription;
|
| 269 |
private long? formAssignedTo;
|
|
|
|
| 270 |
private decimal? formActualHours = 7m;
|
| 271 |
private DateTime formStartDate = DateTime.Today;
|
| 272 |
private DateTime formDueDate = DateTime.Today;
|
|
@@ -490,6 +469,7 @@
|
|
| 490 |
Title = formTitle.Trim(),
|
| 491 |
Description = formDescription,
|
| 492 |
AssignedTo = formAssignedTo,
|
|
|
|
| 493 |
ActualHours = formActualHours,
|
| 494 |
StartDate = formStartDate,
|
| 495 |
DueDate = formDueDate,
|
|
@@ -526,6 +506,7 @@
|
|
| 526 |
formTitle = "";
|
| 527 |
formDescription = null;
|
| 528 |
formAssignedTo = currentUserId;
|
|
|
|
| 529 |
formActualHours = 7m;
|
| 530 |
formStartDate = DateTime.Today;
|
| 531 |
formDueDate = DateTime.Today;
|
|
|
|
| 33 |
}
|
| 34 |
else
|
| 35 |
{
|
| 36 |
+
<div class="w-full rounded-2xl border border-slate-200 bg-white p-6 shadow-sm">
|
| 37 |
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
| 38 |
+
<div class="md:col-span-2">
|
| 39 |
+
<label class="block text-sm font-medium text-slate-700 mb-1.5">Task <span class="text-red-500">*</span></label>
|
| 40 |
+
<select value="@formTaskId" @onchange="OnTaskChanged" class="w-full rounded-lg border @(isTaskError ? "border-red-500 focus:ring-red-500" : "border-slate-200 focus:ring-violet-600") px-3 py-2 text-sm text-slate-900 focus:outline-none focus:ring-2 focus:border-transparent transition-all">
|
| 41 |
+
<option value="0">Select task...</option>
|
| 42 |
+
@foreach (var task in tasks)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
{
|
| 44 |
+
<option value="@task.Id">@task.Title</option>
|
| 45 |
}
|
| 46 |
+
</select>
|
| 47 |
+
@if (isTaskError)
|
| 48 |
+
{
|
| 49 |
+
<p class="mt-1 text-xs text-red-500">@taskErrorMessage</p>
|
| 50 |
+
}
|
| 51 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|
| 53 |
+
<div class="md:col-span-2">
|
| 54 |
+
<label class="block text-sm font-medium text-slate-700 mb-1.5">Issue Title <span class="text-red-500">*</span></label>
|
| 55 |
+
<input @bind="formTitle" type="text" placeholder="What did you work on?"
|
| 56 |
+
class="w-full rounded-lg border @(isTitleError ? "border-red-500 focus:ring-red-500" : "border-slate-200 focus:ring-violet-600") px-3 py-2 text-sm text-slate-900 placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:border-transparent transition-all" />
|
| 57 |
+
@if (isTitleError)
|
| 58 |
+
{
|
| 59 |
+
<p class="mt-1 text-xs text-red-500">@titleErrorMessage</p>
|
| 60 |
+
}
|
| 61 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
|
| 63 |
+
<div class="md:col-span-2">
|
| 64 |
+
<label class="block text-sm font-medium text-slate-700 mb-1.5">Description</label>
|
| 65 |
+
<textarea @bind="formDescription" rows="4" placeholder="Add a short note about the work you did..."
|
| 66 |
+
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-violet-600 focus:border-transparent transition-all"></textarea>
|
| 67 |
+
</div>
|
| 68 |
|
| 69 |
+
<div>
|
| 70 |
+
<label class="block text-sm font-medium text-slate-700 mb-1.5">Estimated Hours</label>
|
| 71 |
+
<input @bind="formEstimatedHours" type="number" step="0.25" min="0"
|
| 72 |
+
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-violet-600 focus:border-transparent transition-all"
|
| 73 |
+
placeholder="e.g. 4" />
|
| 74 |
+
</div>
|
| 75 |
|
| 76 |
+
<div>
|
| 77 |
+
<label class="block text-sm font-medium text-slate-700 mb-1.5">Actual Hours</label>
|
| 78 |
+
<input @bind="formActualHours" type="number" step="0.25" min="0" max="7"
|
| 79 |
+
class="w-full rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-900 focus:outline-none focus:ring-2 focus:ring-violet-600 focus:border-transparent transition-all"
|
| 80 |
+
placeholder="e.g. 7" />
|
| 81 |
+
<p class="mt-1 text-xs text-slate-500">Use the total hours you spent today. For your standard day, this is usually 7 hours.</p>
|
| 82 |
+
</div>
|
| 83 |
|
| 84 |
+
<div>
|
| 85 |
+
<label class="block text-sm font-medium text-slate-700 mb-1.5">Start Date</label>
|
| 86 |
+
<input @bind="formStartDate" type="date"
|
| 87 |
+
class="w-full rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-900 focus:outline-none focus:ring-2 focus:ring-violet-600 focus:border-transparent transition-all" />
|
| 88 |
+
</div>
|
|
|
|
|
|
|
|
|
|
| 89 |
|
| 90 |
+
<div>
|
| 91 |
+
<label class="block text-sm font-medium text-slate-700 mb-1.5">Due Date</label>
|
| 92 |
+
<input @bind="formDueDate" type="date"
|
| 93 |
+
class="w-full rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-900 focus:outline-none focus:ring-2 focus:ring-violet-600 focus:border-transparent transition-all" />
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
</div>
|
| 95 |
|
| 96 |
+
<div>
|
| 97 |
+
<label class="block text-sm font-medium text-slate-700 mb-1.5">Status</label>
|
| 98 |
+
<select @bind="formStatusId" class="w-full rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-900 focus:outline-none focus:ring-2 focus:ring-violet-600 focus:border-transparent transition-all">
|
| 99 |
+
<option value="@AppTaskStatus.Todo">To Do</option>
|
| 100 |
+
<option value="@AppTaskStatus.InProgress">In Progress</option>
|
| 101 |
+
<option value="@AppTaskStatus.Done">Done</option>
|
| 102 |
+
</select>
|
| 103 |
</div>
|
| 104 |
|
| 105 |
+
<div>
|
| 106 |
+
<label class="block text-sm font-medium text-slate-700 mb-1.5">Priority</label>
|
| 107 |
+
<select @bind="formPriorityId" class="w-full rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-900 focus:outline-none focus:ring-2 focus:ring-violet-600 focus:border-transparent transition-all">
|
| 108 |
+
<option value="@TaskPriority.Low">Low</option>
|
| 109 |
+
<option value="@TaskPriority.Medium">Medium</option>
|
| 110 |
+
<option value="@TaskPriority.High">High</option>
|
| 111 |
+
</select>
|
| 112 |
+
</div>
|
| 113 |
|
| 114 |
+
<div>
|
| 115 |
+
<label class="block text-sm font-medium text-slate-700 mb-1.5">Project</label>
|
| 116 |
+
<input value="@SelectedProjectLabel" readonly
|
| 117 |
+
class="w-full rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-sm text-slate-700 focus:outline-none" />
|
| 118 |
</div>
|
| 119 |
|
| 120 |
+
<div>
|
| 121 |
+
<label class="block text-sm font-medium text-slate-700 mb-1.5">Assignee</label>
|
| 122 |
+
<div class="relative">
|
| 123 |
+
<button type="button"
|
| 124 |
+
@onclick="ToggleAssigneeDropdown"
|
| 125 |
+
class="flex h-11 w-full items-center justify-between rounded-lg border border-slate-200 bg-white px-3 text-left text-sm text-slate-900 shadow-sm transition-colors hover:border-violet-300 hover:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-violet-600">
|
| 126 |
+
<span class="truncate @(formAssignedTo.HasValue ? "font-medium text-slate-900" : "text-slate-400")">@SelectedAssigneeLabel</span>
|
| 127 |
+
<i data-lucide="chevron-down" class="ml-3 h-4 w-4 shrink-0 text-slate-400"></i>
|
| 128 |
+
</button>
|
| 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"
|
| 136 |
+
class="inline-flex h-7 w-7 items-center justify-center rounded-lg text-slate-400 transition-colors hover:bg-slate-100 hover:text-slate-600"
|
| 137 |
+
aria-label="Close assignee dropdown">
|
| 138 |
+
<i data-lucide="x" class="h-4 w-4"></i>
|
| 139 |
+
</button>
|
| 140 |
+
</div>
|
| 141 |
+
<div class="relative">
|
| 142 |
+
<input @bind="assigneeSearchInput"
|
| 143 |
+
@onfocus="EnsureAssigneeDropdownOpen"
|
| 144 |
+
type="text"
|
| 145 |
+
placeholder="Search project members..."
|
| 146 |
+
class="w-full rounded-xl border border-slate-200 bg-white py-2 pl-9 pr-3 text-sm text-slate-900 placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-violet-600 focus:border-transparent" />
|
| 147 |
+
<i data-lucide="search" class="absolute left-3 top-2.5 h-4 w-4 text-slate-400"></i>
|
| 148 |
+
</div>
|
| 149 |
|
| 150 |
+
@if (isLoadingProjectMembers)
|
| 151 |
+
{
|
| 152 |
+
<div class="mt-3 rounded-xl border border-dashed border-slate-200 bg-slate-50 px-3 py-4 text-center text-xs text-slate-500">
|
| 153 |
+
Loading project members...
|
| 154 |
+
</div>
|
| 155 |
+
}
|
| 156 |
+
else
|
| 157 |
+
{
|
| 158 |
+
<div class="mt-3 max-h-56 overflow-y-auto space-y-2 pr-1">
|
| 159 |
+
<button type="button"
|
| 160 |
+
@onclick="() => SetFormAssignee(null)"
|
| 161 |
+
class="flex w-full items-center gap-3 rounded-xl border px-3 py-3 text-left transition-colors @(formAssignedTo.HasValue ? "border-slate-200 bg-white hover:bg-slate-50" : "border-violet-600 bg-violet-50/40")">
|
| 162 |
+
<span class="flex h-9 w-9 items-center justify-center rounded-full bg-slate-100 text-slate-500 text-xs font-bold shrink-0">
|
| 163 |
+
<i data-lucide="user-minus" class="h-4 w-4"></i>
|
| 164 |
+
</span>
|
| 165 |
+
<div class="min-w-0 flex-1">
|
| 166 |
+
<p class="text-sm font-semibold text-slate-900">Unassigned</p>
|
| 167 |
+
</div>
|
| 168 |
+
</button>
|
| 169 |
|
| 170 |
+
@foreach (var user in FilteredProjectMembers)
|
| 171 |
+
{
|
| 172 |
+
var isSelected = formAssignedTo == user.Id;
|
| 173 |
+
<button type="button"
|
| 174 |
+
@onclick="() => SetFormAssignee(user.Id)"
|
| 175 |
+
class="flex w-full items-center gap-3 rounded-xl border px-3 py-3 text-left transition-colors @(isSelected ? "border-violet-600 bg-violet-50/40" : "border-slate-100 bg-white hover:border-slate-200 hover:bg-slate-50")">
|
| 176 |
+
<span class="flex h-9 w-9 items-center justify-center rounded-full bg-violet-100 text-violet-700 text-xs font-bold shrink-0">
|
| 177 |
+
@(user.FirstName.Length > 0 ? user.FirstName[0].ToString().ToUpper() : "")@(user.LastName.Length > 0 ? user.LastName[0].ToString().ToUpper() : "")
|
| 178 |
+
</span>
|
| 179 |
+
<div class="min-w-0 flex-1">
|
| 180 |
+
<p class="truncate text-sm font-semibold text-slate-900">@user.FirstName @user.LastName</p>
|
| 181 |
+
<p class="truncate text-xs text-slate-500">@@@user.Username</p>
|
| 182 |
+
</div>
|
| 183 |
+
</button>
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
@if (!FilteredProjectMembers.Any())
|
| 187 |
+
{
|
| 188 |
+
<div class="rounded-xl border border-dashed border-slate-200 bg-slate-50 px-3 py-4 text-center text-xs text-slate-500">
|
| 189 |
+
No members match the current search.
|
| 190 |
+
</div>
|
| 191 |
+
}
|
| 192 |
+
</div>
|
| 193 |
+
}
|
| 194 |
+
</div>
|
| 195 |
+
}
|
| 196 |
</div>
|
| 197 |
</div>
|
| 198 |
</div>
|
| 199 |
+
|
| 200 |
+
<div class="mt-6 flex items-center justify-end gap-3">
|
| 201 |
+
<button @onclick="ResetForm" class="inline-flex items-center rounded-lg border border-slate-200 bg-white px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50 transition-colors shadow-sm">
|
| 202 |
+
Reset
|
| 203 |
+
</button>
|
| 204 |
+
<button @onclick="RequestSaveConfirmation" disabled="@isSaving" class="inline-flex items-center rounded-lg bg-violet-600 px-5 py-2 text-sm font-semibold text-white hover:bg-violet-700 disabled:opacity-50 transition-colors shadow-sm">
|
| 205 |
+
@(isSaving ? "Saving..." : "Add Issue")
|
| 206 |
+
</button>
|
| 207 |
+
</div>
|
| 208 |
+
|
| 209 |
+
@if (!string.IsNullOrWhiteSpace(errorMessage))
|
| 210 |
+
{
|
| 211 |
+
<p class="mt-4 text-sm text-red-600">@errorMessage</p>
|
| 212 |
+
}
|
| 213 |
</div>
|
| 214 |
}
|
| 215 |
</div>
|
|
|
|
| 245 |
private string formTitle = "";
|
| 246 |
private string? formDescription;
|
| 247 |
private long? formAssignedTo;
|
| 248 |
+
private decimal? formEstimatedHours;
|
| 249 |
private decimal? formActualHours = 7m;
|
| 250 |
private DateTime formStartDate = DateTime.Today;
|
| 251 |
private DateTime formDueDate = DateTime.Today;
|
|
|
|
| 469 |
Title = formTitle.Trim(),
|
| 470 |
Description = formDescription,
|
| 471 |
AssignedTo = formAssignedTo,
|
| 472 |
+
EstimatedHours = formEstimatedHours,
|
| 473 |
ActualHours = formActualHours,
|
| 474 |
StartDate = formStartDate,
|
| 475 |
DueDate = formDueDate,
|
|
|
|
| 506 |
formTitle = "";
|
| 507 |
formDescription = null;
|
| 508 |
formAssignedTo = currentUserId;
|
| 509 |
+
formEstimatedHours = null;
|
| 510 |
formActualHours = 7m;
|
| 511 |
formStartDate = DateTime.Today;
|
| 512 |
formDueDate = DateTime.Today;
|