Polish Store endpoints: 409 conflicts and confirm-before-delete

Backend pre-checks duplicate names on POST/PUT and returns 409
Conflict (replacing the previous 500 from the unique constraint).
DELETE-with-active-lists also returns 409 instead of 400 for
semantic accuracy.

Frontend addStore and saveEdit now surface API errors via toast
instead of failing silently. Delete is now gated by a confirmation
modal so accidental clicks no longer destroy data.
This commit is contained in:
Josh Rogers
2026-05-08 21:07:57 -05:00
parent 7fcae09afb
commit d4db819e72
3 changed files with 114 additions and 19 deletions
@@ -77,10 +77,49 @@ public class StoreEndpointsTests : AuthenticatedIntegrationTest
var response = await Client.DeleteAsync($"/api/stores/{store.Id}");
await Assert.That(response.StatusCode).IsEqualTo(HttpStatusCode.BadRequest);
await Assert.That(response.StatusCode).IsEqualTo(HttpStatusCode.Conflict);
await Assert.That(await UseDbAsync(db => db.Stores.AnyAsync(s => s.Id == store.Id))).IsTrue();
}
[Test]
public async Task Create_returns_409_for_duplicate_name()
{
await Data.CreateStoreAsync(b => b.Named("Kroger"));
var response = await Client.PostAsJsonAsync("/api/stores",
new StoreEndpoints.CreateStoreRequest("Kroger"));
await Assert.That(response.StatusCode).IsEqualTo(HttpStatusCode.Conflict);
await Assert.That(await UseDbAsync(db => db.Stores.CountAsync())).IsEqualTo(1);
}
[Test]
public async Task Update_returns_409_when_renaming_to_existing_name()
{
await Data.CreateStoreAsync(b => b.Named("Kroger"));
var publix = await Data.CreateStoreAsync(b => b.Named("Publix"));
var response = await Client.PutAsJsonAsync($"/api/stores/{publix.Id}",
new StoreEndpoints.UpdateStoreRequest("Kroger", 0));
await Assert.That(response.StatusCode).IsEqualTo(HttpStatusCode.Conflict);
var refreshed = await UseDbAsync(db => db.Stores.SingleAsync(s => s.Id == publix.Id));
await Assert.That(refreshed.Name).IsEqualTo("Publix");
}
[Test]
public async Task Update_allows_keeping_same_name()
{
var store = await Data.CreateStoreAsync(b => b.Named("Kroger"));
var response = await Client.PutAsJsonAsync($"/api/stores/{store.Id}",
new StoreEndpoints.UpdateStoreRequest("Kroger", 7));
await Assert.That(response.StatusCode).IsEqualTo(HttpStatusCode.OK);
var refreshed = await UseDbAsync(db => db.Stores.SingleAsync(s => s.Id == store.Id));
await Assert.That(refreshed.SortOrder).IsEqualTo(7);
}
[Test]
public async Task Endpoints_require_authentication()
{