From 5eec80bbca0c9ad7a3827679b34bb7c93c4ff4fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Dvo=C5=99=C3=A1k?= <150935816+Dvorinka@users.noreply.github.com> Date: Mon, 28 Sep 2026 21:53:27 +0200 Subject: [PATCH] feat(frontend): auto-derive slugs from names on create forms (#48) The org/project/bucket/collection create forms required typing a slug by hand. While the slug field is untouched (empty or still equal to the derived value), naming the entity fills its slug; a manual slug edit stops the derivation and is sanitized to slug form as you type. Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- CHANGELOG.md | 4 ++ apps/frontend/src/App.tsx | 11 ++--- apps/frontend/src/lib/__tests__/slug.test.ts | 45 ++++++++++++++++++++ apps/frontend/src/lib/slug.ts | 27 ++++++++++++ 4 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 apps/frontend/src/lib/__tests__/slug.test.ts create mode 100644 apps/frontend/src/lib/slug.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 1734e30..df2abd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to Primora. Format follows [Keep a Changelog](https://keepac ## [Unreleased] +### Added + +- **Slugs auto-derive from names** — typing an organization, project, bucket, or collection name now fills its slug (`TDvorak` → `tdvorak`); hand-editing the slug stops the derivation, and manual edits are sanitized as you type. Edit forms stay manual. + ## [0.8.3] - 2026-09-28 ### Added diff --git a/apps/frontend/src/App.tsx b/apps/frontend/src/App.tsx index d25f05d..880ffa2 100644 --- a/apps/frontend/src/App.tsx +++ b/apps/frontend/src/App.tsx @@ -28,6 +28,7 @@ import { Show, createEffect, createMemo, createResource, createSignal, onCleanup import { authClient, fetchApiToken } from "./lib/auth-client"; import { configureApiToken, errorMessage } from "./lib/api"; import { isDemoMode, disableDemoMode, enableDemoMode, demoSession, demoService } from "./lib/demo-mode"; +import { applySlugField } from "./lib/slug"; import { AgentSetupModal, AppShell, @@ -1670,14 +1671,14 @@ export default function App() { label="Organization name" placeholder="Acme Corporation" value={organizationInput().name} - onInput={(e) => setOrganizationInput((c) => ({ ...c, name: e.currentTarget.value }))} + onInput={(e) => setOrganizationInput((c) => applySlugField(c, "name", e.currentTarget.value))} required /> setOrganizationInput((c) => ({ ...c, slug: e.currentTarget.value }))} + onInput={(e) => setOrganizationInput((c) => applySlugField(c, "slug", e.currentTarget.value))} required />