Introduce a workspace member page. (#16031)
- Refactored workspace member details into a focused Infos-only page. - Aligned the flow with SettingsProfile, including controlled name inputs, debounced saves, and stable instance IDs. - Added a dedicated member-picture upload flow. Introduced the MemberPictureUploader, connected to the uploadWorkspaceMemberProfilePicture mutation. - Backend now includes a workspace-member resolver/module for profile-picture uploads. The endpoint is permission-guarded, streams files through FileUploadService, and returns the signed file without modifying the member entity. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Adds a workspace member detail page with picture/name management, integrates a new avatar upload mutation, and updates list routing; replaces the old profile picture uploader across profile and onboarding. > > - **Frontend** > - **Settings Members**: > - Add `pages/settings/members/SettingsWorkspaceMember` with `MemberInfosTab`, `MemberNameFields`, and `MemberEmailField` for viewing/editing member info. > - Update routes in `SettingsRoutes` and add `SettingsPath.WorkspaceMemberPage`. > - Update `SettingsWorkspaceMembers` to navigate to member detail on row click and simplify row actions (remove dropdown menu). > - **Avatar Upload**: > - Introduce `WorkspaceMemberPictureUploader` using `uploadWorkspaceMemberProfilePicture` mutation. > - Replace `ProfilePictureUploader` in `SettingsProfile` and `onboarding/CreateProfile`. > - **GraphQL (client)**: > - Add `uploadWorkspaceMemberProfilePicture` mutation types/hooks in `generated(-metadata)/graphql.ts`. > - **Backend** > - Add `UserWorkspaceResolver` with `uploadWorkspaceMemberProfilePicture` mutation guarded by `WorkspaceAuthGuard` and `SettingsPermissionGuard` (WORKSPACE_MEMBERS), using `FileUploadService`. > - Register resolver and `PermissionsModule` in `UserWorkspaceModule`. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 359652f8c94d093a69469969874131e525d3dc6f. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
+4
-1
@@ -11,12 +11,14 @@ import { FileUploadModule } from 'src/engine/core-modules/file/file-upload/file-
|
||||
import { FileModule } from 'src/engine/core-modules/file/file.module';
|
||||
import { OnboardingModule } from 'src/engine/core-modules/onboarding/onboarding.module';
|
||||
import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||
import { UserWorkspaceResolver } from 'src/engine/core-modules/user-workspace/user-workspace.resolver';
|
||||
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
|
||||
import { UserEntity } from 'src/engine/core-modules/user/user.entity';
|
||||
import { WorkspaceInvitationModule } from 'src/engine/core-modules/workspace-invitation/workspace-invitation.module';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
import { RoleTargetsEntity } from 'src/engine/metadata-modules/role/role-targets.entity';
|
||||
import { UserRoleModule } from 'src/engine/metadata-modules/user-role/user-role.module';
|
||||
import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
|
||||
@@ -44,12 +46,13 @@ import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/works
|
||||
FileUploadModule,
|
||||
FileModule,
|
||||
TokenModule,
|
||||
PermissionsModule,
|
||||
OnboardingModule,
|
||||
],
|
||||
services: [UserWorkspaceService],
|
||||
}),
|
||||
],
|
||||
exports: [UserWorkspaceService],
|
||||
providers: [UserWorkspaceService],
|
||||
providers: [UserWorkspaceService, UserWorkspaceResolver],
|
||||
})
|
||||
export class UserWorkspaceModule {}
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
import { UseGuards } from '@nestjs/common';
|
||||
import { Args, Mutation, Resolver } from '@nestjs/graphql';
|
||||
|
||||
import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs';
|
||||
|
||||
import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface';
|
||||
|
||||
import type { FileUpload } from 'graphql-upload/processRequest.mjs';
|
||||
|
||||
import { SignedFileDTO } from 'src/engine/core-modules/file/file-upload/dtos/signed-file.dto';
|
||||
import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||
import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
import { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants';
|
||||
import { streamToBuffer } from 'src/utils/stream-to-buffer';
|
||||
|
||||
@Resolver()
|
||||
export class UserWorkspaceResolver {
|
||||
constructor(private readonly fileUploadService: FileUploadService) {}
|
||||
|
||||
@Mutation(() => SignedFileDTO)
|
||||
@UseGuards(
|
||||
WorkspaceAuthGuard,
|
||||
SettingsPermissionGuard(PermissionFlagType.WORKSPACE_MEMBERS),
|
||||
)
|
||||
async uploadWorkspaceMemberProfilePicture(
|
||||
@AuthWorkspace() { id: workspaceId }: WorkspaceEntity,
|
||||
@Args({ name: 'file', type: () => GraphQLUpload })
|
||||
{ createReadStream, filename, mimetype }: FileUpload,
|
||||
): Promise<SignedFileDTO> {
|
||||
const stream = createReadStream();
|
||||
const buffer = await streamToBuffer(stream);
|
||||
const fileFolder = FileFolder.ProfilePicture;
|
||||
|
||||
const { files } = await this.fileUploadService.uploadImage({
|
||||
file: buffer,
|
||||
filename,
|
||||
mimeType: mimetype,
|
||||
fileFolder,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
return files[0];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user