feat(auth): collect the workspace logo on the sign-up creation step (#21723)
## What & why A single, consistent **workspace-creation step** for both multi-workspace and single-workspace self-host — collecting **name + logo** (and the **subdomain** in multi-workspace) — which **removes the duplicate name/logo prompt** that previously reappeared on the workspace subdomain (reported after #21641). ## Changes **One creation form for both modes** - With 0 workspaces, both multi-workspace and single-workspace route to the shared `SignInUpWorkspaceCreationForm`; `SignInUp` renders it for the `WorkspaceCreation` step regardless of domain/scope. - The subdomain field shows only in multi-workspace; single-workspace keeps its fixed address. **Logo on the creation step** - New scoped `uploadNewWorkspaceLogo(workspaceId, file)` mutation: the creator sets a logo on their just-created `PENDING_CREATION` workspace via the workspace-agnostic token (membership enforced — only the creator is a member at that point), reusing `uploadWorkspacePicture`. Upload size is capped via `settings.storage.maxFileSize` (also applied to the existing logo / profile-picture uploads). - The picked file is held locally (object-URL preview, revoked on unmount) and uploaded right after creation (non-fatal on failure). **Onboarding step → pure activation loader** - The old "Create your workspace" form (name + logo) is removed. The onboarding step now activates the pending workspace on mount and shows the loader, with a **Retry** action on failure. ## Testing - typecheck (front + server) ✅; oxlint + oxfmt clean on changed files ✅ - Unit tests: `auth.resolver.spec`, `useWorkspaceSubdomainField`, `SignInUpWorkspaceCreationForm` (multi + single-workspace), `useAuth` ✅ - Metadata GraphQL + `twenty-client-sdk` schema regenerated. Follow-up to #21641. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Xw37hR5seiCyWnppG9z4op --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.ent
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { RoleService } from 'src/engine/metadata-modules/role/role.service';
|
||||
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
||||
import { MEMBER_ROLE_LABEL } from 'src/engine/metadata-modules/permissions/constants/member-role-label.constants';
|
||||
import { InjectWorkspaceScopedRepository } from 'src/engine/twenty-orm/workspace-scoped-repository/inject-workspace-scoped-repository.decorator';
|
||||
import { WorkspaceScopedRepository } from 'src/engine/twenty-orm/workspace-scoped-repository/workspace-scoped-repository';
|
||||
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
|
||||
@@ -117,11 +118,17 @@ export class WorkspaceManagerService {
|
||||
});
|
||||
}
|
||||
|
||||
const memberRole = await this.roleService.createMemberRole({
|
||||
workspaceId,
|
||||
ownerFlatApplication: workspaceCustomFlatApplication,
|
||||
const existingMemberRole = await this.roleRepository.findOne(workspaceId, {
|
||||
where: { label: MEMBER_ROLE_LABEL },
|
||||
});
|
||||
|
||||
const memberRole =
|
||||
existingMemberRole ??
|
||||
(await this.roleService.createMemberRole({
|
||||
workspaceId,
|
||||
ownerFlatApplication: workspaceCustomFlatApplication,
|
||||
}));
|
||||
|
||||
await this.workspaceRepository.update(workspaceId, {
|
||||
defaultRoleId: memberRole.id,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user