From 8f9c83dc2a9ef79f32819ded46e54097ad475ee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Gremaud?= Date: Tue, 1 Oct 2024 15:42:18 +0200 Subject: [PATCH] Add more components + base for history --- k356/app/settings.py | 1 + k356/app/utils/api/api_list.py | 1 + k356/app/utils/models.py | 4 +- .../migrations/0005_alter_property_type.py | 36 ++ ...calitem_historicalitemrelation_and_more.py | 325 ++++++++++++++++++ k356/items/models.py | 21 +- .../components/DynField/template.html | 70 ++++ .../templates/components/DynField/vue.js | 47 +++ .../components/ItemDetail/template.html | 60 +++- .../templates/components/ItemDetail/vue.js | 150 ++++---- .../ItemRelationDetail/template.html | 56 ++- .../components/ItemRelationDetail/vue.js | 55 ++- .../components/ItemView/template.html | 3 - .../templates/components/ItemView/vue.js | 208 ++--------- .../LinkedPropertyList/template.html | 1 + .../components/LinkedPropertyList/vue.js | 8 + .../components/PropertyDetail/template.html | 62 +++- .../components/PropertyDetail/vue.js | 75 ++++ .../components/PropertyView/template.html | 1 - .../templates/components/PropertyView/vue.js | 169 +-------- .../RelationPropertyList/template.html | 1 + .../components/RelationPropertyList/vue.js | 8 + k356/items/urls.py | 12 +- k356/items/views/base.py | 10 +- k356/items/views/item_view.py | 3 +- k356/items/views/property_view.py | 28 +- k356/items/views/relation_view.py | 24 ++ k356/main/templates/components/Loading/vue.js | 8 +- k356/pyproject.toml | 1 + k356/templates/base.html | 11 +- .../base_components/glist/template.html | 72 ++-- k356/templates/base_components/glist/vue.js | 54 ++- k356/templates/scripts.js | 104 ------ k356/templates/vue/index.js | 140 +++++--- k356/templates/vue/plugins.js | 40 ++- k356/templates/vue/stores.js | 108 ++++++ .../migrations/0006_historicalusersettings.py | 57 +++ k356/users/models.py | 5 + 38 files changed, 1374 insertions(+), 665 deletions(-) create mode 100644 k356/items/migrations/0005_alter_property_type.py create mode 100644 k356/items/migrations/0006_historicalitem_historicalitemrelation_and_more.py create mode 100644 k356/items/templates/components/DynField/template.html create mode 100644 k356/items/templates/components/DynField/vue.js create mode 100644 k356/items/templates/components/LinkedPropertyList/template.html create mode 100644 k356/items/templates/components/LinkedPropertyList/vue.js create mode 100644 k356/items/templates/components/RelationPropertyList/template.html create mode 100644 k356/items/templates/components/RelationPropertyList/vue.js create mode 100644 k356/templates/vue/stores.js create mode 100644 k356/users/migrations/0006_historicalusersettings.py diff --git a/k356/app/settings.py b/k356/app/settings.py index bf3b656..71da10d 100644 --- a/k356/app/settings.py +++ b/k356/app/settings.py @@ -75,6 +75,7 @@ BOWER_INSTALLED_APPS = [ "https://cdn.jsdelivr.net/npm/sweetalert2", "https://cdn.jsdelivr.net/npm/vue-resource", "https://unpkg.com/vuex@3.6.2/dist/vuex.js", + "vuex-extensions=https://unpkg.com/vuex-extensions@4.1.0/lib/index.js", "https://cdn.jsdelivr.net/npm/js-cookie@3.0.5/dist/js.cookie.min.js", "https://unpkg.com/vue-router@3/dist/vue-router.js", "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css", diff --git a/k356/app/utils/api/api_list.py b/k356/app/utils/api/api_list.py index a662697..ab22438 100644 --- a/k356/app/utils/api/api_list.py +++ b/k356/app/utils/api/api_list.py @@ -13,5 +13,6 @@ def header_for_table(model): "text": "Actions", "value": "actions", "sortable": False, + "details": False, }, ] diff --git a/k356/app/utils/models.py b/k356/app/utils/models.py index 51760e2..cff5aa8 100644 --- a/k356/app/utils/models.py +++ b/k356/app/utils/models.py @@ -25,6 +25,8 @@ class BaseQuerySet(models.QuerySet): "editable": field.name not in self.model.Serialization.excluded_fields_edit, "field_widget": "v-textarea", "choices": None, + "details": True, + "dynamic_field_type": field.name in self.model.Serialization.dynamic_field_type, } } @@ -48,7 +50,6 @@ class BaseQuerySet(models.QuerySet): if field.choices: ret[field.name].update( - text="", field_widget="v-select", choices=[ { @@ -92,6 +93,7 @@ class BaseModel(models.Model): # Exclude fields from serialization excluded_fields = [] excluded_fields_edit = ["id", "created_at", "last_modified_at"] + dynamic_field_type = [] class Encryption: fields = ["name", "description", "custom_identifier"] diff --git a/k356/items/migrations/0005_alter_property_type.py b/k356/items/migrations/0005_alter_property_type.py new file mode 100644 index 0000000..b6c40c1 --- /dev/null +++ b/k356/items/migrations/0005_alter_property_type.py @@ -0,0 +1,36 @@ +# Generated by Django 5.1.1 on 2024-09-30 19:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("items", "0004_alter_property_type"), + ] + + operations = [ + migrations.AlterField( + model_name="property", + name="type", + field=models.CharField( + choices=[ + ("text", "Text"), + ("date", "Date"), + ("datetime", "Date & time"), + ("time", "Time"), + ("duration", "Duration"), + ("uuid", "UUID"), + ("number", "Number"), + ("float", "Float"), + ("boolean", "Boolean"), + ("email", "Email"), + ("ipv4", "IPv4 address"), + ("ipv6", "IPv6 address"), + ("json", "JSON"), + ], + default="text", + max_length=32, + ), + ), + ] diff --git a/k356/items/migrations/0006_historicalitem_historicalitemrelation_and_more.py b/k356/items/migrations/0006_historicalitem_historicalitemrelation_and_more.py new file mode 100644 index 0000000..9b77cfc --- /dev/null +++ b/k356/items/migrations/0006_historicalitem_historicalitemrelation_and_more.py @@ -0,0 +1,325 @@ +# Generated by Django 5.1.1 on 2024-10-01 13:41 + +import django.db.models.deletion +import simple_history.models +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("items", "0005_alter_property_type"), + ("users", "0006_historicalusersettings"), + ] + + operations = [ + migrations.CreateModel( + name="HistoricalItem", + fields=[ + ("id", models.UUIDField(db_index=True, default=uuid.uuid4, editable=False)), + ("custom_identifier", models.TextField(blank=True, max_length=2048, null=True)), + ("created_at", models.DateTimeField(blank=True, editable=False)), + ("last_modified_at", models.DateTimeField(blank=True, editable=False)), + ("name", models.TextField(max_length=2048)), + ("description", models.TextField(max_length=2048)), + ("history_id", models.AutoField(primary_key=True, serialize=False)), + ("history_date", models.DateTimeField(db_index=True)), + ("history_change_reason", models.CharField(max_length=100, null=True)), + ("history_type", models.CharField(choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], max_length=1)), + ( + "author", + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to="users.usersettings", + ), + ), + ( + "history_user", + models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="+", to="users.usersettings"), + ), + ( + "type", + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to="items.itemtype", + ), + ), + ], + options={ + "verbose_name": "historical item", + "verbose_name_plural": "historical items", + "ordering": ("-history_date", "-history_id"), + "get_latest_by": ("history_date", "history_id"), + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + migrations.CreateModel( + name="HistoricalItemRelation", + fields=[ + ("id", models.UUIDField(db_index=True, default=uuid.uuid4, editable=False)), + ("name", models.TextField(blank=True, max_length=2048, null=True)), + ("description", models.TextField(blank=True, max_length=2048, null=True)), + ("custom_identifier", models.TextField(blank=True, max_length=2048, null=True)), + ("created_at", models.DateTimeField(blank=True, editable=False)), + ("last_modified_at", models.DateTimeField(blank=True, editable=False)), + ("history_id", models.AutoField(primary_key=True, serialize=False)), + ("history_date", models.DateTimeField(db_index=True)), + ("history_change_reason", models.CharField(max_length=100, null=True)), + ("history_type", models.CharField(choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], max_length=1)), + ( + "author", + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to="users.usersettings", + ), + ), + ( + "child", + models.ForeignKey( + blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="items.item" + ), + ), + ( + "history_user", + models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="+", to="users.usersettings"), + ), + ( + "parent", + models.ForeignKey( + blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="items.item" + ), + ), + ], + options={ + "verbose_name": "historical item relation", + "verbose_name_plural": "historical item relations", + "ordering": ("-history_date", "-history_id"), + "get_latest_by": ("history_date", "history_id"), + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + migrations.CreateModel( + name="HistoricalItemType", + fields=[ + ("id", models.UUIDField(db_index=True, default=uuid.uuid4, editable=False)), + ("name", models.TextField(blank=True, max_length=2048, null=True)), + ("description", models.TextField(blank=True, max_length=2048, null=True)), + ("custom_identifier", models.TextField(blank=True, max_length=2048, null=True)), + ("created_at", models.DateTimeField(blank=True, editable=False)), + ("last_modified_at", models.DateTimeField(blank=True, editable=False)), + ("history_id", models.AutoField(primary_key=True, serialize=False)), + ("history_date", models.DateTimeField(db_index=True)), + ("history_change_reason", models.CharField(max_length=100, null=True)), + ("history_type", models.CharField(choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], max_length=1)), + ( + "author", + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to="users.usersettings", + ), + ), + ( + "history_user", + models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="+", to="users.usersettings"), + ), + ], + options={ + "verbose_name": "historical item type", + "verbose_name_plural": "historical item types", + "ordering": ("-history_date", "-history_id"), + "get_latest_by": ("history_date", "history_id"), + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + migrations.CreateModel( + name="HistoricalLinkedProperty", + fields=[ + ("id", models.UUIDField(db_index=True, default=uuid.uuid4, editable=False)), + ("name", models.TextField(blank=True, max_length=2048, null=True)), + ("description", models.TextField(blank=True, max_length=2048, null=True)), + ("custom_identifier", models.TextField(blank=True, max_length=2048, null=True)), + ("created_at", models.DateTimeField(blank=True, editable=False)), + ("last_modified_at", models.DateTimeField(blank=True, editable=False)), + ("value", models.TextField(max_length=2048)), + ("history_id", models.AutoField(primary_key=True, serialize=False)), + ("history_date", models.DateTimeField(db_index=True)), + ("history_change_reason", models.CharField(max_length=100, null=True)), + ("history_type", models.CharField(choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], max_length=1)), + ( + "author", + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to="users.usersettings", + ), + ), + ( + "history_user", + models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="+", to="users.usersettings"), + ), + ( + "item", + models.ForeignKey( + blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="items.item" + ), + ), + ( + "property", + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to="items.property", + ), + ), + ], + options={ + "verbose_name": "historical linked property", + "verbose_name_plural": "historical linked propertys", + "ordering": ("-history_date", "-history_id"), + "get_latest_by": ("history_date", "history_id"), + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + migrations.CreateModel( + name="HistoricalProperty", + fields=[ + ("id", models.UUIDField(db_index=True, default=uuid.uuid4, editable=False)), + ("name", models.TextField(blank=True, max_length=2048, null=True)), + ("description", models.TextField(blank=True, max_length=2048, null=True)), + ("custom_identifier", models.TextField(blank=True, max_length=2048, null=True)), + ("created_at", models.DateTimeField(blank=True, editable=False)), + ("last_modified_at", models.DateTimeField(blank=True, editable=False)), + ( + "type", + models.CharField( + choices=[ + ("text", "Text"), + ("date", "Date"), + ("datetime", "Date & time"), + ("time", "Time"), + ("duration", "Duration"), + ("uuid", "UUID"), + ("number", "Number"), + ("float", "Float"), + ("boolean", "Boolean"), + ("email", "Email"), + ("ipv4", "IPv4 address"), + ("ipv6", "IPv6 address"), + ("json", "JSON"), + ], + default="text", + max_length=32, + ), + ), + ("history_id", models.AutoField(primary_key=True, serialize=False)), + ("history_date", models.DateTimeField(db_index=True)), + ("history_change_reason", models.CharField(max_length=100, null=True)), + ("history_type", models.CharField(choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], max_length=1)), + ( + "author", + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to="users.usersettings", + ), + ), + ( + "history_user", + models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="+", to="users.usersettings"), + ), + ], + options={ + "verbose_name": "historical property", + "verbose_name_plural": "historical propertys", + "ordering": ("-history_date", "-history_id"), + "get_latest_by": ("history_date", "history_id"), + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + migrations.CreateModel( + name="HistoricalRelationProperty", + fields=[ + ("id", models.UUIDField(db_index=True, default=uuid.uuid4, editable=False)), + ("name", models.TextField(blank=True, max_length=2048, null=True)), + ("description", models.TextField(blank=True, max_length=2048, null=True)), + ("custom_identifier", models.TextField(blank=True, max_length=2048, null=True)), + ("created_at", models.DateTimeField(blank=True, editable=False)), + ("last_modified_at", models.DateTimeField(blank=True, editable=False)), + ("value", models.TextField(max_length=2048)), + ("history_id", models.AutoField(primary_key=True, serialize=False)), + ("history_date", models.DateTimeField(db_index=True)), + ("history_change_reason", models.CharField(max_length=100, null=True)), + ("history_type", models.CharField(choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], max_length=1)), + ( + "author", + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to="users.usersettings", + ), + ), + ( + "history_user", + models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="+", to="users.usersettings"), + ), + ( + "property", + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to="items.property", + ), + ), + ( + "relation", + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to="items.itemrelation", + ), + ), + ], + options={ + "verbose_name": "historical relation property", + "verbose_name_plural": "historical relation propertys", + "ordering": ("-history_date", "-history_id"), + "get_latest_by": ("history_date", "history_id"), + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + ] diff --git a/k356/items/models.py b/k356/items/models.py index 9277f1c..fb0804b 100644 --- a/k356/items/models.py +++ b/k356/items/models.py @@ -6,6 +6,9 @@ from app.utils.models import BaseModel from users.models import UserSettings +from simple_history.models import HistoricalRecords + + class ItemBase(BaseModel): class Meta: abstract = True @@ -15,6 +18,15 @@ class ItemBase(BaseModel): excluded_fields_edit = BaseModel.Serialization.excluded_fields_edit + ["author"] author = models.ForeignKey(UserSettings, on_delete=models.PROTECT) + history = HistoricalRecords(inherit=True, user_model=UserSettings) + + @property + def _history_user(self): + return self.author + + @_history_user.setter + def _history_user(self, value): + self.author = value class ItemType(ItemBase): @@ -56,7 +68,8 @@ class PropertyType(models.TextChoices): FLOAT = "float", _("Float") BOOLEAN = "boolean", _("Boolean") EMAIL = "email", _("Email") - IP = "ip", _("IP address") + IPV4 = "ipv4", _("IPv4 address") + IPV6 = "ipv6", _("IPv6 address") JSON = "json", _("JSON") # TODO: Add more property types (location, etc) @@ -70,6 +83,12 @@ class BaseLinkedProperty(ItemBase): class Meta: abstract = True + class Serialization(ItemBase.Serialization): + dynamic_field_type = ItemBase.Serialization.dynamic_field_type + ["value"] + + class Encryption(ItemBase.Encryption): + fields = ItemBase.Encryption.fields + ["value"] + property = models.ForeignKey(Property, on_delete=models.CASCADE) # Value is encrypted too diff --git a/k356/items/templates/components/DynField/template.html b/k356/items/templates/components/DynField/template.html new file mode 100644 index 0000000..daf9a7b --- /dev/null +++ b/k356/items/templates/components/DynField/template.html @@ -0,0 +1,70 @@ +{% load i18n %} + +
+ + + + + + + + + + + + +
diff --git a/k356/items/templates/components/DynField/vue.js b/k356/items/templates/components/DynField/vue.js new file mode 100644 index 0000000..88ff7d5 --- /dev/null +++ b/k356/items/templates/components/DynField/vue.js @@ -0,0 +1,47 @@ +DynField = { + template: "#DynField", + router_path: "/", + delimiters: ["[[", "]]"], + props: { + field: { + default: null, + }, + item: { + default: function() { + return {} + }, + } + }, + + data: function() { + return { + test: null, + modal: null, + rules: { + required: value => !!value || "{{_('Required') | escapejs}}", + email: value => { + const pattern = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ + return pattern.test(value) || "{{_('Invalid E-mail') | escapejs}}" + }, + uuid: value => { + const pattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/ + return pattern.test(value) || "{{_('Invalid UUID') | escapejs}}" + }, + ipv4: value => { + const pattern = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/ + return pattern.test(value) || "{{_('Invalid IPv4') | escapejs}}" + }, + }, + } + }, + + computed: { + field_type: function() { + if (this.item?.property?.type == undefined) { + return "v-textarea" + } + + return this.item?.property?.type + }, + } +} diff --git a/k356/items/templates/components/ItemDetail/template.html b/k356/items/templates/components/ItemDetail/template.html index b2149e3..767751a 100644 --- a/k356/items/templates/components/ItemDetail/template.html +++ b/k356/items/templates/components/ItemDetail/template.html @@ -2,18 +2,40 @@
-
{% trans "Properties" %} [[ this.$route.params.id ]]
+
{% trans "Item" %} [[ this.$route.params.id ]]
- + + + +
+
+ +
+
{% trans "Properties" %}
+
+ + @deleteItem="deleteLinkedProperty" + @createItem="createLinkedProperty" + @editItem="editLinkedProperty" + >
@@ -23,12 +45,12 @@
@@ -38,13 +60,13 @@
diff --git a/k356/items/templates/components/ItemDetail/vue.js b/k356/items/templates/components/ItemDetail/vue.js index c18a050..679bda0 100644 --- a/k356/items/templates/components/ItemDetail/vue.js +++ b/k356/items/templates/components/ItemDetail/vue.js @@ -3,95 +3,109 @@ ItemDetail = { router_path: "/ItemDetail/:id", delimiters: ["[[", "]]"], - data: function() { - return { - properties: [], - linked_properties: [], - children: [], - parents: [], - // TODO: Also remove this tedious things - properties_headers: [], - linked_properties_headers: [], - children_headers: [], - parents_headers: [], - } - }, - computed: { - // TODO: Remove this by a generic things at some points, this become tedious and repetitive - properties_efields: function() { - return this.properties_headers.filter(e => e.encrypted).map(e => e.value) + object: function() { + return this.$store.state.items.items.find(i => i.id == this.$route.params.id) }, - linked_properties_efields: function() { - return this.linked_properties_headers.filter(e => e.encrypted).map(e => e.value) + linked_properties: function() { + return this.$store.state.linkedProperties.items.filter(lp => lp.item == this.$route.params.id) }, - children_efields: function() { - return this.children_headers.filter(e => e.encrypted).map(e => e.value) + linked_properties_headers: function() { + return this.$store.state.linkedProperties.headers }, - parents_efields: function() { - return this.parents_headers.filter(e => e.encrypted).map(e => e.value) + properties: function() { + return this.$store.state.properties.items.filter(p => this.linked_properties.map(e => e.property).includes(p.id)) }, - }, - mounted: function() { + properties_headers: function() { + return this.$store.state.properties.headers + }, - this.reload() + children: function() { + return this.$store.state.relations.items.filter(p => p.parent == this.$route.params.id) + }, + parents: function() { + return this.$store.state.relations.items.filter(p => p.child == this.$route.params.id) + }, + + children_headers: function() { + return this.$store.state.relations.headers + }, + + headers: function() { + return this.$store.state.items.headers + }, + + all_items: function() { + return this.$store.state.items.items + }, + + all_properties: function() { + return this.$store.state.properties.items + }, }, methods: { - async reload () { - - try { - - const response = await this.$http.get(Urls["items:details"](this.$route.params.id)) - - this.properties_headers = response.data.properties_headers - this.linked_properties_headers = response.data.linked_properties_headers - this.children_headers = response.data.children_headers - this.parents_headers = response.data.parents_headers - - // TODO: TEDIOUUUUUS - response.data.parents.forEach(async e => { - this.parents.push(await this.decryptObject(this.parents_efields, e)) - }) - - response.data.children.forEach(async e => { - this.children.push(await this.decryptObject(this.children_efields, e)) - }) - - response.data.properties.forEach(async e => { - this.properties.push(await this.decryptObject(this.properties_efields, e)) - }) - - response.data.linked_properties.forEach(async e => { - this.linked_properties.push(await this.decryptObject(this.linked_properties_efields, e)) - }) - - } catch (err) { - - Swal.fire({title: "{{_('Error during loading of items') | escapejs}}", icon: "error", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}) - - throw err - - } - + linkedPropertyEdition (method, item) { + return this.object_edit("items:linked.property.edit", "items:linked.property.create", 'linkedProperties', method, item) }, - async deleteItem () { - + relationPropertiesEdition (method, item) { + return this.object_edit("items:relation.property.edit", "items:relation.property.create", 'relationProperties', method, item) }, - async createItem () { - + relationEdition (method, item) { + return this.object_edit("items:relation.edit", "items:relation.create", 'relations', method, item) }, - async editItem() { - + async deleteLinkedProperty (item) { + await this.linkedPropertyEdition("delete", item) + this.$store.commit("linkedProperties/removeItem", item.id) }, + + async createLinkedProperty (item) { + item.item = this.$route.params.id + const new_item = await this.linkedPropertyEdition("post", item) + this.$store.commit("linkedProperties/addItem", new_item) + }, + + async editLinkedProperty (item) { + item.item = this.$route.params.id + const new_item = await this.linkedPropertyEdition("post", item) + this.$store.commit("linkedProperties/editItem", new_item) + }, + + async deleteProperty (item) { + console.log(item) + }, + + async createProperty (item) { + console.log(item) + }, + + async editProperty (item) { + console.log(item) + }, + + async deleteRelation (item) { + await this.relationEdition("delete", item) + this.$store.commit("relations/removeItem", item.id) + }, + + async createRelation (item) { + const new_item = await this.relationEdition("post", item) + this.$store.commit("relations/addItem", new_item) + }, + + async editRelation (item) { + const new_item = await this.relationEdition("post", item) + this.$store.commit("relations/editItem", new_item) + }, + } } diff --git a/k356/items/templates/components/ItemRelationDetail/template.html b/k356/items/templates/components/ItemRelationDetail/template.html index 6489893..dd1388a 100644 --- a/k356/items/templates/components/ItemRelationDetail/template.html +++ b/k356/items/templates/components/ItemRelationDetail/template.html @@ -1 +1,55 @@ -
[[ this.$route.params.id ]]
+{% load i18n %} + +
+
+
{% trans "Relation" %} [[ this.$route.params.id ]]
+
+ + + + + +
+
+ +
+
{% trans "Properties" %}
+
+ +
+
+
diff --git a/k356/items/templates/components/ItemRelationDetail/vue.js b/k356/items/templates/components/ItemRelationDetail/vue.js index 8fa3281..4ef1ee7 100644 --- a/k356/items/templates/components/ItemRelationDetail/vue.js +++ b/k356/items/templates/components/ItemRelationDetail/vue.js @@ -3,24 +3,55 @@ ItemRelationDetail = { router_path: "/ItemRelationDetail/:id", delimiters: ["[[", "]]"], - data: function() { - return { - data: null + computed: { + object: function() { + return this.$store.state.relations.items.find(i => i.id == this.$route.params.id) + }, + + headers: function() { + return this.$store.state.relations.headers + }, + + relation_properties: function() { + return this.$store.state.relationProperties.items.filter(i => i.relation == this.$route.params.id) + }, + + relation_properties_headers: function() { + return this.$store.state.relationProperties.headers + }, + + all_properties: function() { + return this.$store.state.properties.items } }, - mounted: function() { - - this.reload() - - }, - methods: { - async reload () { + showItem (id) { + this.$router.push({ name: 'ItemDetail', params: { id: id }}) + }, - const response = await this.$http.get(Urls["items:relation.details"](this.$route.params.id)) + relationPropertyEdition (method, item) { + return this.object_edit("items:relation.property.edit", "items:relation.property.create", 'relationProperties', method, item) + }, + + async deleteRelationProperty (item) { + await this.relationPropertyEdition("delete", item) + this.$store.commit("relationProperties/removeItem", item.id) + }, + + async createRelationProperty (item) { + item.relation = this.$route.params.id + const new_item = await this.relationPropertyEdition("post", item) + this.$store.commit("relationProperties/addItem", new_item) + }, + + async editRelationProperty (item) { + item.relation = this.$route.params.id + const new_item = await this.relationPropertyEdition("post", item) + this.$store.commit("relationProperties/editItem", new_item) + }, - } } + } diff --git a/k356/items/templates/components/ItemView/template.html b/k356/items/templates/components/ItemView/template.html index 7842790..cf5edbc 100644 --- a/k356/items/templates/components/ItemView/template.html +++ b/k356/items/templates/components/ItemView/template.html @@ -9,7 +9,6 @@ :items="items" :items_headers="items_headers" :items_relations="{'type': types}" - :hidden_fields="[]" group_by="type__name" @deleteItem="deleteItem" @createItem="createItem" @@ -26,8 +25,6 @@ e.encrypted).map(e => e.value) - }, - - types_efields: function() { - return this.types_headers.filter(e => e.encrypted).map(e => e.value) - }, + ...Vuex.mapState({ + items: state => state.items.items, + items_headers: state => state.items.headers, + types: state => state.types.items, + types_headers: state => state.types.headers, + }), }, methods: { - async reload () { - - try { - const response = await this.$http.get(Urls["items:list"]()) - - this.items_headers = response.data.result.items_headers - this.types_headers = response.data.result.types_headers - - // Decrypt all item the push - response.data.result.items.forEach(async item => { - const new_item = await this.decryptObject(this.items_efields, item) - - this.items.push(new_item) - }) - - // Decrypt all type the push - response.data.result.types.forEach(async type => { - const new_type = await this.decryptObject(this.types_efields, type) - - this.types.push(new_type) - }) - - } catch (err) { - - Swal.fire({title: "{{_('Error during loading of items') | escapejs}}", icon: "error", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}) - - } - - }, - - async object_edit(url_edit, url_create, encrypted_fields, method, obj) { - - let url = null - - if (obj.id == undefined || obj.id == null) { - url = Urls[url_create]() - } else { - url = Urls[url_edit](obj.id) - } - - try { - - const newobj = await this.encryptObject(encrypted_fields, obj) - const response = await this.$http[method](url, newobj) - - if (method != "delete") { - return await this.decryptObject(encrypted_fields, response.data.object) - } - - } catch (err) { - - let msg = "{{_('Error during edition') | escapejs}}" - if (method == "delete") { - msg = "{{_('Error during deletion') | escapejs}}" - } - - Swal.fire({title: msg, icon: "error", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}) - - throw err - - } - - }, - item_edition (method, item) { - return this.object_edit("items:edit", "items:create", this.items_efields, method, item) + return this.object_edit("items:edit", "items:create", 'items', method, item) }, type_edition (method, item) { - return this.object_edit("items:type.edit", "items:type.create", this.types_efields, method, item) + return this.object_edit("items:type.edit", "items:type.create", 'types', method, item) }, async createItem (item) { - - try { - - const new_item = await this.item_edition("post", item) - this.items.push(new_item) - - Swal.fire({title: "{{_('Item successfully created!') | escapejs}}", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}) - - } catch (err) { - - } - + const new_item = await this.item_edition("post", item) + this.$store.commit("items/addItem", new_item) }, - async editItem (index, item) { - - try { - - // Remove the item - this.items.splice(index, 1) - - const new_item = await this.item_edition("post", item) - - // Add the new item - this.items.push(new_item) - - Swal.fire({title: "{{_('Item successfully edited') | escapejs}}", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}) - - } catch (err) { - - this.items.push(item) - - } - + async editItem (item) { + const new_item = await this.item_edition("post", item) + this.$store.commit("items/editItem", new_item) }, - async deleteItem (index) { - - var item = this.items[index] - - try { - - // Remove the item - this.items.splice(index, 1) - await this.item_edition("delete", item) - - Swal.fire({title: "{{_('Item successfully deleted!') | escapejs}}", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}) - - } catch (err) { - - this.items.push(item) - - } - + async deleteItem (item) { + await this.item_edition("delete", item) + this.$store.commit("items/removeItem", item.id) }, async createType (type) { - - try { - - const new_type = await this.type_edition("post", type) - this.types.push(new_type) - - Swal.fire({title: "{{_('Type successfully created!') | escapejs}}", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}) - - } catch (err) { - - } - + const new_type = await this.type_edition("post", type) + this.$store.commit("types/addItem", new_type) }, - async editType (index, type) { - - try { - - this.types.splice(index, 1) - - const new_type = await this.type_edition("post", type) - - this.types.push(new_type) - - Swal.fire({title: "{{_('Type successfully edited') | escapejs}}", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}) - - } catch (err) { - - this.types.push(type) - - } - + async editType (type) { + const new_type = await this.type_edition("post", type) + this.$store.commit("types/editItem", new_type) }, - async deleteType (index) { - - var type = this.types[index] - - try { - - // Remove the type - this.types.splice(index, 1) - - await this.type_edition("delete", type) - - Swal.fire({title: "{{_('Type successfully deleted!') | escapejs}}", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}) - - } catch (err) { - - this.types.push(type) - - } + async deleteType (type) { + await this.type_edition("delete", type) + this.$store.commit("types/removeItem", type.id) }, }, diff --git a/k356/items/templates/components/LinkedPropertyList/template.html b/k356/items/templates/components/LinkedPropertyList/template.html new file mode 100644 index 0000000..fb816ff --- /dev/null +++ b/k356/items/templates/components/LinkedPropertyList/template.html @@ -0,0 +1 @@ +{% extends "base_components/glist/template.html" %} diff --git a/k356/items/templates/components/LinkedPropertyList/vue.js b/k356/items/templates/components/LinkedPropertyList/vue.js new file mode 100644 index 0000000..c775184 --- /dev/null +++ b/k356/items/templates/components/LinkedPropertyList/vue.js @@ -0,0 +1,8 @@ +{% extends "base_components/glist/vue.js" %} +{% load main %} + +{% block component %} + {% define 'items' 'items' %} + {% define 'show_url' 'PropertyDetail' %} + {{ block.super }} +{% endblock %} diff --git a/k356/items/templates/components/PropertyDetail/template.html b/k356/items/templates/components/PropertyDetail/template.html index fdafe63..e04780b 100644 --- a/k356/items/templates/components/PropertyDetail/template.html +++ b/k356/items/templates/components/PropertyDetail/template.html @@ -1 +1,61 @@ -
PropertyDetail
+{% load i18n %} + +
+
+
{% trans "Property" %} [[ this.$route.params.id ]]
+
+ + + + + +
+
+ +
+
{% trans "Item Properties" %}
+
+ +
+
+ +
+
{% trans "Relation Properties" %}
+
+ +
+
+
diff --git a/k356/items/templates/components/PropertyDetail/vue.js b/k356/items/templates/components/PropertyDetail/vue.js index 64ba2de..f8d880f 100644 --- a/k356/items/templates/components/PropertyDetail/vue.js +++ b/k356/items/templates/components/PropertyDetail/vue.js @@ -1,4 +1,79 @@ PropertyDetail = { template: "#PropertyDetail", router_path: "/PropertyDetail/:id", + delimiters: ["[[", "]]"], + + computed: { + object: function() { + return this.$store.state.properties.items.find(i => i.id == this.$route.params.id) + }, + + headers: function() { + return this.$store.state.properties.headers + }, + + linked_properties: function() { + return this.$store.state.linkedProperties.items.filter(i => i.property == this.$route.params.id) + }, + + linked_properties_headers: function() { + return this.$store.state.linkedProperties.headers + }, + + relation_properties: function() { + return this.$store.state.relationProperties.items.filter(i => i.property == this.$route.params.id) + }, + + relation_properties_headers: function() { + return this.$store.state.relationProperties.headers + }, + + all_items: function() { + return this.$store.state.items.items + } + }, + + methods: { + linkedPropertyEdition (method, item) { + return this.object_edit("items:linked.property.edit", "items:linked.property.create", 'linkedProperties', method, item) + }, + + relationPropertyEdition (method, item) { + return this.object_edit("items:relation.property.edit", "items:relation.property.create", 'relationProperties', method, item) + }, + + async deleteLinkedProperty (item) { + await this.linkedPropertyEdition("delete", item) + this.$store.commit("linkedProperties/removeItem", item.id) + }, + + async createLinkedProperty (item) { + item.property = this.$route.params.id + const new_item = await this.linkedPropertyEdition("post", item) + this.$store.commit("linkedProperties/addItem", new_item) + }, + + async editLinkedProperty (item) { + item.property = this.$route.params.id + const new_item = await this.linkedPropertyEdition("post", item) + this.$store.commit("linkedProperties/editItem", new_item) + }, + + async deleteRelationProperty (item) { + await this.relationPropertyEdition("delete", item) + this.$store.commit("relationProperties/removeItem", item.id) + }, + + async createRelationProperty (item) { + item.property = this.$route.params.id + const new_item = await this.relationPropertyEdition("post", item) + this.$store.commit("relationProperties/addItem", new_item) + }, + + async editRelationProperty (item) { + item.property = this.$route.params.id + const new_item = await this.relationPropertyEdition("post", item) + this.$store.commit("relationProperties/editItem", new_item) + }, + } }, diff --git a/k356/items/templates/components/PropertyView/template.html b/k356/items/templates/components/PropertyView/template.html index 1035ebd..a2a2ddc 100644 --- a/k356/items/templates/components/PropertyView/template.html +++ b/k356/items/templates/components/PropertyView/template.html @@ -9,7 +9,6 @@ :items="properties" :items_headers="properties_headers" :items_relations="{}" - :hidden_fields="[]" group_by="type" @deleteItem="deleteItem" @createItem="createItem" diff --git a/k356/items/templates/components/PropertyView/vue.js b/k356/items/templates/components/PropertyView/vue.js index 45d18b0..896d7c7 100644 --- a/k356/items/templates/components/PropertyView/vue.js +++ b/k356/items/templates/components/PropertyView/vue.js @@ -4,21 +4,11 @@ PropertyView = { delimiters: ["[[", "]]"], props: [], - data: function() { - return { - properties: [], - properties_headers: [], - } - }, - - mounted: function() { - this.reload() - }, - computed: { - properties_encrypted_fields: function() { - return this.properties_headers.filter(e => e.encrypted).map(e => e.value) - }, + ...Vuex.mapState({ + properties: state => state.properties.items, + properties_headers: state => state.properties.headers, + }), properties_relations: function() { return this.properties_headers.filter(e => e.choices != null) @@ -27,156 +17,23 @@ PropertyView = { methods: { - async reload () { - - try { - const response = await this.$http.get(Urls["items:property.list"]()) - - this.properties_headers = response.data.result.properties_headers - - // Decrypt all item the push - response.data.result.properties.forEach(async item => { - const new_item = await this.decryptObject(this.properties_encrypted_fields, item) - - this.properties.push(new_item) - }) - - } catch (err) { - - Swal.fire({title: "{{_('Error during loading of properties') | escapejs}}", icon: "error", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}) - - throw err - - } - - }, - - async decryptObject (encrypted_fields, obj) { - // Decrypt all fields and return a new object - - var newobj = {} - await Promise.all(Object.keys(obj).map(async field => { - if (encrypted_fields.includes(field) && obj[field] != null) { - newobj[field] = await this.decrypt(obj[field]) - } else { - newobj[field] = obj[field] - } - })) - - return newobj - - }, - - async encryptObject (encrypted_fields, obj) { - // Encrypt all fields and return a new object - - var newobj = {} - - await Promise.all(Object.keys(obj).map(async field => { - if (encrypted_fields.includes(field) && obj[field] != null) { - newobj[field] = await this.encrypt(obj[field]) - } else { - newobj[field] = obj[field] - } - })) - - return newobj - - }, - - async object_edit(url_edit, url_create, encrypted_fields, method, obj) { - - let url = null - - if (obj.id == undefined || obj.id == null) { - url = Urls[url_create]() - } else { - url = Urls[url_edit](obj.id) - } - - try { - - const newobj = await this.encryptObject(encrypted_fields, obj) - const response = await this.$http[method](url, newobj) - - if (method != "delete") { - return await this.decryptObject(encrypted_fields, response.data.object) - } - - } catch (err) { - - let msg = "{{_('Error during edition') | escapejs}}" - if (method == "delete") { - msg = "{{_('Error during deletion') | escapejs}}" - } - - Swal.fire({title: msg, icon: "error", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}) - - throw err - - } - - }, - property_edition (method, item) { - return this.object_edit("items:property.edit", "items:property.create", this.properties_encrypted_fields, method, item) + return this.object_edit("items:property.edit", "items:property.create", "properties", method, item) }, async createItem (item) { - - try { - - const new_item = await this.property_edition("post", item) - this.properties.push(new_item) - - Swal.fire({title: "{{_('Item successfully created!') | escapejs}}", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}) - - } catch (err) { - - } - + const new_item = await this.property_edition("post", item) + this.$store.commit("properties/addItem", new_item) }, - async editItem (index, item) { - - try { - - // Remove the item - this.properties.splice(index, 1) - - const new_item = await this.property_edition("post", item) - - // Add the new item - this.properties.push(new_item) - - Swal.fire({title: "{{_('Item successfully edited') | escapejs}}", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}) - - } catch (err) { - - this.properties.push(item) - - } - + async editItem (item) { + const new_item = await this.property_edition("post", item) + this.$store.commit("properties/editItem", new_item) }, - async deleteItem (index) { - - var item = this.properties[index] - - try { - - // Remove the item - this.properties.splice(index, 1) - await this.property_edition("delete", item) - - Swal.fire({title: "{{_('Item successfully deleted!') | escapejs}}", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}) - - } catch (err) { - - this.properties.push(item) - - } - + async deleteItem (item) { + await this.property_edition("delete", item) + this.$store.commit("properties/removeItem", item.id) }, }, diff --git a/k356/items/templates/components/RelationPropertyList/template.html b/k356/items/templates/components/RelationPropertyList/template.html new file mode 100644 index 0000000..fb816ff --- /dev/null +++ b/k356/items/templates/components/RelationPropertyList/template.html @@ -0,0 +1 @@ +{% extends "base_components/glist/template.html" %} diff --git a/k356/items/templates/components/RelationPropertyList/vue.js b/k356/items/templates/components/RelationPropertyList/vue.js new file mode 100644 index 0000000..62b5464 --- /dev/null +++ b/k356/items/templates/components/RelationPropertyList/vue.js @@ -0,0 +1,8 @@ +{% extends "base_components/glist/vue.js" %} +{% load main %} + +{% block component %} + {% define 'items' 'items' %} + {% define 'show_url' 'ItemDetail' %} + {{ block.super }} +{% endblock %} diff --git a/k356/items/urls.py b/k356/items/urls.py index b766fcd..37c956d 100644 --- a/k356/items/urls.py +++ b/k356/items/urls.py @@ -19,9 +19,15 @@ urlpatterns = [ path("property/list", property_view.property_list, name="property.list"), path("property/", property_view.property_edit, name="property.edit"), path("property/create", property_view.property_edit, {"id": None}, name="property.create"), + # Linked property + path("property/linked/", property_view.linked_property_edit, name="linked.property.edit"), + path("property/linked/create", property_view.linked_property_edit, {"id": None}, name="linked.property.create"), + # Relation property + path("property/relation/", property_view.relation_property_edit, name="relation.property.edit"), + path("property/relation/create", property_view.relation_property_edit, {"id": None}, name="relation.property.create"), # Relations - # path("relation/list", relation_view.relation_list, name="relation.list"), - # path("relation/", relation_view.relation_edit, name="relation.edit"), + path("relation/list", relation_view.relation_list, name="relation.list"), + path("relation/", relation_view.relation_edit, name="relation.edit"), path("relation//details", relation_view.relation_details, name="relation.details"), - # path("relation/create", relation_view.relation_edit, {"id": None}, name="relation.create"), + path("relation/create", relation_view.relation_edit, {"id": None}, name="relation.create"), ] diff --git a/k356/items/views/base.py b/k356/items/views/base.py index a21a0d6..fd54584 100644 --- a/k356/items/views/base.py +++ b/k356/items/views/base.py @@ -41,10 +41,16 @@ def generic_edit(model, request, id=None): continue if isinstance(field, RelatedField): - # For now, disregard related field (fk, m2m, 1-1) if isinstance(field, models.ForeignKey): - setattr(item, f"{field.name}_id", data[field.name]) + # Also allow for nested object (giving the full object instead of the id only) + if isinstance(data[field.name], dict): + setattr(item, f"{field.name}_id", data[field.name]["id"]) + + else: + setattr(item, f"{field.name}_id", data[field.name]) + + # For now, disregard m2m fields continue if field.name not in data: diff --git a/k356/items/views/item_view.py b/k356/items/views/item_view.py index 0e7da41..6e50a70 100644 --- a/k356/items/views/item_view.py +++ b/k356/items/views/item_view.py @@ -21,7 +21,7 @@ def item_list(request): "types": list(types.serialize()), "types_headers": header_for_table(ItemType), }, - "count": items.count(), + "count": items.count() + types.count(), } ) @@ -44,6 +44,7 @@ def item_details(request, id): return JsonResponse( { "object": item.serialize(), + "headers": header_for_table(Item), "parents": list(item.parents.serialize()), "parents_headers": header_for_table(ItemRelation), "children": list(item.children.serialize()), diff --git a/k356/items/views/property_view.py b/k356/items/views/property_view.py index 02d2032..b6eb83f 100644 --- a/k356/items/views/property_view.py +++ b/k356/items/views/property_view.py @@ -3,22 +3,28 @@ from django.http import JsonResponse from app.utils.api.api_list import header_for_table -from items.models import Property +from items.models import LinkedProperty, Property, RelationProperty from items.views.base import generic_edit @login_required def property_list(request): - items = Property.objects.filter(author=request.user.setting) + properties = Property.objects.filter(author=request.user.setting) + linked_properties = LinkedProperty.objects.filter(author=request.user.setting) + relation_properties = RelationProperty.objects.filter(author=request.user.setting) return JsonResponse( { "result": { - "properties": list(items.serialize()), + "properties": list(properties.serialize()), "properties_headers": header_for_table(Property), + "linked_properties": list(linked_properties.serialize()), + "linked_properties_headers": header_for_table(LinkedProperty), + "relation_properties": list(relation_properties.serialize()), + "relation_properties_headers": header_for_table(RelationProperty), }, - "count": items.count(), + "count": properties.count() + linked_properties.count() + relation_properties.count(), } ) @@ -28,3 +34,17 @@ def property_edit(request, id=None): """Create/edit property view.""" return generic_edit(Property, request, id) + + +@login_required +def linked_property_edit(request, id=None): + """Create/edit linked property view.""" + + return generic_edit(LinkedProperty, request, id) + + +@login_required +def relation_property_edit(request, id=None): + """Create/edit relation property view.""" + + return generic_edit(RelationProperty, request, id) diff --git a/k356/items/views/relation_view.py b/k356/items/views/relation_view.py index 10d1740..dfac7c5 100644 --- a/k356/items/views/relation_view.py +++ b/k356/items/views/relation_view.py @@ -4,6 +4,23 @@ from django.http import JsonResponse from app.utils.api.api_list import header_for_table from items.models import Item, ItemRelation, Property, RelationProperty +from items.views.base import generic_edit + + +@login_required +def relation_list(request): + + relations = ItemRelation.objects.filter(author=request.user.setting) + + return JsonResponse( + { + "result": { + "relations": list(relations.serialize()), + "headers": header_for_table(ItemRelation), + }, + "count": relations.count(), + } + ) @login_required @@ -27,3 +44,10 @@ def relation_details(request, id): "relation_properties_headers": header_for_table(RelationProperty), } ) + + +@login_required +def relation_edit(request, id=None): + """Create/edit relation view.""" + + return generic_edit(ItemRelation, request, id) diff --git a/k356/main/templates/components/Loading/vue.js b/k356/main/templates/components/Loading/vue.js index 3133aee..59a9d70 100644 --- a/k356/main/templates/components/Loading/vue.js +++ b/k356/main/templates/components/Loading/vue.js @@ -9,17 +9,13 @@ Loading = { } }, - mounted: function() { - // FIX: Remove this, this is the key for debugging - this.generate_aes_key('asd') - }, - methods: { async generate_aes_key (password) { - const key = await this.deriveKeyFromPassphrase(password, "{{ user_setting.id }}--aes") + const key = await this.deriveKeyFromPassphrase(password, "{{ user_setting.id }}--aes") this.$emit("update_key", key) + }, } diff --git a/k356/pyproject.toml b/k356/pyproject.toml index 63ddf66..017fa8b 100644 --- a/k356/pyproject.toml +++ b/k356/pyproject.toml @@ -62,6 +62,7 @@ authors = [] python = "^3.11" Django = "^5.0" django-bower = {git = "https://github.com/ArcaniteSolutions/django-bower.git"} +django-simple-history = "^3.7" [tool.poetry.dev-dependencies] black = "^24.4.0" diff --git a/k356/templates/base.html b/k356/templates/base.html index e4eb500..83605fc 100644 --- a/k356/templates/base.html +++ b/k356/templates/base.html @@ -41,7 +41,10 @@