(int boardId, string username, string role, int inviterId)
| 398 | } |
| 399 | |
| 400 | public async Task<BoardMemberDto?> InviteMemberAsync(int boardId, string username, string role, int inviterId) |
| 401 | { |
| 402 | // Only the owner or an admin can invite |
| 403 | var board = await _db.Boards.Include(b => b.Members).FirstOrDefaultAsync(b => b.Id == boardId); |
| 404 | if (board == null) return null; |
| 405 | |
| 406 | // Only Workspace Owners or Admins can invite to boards |
| 407 | var workspace = await _db.Workspaces |
| 408 | .Include(w => w.Members) |
| 409 | .FirstOrDefaultAsync(w => w.Id == board.WorkspaceId); |
| 410 | |
| 411 | if (workspace == null) return null; |
| 412 | |
| 413 | var isWsAdmin = workspace.OwnerId == inviterId || |
| 414 | workspace.Members.Any(m => m.UserId == inviterId && m.Role == "Admin" && m.Status == "Accepted"); |
| 415 | |
| 416 | var isBoardAdmin = board.OwnerId == inviterId || |
| 417 | board.Members.Any(m => m.UserId == inviterId && m.Role == "Admin" && m.Status == "Accepted"); |
| 418 | |
| 419 | if (!isWsAdmin && !isBoardAdmin) return null; |
| 420 | |
| 421 | var user = await _db.Users.FirstOrDefaultAsync(u => u.Username == username); |
| 422 | if (user == null) return null; |
| 423 | |
| 424 | // Cannot invite yourself |
| 425 | if (user.Id == inviterId) return null; |
| 426 | |
| 427 | // Check if already a member or invited |
| 428 | var existing = await _db.BoardMembers |
| 429 | .FirstOrDefaultAsync(bm => bm.BoardId == boardId && bm.UserId == user.Id); |
| 430 | |
| 431 | if (existing != null) |
| 432 | { |
| 433 | // Update role and set back to Pending so they can accept/be notified |
| 434 | existing.Role = role; |
| 435 | existing.Status = "Pending"; |
| 436 | await _db.SaveChangesAsync(); |
| 437 | |
| 438 | return new BoardMemberDto |
| 439 | { |
| 440 | Id = existing.Id, |
| 441 | UserId = user.Id, |
| 442 | Username = user.Username, |
| 443 | Email = user.Email, |
| 444 | Role = existing.Role, |
| 445 | JoinedAt = existing.JoinedAt, |
| 446 | AvatarUrl = user.AvatarUrl, |
| 447 | BoardId = boardId |
| 448 | }; |
| 449 | } |
| 450 | |
| 451 | var member = new BoardMember |
| 452 | { |
| 453 | UserId = user.Id, |
| 454 | BoardId = boardId, |
| 455 | Role = role, |
| 456 | Status = "Pending" |
| 457 | }; |
no outgoing calls