Skip to content

fix: improve onboarding form slug generation UX#3967

Closed
naaa760 wants to merge 6 commits intounkeyed:mainfrom
naaa760:slug-enhance
Closed

fix: improve onboarding form slug generation UX#3967
naaa760 wants to merge 6 commits intounkeyed:mainfrom
naaa760:slug-enhance

Conversation

@naaa760
Copy link

@naaa760 naaa760 commented Sep 14, 2025

What does this PR do?

  • Improves the user experience in the onboarding flow by implementing real-time slug generation for workspace creation. Previously, the slug was only generated when users moved away from the workspace name field (onBlur), which provided poor feedback. Now the slug updates immediately as users type (onChange), matching the behavior of the project creation form.

Changes made:

  • Added handleNameChange function for real-time slug generation
  • Replaced onBlur event with onChange registration in the workspace name input
  • Maintains existing logic for manual slug editing and form validation
  • Added proper React import for TypeScript compatibility

fixes #3966

How should this be tested?

  • Navigate to the onboarding flow (/new)
  • Start typing in the "Workspace name" field
  • Verify that the "Workspace URL handle" field updates in real-time as you type
  • Verify that manual editing of the slug field still works correctly
  • Verify that once manually edited, the slug field stops auto-updating

Summary by CodeRabbit

  • New Features

    • Slug now auto-populates in real time as you type the workspace name.
    • Manual slug edits are preserved; once you modify the slug, it won’t be auto-updated.
    • Slug generation occurs on name change for a smoother setup flow; validation still runs as you type.
  • Bug Fixes

    • Session cookie max-age calculation clamped to prevent negative values, avoiding incorrect cookie lifetimes.

Update workspace name field to generate slug in real-time as user types,
replacing onBlur behavior with onChange for better user experience.
@changeset-bot
Copy link

changeset-bot bot commented Sep 14, 2025

⚠️ No Changeset found

Latest commit: 6c15e0a

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link

vercel bot commented Sep 14, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
engineering Ready Ready Preview Comment Sep 18, 2025 3:10am

@vercel
Copy link

vercel bot commented Sep 14, 2025

@naaa760 is attempting to deploy a commit to the Unkey Team on Vercel.

A member of the Team first needs to authorize it.

@CLAassistant
Copy link

CLAassistant commented Sep 14, 2025

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ naaa760
❌ neha


neha seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 14, 2025

Warning

Rate limit exceeded

@naaa760 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 1 minutes and 16 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 1c2e7b3 and 6c15e0a.

📒 Files selected for processing (3)
  • apps/dashboard/app/(app)/api/auth/refresh/route.ts (2 hunks)
  • apps/dashboard/app/new/hooks/use-workspace-step.tsx (5 hunks)
  • apps/dashboard/lib/auth/cookies.ts (5 hunks)
📝 Walkthrough

Walkthrough

Auto-generate workspace slug as the user types the workspace name (unless the slug was manually edited); mark manual slug edits to stop auto-generation. Also clamp session cookie maxAge to a non-negative value. No public API/signature changes.

Changes

Cohort / File(s) Summary
Onboarding workspace slug handling
apps/dashboard/app/new/hooks/use-workspace-step.tsx
- Add type-only React import for ChangeEvent typing
- Add slugManuallyEdited state
- Add handleNameChange to auto-generate slug via slugify(name) when slug wasn't manually edited
- Wire workspaceName input to onChange: handleNameChange and remove onBlur slug generation
- Mark slug input edits to set slugManuallyEdited and stop auto-generation
Session refresh route
apps/dashboard/app/(app)/api/auth/refresh/route.ts
- Clamp cookie maxAge to non-negative with Math.max(0, ...) when setting session cookie
- Reformat some Response.json calls (no payload/status change)
Cookie helpers
apps/dashboard/lib/auth/cookies.ts
- Clamp maxAge to non-negative in setSessionCookie using Math.max(0, ...)
- Minor signature/formatting line-wrapping changes (no behavioral changes)

Sequence Diagram(s)

sequenceDiagram
  participant U as User
  participant Form as Onboarding Form
  participant Hook as use-workspace-step
  participant Val as Form Validation

  U->>Form: type workspace name (onChange)
  Form->>Hook: handleNameChange(name)
  alt slug not manually edited
    Hook->>Hook: slug = slugify(name)
    Hook->>Form: setValue("slug", slug)
    Hook->>Val: trigger("slug")
  else manually edited
    Hook->>Form: no auto-update
  end

  U->>Form: type in slug input (onChange)
  Form->>Hook: set slugManuallyEdited = true
  Hook->>Val: trigger("slug")
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

Bug

Suggested reviewers

  • mcstepp
  • chronark
  • perkinsjr
  • ogzhanolguncu

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The PR includes unrelated functional changes to session cookie handling in apps/dashboard/app/(app)/api/auth/refresh/route.ts and apps/dashboard/lib/auth/cookies.ts that clamp maxAge to non-negative values. Those cookie/authentication edits address a separate behavioral issue and are not part of issue #3966, which is strictly about slug generation UX. Because they alter authentication-related behavior, they are out-of-scope for this enhancement PR and increase the review surface and risk of coupling unrelated fixes. Please split the cookie-related fixes into a separate PR (with an associated issue) or add an explicit justification, tests, and documentation in this PR explaining why they must be included here; if you keep them in this PR, add targeted tests and a clear description of the behavioral impact to aid reviewers. Separating these concerns will simplify review and reduce the chance of unexpected side effects during merge.
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title "fix: improve onboarding form slug generation UX" accurately and concisely describes the primary change in this PR: improving slug generation UX in the onboarding form. It directly reflects the move from onBlur to onChange for real-time slug updates implemented in use-workspace-step.tsx and highlights the user-experience focus. The phrasing is specific, short, and free of extraneous information, so a reviewer scanning history will understand the main intent. Therefore it meets the repository's title criteria.
Linked Issues Check ✅ Passed The implemented changes satisfy the objectives of issue #3966: the hook now generates the slug onChange via handleNameChange, the workspace name input was switched from onBlur to onChange, and manual slug edits are preserved so auto-updates stop after user edits. These modifications align with the request to match the project creation form's live-slug behavior and preserve manual editing semantics. Based on the provided summaries, the coding requirements from the linked issue appear to be met.
Description Check ✅ Passed The PR description contains a clear summary, lists the specific changes made, documents motivation, references fixes #3966, and provides concrete testing steps for the onboarding flow. The description omits the repository template's "Type of change" section and the required Checklist items, which are the only notable deviations from the template. The present content covers the main informational needs for review (what changed and how to test). On balance the description is mostly complete and usable for reviewers but does not fully match the template.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Contributor

github-actions bot commented Sep 14, 2025

Thank you for following the naming conventions for pull request titles! 🙏

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/dashboard/app/new/hooks/use-workspace-step.tsx (2)

179-191: Bug: overriding RHF’s onChange breaks slug field state/validation.

Prop-level onChange after {...register("slug")} replaces RHF’s handler; form won’t see slug edits. Also make the “manual edit” flag sticky.

-            <FormInput
-              {...form.register("slug")}
+            <FormInput
+              {...form.register("slug", {
+                onChange: () => {
+                  // Sticky: once edited manually, never auto-update again
+                  setSlugManuallyEdited(true);
+                },
+              })}
               placeholder="enter-a-handle"
               label="Workspace URL handle"
               required
               error={form.formState.errors.slug?.message}
               prefix="app.unkey.com/"
               maxLength={64}
-              onChange={(evt) => {
-                const v = evt.currentTarget.value;
-                setSlugManuallyEdited(v.length > 0);
-              }}
             />

219-227: Optional: handle diacritics for better international slugs (keep inline).

Small enhancement: strip diacritics before filtering. Keeping it inline aligns with mcstepp’s earlier preference for POCs.

-const slugify = (text: string): string => {
-  return text
-    .toLowerCase()
-    .trim()
-    .replace(/[^a-z0-9\s-]/g, "") // Remove special chars except lowercase letters, numbers, spaces, and hyphens
-    .replace(/\s+/g, "-") // Replace spaces with hyphens
-    .replace(/-+/g, "-") // Replace multiple hyphens with single
-    .replace(/^-|-$/g, ""); // Remove leading/trailing hyphens
-};
+const slugify = (text: string): string => {
+  return text
+    .normalize("NFD")                    // split base chars and diacritics
+    .replace(/\p{Diacritic}/gu, "")      // drop diacritics (requires modern runtime)
+    .toLowerCase()
+    .trim()
+    .replace(/[^a-z0-9\s-]/g, "")        // keep lowercase letters, numbers, spaces, hyphens
+    .replace(/\s+/g, "-")                // spaces -> hyphens
+    .replace(/-+/g, "-")                 // collapse hyphens
+    .replace(/^-|-$/g, "");              // trim leading/trailing hyphens
+};
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 10f390e and 988ff8c.

📒 Files selected for processing (1)
  • apps/dashboard/app/new/hooks/use-workspace-step.tsx (5 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-07-28T19:42:37.047Z
Learnt from: mcstepp
PR: unkeyed/unkey#3662
File: apps/dashboard/app/(app)/projects/page.tsx:74-81
Timestamp: 2025-07-28T19:42:37.047Z
Learning: In apps/dashboard/app/(app)/projects/page.tsx, the user mcstepp prefers to keep placeholder functions like generateSlug inline during POC/demonstration phases rather than extracting them to utility modules, with plans to refactor later when the feature matures beyond the proof-of-concept stage.

Applied to files:

  • apps/dashboard/app/new/hooks/use-workspace-step.tsx
📚 Learning: 2024-10-04T17:27:09.821Z
Learnt from: chronark
PR: unkeyed/unkey#2146
File: apps/dashboard/app/(app)/apis/[apiId]/settings/default-prefix.tsx:74-75
Timestamp: 2024-10-04T17:27:09.821Z
Learning: In `apps/dashboard/app/(app)/apis/[apiId]/settings/default-prefix.tsx`, the hidden `<input>` elements for `workspaceId` and `keyAuthId` work correctly without being registered with React Hook Form.

Applied to files:

  • apps/dashboard/app/new/hooks/use-workspace-step.tsx
🔇 Additional comments (2)
apps/dashboard/app/new/hooks/use-workspace-step.tsx (2)

26-26: Validation message clarity LGTM.


170-172: Registering onChange via RHF options is correct.

Once the handler above is fixed, this wiring preserves RHF semantics.

Please verify the slug updates on every keystroke in “Workspace name” until the slug input is manually edited.

Comment on lines +103 to +115
const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const name = e.target.value;
const currentSlug = form.getValues("slug");
const isSlugDirty = form.formState.dirtyFields.slug;

// Only auto-generate if slug is empty, not dirty, and hasn't been manually edited
if (!currentSlug && !isSlugDirty && !slugManuallyEdited) {
form.setValue("slug", slugify(name), {
shouldValidate: true,
});
}
};

Copy link
Contributor

@coderabbitai coderabbitai bot Sep 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Auto-slug stops after the first update; keep syncing until manual edit.

The !currentSlug guard prevents further updates once the slug is first set. Also mark the field dirty.

-  const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
-    const name = e.target.value;
-    const currentSlug = form.getValues("slug");
-    const isSlugDirty = form.formState.dirtyFields.slug;
-
-    // Only auto-generate if slug is empty, not dirty, and hasn't been manually edited
-    if (!currentSlug && !isSlugDirty && !slugManuallyEdited) {
-      form.setValue("slug", slugify(name), {
-        shouldValidate: true,
-      });
-    }
-  };
+  const handleNameChange = (e: ChangeEvent<HTMLInputElement>) => {
+    const name = e.target.value;
+    if (!slugManuallyEdited) {
+      form.setValue("slug", slugify(name), {
+        shouldValidate: true,
+        shouldDirty: true,
+      });
+    }
+  };

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/dashboard/app/new/hooks/use-workspace-step.tsx around lines 103 to 115,
the auto-slug logic stops updating after the first auto-fill because of the
`!currentSlug` guard and doesn't mark the field as dirty; remove the
`!currentSlug` check so the slug continues to sync on every name change until
the user manually edits it, and when calling form.setValue for "slug" include
the option to mark the field dirty (e.g., shouldDirty: true) so react-hook-form
knows it was programmatically modified.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@perkinsjr Thank you for addressing the auto-slug synchronization issue! I'm glad the logic has been updated to continue syncing the slug with the workspace name until the user manually edits it.


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

@perkinsjr
Copy link
Member

Hi @naaa760 Please run pnpm fmt from the root of the Unkey monorepo.

Also please check the Coderabbit comments for potential bugs.

@naaa760
Copy link
Author

naaa760 commented Sep 15, 2025

Hi @perkinsjr
fixed codeRabbit feedback: clamp cookie maxAge, fix auto-slug sync, optimize React imports

  • Clamp cookie maxAge calculations to prevent negative values
  • Fix auto-slug generation to continue syncing until manual edit
  • Convert React import to type-only where appropriate

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
apps/dashboard/lib/auth/cookies.ts (1)

67-88: Bug: cookie not deleted when value is falsy and no reason provided.

Per the docstring, the cookie should always be cleared if value is null/undefined. Currently it only deletes when reason is set.

   if (value) {
     await setCookie({
       name: cookieName,
       value: value,
       options: {
         httpOnly: true,
         secure: true,
         sameSite: "strict",
       },
     })
     return
   }

-  if (reason) {
-    console.error("Session refresh failed:", reason)
-    await deleteCookie(cookieName)
-  }
+  if (reason) {
+    console.error("Session refresh failed:", reason)
+  }
+  await deleteCookie(cookieName)
apps/dashboard/app/new/hooks/use-workspace-step.tsx (2)

189-193: Potential RHF onChange override: wire through register to avoid stomping.

Passing onChange alongside {...form.register("slug")} can override RHF’s handler depending on how FormInput spreads props. Safer to attach via register’s onChange so both fire.

-            <FormInput
-              {...form.register("slug")}
+            <FormInput
+              {...form.register("slug", {
+                onChange: (evt) => {
+                  const v = (evt as React.ChangeEvent<HTMLInputElement>).currentTarget.value
+                  setSlugManuallyEdited(v.length > 0)
+                },
+              })}
               placeholder="enter-a-handle"
               label="Workspace URL handle"
               required
               error={form.formState.errors.slug?.message}
               prefix="app.unkey.com/"
               maxLength={64}
-              onChange={(evt) => {
-                const v = evt.currentTarget.value;
-                setSlugManuallyEdited(v.length > 0);
-              }}
             />

Also applies to: 182-193


221-229: Mirror schema limit: enforce 64-char cap in slugify.

Prevents transient validation churn when programmatically setting long slugs.

 const slugify = (text: string): string => {
-  return text
+  return text
     .toLowerCase()
     .trim()
     .replace(/[^a-z0-9\s-]/g, "") // Remove special chars except lowercase letters, numbers, spaces, and hyphens
     .replace(/\s+/g, "-") // Replace spaces with hyphens
     .replace(/-+/g, "-") // Replace multiple hyphens with single
-    .replace(/^-|-$/g, ""); // Remove leading/trailing hyphens
+    .replace(/^-|-$/g, "") // Remove leading/trailing hyphens
+    .slice(0, 64);
 };
♻️ Duplicate comments (1)
apps/dashboard/app/new/hooks/use-workspace-step.tsx (1)

8-9: Nit: prefer type-only event import; drop default React type.

Use ChangeEvent directly and avoid the extra default type import. This repeats prior feedback.

-import type React from "react";
-import { useRef, useState } from "react";
+import { useRef, useState, type ChangeEvent } from "react";
@@
-  const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+  const handleNameChange = (e: ChangeEvent<HTMLInputElement>) => {

Also applies to: 105-116

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 988ff8c and 1c2e7b3.

📒 Files selected for processing (3)
  • apps/dashboard/app/(app)/api/auth/refresh/route.ts (2 hunks)
  • apps/dashboard/app/new/hooks/use-workspace-step.tsx (5 hunks)
  • apps/dashboard/lib/auth/cookies.ts (5 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-05-05T17:55:59.607Z
Learnt from: mcstepp
PR: unkeyed/unkey#3215
File: apps/dashboard/lib/auth/sessions.ts:47-51
Timestamp: 2025-05-05T17:55:59.607Z
Learning: Local auth cookies in apps/dashboard/lib/auth/sessions.ts intentionally omit HttpOnly and Secure flags to allow easier debugging during local development. This is by design as these cookies are only used in local development environments, not production.

Applied to files:

  • apps/dashboard/lib/auth/cookies.ts
📚 Learning: 2025-07-28T19:42:37.047Z
Learnt from: mcstepp
PR: unkeyed/unkey#3662
File: apps/dashboard/app/(app)/projects/page.tsx:74-81
Timestamp: 2025-07-28T19:42:37.047Z
Learning: In apps/dashboard/app/(app)/projects/page.tsx, the user mcstepp prefers to keep placeholder functions like generateSlug inline during POC/demonstration phases rather than extracting them to utility modules, with plans to refactor later when the feature matures beyond the proof-of-concept stage.

Applied to files:

  • apps/dashboard/app/new/hooks/use-workspace-step.tsx
📚 Learning: 2024-10-04T17:27:09.821Z
Learnt from: chronark
PR: unkeyed/unkey#2146
File: apps/dashboard/app/(app)/apis/[apiId]/settings/default-prefix.tsx:74-75
Timestamp: 2024-10-04T17:27:09.821Z
Learning: In `apps/dashboard/app/(app)/apis/[apiId]/settings/default-prefix.tsx`, the hidden `<input>` elements for `workspaceId` and `keyAuthId` work correctly without being registered with React Hook Form.

Applied to files:

  • apps/dashboard/app/new/hooks/use-workspace-step.tsx
🔇 Additional comments (9)
apps/dashboard/lib/auth/cookies.ts (4)

122-126: Good fix: clamp Max-Age to non-negative.

Prevents invalid negative Max-Age due to clock skew or stale expirations.


131-132: LGTM: default param for options.

Makes the helper easier to consume.


156-159: LGTM: clearer SameSite capitalization.


108-128: Unify session cookie policy and avoid drift.
setSessionCookie (apps/dashboard/lib/auth/cookies.ts:108–128) uses SameSite: "strict" while the refresh route writes "lax"; route all session-cookie writes (refresh and other handlers) through setSessionCookie so httpOnly, secure, sameSite, path and maxAge remain consistent and reduce CSRF risk.

apps/dashboard/app/(app)/api/auth/refresh/route.ts (2)

11-14: LGTM: formatting only.


38-41: LGTM: formatting only.

apps/dashboard/app/new/hooks/use-workspace-step.tsx (3)

27-28: LGTM: clearer validation copy.


60-64: Good fix: clamp Max-Age to non-negative.


172-175: LGTM: name field keeps slug in sync via register onChange.

Comment on lines 28 to 32
maxAge: Math.max(
0,
Math.floor((expiresAt.getTime() - Date.now()) / 1000)
), // Convert to seconds
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Good clamp; also centralize cookie creation via setSessionCookie.

Prevents negative Max-Age. To avoid attribute drift (e.g., SameSite mismatch with cookies.ts), call the shared helper.

-import { setCookie } from "@/lib/auth/cookies";
-import { UNKEY_SESSION_COOKIE } from "@/lib/auth/types";
+import { setSessionCookie } from "@/lib/auth/cookies";
@@
-    await setCookie({
-      name: UNKEY_SESSION_COOKIE,
-      value: newToken,
-      options: {
-        httpOnly: true,
-        secure: true,
-        sameSite: "lax",
-        path: "/",
-        maxAge: Math.max(
-          0,
-          Math.floor((expiresAt.getTime() - Date.now()) / 1000)
-        ), // Convert to seconds
-      },
-    });
+    await setSessionCookie({ token: newToken, expiresAt });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
maxAge: Math.max(
0,
Math.floor((expiresAt.getTime() - Date.now()) / 1000)
), // Convert to seconds
},
import { setSessionCookie } from "@/lib/auth/cookies";
await setSessionCookie({ token: newToken, expiresAt });
🤖 Prompt for AI Agents
In apps/dashboard/app/(app)/api/auth/refresh/route.ts around lines 28 to 32, the
code manually computes and sets a clamped Max-Age for the session cookie which
prevents negative values but can cause attribute drift; replace the manual
cookie creation with a call to the shared setSessionCookie helper (passing the
computed maxAge as Math.max(0, Math.floor((expiresAt.getTime() - Date.now()) /
1000))) so the cookie attributes (SameSite, Path, Secure, etc.) are centralized
and consistent across the app.

Comment on lines +105 to +116
const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const name = e.target.value;
const currentSlug = form.getValues("slug");
const isSlugDirty = form.formState.dirtyFields.slug;

// Only auto-generate if slug hasn't been manually edited
if (!slugManuallyEdited) {
form.setValue("slug", slugify(name), {
shouldValidate: true,
});
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Keep RHF state accurate and clamp slug length.

Mark the field dirty so RHF treats programmatic updates as user changes. Also trim to 64 chars to mirror the schema and input maxLength.

   if (!slugManuallyEdited) {
-    form.setValue("slug", slugify(name), {
-      shouldValidate: true,
-    });
+    form.setValue("slug", slugify(name).slice(0, 64), {
+      shouldValidate: true,
+      shouldDirty: true,
+    });
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const name = e.target.value;
const currentSlug = form.getValues("slug");
const isSlugDirty = form.formState.dirtyFields.slug;
// Only auto-generate if slug hasn't been manually edited
if (!slugManuallyEdited) {
form.setValue("slug", slugify(name), {
shouldValidate: true,
});
}
};
const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const name = e.target.value;
const currentSlug = form.getValues("slug");
const isSlugDirty = form.formState.dirtyFields.slug;
// Only auto-generate if slug hasn't been manually edited
if (!slugManuallyEdited) {
form.setValue("slug", slugify(name).slice(0, 64), {
shouldValidate: true,
shouldDirty: true,
});
}
};
🤖 Prompt for AI Agents
In apps/dashboard/app/new/hooks/use-workspace-step.tsx around lines 105 to 116,
the auto-generated slug is being set programmatically but not marked dirty and
can exceed the schema/input max length; update the handler to clamp the slug to
64 characters (e.g. take slugify(name) then slice to 64) and call
form.setValue("slug", clampedSlug, { shouldValidate: true, shouldDirty: true })
so RHF treats the programmatic update as a user change and validation/dirty
state remain correct.

Comment on lines +26 to 31
export async function getCookie(
name: string,
request?: NextRequest
): Promise<string | null> {
const cookieStore = request?.cookies || cookies();
return cookieStore.get(name)?.value ?? null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Minor: drop unnecessary async from getCookie.

This function is synchronous; removing async tightens types and avoids implicit Promise wrapping.

-export async function getCookie(
+export function getCookie(
   name: string,
   request?: NextRequest
-): Promise<string | null> {
+): string | null {
   const cookieStore = request?.cookies || cookies()
   return cookieStore.get(name)?.value ?? null
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export async function getCookie(
name: string,
request?: NextRequest
): Promise<string | null> {
const cookieStore = request?.cookies || cookies();
return cookieStore.get(name)?.value ?? null;
export function getCookie(
name: string,
request?: NextRequest
): string | null {
const cookieStore = request?.cookies || cookies();
return cookieStore.get(name)?.value ?? null;
}
🤖 Prompt for AI Agents
In apps/dashboard/lib/auth/cookies.ts around lines 26 to 31, the getCookie
function is marked async despite being fully synchronous; remove the async
keyword and update the return type from Promise<string | null> to string | null
so the function returns a plain value and avoids unnecessary Promise wrapping,
then run TypeScript checks to ensure callers still handle the non-Promise return
(adjust call sites if they awaited the function).

Copy link
Member

@perkinsjr perkinsjr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are there changes to the api/auth/refresh and /lib/auth?

This change should only affect the use-workspace-step.tsx

It looks like you open the file and then your IDE formatted them, please revert them.

Then run pnpm fmt and that should resolve the changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Enhancement] Improve Onboarding Form Slug Generation

3 participants