Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Replace AllUsers-endpoint with Users-Search endpoint on Team-page #6505

Merged
merged 4 commits into from
Feb 12, 2025
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,18 @@ export class ProjectApiService extends DomainApiService {
});
}

getUserSearchResults(
projectId: Signal<number | string>,
searchQuery: Signal<string>,
) {
return this.generateQueryOptions<ProjectUser[]>({
path: [BASE_ENDPOINT, projectId, 'users', 'search'],
params: {
username: searchQuery,
},
});
}

getProjectAttributes({
projectId,
includeProgramRegistrationAttributes = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export class UserApiService extends DomainApiService {
});
}

udpateUserDisplayName({
updateUserDisplayName({
id,
displayName,
}: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
inject,
input,
model,
signal,
} from '@angular/core';
import {
FormControl,
Expand All @@ -28,7 +29,6 @@ import { FormFieldWrapperComponent } from '~/components/form-field-wrapper/form-
import { ProjectApiService } from '~/domains/project/project.api.service';
import { ProjectUserWithRolesLabel } from '~/domains/project/project.model';
import { RoleApiService } from '~/domains/role/role.api.service';
import { UserApiService } from '~/domains/user/user.api.service';
import { ToastService } from '~/services/toast.service';
import {
generateFieldErrors,
Expand Down Expand Up @@ -60,16 +60,23 @@ export class AddProjectTeamUserFormComponent {
readonly userToEdit = input<ProjectUserWithRolesLabel | undefined>();

private projectApiService = inject(ProjectApiService);
private userApiService = inject(UserApiService);
private roleApiService = inject(RoleApiService);
private toastService = inject(ToastService);

readonly isEditing = computed(() => !!this.userToEdit());

roles = injectQuery(this.roleApiService.getRoles());
allUsers = injectQuery(this.userApiService.getAllUsers());
userSearchResults = injectQuery(
this.projectApiService.getUserSearchResults(
this.projectId,
// NOTE: Hard-coded a generic search term, to replicate the "All Users"-endpoint; Should be replaced by user-provided search-query.
signal('@'),
),
);
projectUsers = injectQuery(
this.projectApiService.getProjectUsers(this.projectId),
);

formGroup = new FormGroup({
userValue: new FormControl<number>(-1, {
// eslint-disable-next-line @typescript-eslint/unbound-method -- https://github.com/typescript-eslint/typescript-eslint/issues/1929#issuecomment-618695608
Expand All @@ -86,6 +93,7 @@ export class AddProjectTeamUserFormComponent {
nonNullable: true,
}),
});

formFieldErrors = generateFieldErrors<AddUserToTeamFormGroup>(
this.formGroup,
{
Expand All @@ -99,30 +107,33 @@ export class AddProjectTeamUserFormComponent {
},
},
);

readonly availableUsersIsLoading = computed(
() =>
this.allUsers.isPending() ||
this.userSearchResults.isPending() ||
(!this.isEditing() && this.projectUsers.isPending()),
);

readonly availableUsers = computed(() => {
const allUsers = this.allUsers.data();
const userSearchResults = this.userSearchResults.data();
const projectUsers = this.projectUsers.data();

if (!projectUsers || !allUsers) {
if (!projectUsers || !userSearchResults) {
return [];
}

if (this.isEditing()) {
return allUsers;
return userSearchResults;
}

return allUsers.filter(
return userSearchResults.filter(
(anyUser) =>
!projectUsers.some(
(thisProjectUser) => thisProjectUser.id === anyUser.id,
),
);
});

assignUserMutation = injectMutation(() => ({
mutationFn: ({
userValue,
Expand Down Expand Up @@ -170,6 +181,7 @@ export class AddProjectTeamUserFormComponent {
void this.projectApiService.invalidateCache(this.projectId);
},
}));

constructor() {
effect(() => {
const user = this.userToEdit();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ import { ToastService } from '~/services/toast.service';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ProjectTeamPageComponent {
// this is injected by the router
readonly projectId = input.required<string>();

private projectApiService = inject(ProjectApiService);
Expand Down Expand Up @@ -158,6 +157,7 @@ export class ProjectTeamPageComponent {
},
},
]);

openForm(formMode: 'add' | 'edit') {
this.formMode.set(formMode);
this.formVisible.set(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export class AddUserFormComponent {
usernameValue: string;
}) => {
if (this.isEditing()) {
return this.userApiService.udpateUserDisplayName({
return this.userApiService.updateUserDisplayName({
id: this.userToEdit()?.id,
displayName: displayNameValue,
});
Expand Down
7 changes: 3 additions & 4 deletions services/121-service/src/user/user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,14 +287,13 @@ export class UserController {
);
}

// This endpoint searches users accross all programs, which is needed to add a user to a program
// We did not create an extra permission for this as it is always used in combination with adding new users to a program
// ProgramId is therefore not needed in the service
@AuthenticatedUser({ permissions: [PermissionEnum.AidWorkerProgramUPDATE] })
@ApiTags('users')
@ApiOperation({
summary:
'Search for users who are already part of a program or who can be added to a program, based on their username or a substring of their username.',
'Search, across all programs, for users who are already part of a program or who can be added to a program, based on their username or a substring of their username.' +
'Is **NOT** limited to the provided `programId`;' +
"The `programId` used to check for the requesting user's permissions only.",
})
@ApiQuery({ name: 'username', required: true, type: 'string' })
@ApiResponse({
Expand Down
Loading