fix(custom-metrics): say "name taken" only for a name clash

The form read every 409 as "a metric with this name already exists".
Now that it keeps one idempotency key across taps, a second tap while
the first create is still running also answers 409, and the form told
the person their name was taken. The create and rename routes send
`customMetric.name_taken` with the clash, and the form matches the code.
This commit is contained in:
Marc-André Bombeck committed 2026-11-12 12:52:39 +01:00
1 parent 671245c782
commit 706af61cdd
6 files changed
+14 -3

No files matched your search

@@ -225,6 +225,7 @@ describe("PATCH /api/custom-metrics/[id]", () => {
const res = await PATCH(patchReq({ name: "Other" }), params("cm-1"));
expect(res.status).toBe(409);
expect((await res.json()).meta.errorCode).toBe("customMetric.name_taken");
expect(prisma.customMetric.update).not.toHaveBeenCalled();
});
});
+3 -1
View File
@@ -111,7 +111,9 @@ export const PATCH = apiHandler(
select: { id: true },
});
if (clash) {
return apiError("A custom metric with this name already exists", 409);
return apiError("A custom metric with this name already exists", 409, {
errorCode: "customMetric.name_taken",
});
}
}
@@ -134,6 +134,9 @@ describe("POST /api/custom-metrics", () => {
const res = await POST(postReq({ name: "Grip strength", unit: "kg" }));
expect(res.status).toBe(409);
// The form names the clash by this code: a 409 without it can be the
// first save still running under the same key.
expect((await res.json()).meta.errorCode).toBe("customMetric.name_taken");
expect(prisma.customMetric.create).not.toHaveBeenCalled();
});
+3 -1
View File
@@ -100,7 +100,9 @@ async function postCustomMetric(request: NextRequest) {
select: { id: true, deletedAt: true },
});
if (existing && existing.deletedAt === null) {
return apiError("A custom metric with this name already exists", 409);
return apiError("A custom metric with this name already exists", 409, {
errorCode: "customMetric.name_taken",
});
}
// Field-by-field assignment — never spread `parsed.data`.