add sort keys to things, add options object for "multiline"
This commit is contained in:
@@ -28,6 +28,7 @@ anyhow = "1.0.38"
|
||||
thiserror = "1.0.24"
|
||||
clap = "2"
|
||||
serde_with = "1.6.4"
|
||||
indexmap = { version = "1.6.1", features = ["serde-1"] }
|
||||
|
||||
tokio = { version="0.2.6", features=["full"] }
|
||||
|
||||
|
||||
@@ -14,9 +14,6 @@ export default {
|
||||
let properties = this.schema.prop_models.filter((m) => m.object === model.id);
|
||||
let relations = this.schema.rel_models.filter((m) => m.object === model.id);
|
||||
|
||||
properties.sort((a, b) => a.name.localeCompare(b.name));
|
||||
relations.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
let values = {};
|
||||
properties.forEach((p) => {
|
||||
let existing = object.values[p.id] || [];
|
||||
@@ -38,8 +35,8 @@ export default {
|
||||
}
|
||||
});
|
||||
|
||||
properties = keyBy(properties, 'id');
|
||||
relations = keyBy(relations, 'id');
|
||||
let propertiesById = keyBy(properties, 'id');
|
||||
let relationsById = keyBy(relations, 'id');
|
||||
|
||||
let model_names = {};
|
||||
this.schema.obj_models.forEach((m) => {
|
||||
@@ -49,7 +46,9 @@ export default {
|
||||
return {
|
||||
model,
|
||||
properties,
|
||||
propertiesById,
|
||||
relations,
|
||||
relationsById,
|
||||
haveRelations: !isEmpty(relations),
|
||||
model_names,
|
||||
values,
|
||||
@@ -67,7 +66,7 @@ export default {
|
||||
let values = [];
|
||||
forEach(objCopy(this.values), (vv, prop_model_id) => {
|
||||
for (let v of vv) {
|
||||
if (isEqual(v.value, {"String": ""}) && this.properties[prop_model_id].optional) {
|
||||
if (isEqual(v.value, {"String": ""}) && this.propertiesById[prop_model_id].optional) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -148,7 +147,7 @@ export default {
|
||||
</div>
|
||||
|
||||
<div class="form-horizontal container">
|
||||
<edit-property v-for="(property, pi) in properties" :model="property" :values="values[property.id]" :key="pi"></edit-property>
|
||||
<edit-property v-for="property in properties" :model="property" :values="values[property.id]" :key="property.id"></edit-property>
|
||||
</div>
|
||||
|
||||
<div v-if="haveRelations">
|
||||
|
||||
@@ -12,12 +12,9 @@ export default {
|
||||
|
||||
let properties = this.schema.prop_models.filter((m) => m.object === model.id);
|
||||
|
||||
properties.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
if (isEmpty(properties)) {
|
||||
properties = [];
|
||||
} else {
|
||||
properties = keyBy(properties, 'id');
|
||||
let propertiesById = {};
|
||||
if (!isEmpty(properties)) {
|
||||
propertiesById = keyBy(properties, 'id');
|
||||
}
|
||||
|
||||
let related_model = this.schema.obj_models.find((m) => m.id === model.related);
|
||||
@@ -35,6 +32,7 @@ export default {
|
||||
model,
|
||||
related_model,
|
||||
properties,
|
||||
propertiesById,
|
||||
object_names: choices,
|
||||
instances: objCopy(this.initialInstances),
|
||||
}
|
||||
@@ -56,7 +54,7 @@ export default {
|
||||
let values = [];
|
||||
forEach(instance.values, (vv, prop_model_id) => {
|
||||
for (let v of vv) {
|
||||
if (isEqual(v.value, {"String": ""}) && this.properties[prop_model_id].optional) {
|
||||
if (isEqual(v.value, {"String": ""}) && this.propertiesById[prop_model_id].optional) {
|
||||
continue;
|
||||
}
|
||||
v.model = castId(prop_model_id);
|
||||
@@ -68,7 +66,6 @@ export default {
|
||||
instance.values = values;
|
||||
relations.push(instance);
|
||||
})
|
||||
console.log('collected', relations);
|
||||
return relations;
|
||||
},
|
||||
|
||||
@@ -112,9 +109,9 @@ export default {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<edit-property v-for="(property, id) in properties"
|
||||
<edit-property v-for="property in properties"
|
||||
:model="property"
|
||||
:values="instance.values[id]" :key="id"></edit-property>
|
||||
:values="instance.values[property.id]" :key="property.id"></edit-property>
|
||||
</div>
|
||||
|
||||
<div class="mt-2 mb-2">
|
||||
|
||||
@@ -12,9 +12,6 @@ export default {
|
||||
let properties = this.schema.prop_models.filter((m) => m.object === model.id);
|
||||
let relations = this.schema.rel_models.filter((m) => m.object === model.id);
|
||||
|
||||
properties.sort((a, b) => a.name.localeCompare(b.name));
|
||||
relations.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
let values = {};
|
||||
properties.forEach((p) => {
|
||||
if (p.optional) {
|
||||
@@ -24,8 +21,8 @@ export default {
|
||||
}
|
||||
});
|
||||
|
||||
properties = keyBy(properties, 'id');
|
||||
relations = keyBy(relations, 'id');
|
||||
let propertiesById = keyBy(properties, 'id');
|
||||
let relationsById = keyBy(relations, 'id');
|
||||
|
||||
let model_names = {};
|
||||
this.schema.obj_models.forEach((m) => {
|
||||
@@ -36,6 +33,8 @@ export default {
|
||||
model,
|
||||
properties,
|
||||
relations,
|
||||
propertiesById,
|
||||
relationsById,
|
||||
haveRelations: !isEmpty(relations),
|
||||
model_names,
|
||||
values,
|
||||
|
||||
@@ -12,12 +12,9 @@ export default {
|
||||
|
||||
let properties = this.schema.prop_models.filter((m) => m.object === model.id);
|
||||
|
||||
properties.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
if (isEmpty(properties)) {
|
||||
properties = [];
|
||||
} else {
|
||||
properties = keyBy(properties, 'id');
|
||||
let propertiesById = {};
|
||||
if (!isEmpty(properties)) {
|
||||
propertiesById = keyBy(properties, 'id');
|
||||
}
|
||||
|
||||
let related_model = this.schema.obj_models.find((m) => m.id === model.related);
|
||||
@@ -52,6 +49,7 @@ export default {
|
||||
model,
|
||||
related_model,
|
||||
properties,
|
||||
propertiesById,
|
||||
object_names: choices,
|
||||
instances,
|
||||
}
|
||||
@@ -71,7 +69,7 @@ export default {
|
||||
forEach(instance.values, (vv, prop_model_id) => {
|
||||
|
||||
for (let v of vv) {
|
||||
if (isEqual(v, {"String": ""}) && this.properties[prop_model_id].optional) {
|
||||
if (isEqual(v, {"String": ""}) && this.propertiesById[prop_model_id].optional) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -131,9 +129,9 @@ export default {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<property v-for="(property, id) in properties"
|
||||
<property v-for="property in properties"
|
||||
:model="property"
|
||||
:values="instance.values[id]" :key="id"></property>
|
||||
:values="instance.values[property.id]" :key="property.id"></property>
|
||||
</div>
|
||||
|
||||
<div class="mt-2 mb-2">
|
||||
|
||||
@@ -6,6 +6,7 @@ import StringValue from "./components/value/StringValue.vue";
|
||||
import DecimalValue from "./components/value/DecimalValue.vue";
|
||||
import BooleanValue from "./components/value/BooleanValue.vue";
|
||||
import IntegerValue from "./components/value/IntegerValue.vue";
|
||||
import TextValue from "./components/value/TextValue.vue";
|
||||
import PropertyField from "./components/PropertyField.vue";
|
||||
|
||||
import NewObjectForm from "./components/NewObjectForm.vue";
|
||||
@@ -14,9 +15,11 @@ import NewRelationForm from "./components/NewRelationForm.vue"
|
||||
import EditObjectForm from "./components/EditObjectForm.vue";
|
||||
import EditRelationForm from "./components/EditRelationForm.vue";
|
||||
import EditPropertyField from "./components/EditPropertyField.vue";
|
||||
import {qs} from "./utils";
|
||||
|
||||
function registerComponents(app) {
|
||||
app.component('string-value', StringValue);
|
||||
app.component('text-value', StringValue);
|
||||
app.component('integer-value', IntegerValue);
|
||||
app.component('decimal-value', DecimalValue);
|
||||
app.component('boolean-value', BooleanValue);
|
||||
@@ -48,6 +51,27 @@ window.Yopa = {
|
||||
|
||||
// ...
|
||||
return instance;
|
||||
},
|
||||
propertyEditForm() {
|
||||
// multiple and unique are XORed. This is also enforced server-side
|
||||
let multiple = qs('#multiple');
|
||||
let unique = qs('#unique');
|
||||
let type = qs('#data_type');
|
||||
unique.addEventListener('input', function () {
|
||||
multiple.checked &= !unique.checked;
|
||||
})
|
||||
multiple.addEventListener('input', function () {
|
||||
unique.checked &= !multiple.checked;
|
||||
})
|
||||
|
||||
type.addEventListener('input', function () {
|
||||
console.log(type.value);
|
||||
toggleOptionalBoxes();
|
||||
})
|
||||
function toggleOptionalBoxes() {
|
||||
qs('#string-options').classList.toggle('hidden', type.value !== 'String');
|
||||
}
|
||||
toggleOptionalBoxes();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -17,3 +17,7 @@ table.object-display {
|
||||
width: 225px;
|
||||
}
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@@ -43,3 +43,6 @@ export function isEmpty(object) {
|
||||
|
||||
return lodash_isEmpty(object)
|
||||
}
|
||||
|
||||
export function qs(s) { return document.querySelector(s); }
|
||||
export function qss(s) { return document.querySelectorAll(s); }
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -4129,3 +4129,6 @@ table.object-display {
|
||||
margin-bottom: 0.6rem; }
|
||||
table.object-display tbody th {
|
||||
width: 225px; }
|
||||
|
||||
.hidden {
|
||||
display: none !important; }
|
||||
|
||||
@@ -24,6 +24,17 @@
|
||||
</div>
|
||||
{% endmacro input %}
|
||||
|
||||
{% macro integer(name, label, value) %}
|
||||
<div class="form-group cols">
|
||||
<div class="col-3 pl-2">
|
||||
<label class="form-label" for="{{name}}">{{label}}</label>
|
||||
</div>
|
||||
<div class="col-9 pr-2">
|
||||
<input type="number" step="1" class="form-input input-inline" id="{{name}}" name="{{name}}" value="{{value}}" autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro input %}
|
||||
|
||||
{% macro checkbox(name, label, checked) %}
|
||||
<div class="form-group cols">
|
||||
<div class="col-3 pl-2">
|
||||
|
||||
@@ -16,6 +16,8 @@ Define object
|
||||
|
||||
<div class="form-horizontal container">
|
||||
{{ form::text(name="name", label="Name", value=old.name) }}
|
||||
|
||||
{{ form::integer(name="sort_key", label="Sort order", value=old.sort_key) }}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ Edit object model
|
||||
<div class="form-horizontal container">
|
||||
{{ form::text(name="name", label="Name", value=model.name) }}
|
||||
|
||||
{{ form::integer(name="sort_key", label="Sort order", value=model.sort_key) }}
|
||||
|
||||
<div class="form-group cols">
|
||||
<div class="col-3 pl-2">
|
||||
<label class="form-label" for="name_property">Name property</label>
|
||||
|
||||
@@ -38,20 +38,18 @@ Define property
|
||||
</div>
|
||||
|
||||
{{ form::text(name="default", label="Default", value=old.default) }}
|
||||
|
||||
{{ form::integer(name="sort_key", label="Sort order", value=old.sort_key) }}
|
||||
|
||||
<div class="hidden" id="string-options">
|
||||
{{ form::checkbox(name="opt_multiline", label="Multi-line", checked=old.opt_multiline) }}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
// multiple and unique are XORed. This is also enforced server-side
|
||||
let multiple = document.getElementById('multiple');
|
||||
let unique = document.getElementById('unique');
|
||||
unique.addEventListener('input', function () {
|
||||
multiple.checked &= !unique.checked;
|
||||
})
|
||||
multiple.addEventListener('input', function () {
|
||||
unique.checked &= !multiple.checked;
|
||||
})
|
||||
Yopa.propertyEditForm()
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
@@ -36,20 +36,18 @@ Edit property
|
||||
</div>
|
||||
|
||||
{{ form::text(name="default", label="Default", value=model.default|print_typed_value) }}
|
||||
|
||||
{{ form::integer(name="sort_key", label="Sort order", value=model.sort_key) }}
|
||||
|
||||
<div id="string-options">
|
||||
{{ form::checkbox(name="opt_multiline", label="Multi-line", checked=model.options.multiline) }}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
// multiple and unique are XORed. This is also enforced server-side
|
||||
let multiple = document.getElementById('multiple');
|
||||
let unique = document.getElementById('unique');
|
||||
unique.addEventListener('input', function () {
|
||||
multiple.checked &= !unique.checked;
|
||||
})
|
||||
multiple.addEventListener('input', function () {
|
||||
unique.checked &= !multiple.checked;
|
||||
})
|
||||
Yopa.propertyEditForm()
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ Define relation
|
||||
{{ form::text(name="reciprocal_name", label="Reciprocal name", value=old.reciprocal_name) }}
|
||||
{{ form::checkbox(name="optional", label="Optional", checked=old.optional) }}
|
||||
{{ form::checkbox(name="multiple", label="Multiple", checked=old.multiple) }}
|
||||
{{ form::integer(name="sort_key", label="Sort order", value=old.sort_key) }}
|
||||
|
||||
<div class="form-group cols">
|
||||
<div class="col-3 pl-2">
|
||||
|
||||
@@ -19,6 +19,7 @@ Edit relation
|
||||
{{ form::text(name="reciprocal_name", label="Reciprocal name", value=model.reciprocal_name) }}
|
||||
{{ form::checkbox(name="optional", label="Optional", checked=model.optional) }}
|
||||
{{ form::checkbox(name="multiple", label="Multiple", checked=model.multiple) }}
|
||||
{{ form::integer(name="sort_key", label="Sort order", value=model.sort_key) }}
|
||||
</div>
|
||||
|
||||
<p>The related object cannot be changed. Create a new relation if needed.</p>
|
||||
|
||||
@@ -4,6 +4,8 @@ extern crate actix_web;
|
||||
extern crate log;
|
||||
#[macro_use]
|
||||
extern crate thiserror;
|
||||
#[macro_use]
|
||||
extern crate serde_json;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Deref;
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::tera_ext::TeraExt;
|
||||
use crate::TERA;
|
||||
use actix_session::Session;
|
||||
use actix_web::Responder;
|
||||
use yopa::model::{PropertyModel, RelationModel, ObjectModel};
|
||||
|
||||
pub(crate) mod object;
|
||||
pub(crate) mod property;
|
||||
@@ -32,7 +33,7 @@ pub(crate) async fn list(
|
||||
.into_iter()
|
||||
.map(|rm| {
|
||||
let mut rprops = model_props.get(&rm.id).cloned().unwrap_or_default();
|
||||
rprops.sort_by_key(|m| &m.name);
|
||||
rprops.sort_by(PropertyModel::order_refs);
|
||||
|
||||
RelationModelDisplay {
|
||||
model: rm,
|
||||
@@ -41,7 +42,7 @@ pub(crate) async fn list(
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
relations.sort_by_key(|d| &d.model.name);
|
||||
relations.sort_by(|a, b| RelationModel::order_refs(&a.model, &b.model));
|
||||
|
||||
// Relations coming INTO this model
|
||||
let reciprocal_relations = model_rec_relations.remove(&om.id).unwrap_or_default();
|
||||
@@ -49,7 +50,7 @@ pub(crate) async fn list(
|
||||
.into_iter()
|
||||
.map(|rm| {
|
||||
let mut rprops = model_props.get(&rm.id).cloned().unwrap_or_default();
|
||||
rprops.sort_by_key(|m| &m.name);
|
||||
rprops.sort_by(PropertyModel::order_refs);
|
||||
|
||||
RelationModelDisplay {
|
||||
model: rm,
|
||||
@@ -58,21 +59,22 @@ pub(crate) async fn list(
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
reciprocal_relations.sort_by_key(|d| &d.model.reciprocal_name);
|
||||
reciprocal_relations.sort_by(|a, b| RelationModel::reciprocal_order_refs(&a.model, &b.model));
|
||||
|
||||
let mut properties = model_props.remove(&om.id).unwrap_or_default();
|
||||
properties.sort_by_key(|m| &m.name);
|
||||
properties.sort_by(PropertyModel::order_refs);
|
||||
|
||||
models.push(ObjectModelDisplay {
|
||||
id: om.id,
|
||||
name: &om.name,
|
||||
model: &om,
|
||||
properties,
|
||||
relations,
|
||||
reciprocal_relations,
|
||||
})
|
||||
}
|
||||
|
||||
models.sort_by_key(|m| m.name);
|
||||
models.sort_by(|a, b| ObjectModel::order_refs(&a.model, &b.model));
|
||||
|
||||
let mut ctx = tera::Context::new();
|
||||
ctx.insert("models", &models);
|
||||
|
||||
@@ -16,6 +16,8 @@ use itertools::Itertools;
|
||||
pub(crate) struct ObjectModelDisplay<'a> {
|
||||
pub(crate) id: yopa::ID,
|
||||
pub(crate) name: &'a str,
|
||||
#[serde(skip)]
|
||||
pub(crate) model : &'a ObjectModel,
|
||||
pub(crate) properties: Vec<&'a PropertyModel>,
|
||||
pub(crate) relations: Vec<RelationModelDisplay<'a>>,
|
||||
pub(crate) reciprocal_relations: Vec<RelationModelDisplay<'a>>,
|
||||
@@ -43,6 +45,8 @@ pub(crate) struct ObjectModelForm {
|
||||
// #[serde(with="serde_with::rust::default_on_error")] // This is because "" can be selected
|
||||
#[serde(with = "my_string_empty_as_none")]
|
||||
pub name_property: Option<ID>,
|
||||
#[serde(default)]
|
||||
pub sort_key: i64,
|
||||
}
|
||||
|
||||
#[post("/model/object/create")]
|
||||
@@ -57,6 +61,7 @@ pub(crate) async fn create(
|
||||
id: Default::default(),
|
||||
name: form.name.clone(),
|
||||
name_property: form.name_property,
|
||||
sort_key: form.sort_key
|
||||
}) {
|
||||
Ok(_id) => {
|
||||
wg.persist().err_to_500()?;
|
||||
@@ -101,6 +106,7 @@ pub(crate) async fn update_form(
|
||||
&ObjectModelForm {
|
||||
name: model.name.to_string(),
|
||||
name_property: model.name_property,
|
||||
sort_key: model.sort_key,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -127,6 +133,7 @@ pub(crate) async fn update(
|
||||
id,
|
||||
name: form.name.clone(),
|
||||
name_property: form.name_property,
|
||||
sort_key: form.sort_key,
|
||||
}) {
|
||||
Ok(_id) => {
|
||||
wg.persist().err_to_500()?;
|
||||
|
||||
@@ -2,7 +2,7 @@ use actix_session::Session;
|
||||
use actix_web::{web, Responder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use yopa::model::PropertyModel;
|
||||
use yopa::model::{PropertyModel, PropertyOptions};
|
||||
use yopa::{DataType, TypedValue, ID};
|
||||
|
||||
use crate::routes::models::relation::ObjectOrRelationModelDisplay;
|
||||
@@ -37,6 +37,8 @@ pub(crate) async fn create_form(
|
||||
unique: false,
|
||||
data_type: DataType::String,
|
||||
default: "".to_string(),
|
||||
sort_key: 1000, // big number so it goes at the end by default
|
||||
opt_multiline: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -78,6 +80,10 @@ pub(crate) struct PropertyModelCreateForm {
|
||||
/// Default value to be parsed to the data type
|
||||
/// May be unused if empty and optional
|
||||
pub default: String,
|
||||
#[serde(default)]
|
||||
pub sort_key: i64,
|
||||
#[serde(default)]
|
||||
pub opt_multiline: bool,
|
||||
}
|
||||
|
||||
fn parse_default(data_type: DataType, default: String) -> Result<TypedValue, String> {
|
||||
@@ -151,6 +157,10 @@ pub(crate) async fn create(
|
||||
unique,
|
||||
data_type: form.data_type,
|
||||
default,
|
||||
sort_key: form.sort_key,
|
||||
options: PropertyOptions {
|
||||
multiline: form.opt_multiline
|
||||
}
|
||||
}) {
|
||||
Ok(_id) => {
|
||||
wg.persist().err_to_500()?;
|
||||
@@ -202,6 +212,10 @@ pub(crate) struct PropertyModelEditForm {
|
||||
/// Default value to be parsed to the data type
|
||||
/// May be unused if empty and optional
|
||||
pub default: String,
|
||||
#[serde(default)]
|
||||
pub sort_key: i64,
|
||||
#[serde(default)]
|
||||
pub opt_multiline: bool,
|
||||
}
|
||||
|
||||
#[get("/model/property/update/{model_id}")]
|
||||
@@ -227,9 +241,11 @@ pub(crate) async fn update_form(
|
||||
model.optional = form.optional;
|
||||
model.multiple = form.multiple;
|
||||
model.unique = form.unique;
|
||||
model.sort_key = form.sort_key;
|
||||
model.options.multiline = form.opt_multiline;
|
||||
context.insert("model", &model);
|
||||
} else {
|
||||
context.insert("model", model);
|
||||
context.insert("model", &model);
|
||||
}
|
||||
|
||||
TERA.build_response("models/property_update", &context)
|
||||
@@ -265,6 +281,10 @@ pub(crate) async fn update(
|
||||
unique: form.unique,
|
||||
data_type: form.data_type,
|
||||
default,
|
||||
sort_key: form.sort_key,
|
||||
options: PropertyOptions {
|
||||
multiline: form.opt_multiline
|
||||
}
|
||||
}) {
|
||||
Ok(_id) => {
|
||||
wg.persist().err_to_500()?;
|
||||
|
||||
@@ -42,6 +42,7 @@ pub(crate) async fn create_form(
|
||||
optional: false,
|
||||
multiple: false,
|
||||
related: Default::default(),
|
||||
sort_key: 1000
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -70,6 +71,8 @@ pub(crate) struct RelationModelCreateForm {
|
||||
#[serde(default)]
|
||||
pub multiple: bool,
|
||||
pub related: ID,
|
||||
#[serde(default)]
|
||||
pub sort_key: i64,
|
||||
}
|
||||
|
||||
#[post("/model/relation/create")]
|
||||
@@ -88,6 +91,7 @@ pub(crate) async fn create(
|
||||
optional: form.optional,
|
||||
multiple: form.multiple,
|
||||
related: form.related,
|
||||
sort_key: form.sort_key,
|
||||
}) {
|
||||
Ok(_id) => {
|
||||
wg.persist().err_to_500()?;
|
||||
@@ -140,6 +144,8 @@ pub(crate) struct RelationModelEditForm {
|
||||
pub optional: bool,
|
||||
#[serde(default)]
|
||||
pub multiple: bool,
|
||||
#[serde(default)]
|
||||
pub sort_key : i64,
|
||||
}
|
||||
|
||||
#[get("/model/relation/update/{model_id}")]
|
||||
@@ -189,6 +195,7 @@ pub(crate) async fn update(
|
||||
optional: form.optional,
|
||||
multiple: form.multiple,
|
||||
related: Default::default(), // dummy
|
||||
sort_key: form.sort_key,
|
||||
}) {
|
||||
Ok(_id) => {
|
||||
wg.persist().err_to_500()?;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use actix_session::Session;
|
||||
use actix_web::{web, HttpResponse, Responder};
|
||||
@@ -7,6 +6,7 @@ use heck::TitleCase;
|
||||
use itertools::Itertools;
|
||||
use json_dotpath::DotPaths;
|
||||
use serde::Serialize;
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use yopa::{data, model, Storage, ID};
|
||||
|
||||
@@ -37,11 +37,18 @@ pub struct ObjectDisplay<'a> {
|
||||
pub name: Cow<'a, str>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct ObjectCreate<'a> {
|
||||
pub id: ID,
|
||||
pub model: ID,
|
||||
pub name: Cow<'a, str>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, Clone)]
|
||||
pub struct ObjectCreateData<'a> {
|
||||
pub model_id: ID,
|
||||
pub schema: Schema<'a>,
|
||||
pub objects: Vec<ObjectDisplay<'a>>,
|
||||
pub objects: Vec<ObjectCreate<'a>>,
|
||||
}
|
||||
|
||||
#[get("/object/create/{model_id}")]
|
||||
@@ -96,11 +103,12 @@ fn prepare_object_create_data(rg: &Storage, model_id: ID) -> actix_web::Result<O
|
||||
rel_models: relations,
|
||||
prop_models: rg
|
||||
.get_property_models_for_parents(prop_object_ids)
|
||||
.sorted_by(PropertyModel::order_refs)
|
||||
.collect(),
|
||||
},
|
||||
objects: rg
|
||||
.get_objects_of_types(related_ids)
|
||||
.map(|o| ObjectDisplay {
|
||||
.map(|o| ObjectCreate {
|
||||
id: o.id,
|
||||
model: o.model,
|
||||
name: rg.get_object_name(o),
|
||||
@@ -115,8 +123,6 @@ pub(crate) async fn create(
|
||||
store: crate::YopaStoreWrapper,
|
||||
session: Session,
|
||||
) -> actix_web::Result<impl Responder> {
|
||||
warn!("{:?}", form);
|
||||
|
||||
let mut wg = store.write().await;
|
||||
let form = form.into_inner();
|
||||
|
||||
@@ -161,7 +167,7 @@ pub(crate) async fn list_inner(
|
||||
|
||||
let models: Vec<_> = rg
|
||||
.get_object_models()
|
||||
.sorted_by_key(|m| &m.name)
|
||||
.sorted_by(ObjectModel::order_refs)
|
||||
.map(|model| {
|
||||
let objects = objects_by_model.remove(&model.id).unwrap_or_default();
|
||||
let mut objects = objects
|
||||
@@ -239,7 +245,9 @@ pub(crate) async fn detail(
|
||||
context.insert("model", model);
|
||||
context.insert("kind", &rg.get_model_name(object.model));
|
||||
|
||||
let relations = rg.get_relations_for_object(object_id).collect_vec();
|
||||
let relations = rg
|
||||
.get_relations_for_object(object_id)
|
||||
.collect_vec();
|
||||
let reci_relations = rg
|
||||
.get_reciprocal_relations_for_object(object_id)
|
||||
.collect_vec();
|
||||
@@ -266,7 +274,7 @@ pub(crate) async fn detail(
|
||||
})
|
||||
}
|
||||
|
||||
view_object_properties.sort_by_key(|p| &p.model.name);
|
||||
view_object_properties.sort_by(|a, b| PropertyModel::order(a.model, b.model));
|
||||
|
||||
context.insert("properties", &view_object_properties);
|
||||
}
|
||||
@@ -300,7 +308,7 @@ pub(crate) async fn detail(
|
||||
})
|
||||
}
|
||||
|
||||
view_rel_properties.sort_by_key(|p| &p.model.name);
|
||||
view_rel_properties.sort_by(|a, b| PropertyModel::order(a.model, b.model));
|
||||
|
||||
let related_name = rg.get_object_name(related_obj);
|
||||
|
||||
@@ -324,7 +332,7 @@ pub(crate) async fn detail(
|
||||
})
|
||||
}
|
||||
|
||||
relation_views.sort_by_key(|r| &r.model.name);
|
||||
relation_views.sort_by(|a, b| RelationModel::order_refs(&a.model, &b.model));
|
||||
|
||||
context.insert("relations", &relation_views);
|
||||
}
|
||||
@@ -358,7 +366,7 @@ pub(crate) async fn detail(
|
||||
})
|
||||
}
|
||||
|
||||
view_rel_properties.sort_by_key(|p| &p.model.name);
|
||||
view_rel_properties.sort_by(|a, b| PropertyModel::order(a.model, b.model));
|
||||
|
||||
let related_name = rg.get_object_name(related_obj);
|
||||
|
||||
@@ -383,7 +391,7 @@ pub(crate) async fn detail(
|
||||
})
|
||||
}
|
||||
|
||||
relation_views.sort_by_key(|r| &r.model.reciprocal_name);
|
||||
relation_views.sort_by(|a, b| RelationModel::reciprocal_order_refs(&a.model, &b.model));
|
||||
|
||||
context.insert("reciprocal_relations", &relation_views);
|
||||
}
|
||||
@@ -396,11 +404,11 @@ struct EnrichedObject<'a> {
|
||||
id: ID,
|
||||
model: ID,
|
||||
name: Cow<'a, str>,
|
||||
values: HashMap<
|
||||
values: IndexMap<
|
||||
String, /* ID but as string so serde will stop exploding */
|
||||
Vec<&'a data::Value>,
|
||||
>,
|
||||
relations: HashMap<String /* ID */, Vec<EnrichedRelation<'a>>>,
|
||||
relations: IndexMap<String /* ID */, Vec<EnrichedRelation<'a>>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, Clone)]
|
||||
@@ -409,7 +417,7 @@ struct EnrichedRelation<'a> {
|
||||
object: ID,
|
||||
model: ID,
|
||||
related: ID,
|
||||
values: HashMap<String /* ID */, Vec<&'a data::Value>>,
|
||||
values: IndexMap<String /* ID */, Vec<&'a data::Value>>,
|
||||
}
|
||||
|
||||
#[get("/object/update/{id}")]
|
||||
@@ -444,8 +452,8 @@ pub(crate) async fn update_form(
|
||||
|
||||
let create_data = prepare_object_create_data(&rg, model.id)?;
|
||||
|
||||
let mut value_map = HashMap::new();
|
||||
let mut relation_map = HashMap::new();
|
||||
let mut value_map = IndexMap::new();
|
||||
let mut relation_map = IndexMap::new();
|
||||
|
||||
// Some properties may have no values, so we first check what IDs to expect
|
||||
let prop_ids = create_data
|
||||
@@ -453,6 +461,7 @@ pub(crate) async fn update_form(
|
||||
.prop_models
|
||||
.iter()
|
||||
.filter(|p| p.object == model.id)
|
||||
.sorted_by(PropertyModel::order_refs2)
|
||||
.map(|p| p.id)
|
||||
.collect_vec();
|
||||
|
||||
@@ -500,7 +509,7 @@ pub(crate) async fn update_form(
|
||||
.unwrap_or_default();
|
||||
|
||||
for rel in relations {
|
||||
let mut relation_values_map = HashMap::new();
|
||||
let mut relation_values_map = IndexMap::new();
|
||||
|
||||
// values keyed by model
|
||||
let mut rel_values = relation_values_grouped_by_instance
|
||||
|
||||
Reference in New Issue
Block a user