api/routes/modules/assignments/tickets/post.rs
1//! Ticket creation handler.
2//!
3//! Provides an endpoint to create a new ticket for an assignment.
4//!
5//! Only authenticated users can create tickets, and each ticket is linked
6//! to the assignment and the user who created it.
7
8use axum::{
9 Extension, Json,
10 extract::{Path, State},
11 http::StatusCode,
12 response::IntoResponse,
13};
14use db::models::tickets::Model as TicketModel;
15use serde::Deserialize;
16use util::state::AppState;
17
18use crate::{auth::AuthUser, response::ApiResponse, routes::modules::assignments::tickets::common::TicketResponse};
19
20/// Request payload for creating a ticket.
21#[derive(Debug, Deserialize)]
22pub struct TicketRequest {
23 /// Title of the ticket
24 pub title: String,
25 /// Detailed description of the issue or request
26 pub description: String,
27}
28
29/// Creates a new ticket.
30///
31/// **Endpoint:** `POST /modules/{module_id}/assignments/{assignment_id}/tickets`
32/// **Permissions:** Authenticated users can create tickets for the assignment.
33///
34/// ### Path parameters
35/// - `module_id` → ID of the module (unused in handler, kept for route consistency)
36/// - `assignment_id` → ID of the assignment for which the ticket is created
37///
38/// ### Request body
39/// ```json
40/// {
41/// "title": "Issue with submission",
42/// "description": "I am unable to submit my assignment due to..."
43/// }
44/// ```
45///
46/// ### Responses
47/// - `200 OK` → Ticket created successfully
48/// ```json
49/// {
50/// "success": true,
51/// "data": {
52/// "id": 123,
53/// "title": "Issue with submission",
54/// "description": "I am unable to submit my assignment due to...",
55/// "status": "open",
56/// "user_id": 456
57/// },
58/// "message": "Ticket created successfully"
59/// }
60/// ```
61/// - `500 Internal Server Error` → Failed to create the ticket
62/// ```json
63/// {
64/// "success": false,
65/// "data": null,
66/// "message": "Failed to create ticket: <error message>"
67/// }
68/// ```
69pub async fn create_ticket(
70 State(app_state): State<AppState>,
71 Path((_, assignment_id)): Path<(i64, i64)>,
72 Extension(AuthUser(claims)): Extension<AuthUser>,
73 Json(req): Json<TicketRequest>,
74) -> impl IntoResponse {
75 let db = app_state.db();
76 let user_id = claims.sub;
77
78 match TicketModel::create(db, assignment_id, user_id, &req.title, &req.description).await {
79 Ok(ticket) => {
80 let response = TicketResponse::from(ticket);
81 (
82 StatusCode::OK,
83 Json(ApiResponse::success(
84 response,
85 "Ticket created successfully",
86 )),
87 )
88 }
89 Err(e) => (
90 StatusCode::INTERNAL_SERVER_ERROR,
91 Json(ApiResponse::<TicketResponse>::error(format!(
92 "Failed to create ticket: {}",
93 e
94 ))),
95 ),
96 }
97}