Display locations

This commit is contained in:
Adrian Woźniak 2023-08-22 16:25:40 +02:00
parent 5e806454f9
commit 4edccc7661
6 changed files with 132 additions and 6 deletions

View File

@ -76,6 +76,18 @@ impl MigrationTrait for Migration {
) )
.await?; .await?;
m.create_index(
IndexCreateStatement::default()
.unique()
.name("uniq-location")
.table(ParkingSpaceLocation::ParkingSpaceLocations)
.col(ParkingSpaceLocation::Name)
.col(ParkingSpaceLocation::Number)
.col(ParkingSpaceLocation::Stage)
.to_owned(),
)
.await?;
Ok(()) Ok(())
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 KiB

View File

@ -3,16 +3,23 @@ use actix_web::web::{scope, Data, Form, ServiceConfig};
use actix_web::{get, post, HttpRequest, HttpResponse}; use actix_web::{get, post, HttpRequest, HttpResponse};
use askama_actix::Template; use askama_actix::Template;
use autometrics::autometrics; use autometrics::autometrics;
use oswilno_contract::accounts;
use oswilno_contract::parking_space_rents; use oswilno_contract::parking_space_rents;
use oswilno_contract::parking_spaces; use oswilno_contract::parking_spaces;
use oswilno_contract::{accounts, parking_space_locations};
use oswilno_session::{Authenticated, MaybeAuthenticated}; use oswilno_session::{Authenticated, MaybeAuthenticated};
use oswilno_view::{is_partial, Blank, Layout, Main, MainOpts, SearchOpts, SessionOpts}; use oswilno_view::{
filters, is_partial, Blank, Errors, Lang, Layout, Main, MainOpts, SearchOpts, SessionOpts,
TranslationStorage,
};
use sea_orm::prelude::*; use sea_orm::prelude::*;
use sea_orm::ActiveValue::{NotSet, Set}; use sea_orm::ActiveValue::{NotSet, Set};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
pub async fn init(db: Arc<sea_orm::DatabaseConnection>) {
ensure_locations(db).await;
}
pub fn mount(config: &mut ServiceConfig) { pub fn mount(config: &mut ServiceConfig) {
config.service(root).service( config.service(root).service(
scope("/parking-spaces") scope("/parking-spaces")
@ -36,6 +43,7 @@ struct AllPartialParkingSpace {
parking_space_rents: Vec<parking_space_rents::Model>, parking_space_rents: Vec<parking_space_rents::Model>,
parking_space_by_id: HashMap<i32, parking_spaces::Model>, parking_space_by_id: HashMap<i32, parking_spaces::Model>,
account_by_id: HashMap<i32, accounts::Model>, account_by_id: HashMap<i32, accounts::Model>,
locations: Vec<parking_space_locations::Model>,
session: Option<SessionOpts>, session: Option<SessionOpts>,
} }
@ -114,19 +122,45 @@ async fn load_parking_spaces(db: Arc<DatabaseConnection>) -> AllPartialParkingSp
}) })
}; };
let locations = load_locations(db).await;
AllPartialParkingSpace { AllPartialParkingSpace {
account_by_id, account_by_id,
parking_space_rents: rents, parking_space_rents: rents,
parking_space_by_id, parking_space_by_id,
locations,
session: None, session: None,
} }
} }
async fn load_locations(db: Arc<DatabaseConnection>) -> Vec<parking_space_locations::Model> {
use oswilno_contract::parking_space_locations::*;
let mut v = Entity::find().all(&*db).await.unwrap();
v.sort_by(|l, r| {
l.name
.cmp(&r.name)
.cmp(&l.number.cmp(&r.number))
.cmp(&l.stage.cmp(&r.stage))
});
v
}
#[get("/form")] #[get("/form")]
async fn form_show(req: HttpRequest, session: Authenticated) -> HttpResponse { async fn form_show(
req: HttpRequest,
session: Authenticated,
t: Data<TranslationStorage>,
lang: Lang,
db: Data<DatabaseConnection>,
) -> HttpResponse {
let db = db.into_inner();
let session = session.into(); let session = session.into();
let body = ParkingSpaceFormPartial { let body = ParkingSpaceFormPartial {
..Default::default() form: Default::default(),
locations: load_locations(db.clone()).await,
lang,
errors: Default::default(),
t: t.into_inner(),
}; };
let main = Main { let main = Main {
body, body,
@ -147,10 +181,14 @@ async fn form_show(req: HttpRequest, session: Authenticated) -> HttpResponse {
.body(html.unwrap()) .body(html.unwrap())
} }
#[derive(Debug, Default, Template)] #[derive(Debug, Template)]
#[template(path = "../templates/parking-spaces/form-partial.html")] #[template(path = "../templates/parking-spaces/form-partial.html")]
struct ParkingSpaceFormPartial { struct ParkingSpaceFormPartial {
form: CreateParkingSpace, form: CreateParkingSpace,
lang: Lang,
errors: Errors,
t: Arc<TranslationStorage>,
locations: Vec<parking_space_locations::Model>,
} }
#[derive(Debug, Default, serde::Deserialize)] #[derive(Debug, Default, serde::Deserialize)]
@ -165,6 +203,8 @@ async fn create(
db: Data<sea_orm::DatabaseConnection>, db: Data<sea_orm::DatabaseConnection>,
p: Form<CreateParkingSpace>, p: Form<CreateParkingSpace>,
session: Authenticated, session: Authenticated,
t: Data<TranslationStorage>,
lang: Lang,
) -> HttpResponse { ) -> HttpResponse {
use oswilno_contract::parking_spaces::*; use oswilno_contract::parking_spaces::*;
let CreateParkingSpace { location_id, spot } = p.into_inner(); let CreateParkingSpace { location_id, spot } = p.into_inner();
@ -181,6 +221,10 @@ async fn create(
return HttpResponse::BadRequest().body( return HttpResponse::BadRequest().body(
ParkingSpaceFormPartial { ParkingSpaceFormPartial {
form: CreateParkingSpace { location_id, spot }, form: CreateParkingSpace { location_id, spot },
t: t.into_inner(),
lang,
errors: Default::default(),
locations: load_locations(db.clone()).await,
} }
.render() .render()
.unwrap(), .unwrap(),
@ -189,3 +233,39 @@ async fn create(
unreachable!() unreachable!()
} }
async fn ensure_locations(db: Arc<sea_orm::DatabaseConnection>) {
for (name, number, stage) in [
("Wilno", 1, "1a"),
("Wilno", 1, "1b"),
("Wilno", 1, "2AF1"),
("Wilno", 1, "2AF2"),
("Wilno", 1, "3"),
("Wilno", 1, "4"),
("Wilno", 2, "1"),
("Wilno", 2, "2"),
("Wilno", 2, "3"),
("Wilno", 3, "1"),
("Wilno", 3, "2"),
("Wilno", 3, "3"),
("Wilno", 3, "4"),
("Wilno", 3, "5"),
// 4
("Wilno", 4, "1"),
("Wilno", 4, "2"),
("Wilno", 4, "3"),
// 5
("Wilno", 5, "1"),
("Wilno", 5, "2"),
] {
let _model = oswilno_contract::parking_space_locations::ActiveModel {
name: Set(name.to_string()),
number: Set(number),
stage: Set(stage.to_string()),
..Default::default()
}
.save(&*db)
.await
.ok();
}
}

View File

@ -0,0 +1,31 @@
<section id="main-view" class="min-h-screen flex items-center justify-center">
<section class="max-w-md w-full p-6 bg-white rounded-lg shadow-lg">
{% for error in errors.global() %}
<oswilno-error>{{error|t(lang,t)}}</oswilno-error>
{% endfor %}
<form hx-post="/login" hx-target="#main-view">
<div class="mb-4">
<label for="location_id" class="block mb-2 text-sm text-gray-600">location_id</label>
<select id="location_id" name="location_id" value="{{ form.location_id }}" class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500">
{% for loc in locations %}
<option value="{{loc.id}}">
{{ loc.name }}
{{ loc.number }}
{{ loc.stage }}
</option>
{% endfor %}
</select>
{% for error in errors.field("login") %}
<oswilno-error>{{error|t(lang,t)}}</oswilno-error>
{% endfor %}
</div>
<div class="mb-6">
<input
type="submit"
value="{{"Submit"|t(lang,t)}}"
class="w-32 bg-cyan-600 text-white py-2 rounded-lg mx-auto block focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-cyan-500 mb-2"
/>
</div>
</form>
</section>
</section>

View File

@ -1,3 +1,4 @@
use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use actix_web::web::Data; use actix_web::web::Data;
@ -48,6 +49,8 @@ async fn main() -> std::io::Result<()> {
let session_config = oswilno_session::SessionConfigurator::new(redis.clone()); let session_config = oswilno_session::SessionConfigurator::new(redis.clone());
session_config.translations(&mut l10n); session_config.translations(&mut l10n);
oswilno_parking_space::init(Arc::new(conn.clone())).await;
HttpServer::new(move || { HttpServer::new(move || {
let session_config = session_config.clone(); let session_config = session_config.clone();
let session_factory = session_config.factory(); let session_factory = session_config.factory();