Add history + more components
This commit is contained in:
parent
8f9c83dc2a
commit
d7f0d2a7f2
@ -157,10 +157,8 @@ USE_I18N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
||||
SIMPLE_HISTORY_HISTORY_ID_USE_UUID = True
|
||||
|
||||
|
||||
from app.settingsLocal import *
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.postgres.expressions import ArraySubquery
|
||||
from django.db import models
|
||||
from django.db.models import OuterRef
|
||||
from django.db.models.fields.related import RelatedField
|
||||
from django.db.models.functions import JSONObject
|
||||
|
||||
|
||||
from app.utils.helpers import recursive_getattr
|
||||
@ -78,7 +81,23 @@ class BaseQuerySet(models.QuerySet):
|
||||
for field_name, _ in self.headers().items():
|
||||
fields.append(field_name)
|
||||
|
||||
return self.values(*fields)
|
||||
if hasattr(self.model, "history"):
|
||||
qs = self.annotate(
|
||||
history=ArraySubquery(
|
||||
self.model.history.model.objects.filter(id=OuterRef("id")).values(
|
||||
json=JSONObject(
|
||||
id="history_id",
|
||||
date="history_date",
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
fields.append("history")
|
||||
|
||||
else:
|
||||
qs = self
|
||||
|
||||
return qs.values(*fields)
|
||||
|
||||
|
||||
class BaseManager(models.Manager.from_queryset(BaseQuerySet)):
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# Generated by Django 5.1.1 on 2024-10-01 13:41
|
||||
# Generated by Django 5.1.1 on 2024-10-01 14:14
|
||||
|
||||
import django.db.models.deletion
|
||||
import simple_history.models
|
||||
@ -23,7 +23,7 @@ class Migration(migrations.Migration):
|
||||
("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_id", models.UUIDField(default=uuid.uuid4, editable=False, 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)),
|
||||
@ -71,7 +71,7 @@ class Migration(migrations.Migration):
|
||||
("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_id", models.UUIDField(default=uuid.uuid4, editable=False, 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)),
|
||||
@ -120,7 +120,7 @@ class Migration(migrations.Migration):
|
||||
("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_id", models.UUIDField(default=uuid.uuid4, editable=False, 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)),
|
||||
@ -158,7 +158,7 @@ class Migration(migrations.Migration):
|
||||
("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_id", models.UUIDField(default=uuid.uuid4, editable=False, 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)),
|
||||
@ -234,7 +234,7 @@ class Migration(migrations.Migration):
|
||||
max_length=32,
|
||||
),
|
||||
),
|
||||
("history_id", models.AutoField(primary_key=True, serialize=False)),
|
||||
("history_id", models.UUIDField(default=uuid.uuid4, editable=False, 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)),
|
||||
@ -272,7 +272,7 @@ class Migration(migrations.Migration):
|
||||
("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_id", models.UUIDField(default=uuid.uuid4, editable=False, 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)),
|
||||
|
||||
@ -8,14 +8,82 @@
|
||||
<v-container fluid v-if="object">
|
||||
<template v-for="field in headers">
|
||||
<v-row v-if="field.details">
|
||||
|
||||
<template v-if="field.value == 'type'">
|
||||
<v-col cols="4">
|
||||
<v-subheader>[[ field.text ]]</v-subheader>
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
<v-text-field :value="object[field.value]" readonly dense></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="2" class="d-flex">
|
||||
<v-btn color="primary" dark class="mb-2 flex-grow-1 flex-shrink-1" @click="showType">
|
||||
<v-icon small class="mr-2">mdi-eye</v-icon>
|
||||
{% trans "Link" %}
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<v-col cols="4">
|
||||
<v-subheader>[[ field.text ]]</v-subheader>
|
||||
</v-col>
|
||||
<v-col cols="8">
|
||||
<v-text-field :value="object[field.value]" readonly dense></v-text-field>
|
||||
</v-col>
|
||||
</template>
|
||||
|
||||
</v-row>
|
||||
</template>
|
||||
|
||||
<v-row v-if="object?.history">
|
||||
<v-col cols="4">
|
||||
<v-subheader>{% trans "History version" %}</v-subheader>
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
<v-text-field :value="object.history.length" readonly dense></v-text-field>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="2" class="d-flex">
|
||||
<v-btn color="primary" dark class="mb-2 flex-grow-1 flex-shrink-1" @click="showHistory">
|
||||
<v-icon small class="mr-2">mdi-eye</v-icon>
|
||||
{% trans "View history" %}
|
||||
</v-btn>
|
||||
|
||||
<v-dialog v-model="dialog" max-width="1200px">
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
<span class="text-h5">{% trans "History for" %} [[ $route.params.id ]]</span>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-container>
|
||||
|
||||
<v-data-table
|
||||
:headers="headers.filter(i => i.value != 'id')"
|
||||
:items="history"
|
||||
:items-per-page="50"
|
||||
dense>
|
||||
<template v-slot:item.actions="{ item }">
|
||||
<v-icon small class="mr-2" @click="deleteVersion(item)">mdi-delete</v-icon>
|
||||
<v-icon small @click="useVersion(item)">mdi-content-save-edit</v-icon>
|
||||
</template>
|
||||
</v-data-table>
|
||||
|
||||
</v-container>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="blue darken-1" text @click="closeDialog">
|
||||
<v-icon small class="mr-2">mdi-arrow-left</v-icon>
|
||||
{% trans "Go back" %}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
||||
</div>
|
||||
|
||||
@ -3,6 +3,12 @@ ItemDetail = {
|
||||
router_path: "/ItemDetail/:id",
|
||||
delimiters: ["[[", "]]"],
|
||||
|
||||
data: function() {
|
||||
return {
|
||||
dialog: false,
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
object: function() {
|
||||
return this.$store.state.items.items.find(i => i.id == this.$route.params.id)
|
||||
@ -47,10 +53,51 @@ ItemDetail = {
|
||||
all_properties: function() {
|
||||
return this.$store.state.properties.items
|
||||
},
|
||||
|
||||
history: function() {
|
||||
return this.$store.state.history.items
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
async showHistory () {
|
||||
const response = await this.$http.get(Urls["items:history"](this.$route.params.id))
|
||||
|
||||
this.$store.state.history.headers = response.data.headers
|
||||
this.$store.dispatch("history/setItems", { self: this, items: response.data.history })
|
||||
this.dialog = true
|
||||
},
|
||||
|
||||
showType () {
|
||||
this.$router.push({ name: "ItemTypeDetail", params: { id: this.object.type }})
|
||||
},
|
||||
|
||||
closeDialog () {
|
||||
this.dialog = false
|
||||
this.$store.state.history.items = []
|
||||
},
|
||||
|
||||
async deleteVersion(version) {
|
||||
const response = await this.$http.delete(Urls["items:history.edit"](this.$route.params.id, version.history_id))
|
||||
|
||||
Swal.fire({title: "{{_('Version successfully deleted') | escapejs}}", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000})
|
||||
|
||||
this.closeDialog()
|
||||
},
|
||||
|
||||
async useVersion(version) {
|
||||
const response = await this.$http.post(Urls["items:history.edit"](this.$route.params.id, version.history_id))
|
||||
const efields = this.$store.getters["items/encryptedFields"]
|
||||
const new_item = await this.decryptObject(efields, response.data.object)
|
||||
|
||||
this.$store.commit("items/editItem", new_item)
|
||||
|
||||
Swal.fire({title: "{{_('Version successfully restored') | escapejs}}", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000})
|
||||
|
||||
this.closeDialog()
|
||||
},
|
||||
|
||||
linkedPropertyEdition (method, item) {
|
||||
return this.object_edit("items:linked.property.edit", "items:linked.property.create", 'linkedProperties', method, item)
|
||||
},
|
||||
|
||||
@ -12,11 +12,11 @@
|
||||
<v-subheader>[[ field.text ]]</v-subheader>
|
||||
</v-col>
|
||||
<template v-if="field.value == 'parent' || field.value == 'child'">
|
||||
<v-col cols="7">
|
||||
<v-col cols="6">
|
||||
<v-text-field :value="object[field.value]" readonly dense></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="1">
|
||||
<v-btn color="primary" dark class="mb-2" @click="showItem(object[field.value])">
|
||||
<v-col cols="2" class="d-flex">
|
||||
<v-btn color="primary" dark class="mb-2 flex-grow-1 flex-shrink-1" @click="showItem(object[field.value])">
|
||||
<v-icon small class="mr-2">mdi-eye</v-icon>
|
||||
{% trans "Link" %}
|
||||
</v-btn>
|
||||
|
||||
40
k356/items/templates/components/ItemTypeDetail/template.html
Normal file
40
k356/items/templates/components/ItemTypeDetail/template.html
Normal file
@ -0,0 +1,40 @@
|
||||
{% load i18n %}
|
||||
|
||||
<div>
|
||||
<div class="card mt-4 pt-2 ps-lg-2">
|
||||
<h5 class="card-header">{% trans "Type" %} [[ this.$route.params.id ]]</h5>
|
||||
<div class="card-body">
|
||||
|
||||
<v-container fluid v-if="object">
|
||||
<template v-for="field in headers">
|
||||
<v-row v-if="field.details">
|
||||
<v-col cols="4">
|
||||
<v-subheader>[[ field.text ]]</v-subheader>
|
||||
</v-col>
|
||||
<v-col cols="8">
|
||||
<v-text-field :value="object[field.value]" readonly dense></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
</v-container>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-4 pt-2 ps-lg-2">
|
||||
<h5 class="card-header">{% trans "Items" %}</h5>
|
||||
<div class="card-body">
|
||||
|
||||
<ItemList
|
||||
:items="all_items"
|
||||
:items_headers="items_headers"
|
||||
:items_relations="{'type': [object]}"
|
||||
group_by="type__name"
|
||||
@deleteItem="deleteItem"
|
||||
@createItem="createItem"
|
||||
@editItem="editItem"
|
||||
></ItemList>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
46
k356/items/templates/components/ItemTypeDetail/vue.js
Normal file
46
k356/items/templates/components/ItemTypeDetail/vue.js
Normal file
@ -0,0 +1,46 @@
|
||||
ItemTypeDetail = {
|
||||
template: "#ItemTypeDetail",
|
||||
router_path: "/ItemTypeDetail/:id",
|
||||
delimiters: ["[[", "]]"],
|
||||
|
||||
computed: {
|
||||
object: function() {
|
||||
return this.$store.state.types.items.find(i => i.id == this.$route.params.id)
|
||||
},
|
||||
|
||||
headers: function() {
|
||||
return this.$store.state.types.headers
|
||||
},
|
||||
|
||||
all_items: function() {
|
||||
return this.$store.state.items.items.filter(i => i.type == this.$route.params.id)
|
||||
},
|
||||
|
||||
items_headers: function() {
|
||||
return this.$store.state.items.headers
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
item_edition (method, item) {
|
||||
return this.object_edit("items:edit", "items:create", "items", method, item)
|
||||
},
|
||||
|
||||
async createItem (item) {
|
||||
const new_item = await this.item_edition("post", item)
|
||||
this.$store.commit("items/addItem", new_item)
|
||||
},
|
||||
|
||||
async editItem (item) {
|
||||
const new_item = await this.item_edition("post", item)
|
||||
this.$store.commit("items/editItem", new_item)
|
||||
},
|
||||
|
||||
async deleteItem (item) {
|
||||
await this.item_edition("delete", item)
|
||||
this.$store.commit("items/removeItem", item.id)
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
@ -26,6 +26,7 @@
|
||||
:items="types"
|
||||
:items_headers="types_headers"
|
||||
group_by="[]"
|
||||
show_url="ItemTypeDetail"
|
||||
@deleteItem="deleteType"
|
||||
@createItem="createType"
|
||||
@editItem="editType"
|
||||
|
||||
@ -11,6 +11,8 @@ urlpatterns = [
|
||||
path("list", item_view.item_list, name="list"),
|
||||
path("<uuid:id>", item_view.item_edit, name="edit"),
|
||||
path("<uuid:id>/details", item_view.item_details, name="details"),
|
||||
path("<uuid:id>/history", item_view.item_history, name="history"),
|
||||
path("<uuid:id>/history/<uuid:hid>", item_view.item_history_edit, name="history.edit"),
|
||||
path("create", item_view.item_edit, {"id": None}, name="create"),
|
||||
# Type
|
||||
path("type/<uuid:id>", type_view.type_edit, name="type.edit"),
|
||||
|
||||
@ -55,3 +55,62 @@ def item_details(request, id):
|
||||
"linked_properties_headers": header_for_table(LinkedProperty),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
def item_history(request, id):
|
||||
|
||||
item = Item.objects.filter(author=request.user.setting, id=id).first()
|
||||
|
||||
if not item:
|
||||
return JsonResponse({}, status=404)
|
||||
|
||||
headers = [field for field in Item.objects.headers().keys() if field not in ["id", "history"]]
|
||||
headers.extend(["history_id", "history_date"])
|
||||
|
||||
qs = item.history.order_by("-history_date").values(*headers)
|
||||
return JsonResponse(
|
||||
{
|
||||
"object": item.serialize(),
|
||||
"headers": header_for_table(Item),
|
||||
# Return only the last 50 versions
|
||||
"history": list(qs[:50]),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
def item_history_edit(request, id, hid):
|
||||
|
||||
item = Item.objects.filter(author=request.user.setting, id=id).first()
|
||||
|
||||
if not item:
|
||||
return JsonResponse({}, status=404)
|
||||
|
||||
version = item.history.filter(history_user=request.user.setting, history_id=hid).first()
|
||||
|
||||
if not version:
|
||||
return JsonResponse({}, status=404)
|
||||
|
||||
if request.method == "DELETE":
|
||||
try:
|
||||
version.delete()
|
||||
except Exception:
|
||||
return JsonResponse({}, status=401)
|
||||
|
||||
return JsonResponse({})
|
||||
|
||||
if request.method == "POST":
|
||||
try:
|
||||
version.instance.save(force_update=True)
|
||||
|
||||
except Exception:
|
||||
return JsonResponse({}, status=401)
|
||||
|
||||
return JsonResponse(
|
||||
{
|
||||
"object": version.instance.serialize(),
|
||||
}
|
||||
)
|
||||
|
||||
return JsonResponse({}, status=405)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{% load i18n %}
|
||||
|
||||
<div class="card bg-warning mt-4 pt-2 ps-lg-2">
|
||||
<div class="card bg-warning mt-4 pt-2 ps-lg-2" v-if="ready">
|
||||
<h5 class="card-header">{% trans "K356 is locked" %}</h5>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{% trans "K356 needs an unlock..." %}</h5>
|
||||
@ -8,3 +8,7 @@
|
||||
<input class="form-control" type="password" v-model="password" @keyup.enter="generate_aes_key(password)" autofocus>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card bg-danger mt-4 pt-2 ps-lg-2 d-flex" v-else>
|
||||
<h5 class="card-header flex-grow-1 flex-shrink-1 text-center">{% trans "Cannot load the application. Webcrypto must be enabled" %}</h5>
|
||||
</div>
|
||||
|
||||
@ -9,6 +9,10 @@ Loading = {
|
||||
}
|
||||
},
|
||||
|
||||
mounted: function() {
|
||||
this.generate_aes_key("asd")
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
async generate_aes_key (password) {
|
||||
|
||||
@ -82,12 +82,12 @@
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="app" class="container">
|
||||
<v-container class="container-xs">
|
||||
<Loading @update_key="update_key" v-if="locked"></Loading>
|
||||
<template v-if="!locked">
|
||||
<router-view></router-view>
|
||||
</template>
|
||||
</div>
|
||||
</v-container>
|
||||
|
||||
</div>
|
||||
</v-app>
|
||||
|
||||
@ -4,9 +4,9 @@
|
||||
<v-data-table
|
||||
:headers="citems_headers"
|
||||
:items="items"
|
||||
:items-per-page="50"
|
||||
:search="search"
|
||||
:group-by="group_by"
|
||||
:items-per-page="20"
|
||||
loading
|
||||
dense>
|
||||
<template v-slot:top>
|
||||
@ -121,8 +121,11 @@
|
||||
</template>
|
||||
|
||||
<template v-slot:item.id="{ item }">
|
||||
<v-btn plain @click="showItem(item)">
|
||||
[[ item.id.slice(0, 8) ]]...
|
||||
</v-btn>
|
||||
</template>
|
||||
|
||||
<template v-slot:item.description="{ item }">
|
||||
<template v-if="item.description && item.description.length > 15">
|
||||
[[ item.description.slice(0, 15) ]]...
|
||||
@ -131,9 +134,11 @@
|
||||
[[ item.description ]]
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<template v-slot:item.last_modified_at="{ item }">
|
||||
[[ formatDate(item.last_modified_at) ]]
|
||||
</template>
|
||||
|
||||
<template v-slot:item.created_at="{ item }">
|
||||
[[ formatDate(item.created_at) ]]
|
||||
</template>
|
||||
|
||||
@ -35,6 +35,7 @@ const approuter = new Vue({
|
||||
el: "#main",
|
||||
data: {
|
||||
uuid: "{{ user_settings.id }}",
|
||||
ready: null,
|
||||
},
|
||||
|
||||
computed: {
|
||||
@ -43,7 +44,33 @@ const approuter = new Vue({
|
||||
}
|
||||
},
|
||||
|
||||
mounted: function() {},
|
||||
async created () {
|
||||
|
||||
if (operations == undefined) {
|
||||
|
||||
Swal.fire({title: "{{_('The application cannot be launched. Webcrypto is not available.<br><br>Try another browser!') | escapejs}}", icon: "error", showConfirmButton: false})
|
||||
|
||||
this.ready = false
|
||||
|
||||
} else {
|
||||
|
||||
// Try to generate a random keyPair and encrypting stuff before accepting the client
|
||||
try {
|
||||
|
||||
const keyPair = await this.generateKeyPair()
|
||||
|
||||
this.ready = true
|
||||
|
||||
} catch (err) {
|
||||
|
||||
Swal.fire({title: "{{_('An error occured during testing Webcrypto. Please use a compatible browser.') | escapejs}}", icon: "error", showConfirmButton: false})
|
||||
|
||||
this.ready = false
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
async load_keys (aes_key) {
|
||||
@ -63,7 +90,7 @@ const approuter = new Vue({
|
||||
|
||||
this.$store.commit('encryption/updateKeyPair', keyPair)
|
||||
|
||||
Swal.fire({title: "Successfully loaded K356!", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000});
|
||||
Swal.fire({title: "{{_('Successfully loaded K356!') | escapejs}}", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000});
|
||||
|
||||
} else {
|
||||
|
||||
@ -79,7 +106,7 @@ const approuter = new Vue({
|
||||
|
||||
this.$store.commit('encryption/updateKeyPair', keyPair)
|
||||
|
||||
Swal.fire({title: "Successfully created K356!", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000});
|
||||
Swal.fire({title: "{{_('Successfully created K356!') | escapejs}}", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000});
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -94,6 +94,7 @@ const relationStore = {__proto__: storeMixin, namespaced: true}
|
||||
const propertyStore = {__proto__: storeMixin, namespaced: true}
|
||||
const linkedPropertyStore = {__proto__: storeMixin, namespaced: true}
|
||||
const relationPropertyStore = {__proto__: storeMixin, namespaced: true}
|
||||
const history = {__proto__: storeMixin, namespaced: true}
|
||||
|
||||
const store = new Vuex.Store({
|
||||
modules: {
|
||||
@ -104,5 +105,6 @@ const store = new Vuex.Store({
|
||||
properties: propertyStore,
|
||||
linkedProperties: linkedPropertyStore,
|
||||
relationProperties: relationPropertyStore,
|
||||
history: history,
|
||||
}
|
||||
})
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# Generated by Django 5.1.1 on 2024-10-01 13:41
|
||||
# Generated by Django 5.1.1 on 2024-10-01 14:14
|
||||
|
||||
import django.db.models.deletion
|
||||
import simple_history.models
|
||||
@ -26,7 +26,7 @@ class Migration(migrations.Migration):
|
||||
("last_modified_at", models.DateTimeField(blank=True, editable=False)),
|
||||
("public_key", models.TextField(max_length=2048, null=True)),
|
||||
("private_key", models.TextField(max_length=2048, null=True)),
|
||||
("history_id", models.AutoField(primary_key=True, serialize=False)),
|
||||
("history_id", models.UUIDField(default=uuid.uuid4, editable=False, 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)),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user