Skip to content
15 changes: 13 additions & 2 deletions apps/api/plane/db/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.utils import timezone
import re
from django.core.exceptions import ValidationError

# Module imports
from plane.db.models import FileAsset
Expand All @@ -35,6 +37,13 @@ def get_mobile_default_onboarding():
}


def validate_name(value):
if not re.match(r"^[a-zA-Z0-9_\- ]+$", value):
raise ValidationError(
"name can only contain letters, numbers, hyphens (-), and underscores (_)"
)


class User(AbstractBaseUser, PermissionsMixin):
id = models.UUIDField(
default=uuid.uuid4, unique=True, editable=False, db_index=True, primary_key=True
Expand All @@ -46,8 +55,10 @@ class User(AbstractBaseUser, PermissionsMixin):

# identity
display_name = models.CharField(max_length=255, default="")
first_name = models.CharField(max_length=255, blank=True)
last_name = models.CharField(max_length=255, blank=True)
first_name = models.CharField(
max_length=255, blank=True, validators=[validate_name]
)
last_name = models.CharField(max_length=255, blank=True, validators=[validate_name])
# avatar
avatar = models.TextField(blank=True)
avatar_asset = models.ForeignKey(
Expand Down
5 changes: 5 additions & 0 deletions apps/web/core/components/onboarding/steps/profile/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,11 @@ export const ProfileSetupStep: FC<Props> = observer(({ handleStepChange }) => {
value: 24,
message: "Name must be within 24 characters.",
},
validate: (value) => {
if (!/^[a-zA-Z0-9 _-]+$/.test(value))
return "First name can only contain letters, numbers, hyphens, and underscores.";
return true;
},
Comment on lines +216 to +220
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Align rule with copy and block whitespace-only names

Message says “First name” though label is “Name”, and you allow spaces but don’t mention them; also accepts only-spaces. Trim and update message.

-              validate: (value) => {
-                if (!/^[a-zA-Z0-9 _-]+$/.test(value))
-                  return "First name can only contain letters, numbers, hyphens, and underscores.";
-                return true;
-              },
+              validate: (value) => {
+                const v = value.trim();
+                if (v.length === 0) return "Name can't be empty or only spaces.";
+                if (!/^[A-Za-z0-9 _-]+$/.test(v))
+                  return "Name can only contain letters, numbers, spaces, hyphens (-), and underscores (_).";
+                return true;
+              },

If the PR removes last name in onboarding, also delete it from:

  • TProfileSetupFormValues and defaultValues
  • handleSubmitUserDetail payload (Line 84)

🏁 Script executed:

#!/bin/bash
# Find remaining onboarding references to last_name
rg -nP -C2 '\blast_name\b' apps/web/core/components/onboarding

Length of output: 8338


Update name validation to trim whitespace and align message copy

apps/web/core/components/onboarding/steps/profile/root.tsx (lines 216–220):

- validate: (value) => {
-   if (!/^[a-zA-Z0-9 _-]+$/.test(value))
-     return "First name can only contain letters, numbers, hyphens, and underscores.";
-   return true;
- },
+ validate: (value) => {
+   const trimmed = value.trim();
+   if (trimmed.length === 0) return "Name can't be empty or only spaces.";
+   if (!/^[A-Za-z0-9 _-]+$/.test(trimmed))
+     return "Name can only contain letters, numbers, spaces, hyphens (-), and underscores (_).";
+   return 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
validate: (value) => {
if (!/^[a-zA-Z0-9 _-]+$/.test(value))
return "First name can only contain letters, numbers, hyphens, and underscores.";
return true;
},
validate: (value) => {
const trimmed = value.trim();
if (trimmed.length === 0)
return "Name can't be empty or only spaces.";
if (!/^[A-Za-z0-9 _-]+$/.test(trimmed))
return "Name can only contain letters, numbers, spaces, hyphens (-), and underscores (_).";
return true;
},
🤖 Prompt for AI Agents
In apps/web/core/components/onboarding/steps/profile/root.tsx around lines 216
to 220, update the validation to trim leading/trailing whitespace before testing
and adjust the error copy to mention spaces explicitly: compute const v = (value
|| "").trim(); then run the regex test against v and if it fails return "First
name can only contain letters, numbers, spaces, hyphens, and underscores."
(optionally also check if v === "" and return a required-name message if
needed).

}}
render={({ field: { value, onChange, ref } }) => (
<input
Expand Down
13 changes: 13 additions & 0 deletions apps/web/core/components/profile/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,11 @@ export const ProfileForm = observer((props: TProfileFormProps) => {
name="first_name"
rules={{
required: "Please enter first name",
validate: (value) => {
if (!/^[a-zA-Z0-9 _-]+$/.test(value))
return "First name can only contain letters, numbers, hyphens, and underscores.";
return true;
},
}}
render={({ field: { value, onChange, ref } }) => (
<Input
Expand All @@ -271,6 +276,13 @@ export const ProfileForm = observer((props: TProfileFormProps) => {
<Controller
control={control}
name="last_name"
rules={{
validate: (value) => {
if (!/^[a-zA-Z0-9 _-]+$/.test(value))
return "Last name can only contain letters, numbers, hyphens, and underscores.";
return true;
},
}}
render={({ field: { value, onChange, ref } }) => (
<Input
id="last_name"
Expand All @@ -287,6 +299,7 @@ export const ProfileForm = observer((props: TProfileFormProps) => {
/>
)}
/>
{errors?.last_name && <span className="text-xs text-red-500">{errors?.last_name?.message}</span>}
</div>
<div className="flex flex-col gap-1">
<h4 className="text-sm font-medium text-custom-text-200">
Expand Down
Loading