diff --git a/src/main/ipc/profiles/orca-profile-org-members-handlers.ts b/src/main/ipc/profiles/orca-profile-org-members-handlers.ts index 66dab1748ce..29e6a8ef904 100644 --- a/src/main/ipc/profiles/orca-profile-org-members-handlers.ts +++ b/src/main/ipc/profiles/orca-profile-org-members-handlers.ts @@ -1,4 +1,4 @@ -import { ORCA_ORG_NAME_MAX_LENGTH } from '../../../shared/orca-org-management' +import { isValidOrgName, ORCA_ORG_NAME_MAX_LENGTH } from '../../../shared/orca-org-management' import type { OrcaProfileOrgCreateResult } from '../../../shared/orca-org-management' import { ipcMain } from 'electron' import type { @@ -85,7 +85,7 @@ function orgMemberRemoveArgsFromUnknown(args: unknown): OrcaProfileOrgMemberRemo function orgCreateNameFromUnknown(args: unknown): string { const record = args && typeof args === 'object' ? (args as Record) : {} const name = typeof record.name === 'string' ? record.name.trim() : '' - if (!name || name.length > ORCA_ORG_NAME_MAX_LENGTH) { + if (!name || name.length > ORCA_ORG_NAME_MAX_LENGTH || !isValidOrgName(name)) { throw new Error('invalid_orca_org_name') } return name diff --git a/src/main/orca-profiles/profile-cloud-dev-org-members.ts b/src/main/orca-profiles/profile-cloud-dev-org-members.ts index e5a72444869..877032a6f53 100644 --- a/src/main/orca-profiles/profile-cloud-dev-org-members.ts +++ b/src/main/orca-profiles/profile-cloud-dev-org-members.ts @@ -1,3 +1,4 @@ +import { isValidOrgName } from '../../shared/orca-org-management' import type { OrcaProfileOrgCreateResult } from '../../shared/orca-org-management' import { randomUUID } from 'node:crypto' import { addDevOrganization, removeDevOrganization } from './profile-cloud-dev-auth' @@ -160,7 +161,7 @@ export function removeDevOrcaCloudOrgMember( export function createDevOrcaCloudOrg(name: string): OrcaProfileOrgCreateResult { const trimmed = name.trim() - if (!trimmed) { + if (!isValidOrgName(trimmed)) { return { status: 'invalid' } } const orgId = `dev-team-${randomUUID().slice(0, 8)}` diff --git a/src/main/orca-profiles/profile-cloud-org-members-service.ts b/src/main/orca-profiles/profile-cloud-org-members-service.ts index c06408e93a1..34769527669 100644 --- a/src/main/orca-profiles/profile-cloud-org-members-service.ts +++ b/src/main/orca-profiles/profile-cloud-org-members-service.ts @@ -285,6 +285,9 @@ export async function createOrcaProfileOrg( if (result.error.statusCode === 429) { return { status: 'rate-limited' } } + if (result.error.statusCode === 409) { + return { status: 'name-taken' } + } return result.error.statusCode === 400 ? { status: 'invalid' } : { status: 'failed', error: result.error.message } diff --git a/src/renderer/src/components/billing/team/TeamCreateView.tsx b/src/renderer/src/components/billing/team/TeamCreateView.tsx index da54718870e..f90867ee730 100644 --- a/src/renderer/src/components/billing/team/TeamCreateView.tsx +++ b/src/renderer/src/components/billing/team/TeamCreateView.tsx @@ -1,4 +1,4 @@ -import { ORCA_ORG_NAME_MAX_LENGTH } from '../../../../../shared/orca-org-management' +import { isValidOrgName, ORCA_ORG_NAME_MAX_LENGTH } from '../../../../../shared/orca-org-management' import { useId, useState } from 'react' import { ArrowRight, Users } from 'lucide-react' import { Button } from '@/components/ui/button' @@ -27,6 +27,8 @@ export function TeamCreateView(props: TeamCreateViewProps): React.JSX.Element { const id = useId() const [name, setName] = useState('') const trimmed = name.trim() + const nameValid = isValidOrgName(trimmed) + const showRule = trimmed.length > 0 && !nameValid return (
{ event.preventDefault() - if (trimmed && !props.creating && !props.unavailable) { + if (nameValid && !props.creating && !props.unavailable) { props.onCreate(trimmed) } }} @@ -70,8 +72,8 @@ export function TeamCreateView(props: TeamCreateViewProps): React.JSX.Element { 'auto.components.billing.team.teamNamePlaceholder', 'Acme Engineering' )} - aria-invalid={props.error ? true : undefined} - aria-describedby={props.error ? `${id}-error` : undefined} + aria-invalid={props.error || showRule ? true : undefined} + aria-describedby={props.error ? `${id}-error` : showRule ? `${id}-rule` : undefined} onChange={(event) => setName(event.target.value)} readOnly={props.creating} aria-disabled={props.creating || undefined} @@ -79,7 +81,7 @@ export function TeamCreateView(props: TeamCreateViewProps): React.JSX.Element { />