Problem
Found while testing FestivalDialog's edit flow (pre-existing, not introduced by #495). All three affect both FestivalDialog.tsx and FestivalEditionManagement.tsx, since they share the same slug helpers and mutation-error pattern.
1. Can't type a dash into the slug field
handleSlugChange runs sanitizeSlug/generateSlug (src/lib/slug.ts) on every keystroke via the controlled input's onChange:
function handleSlugChange(slug: string) {
const cleanSlug = sanitizeSlug(slug);
setFormData((prev) => ({ ...prev, slug: cleanSlug }));
...
}
generateSlug strips leading/trailing hyphens:
Typing "boom-festival" character by character passes through the state "boom-" after the 5th keystroke — at that instant the hyphen is trailing, so it gets stripped immediately, snapping the field back to "boom". The next keystroke ("f") then appends directly to "boom", not "boom-", so a hyphen typed in the middle of a slug can never survive a normal sequence of keystrokes. (The only way to get a hyphenated slug into the field today is to paste the whole value at once, which sidesteps the per-keystroke sanitization.)
2. No client-side uniqueness check
handleSubmit in both dialogs validates only presence and format (isValidSlug) before submitting:
if (!isValidSlug(formData.slug)) {
toast({ title: "Error", description: "Please enter a valid slug", ... });
return;
}
There's no query against existing festivals/editions to warn about a taken slug before hitting submit — the only enforcement is the DB's unique constraint (festivals_slug_unique, festival_editions_slug_festival_unique), discovered only after a failed save.
3. Generic error message on a real unique-constraint conflict
useUpdateFestivalMutation's (and the edition equivalent's) onError doesn't discriminate the failure cause:
onError: (error) => {
console.error("Error updating festival:", error);
toast({ title: "Error", description: "Failed to update festival", variant: "destructive" });
},
A slug conflict (Postgres 23505 unique_violation) surfaces the same generic "Failed to update festival" as any other failure, giving the user no indication of what to actually fix.
Related
#467 already designs the right treatment for this exact class of problem — explicit slug field, format validation, a client-side uniqueness pre-check, and mapping a constraint-violation error to a friendly message — but scoped to the sets/artists edit forms only. This issue is the same treatment needed for the festival/festival-edition edit forms, which #467 doesn't cover.
The dash-typing bug (#1) is a new finding not mentioned in #467's design — worth accounting for there too, since #467 also plans to reuse isValidSlug/sanitizeSlug from src/lib/slug.ts for its new slug field, which would inherit the same per-keystroke corruption unless the field sets its value on blur/paste rather than sanitizing every keystroke.
Suggested fix
- Sanitize on blur (or debounce/only strip trailing hyphens on submit), not on every keystroke — so a hyphen typed mid-string survives.
- Add a lightweight uniqueness query (scoped appropriately — global for festivals, per-festival for editions) run before submit, surfaced as a form/toast warning.
- In both
useUpdateFestivalMutation's and the edition mutation's onError, inspect the Postgrest error code (23505) and show a specific "This slug is already taken" message instead of the generic fallback.
Context
Reported during review of PR #495 (fix-157/finish-mutate-migration), which touches the same files for an unrelated mutateAsync→mutate migration but didn't introduce these bugs.
Problem
Found while testing
FestivalDialog's edit flow (pre-existing, not introduced by #495). All three affect bothFestivalDialog.tsxandFestivalEditionManagement.tsx, since they share the same slug helpers and mutation-error pattern.1. Can't type a dash into the slug field
handleSlugChangerunssanitizeSlug/generateSlug(src/lib/slug.ts) on every keystroke via the controlled input'sonChange:generateSlugstrips leading/trailing hyphens:Typing "boom-festival" character by character passes through the state "boom-" after the 5th keystroke — at that instant the hyphen is trailing, so it gets stripped immediately, snapping the field back to "boom". The next keystroke ("f") then appends directly to "boom", not "boom-", so a hyphen typed in the middle of a slug can never survive a normal sequence of keystrokes. (The only way to get a hyphenated slug into the field today is to paste the whole value at once, which sidesteps the per-keystroke sanitization.)
2. No client-side uniqueness check
handleSubmitin both dialogs validates only presence and format (isValidSlug) before submitting:There's no query against existing festivals/editions to warn about a taken slug before hitting submit — the only enforcement is the DB's unique constraint (
festivals_slug_unique,festival_editions_slug_festival_unique), discovered only after a failed save.3. Generic error message on a real unique-constraint conflict
useUpdateFestivalMutation's (and the edition equivalent's)onErrordoesn't discriminate the failure cause:A slug conflict (Postgres
23505unique_violation) surfaces the same generic "Failed to update festival" as any other failure, giving the user no indication of what to actually fix.Related
#467 already designs the right treatment for this exact class of problem — explicit slug field, format validation, a client-side uniqueness pre-check, and mapping a constraint-violation error to a friendly message — but scoped to the sets/artists edit forms only. This issue is the same treatment needed for the festival/festival-edition edit forms, which #467 doesn't cover.
The dash-typing bug (#1) is a new finding not mentioned in #467's design — worth accounting for there too, since #467 also plans to reuse
isValidSlug/sanitizeSlugfromsrc/lib/slug.tsfor its new slug field, which would inherit the same per-keystroke corruption unless the field sets its value on blur/paste rather than sanitizing every keystroke.Suggested fix
useUpdateFestivalMutation's and the edition mutation'sonError, inspect the Postgrest error code (23505) and show a specific "This slug is already taken" message instead of the generic fallback.Context
Reported during review of PR #495 (
fix-157/finish-mutate-migration), which touches the same files for an unrelated mutateAsync→mutate migration but didn't introduce these bugs.