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

Show related projects (by lang code) in same org when creating project #979

Merged
merged 31 commits into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
55572fe
Add GraphQL query for projects in org by lang code
rmunn Jul 23, 2024
76cd9d8
Display related projects in project create page
rmunn Jul 24, 2024
0943e11
Add constants for lang tag lookup
rmunn Jul 24, 2024
47a1ded
Use lookup table to convert 3-char codes to 2-char
rmunn Jul 24, 2024
b015ce9
Update two-to-three lang tags constants
rmunn Jul 24, 2024
0f65d0c
Move "related projects" section to above description
rmunn Jul 26, 2024
b27b46d
Move related-project text to en.json
rmunn Jul 30, 2024
c696383
Add ability for users to request joining projects
rmunn Jul 31, 2024
99266be
Update API call to return user's name for frontend
rmunn Aug 1, 2024
3257db2
Switch to using project page's invite modal
rmunn Aug 1, 2024
3cbddba
Better UI for related projects: radio buttons
rmunn Aug 2, 2024
33daa23
Require interacting with related-projects list
rmunn Aug 2, 2024
e65f12b
Allow passing extra classes into RadioButtonGroup
rmunn Aug 5, 2024
fa63cc0
Don't use RadioButtonGroup in project-create form
rmunn Aug 5, 2024
80170b8
Add project description to related-projects view
rmunn Aug 5, 2024
fc120b9
Rename "ask to join project" mutation
rmunn Aug 5, 2024
9d26f08
Move "ask to join" text into en.json
rmunn Aug 5, 2024
efb88db
Notification now includes project name
rmunn Aug 5, 2024
a3169cd
Prevent "are you sure you want to leave?" popup
rmunn Aug 5, 2024
1f7e5f1
Only leave form if API request succeeded
rmunn Aug 5, 2024
d7d0487
Make URL query params open the add member modal
rmunn Aug 5, 2024
0c0fa9e
Fix several Typescript and lint issues
rmunn Aug 6, 2024
a697749
Add query for related projects by name
rmunn Aug 6, 2024
67dba7f
Filter related-by-name query in GraphQL
rmunn Aug 6, 2024
6699541
De-duplicate related projects
rmunn Aug 6, 2024
0ba3b73
Simplify nested store complexity with helper fn
rmunn Aug 6, 2024
4ed1ec9
Fix "store is undefined" error
rmunn Aug 7, 2024
5f0bbc2
Fix "filter is undefined" error
rmunn Aug 7, 2024
8866462
Address most review concerns
rmunn Aug 8, 2024
297e8a5
Just use .query on related-projects queries
rmunn Aug 8, 2024
d0a3040
Slightly simpler store set calls
rmunn Aug 8, 2024
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
48 changes: 48 additions & 0 deletions backend/LexBoxApi/GraphQL/LexQueries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,54 @@ public IQueryable<DraftProject> DraftProjects(LexBoxDbContext context)
return context.DraftProjects;
}

public record ProjectsByLangCodeAndOrgInput(Guid OrgId, string LangCode);
[UseProjection]
[UseSorting]
public IQueryable<Project> ProjectsByLangCodeAndOrg(LoggedInContext loggedInContext, LexBoxDbContext context, IPermissionService permissionService, ProjectsByLangCodeAndOrgInput input)
{
var userId = loggedInContext.User.Id;
var authorized = loggedInContext.User.IsAdmin || permissionService.IsOrgMember(input.OrgId);
// Convert 3-letter code to 2-letter code if relevant, otherwise leave as-is
var langCode = Services.LangTagConstants.ThreeToTwo.GetValueOrDefault(input.LangCode, input.LangCode);
if (!authorized) throw new UnauthorizedAccessException();
rmunn marked this conversation as resolved.
Show resolved Hide resolved
var query = context.Projects.Where(p =>
p.Organizations.Any(o => o.Id == input.OrgId) &&
p.FlexProjectMetadata != null &&
p.FlexProjectMetadata.WritingSystems != null &&
p.FlexProjectMetadata.WritingSystems.VernacularWss.Any(ws =>
ws.IsActive && (
ws.Tag == langCode ||
ws.Tag == $"qaa-x-{langCode}" ||
ws.Tag.StartsWith($"{langCode}-")
)
)
);
// Org admins can see all projects, everyone else can only see non-confidential
if (!permissionService.CanEditOrg(input.OrgId))
{
query = query.Where(p => p.IsConfidential == false);
}
return query;
}

public record ProjectsInMyOrgInput(Guid OrgId);
[UseProjection]
[UseFiltering]
[UseSorting]
public IQueryable<Project> ProjectsInMyOrg(LoggedInContext loggedInContext, LexBoxDbContext context, IPermissionService permissionService, ProjectsInMyOrgInput input)
{
var userId = loggedInContext.User.Id;
var authorized = loggedInContext.User.IsAdmin || permissionService.IsOrgMember(input.OrgId);
if (!authorized) throw new UnauthorizedAccessException();
var query = context.Projects.Where(p => p.Organizations.Any(o => o.Id == input.OrgId));
// Org admins can see all projects, everyone else can only see non-confidential
if (!permissionService.CanEditOrg(input.OrgId))
{
query = query.Where(p => p.IsConfidential == false);
}
return query;
}

[UseSingleOrDefault]
[UseProjection]
public async Task<IQueryable<Project>> ProjectById(LexBoxDbContext context, IPermissionService permissionService, Guid projectId)
Expand Down
36 changes: 36 additions & 0 deletions backend/LexBoxApi/GraphQL/ProjectMutations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,42 @@ await dbContext.ProjectUsers
return dbContext.ProjectUsers.Where(u => u.Id == projectUser.Id);
}

[Error<NotFoundException>]
[Error<DbError>]
[Error<ProjectMembersMustBeVerified>]
[Error<ProjectMembersMustBeVerifiedForRole>]
[UseMutationConvention]
[UseFirstOrDefault]
[UseProjection]
public async Task<IQueryable<Project>> AskToJoinProject(
IPermissionService permissionService,
LoggedInContext loggedInContext,
Guid projectId,
LexBoxDbContext dbContext,
[Service] IEmailService emailService)
{
await permissionService.AssertCanAskToJoinProject(projectId);

var user = await dbContext.Users.FindAsync(loggedInContext.User.Id);
if (user is null) throw new UnauthorizedAccessException();
user.AssertHasVerifiedEmailForRole(ProjectRole.Editor);

var project = await dbContext.Projects
.Include(p => p.Users)
.ThenInclude(u => u.User)
.Where(p => p.Id == projectId)
.FirstOrDefaultAsync();
NotFoundException.ThrowIfNull(project);

var managers = project.Users.Where(u => u.Role == ProjectRole.Manager);
foreach (var manager in managers)
{
if (manager.User is null) continue;
await emailService.SendJoinProjectRequestEmail(manager.User, user, project);
}
return dbContext.Projects.Where(p => p.Id == projectId);
}

[Error<NotFoundException>]
[Error<DbError>]
[Error<RequiredException>]
Expand Down
2 changes: 2 additions & 0 deletions backend/LexBoxApi/Services/Email/EmailTemplates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public enum EmailTemplate
PasswordChanged,
CreateAccountRequestProject,
CreateAccountRequestOrg,
JoinProjectRequest,
CreateProjectRequest,
ApproveProjectRequest,
UserAdded,
Expand All @@ -35,6 +36,7 @@ public record OrgInviteEmail(string Email, string ManagerName, string OrgName, s

public record PasswordChangedEmail(string Name) : EmailTemplateBase(EmailTemplate.PasswordChanged);

public record JoinProjectRequestEmail(string ManagerName, string RequestingUserName, Guid requestingUserId, string ProjectCode, string ProjectName) : EmailTemplateBase(EmailTemplate.JoinProjectRequest);
public record CreateProjectRequestUser(string Name, string Email);
public record CreateProjectRequestEmail(string Name, CreateProjectRequestUser User, CreateProjectInput Project) : EmailTemplateBase(EmailTemplate.CreateProjectRequest);
public record ApproveProjectRequestEmail(string Name, CreateProjectRequestUser User, CreateProjectInput Project) : EmailTemplateBase(EmailTemplate.ApproveProjectRequest);
Expand Down
1 change: 1 addition & 0 deletions backend/LexBoxApi/Services/Email/IEmailService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public Task SendCreateAccountWithProjectEmail(

public Task SendPasswordChangedEmail(User user);

public Task SendJoinProjectRequestEmail(User projectManagers, User requestingUser, Project project);
public Task SendCreateProjectRequestEmail(LexAuthUser user, CreateProjectInput projectInput);
public Task SendApproveProjectRequestEmail(User user, CreateProjectInput projectInput);
public Task SendUserAddedEmail(User user, string projectName, string projectCode);
Expand Down
7 changes: 7 additions & 0 deletions backend/LexBoxApi/Services/EmailService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,13 @@ public async Task SendPasswordChangedEmail(User user)
await SendEmailWithRetriesAsync(email);
}

public async Task SendJoinProjectRequestEmail(User projectManager, User requestingUser, Project project)
{
var email = StartUserEmail(projectManager) ?? throw new ArgumentNullException("emailAddress");
await RenderEmail(email, new JoinProjectRequestEmail(projectManager.Name, requestingUser.Name, requestingUser.Id, project.Code, project.Name), projectManager.LocalizationCode);
await SendEmailWithRetriesAsync(email);
}

public async Task SendCreateProjectRequestEmail(LexAuthUser user, CreateProjectInput projectInput)
{
var email = new MimeMessage();
Expand Down
Loading
Loading