Files
twenty/packages/twenty-server/test/integration/graphql/utils/activate-workspace.util.ts
T
Félix Malfait 6a1b28bc12 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>
2026-06-18 17:56:14 +02:00

59 lines
1.6 KiB
TypeScript

import gql from 'graphql-tag';
import { type CommonResponseBody } from 'test/integration/metadata/types/common-response-body.type';
import { warnIfErrorButNotExpectedToFail } from 'test/integration/metadata/utils/warn-if-error-but-not-expected-to-fail.util';
import { warnIfNoErrorButExpectedToFail } from 'test/integration/metadata/utils/warn-if-no-error-but-expected-to-fail.util';
import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util';
import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
type ActivateWorkspaceUtilArgs = {
accessToken: string;
expectToFail?: boolean;
};
export const activateWorkspace = async ({
accessToken,
expectToFail,
}: ActivateWorkspaceUtilArgs): CommonResponseBody<{
activateWorkspace: WorkspaceEntity;
}> => {
const mutation = gql`
mutation ActivateWorkspace($input: ActivateWorkspaceInput!) {
activateWorkspace(data: $input) {
id
displayName
activationStatus
subdomain
inviteHash
logo
}
}
`;
const response = await makeMetadataAPIRequest(
{
query: mutation,
variables: {
input: {},
},
},
accessToken,
);
if (expectToFail === true) {
warnIfNoErrorButExpectedToFail({
response,
errorMessage: 'Activate workspace should have failed but did not',
});
}
if (expectToFail === false) {
warnIfErrorButNotExpectedToFail({
response,
errorMessage: 'Activate workspace has failed but should not',
});
}
return { data: response.body.data, errors: response.body.errors };
};