Skip to content

Commit 63720ac

Browse files
committed
Fix grower save bug, add name sorting, and clarify status meanings
- #155: Remove service-layer permission check from UpdateGrowerAsync and align the PUT controller endpoint to use EditModule policy (matching the POST). The extra IsAuthorized call in the service returned null for users without explicit module Edit permission, producing the 'Unable to save changes' error. - #161: Add ORDER BY GrowerName to all grower list queries in the repository so the list is consistently alphabetical across filters. - #149: Add tooltip (title attribute) to status badges in Grower Index and Edit pages explaining the difference between Active, Inactive, and Exited so users understand what each status means on hover. https://claude.ai/code/session_013s3rc5YSBFQ5U9Ef1bH13c
1 parent a8bbe4f commit 63720ac

5 files changed

Lines changed: 33 additions & 20 deletions

File tree

Client/Modules/Grower/Edit.razor

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@ else
2929
@if (_growerId > 0)
3030
{
3131
<div class="d-flex align-items-center gap-2 flex-wrap">
32-
<span class="badge @GetStatusBadgeClass(_grower.Status)">@GetStatusText(_grower.Status)</span>
32+
<span class="badge @GetStatusBadgeClass(_grower.Status)"
33+
title="@GetStatusDescription(_grower.Status)"
34+
aria-label="Status: @GetStatusText(_grower.Status) — @GetStatusDescription(_grower.Status)">
35+
@GetStatusText(_grower.Status)
36+
</span>
3337
@if (UserSecurity.IsAuthorized(PageState.User, AppRoleNames.TenTreesAdmin))
3438
{
3539
@if (_grower.Status == GrowerStatus.Active)
@@ -360,7 +364,7 @@ else
360364
{
361365
<span class="badge bg-secondary me-1">General</span>
362366
}
363-
<small class="text-muted">@note.CreatedOn.ToLocalTime().ToString("yyyy-MM-dd HH:mm") — @note.CreatedBy</small>
367+
<small class="text-muted">@note.CreatedOn.ToLocalTime().ToString("yyyy-MM-dd HH:mm") @note.CreatedBy</small>
364368
</div>
365369
<div class="text-body" style="white-space: pre-wrap;">@note.Text</div>
366370
</li>
@@ -657,4 +661,12 @@ else
657661
GrowerStatus.Exited => "Exited",
658662
_ => status.ToString()
659663
};
664+
665+
private string GetStatusDescription(GrowerStatus status) => status switch
666+
{
667+
GrowerStatus.Active => "Currently participating in the program.",
668+
GrowerStatus.Inactive => "Temporarily paused (e.g. illness or travel) but has not left the program.",
669+
GrowerStatus.Exited => "Has permanently left the program.",
670+
_ => string.Empty
671+
};
660672
}

Client/Modules/Grower/Index.razor

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@
123123
<td>@grower.GrowerName</td>
124124
<td>@GetVillageName(grower.VillageId)</td>
125125
<td>
126-
<span class="badge @GetStatusBadgeClass(grower.Status)">
126+
<span class="badge @GetStatusBadgeClass(grower.Status)"
127+
title="@GetStatusDescription(grower.Status)">
127128
@GetStatusText(grower.Status)
128129
</span>
129130
</td>
@@ -342,6 +343,14 @@
342343
};
343344
}
344345

346+
private string GetStatusDescription(GrowerStatus status) => status switch
347+
{
348+
GrowerStatus.Active => "Currently participating in the program.",
349+
GrowerStatus.Inactive => "Temporarily paused (e.g. illness or travel) but has not left the program.",
350+
GrowerStatus.Exited => "Has permanently left the program.",
351+
_ => string.Empty
352+
};
353+
345354
private async Task DeleteGrower(Models.Grower grower)
346355
{
347356
try

Server/Controllers/GrowerController.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ public async Task<ActionResult<GrowerStatusSummary>> GetStatusSummary(int? villa
179179
}
180180

181181
[HttpPut("{id}")]
182-
[Authorize]
182+
[Authorize(Policy = PolicyNames.EditModule)]
183183
public async Task<ActionResult<Models.Grower>> Put(int id, [FromBody] Models.Grower grower, int moduleId)
184184
{
185185
try

Server/Repository/GrowerRepository.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,32 +58,32 @@ public Models.Grower GetGrower(int growerId, bool tracking)
5858
query = query.Where(g => g.VillageId == villageId.Value);
5959
}
6060

61-
return query.ToList();
61+
return query.OrderBy(g => g.GrowerName).ToList();
6262
}
6363

6464
public IEnumerable<Models.Grower> GetGrowersByVillage(int villageId)
6565
{
6666
using var db = _factory.CreateDbContext();
67-
return db.Grower.Where(g => g.VillageId == villageId).ToList();
67+
return db.Grower.Where(g => g.VillageId == villageId).OrderBy(g => g.GrowerName).ToList();
6868
}
6969

7070
public IEnumerable<Models.Grower> GetGrowersByMentor(string mentorUsername)
7171
{
7272
using var db = _factory.CreateDbContext();
73-
return db.Grower.Where(g => g.MentorUsername == mentorUsername).ToList();
73+
return db.Grower.Where(g => g.MentorUsername == mentorUsername).OrderBy(g => g.GrowerName).ToList();
7474
}
7575

7676
public IEnumerable<Models.Grower> GetGrowersByStatus(GrowerStatus status, int? villageId = null)
7777
{
7878
using var db = _factory.CreateDbContext();
7979
var query = db.Grower.Where(g => g.Status == status);
80-
80+
8181
if (villageId.HasValue)
8282
{
8383
query = query.Where(g => g.VillageId == villageId.Value);
8484
}
85-
86-
return query.ToList();
85+
86+
return query.OrderBy(g => g.GrowerName).ToList();
8787
}
8888

8989
public IEnumerable<Models.Grower> GetActiveGrowers(int? villageId = null)

Server/Services/GrowerService.cs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -178,16 +178,8 @@ public Task<GrowerStatusSummary> GetStatusSummaryAsync(int? villageId = null)
178178

179179
public Task<Models.Grower> UpdateGrowerAsync(Models.Grower grower, int moduleId)
180180
{
181-
if (_userPermissions.IsAuthorized(_accessor.HttpContext.User, _alias.SiteId, EntityNames.Module, moduleId, PermissionNames.Edit))
182-
{
183-
grower = _growerRepository.UpdateGrower(grower);
184-
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Grower Updated {Grower}", grower);
185-
}
186-
else
187-
{
188-
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Grower Update Attempt {Grower}", grower);
189-
grower = null;
190-
}
181+
grower = _growerRepository.UpdateGrower(grower);
182+
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Grower Updated {Grower}", grower);
191183
return Task.FromResult(grower);
192184
}
193185

0 commit comments

Comments
 (0)