Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/__tests__/unit/lib/constants.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ describe("constants", () => {
expect(WORKSPACE_SLUG_PATTERNS.VALID.test("invalid_")).toBe(false);
});

it("should have min length of 2", () => {
expect(WORKSPACE_SLUG_PATTERNS.MIN_LENGTH).toBe(2);
it("should have min length of 3", () => {
expect(WORKSPACE_SLUG_PATTERNS.MIN_LENGTH).toBe(3);
});

it("should have max length of 50", () => {
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/unit/lib/schemas/workspace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ describe("updateWorkspaceSchema", () => {
const validSlugs = [
"my-workspace",
"test123",
"a1",
"a1b",
"workspace-with-many-hyphens",
"123numbers",
"work_space",
Expand Down
6 changes: 3 additions & 3 deletions src/__tests__/unit/services/validateWorkspaceSlug.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describe("validateWorkspaceSlug", () => {
});

test("should accept minimum length slug", () => {
const result = validateWorkspaceSlug("ab");
const result = validateWorkspaceSlug("abc");
expect(result).toEqual({ isValid: true });
});

Expand Down Expand Up @@ -193,7 +193,7 @@ describe("validateWorkspaceSlug", () => {
});

test("should reject slug with only hyphens", () => {
const result = validateWorkspaceSlug("--");
const result = validateWorkspaceSlug("---");
expect(result).toEqual({
isValid: false,
error: WORKSPACE_ERRORS.SLUG_INVALID_FORMAT
Expand Down Expand Up @@ -340,7 +340,7 @@ describe("validateWorkspaceSlug", () => {
});

test("should handle slug at exact min length boundary", () => {
const minLengthSlug = "ab"; // exactly MIN_LENGTH characters
const minLengthSlug = "abc"; // exactly MIN_LENGTH characters
const result = validateWorkspaceSlug(minLengthSlug);
expect(result).toEqual({ isValid: true });
});
Expand Down
6 changes: 3 additions & 3 deletions src/__tests__/unit/services/workspace-create.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ describe("createWorkspace - Unit Tests", () => {
"my-workspace",
"test-123",
"workspace-with-hyphens",
"a1",
"a1b",
];

for (const slug of validSlugs) {
Expand All @@ -290,7 +290,7 @@ describe("createWorkspace - Unit Tests", () => {
await expect(
createWorkspace({
name: "Test",
slug: "a", // 1 character (min is 2)
slug: "a", // 1 character (min is 3)
ownerId: mockUserId,
})
).rejects.toThrow(WORKSPACE_ERRORS.SLUG_INVALID_LENGTH);
Expand All @@ -312,7 +312,7 @@ describe("createWorkspace - Unit Tests", () => {
(db.workspace.count as Mock).mockResolvedValue(0);
(db.workspace.findUnique as Mock).mockResolvedValue(null);

const minSlug = "ab"; // 2 characters
const minSlug = "abc"; // 3 characters
const mockWorkspace = createMockWorkspace({ slug: minSlug });
(db.workspace.create as Mock).mockResolvedValue(mockWorkspace);

Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/unit/services/workspace.slug.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ describe("Workspace Slug Validation", () => {
test("should accept valid slugs", () => {
expect(validateWorkspaceSlug("my-workspace")).toEqual({ isValid: true });
expect(validateWorkspaceSlug("workspace123")).toEqual({ isValid: true });
expect(validateWorkspaceSlug("a1")).toEqual({ isValid: true });
expect(validateWorkspaceSlug("a1b")).toEqual({ isValid: true });
expect(validateWorkspaceSlug("test-workspace-123")).toEqual({ isValid: true });
});

Expand Down
20 changes: 13 additions & 7 deletions src/components/CreateWorkspaceDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ export function CreateWorkspaceDialog({
// Validation
const newErrors: Record<string, string> = {};
if (!formData.name.trim()) newErrors.name = "Name is required";
if (!formData.slug.trim()) newErrors.slug = "Slug is required";
if (!formData.slug.trim()) {
newErrors.slug = "Slug is required";
} else if (formData.slug.trim().length < 3) {
newErrors.slug = "Workspace name must be between 3 and 50 characters.";
}
// The session callback in nextauth.ts ensures user.id is present
const userId = (session?.user as { id?: string })?.id;
if (!userId) newErrors.ownerId = "User not authenticated";
Expand Down Expand Up @@ -124,12 +128,14 @@ export function CreateWorkspaceDialog({
id="slug"
placeholder="e.g., my-team"
value={formData.slug}
onChange={(e) =>
setFormData({
...formData,
slug: e.target.value,
})
}
onChange={(e) => {
const val = e.target.value;
setFormData({ ...formData, slug: val });
if (val.trim().length > 0 && val.trim().length < 3) {
// Clear length error while still typing — defer until 3+ chars
setErrors((prev) => ({ ...prev, slug: "" }));
}
}}
className={errors.slug ? "border-destructive" : ""}
disabled={loading}
/>
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/error-display.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const ERROR_CONFIGS: Record<string, ErrorDisplayConfig> = {
[WORKSPACE_ERRORS.SLUG_INVALID_LENGTH]: {
type: 'error',
title: 'Invalid Name Length',
description: 'Workspace name must be between 2 and 50 characters.',
description: 'Workspace name must be between 3 and 50 characters.',
},
[WORKSPACE_ERRORS.SLUG_RESERVED]: {
type: 'error',
Expand Down
4 changes: 2 additions & 2 deletions src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export const RESERVED_WORKSPACE_SLUGS = [
// Workspace slug validation patterns
export const WORKSPACE_SLUG_PATTERNS = {
VALID: /^[a-z0-9]([a-z0-9_-])*[a-z0-9]$|^[a-z0-9]$/,
MIN_LENGTH: 2,
MIN_LENGTH: 3,
MAX_LENGTH: 50,
} as const;

Expand Down Expand Up @@ -129,7 +129,7 @@ export const WORKSPACE_ERRORS = {
"This workspace name is reserved. Please choose a different name.",
SLUG_INVALID_FORMAT:
"Workspace name must start and end with letters or numbers, and can only contain letters, numbers, hyphens, and underscores.",
SLUG_INVALID_LENGTH: "Workspace name must be between 2 and 50 characters.",
SLUG_INVALID_LENGTH: "Workspace name must be between 3 and 50 characters.",
SLUG_ALREADY_EXISTS:
"A workspace with this name already exists. Please choose a different name.",
WORKSPACE_LIMIT_EXCEEDED:
Expand Down
Loading