koprogo_api/infrastructure/web/handlers/
marketplace_handlers.rs1use actix_web::{get, post, web, HttpResponse, ResponseError};
2use chrono::Datelike;
3use uuid::Uuid;
4
5use crate::application::dto::{
6 ContractEvaluationsAnnualReportDto, CreateServiceProviderDto, SearchServiceProvidersQuery,
7};
8use crate::infrastructure::web::middleware::scope_guard::verify_building_org_access;
9use crate::infrastructure::web::middleware::AuthenticatedUser;
10use crate::infrastructure::web::AppState;
11
12#[get("/marketplace/providers")]
15pub async fn search_service_providers(
16 state: web::Data<AppState>,
17 query: web::Query<SearchServiceProvidersQuery>,
18) -> Result<HttpResponse, actix_web::Error> {
19 let results = state
20 .service_provider_use_cases
21 .search(&query.into_inner())
22 .await
23 .map_err(actix_web::error::ErrorInternalServerError)?;
24 Ok(HttpResponse::Ok().json(results))
25}
26
27#[get("/marketplace/providers/{slug}")]
30pub async fn get_provider_by_slug(
31 state: web::Data<AppState>,
32 slug: web::Path<String>,
33) -> Result<HttpResponse, actix_web::Error> {
34 let slug_str = slug.into_inner();
35 match state
36 .service_provider_use_cases
37 .find_by_slug(&slug_str)
38 .await
39 .map_err(actix_web::error::ErrorInternalServerError)?
40 {
41 Some(provider) => Ok(HttpResponse::Ok().json(provider)),
42 None => Ok(HttpResponse::NotFound().json(serde_json::json!({
43 "error": format!("Provider not found: {}", slug_str)
44 }))),
45 }
46}
47
48#[post("/service-providers")]
68pub async fn create_service_provider(
69 state: web::Data<AppState>,
70 request: web::Json<CreateServiceProviderDto>,
71 user: AuthenticatedUser,
72) -> Result<HttpResponse, actix_web::Error> {
73 if !matches!(user.role.as_str(), "syndic" | "superadmin") {
74 return Ok(HttpResponse::Forbidden().json(serde_json::json!({
75 "error": "Only syndic or superadmin can register a service provider",
76 "code": "invalid_role",
77 })));
78 }
79
80 let org_id = user
81 .organization_id
82 .ok_or_else(|| actix_web::error::ErrorBadRequest("Organization ID required"))?;
83
84 let response = state
85 .service_provider_use_cases
86 .create(org_id, request.into_inner())
87 .await
88 .map_err(actix_web::error::ErrorBadRequest)?;
89
90 Ok(HttpResponse::Created().json(response))
91}
92
93#[get("/buildings/{building_id}/reports/contract-evaluations/annual")]
96pub async fn get_contract_evaluations_annual(
97 state: web::Data<AppState>,
98 building_id: web::Path<Uuid>,
99 web::Query(params): web::Query<std::collections::HashMap<String, String>>,
100 user: AuthenticatedUser,
101) -> Result<HttpResponse, actix_web::Error> {
102 let building_id = building_id.into_inner();
103
104 if let Err(err) = verify_building_org_access(
111 &user,
112 building_id,
113 &state.building_use_cases,
114 &state.acp_use_cases,
115 )
116 .await
117 {
118 return Ok(err.error_response());
119 }
120 let year = params
121 .get("year")
122 .and_then(|y| y.parse::<i32>().ok())
123 .unwrap_or_else(|| chrono::Local::now().year());
124
125 let report = ContractEvaluationsAnnualReportDto {
128 building_id: building_id.to_string(),
129 report_year: year,
130 total_evaluations: 0,
131 total_providers_evaluated: 0,
132 average_global_score: 0.0,
133 recommendation_rate: 0.0,
134 evaluations: vec![],
135 };
136
137 Ok(HttpResponse::Ok().json(report))
138}