koprogo_api/infrastructure/web/handlers/
notice_handlers.rs1use crate::application::dto::{CreateNoticeDto, SetExpirationDto, UpdateNoticeDto};
2use crate::domain::entities::{NoticeCategory, NoticeStatus, NoticeType};
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("/notices")]
12pub async fn create_notice(
13 data: web::Data<AppState>,
14 auth: AuthenticatedUser,
15 request: web::Json<CreateNoticeDto>,
16) -> impl Responder {
17 match data
18 .notice_use_cases
19 .create_notice(auth.user_id, request.into_inner())
20 .await
21 {
22 Ok(notice) => HttpResponse::Created().json(notice),
23 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({"error": e})),
24 }
25}
26
27#[get("/notices/{id}")]
31pub async fn get_notice(data: web::Data<AppState>, id: web::Path<Uuid>) -> impl Responder {
32 match data.notice_use_cases.get_notice(id.into_inner()).await {
33 Ok(notice) => HttpResponse::Ok().json(notice),
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}/notices")]
48pub async fn list_building_notices(
49 data: web::Data<AppState>,
50 building_id: web::Path<Uuid>,
51) -> impl Responder {
52 match data
53 .notice_use_cases
54 .list_building_notices(building_id.into_inner())
55 .await
56 {
57 Ok(notices) => HttpResponse::Ok().json(notices),
58 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
59 }
60}
61
62#[get("/buildings/{building_id}/notices/published")]
66pub async fn list_published_notices(
67 data: web::Data<AppState>,
68 building_id: web::Path<Uuid>,
69) -> impl Responder {
70 match data
71 .notice_use_cases
72 .list_published_notices(building_id.into_inner())
73 .await
74 {
75 Ok(notices) => HttpResponse::Ok().json(notices),
76 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
77 }
78}
79
80#[get("/buildings/{building_id}/notices/pinned")]
84pub async fn list_pinned_notices(
85 data: web::Data<AppState>,
86 building_id: web::Path<Uuid>,
87) -> impl Responder {
88 match data
89 .notice_use_cases
90 .list_pinned_notices(building_id.into_inner())
91 .await
92 {
93 Ok(notices) => HttpResponse::Ok().json(notices),
94 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
95 }
96}
97
98#[get("/buildings/{building_id}/notices/type/{notice_type}")]
102pub async fn list_notices_by_type(
103 data: web::Data<AppState>,
104 path: web::Path<(Uuid, String)>,
105) -> impl Responder {
106 let (building_id, notice_type_str) = path.into_inner();
107
108 let notice_type = match serde_json::from_str::<NoticeType>(&format!("\"{}\"", notice_type_str))
110 {
111 Ok(nt) => nt,
112 Err(_) => {
113 return HttpResponse::BadRequest().json(serde_json::json!({
114 "error": format!("Invalid notice type: {}. Valid types: Announcement, Event, LostAndFound, ClassifiedAd", notice_type_str)
115 }))
116 }
117 };
118
119 match data
120 .notice_use_cases
121 .list_notices_by_type(building_id, notice_type)
122 .await
123 {
124 Ok(notices) => HttpResponse::Ok().json(notices),
125 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
126 }
127}
128
129#[get("/buildings/{building_id}/notices/category/{category}")]
133pub async fn list_notices_by_category(
134 data: web::Data<AppState>,
135 path: web::Path<(Uuid, String)>,
136) -> impl Responder {
137 let (building_id, category_str) = path.into_inner();
138
139 let category = match serde_json::from_str::<NoticeCategory>(&format!("\"{}\"", category_str)) {
141 Ok(c) => c,
142 Err(_) => {
143 return HttpResponse::BadRequest().json(serde_json::json!({
144 "error": format!("Invalid category: {}. Valid categories: General, Maintenance, Social, Security, Environment, Parking, Other", category_str)
145 }))
146 }
147 };
148
149 match data
150 .notice_use_cases
151 .list_notices_by_category(building_id, category)
152 .await
153 {
154 Ok(notices) => HttpResponse::Ok().json(notices),
155 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
156 }
157}
158
159#[get("/buildings/{building_id}/notices/status/{status}")]
163pub async fn list_notices_by_status(
164 data: web::Data<AppState>,
165 path: web::Path<(Uuid, String)>,
166) -> impl Responder {
167 let (building_id, status_str) = path.into_inner();
168
169 let status = match serde_json::from_str::<NoticeStatus>(&format!("\"{}\"", status_str)) {
171 Ok(s) => s,
172 Err(_) => {
173 return HttpResponse::BadRequest().json(serde_json::json!({
174 "error": format!("Invalid status: {}. Valid statuses: Draft, Published, Archived, Expired", status_str)
175 }))
176 }
177 };
178
179 match data
180 .notice_use_cases
181 .list_notices_by_status(building_id, status)
182 .await
183 {
184 Ok(notices) => HttpResponse::Ok().json(notices),
185 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
186 }
187}
188
189#[get("/owners/{author_id}/notices")]
193pub async fn list_author_notices(
194 data: web::Data<AppState>,
195 author_id: web::Path<Uuid>,
196) -> impl Responder {
197 match data
198 .notice_use_cases
199 .list_author_notices(author_id.into_inner())
200 .await
201 {
202 Ok(notices) => HttpResponse::Ok().json(notices),
203 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
204 }
205}
206
207#[put("/notices/{id}")]
211pub async fn update_notice(
212 data: web::Data<AppState>,
213 auth: AuthenticatedUser,
214 id: web::Path<Uuid>,
215 request: web::Json<UpdateNoticeDto>,
216) -> impl Responder {
217 match data
218 .notice_use_cases
219 .update_notice(id.into_inner(), auth.user_id, request.into_inner())
220 .await
221 {
222 Ok(notice) => HttpResponse::Ok().json(notice),
223 Err(e) => {
224 if e.contains("Unauthorized") {
225 HttpResponse::Forbidden().json(serde_json::json!({"error": e}))
226 } else if e.contains("not found") {
227 HttpResponse::NotFound().json(serde_json::json!({"error": e}))
228 } else {
229 HttpResponse::BadRequest().json(serde_json::json!({"error": e}))
230 }
231 }
232 }
233}
234
235#[post("/notices/{id}/publish")]
239pub async fn publish_notice(
240 data: web::Data<AppState>,
241 auth: AuthenticatedUser,
242 id: web::Path<Uuid>,
243) -> impl Responder {
244 match data
245 .notice_use_cases
246 .publish_notice(id.into_inner(), auth.user_id)
247 .await
248 {
249 Ok(notice) => HttpResponse::Ok().json(notice),
250 Err(e) => {
251 if e.contains("Unauthorized") {
252 HttpResponse::Forbidden().json(serde_json::json!({"error": e}))
253 } else if e.contains("not found") {
254 HttpResponse::NotFound().json(serde_json::json!({"error": e}))
255 } else {
256 HttpResponse::BadRequest().json(serde_json::json!({"error": e}))
257 }
258 }
259 }
260}
261
262#[post("/notices/{id}/archive")]
266pub async fn archive_notice(
267 data: web::Data<AppState>,
268 auth: AuthenticatedUser,
269 id: web::Path<Uuid>,
270) -> impl Responder {
271 match data
272 .notice_use_cases
273 .archive_notice(id.into_inner(), auth.user_id, &auth.role)
274 .await
275 {
276 Ok(notice) => HttpResponse::Ok().json(notice),
277 Err(e) => {
278 if e.contains("Unauthorized") {
279 HttpResponse::Forbidden().json(serde_json::json!({"error": e}))
280 } else if e.contains("not found") {
281 HttpResponse::NotFound().json(serde_json::json!({"error": e}))
282 } else {
283 HttpResponse::BadRequest().json(serde_json::json!({"error": e}))
284 }
285 }
286 }
287}
288
289#[post("/notices/{id}/pin")]
293pub async fn pin_notice(
294 data: web::Data<AppState>,
295 auth: AuthenticatedUser,
296 id: web::Path<Uuid>,
297) -> impl Responder {
298 match data
299 .notice_use_cases
300 .pin_notice(id.into_inner(), &auth.role)
301 .await
302 {
303 Ok(notice) => HttpResponse::Ok().json(notice),
304 Err(e) => {
305 if e.contains("Unauthorized") {
306 HttpResponse::Forbidden().json(serde_json::json!({"error": e}))
307 } else if e.contains("not found") {
308 HttpResponse::NotFound().json(serde_json::json!({"error": e}))
309 } else {
310 HttpResponse::BadRequest().json(serde_json::json!({"error": e}))
311 }
312 }
313 }
314}
315
316#[post("/notices/{id}/unpin")]
320pub async fn unpin_notice(
321 data: web::Data<AppState>,
322 auth: AuthenticatedUser,
323 id: web::Path<Uuid>,
324) -> impl Responder {
325 match data
326 .notice_use_cases
327 .unpin_notice(id.into_inner(), &auth.role)
328 .await
329 {
330 Ok(notice) => HttpResponse::Ok().json(notice),
331 Err(e) => {
332 if e.contains("Unauthorized") {
333 HttpResponse::Forbidden().json(serde_json::json!({"error": e}))
334 } else if e.contains("not found") {
335 HttpResponse::NotFound().json(serde_json::json!({"error": e}))
336 } else {
337 HttpResponse::BadRequest().json(serde_json::json!({"error": e}))
338 }
339 }
340 }
341}
342
343#[put("/notices/{id}/expiration")]
347pub async fn set_expiration(
348 data: web::Data<AppState>,
349 auth: AuthenticatedUser,
350 id: web::Path<Uuid>,
351 request: web::Json<SetExpirationDto>,
352) -> impl Responder {
353 match data
354 .notice_use_cases
355 .set_expiration(id.into_inner(), auth.user_id, request.into_inner())
356 .await
357 {
358 Ok(notice) => HttpResponse::Ok().json(notice),
359 Err(e) => {
360 if e.contains("Unauthorized") {
361 HttpResponse::Forbidden().json(serde_json::json!({"error": e}))
362 } else if e.contains("not found") {
363 HttpResponse::NotFound().json(serde_json::json!({"error": e}))
364 } else {
365 HttpResponse::BadRequest().json(serde_json::json!({"error": e}))
366 }
367 }
368 }
369}
370
371#[delete("/notices/{id}")]
375pub async fn delete_notice(
376 data: web::Data<AppState>,
377 auth: AuthenticatedUser,
378 id: web::Path<Uuid>,
379) -> impl Responder {
380 match data
381 .notice_use_cases
382 .delete_notice(id.into_inner(), auth.user_id)
383 .await
384 {
385 Ok(_) => HttpResponse::NoContent().finish(),
386 Err(e) => {
387 if e.contains("Unauthorized") {
388 HttpResponse::Forbidden().json(serde_json::json!({"error": e}))
389 } else if e.contains("not found") {
390 HttpResponse::NotFound().json(serde_json::json!({"error": e}))
391 } else {
392 HttpResponse::BadRequest().json(serde_json::json!({"error": e}))
393 }
394 }
395 }
396}
397
398#[get("/buildings/{building_id}/notices/statistics")]
402pub async fn get_notice_statistics(
403 data: web::Data<AppState>,
404 building_id: web::Path<Uuid>,
405) -> impl Responder {
406 match data
407 .notice_use_cases
408 .get_statistics(building_id.into_inner())
409 .await
410 {
411 Ok(stats) => HttpResponse::Ok().json(stats),
412 Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({"error": e})),
413 }
414}