Added functions for adding, deleting and showing tickets

As said, functions got implemented. Also minor changes to migration
layout and dependencies
This commit is contained in:
2026-04-22 20:40:28 +02:00
parent 1725d2538c
commit 8b34dac813
10 changed files with 134 additions and 9 deletions

4
backend/Cargo.lock generated
View File

@@ -1493,6 +1493,7 @@ checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6"
dependencies = [ dependencies = [
"base64", "base64",
"bytes", "bytes",
"chrono",
"crc", "crc",
"crossbeam-queue", "crossbeam-queue",
"either", "either",
@@ -1569,6 +1570,7 @@ dependencies = [
"bitflags", "bitflags",
"byteorder", "byteorder",
"bytes", "bytes",
"chrono",
"crc", "crc",
"digest", "digest",
"dotenvy", "dotenvy",
@@ -1610,6 +1612,7 @@ dependencies = [
"base64", "base64",
"bitflags", "bitflags",
"byteorder", "byteorder",
"chrono",
"crc", "crc",
"dotenvy", "dotenvy",
"etcetera", "etcetera",
@@ -1644,6 +1647,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea"
dependencies = [ dependencies = [
"atoi", "atoi",
"chrono",
"flume", "flume",
"futures-channel", "futures-channel",
"futures-core", "futures-core",

View File

@@ -7,7 +7,7 @@ edition = "2024"
axum = "0.8.9" axum = "0.8.9"
serde = { version = "1.0.228", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.149" serde_json = "1.0.149"
sqlx = { version = "0.8.6", features = ["postgres", "runtime-tokio", "tls-native-tls"] } sqlx = { version = "0.8.6", features = ["postgres", "runtime-tokio", "tls-native-tls", "chrono"] }
tokio = { version = "1.52.1", features = ["rt-multi-thread", "macros"] } tokio = { version = "1.52.1", features = ["rt-multi-thread", "macros"] }
dotenv = "0.15.0" dotenv = "0.15.0"
chrono = { version = "0.4.44", features = ["serde"] } chrono = { version = "0.4.44", features = ["serde"] }

View File

@@ -0,0 +1 @@
mod ticket;

View File

@@ -0,0 +1,103 @@
use std::sync::Arc;
use axum::{
Json,
extract::{Path, State},
http::StatusCode,
response::IntoResponse,
};
use serde_json::json;
use sqlx::query;
use crate::{
AppState,
models::{Ticket, TicketCreateScheme, TicketResponse},
};
pub async fn create_ticket(
State(data): State<Arc<AppState>>,
Json(body): Json<TicketCreateScheme>,
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
let query = query(
r#"INSERT INTO tickets (category, description, betreff, room) VALUES ($1, $2, $3, $4)"#,
)
.bind(body.category.to_string())
.bind(body.description.to_string())
.bind(body.betreff.to_string())
.bind(body.room.to_string())
.execute(&data.db)
.await;
if let Err(err) = query {
return Err((
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({"status": "error", "message": format!("{:?}", err),})),
));
}
let response_status = serde_json::json!({"status": "success"});
Ok(Json(response_status))
}
pub async fn delete_ticket(
Path(id): Path<i32>,
State(data): State<Arc<AppState>>,
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
let query = sqlx::query(r#"DELETE FROM tickets WHERE id = $1"#)
.bind(id)
.execute(&data.db)
.await
.map_err(|e| {
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({"status": "error", "message": format!("{:?}", e)})),
)
})?;
if query.rows_affected() == 0 {
let error_response = serde_json::json!({
"status": "error",
"message": format!("Ticket with ID {} not found", id)
});
return Err((StatusCode::NOT_FOUND, Json(error_response)));
}
Ok(StatusCode::NO_CONTENT)
}
pub async fn get_tickets(
State(data): State<Arc<AppState>>,
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
let tickets =
sqlx::query_as(r#"SELECT * FROM tickets WHERE status <> 'Archived' ORDER BY date DESC"#)
.fetch_all(&data.db)
.await
.map_err(|e| {
let error_response = serde_json::json!({
"status": "error",
"message": format!("Database error: {}", e),
});
(StatusCode::INTERNAL_SERVER_ERROR, Json(error_response))
})?;
let ticket_response = tickets
.iter()
.map(|ticket| filter_record(&ticket))
.collect::<Vec<TicketResponse>>();
let json_response = serde_json::json!(ticket_response);
Ok(Json(json_response))
}
fn filter_record(ticket: &Ticket) -> TicketResponse {
TicketResponse {
id: ticket.id.to_owned(),
category: ticket.category.to_owned(),
betreff: ticket.betreff.to_owned(),
description: ticket.description.to_owned(),
room: ticket.room.to_owned(),
status: ticket.status.to_owned(),
date: ticket.date.to_owned(),
user_id: ticket.user_id.to_owned(),
}
}

View File

@@ -1,8 +1,9 @@
use std::fmt::Display; use std::fmt::Display;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::{Decode, prelude::Type};
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] #[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Decode, Type)]
pub enum Category { pub enum Category {
WhiteboardBeamer, WhiteboardBeamer,
Internet, Internet,
@@ -25,7 +26,7 @@ impl Display for Category {
} }
} }
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] #[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Decode, Type)]
pub enum Status { pub enum Status {
ToDo, ToDo,
InProgress, InProgress,
@@ -56,6 +57,18 @@ pub struct Ticket {
pub user_id: i16, pub user_id: i16,
} }
#[derive(Deserialize, Serialize, Debug, PartialEq)]
pub struct TicketResponse {
pub id: i32,
pub category: Category,
pub betreff: String,
pub description: String,
pub room: i16,
pub status: Status,
pub date: chrono::NaiveDateTime,
pub user_id: i16,
}
#[derive(Deserialize, Serialize, PartialEq, Debug)] #[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct User { pub struct User {
pub id: i16, pub id: i16,

View File

@@ -0,0 +1,2 @@
DROP TYPE category;
DROP TYPE status;

View File

@@ -0,0 +1,2 @@
CREATE TYPE category AS ENUM('Whiteboard Beamer', 'Internet', 'iPad Koffer', 'Apple TV', 'Docu Cam', 'Sonstiges');
CREATE TYPE status AS ENUM('ToDo', 'InProgress', 'Done', 'Archived');

View File

@@ -0,0 +1,2 @@
DROP TABLE tickets;

View File

@@ -1,12 +1,10 @@
CREATE TYPE category AS ENUM('Whiteboard Beamer', 'Internet', 'iPad Koffer', 'Apple TV', 'Docu Cam', 'Sonstiges')
CREAYE TYPE status AS ENUM('ToDo', 'InProgress', 'Done', 'Archived')
CREATE TABLE IF NOT EXISTS tickets ( CREATE TABLE IF NOT EXISTS tickets (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
category category NOT NULL DEFAULT 'Sonstiges', category category NOT NULL DEFAULT 'Sonstiges',
betreff VARCHAR(100), betreff VARCHAR(100),
description VARCHAR, description TEXT,
room SMALLINT, room SMALLINT,
status status NOT NULL DEFAULT 'ToDo', status status NOT NULL DEFAULT 'ToDo',
date TIMESTAMP, date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
user_id SMALLINT user_id SMALLINT
); );

View File

@@ -1,5 +1,5 @@
CREATE TABLE users ( CREATE TABLE users (
id SMALLINT PRIMARY KEY AUTOINCREMENT, id SMALLINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name VARCHAR(30), name VARCHAR(30),
firstname VARCHAR(30), firstname VARCHAR(30),
is_admin BOOLEAN NOT NULL DEFAULT false is_admin BOOLEAN NOT NULL DEFAULT false