CSC/ECE 517 Fall 2025 - E2565. Integration of JoinTeamRequests

From Expertiza_Wiki
Revision as of 02:14, 11 November 2025 by Vpatel29 (talk | contribs) (Created page with "= E2565. Integration of JoinTeamRequests = == Overview == This document describes the design and implementation plan for integrating JoinTeamRequests functionality in Expertiza. The feature enables students to respond to teammate advertisements by creating join team requests, which topic holders can then approve or decline. This integration will complete the teammate advertisement workflow by connecting the advertisement creation (already implemented) with the join tea...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

E2565. Integration of JoinTeamRequests

Overview

This document describes the design and implementation plan for integrating JoinTeamRequests functionality in Expertiza. The feature enables students to respond to teammate advertisements by creating join team requests, which topic holders can then approve or decline. This integration will complete the teammate advertisement workflow by connecting the advertisement creation (already implemented) with the join team request flow.

Problem Statement

Currently, Expertiza supports teammate advertisements for assignments with topics. Topic holders can create advertisements on the "Your Team" page. However, the complete flow for responding to advertisements and managing join team requests is not fully integrated. The following gaps exist:

  1. The join_team_requests_controller has action privilege checks scattered across methods instead of using a centralized `action_allowed?` method
  1. There is no frontend UI for viewing advertisements on the Signup Sheet page
  1. There is no UI for creating join team requests when responding to advertisements
  1. The "Received Requests" section is missing from the StudentTeamView page
  1. There is no method to accept join team requests (only decline exists)
  1. The controller needs better integration with the frontend

Goals

  1. Refactor the join_team_requests_controller to use a single `action_allowed?` method for privilege checking
  1. Add a trumpet icon on the Signup Sheet page to indicate when advertisements exist for topics
  1. Create a frontend UI for viewing advertisements and creating join team requests
  1. Add a "Received Requests" section to StudentTeamView.tsx for managing incoming join team requests
  1. Implement an accept method for join team requests
  1. Ensure proper validation that teams cannot accept requests when full
  1. Conduct comprehensive testing of the entire flow

Design

Backend Design

Controller Refactoring

The `Api::V1::JoinTeamRequestsController` currently has privilege checks scattered across methods:

- `index` method checks for administrator role directly - `action_allowed?` only checks if user is a student

Refactored Design:

The `action_allowed?` method will be enhanced to handle different actions based on the action being performed:

def action_allowed?
  case action_name.to_sym
  when :index
    @current_user.administrator?
  when :create, :show, :update, :destroy, :decline, :accept
    @current_user.student?
  else
    false
  end
end

All methods will rely on this centralized privilege check, removing individual checks from methods like `index`.

New Methods

1. Accept Method

Add a new `accept` method to handle accepting join team requests:

# PATCH/PUT api/v1/join_team_requests/:id/accept
# Accepts a join team request and adds the participant to the team
def accept
  team = @join_team_request.team
  
  # Check if team is full
  if team.full?
    return render json: { error: 'This team is full.' }, status: :unprocessable_entity
  end
  
  # Check if participant is already on the team
  if team.participants.include?(@join_team_request.participant)
    return render json: { error: 'Participant already belongs to the team' }, status: :unprocessable_entity
  end
  
  ActiveRecord::Base.transaction do
    # Add participant to team
    result = team.add_member(@join_team_request.participant)
    
    if result[:success]
      # Update request status
      @join_team_request.update!(reply_status: ACCEPTED)
      render json: { message: 'JoinTeamRequest accepted successfully', join_team_request: @join_team_request }, status: :ok
    else
      render json: { error: result[:error] }, status: :unprocessable_entity
      raise ActiveRecord::Rollback
    end
  end
rescue => e
  render json: { error: "Failed to accept request: #{e.message}" }, status: :unprocessable_entity
end

2. Get Join Team Requests for Team

Add a method to fetch all join team requests for a specific team (for the "Received Requests" section):

# GET api/v1/join_team_requests/team/:team_id
# Gets all join team requests for a specific team
def team_requests
  team = Team.find(params[:team_id])
  
  # Verify current user is a member of the team
  unless team.participants.exists?(user_id: @current_user.id)
    return render json: { error: 'Unauthorized' }, status: :unauthorized
  end
  
  join_team_requests = JoinTeamRequest.where(team_id: params[:team_id])
    .includes(:participant => :user)
    .order(created_at: :desc)
  
  render json: join_team_requests, status: :ok
end

3. Get Advertisements for Assignment

Add a method to fetch all advertisements for an assignment (for the Signup Sheet page):

# GET api/v1/signed_up_teams/advertisements/:assignment_id
# Gets all advertisements for an assignment
def advertisements
  assignment = Assignment.find(params[:assignment_id])
  signed_up_teams = SignedUpTeam.joins(:team, :sign_up_topic)
    .where(sign_up_topics: { assignment_id: params[:assignment_id] })
    .where(advertise_for_partner: true)
    .includes(:team, :sign_up_topic)
  
  advertisements = signed_up_teams.map do |sut|
    {
      id: sut.id,
      team_id: sut.team_id,
      team_name: sut.team.name,
      topic_id: sut.sign_up_topic_id,
      topic_name: sut.sign_up_topic.topic_name,
      comments_for_advertisement: sut.comments_for_advertisement,
      created_at: sut.created_at
    }
  end
  
  render json: advertisements, status: :ok
end

Model Updates

The `JoinTeamRequest` model needs to establish proper associations:

class JoinTeamRequest < ApplicationRecord
  belongs_to :team
  belongs_to :participant
  
  ACCEPTED_STATUSES = %w[ACCEPTED DECLINED PENDING]
  validates :reply_status, inclusion: { in: ACCEPTED_STATUSES }
  
  scope :pending, -> { where(reply_status: 'PENDING') }
  scope :for_team, ->(team_id) { where(team_id: team_id) }
end

Routes Updates

Add new routes for the additional endpoints:

resources :join_team_requests do
  collection do
    get 'team/:team_id', to: 'join_team_requests#team_requests'
    post 'decline/:id', to: 'join_team_requests#decline'
    post 'accept/:id', to: 'join_team_requests#accept'
  end
end

resources :signed_up_teams do
collection do
get 'advertisements/:assignment_id', to: 'signed_up_teams#advertisements'
end
member do
post :create_advertisement
patch :update_advertisement
delete :remove_advertisement
end
end

Frontend Design

Signup Sheet Page Enhancement

Create a new component or enhance existing signup sheet to display advertisements:

Component: SignupSheet.tsx

This component will:

- Display all topics for an assignment - Show a trumpet icon (🔔) next to topics that have advertisements - Allow users to click on topics with advertisements to view details - Provide a modal/dialog to view advertisement details and create a join team request

interface Advertisement {
  id: number;
  team_id: number;
  team_name: string;
  topic_id: number;
  topic_name: string;
  comments_for_advertisement: string;
  created_at: string;
}

interface SignUpTopic {
id: number;
topic_name: string;
topic_identifier: string;
max_choosers: number;
description?: string;
has_advertisement?: boolean;
advertisement?: Advertisement;
}

The UI will show:

- A table/list of topics - Trumpet icon indicator for topics with advertisements - Click handler to open advertisement details modal

Join Team Request Modal

Create a modal component for creating join team requests:

Component: JoinTeamRequestModal.tsx

This modal will:

- Display advertisement details (team name, topic, desired qualifications) - Provide a text area for comments (optional) - Show a "Send Request" button - Handle form submission and API calls

interface JoinTeamRequestModalProps {
  show: boolean;
  onHide: () => void;
  advertisement: Advertisement;
  assignmentId: number;
  onSuccess: () => void;
}

StudentTeamView Enhancement

Add a "Received Requests" section to `StudentTeamView.tsx`:

Section: Received Join Team Requests

This section will:

- Display all pending join team requests for the current team - Show request details: requester name, email, comments, date - Provide "Approve" and "Decline" buttons for each request - Disable approve button if team is full - Show status for non-pending requests

interface JoinTeamRequest {
  id: number;
  participant_id: number;
  team_id: number;
  comments: string;
  reply_status: 'PENDING' | 'ACCEPTED' | 'DECLINED';
  created_at: string;
  participant: {
    id: number;
    user: {
      id: number;
      name: string;
      full_name: string;
      email: string;
    };
  };
}

The UI structure will be similar to the existing "Received Invitations" section, with:

- Table displaying request information - Action buttons (Approve/Decline) for pending requests - Status display for processed requests

API Hooks

Create or extend hooks for join team requests:

File: hooks/useJoinTeamRequest.ts

export const useJoinTeamRequest = () => {
  const createRequestAPI = useAPI();
  const acceptRequestAPI = useAPI();
  const declineRequestAPI = useAPI();
  const fetchTeamRequestsAPI = useAPI();
  const fetchAdvertisementsAPI = useAPI();

const createJoinTeamRequest = (teamId: number, assignmentId: number, comments?: string) =>
createRequestAPI.sendRequest({
url: `/join_team_requests`,
method: 'POST',
data: { team_id: teamId, assignment_id: assignmentId, comments }
});

const acceptJoinTeamRequest = (requestId: number) =>
acceptRequestAPI.sendRequest({
url: `/join_team_requests/accept/${requestId}`,
method: 'POST'
});

const declineJoinTeamRequest = (requestId: number) =>
declineRequestAPI.sendRequest({
url: `/join_team_requests/decline/${requestId}`,
method: 'POST'
});

const fetchTeamRequests = (teamId: number) =>
fetchTeamRequestsAPI.sendRequest({
url: `/join_team_requests/team/${teamId}`
});

const fetchAdvertisements = (assignmentId: number) =>
fetchAdvertisementsAPI.sendRequest({
url: `/signed_up_teams/advertisements/${assignmentId}`
});

return {
createJoinTeamRequest,
acceptJoinTeamRequest,
declineJoinTeamRequest,
fetchTeamRequests,
fetchAdvertisements,
createRequestAPI,
acceptRequestAPI,
declineRequestAPI,
fetchTeamRequestsAPI,
fetchAdvertisementsAPI
};
};

Implementation Plan

Phase 1: Backend Refactoring

  1. Refactor action_allowed? method
    1. Update `Api::V1::JoinTeamRequestsController#action_allowed?` to handle all actions
    1. Remove individual privilege checks from `index` method
    1. Ensure all actions go through the centralized check
  1. Add accept method
    1. Implement `accept` method with team full validation
    1. Add transaction handling for atomic operations
    1. Update request status to ACCEPTED
    1. Add participant to team using `team.add_member`
  1. Add team_requests method
    1. Implement method to fetch requests for a team
    1. Add authorization check (user must be team member)
    1. Include participant and user data in response
  1. Add advertisements method to SignedUpTeamsController
    1. Implement method to fetch all advertisements for an assignment
    1. Join with teams and sign_up_topics
    1. Return formatted advertisement data
  1. Update JoinTeamRequest model
    1. Add `belongs_to :team` association
    1. Add scopes for filtering
    1. Update validations
  1. Update routes
    1. Add routes for new endpoints
    1. Ensure RESTful conventions

Phase 2: Frontend - Signup Sheet

  1. Create SignupSheet component (if not exists)
    1. Display topics for an assignment
    1. Fetch advertisements for the assignment
    1. Map advertisements to topics
    1. Display trumpet icon for topics with advertisements
  1. Create JoinTeamRequestModal component
    1. Design modal UI
    1. Implement form with comments field
    1. Add submit handler
    1. Handle success/error states
  1. Integrate modal with SignupSheet
    1. Add click handler for topics with advertisements
    1. Open modal with advertisement details
    1. Handle modal close

Phase 3: Frontend - StudentTeamView

  1. Add Received Requests section
    1. Create section similar to "Received Invitations"
    1. Fetch join team requests for current team
    1. Display requests in table format
  1. Implement approve/decline functionality
    1. Add approve button handler
    1. Add decline button handler
    1. Check team full status before allowing approve
    1. Update UI after actions
  1. Add useJoinTeamRequest hook
    1. Create hook file
    1. Implement all API methods
    1. Export hook for use in components

Phase 4: Integration and Testing

  1. End-to-end testing
    1. Test advertisement creation → request creation → approval flow
    1. Test decline flow
    1. Test team full validation
    1. Test authorization checks
  1. Unit testing
    1. Test controller methods
    1. Test model validations
    1. Test frontend components
  1. Integration testing
    1. Test API endpoints
    1. Test frontend-backend integration
    1. Test error handling

Files Changed/Added

Backend Files

Modified:

- `app/controllers/api/v1/join_team_requests_controller.rb` - Refactor action_allowed?, add accept and team_requests methods - `app/controllers/api/v1/signed_up_teams_controller.rb` - Add advertisements method - `app/models/join_team_request.rb` - Add associations and scopes - `config/routes.rb` - Add new routes

Added:

- `spec/requests/api/v1/join_team_requests_spec.rb` - Request specs for new methods - `spec/models/join_team_request_spec.rb` - Model specs

Frontend Files

Modified:

- `src/pages/Student Teams/StudentTeamView.tsx` - Add Received Requests section - `src/hooks/useStudentTeam.ts` - Add join team request methods (or create new hook)

Added:

- `src/pages/SignUpSheet/SignUpSheet.tsx` - Signup sheet component with advertisement indicators - `src/pages/SignUpSheet/JoinTeamRequestModal.tsx` - Modal for creating join team requests - `src/hooks/useJoinTeamRequest.ts` - Hook for join team request API calls - `src/pages/SignUpSheet/SignUpSheet.module.css` - Styles for signup sheet - `src/pages/SignUpSheet/JoinTeamRequestModal.module.css` - Styles for modal

Testing Plan

Backend Testing

Unit Tests

  1. JoinTeamRequestsController Tests
    1. Test action_allowed? for different actions and user roles
    1. Test accept method with valid request
    1. Test accept method when team is full
    1. Test accept method when participant already on team
    1. Test team_requests method authorization
    1. Test team_requests method returns correct data
  1. SignedUpTeamsController Tests
    1. Test advertisements method returns correct data
    1. Test advertisements method filters correctly
  1. JoinTeamRequest Model Tests
    1. Test associations
    1. Test validations
    1. Test scopes

Integration Tests

  1. End-to-End Flow Tests
    1. Create advertisement → Create join request → Accept request
    1. Create advertisement → Create join request → Decline request
    1. Test team full validation prevents acceptance
    1. Test authorization prevents unauthorized access

Frontend Testing

Component Tests

  1. SignUpSheet Component
    1. Test advertisement indicators display correctly
    1. Test modal opens on click
    1. Test data fetching
  1. JoinTeamRequestModal Component
    1. Test form submission
    1. Test validation
    1. Test success/error handling
  1. StudentTeamView Component
    1. Test Received Requests section displays
    1. Test approve functionality
    1. Test decline functionality
    1. Test team full validation

Integration Tests

  1. API Integration
    1. Test all API calls work correctly
    1. Test error handling
    1. Test loading states

Edge Cases and Error Handling

Backend Edge Cases

  1. Team Full Validation

- Check team capacity before accepting request - Return appropriate error message - Prevent race conditions with database transactions

  1. Authorization

- Verify user is team member before showing requests - Verify user is student before creating requests - Verify user is administrator before viewing all requests

  1. Duplicate Requests

- Prevent creating duplicate requests from same participant - Handle case where participant already on team

  1. Concurrent Modifications

- Use transactions for atomic operations - Handle race conditions in team membership updates

Frontend Edge Cases

  1. Network Errors

- Display appropriate error messages - Handle timeout scenarios - Provide retry mechanisms

  1. Empty States

- Show appropriate messages when no advertisements exist - Show appropriate messages when no requests exist

  1. Loading States

- Display loading indicators during API calls - Prevent multiple simultaneous requests

  1. Form Validation

- Validate required fields - Provide user feedback for errors

Security Considerations

  1. Authorization Checks

- All endpoints must verify user permissions - Team members can only see requests for their team - Students can only create requests for themselves

  1. Input Validation

- Sanitize all user inputs - Validate team_id and assignment_id parameters - Prevent SQL injection through parameterized queries

  1. Rate Limiting

- Consider rate limiting for request creation - Prevent spam requests

Future Enhancements

  1. Email notifications when join team requests are created
  1. Email notifications when requests are approved/declined
  1. Bulk approve/decline functionality
  1. Request history and audit trail
  1. Advanced filtering and search for requests

References

- Expertiza Wiki: E2252. Refactor auth controller.rb & password retrieval controller.rb - Rails API Documentation - React Router Documentation - Expertiza Codebase: Existing invitation and team management implementations