feat: WIP edit and delete location and area
This commit is contained in:
parent
a1d86992ef
commit
bdc57fd22c
18
src/endpoints/location/delete.rs
Normal file
18
src/endpoints/location/delete.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use actix_web::{http::header::LOCATION, web, HttpResponse, Responder};
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::{endpoints::IdPath, models::User, utils::ApplicationError};
|
||||
|
||||
|
||||
#[actix_web::delete("/locations/delete/{id}")]
|
||||
pub async fn delete (
|
||||
user: web::ReqData<User>,
|
||||
pool: web::Data<PgPool>,
|
||||
path: web::Path<IdPath>
|
||||
) -> Result<impl Responder, ApplicationError> {
|
||||
|
||||
Ok(HttpResponse::Found()
|
||||
.insert_header((LOCATION, "/locations"))
|
||||
.insert_header(("HX-LOCATION", "/locations"))
|
||||
.finish())
|
||||
}
|
38
src/endpoints/location/get_edit.rs
Normal file
38
src/endpoints/location/get_edit.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use actix_web::{web, HttpResponse, Responder};
|
||||
use askama_actix::TemplateToResponse;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::{
|
||||
endpoints::{location::LocationTemplate, IdPath},
|
||||
models::{Area, Location, Role, User},
|
||||
utils::ApplicationError,
|
||||
};
|
||||
|
||||
#[actix_web::get("/locations/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::AreaManager && user.role == Role::Admin {
|
||||
return Err(ApplicationError::Unauthorized);
|
||||
}
|
||||
|
||||
let Some(location) = Location::read_by_id(pool.get_ref(), path.id).await? else {
|
||||
return Ok(HttpResponse::NotFound().finish());
|
||||
};
|
||||
|
||||
let mut areas = None;
|
||||
|
||||
if user.role == Role::Admin {
|
||||
areas = Some(Area::read_all(pool.get_ref()).await?);
|
||||
}
|
||||
|
||||
let template = LocationTemplate {
|
||||
user: user.into_inner(),
|
||||
areas,
|
||||
location: Some(location),
|
||||
};
|
||||
|
||||
Ok(template.to_response())
|
||||
}
|
@ -1,16 +1,8 @@
|
||||
use actix_web::{web, HttpResponse, Responder};
|
||||
use askama::Template;
|
||||
use askama_actix::TemplateToResponse;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::models::{Area, Role, User};
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "location/new.html")]
|
||||
pub struct NewLocationTemplate {
|
||||
user: User,
|
||||
areas: Option<Vec<Area>>,
|
||||
}
|
||||
use crate::{endpoints::location::LocationTemplate, models::{Area, Role, User}};
|
||||
|
||||
#[actix_web::get("/locations/new")]
|
||||
pub async fn get(user: web::ReqData<User>, pool: web::Data<PgPool>) -> impl Responder {
|
||||
@ -21,9 +13,10 @@ pub async fn get(user: web::ReqData<User>, pool: web::Data<PgPool>) -> impl Resp
|
||||
areas = Some(Area::read_all(pool.get_ref()).await.unwrap());
|
||||
}
|
||||
|
||||
let template = NewLocationTemplate {
|
||||
let template = LocationTemplate {
|
||||
user: user.into_inner(),
|
||||
areas,
|
||||
location: None
|
||||
};
|
||||
|
||||
return template.to_response();
|
||||
|
@ -1,3 +1,26 @@
|
||||
pub mod get_overview;
|
||||
use askama::Template;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::models::{Area, Location, Role, User};
|
||||
use crate::filters;
|
||||
|
||||
pub mod get_new;
|
||||
pub mod get_overview;
|
||||
pub mod post_new;
|
||||
pub mod get_edit;
|
||||
pub mod post_edit;
|
||||
pub mod delete;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "location/new_or_edit.html")]
|
||||
pub struct LocationTemplate {
|
||||
user: User,
|
||||
areas: Option<Vec<Area>>,
|
||||
location: Option<Location>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct LocationForm {
|
||||
name: String,
|
||||
area: Option<i32>,
|
||||
}
|
||||
|
33
src/endpoints/location/post_edit.rs
Normal file
33
src/endpoints/location/post_edit.rs
Normal file
@ -0,0 +1,33 @@
|
||||
use actix_web::{http::header::LOCATION, web, HttpResponse, Responder};
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::{
|
||||
endpoints::{location::LocationForm, IdPath},
|
||||
models::{Location, Role, User},
|
||||
utils::ApplicationError,
|
||||
};
|
||||
|
||||
#[actix_web::post("/locations/edit/{id}")]
|
||||
pub async fn post(
|
||||
user: web::ReqData<User>,
|
||||
pool: web::Data<PgPool>,
|
||||
form: web::Form<LocationForm>,
|
||||
path: web::Path<IdPath>,
|
||||
) -> Result<impl Responder, ApplicationError> {
|
||||
if user.role == Role::AreaManager && user.role == Role::Admin {
|
||||
return Err(ApplicationError::Unauthorized);
|
||||
}
|
||||
|
||||
let area_id = if user.role == Role::Admin && form.area.is_some() {
|
||||
form.area.unwrap()
|
||||
} else {
|
||||
user.area_id
|
||||
};
|
||||
|
||||
Location::update(pool.get_ref(), path.id, &form.name, area_id).await?;
|
||||
|
||||
Ok(HttpResponse::Found()
|
||||
.insert_header((LOCATION, "/locations"))
|
||||
.insert_header(("HX-LOCATION", "/locations"))
|
||||
.finish())
|
||||
}
|
@ -1,42 +1,38 @@
|
||||
use actix_web::{http::header::LOCATION, web, HttpResponse, Responder};
|
||||
use serde::Deserialize;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::models::{Location, Role, User};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct NewLocationForm {
|
||||
name: String,
|
||||
area: Option<i32>,
|
||||
}
|
||||
use crate::{
|
||||
endpoints::location::LocationForm,
|
||||
models::{Location, Role, User}, utils::ApplicationError,
|
||||
};
|
||||
|
||||
#[actix_web::post("/locations/new")]
|
||||
pub async fn post(
|
||||
user: web::ReqData<User>,
|
||||
pool: web::Data<PgPool>,
|
||||
form: web::Form<NewLocationForm>,
|
||||
) -> impl Responder {
|
||||
form: web::Form<LocationForm>,
|
||||
) -> Result<impl Responder, ApplicationError> {
|
||||
if user.role == Role::AreaManager && user.role == Role::Admin {
|
||||
return Err(ApplicationError::Unauthorized);
|
||||
}
|
||||
|
||||
if user.role == Role::AreaManager || user.role == Role::Admin {
|
||||
let mut area_id = user.area_id;
|
||||
|
||||
if user.role == Role::Admin && form.area.is_some() {
|
||||
area_id = form.area.unwrap();
|
||||
}
|
||||
|
||||
// TODO: rework
|
||||
match Location::create(pool.get_ref(), &form.name, area_id).await {
|
||||
Ok(_) => {
|
||||
return HttpResponse::Found()
|
||||
return Ok(HttpResponse::Found()
|
||||
.insert_header((LOCATION, "/locations"))
|
||||
.insert_header(("HX-LOCATION", "/locations"))
|
||||
.finish()
|
||||
.finish())
|
||||
}
|
||||
Err(err) => {
|
||||
println!("{}", err);
|
||||
return HttpResponse::InternalServerError().finish();
|
||||
return Ok(HttpResponse::InternalServerError().finish());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return HttpResponse::Unauthorized().finish();
|
||||
}
|
||||
|
@ -41,9 +41,7 @@ impl Location {
|
||||
}
|
||||
|
||||
pub async fn read_all(pool: &PgPool) -> anyhow::Result<Vec<Location>> {
|
||||
let records = query!("SELECT * FROM location")
|
||||
.fetch_all(pool)
|
||||
.await?;
|
||||
let records = query!("SELECT * FROM location").fetch_all(pool).await?;
|
||||
|
||||
let locations = records
|
||||
.iter()
|
||||
@ -78,4 +76,42 @@ impl Location {
|
||||
|
||||
Ok(locations)
|
||||
}
|
||||
|
||||
pub async fn read_by_id(pool: &PgPool, id: i32) -> super::Result<Option<Location>> {
|
||||
let record = query!("SELECT * FROM location WHERE id = $1;", id)
|
||||
.fetch_optional(pool)
|
||||
.await?;
|
||||
|
||||
let location = record.and_then(|r| {
|
||||
Some(Location {
|
||||
id: r.id,
|
||||
name: r.name,
|
||||
area_id: r.areaid,
|
||||
area: None,
|
||||
})
|
||||
});
|
||||
|
||||
Ok(location)
|
||||
}
|
||||
|
||||
pub async fn update(pool: &PgPool, id: i32, name: &str, area_id: i32) -> super::Result<()> {
|
||||
query!(
|
||||
"UPDATE location SET name = $1, areaid = $2 WHERE id = $3;",
|
||||
name,
|
||||
area_id,
|
||||
id
|
||||
)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete(pool: &PgPool, id: i32) -> super::Result<()> {
|
||||
sqlx::query!("DELETE FROM location WHERE id = $1;", id)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
<div class="container">
|
||||
{% if area.is_some() %}
|
||||
<form method="post" action="/area/edit/{{ area.as_ref().unwrap().id }}">
|
||||
<h1 class="title">Bereich '{{ area.as_ref().unwrap().name }}' anlegen</h1>
|
||||
<h1 class="title">Bereich '{{ area.as_ref().unwrap().name }}' bearbeiten</h1>
|
||||
{% else %}
|
||||
<form method="post" action="/area/new">
|
||||
<h1 class="title">Neuen Bereich anlegen</h1>
|
||||
|
@ -1,73 +0,0 @@
|
||||
{% extends "nav.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<form method="post" action="/locations/new">
|
||||
<h1 class="title">Neuen Ort anlegen</h1>
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="label">Name</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input class="input" type="text" name="name" required placeholder="Zentralstadion" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if user.role == Role::Admin %}
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="label">Bereich</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field is-narrow">
|
||||
<div class="control">
|
||||
<div class="select is-fullwidth">
|
||||
<select required name="area">
|
||||
{% for area in areas.as_ref().unwrap() %}
|
||||
<option value="{{ area.id }}">{{ area.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label"></div>
|
||||
<div class="field-body">
|
||||
<div class="field is-grouped">
|
||||
<div class="control">
|
||||
<button class="button is-success">
|
||||
<svg class="icon">
|
||||
<use href="/static/feather-sprite.svg#check-circle" />
|
||||
</svg>
|
||||
<span>Speichern</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-link is-light" hx-boost="true" href="/locations">
|
||||
<svg class="icon">
|
||||
<use href="/static/feather-sprite.svg#arrow-left" />
|
||||
</svg>
|
||||
<span>Zurück</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
</script>
|
||||
{% endblock %}
|
90
templates/location/new_or_edit.html
Normal file
90
templates/location/new_or_edit.html
Normal file
@ -0,0 +1,90 @@
|
||||
{% extends "nav.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
{% if location.is_some() %}
|
||||
<form method="post" action="/locations/edit/{{ location.as_ref().unwrap().id }}">
|
||||
<h1 class="title">Veranstaltungsort '{{ location.as_ref().unwrap().name }}' bearbeiten</h1>
|
||||
{% else %}
|
||||
<form method="post" action="/locations/new">
|
||||
<h1 class="title">Neuen Ort anlegen</h1>
|
||||
{% endif %}
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="label">Name</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input class="input" type="text" name="name" required placeholder="Zentralstadion" {% if
|
||||
location.is_some() -%} value="{{ location.as_ref().unwrap().name }}" {% endif -%} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if user.role == Role::Admin %}
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="label">Bereich</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field is-narrow">
|
||||
<div class="control">
|
||||
<div class="select is-fullwidth">
|
||||
<select required name="area">
|
||||
{%- let areaid = None %}
|
||||
{% if let Some(loc) = location %}
|
||||
{% let areaid = loc.area_id %}
|
||||
{% endif -%}
|
||||
{% for area in areas.as_ref().unwrap() %}
|
||||
<option {{ areaid|is_some_and_eq(area.id)|cond_show("selected") }} value="{{ area.id }}">{{
|
||||
area.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label"></div>
|
||||
<div class="field-body">
|
||||
<div class="field is-grouped">
|
||||
<div class="control">
|
||||
<button class="button is-success">
|
||||
<svg class="icon">
|
||||
<use href="/static/feather-sprite.svg#check-circle" />
|
||||
</svg>
|
||||
<span>
|
||||
{% if location.is_some() %}
|
||||
Speichern
|
||||
{% else %}
|
||||
Erstellen
|
||||
{% endif %}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-link is-light" hx-boost="true" href="/locations">
|
||||
<svg class="icon">
|
||||
<use href="/static/feather-sprite.svg#arrow-left" />
|
||||
</svg>
|
||||
<span>Zurück</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
</script>
|
||||
{% endblock %}
|
Loading…
x
Reference in New Issue
Block a user