feat: WIP clothing edit and delete
This commit is contained in:
parent
5b5e312152
commit
8d4a981055
27
web/src/endpoints/clothing/delete.rs
Normal file
27
web/src/endpoints/clothing/delete.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use actix_web::{web, HttpResponse, Responder};
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::{
|
||||
endpoints::IdPath,
|
||||
models::{Clothing, Role, User},
|
||||
utils::ApplicationError,
|
||||
};
|
||||
|
||||
#[actix_web::delete("/clothing/{id}")]
|
||||
pub async fn delete(
|
||||
user: web::ReqData<User>,
|
||||
pool: web::Data<PgPool>,
|
||||
path: web::Path<IdPath>,
|
||||
) -> Result<impl Responder, ApplicationError> {
|
||||
if user.role != Role::Admin {
|
||||
return Err(ApplicationError::Unauthorized);
|
||||
}
|
||||
|
||||
let Some(clothing) = Clothing::read(pool.get_ref(), path.id).await? else {
|
||||
return Ok(HttpResponse::NotFound().finish());
|
||||
};
|
||||
|
||||
Clothing::delete(pool.get_ref(), clothing.id).await?;
|
||||
|
||||
Ok(HttpResponse::Ok().finish())
|
||||
}
|
30
web/src/endpoints/clothing/get_edit.rs
Normal file
30
web/src/endpoints/clothing/get_edit.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use actix_web::{web, HttpResponse, Responder};
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::{
|
||||
endpoints::{clothing::EditClothingPartialTemplate, IdPath},
|
||||
models::{Clothing, Role, User},
|
||||
utils::{ApplicationError, TemplateResponse},
|
||||
};
|
||||
|
||||
#[actix_web::get("/clothing/edit/{id}")]
|
||||
pub async fn get(
|
||||
user: web::ReqData<User>,
|
||||
pool: web::Data<PgPool>,
|
||||
path: web::Path<IdPath>,
|
||||
) -> Result<impl Responder, ApplicationError> {
|
||||
if user.role != Role::Admin {
|
||||
return Err(ApplicationError::Unauthorized);
|
||||
}
|
||||
|
||||
let Some(clothing) = Clothing::read(pool.get_ref(), path.id).await? else {
|
||||
return Ok(HttpResponse::NotFound().finish());
|
||||
};
|
||||
|
||||
let template = EditClothingPartialTemplate {
|
||||
id: Some(clothing.id),
|
||||
name: Some(clothing.name),
|
||||
};
|
||||
|
||||
Ok(template.to_response()?)
|
||||
}
|
21
web/src/endpoints/clothing/get_new.rs
Normal file
21
web/src/endpoints/clothing/get_new.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use actix_web::{web, Responder};
|
||||
|
||||
use crate::{
|
||||
endpoints::clothing::EditClothingPartialTemplate,
|
||||
models::{Role, User},
|
||||
utils::{ApplicationError, TemplateResponse},
|
||||
};
|
||||
|
||||
#[actix_web::get("/clothing/new")]
|
||||
pub async fn get(user: web::ReqData<User>) -> Result<impl Responder, ApplicationError> {
|
||||
if user.role != Role::Admin {
|
||||
return Err(ApplicationError::Unauthorized);
|
||||
}
|
||||
|
||||
let template = EditClothingPartialTemplate {
|
||||
id: None,
|
||||
name: None,
|
||||
};
|
||||
|
||||
Ok(template.to_response()?)
|
||||
}
|
27
web/src/endpoints/clothing/get_read.rs
Normal file
27
web/src/endpoints/clothing/get_read.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use actix_web::{web, HttpResponse, Responder};
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::{
|
||||
endpoints::{clothing::ReadClothingPartialTemplate, IdPath},
|
||||
models::{Clothing, Role, User},
|
||||
utils::{ApplicationError, TemplateResponse},
|
||||
};
|
||||
|
||||
#[actix_web::get("/clothing/{id}")]
|
||||
pub async fn get(
|
||||
user: web::ReqData<User>,
|
||||
pool: web::Data<PgPool>,
|
||||
path: web::Path<IdPath>,
|
||||
) -> Result<impl Responder, ApplicationError> {
|
||||
if user.role != Role::Admin {
|
||||
return Err(ApplicationError::Unauthorized);
|
||||
}
|
||||
|
||||
let Some(clothing) = Clothing::read(pool.get_ref(), path.id).await? else {
|
||||
return Ok(HttpResponse::NotFound().finish());
|
||||
};
|
||||
|
||||
let template = ReadClothingPartialTemplate { c: clothing };
|
||||
|
||||
Ok(template.to_response()?)
|
||||
}
|
@ -1 +1,31 @@
|
||||
use askama::Template;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::models::Clothing;
|
||||
use crate::filters;
|
||||
|
||||
pub mod delete;
|
||||
pub mod get_edit;
|
||||
pub mod get_new;
|
||||
pub mod get_overview;
|
||||
pub mod get_read;
|
||||
pub mod post_edit;
|
||||
pub mod post_new;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "clothing/clothing_entry_edit.html")]
|
||||
struct EditClothingPartialTemplate {
|
||||
id: Option<i32>,
|
||||
name: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "clothing/clothing_entry_read.html")]
|
||||
struct ReadClothingPartialTemplate {
|
||||
c: Clothing,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct NewOrEditClothingForm {
|
||||
name: String,
|
||||
}
|
||||
|
35
web/src/endpoints/clothing/post_edit.rs
Normal file
35
web/src/endpoints/clothing/post_edit.rs
Normal file
@ -0,0 +1,35 @@
|
||||
use actix_web::{web, HttpResponse, Responder};
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::{
|
||||
endpoints::{
|
||||
clothing::{NewOrEditClothingForm, ReadClothingPartialTemplate},
|
||||
IdPath,
|
||||
},
|
||||
models::{Clothing, Role, User},
|
||||
utils::{ApplicationError, TemplateResponse},
|
||||
};
|
||||
|
||||
#[actix_web::post("/clothing/{id}")]
|
||||
pub async fn post(
|
||||
user: web::ReqData<User>,
|
||||
pool: web::Data<PgPool>,
|
||||
path: web::Path<IdPath>,
|
||||
form: web::Form<NewOrEditClothingForm>,
|
||||
) -> Result<impl Responder, ApplicationError> {
|
||||
if user.role == Role::Admin {
|
||||
return Err(ApplicationError::Unauthorized);
|
||||
}
|
||||
|
||||
let Some(mut clothing) = Clothing::read(pool.get_ref(), path.id).await? else {
|
||||
return Ok(HttpResponse::NotFound().finish());
|
||||
};
|
||||
|
||||
clothing.name = form.name.to_string();
|
||||
|
||||
Clothing::update(pool.get_ref(), clothing.id, &clothing.name).await?;
|
||||
|
||||
let template = ReadClothingPartialTemplate { c: clothing };
|
||||
|
||||
Ok(template.to_response()?)
|
||||
}
|
25
web/src/endpoints/clothing/post_new.rs
Normal file
25
web/src/endpoints/clothing/post_new.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use actix_web::{web, Responder};
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::{
|
||||
endpoints::clothing::{NewOrEditClothingForm, ReadClothingPartialTemplate},
|
||||
models::{Clothing, Role, User},
|
||||
utils::{ApplicationError, TemplateResponse},
|
||||
};
|
||||
|
||||
#[actix_web::post("/clothing")]
|
||||
pub async fn post(
|
||||
user: web::ReqData<User>,
|
||||
pool: web::Data<PgPool>,
|
||||
form: web::Form<NewOrEditClothingForm>,
|
||||
) -> Result<impl Responder, ApplicationError> {
|
||||
if user.role == Role::Admin {
|
||||
return Err(ApplicationError::Unauthorized);
|
||||
}
|
||||
|
||||
let clothing = Clothing::create(pool.get_ref(), &form.name).await?;
|
||||
|
||||
let template = ReadClothingPartialTemplate { c: clothing };
|
||||
|
||||
Ok(template.to_response()?)
|
||||
}
|
@ -93,4 +93,12 @@ pub fn init(cfg: &mut ServiceConfig) {
|
||||
|
||||
cfg.service(vehicle_assignment::post_new::post);
|
||||
cfg.service(vehicle_assignment::delete::delete);
|
||||
|
||||
cfg.service(clothing::get_overview::get);
|
||||
cfg.service(clothing::get_edit::get);
|
||||
cfg.service(clothing::get_new::get);
|
||||
cfg.service(clothing::get_read::get);
|
||||
cfg.service(clothing::delete::delete);
|
||||
cfg.service(clothing::post_new::post);
|
||||
cfg.service(clothing::post_edit::post);
|
||||
}
|
||||
|
@ -9,12 +9,17 @@ pub struct Clothing {
|
||||
}
|
||||
|
||||
impl Clothing {
|
||||
pub async fn create(pool: &PgPool, name: &str) -> Result<()> {
|
||||
query!("INSERT INTO clothing (name) VALUES ($1);", name)
|
||||
.execute(pool)
|
||||
pub async fn create(pool: &PgPool, name: &str) -> Result<Clothing> {
|
||||
let r = query!("INSERT INTO clothing (name) VALUES ($1) RETURNING id;", name)
|
||||
.fetch_one(pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
let created_clothing = Clothing {
|
||||
id: r.id,
|
||||
name: name.to_string()
|
||||
};
|
||||
|
||||
Ok(created_clothing)
|
||||
}
|
||||
|
||||
pub async fn read_all(pool: &PgPool) -> Result<Vec<Clothing>> {
|
||||
|
@ -1,15 +1,33 @@
|
||||
<li>
|
||||
<input class="input" type="text" value="{{ c.name }}" />
|
||||
<div class="buttons">
|
||||
<button class="button">
|
||||
<svg class="icon">
|
||||
<use href="/static/feather-sprite.svg#check-circle" />
|
||||
</svg>
|
||||
</button>
|
||||
<button class="button">
|
||||
<svg class="icon">
|
||||
<use href="/static/feather-sprite.svg#x-circle" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<form method="post" action="/clothing/{% if let Some(id) = id %}{{ id }}{% else %}new{% endif %}">
|
||||
<div class="level">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<input class="input" type="text" {{ name|insert_value }} />
|
||||
</div>
|
||||
|
||||
<div class="level-item buttons are-small">
|
||||
<button class="button">
|
||||
<svg class="icon">
|
||||
<use href="/static/feather-sprite.svg#check-circle" />
|
||||
</svg>
|
||||
</button>
|
||||
{% if let Some(id) = id %}
|
||||
<button class="button" type="button" hx-get="/clothing/{{ id }}" hx-swap="outerHTML"
|
||||
hx-target="closest li">
|
||||
<svg class="icon">
|
||||
<use href="/static/feather-sprite.svg#x-circle" />
|
||||
</svg>
|
||||
</button>
|
||||
{% else %}
|
||||
<button class="button" type="button" _="on click remove the closest <li/>">
|
||||
<svg class="icon">
|
||||
<use href="/static/feather-sprite.svg#x-circle" />
|
||||
</svg>
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
|
@ -1,15 +1,23 @@
|
||||
<li>
|
||||
{{ c.name }}"
|
||||
<div class="buttons">
|
||||
<button class="button">
|
||||
<svg class="icon">
|
||||
<use href="/static/feather-sprite.svg#edit" />
|
||||
</svg>
|
||||
</button>
|
||||
<button class="button">
|
||||
<svg class="icon">
|
||||
<use href="/static/feather-sprite.svg#x-circle" />
|
||||
</svg>
|
||||
</button>
|
||||
<div class="level">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
{{ c.name }}
|
||||
</div>
|
||||
|
||||
<div class="level-item buttons are-small">
|
||||
<button class="button" hx-get="/clothing/edit/{{ c.id }}" hx-swap="outerHTML" hx-target="closest li">
|
||||
<svg class="icon">
|
||||
<use href="/static/feather-sprite.svg#edit" />
|
||||
</svg>
|
||||
</button>
|
||||
<button class="button" hx-delete="/clothing/{{ c.id }}" hx-swap="delete"
|
||||
hx-target="closest li" hx-trigger="confirmed">
|
||||
<svg class="icon">
|
||||
<use href="/static/feather-sprite.svg#trash-2" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
@ -13,6 +13,9 @@
|
||||
{% for c in clothings %}
|
||||
{% include "clothing_entry_read.html" %}
|
||||
{% endfor %}
|
||||
<li>
|
||||
<button hx-get="/clothing/new" hx-swap="beforebegin">neue Anzugsordnung +</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
Loading…
x
Reference in New Issue
Block a user