patches v1.7.2 #115

Merged
jkeffects merged 8 commits from develop into main 2025-07-18 14:15:54 +00:00
11 changed files with 238 additions and 45 deletions

105
package-lock.json generated
View file

@ -38,6 +38,7 @@
"pwacompat": "^2.0.17",
"qrcode": "^1.5.4",
"qs": "^6.14.0",
"quill": "^2.0.3",
"socket.io-client": "^4.8.1",
"unplugin-vue-markdown": "^29.1.0",
"uuid": "^11.1.0",
@ -4652,6 +4653,52 @@
"vue": "^3.2.41"
}
},
"node_modules/@vueup/vue-quill/node_modules/eventemitter3": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
"integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==",
"license": "MIT"
},
"node_modules/@vueup/vue-quill/node_modules/fast-diff": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz",
"integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==",
"license": "Apache-2.0"
},
"node_modules/@vueup/vue-quill/node_modules/parchment": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz",
"integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==",
"license": "BSD-3-Clause"
},
"node_modules/@vueup/vue-quill/node_modules/quill": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz",
"integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==",
"license": "BSD-3-Clause",
"dependencies": {
"clone": "^2.1.1",
"deep-equal": "^1.0.1",
"eventemitter3": "^2.0.3",
"extend": "^3.0.2",
"parchment": "^1.1.4",
"quill-delta": "^3.6.2"
}
},
"node_modules/@vueup/vue-quill/node_modules/quill/node_modules/quill-delta": {
"version": "3.6.3",
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz",
"integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==",
"license": "MIT",
"dependencies": {
"deep-equal": "^1.0.1",
"extend": "^3.0.2",
"fast-diff": "1.1.2"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/acorn": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
@ -6219,9 +6266,9 @@
"license": "MIT"
},
"node_modules/eventemitter3": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
"integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
"license": "MIT"
},
"node_modules/execa": {
@ -6280,7 +6327,6 @@
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
"integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/fast-glob": {
@ -8129,6 +8175,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
"license": "MIT"
},
"node_modules/lodash.clonedeep": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
@ -8710,9 +8762,9 @@
}
},
"node_modules/parchment": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz",
"integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/parchment/-/parchment-3.0.0.tgz",
"integrity": "sha512-HUrJFQ/StvgmXRcQ1ftY6VEZUq3jA2t9ncFN4F84J/vN0/FPpQF+8FKXb3l6fLces6q0uOHj6NJn+2xvZnxO6A==",
"license": "BSD-3-Clause"
},
"node_modules/parent-module": {
@ -9111,17 +9163,18 @@
"license": "MIT"
},
"node_modules/quill": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz",
"integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==",
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/quill/-/quill-2.0.3.tgz",
"integrity": "sha512-xEYQBqfYx/sfb33VJiKnSJp8ehloavImQ2A6564GAbqG55PGw1dAWUn1MUbQB62t0azawUS2CZZhWCjO8gRvTw==",
"license": "BSD-3-Clause",
"dependencies": {
"clone": "^2.1.1",
"deep-equal": "^1.0.1",
"eventemitter3": "^2.0.3",
"extend": "^3.0.2",
"parchment": "^1.1.4",
"quill-delta": "^3.6.2"
"eventemitter3": "^5.0.1",
"lodash-es": "^4.17.21",
"parchment": "^3.0.0",
"quill-delta": "^5.1.0"
},
"engines": {
"npm": ">=8.2.3"
}
},
"node_modules/quill-delta": {
@ -9141,24 +9194,18 @@
"integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
"license": "Apache-2.0"
},
"node_modules/quill/node_modules/fast-diff": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz",
"integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==",
"license": "Apache-2.0"
},
"node_modules/quill/node_modules/quill-delta": {
"version": "3.6.3",
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz",
"integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-5.1.0.tgz",
"integrity": "sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==",
"license": "MIT",
"dependencies": {
"deep-equal": "^1.0.1",
"extend": "^3.0.2",
"fast-diff": "1.1.2"
"fast-diff": "^1.3.0",
"lodash.clonedeep": "^4.5.0",
"lodash.isequal": "^4.5.0"
},
"engines": {
"node": ">=0.10"
"node": ">= 12.0.0"
}
},
"node_modules/randombytes": {

View file

@ -53,6 +53,7 @@
"pwacompat": "^2.0.17",
"qrcode": "^1.5.4",
"qs": "^6.14.0",
"quill": "^2.0.3",
"socket.io-client": "^4.8.1",
"unplugin-vue-markdown": "^29.1.0",
"uuid": "^11.1.0",

View file

@ -0,0 +1,151 @@
<template>
<div :id="id" :style="style" class="flex flex-col">
<div ref="quill"></div>
</div>
</template>
<script setup lang="ts">
import { markRaw, onMounted, onUnmounted, ref, useTemplateRef, watch, type PropType } from "vue";
import Quill, { Delta, type QuillOptions } from "quill";
import "quill/dist/quill.core.css";
import "quill/dist/quill.snow.css";
import isEqual from "lodash.isequal";
import cloneDeep from "lodash.clonedeep";
type RangeStatic = { index: number; length: number };
const quillElement = useTemplateRef("quill");
const instance = ref<undefined | Quill>();
const model = ref<string | Delta>("");
const props = defineProps({
id: String,
style: [String, Object],
options: {
type: Object as PropType<QuillOptions>,
default: {},
},
toolbar: {
type: [String, Array, Object],
default: "",
},
content: {
type: [String, Object] as PropType<string | Delta | null>,
default: "",
},
contentType: {
type: String as PropType<"delta" | "html" | "text">,
default: "text",
validator: (value: string) => {
return ["delta", "html", "text"].includes(value);
},
},
readonly: { type: Boolean, default: false },
placeholder: String,
});
const emit = defineEmits({
"update:content": (content: Delta | string) => true,
textChange: (tc: { delta: Delta; oldDelta: Delta; source: string }) => true,
selectionChange: (sc: { range: RangeStatic; oldRange: RangeStatic; source: string }) => true,
focus: () => true,
blur: () => true,
ready: (quill: Quill) => true,
});
watch(
() => props.content,
(val, oldVal) => {
if (!instance.value || !val || isEqual(val, model.value)) return;
setContent(cloneDeep(val));
},
{ deep: true }
);
watch(
() => props.readonly,
(val, oldVal) => {
instance.value?.enable(val);
}
);
watch(model, (val, oldVal) => {
emit("update:content", cloneDeep(val));
});
onMounted(() => {
if (!quillElement.value) return;
const quill = new Quill(quillElement.value, {
theme: "snow",
modules: {
toolbar: props.toolbar,
},
placeholder: props.placeholder,
readOnly: props.readonly,
...props.options,
});
quill.on(Quill.events.SELECTION_CHANGE, (range, oldRange, source) => {
if (!range) {
emit("blur");
} else {
emit("focus");
}
emit("selectionChange", { range, oldRange, source });
});
quill.on(Quill.events.TEXT_CHANGE, (delta, oldDelta, source) => {
model.value = getContent();
emit("textChange", { delta, oldDelta, source });
});
emit("ready", quill);
instance.value = markRaw(quill);
setContent(cloneDeep(props.content ?? ""));
});
onUnmounted(() => {
if (instance.value) {
instance.value.off(Quill.events.SELECTION_CHANGE);
instance.value.off(Quill.events.TEXT_CHANGE);
instance.value = undefined;
}
});
function getContent(): string | Delta {
if (props.contentType === "delta") {
return (instance.value?.getContents() ?? {}) as Delta;
} else if (props.contentType === "html") {
return instance.value?.getSemanticHTML().replace(/&nbsp;/g, " ") ?? "";
} else {
return instance.value?.getText().replace(/&nbsp;/g, " ") ?? "";
}
}
function setContent(content: Delta | string) {
if (content == "") return;
if (props.contentType === "delta") {
if (typeof content !== "string") {
instance.value?.setContents(content);
}
} else if (props.contentType === "html") {
if (typeof content === "string" && instance.value) {
instance.value.clipboard.dangerouslyPasteHTML(content);
let delta = instance.value.clipboard.convert({ html: content });
instance.value?.setContents(delta);
}
} else {
if (typeof content === "string") {
instance.value?.setText(content);
}
}
}
</script>
<style>
.ql-container {
border-bottom-left-radius: 0.5em;
border-bottom-right-radius: 0.5em;
}
.ql-toolbar {
background: var(--color-gray-100);
border-top-left-radius: 0.5em;
border-top-right-radius: 0.5em;
}
</style>

View file

@ -52,8 +52,7 @@ import { defineComponent } from "vue";
import { mapActions, mapState, mapWritableState } from "pinia";
import Spinner from "@/components/Spinner.vue";
import { useNewsletterStore } from "@/stores/admin/club/newsletter/newsletter";
import { QuillEditor } from "@vueup/vue-quill";
import "@vueup/vue-quill/dist/vue-quill.snow.css";
import QuillEditor from "@/components/QuillEditor.vue";
import { toolbarOptions } from "@/helpers/quillConfig";
import { useAbilityStore } from "@/stores/ability";
</script>

View file

@ -107,8 +107,7 @@
import { defineComponent } from "vue";
import { mapActions, mapState, mapWritableState } from "pinia";
import Spinner from "@/components/Spinner.vue";
import { QuillEditor } from "@vueup/vue-quill";
import "@vueup/vue-quill/dist/vue-quill.snow.css";
import QuillEditor from "@/components/QuillEditor.vue";
import { toolbarOptions } from "@/helpers/quillConfig";
import { useNewsletterDatesStore } from "@/stores/admin/club/newsletter/newsletterDates";
import { useAbilityStore } from "@/stores/ability";

View file

@ -18,13 +18,12 @@
<label for="summary">Zusammenfassung</label>
<QuillEditor
id="summary"
theme="snow"
placeholder="Zusammenfassung zum Newsletter..."
style="height: 250px; max-height: 250px; min-height: 250px"
contentType="html"
:toolbar="toolbarOptions"
v-model:content="activeNewsletterObj.description"
:enable="can('create', 'club', 'newsletter')"
:readonly="!can('create', 'club', 'newsletter')"
:style="!can('create', 'club', 'newsletter') ? 'opacity: 75%; background: rgb(243 244 246)' : ''"
/>
</div>
@ -42,8 +41,7 @@ import { defineComponent } from "vue";
import { mapActions, mapState, mapWritableState } from "pinia";
import Spinner from "@/components/Spinner.vue";
import { useNewsletterStore } from "@/stores/admin/club/newsletter/newsletter";
import { QuillEditor } from "@vueup/vue-quill";
import "@vueup/vue-quill/dist/vue-quill.snow.css";
import QuillEditor from "@/components/QuillEditor.vue";
import { toolbarOptions } from "@/helpers/quillConfig";
import { useAbilityStore } from "@/stores/ability";
</script>

View file

@ -78,8 +78,7 @@
import { defineComponent } from "vue";
import { mapActions, mapState, mapWritableState } from "pinia";
import Spinner from "@/components/Spinner.vue";
import { QuillEditor } from "@vueup/vue-quill";
import "@vueup/vue-quill/dist/vue-quill.snow.css";
import QuillEditor from "@/components/QuillEditor.vue";
import { toolbarOptions } from "@/helpers/quillConfig";
import { useProtocolAgendaStore } from "@/stores/admin/club/protocol/protocolAgenda";
import { useAbilityStore } from "@/stores/ability";

View file

@ -78,8 +78,7 @@
import { defineComponent } from "vue";
import { mapActions, mapState, mapWritableState } from "pinia";
import Spinner from "@/components/Spinner.vue";
import { QuillEditor } from "@vueup/vue-quill";
import "@vueup/vue-quill/dist/vue-quill.snow.css";
import QuillEditor from "@/components/QuillEditor.vue";
import { toolbarOptions } from "@/helpers/quillConfig";
import { useProtocolDecisionStore } from "@/stores/admin/club/protocol/protocolDecision";
import { useAbilityStore } from "@/stores/ability";

View file

@ -64,8 +64,7 @@ import { defineComponent } from "vue";
import { mapActions, mapState, mapWritableState } from "pinia";
import Spinner from "@/components/Spinner.vue";
import { useProtocolStore } from "@/stores/admin/club/protocol/protocol";
import { QuillEditor } from "@vueup/vue-quill";
import "@vueup/vue-quill/dist/vue-quill.snow.css";
import QuillEditor from "@/components/QuillEditor.vue";
import { toolbarOptions } from "@/helpers/quillConfig";
import { useAbilityStore } from "@/stores/ability";
</script>

View file

@ -95,8 +95,7 @@
import { defineComponent } from "vue";
import { mapActions, mapState, mapWritableState } from "pinia";
import Spinner from "@/components/Spinner.vue";
import { QuillEditor } from "@vueup/vue-quill";
import "@vueup/vue-quill/dist/vue-quill.snow.css";
import QuillEditor from "@/components/QuillEditor.vue";
import { toolbarOptions } from "@/helpers/quillConfig";
import { useProtocolVotingStore } from "@/stores/admin/club/protocol/protocolVoting";
import { useAbilityStore } from "@/stores/ability";

View file

@ -41,6 +41,8 @@ export default defineConfig({
injectRegister: "auto",
manifest: false,
workbox: {
cleanupOutdatedCaches: true,
skipWaiting: true,
navigateFallbackDenylist: [/^\/api*/],
runtimeCaching: [
{