koprogo_api/infrastructure/web/handlers/
meeting_handlers.rs1use crate::application::dto::{
2 AddAgendaItemRequest, CompleteMeetingRequest, CreateMeetingRequest, PageRequest, PageResponse,
3 UpdateMeetingRequest,
4};
5use crate::infrastructure::audit::{AuditEventType, AuditLogEntry};
6use crate::infrastructure::web::{AppState, AuthenticatedUser};
7use actix_web::{delete, get, post, put, web, HttpResponse, Responder};
8use uuid::Uuid;
9
10#[post("/meetings")]
11pub async fn create_meeting(
12 state: web::Data<AppState>,
13 user: AuthenticatedUser, mut request: web::Json<CreateMeetingRequest>,
15) -> impl Responder {
16 let organization_id = match user.require_organization() {
19 Ok(org_id) => org_id,
20 Err(e) => {
21 return HttpResponse::Unauthorized().json(serde_json::json!({
22 "error": e.to_string()
23 }))
24 }
25 };
26 request.organization_id = organization_id;
27
28 match state
29 .meeting_use_cases
30 .create_meeting(request.into_inner())
31 .await
32 {
33 Ok(meeting) => {
34 AuditLogEntry::new(
36 AuditEventType::MeetingCreated,
37 Some(user.user_id),
38 Some(organization_id),
39 )
40 .with_resource("Meeting", meeting.id)
41 .log();
42
43 HttpResponse::Created().json(meeting)
44 }
45 Err(err) => {
46 AuditLogEntry::new(
48 AuditEventType::MeetingCreated,
49 Some(user.user_id),
50 Some(organization_id),
51 )
52 .with_error(err.clone())
53 .log();
54
55 HttpResponse::BadRequest().json(serde_json::json!({
56 "error": err
57 }))
58 }
59 }
60}
61
62#[get("/meetings/{id}")]
63pub async fn get_meeting(state: web::Data<AppState>, id: web::Path<Uuid>) -> impl Responder {
64 match state.meeting_use_cases.get_meeting(*id).await {
65 Ok(Some(meeting)) => HttpResponse::Ok().json(meeting),
66 Ok(None) => HttpResponse::NotFound().json(serde_json::json!({
67 "error": "Meeting not found"
68 })),
69 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({
70 "error": err
71 })),
72 }
73}
74
75#[get("/meetings")]
76pub async fn list_meetings(
77 state: web::Data<AppState>,
78 user: AuthenticatedUser,
79 page_request: web::Query<PageRequest>,
80) -> impl Responder {
81 let organization_id = user.organization_id;
82
83 match state
84 .meeting_use_cases
85 .list_meetings_paginated(&page_request, organization_id)
86 .await
87 {
88 Ok((meetings, total)) => {
89 let response =
90 PageResponse::new(meetings, page_request.page, page_request.per_page, total);
91 HttpResponse::Ok().json(response)
92 }
93 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({
94 "error": err
95 })),
96 }
97}
98
99#[get("/buildings/{building_id}/meetings")]
100pub async fn list_meetings_by_building(
101 state: web::Data<AppState>,
102 building_id: web::Path<Uuid>,
103) -> impl Responder {
104 match state
105 .meeting_use_cases
106 .list_meetings_by_building(*building_id)
107 .await
108 {
109 Ok(meetings) => HttpResponse::Ok().json(meetings),
110 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({
111 "error": err
112 })),
113 }
114}
115
116#[put("/meetings/{id}")]
117pub async fn update_meeting(
118 state: web::Data<AppState>,
119 id: web::Path<Uuid>,
120 request: web::Json<UpdateMeetingRequest>,
121) -> impl Responder {
122 match state
123 .meeting_use_cases
124 .update_meeting(*id, request.into_inner())
125 .await
126 {
127 Ok(meeting) => HttpResponse::Ok().json(meeting),
128 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({
129 "error": err
130 })),
131 }
132}
133
134#[post("/meetings/{id}/agenda")]
135pub async fn add_agenda_item(
136 state: web::Data<AppState>,
137 id: web::Path<Uuid>,
138 request: web::Json<AddAgendaItemRequest>,
139) -> impl Responder {
140 match state
141 .meeting_use_cases
142 .add_agenda_item(*id, request.into_inner())
143 .await
144 {
145 Ok(meeting) => HttpResponse::Ok().json(meeting),
146 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({
147 "error": err
148 })),
149 }
150}
151
152#[post("/meetings/{id}/complete")]
153pub async fn complete_meeting(
154 state: web::Data<AppState>,
155 user: AuthenticatedUser,
156 id: web::Path<Uuid>,
157 request: web::Json<CompleteMeetingRequest>,
158) -> impl Responder {
159 match state
160 .meeting_use_cases
161 .complete_meeting(*id, request.into_inner())
162 .await
163 {
164 Ok(meeting) => {
165 AuditLogEntry::new(
167 AuditEventType::MeetingCompleted,
168 Some(user.user_id),
169 user.organization_id,
170 )
171 .with_resource("Meeting", *id)
172 .log();
173
174 HttpResponse::Ok().json(meeting)
175 }
176 Err(err) => {
177 AuditLogEntry::new(
179 AuditEventType::MeetingCompleted,
180 Some(user.user_id),
181 user.organization_id,
182 )
183 .with_resource("Meeting", *id)
184 .with_error(err.clone())
185 .log();
186
187 HttpResponse::BadRequest().json(serde_json::json!({
188 "error": err
189 }))
190 }
191 }
192}
193
194#[post("/meetings/{id}/cancel")]
195pub async fn cancel_meeting(state: web::Data<AppState>, id: web::Path<Uuid>) -> impl Responder {
196 match state.meeting_use_cases.cancel_meeting(*id).await {
197 Ok(meeting) => HttpResponse::Ok().json(meeting),
198 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({
199 "error": err
200 })),
201 }
202}
203
204#[delete("/meetings/{id}")]
205pub async fn delete_meeting(state: web::Data<AppState>, id: web::Path<Uuid>) -> impl Responder {
206 match state.meeting_use_cases.delete_meeting(*id).await {
207 Ok(true) => HttpResponse::NoContent().finish(),
208 Ok(false) => HttpResponse::NotFound().json(serde_json::json!({
209 "error": "Meeting not found"
210 })),
211 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({
212 "error": err
213 })),
214 }
215}