old flash in new prop form, prop edit form
This commit is contained in:
@@ -5,5 +5,6 @@
|
||||
{%- endif -%}
|
||||
{%- if prop.optional %}, OPTIONAL{% endif %}
|
||||
{%- if prop.multiple %}, MULTIPLE{% endif %}
|
||||
<a href="/model/property/update/{{prop.id}}">Edit property</a> ·
|
||||
<a href="/model/property/delete/{{prop.id}}" onclick="return confirm('Delete property?')">Delete property</a>
|
||||
{% endmacro input %}
|
||||
|
||||
@@ -19,25 +19,25 @@ Define property
|
||||
<input type="hidden" name="object" value="{{object.id}}">
|
||||
|
||||
<label for="name">Name:</label>
|
||||
<input type="text" id="name" name="name" autocomplete="off"><br>
|
||||
<input type="text" id="name" name="name" value="{{old.name}}" autocomplete="off"><br>
|
||||
|
||||
<label for="optional">Optional:</label>
|
||||
<input type="checkbox" name="optional" id="optional" value="true" autocomplete="off">
|
||||
<input type="checkbox" name="optional" id="optional" value="true" {{opt(checked=old.optional)}} autocomplete="off">
|
||||
<br>
|
||||
|
||||
<label for="multiple">Multiple:</label>
|
||||
<input type="checkbox" name="multiple" id="multiple" value="true" autocomplete="off"><br>
|
||||
<input type="checkbox" name="multiple" id="multiple" value="true" {{opt(checked=old.multiple)}} autocomplete="off"><br>
|
||||
|
||||
<label for="data_type">Type:</label>
|
||||
<select name="data_type" id="data_type" autocomplete="off">
|
||||
<option value="String" selected>String</option>
|
||||
<option value="Integer">Integer</option>
|
||||
<option value="Decimal">Decimal</option>
|
||||
<option value="Boolean">Boolean</option>
|
||||
<option value="String" {{selected(opt="String",val=old.data_type)}}>String</option>
|
||||
<option value="Integer" {{selected(opt="Integer",val=old.data_type)}}>Integer</option>
|
||||
<option value="Decimal" {{selected(opt="Decimal",val=old.data_type)}}>Decimal</option>
|
||||
<option value="Boolean" {{selected(opt="Boolean",val=old.data_type)}}>Boolean</option>
|
||||
</select><br>
|
||||
|
||||
<label for="default">Default:</label>
|
||||
<input type="text" id="default" name="default" autocomplete="off"><br>
|
||||
<input type="text" id="default" name="default" value="{{old.default}}" autocomplete="off"><br>
|
||||
|
||||
<input type="submit" value="Save">
|
||||
</form>
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
{% extends "_layout" %}
|
||||
|
||||
{% block title -%}
|
||||
Edit property
|
||||
{%- endblock %}
|
||||
|
||||
{% block nav -%}
|
||||
<a href="/">Home</a>
|
||||
{%- endblock %}
|
||||
|
||||
{% block content -%}
|
||||
|
||||
<h1>Edit property {{model.name}}</h1>
|
||||
|
||||
<form action="/model/property/update/{{model.id}}" method="POST">
|
||||
<label for="name">Name:</label>
|
||||
<input type="text" id="name" name="name" value="{{model.name}}" autocomplete="off"><br>
|
||||
|
||||
<label for="optional">Optional:</label>
|
||||
<input type="checkbox" name="optional" id="optional" value="true" {{opt(checked=model.optional)}} autocomplete="off">
|
||||
<br>
|
||||
|
||||
<label for="multiple">Multiple:</label>
|
||||
<input type="checkbox" name="multiple" id="multiple" value="true" {{opt(checked=model.multiple)}} autocomplete="off"><br>
|
||||
|
||||
<label for="data_type">Type:</label>
|
||||
<select name="data_type" id="data_type" autocomplete="off">
|
||||
<option value="String" {{selected(val=model.data_type, opt="String")}}>String</option>
|
||||
<option value="Integer" {{selected(val=model.data_type, opt="Integer")}}>Integer</option>
|
||||
<option value="Decimal" {{selected(val=model.data_type, opt="Decimal")}}>Decimal</option>
|
||||
<option value="Boolean" {{selected(val=model.data_type, opt="Boolean")}}>Boolean</option>
|
||||
</select><br>
|
||||
|
||||
<label for="default">Default:</label>
|
||||
<input type="text" id="default" name="default" value="{{model.default | print_typed_value}}" autocomplete="off"><br>
|
||||
|
||||
<input type="submit" value="Save">
|
||||
</form>
|
||||
|
||||
{%- endblock %}
|
||||
+21
-4
@@ -56,6 +56,7 @@ pub(crate) static TERA: Lazy<Tera> = Lazy::new(|| {
|
||||
Err(tera::Error::msg("Expected nonenmpty object"))
|
||||
});
|
||||
|
||||
// opt(checked=foo.is_checked)
|
||||
tera.register_function("opt", |args: &HashMap<String, Value>| -> tera::Result<Value> {
|
||||
if args.len() != 1 {
|
||||
return Err("Expected 1 argument".into());
|
||||
@@ -71,6 +72,20 @@ pub(crate) static TERA: Lazy<Tera> = Lazy::new(|| {
|
||||
}
|
||||
});
|
||||
|
||||
// selected(val=foo.color,opt="Red")
|
||||
tera.register_function("selected", |args: &HashMap<String, Value>| -> tera::Result<Value> {
|
||||
match (args.get("val"), args.get("opt")) {
|
||||
(Some(v), Some(w)) => {
|
||||
if v == w {
|
||||
Ok(Value::String("selected".into()))
|
||||
} else {
|
||||
Ok(Value::Null)
|
||||
}
|
||||
},
|
||||
_ => Err("Expected val and opt args".into()),
|
||||
}
|
||||
});
|
||||
|
||||
// TODO need to inject HttpRequest::url_for() into tera context, but it then can't be accessed by the functions.
|
||||
// tera.register_function("url_for", |args: HashMap<String, Value>| -> tera::Result<Value> {
|
||||
// match args.get("name") {
|
||||
@@ -131,6 +146,8 @@ async fn main() -> std::io::Result<()> {
|
||||
//
|
||||
.service(routes::property_model::create_form)
|
||||
.service(routes::property_model::create)
|
||||
.service(routes::property_model::update_form)
|
||||
.service(routes::property_model::update)
|
||||
.service(routes::property_model::delete)
|
||||
.service(static_files)
|
||||
.default_service(web::to(|| HttpResponse::NotFound().body("File or endpoint not found")))
|
||||
@@ -169,7 +186,7 @@ fn init_yopa() -> YopaStoreWrapper {
|
||||
optional: false,
|
||||
multiple: true,
|
||||
data_type: DataType::String,
|
||||
default: None,
|
||||
default: TypedValue::String("".into()),
|
||||
}).unwrap();
|
||||
|
||||
store.define_property(model::PropertyModel {
|
||||
@@ -179,7 +196,7 @@ fn init_yopa() -> YopaStoreWrapper {
|
||||
optional: false,
|
||||
multiple: false,
|
||||
data_type: DataType::String,
|
||||
default: None,
|
||||
default: TypedValue::String("".into()),
|
||||
}).unwrap();
|
||||
|
||||
store.define_property(model::PropertyModel {
|
||||
@@ -189,7 +206,7 @@ fn init_yopa() -> YopaStoreWrapper {
|
||||
optional: true,
|
||||
multiple: true,
|
||||
data_type: DataType::String,
|
||||
default: Some(TypedValue::String("Pepa Novák".into())),
|
||||
default: TypedValue::String("Pepa Novák".into()),
|
||||
}).unwrap();
|
||||
|
||||
let rel_book_id = store.define_relation(model::RelationModel {
|
||||
@@ -209,7 +226,7 @@ fn init_yopa() -> YopaStoreWrapper {
|
||||
optional: true,
|
||||
multiple: false,
|
||||
data_type: DataType::Integer,
|
||||
default: None,
|
||||
default: TypedValue::Integer(0),
|
||||
}).unwrap();
|
||||
|
||||
store.define_relation(model::RelationModel {
|
||||
|
||||
@@ -58,7 +58,7 @@ pub(crate) async fn create(
|
||||
redirect("/")
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error creating model: {:?}", e);
|
||||
warn!("Error creating model: {}", e);
|
||||
session.flash_error(e.to_string());
|
||||
session.set("old", form);
|
||||
redirect("/model/object/create")
|
||||
@@ -112,7 +112,7 @@ pub(crate) async fn update(
|
||||
redirect("/")
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error updating model: {:?}", e);
|
||||
warn!("Error updating model: {}", e);
|
||||
session.flash_error(e.to_string());
|
||||
session.set("old", form);
|
||||
redirect(format!("/model/object/update/{}", id))
|
||||
@@ -135,7 +135,7 @@ pub(crate) async fn delete(
|
||||
redirect("/")
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error deleting object model: {:?}", e);
|
||||
warn!("Error deleting object model: {}", e);
|
||||
session.flash_error(e.to_string());
|
||||
redirect("/") // back?
|
||||
}
|
||||
|
||||
@@ -22,6 +22,20 @@ pub(crate) async fn create_form(
|
||||
|
||||
let rg = store.read().await;
|
||||
|
||||
// Re-fill old values
|
||||
if let Ok(Some(form)) = session.take::<PropertyModelCreateForm>("old") {
|
||||
context.insert("old", &form);
|
||||
} else {
|
||||
context.insert("old", &PropertyModelCreateForm {
|
||||
object: Default::default(),
|
||||
name: "".to_string(),
|
||||
optional: false,
|
||||
multiple: false,
|
||||
data_type: DataType::String,
|
||||
default: "".to_string()
|
||||
});
|
||||
}
|
||||
|
||||
debug!("ID = {}", object_id);
|
||||
|
||||
let object = {
|
||||
@@ -46,8 +60,8 @@ pub(crate) async fn create_form(
|
||||
TERA.build_response("property_create", &context)
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub(crate) struct PropertyModelCreate {
|
||||
#[derive(Serialize,Deserialize)]
|
||||
pub(crate) struct PropertyModelCreateForm {
|
||||
pub object: ID,
|
||||
pub name: String,
|
||||
#[serde(default)]
|
||||
@@ -60,9 +74,49 @@ pub(crate) struct PropertyModelCreate {
|
||||
pub default: String,
|
||||
}
|
||||
|
||||
fn parse_default(data_type : DataType, default : String) -> Result<TypedValue, String> {
|
||||
Ok(match data_type {
|
||||
DataType::String => {
|
||||
TypedValue::String(default.into())
|
||||
}
|
||||
DataType::Integer => {
|
||||
if default.is_empty() {
|
||||
TypedValue::Integer(0)
|
||||
} else {
|
||||
// TODO better error reporting
|
||||
TypedValue::Integer(default.parse()
|
||||
.map_err(|_| {
|
||||
format!("Error parsing \"{}\" as integer", default)
|
||||
})?)
|
||||
}
|
||||
}
|
||||
DataType::Decimal => {
|
||||
if default.is_empty() {
|
||||
TypedValue::Decimal(0.0)
|
||||
} else {
|
||||
// TODO better error reporting
|
||||
TypedValue::Decimal(default.parse()
|
||||
.map_err(|_| {
|
||||
format!("Error parsing \"{}\" as decimal", default)
|
||||
})?)
|
||||
}
|
||||
}
|
||||
DataType::Boolean => {
|
||||
if default.is_empty() {
|
||||
TypedValue::Boolean(false)
|
||||
} else {
|
||||
TypedValue::String(default.clone().into())
|
||||
.cast_to(DataType::Boolean).map_err(|_| {
|
||||
format!("Error parsing \"{}\" as boolean", default)
|
||||
})?
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[post("/model/property/create")]
|
||||
pub(crate) async fn create(
|
||||
form: web::Form<PropertyModelCreate>,
|
||||
form: web::Form<PropertyModelCreateForm>,
|
||||
store: crate::YopaStoreWrapper,
|
||||
session: Session,
|
||||
) -> actix_web::Result<impl Responder> {
|
||||
@@ -72,6 +126,16 @@ pub(crate) async fn create(
|
||||
let optional = form.optional;
|
||||
let multiple = form.multiple;
|
||||
|
||||
let default = match parse_default(form.data_type, form.default.clone()) {
|
||||
Ok(def) => def,
|
||||
Err(msg) => {
|
||||
warn!("{}", msg);
|
||||
session.flash_error(msg);
|
||||
session.set("old", &form);
|
||||
return redirect(format!("/model/property/create/{}", form.object));
|
||||
}
|
||||
};
|
||||
|
||||
match wg.define_property(PropertyModel {
|
||||
id: Default::default(),
|
||||
object: form.object,
|
||||
@@ -79,61 +143,7 @@ pub(crate) async fn create(
|
||||
optional,
|
||||
multiple,
|
||||
data_type: form.data_type,
|
||||
default: {
|
||||
match form.data_type {
|
||||
DataType::String => {
|
||||
if form.default.is_empty() && optional {
|
||||
None
|
||||
} else {
|
||||
Some(TypedValue::String(form.default.into()))
|
||||
}
|
||||
}
|
||||
DataType::Integer => {
|
||||
if form.default.is_empty() {
|
||||
if optional {
|
||||
None
|
||||
} else {
|
||||
Some(TypedValue::Integer(0))
|
||||
}
|
||||
} else {
|
||||
// TODO better error reporting
|
||||
Some(TypedValue::Integer(form.default.parse()
|
||||
.map_err(|_| {
|
||||
actix_web::error::ErrorBadRequest(format!("Error parsing \"{}\" as integer", form.default))
|
||||
})?))
|
||||
}
|
||||
}
|
||||
DataType::Decimal => {
|
||||
if form.default.is_empty() {
|
||||
if optional {
|
||||
None
|
||||
} else {
|
||||
Some(TypedValue::Decimal(0.0))
|
||||
}
|
||||
} else {
|
||||
// TODO better error reporting
|
||||
Some(TypedValue::Decimal(form.default.parse()
|
||||
.map_err(|_| {
|
||||
actix_web::error::ErrorBadRequest(format!("Error parsing \"{}\" as decimal", form.default))
|
||||
})?))
|
||||
}
|
||||
}
|
||||
DataType::Boolean => {
|
||||
if form.default.is_empty() {
|
||||
if optional {
|
||||
None
|
||||
} else {
|
||||
Some(TypedValue::Boolean(false))
|
||||
}
|
||||
} else {
|
||||
Some(TypedValue::String(form.default.clone().into())
|
||||
.cast_to(DataType::Boolean).map_err(|_| {
|
||||
actix_web::error::ErrorBadRequest(format!("Error parsing \"{}\" as boolean", form.default))
|
||||
})?)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
default,
|
||||
}) {
|
||||
Ok(_id) => {
|
||||
debug!("Property created, redirecting to root");
|
||||
@@ -141,8 +151,9 @@ pub(crate) async fn create(
|
||||
redirect("/")
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error creating property model: {:?}", e);
|
||||
warn!("Error creating property model: {}", e);
|
||||
session.flash_error(e.to_string());
|
||||
session.set("old", &form);
|
||||
redirect(format!("/model/property/create/{}", form.object))
|
||||
}
|
||||
}
|
||||
@@ -162,9 +173,96 @@ pub(crate) async fn delete(
|
||||
redirect("/")
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error deleting property: {:?}", e);
|
||||
warn!("Error deleting property: {}", e);
|
||||
session.flash_error(e.to_string());
|
||||
redirect("/") // back?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub(crate) struct PropertyModelEditForm {
|
||||
pub name: String,
|
||||
#[serde(default)]
|
||||
pub optional: bool,
|
||||
#[serde(default)]
|
||||
pub multiple: bool,
|
||||
pub data_type: DataType,
|
||||
/// Default value to be parsed to the data type
|
||||
/// May be unused if empty and optional
|
||||
pub default: String,
|
||||
}
|
||||
|
||||
|
||||
#[get("/model/property/update/{model_id}")]
|
||||
pub(crate) async fn update_form(
|
||||
model_id: web::Path<ID>,
|
||||
store: crate::YopaStoreWrapper,
|
||||
session: Session,
|
||||
) -> actix_web::Result<impl Responder> {
|
||||
let mut context = tera::Context::new();
|
||||
session.render_flash(&mut context);
|
||||
let rg = store.read().await;
|
||||
|
||||
let model = rg.get_property_model(*model_id)
|
||||
.ok_or_else(|| actix_web::error::ErrorNotFound("No such model"))?;
|
||||
|
||||
// Re-fill old values
|
||||
if let Ok(Some(form)) = session.take::<PropertyModelEditForm>("old") {
|
||||
let mut model = model.clone();
|
||||
model.name = form.name;
|
||||
model.data_type = form.data_type;
|
||||
model.default = TypedValue::String(form.default.into());
|
||||
model.optional = form.optional;
|
||||
model.multiple = form.multiple;
|
||||
context.insert("model", &model);
|
||||
} else {
|
||||
context.insert("model", model);
|
||||
}
|
||||
|
||||
TERA.build_response("property_update", &context)
|
||||
}
|
||||
|
||||
#[post("/model/property/update/{model_id}")]
|
||||
pub(crate) async fn update(
|
||||
model_id: web::Path<ID>,
|
||||
form: web::Form<PropertyModelEditForm>,
|
||||
store: crate::YopaStoreWrapper,
|
||||
session: Session,
|
||||
) -> actix_web::Result<impl Responder> {
|
||||
let mut wg = store.write().await;
|
||||
let form = form.into_inner();
|
||||
|
||||
let id = model_id.into_inner();
|
||||
let default = match parse_default(form.data_type, form.default.clone()) {
|
||||
Ok(def) => def,
|
||||
Err(msg) => {
|
||||
warn!("{}", msg);
|
||||
session.flash_error(msg);
|
||||
session.set("old", form);
|
||||
return redirect(format!("/model/property/update/{}", id));
|
||||
}
|
||||
};
|
||||
|
||||
match wg.update_property(PropertyModel {
|
||||
id,
|
||||
object: Default::default(), // dummy
|
||||
name: form.name.clone(),
|
||||
optional: form.optional,
|
||||
multiple: form.multiple,
|
||||
data_type: form.data_type,
|
||||
default,
|
||||
}) {
|
||||
Ok(_id) => {
|
||||
debug!("Relation updated, redirecting to root");
|
||||
session.flash_success(format!("Property \"{}\" updated.", form.name));
|
||||
redirect("/")
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error updating model: {}", e);
|
||||
session.flash_error(e.to_string());
|
||||
session.set("old", form);
|
||||
redirect(format!("/model/relation/update/{}", id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ pub(crate) async fn create(
|
||||
redirect("/")
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error creating relation model: {:?}", e);
|
||||
warn!("Error creating relation model: {}", e);
|
||||
session.flash_error(e.to_string());
|
||||
redirect(format!("/model/relation/create/{}", form.object))
|
||||
}
|
||||
@@ -105,7 +105,7 @@ pub(crate) async fn delete(
|
||||
redirect("/")
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error deleting relation model: {:?}", e);
|
||||
warn!("Error deleting relation model: {}", e);
|
||||
session.flash_error(e.to_string());
|
||||
redirect("/") // back?
|
||||
}
|
||||
@@ -175,7 +175,7 @@ pub(crate) async fn update(
|
||||
redirect("/")
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error updating model: {:?}", e);
|
||||
warn!("Error updating model: {}", e);
|
||||
session.flash_error(e.to_string());
|
||||
session.set("old", form);
|
||||
redirect(format!("/model/relation/update/{}", id))
|
||||
|
||||
Reference in New Issue
Block a user