Skip to content
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
1 change: 1 addition & 0 deletions backend/app/models/Task.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class TaskType(str, PyEnum):
VOLUNTEER_APP_REVIEW = "volunteer_app_review"
PROFILE_UPDATE = "profile_update"
MATCHING = "matching"
USER_OPT_OUT = "user_opt_out"


class TaskPriority(str, PyEnum):
Expand Down
1 change: 1 addition & 0 deletions backend/app/schemas/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class TaskType(str, Enum):
VOLUNTEER_APP_REVIEW = "volunteer_app_review"
PROFILE_UPDATE = "profile_update"
MATCHING = "matching"
USER_OPT_OUT = "user_opt_out"


class TaskPriority(str, Enum):
Expand Down
17 changes: 17 additions & 0 deletions backend/app/services/implementations/user_service.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
from datetime import datetime
from typing import List
from uuid import UUID

Expand All @@ -18,6 +19,9 @@
RankingPreference,
Role,
Task,
TaskPriority,
TaskStatus,
TaskType,
Treatment,
User,
UserData,
Expand Down Expand Up @@ -234,6 +238,19 @@ async def soft_delete_user_by_id(self, user_id: str):
raise HTTPException(status_code=404, detail="User not found")

db_user.active = False

# Create a User Opt Out task for admin visibility (start and end date = day of opt-out)
opt_out_time = datetime.utcnow()
opt_out_task = Task(
participant_id=db_user.id,
type=TaskType.USER_OPT_OUT,
priority=TaskPriority.NO_STATUS,
status=TaskStatus.PENDING,
start_date=opt_out_time,
end_date=opt_out_time,
)
self.db.add(opt_out_task)

self.db.commit()
except ValueError:
raise HTTPException(status_code=400, detail="Invalid user ID format")
Expand Down
14 changes: 12 additions & 2 deletions frontend/src/APIClients/taskAPIClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ export interface BackendTask {
participantName: string | null;
participantEmail: string | null;
participantRoleId: number | null;
type: 'intake_form_review' | 'volunteer_app_review' | 'profile_update' | 'matching';
type:
| 'intake_form_review'
| 'volunteer_app_review'
| 'profile_update'
| 'matching'
| 'user_opt_out';
priority: 'no_status' | 'low' | 'medium' | 'high';
status: 'pending' | 'in_progress' | 'completed';
assigneeId: string | null;
Expand All @@ -26,7 +31,12 @@ export interface TaskListResponse {

export interface UpdateTaskRequest {
participantId?: string;
type?: 'intake_form_review' | 'volunteer_app_review' | 'profile_update' | 'matching';
type?:
| 'intake_form_review'
| 'volunteer_app_review'
| 'profile_update'
| 'matching'
| 'user_opt_out';
priority?: 'no_status' | 'low' | 'medium' | 'high';
status?: 'pending' | 'in_progress' | 'completed';
assigneeId?: string | null;
Expand Down
8 changes: 5 additions & 3 deletions frontend/src/pages/admin/tasks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ const mapAPITaskToFrontend = (
volunteer_app_review: 'Ranking / Secondary App Review',
profile_update: 'Profile Update',
matching: 'Matching',
user_opt_out: 'User Opt Out',
};

// Map backend priority to frontend priority
Expand All @@ -98,6 +99,7 @@ const mapAPITaskToFrontend = (
volunteer_app_review: 'secondary_app',
profile_update: 'profile_updates',
matching: 'matching_requests',
user_opt_out: 'user_opt_outs',
};

// Format dates from ISO to DD/MM/YY
Expand Down Expand Up @@ -132,12 +134,12 @@ const mapAPITaskToFrontend = (
startDate: formatDate(apiTask.startDate),
endDate: apiTask.endDate ? formatDate(apiTask.endDate) : formatDate(apiTask.startDate),
priority: priorityMap[apiTask.priority] || 'Add status',
type: typeMap[apiTask.type] || 'Intake Form Review',
type: typeMap[apiTask.type] ?? 'Intake Form Review',
assignee: assignee?.name,
completed: apiTask.status === 'completed',
userType,
category: categoryMap[apiTask.type] || 'intake_screening',
description: apiTask.description || `Task for ${typeMap[apiTask.type]}`,
category: categoryMap[apiTask.type] ?? 'intake_screening',
description: apiTask.description ?? `Task for ${typeMap[apiTask.type] ?? 'Intake Form Review'}`,
};
};

Expand Down
21 changes: 19 additions & 2 deletions frontend/src/types/adminTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,24 @@ export interface Task {
id: string;
name: string;
participantId?: string;
type: 'Intake Form Review' | 'Ranking / Secondary App Review' | 'Matching' | 'Profile Update';
type:
| 'Intake Form Review'
| 'Ranking / Secondary App Review'
| 'Matching'
| 'Profile Update'
| 'User Opt Out';
startDate: string;
endDate: string;
priority: 'High' | 'Medium' | 'Low' | 'Add status';
assignee?: string;
completed: boolean;
userType: 'Participant' | 'Volunteer';
category: 'intake_screening' | 'secondary_app' | 'matching_requests' | 'profile_updates';
category:
| 'intake_screening'
| 'secondary_app'
| 'matching_requests'
| 'profile_updates'
| 'user_opt_outs';
description?: string;
}

Expand All @@ -32,6 +42,7 @@ export const categoryLabels: Record<Task['category'], string> = {
secondary_app: 'Review secondary application / ranking forms',
matching_requests: 'Participants requesting a match',
profile_updates: 'User profile updates',
user_opt_outs: 'User opt outs',
};

export const taskCategories: TaskCategory[] = [
Expand Down Expand Up @@ -59,4 +70,10 @@ export const taskCategories: TaskCategory[] = [
categoryKey: 'profile_updates',
bgColor: '#EEEEEC',
},
{
id: '5',
name: 'User opt outs',
categoryKey: 'user_opt_outs',
bgColor: 'rgba(200, 200, 200, 0.4)',
},
];
2 changes: 2 additions & 0 deletions frontend/src/utils/taskHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const getTypeColor = (type: string): { bg: string; color: string } => {
'Ranking / Secondary App Review': { bg: COLORS.bgTealLight, color: COLORS.teal },
Matching: { bg: COLORS.bgPinkLight, color: COLORS.red },
'Profile Update': { bg: COLORS.bgGrayLight, color: COLORS.gray700 },
'User Opt Out': { bg: 'rgba(200, 200, 200, 0.4)', color: COLORS.gray700 },
};
return typeColors[type] || { bg: COLORS.bgGrayLight, color: COLORS.gray700 };
};
Expand All @@ -28,6 +29,7 @@ export const getCategoryColor = (categoryKey: string): string => {
secondary_app: COLORS.bgTealLight,
matching_requests: COLORS.bgPinkLight,
profile_updates: COLORS.bgGrayLight,
user_opt_outs: 'rgba(200, 200, 200, 0.4)',
};
return categoryColors[categoryKey] || COLORS.bgGrayLight;
};
2 changes: 2 additions & 0 deletions frontend/src/utils/taskLinkHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export const getParticipantLink = (task: Task): string => {
return `${baseUrl}?tab=profile`;
} else if (task.type === 'Intake Form Review' || task.type === 'Ranking / Secondary App Review') {
return `${baseUrl}?tab=forms`;
} else if (task.type === 'User Opt Out') {
return baseUrl;
} else {
// Default: profile tab
return baseUrl;
Expand Down
Loading