a
This commit is contained in:
@@ -5,129 +5,129 @@ use validator::Validate;
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
||||||
pub struct TeamsCreateRequestDto {
|
pub struct TeamsCreateRequestDto {
|
||||||
#[validate(length(min = 3, max = 100, message = "Team name must be between 3 and 100 characters"))]
|
#[validate(length(min = 3, max = 100, message = "Team name must be between 3 and 100 characters"))]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
||||||
#[validate(length(max = 500, message = "Description cannot exceed 500 characters"))]
|
#[validate(length(max = 500, message = "Description cannot exceed 500 characters"))]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub is_open: Option<bool>,
|
pub is_open: Option<bool>,
|
||||||
|
|
||||||
#[validate(range(min = 2, max = 50, message = "Max members must be between 2 and 50"))]
|
#[validate(range(min = 2, max = 50, message = "Max members must be between 2 and 50"))]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub max_members: Option<i32>,
|
pub max_members: Option<i32>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub skills_required: Option<Vec<String>>,
|
pub skills_required: Option<Vec<String>>,
|
||||||
|
|
||||||
#[validate(length(max = 100, message = "Location cannot exceed 100 characters"))]
|
#[validate(length(max = 100, message = "Location cannot exceed 100 characters"))]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub location: Option<String>,
|
pub location: Option<String>,
|
||||||
|
|
||||||
#[validate(url(message = "Invalid avatar URL"))]
|
#[validate(url(message = "Invalid avatar URL"))]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub avatar: Option<String>,
|
pub avatar: Option<String>,
|
||||||
|
|
||||||
#[validate(url(message = "Invalid website URL"))]
|
#[validate(url(message = "Invalid website URL"))]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub website_url: Option<String>,
|
pub website_url: Option<String>,
|
||||||
|
|
||||||
#[validate(url(message = "Invalid GitHub URL"))]
|
#[validate(url(message = "Invalid GitHub URL"))]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub github_url: Option<String>,
|
pub github_url: Option<String>,
|
||||||
|
|
||||||
#[validate(length(min = 1, message = "Member emails cannot be empty"))]
|
#[validate(length(min = 1, message = "Member emails cannot be empty"))]
|
||||||
pub member_emails: Vec<String>,
|
pub member_emails: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
||||||
pub struct TeamsUpdateRequestDto {
|
pub struct TeamsUpdateRequestDto {
|
||||||
#[validate(length(min = 3, max = 100, message = "Team name must be between 3 and 100 characters"))]
|
#[validate(length(min = 3, max = 100, message = "Team name must be between 3 and 100 characters"))]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
|
|
||||||
#[validate(length(max = 500, message = "Description cannot exceed 500 characters"))]
|
#[validate(length(max = 500, message = "Description cannot exceed 500 characters"))]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub is_open: Option<bool>,
|
pub is_open: Option<bool>,
|
||||||
|
|
||||||
#[validate(range(min = 2, max = 50, message = "Max members must be between 2 and 50"))]
|
#[validate(range(min = 2, max = 50, message = "Max members must be between 2 and 50"))]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub max_members: Option<i32>,
|
pub max_members: Option<i32>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub skills_required: Option<Vec<String>>,
|
pub skills_required: Option<Vec<String>>,
|
||||||
|
|
||||||
#[validate(length(max = 100, message = "Location cannot exceed 100 characters"))]
|
#[validate(length(max = 100, message = "Location cannot exceed 100 characters"))]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub location: Option<String>,
|
pub location: Option<String>,
|
||||||
|
|
||||||
#[validate(url(message = "Invalid avatar URL"))]
|
#[validate(url(message = "Invalid avatar URL"))]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub avatar: Option<String>,
|
pub avatar: Option<String>,
|
||||||
|
|
||||||
#[validate(url(message = "Invalid website URL"))]
|
#[validate(url(message = "Invalid website URL"))]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub website_url: Option<String>,
|
pub website_url: Option<String>,
|
||||||
|
|
||||||
#[validate(url(message = "Invalid GitHub URL"))]
|
#[validate(url(message = "Invalid GitHub URL"))]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub github_url: Option<String>,
|
pub github_url: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
||||||
pub struct TeamInviteRequestDto {
|
pub struct TeamInviteRequestDto {
|
||||||
#[validate(length(min = 1, message = "Member emails cannot be empty"))]
|
#[validate(length(min = 1, message = "Member emails cannot be empty"))]
|
||||||
pub member_emails: Vec<String>,
|
pub member_emails: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||||
pub struct TeamAcceptInvitationRequestDto {
|
pub struct TeamAcceptInvitationRequestDto {
|
||||||
pub token: String,
|
pub token: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||||
pub struct TeamsDetailItemDto {
|
pub struct TeamsDetailItemDto {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub leader: TeamMemberDto,
|
pub leader: TeamMemberDto,
|
||||||
pub is_open: bool,
|
pub is_open: bool,
|
||||||
pub max_members: Option<i32>,
|
pub max_members: Option<i32>,
|
||||||
pub current_member_count: i32,
|
pub current_member_count: i32,
|
||||||
pub skills_required: Option<Vec<String>>,
|
pub skills_required: Option<Vec<String>>,
|
||||||
pub location: Option<String>,
|
pub location: Option<String>,
|
||||||
pub avatar: Option<String>,
|
pub avatar: Option<String>,
|
||||||
pub website_url: Option<String>,
|
pub website_url: Option<String>,
|
||||||
pub github_url: Option<String>,
|
pub github_url: Option<String>,
|
||||||
pub members: Option<Vec<TeamMemberDto>>,
|
pub members: Option<Vec<TeamMemberDto>>,
|
||||||
pub is_active: bool,
|
pub is_active: bool,
|
||||||
pub created_at: String,
|
pub created_at: String,
|
||||||
pub updated_at: String,
|
pub updated_at: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||||
pub struct MemberTeamsDetailItemDto {
|
pub struct MemberTeamsDetailItemDto {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub leader: TeamMemberDto,
|
pub leader: TeamMemberDto,
|
||||||
pub is_open: bool,
|
pub is_open: bool,
|
||||||
pub max_members: Option<i32>,
|
pub max_members: Option<i32>,
|
||||||
pub current_member_count: i32,
|
pub current_member_count: i32,
|
||||||
pub skills_required: Option<Vec<String>>,
|
pub skills_required: Option<Vec<String>>,
|
||||||
pub location: Option<String>,
|
pub location: Option<String>,
|
||||||
pub avatar: Option<String>,
|
pub avatar: Option<String>,
|
||||||
pub website_url: Option<String>,
|
pub website_url: Option<String>,
|
||||||
pub github_url: Option<String>,
|
pub github_url: Option<String>,
|
||||||
pub members: Vec<TeamMemberDto>, // Always include members for authenticated users
|
pub members: Vec<TeamMemberDto>, // Always include members for authenticated users
|
||||||
pub is_active: bool,
|
pub is_active: bool,
|
||||||
pub created_at: String,
|
pub created_at: String,
|
||||||
pub updated_at: String,
|
pub updated_at: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||||
@@ -179,168 +179,106 @@ pub struct PublicTeamsDetailItemDto {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||||
pub struct TeamMemberDto {
|
pub struct TeamMemberDto {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub user_id: String,
|
pub user_id: String,
|
||||||
pub fullname: String,
|
pub fullname: String,
|
||||||
pub email: Option<String>,
|
pub email: Option<String>,
|
||||||
pub avatar: Option<String>,
|
pub avatar: Option<String>,
|
||||||
pub role: String,
|
pub role: String,
|
||||||
pub skills: Option<Vec<String>>,
|
pub skills: Option<Vec<String>>,
|
||||||
pub joined_at: String,
|
pub joined_at: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||||
pub struct TeamInvitationDto {
|
pub struct TeamInvitationDto {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub team_id: String,
|
pub team_id: String,
|
||||||
pub team_name: String,
|
pub team_name: String,
|
||||||
pub email: String,
|
pub email: String,
|
||||||
pub inviter_name: String,
|
pub inviter_name: String,
|
||||||
pub status: String,
|
pub status: String,
|
||||||
pub expires_at: String,
|
pub expires_at: String,
|
||||||
pub invited_at: String,
|
pub invited_at: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct TeamsDetailQueryDto {
|
pub struct TeamsDetailQueryDto {
|
||||||
pub id: Thing,
|
pub id: Thing,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub leader_id: Thing,
|
pub leader_id: Thing,
|
||||||
pub is_open: bool,
|
pub is_open: bool,
|
||||||
pub max_members: Option<i32>,
|
pub max_members: Option<i32>,
|
||||||
pub skills_required: Option<Vec<String>>,
|
pub skills_required: Option<Vec<String>>,
|
||||||
pub location: Option<String>,
|
pub location: Option<String>,
|
||||||
pub avatar: Option<String>,
|
pub avatar: Option<String>,
|
||||||
pub website_url: Option<String>,
|
pub website_url: Option<String>,
|
||||||
pub github_url: Option<String>,
|
pub github_url: Option<String>,
|
||||||
pub is_active: bool,
|
pub is_active: bool,
|
||||||
pub is_deleted: bool,
|
pub is_deleted: bool,
|
||||||
pub created_at: String,
|
pub created_at: String,
|
||||||
pub updated_at: String,
|
pub updated_at: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct TeamsListQueryDto {
|
pub struct TeamsListQueryDto {
|
||||||
pub id: Thing,
|
pub id: Thing,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub leader_id: Thing,
|
pub leader_id: Thing,
|
||||||
pub is_open: bool,
|
pub is_open: bool,
|
||||||
pub max_members: Option<i32>,
|
pub max_members: Option<i32>,
|
||||||
pub skills_required: Option<Vec<String>>,
|
pub skills_required: Option<Vec<String>>,
|
||||||
pub location: Option<String>,
|
pub location: Option<String>,
|
||||||
pub avatar: Option<String>,
|
pub avatar: Option<String>,
|
||||||
pub created_at: String,
|
pub created_at: String,
|
||||||
pub updated_at: String,
|
pub updated_at: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct TeamMembersQueryDto {
|
pub struct TeamMembersQueryDto {
|
||||||
pub id: Thing,
|
pub id: Thing,
|
||||||
pub team_id: Thing,
|
pub team_id: Thing,
|
||||||
pub user_id: Thing,
|
pub user_id: Thing,
|
||||||
pub role: String,
|
pub role: String,
|
||||||
pub joined_at: String,
|
pub joined_at: String,
|
||||||
pub is_active: bool,
|
pub is_active: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct TeamInvitationsQueryDto {
|
pub struct TeamInvitationsQueryDto {
|
||||||
pub id: Thing,
|
pub id: Thing,
|
||||||
pub team_id: Thing,
|
pub team_id: Thing,
|
||||||
pub email: String,
|
pub email: String,
|
||||||
pub inviter_id: Thing,
|
pub inviter_id: Thing,
|
||||||
pub invite_code: String,
|
pub invite_code: String,
|
||||||
pub expires_at: String,
|
pub expires_at: String,
|
||||||
pub status: String,
|
pub status: String,
|
||||||
pub invited_at: String,
|
pub invited_at: String,
|
||||||
pub accepted_at: Option<String>,
|
pub accepted_at: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||||
pub struct TeamsSearchQueryDto {
|
pub struct TeamsSearchQueryDto {
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub query: Option<String>,
|
pub query: Option<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub open: Option<bool>,
|
pub open: Option<bool>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub skills: Option<Vec<String>>,
|
pub skills: Option<Vec<String>>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub location: Option<String>,
|
pub location: Option<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub page: Option<i64>,
|
pub page: Option<i64>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub per_page: Option<i64>,
|
pub per_page: Option<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TeamsDetailQueryDto {
|
|
||||||
pub fn from(self) -> Self {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TeamsListQueryDto {
|
|
||||||
pub fn from(self) -> TeamsListItemDto {
|
|
||||||
TeamsListItemDto {
|
|
||||||
id: self.id.id.to_raw(),
|
|
||||||
name: self.name,
|
|
||||||
description: self.description,
|
|
||||||
leader: TeamMemberDto {
|
|
||||||
id: String::new(),
|
|
||||||
user_id: self.leader_id.id.to_raw(),
|
|
||||||
fullname: String::new(),
|
|
||||||
email: None,
|
|
||||||
avatar: None,
|
|
||||||
role: "leader".to_string(),
|
|
||||||
skills: None,
|
|
||||||
joined_at: self.created_at.clone(),
|
|
||||||
},
|
|
||||||
is_open: self.is_open,
|
|
||||||
current_member_count: 1,
|
|
||||||
max_members: self.max_members,
|
|
||||||
skills_required: self.skills_required,
|
|
||||||
location: self.location,
|
|
||||||
avatar: self.avatar,
|
|
||||||
created_at: self.created_at,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_admin_list_dto(self) -> AdminTeamsListItemDto {
|
|
||||||
let members = vec![];
|
|
||||||
AdminTeamsListItemDto {
|
|
||||||
id: self.id.id.to_raw(),
|
|
||||||
name: self.name,
|
|
||||||
description: self.description,
|
|
||||||
leader: TeamMemberDto {
|
|
||||||
id: String::new(),
|
|
||||||
user_id: self.leader_id.id.to_raw(),
|
|
||||||
fullname: String::new(),
|
|
||||||
email: None,
|
|
||||||
avatar: None,
|
|
||||||
role: "leader".to_string(),
|
|
||||||
skills: None,
|
|
||||||
joined_at: self.created_at.clone(),
|
|
||||||
},
|
|
||||||
is_open: self.is_open,
|
|
||||||
current_member_count: members.len() as i32 + 1,
|
|
||||||
max_members: self.max_members,
|
|
||||||
skills_required: self.skills_required,
|
|
||||||
location: self.location,
|
|
||||||
avatar: self.avatar,
|
|
||||||
website_url: None,
|
|
||||||
github_url: None,
|
|
||||||
is_active: true,
|
|
||||||
is_deleted: false,
|
|
||||||
created_at: self.created_at,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||||
pub struct AdminTeamsListItemDto {
|
pub struct AdminTeamsListItemDto {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
@@ -381,9 +319,39 @@ pub struct AdminTeamsDetailItemDto {
|
|||||||
pub updated_at: String,
|
pub updated_at: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TeamsDetailQueryDto {
|
||||||
|
pub fn into_detail_dto(self) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TeamsListQueryDto {
|
impl TeamsListQueryDto {
|
||||||
pub fn to_admin_list_dto(self) -> AdminTeamsListItemDto {
|
pub fn into_list_item_dto(self) -> TeamsListItemDto {
|
||||||
let members = vec![];
|
TeamsListItemDto {
|
||||||
|
id: self.id.id.to_raw(),
|
||||||
|
name: self.name,
|
||||||
|
description: self.description,
|
||||||
|
leader: TeamMemberDto {
|
||||||
|
id: String::new(),
|
||||||
|
user_id: self.leader_id.id.to_raw(),
|
||||||
|
fullname: String::new(),
|
||||||
|
email: None,
|
||||||
|
avatar: None,
|
||||||
|
role: "leader".to_string(),
|
||||||
|
skills: None,
|
||||||
|
joined_at: self.created_at.clone(),
|
||||||
|
},
|
||||||
|
is_open: self.is_open,
|
||||||
|
current_member_count: 1,
|
||||||
|
max_members: self.max_members,
|
||||||
|
skills_required: self.skills_required,
|
||||||
|
location: self.location,
|
||||||
|
avatar: self.avatar,
|
||||||
|
created_at: self.created_at,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_admin_list_dto(self) -> AdminTeamsListItemDto {
|
||||||
AdminTeamsListItemDto {
|
AdminTeamsListItemDto {
|
||||||
id: self.id.id.to_raw(),
|
id: self.id.id.to_raw(),
|
||||||
name: self.name,
|
name: self.name,
|
||||||
@@ -399,7 +367,7 @@ impl TeamsListQueryDto {
|
|||||||
joined_at: self.created_at.clone(),
|
joined_at: self.created_at.clone(),
|
||||||
},
|
},
|
||||||
is_open: self.is_open,
|
is_open: self.is_open,
|
||||||
current_member_count: members.len() as i32 + 1,
|
current_member_count: 1, // Placeholder; adjust based on actual member count
|
||||||
max_members: self.max_members,
|
max_members: self.max_members,
|
||||||
skills_required: self.skills_required,
|
skills_required: self.skills_required,
|
||||||
location: self.location,
|
location: self.location,
|
||||||
@@ -414,7 +382,7 @@ impl TeamsListQueryDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TeamsDetailQueryDto {
|
impl TeamsDetailQueryDto {
|
||||||
pub fn to_admin_detail_dto(self, members: Vec<TeamMemberDto>) -> AdminTeamsDetailItemDto {
|
pub fn into_admin_detail_dto(self, members: Vec<TeamMemberDto>) -> AdminTeamsDetailItemDto {
|
||||||
AdminTeamsDetailItemDto {
|
AdminTeamsDetailItemDto {
|
||||||
id: self.id.id.to_raw(),
|
id: self.id.id.to_raw(),
|
||||||
name: self.name,
|
name: self.name,
|
||||||
@@ -445,4 +413,3 @@ impl TeamsDetailQueryDto {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
@@ -110,167 +110,167 @@ impl TeamsService {
|
|||||||
|
|
||||||
impl TeamsServiceTrait for TeamsService {
|
impl TeamsServiceTrait for TeamsService {
|
||||||
fn get_team_list(state: &AppState, meta: MetaRequestDto) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
fn get_team_list(state: &AppState, meta: MetaRequestDto) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||||
let state = state.to_owned();
|
let state = state.to_owned();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let repo = TeamsRepository::new(&state);
|
let repo = TeamsRepository::new(&state);
|
||||||
match repo.query_team_list(meta).await {
|
match repo.query_team_list(meta).await {
|
||||||
Ok(data) => {
|
Ok(data) => {
|
||||||
let response = ResponseListSuccessDto {
|
let response = ResponseListSuccessDto {
|
||||||
data: data.data,
|
data: data.data,
|
||||||
meta: data.meta,
|
meta: data.meta,
|
||||||
};
|
};
|
||||||
success_list_response(response)
|
success_list_response(response)
|
||||||
}
|
|
||||||
Err(e) => common_response(StatusCode::BAD_REQUEST, &e.to_string()),
|
|
||||||
}
|
}
|
||||||
})
|
Err(e) => common_response(StatusCode::BAD_REQUEST, &e.to_string()),
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn get_team_by_id(state: &AppState, id: String) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
fn get_team_by_id(state: &AppState, id: String) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||||
let state = state.to_owned();
|
let state = state.to_owned();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
if Uuid::parse_str(&id).is_err() {
|
if Uuid::parse_str(&id).is_err() {
|
||||||
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
||||||
|
}
|
||||||
|
let repo = TeamsRepository::new(&state);
|
||||||
|
let thing_id = make_thing_from_enum(ResourceEnum::Teams, &id);
|
||||||
|
match repo.query_team_by_id(&thing_id).await {
|
||||||
|
Ok(team) if !team.is_deleted => {
|
||||||
|
let members = repo.query_team_members(&team.id).await.unwrap_or_default();
|
||||||
|
|
||||||
|
// For public team details, only show sensitive info if user is authenticated and part of the team
|
||||||
|
let team_dto = TeamsDetailItemDto {
|
||||||
|
id: team.id.id.to_raw(),
|
||||||
|
name: team.name,
|
||||||
|
description: team.description,
|
||||||
|
leader: TeamMemberDto {
|
||||||
|
id: String::new(),
|
||||||
|
user_id: team.leader_id.id.to_raw(),
|
||||||
|
fullname: String::new(),
|
||||||
|
email: None,
|
||||||
|
avatar: None,
|
||||||
|
role: "leader".to_string(),
|
||||||
|
skills: None,
|
||||||
|
joined_at: team.created_at.clone(),
|
||||||
|
},
|
||||||
|
is_open: team.is_open,
|
||||||
|
max_members: team.max_members,
|
||||||
|
current_member_count: members.len() as i32 + 1,
|
||||||
|
skills_required: team.skills_required,
|
||||||
|
location: team.location,
|
||||||
|
avatar: team.avatar,
|
||||||
|
website_url: team.website_url,
|
||||||
|
github_url: team.github_url,
|
||||||
|
members: None,
|
||||||
|
is_active: team.is_active,
|
||||||
|
created_at: team.created_at,
|
||||||
|
updated_at: team.updated_at,
|
||||||
|
};
|
||||||
|
success_response(ResponseSuccessDto { data: team_dto })
|
||||||
}
|
}
|
||||||
let repo = TeamsRepository::new(&state);
|
Ok(_) => common_response(StatusCode::NOT_FOUND, "Team not found"),
|
||||||
let thing_id = make_thing_from_enum(ResourceEnum::Teams, &id);
|
Err(e) => common_response(StatusCode::NOT_FOUND, &e.to_string()),
|
||||||
match repo.query_team_by_id(&thing_id).await {
|
}
|
||||||
Ok(team) if !team.is_deleted => {
|
})
|
||||||
let members = repo.query_team_members(&team.id).await.unwrap_or_default();
|
}
|
||||||
|
|
||||||
// For public team details, only show sensitive info if user is authenticated and part of the team
|
fn get_member_team_list(state: &AppState, meta: MetaRequestDto) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||||
let team_dto = TeamsDetailItemDto {
|
let state = state.to_owned();
|
||||||
id: team.id.id.to_raw(),
|
Box::pin(async move {
|
||||||
name: team.name,
|
let repo = TeamsRepository::new(&state);
|
||||||
description: team.description,
|
match repo.query_team_list(meta).await {
|
||||||
leader: TeamMemberDto {
|
Ok(data) => {
|
||||||
id: String::new(),
|
let response = ResponseListSuccessDto {
|
||||||
user_id: team.leader_id.id.to_raw(),
|
data: data.data,
|
||||||
fullname: String::new(),
|
meta: data.meta,
|
||||||
email: None,
|
};
|
||||||
avatar: None,
|
success_list_response(response)
|
||||||
role: "leader".to_string(),
|
|
||||||
skills: None,
|
|
||||||
joined_at: team.created_at.clone(),
|
|
||||||
},
|
|
||||||
is_open: team.is_open,
|
|
||||||
max_members: team.max_members,
|
|
||||||
current_member_count: members.len() as i32 + 1,
|
|
||||||
skills_required: team.skills_required,
|
|
||||||
location: team.location,
|
|
||||||
avatar: team.avatar,
|
|
||||||
website_url: team.website_url,
|
|
||||||
github_url: team.github_url,
|
|
||||||
members: None,
|
|
||||||
is_active: team.is_active,
|
|
||||||
created_at: team.created_at,
|
|
||||||
updated_at: team.updated_at,
|
|
||||||
};
|
|
||||||
success_response(ResponseSuccessDto { data: team_dto })
|
|
||||||
}
|
|
||||||
Ok(_) => common_response(StatusCode::NOT_FOUND, "Team not found"),
|
|
||||||
Err(e) => common_response(StatusCode::NOT_FOUND, &e.to_string()),
|
|
||||||
}
|
}
|
||||||
})
|
Err(e) => common_response(StatusCode::BAD_REQUEST, &e.to_string()),
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn get_member_team_list(state: &AppState, meta: MetaRequestDto) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
fn get_member_team_by_id(state: &AppState, id: String) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||||
let state = state.to_owned();
|
let state = state.to_owned();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let repo = TeamsRepository::new(&state);
|
if Uuid::parse_str(&id).is_err() {
|
||||||
match repo.query_team_list(meta).await {
|
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
||||||
Ok(data) => {
|
}
|
||||||
let response = ResponseListSuccessDto {
|
let repo = TeamsRepository::new(&state);
|
||||||
data: data.data,
|
let thing_id = make_thing_from_enum(ResourceEnum::Teams, &id);
|
||||||
meta: data.meta,
|
match repo.query_team_by_id(&thing_id).await {
|
||||||
};
|
Ok(team) if !team.is_deleted => {
|
||||||
success_list_response(response)
|
let members = repo.query_team_members(&team.id).await.unwrap_or_default();
|
||||||
}
|
|
||||||
Err(e) => common_response(StatusCode::BAD_REQUEST, &e.to_string()),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_member_team_by_id(state: &AppState, id: String) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
// For member team details, include all information including members list
|
||||||
let state = state.to_owned();
|
let mut member_dtos = Vec::new();
|
||||||
Box::pin(async move {
|
for member in members {
|
||||||
if Uuid::parse_str(&id).is_err() {
|
match Self::get_user_info_with_privacy(
|
||||||
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
&member.user_id.id.to_raw(),
|
||||||
}
|
"system", // In member context, we show all user info
|
||||||
let repo = TeamsRepository::new(&state);
|
true, // In member context, we show all user info
|
||||||
let thing_id = make_thing_from_enum(ResourceEnum::Teams, &id);
|
|
||||||
match repo.query_team_by_id(&thing_id).await {
|
|
||||||
Ok(team) if !team.is_deleted => {
|
|
||||||
let members = repo.query_team_members(&team.id).await.unwrap_or_default();
|
|
||||||
|
|
||||||
// For member team details, include all information including members list
|
|
||||||
let mut member_dtos = Vec::new();
|
|
||||||
for member in members {
|
|
||||||
match Self::get_user_info_with_privacy(
|
|
||||||
&member.user_id.id.to_raw(),
|
|
||||||
"system", // In member context, we show all user info
|
|
||||||
true, // In member context, we show all user info
|
|
||||||
&state,
|
|
||||||
).await {
|
|
||||||
Ok(mut member_dto) => {
|
|
||||||
member_dto.role = member.role;
|
|
||||||
member_dto.joined_at = member.joined_at;
|
|
||||||
member_dtos.push(member_dto);
|
|
||||||
}
|
|
||||||
Err(_) => continue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add leader with full info
|
|
||||||
let leader_dto = match Self::get_user_info_with_privacy(
|
|
||||||
&team.leader_id.id.to_raw(),
|
|
||||||
"system",
|
|
||||||
true,
|
|
||||||
&state,
|
&state,
|
||||||
).await {
|
).await {
|
||||||
Ok(mut leader_dto) => {
|
Ok(mut member_dto) => {
|
||||||
leader_dto.role = "leader".to_string();
|
member_dto.role = member.role;
|
||||||
leader_dto
|
member_dto.joined_at = member.joined_at;
|
||||||
|
member_dtos.push(member_dto);
|
||||||
}
|
}
|
||||||
Err(_) => TeamMemberDto {
|
Err(_) => continue,
|
||||||
id: String::new(),
|
}
|
||||||
user_id: team.leader_id.id.to_raw(),
|
|
||||||
fullname: String::new(),
|
|
||||||
email: None,
|
|
||||||
avatar: None,
|
|
||||||
role: "leader".to_string(),
|
|
||||||
skills: None,
|
|
||||||
joined_at: team.created_at.clone(),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
member_dtos.insert(0, leader_dto);
|
|
||||||
|
|
||||||
let team_dto = MemberTeamsDetailItemDto {
|
|
||||||
id: team.id.id.to_raw(),
|
|
||||||
name: team.name,
|
|
||||||
description: team.description,
|
|
||||||
leader: leader_dto,
|
|
||||||
is_open: team.is_open,
|
|
||||||
max_members: team.max_members,
|
|
||||||
current_member_count: members.len() as i32 + 1,
|
|
||||||
skills_required: team.skills_required,
|
|
||||||
location: team.location,
|
|
||||||
avatar: team.avatar,
|
|
||||||
website_url: team.website_url,
|
|
||||||
github_url: team.github_url,
|
|
||||||
members: member_dtos,
|
|
||||||
is_active: team.is_active,
|
|
||||||
created_at: team.created_at,
|
|
||||||
updated_at: team.updated_at,
|
|
||||||
};
|
|
||||||
success_response(ResponseSuccessDto { data: team_dto })
|
|
||||||
}
|
}
|
||||||
Ok(_) => common_response(StatusCode::NOT_FOUND, "Team not found"),
|
|
||||||
Err(e) => common_response(StatusCode::NOT_FOUND, &e.to_string()),
|
// Add leader with full info
|
||||||
|
let leader_dto = match Self::get_user_info_with_privacy(
|
||||||
|
&team.leader_id.id.to_raw(),
|
||||||
|
"system",
|
||||||
|
true,
|
||||||
|
&state,
|
||||||
|
).await {
|
||||||
|
Ok(mut leader_dto) => {
|
||||||
|
leader_dto.role = "leader".to_string();
|
||||||
|
leader_dto
|
||||||
|
}
|
||||||
|
Err(_) => TeamMemberDto {
|
||||||
|
id: String::new(),
|
||||||
|
user_id: team.leader_id.id.to_raw(),
|
||||||
|
fullname: String::new(),
|
||||||
|
email: None,
|
||||||
|
avatar: None,
|
||||||
|
role: "leader".to_string(),
|
||||||
|
skills: None,
|
||||||
|
joined_at: team.created_at.clone(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
member_dtos.insert(0, leader_dto);
|
||||||
|
|
||||||
|
let team_dto = MemberTeamsDetailItemDto {
|
||||||
|
id: team.id.id.to_raw(),
|
||||||
|
name: team.name,
|
||||||
|
description: team.description,
|
||||||
|
leader: leader_dto,
|
||||||
|
is_open: team.is_open,
|
||||||
|
max_members: team.max_members,
|
||||||
|
current_member_count: members.len() as i32 + 1,
|
||||||
|
skills_required: team.skills_required,
|
||||||
|
location: team.location,
|
||||||
|
avatar: team.avatar,
|
||||||
|
website_url: team.website_url,
|
||||||
|
github_url: team.github_url,
|
||||||
|
members: member_dtos,
|
||||||
|
is_active: team.is_active,
|
||||||
|
created_at: team.created_at,
|
||||||
|
updated_at: team.updated_at,
|
||||||
|
};
|
||||||
|
success_response(ResponseSuccessDto { data: team_dto })
|
||||||
}
|
}
|
||||||
})
|
Ok(_) => common_response(StatusCode::NOT_FOUND, "Team not found"),
|
||||||
}
|
Err(e) => common_response(StatusCode::NOT_FOUND, &e.to_string()),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn get_public_team_list(state: &AppState, meta: MetaRequestDto) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
fn get_public_team_list(state: &AppState, meta: MetaRequestDto) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||||
let state = state.to_owned();
|
let state = state.to_owned();
|
||||||
@@ -372,8 +372,8 @@ impl TeamsServiceTrait for TeamsService {
|
|||||||
match repo.query_create_invitation(invitation).await {
|
match repo.query_create_invitation(invitation).await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let inviter_user = match users_repo.query_user_by_id(&make_thing_from_enum(ResourceEnum::Users, &claims.user_id)).await {
|
let inviter_user = match users_repo.query_user_by_id(&make_thing_from_enum(ResourceEnum::Users, &claims.user_id)).await {
|
||||||
Ok(user) => user,
|
Ok(user) => user,
|
||||||
Err(_) => continue,
|
Err(_) => continue,
|
||||||
};
|
};
|
||||||
if let Err(e) = Self::send_invitation_email(
|
if let Err(e) = Self::send_invitation_email(
|
||||||
&team_schema.name,
|
&team_schema.name,
|
||||||
@@ -538,8 +538,8 @@ impl TeamsServiceTrait for TeamsService {
|
|||||||
match repo.query_create_invitation(invitation).await {
|
match repo.query_create_invitation(invitation).await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let inviter_user = match users_repo.query_user_by_id(&make_thing_from_enum(ResourceEnum::Users, &claims.user_id)).await {
|
let inviter_user = match users_repo.query_user_by_id(&make_thing_from_enum(ResourceEnum::Users, &claims.user_id)).await {
|
||||||
Ok(user) => user,
|
Ok(user) => user,
|
||||||
Err(_) => return common_response(StatusCode::INTERNAL_SERVER_ERROR, "Failed to get inviter user information"),
|
Err(_) => return common_response(StatusCode::INTERNAL_SERVER_ERROR, "Failed to get inviter user information"),
|
||||||
};
|
};
|
||||||
if let Err(e) = Self::send_invitation_email(
|
if let Err(e) = Self::send_invitation_email(
|
||||||
&team.name,
|
&team.name,
|
||||||
@@ -759,6 +759,11 @@ impl TeamsServiceTrait for TeamsService {
|
|||||||
};
|
};
|
||||||
success_list_response(response)
|
success_list_response(response)
|
||||||
}
|
}
|
||||||
|
Err(e) => common_response(StatusCode::BAD_REQUEST, &e.to_string()),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn get_admin_team_list(state: &AppState, meta: MetaRequestDto) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
fn get_admin_team_list(state: &AppState, meta: MetaRequestDto) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||||
let state = state.to_owned();
|
let state = state.to_owned();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
@@ -777,129 +782,129 @@ impl TeamsServiceTrait for TeamsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_admin_team_by_id(state: &AppState, id: String) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
fn get_admin_team_by_id(state: &AppState, id: String) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||||
let state = state.to_owned();
|
let state = state.to_owned();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
if Uuid::parse_str(&id).is_err() {
|
if Uuid::parse_str(&id).is_err() {
|
||||||
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
||||||
}
|
}
|
||||||
let repo = TeamsRepository::new(&state);
|
let repo = TeamsRepository::new(&state);
|
||||||
let thing_id = make_thing_from_enum(ResourceEnum::Teams, &id);
|
let thing_id = make_thing_from_enum(ResourceEnum::Teams, &id);
|
||||||
match repo.query_team_by_id(&thing_id).await {
|
match repo.query_team_by_id(&thing_id).await {
|
||||||
Ok(team) if !team.is_deleted => {
|
Ok(team) if !team.is_deleted => {
|
||||||
let members = repo.query_team_members(&team.id).await.unwrap_or_default();
|
let members = repo.query_team_members(&team.id).await.unwrap_or_default();
|
||||||
let mut member_dtos = Vec::new();
|
let mut member_dtos = Vec::new();
|
||||||
|
|
||||||
for member in members {
|
for member in members {
|
||||||
match Self::get_user_info_with_privacy(
|
|
||||||
&member.user_id.id.to_raw(),
|
|
||||||
"system", // Admin context - show all sensitive data
|
|
||||||
true, // Admin context - always show sensitive data
|
|
||||||
&state,
|
|
||||||
).await {
|
|
||||||
Ok(mut member_dto) => {
|
|
||||||
member_dto.role = member.role;
|
|
||||||
member_dto.joined_at = member.joined_at;
|
|
||||||
member_dtos.push(member_dto);
|
|
||||||
}
|
|
||||||
Err(_) => continue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add leader with full sensitive info
|
|
||||||
match Self::get_user_info_with_privacy(
|
match Self::get_user_info_with_privacy(
|
||||||
&team.leader_id.id.to_raw(),
|
&member.user_id.id.to_raw(),
|
||||||
"system",
|
"system", // Admin context - show all sensitive data
|
||||||
true,
|
true, // Admin context - always show sensitive data
|
||||||
&state,
|
&state,
|
||||||
).await {
|
).await {
|
||||||
Ok(mut leader_dto) => {
|
Ok(mut member_dto) => {
|
||||||
leader_dto.role = "leader".to_string();
|
member_dto.role = member.role;
|
||||||
member_dtos.insert(0, leader_dto);
|
member_dto.joined_at = member.joined_at;
|
||||||
|
member_dtos.push(member_dto);
|
||||||
}
|
}
|
||||||
Err(_) => {}
|
Err(_) => continue,
|
||||||
}
|
}
|
||||||
|
|
||||||
let team_dto = team.to_admin_detail_dto(member_dtos);
|
|
||||||
success_response(ResponseSuccessDto { data: team_dto })
|
|
||||||
}
|
}
|
||||||
Ok(_) => common_response(StatusCode::NOT_FOUND, "Team not found"),
|
|
||||||
Err(e) => common_response(StatusCode::NOT_FOUND, &e.to_string()),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_admin_team_members(state: &AppState, team_id: String) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
// Add leader with full sensitive info
|
||||||
let state = state.to_owned();
|
|
||||||
Box::pin(async move {
|
|
||||||
if Uuid::parse_str(&team_id).is_err() {
|
|
||||||
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
|
||||||
}
|
|
||||||
let repo = TeamsRepository::new(&state);
|
|
||||||
let thing_id = make_thing_from_enum(ResourceEnum::Teams, &team_id);
|
|
||||||
|
|
||||||
let members = match repo.query_team_members(&thing_id).await {
|
|
||||||
Ok(members) => members,
|
|
||||||
Err(e) => return common_response(StatusCode::BAD_REQUEST, &e.to_string()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut member_dtos = Vec::new();
|
|
||||||
for member in members {
|
|
||||||
match Self::get_user_info_with_privacy(
|
match Self::get_user_info_with_privacy(
|
||||||
&member.user_id.id.to_raw(),
|
&team.leader_id.id.to_raw(),
|
||||||
"system", // Admin context - show all sensitive data
|
"system",
|
||||||
true, // Admin context - always show sensitive data
|
true,
|
||||||
&state,
|
&state,
|
||||||
).await {
|
).await {
|
||||||
Ok(mut member_dto) => {
|
Ok(mut leader_dto) => {
|
||||||
member_dto.role = member.role;
|
leader_dto.role = "leader".to_string();
|
||||||
member_dto.joined_at = member.joined_at;
|
member_dtos.insert(0, leader_dto);
|
||||||
member_dtos.push(member_dto);
|
|
||||||
}
|
}
|
||||||
Err(_) => continue,
|
Err(_) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let team_dto = team.to_admin_detail_dto(member_dtos);
|
||||||
|
success_response(ResponseSuccessDto { data: team_dto })
|
||||||
}
|
}
|
||||||
|
Ok(_) => common_response(StatusCode::NOT_FOUND, "Team not found"),
|
||||||
success_response(ResponseSuccessDto { data: member_dtos })
|
Err(e) => common_response(StatusCode::NOT_FOUND, &e.to_string()),
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn leave_current_team(state: &AppState, claims: imphnen_libs::jsonwebtoken::Claims) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
|
||||||
let state = state.to_owned();
|
|
||||||
Box::pin(async move {
|
|
||||||
let repo = TeamsRepository::new(&state);
|
|
||||||
let user_thing = make_thing_from_enum(ResourceEnum::Users, &claims.user_id);
|
|
||||||
|
|
||||||
// Find the teams that the user is a member of
|
|
||||||
let teams = match repo.query_teams_by_user(&user_thing).await {
|
|
||||||
Ok(teams) => teams,
|
|
||||||
Err(e) => {
|
|
||||||
error!("Failed to query user teams: {}", e);
|
|
||||||
return common_response(StatusCode::INTERNAL_SERVER_ERROR, "Failed to retrieve user teams")
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if teams.is_empty() {
|
|
||||||
return common_response(StatusCode::BAD_REQUEST, "User is not a member of any team");
|
|
||||||
}
|
|
||||||
|
|
||||||
// For now, assume user is in only one team (common case)
|
|
||||||
// In a future enhancement, we could ask the user to specify which team to leave
|
|
||||||
let team = &teams[0];
|
|
||||||
let team_id = team.id.id.to_raw();
|
|
||||||
let team_thing = make_thing_from_enum(ResourceEnum::Teams, &team_id);
|
|
||||||
|
|
||||||
// Check if user is the leader
|
|
||||||
if team.leader_id.id.to_raw() == claims.user_id {
|
|
||||||
return common_response(StatusCode::FORBIDDEN, "Team leader cannot leave the team");
|
|
||||||
}
|
|
||||||
|
|
||||||
match repo.query_remove_team_member(&team_thing, &user_thing).await {
|
|
||||||
Ok(msg) => common_response(StatusCode::OK, &format!("Successfully left team: {}", team.name)),
|
|
||||||
Err(e) => {
|
|
||||||
error!("Failed to remove team member: {}", e);
|
|
||||||
return common_response(StatusCode::INTERNAL_SERVER_ERROR, "Failed to leave team")
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_admin_team_members(state: &AppState, team_id: String) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||||
|
let state = state.to_owned();
|
||||||
|
Box::pin(async move {
|
||||||
|
if Uuid::parse_str(&team_id).is_err() {
|
||||||
|
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
||||||
|
}
|
||||||
|
let repo = TeamsRepository::new(&state);
|
||||||
|
let thing_id = make_thing_from_enum(ResourceEnum::Teams, &team_id);
|
||||||
|
|
||||||
|
let members = match repo.query_team_members(&thing_id).await {
|
||||||
|
Ok(members) => members,
|
||||||
|
Err(e) => return common_response(StatusCode::BAD_REQUEST, &e.to_string()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut member_dtos = Vec::new();
|
||||||
|
for member in members {
|
||||||
|
match Self::get_user_info_with_privacy(
|
||||||
|
&member.user_id.id.to_raw(),
|
||||||
|
"system", // Admin context - show all sensitive data
|
||||||
|
true, // Admin context - always show sensitive data
|
||||||
|
&state,
|
||||||
|
).await {
|
||||||
|
Ok(mut member_dto) => {
|
||||||
|
member_dto.role = member.role;
|
||||||
|
member_dto.joined_at = member.joined_at;
|
||||||
|
member_dtos.push(member_dto);
|
||||||
|
}
|
||||||
|
Err(_) => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
success_response(ResponseSuccessDto { data: member_dtos })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn leave_current_team(state: &AppState, claims: imphnen_libs::jsonwebtoken::Claims) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||||
|
let state = state.to_owned();
|
||||||
|
Box::pin(async move {
|
||||||
|
let repo = TeamsRepository::new(&state);
|
||||||
|
let user_thing = make_thing_from_enum(ResourceEnum::Users, &claims.user_id);
|
||||||
|
|
||||||
|
// Find the teams that the user is a member of
|
||||||
|
let teams = match repo.query_teams_by_user(&user_thing).await {
|
||||||
|
Ok(teams) => teams,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to query user teams: {}", e);
|
||||||
|
return common_response(StatusCode::INTERNAL_SERVER_ERROR, "Failed to retrieve user teams")
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if teams.is_empty() {
|
||||||
|
return common_response(StatusCode::BAD_REQUEST, "User is not a member of any team");
|
||||||
|
}
|
||||||
|
|
||||||
|
// For now, assume user is in only one team (common case)
|
||||||
|
// In a future enhancement, we could ask the user to specify which team to leave
|
||||||
|
let team = &teams[0];
|
||||||
|
let team_id = team.id.id.to_raw();
|
||||||
|
let team_thing = make_thing_from_enum(ResourceEnum::Teams, &team_id);
|
||||||
|
|
||||||
|
// Check if user is the leader
|
||||||
|
if team.leader_id.id.to_raw() == claims.user_id {
|
||||||
|
return common_response(StatusCode::FORBIDDEN, "Team leader cannot leave the team");
|
||||||
|
}
|
||||||
|
|
||||||
|
match repo.query_remove_team_member(&team_thing, &user_thing).await {
|
||||||
|
Ok(msg) => common_response(StatusCode::OK, &format!("Successfully left team: {}", team.name)),
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to remove team member: {}", e);
|
||||||
|
return common_response(StatusCode::INTERNAL_SERVER_ERROR, "Failed to leave team")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user