koprogo_api/infrastructure/web/handlers/
skill_handlers.rs1use crate::application::dto::{CreateSkillDto, UpdateSkillDto};
2use crate::domain::entities::{ExpertiseLevel, SkillCategory};
3use crate::infrastructure::web::app_state::AppState;
4use crate::infrastructure::web::middleware::AuthenticatedUser;
5use actix_web::{delete, get, post, put, web, HttpResponse, Responder};
6use uuid::Uuid;
7
8#[post("/skills")]
12pub async fn create_skill(
13 data: web::Data<AppState>,
14 auth: AuthenticatedUser,
15 request: web::Json<CreateSkillDto>,
16) -> impl Responder {
17 match data
18 .skill_use_cases
19 .create_skill(auth.user_id, request.into_inner())
20 .await
21 {
22 Ok(skill) => HttpResponse::Created().json(skill),
23 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
24 }
25}
26
27#[get("/skills/{id}")]
31pub async fn get_skill(data: web::Data<AppState>, id: web::Path<Uuid>) -> impl Responder {
32 match data.skill_use_cases.get_skill(id.into_inner()).await {
33 Ok(skill) => HttpResponse::Ok().json(skill),
34 Err(e) => {
35 if e.contains("not found") {
36 HttpResponse::NotFound().json(serde_json::json!({"error": e}))
37 } else {
38 HttpResponse::InternalServerError().json(serde_json::json!({"error": e}))
39 }
40 }
41 }
42}
43
44#[get("/buildings/{building_id}/skills")]
48pub async fn list_building_skills(
49 data: web::Data<AppState>,
50 building_id: web::Path<Uuid>,
51) -> impl Responder {
52 match data
53 .skill_use_cases
54 .list_building_skills(building_id.into_inner())
55 .await
56 {
57 Ok(skills) => HttpResponse::Ok().json(skills),
58 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
59 }
60}
61
62#[get("/buildings/{building_id}/skills/available")]
66pub async fn list_available_skills(
67 data: web::Data<AppState>,
68 building_id: web::Path<Uuid>,
69) -> impl Responder {
70 match data
71 .skill_use_cases
72 .list_available_skills(building_id.into_inner())
73 .await
74 {
75 Ok(skills) => HttpResponse::Ok().json(skills),
76 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
77 }
78}
79
80#[get("/buildings/{building_id}/skills/free")]
84pub async fn list_free_skills(
85 data: web::Data<AppState>,
86 building_id: web::Path<Uuid>,
87) -> impl Responder {
88 match data
89 .skill_use_cases
90 .list_free_skills(building_id.into_inner())
91 .await
92 {
93 Ok(skills) => HttpResponse::Ok().json(skills),
94 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
95 }
96}
97
98#[get("/buildings/{building_id}/skills/professional")]
102pub async fn list_professional_skills(
103 data: web::Data<AppState>,
104 building_id: web::Path<Uuid>,
105) -> impl Responder {
106 match data
107 .skill_use_cases
108 .list_professional_skills(building_id.into_inner())
109 .await
110 {
111 Ok(skills) => HttpResponse::Ok().json(skills),
112 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
113 }
114}
115
116#[get("/buildings/{building_id}/skills/category/{category}")]
120pub async fn list_skills_by_category(
121 data: web::Data<AppState>,
122 path: web::Path<(Uuid, String)>,
123) -> impl Responder {
124 let (building_id, category_str) = path.into_inner();
125
126 let category = match serde_json::from_str::<SkillCategory>(&format!("\"{}\"", category_str)) {
128 Ok(c) => c,
129 Err(_) => {
130 return HttpResponse::BadRequest().json(serde_json::json!({
131 "error": format!("Invalid skill category: {}. Valid categories: HomeRepair, Languages, Technology, Education, Arts, Sports, Cooking, Gardening, Health, Legal, Financial, PetCare, Other", category_str)
132 }))
133 }
134 };
135
136 match data
137 .skill_use_cases
138 .list_skills_by_category(building_id, category)
139 .await
140 {
141 Ok(skills) => HttpResponse::Ok().json(skills),
142 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
143 }
144}
145
146#[get("/buildings/{building_id}/skills/expertise/{level}")]
150pub async fn list_skills_by_expertise(
151 data: web::Data<AppState>,
152 path: web::Path<(Uuid, String)>,
153) -> impl Responder {
154 let (building_id, level_str) = path.into_inner();
155
156 let level = match serde_json::from_str::<ExpertiseLevel>(&format!("\"{}\"", level_str)) {
158 Ok(l) => l,
159 Err(_) => {
160 return HttpResponse::BadRequest().json(serde_json::json!({
161 "error": format!("Invalid expertise level: {}. Valid levels: Beginner, Intermediate, Advanced, Expert", level_str)
162 }))
163 }
164 };
165
166 match data
167 .skill_use_cases
168 .list_skills_by_expertise(building_id, level)
169 .await
170 {
171 Ok(skills) => HttpResponse::Ok().json(skills),
172 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
173 }
174}
175
176#[get("/owners/{owner_id}/skills")]
180pub async fn list_owner_skills(
181 data: web::Data<AppState>,
182 owner_id: web::Path<Uuid>,
183) -> impl Responder {
184 match data
185 .skill_use_cases
186 .list_owner_skills(owner_id.into_inner())
187 .await
188 {
189 Ok(skills) => HttpResponse::Ok().json(skills),
190 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
191 }
192}
193
194#[put("/skills/{id}")]
198pub async fn update_skill(
199 data: web::Data<AppState>,
200 auth: AuthenticatedUser,
201 id: web::Path<Uuid>,
202 request: web::Json<UpdateSkillDto>,
203) -> impl Responder {
204 match data
205 .skill_use_cases
206 .update_skill(id.into_inner(), auth.user_id, request.into_inner())
207 .await
208 {
209 Ok(skill) => HttpResponse::Ok().json(skill),
210 Err(e) => {
211 if e.contains("Unauthorized") {
212 HttpResponse::Forbidden().json(serde_json::json!({"error": e}))
213 } else if e.contains("not found") {
214 HttpResponse::NotFound().json(serde_json::json!({"error": e}))
215 } else {
216 HttpResponse::BadRequest().json(serde_json::json!({"error": e}))
217 }
218 }
219 }
220}
221
222#[post("/skills/{id}/mark-available")]
226pub async fn mark_skill_available(
227 data: web::Data<AppState>,
228 auth: AuthenticatedUser,
229 id: web::Path<Uuid>,
230) -> impl Responder {
231 match data
232 .skill_use_cases
233 .mark_skill_available(id.into_inner(), auth.user_id)
234 .await
235 {
236 Ok(skill) => HttpResponse::Ok().json(skill),
237 Err(e) => {
238 if e.contains("Unauthorized") {
239 HttpResponse::Forbidden().json(serde_json::json!({"error": e}))
240 } else if e.contains("not found") {
241 HttpResponse::NotFound().json(serde_json::json!({"error": e}))
242 } else {
243 HttpResponse::BadRequest().json(serde_json::json!({"error": e}))
244 }
245 }
246 }
247}
248
249#[post("/skills/{id}/mark-unavailable")]
253pub async fn mark_skill_unavailable(
254 data: web::Data<AppState>,
255 auth: AuthenticatedUser,
256 id: web::Path<Uuid>,
257) -> impl Responder {
258 match data
259 .skill_use_cases
260 .mark_skill_unavailable(id.into_inner(), auth.user_id)
261 .await
262 {
263 Ok(skill) => HttpResponse::Ok().json(skill),
264 Err(e) => {
265 if e.contains("Unauthorized") {
266 HttpResponse::Forbidden().json(serde_json::json!({"error": e}))
267 } else if e.contains("not found") {
268 HttpResponse::NotFound().json(serde_json::json!({"error": e}))
269 } else {
270 HttpResponse::BadRequest().json(serde_json::json!({"error": e}))
271 }
272 }
273 }
274}
275
276#[delete("/skills/{id}")]
280pub async fn delete_skill(
281 data: web::Data<AppState>,
282 auth: AuthenticatedUser,
283 id: web::Path<Uuid>,
284) -> impl Responder {
285 match data
286 .skill_use_cases
287 .delete_skill(id.into_inner(), auth.user_id)
288 .await
289 {
290 Ok(_) => HttpResponse::NoContent().finish(),
291 Err(e) => {
292 if e.contains("Unauthorized") {
293 HttpResponse::Forbidden().json(serde_json::json!({"error": e}))
294 } else if e.contains("not found") {
295 HttpResponse::NotFound().json(serde_json::json!({"error": e}))
296 } else {
297 HttpResponse::BadRequest().json(serde_json::json!({"error": e}))
298 }
299 }
300 }
301}
302
303#[get("/buildings/{building_id}/skills/statistics")]
307pub async fn get_skill_statistics(
308 data: web::Data<AppState>,
309 building_id: web::Path<Uuid>,
310) -> impl Responder {
311 match data
312 .skill_use_cases
313 .get_skill_statistics(building_id.into_inner())
314 .await
315 {
316 Ok(stats) => HttpResponse::Ok().json(stats),
317 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
318 }
319}