decisions and voting

This commit is contained in:
Julian Krauser 2024-10-18 14:20:58 +02:00
parent c0bfc00862
commit d62436722a
6 changed files with 246 additions and 34 deletions

View file

@ -20,6 +20,8 @@ import { useProtocolStore } from "@/stores/admin/protocol";
import { ArrowPathIcon, CloudArrowUpIcon, CloudIcon, ExclamationTriangleIcon } from "@heroicons/vue/24/outline";
import { useProtocolAgendaStore } from "@/stores/admin/protocolAgenda";
import { useProtocolPresenceStore } from "@/stores/admin/protocolPresence";
import { useProtocolDecisionStore } from "../../../../stores/admin/protocolDecision";
import { useProtocolVotingStore } from "../../../../stores/admin/protocolVoting";
</script>
<script lang="ts">
@ -44,7 +46,6 @@ export default defineComponent({
}, 10000);
},
detectedChangeProtocolAgenda() {
console.log(this.detectedChangeProtocolAgenda);
clearTimeout(this.protocolAgendaTimer);
if (this.detectedChangeProtocolAgenda == false) {
this.setProtocolAgendaSyncingState("synced");
@ -66,6 +67,28 @@ export default defineComponent({
this.synchronizeActiveProtocolPresence();
}, 10000);
},
detectedChangeProtocolDecision() {
clearTimeout(this.protocolDecisionTimer);
this.setProtocolDecisionSyncingState("synced");
if (this.detectedChangeProtocolDecision == false) {
return;
}
this.setProtocolDecisionSyncingState("detectedChanges");
this.protocolDecisionTimer = setTimeout(() => {
this.synchronizeActiveProtocolDecision();
}, 10000);
},
detectedChangeProtocolVoting() {
clearTimeout(this.protocolVotingTimer);
this.setProtocolVotingSyncingState("synced");
if (this.detectedChangeProtocolVoting == false) {
return;
}
this.setProtocolVotingSyncingState("detectedChanges");
this.protocolVotingTimer = setTimeout(() => {
this.synchronizeActiveProtocolVoting();
}, 10000);
},
},
emits: {
syncState(state: "synced" | "syncing" | "detectedChanges" | "failed") {
@ -77,6 +100,8 @@ export default defineComponent({
protocolTimer: undefined as undefined | any,
protocolAgendaTimer: undefined as undefined | any,
protocolPresenceTimer: undefined as undefined | any,
protocolDecisionTimer: undefined as undefined | any,
protocolVotingTimer: undefined as undefined | any,
};
},
mounted() {
@ -86,14 +111,24 @@ export default defineComponent({
if (!this.protocolTimer) clearTimeout(this.protocolTimer);
if (!this.protocolAgendaTimer) clearTimeout(this.protocolAgendaTimer);
if (!this.protocolPresenceTimer) clearTimeout(this.protocolPresenceTimer);
if (!this.protocolDecisionTimer) clearTimeout(this.protocolDecisionTimer);
if (!this.protocolVotingTimer) clearTimeout(this.protocolVotingTimer);
},
computed: {
...mapState(useProtocolStore, ["syncingProtocol", "detectedChangeProtocol"]),
...mapState(useProtocolAgendaStore, ["syncingProtocolAgenda", "detectedChangeProtocolAgenda"]),
...mapState(useProtocolPresenceStore, ["syncingProtocolPresence", "detectedChangeProtocolPresence"]),
...mapState(useProtocolDecisionStore, ["syncingProtocolDecision", "detectedChangeProtocolDecision"]),
...mapState(useProtocolVotingStore, ["syncingProtocolVoting", "detectedChangeProtocolVoting"]),
syncing(): "synced" | "syncing" | "detectedChanges" | "failed" {
let states = [this.syncingProtocol, this.syncingProtocolAgenda, this.syncingProtocolPresence];
let states = [
this.syncingProtocol,
this.syncingProtocolAgenda,
this.syncingProtocolPresence,
this.syncingProtocolDecision,
this.syncingProtocolVoting,
];
if (states.includes("failed")) return "failed";
else if (states.includes("syncing")) return "syncing";
@ -105,13 +140,21 @@ export default defineComponent({
...mapActions(useProtocolStore, ["synchronizeActiveProtocol", "setProtocolSyncingState"]),
...mapActions(useProtocolAgendaStore, ["synchronizeActiveProtocolAgenda", "setProtocolAgendaSyncingState"]),
...mapActions(useProtocolPresenceStore, ["synchronizeActiveProtocolPresence", "setProtocolPresenceSyncingState"]),
...mapActions(useProtocolDecisionStore, ["synchronizeActiveProtocolDecision", "setProtocolDecisionSyncingState"]),
...mapActions(useProtocolVotingStore, ["synchronizeActiveProtocolVoting", "setProtocolVotingSyncingState"]),
syncAll() {
if (!this.protocolTimer) clearTimeout(this.protocolTimer);
if (!this.protocolAgendaTimer) clearTimeout(this.protocolAgendaTimer);
if (!this.protocolPresenceTimer) clearTimeout(this.protocolPresenceTimer);
if (!this.protocolDecisionTimer) clearTimeout(this.protocolDecisionTimer);
if (!this.protocolVotingTimer) clearTimeout(this.protocolVotingTimer);
this.synchronizeActiveProtocol();
this.synchronizeActiveProtocolAgenda();
this.synchronizeActiveProtocolPresence();
this.synchronizeActiveProtocolDecision();
this.synchronizeActiveProtocolVoting();
},
},
});

View file

@ -52,7 +52,7 @@ export const useProtocolAgendaStore = defineStore("protocolAgenda", {
id: res.data,
topic: "",
context: "",
protocolId,
protocolId: Number(protocolId),
});
})
.catch((err) => {});
@ -60,7 +60,6 @@ export const useProtocolAgendaStore = defineStore("protocolAgenda", {
async synchronizeActiveProtocolAgenda() {
this.syncingProtocolAgenda = "syncing";
const protocolId = useProtocolStore().activeProtocol;
console.log(this.agenda, this.origin, differenceWith(this.agenda, this.origin, isEqual));
await http
.patch(`/admin/protocol/${protocolId}/synchronize/agenda`, {

View file

@ -8,38 +8,75 @@ import type {
import { useProtocolStore } from "./protocol";
import cloneDeep from "lodash.clonedeep";
import isEqual from "lodash.isEqual";
import difference from "lodash.difference";
import differenceWith from "lodash.differencewith";
export const useProtocolDecisionStore = defineStore("protocolDecision", {
state: () => {
return {
decision: [] as Array<ProtocolDecisionViewModel>,
origin: [] as Array<ProtocolDecisionViewModel>,
loading: "loading" as "loading" | "fetched" | "failed",
syncingProtocolDecision: "synced" as "synced" | "syncing" | "detectedChanges" | "failed",
};
},
getters: {
detectedChangeProtocolDecision: (state) =>
!isEqual(state.origin, state.decision) && state.syncingProtocolDecision != "syncing",
},
actions: {
setProtocolDecisionSyncingState(state: "synced" | "syncing" | "detectedChanges" | "failed") {
this.syncingProtocolDecision = state;
},
fetchProtocolDecision() {
const protocolId = useProtocolStore().activeProtocol;
this.loading = "loading";
http
.get(`/admin/protocol/${protocolId}/decisions`)
this.fetchProtocolDecisionPromise()
.then((result) => {
this.decision = result.data;
this.origin = result.data;
this.decision = cloneDeep(this.origin);
this.loading = "fetched";
})
.catch((err) => {
this.loading = "failed";
});
},
async synchronizeActiveProtocolDecision(
decision: Array<SyncProtocolDecisionViewModel>
): Promise<AxiosResponse<any, any>> {
fetchProtocolDecisionPromise() {
const protocolId = useProtocolStore().activeProtocol;
const result = await http.patch(`/admin/protocol/${protocolId}/synchronize/decisions`, {
decision: decision,
return http.get(`/admin/protocol/${protocolId}/decisions`);
},
createProtocolDecision() {
const protocolId = useProtocolStore().activeProtocol;
if (protocolId == null) return;
return http
.post(`/admin/protocol/${protocolId}/decision`)
.then((res) => {
this.decision.push({
id: res.data,
topic: "",
context: "",
protocolId: Number(protocolId),
});
this.fetchProtocolDecision();
return result;
})
.catch((err) => {});
},
async synchronizeActiveProtocolDecision() {
this.syncingProtocolDecision = "syncing";
const protocolId = useProtocolStore().activeProtocol;
await http
.patch(`/admin/protocol/${protocolId}/synchronize/decisions`, {
decisions: differenceWith(this.decision, this.origin, isEqual),
})
.then((res) => {
this.syncingProtocolDecision = "synced";
})
.catch((err) => {
this.syncingProtocolDecision = "failed";
});
this.fetchProtocolDecisionPromise()
.then((res) => {
this.origin = res.data;
})
.catch((err) => {});
},
},
});

View file

@ -8,38 +8,78 @@ import type {
import { useProtocolStore } from "./protocol";
import cloneDeep from "lodash.clonedeep";
import isEqual from "lodash.isEqual";
import difference from "lodash.difference";
import differenceWith from "lodash.differencewith";
export const useProtocolVotingStore = defineStore("protocolVoting", {
state: () => {
return {
voting: [] as Array<ProtocolVotingViewModel>,
origin: [] as Array<ProtocolVotingViewModel>,
loading: "loading" as "loading" | "fetched" | "failed",
syncingProtocolVoting: "synced" as "synced" | "syncing" | "detectedChanges" | "failed",
};
},
getters: {
detectedChangeProtocolVoting: (state) =>
!isEqual(state.origin, state.voting) && state.syncingProtocolVoting != "syncing",
},
actions: {
setProtocolVotingSyncingState(state: "synced" | "syncing" | "detectedChanges" | "failed") {
this.syncingProtocolVoting = state;
},
fetchProtocolVoting() {
const protocolId = useProtocolStore().activeProtocol;
this.loading = "loading";
http
.get(`/admin/protocol/${protocolId}/votings`)
this.fetchProtocolVotingPromise()
.then((result) => {
this.voting = result.data;
this.origin = result.data;
this.voting = cloneDeep(this.origin);
this.loading = "fetched";
})
.catch((err) => {
this.loading = "failed";
});
},
async synchronizeActiveProtocolVoting(
voting: Array<SyncProtocolVotingViewModel>
): Promise<AxiosResponse<any, any>> {
fetchProtocolVotingPromise() {
const protocolId = useProtocolStore().activeProtocol;
const result = await http.patch(`/admin/protocol/${protocolId}/synchronize/votings`, {
voting: voting,
return http.get(`/admin/protocol/${protocolId}/votings`);
},
createProtocolVoting() {
const protocolId = useProtocolStore().activeProtocol;
if (protocolId == null) return;
return http
.post(`/admin/protocol/${protocolId}/voting`)
.then((res) => {
this.voting.push({
id: res.data,
topic: "",
context: "",
favour: 0,
abstain: 0,
against: 0,
protocolId: Number(protocolId),
});
this.fetchProtocolVoting();
return result;
})
.catch((err) => {});
},
async synchronizeActiveProtocolVoting() {
this.syncingProtocolVoting = "syncing";
const protocolId = useProtocolStore().activeProtocol;
await http
.patch(`/admin/protocol/${protocolId}/synchronize/votings`, {
votings: differenceWith(this.voting, this.origin, isEqual),
})
.then((res) => {
this.syncingProtocolVoting = "synced";
})
.catch((err) => {
this.syncingProtocolVoting = "failed";
});
this.fetchProtocolVotingPromise()
.then((res) => {
this.origin = res.data;
})
.catch((err) => {});
},
},
});

View file

@ -4,12 +4,50 @@
<p v-else-if="loading == 'failed'" @click="fetchProtocolDecision" class="cursor-pointer">
&#8634; laden fehlgeschlagen
</p>
<div class="flex flex-col gap-2 h-full overflow-y-auto">
<details
v-for="item in decision"
class="flex flex-col gap-2 rounded-lg w-full justify-between border border-primary overflow-hidden min-h-fit"
>
<summary class="flex flex-row gap-2 bg-primary p-2 w-full justify-between items-center cursor-pointer">
<svg
class="fill-white stroke-white opacity-75 w-4 h-4"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
>
<path d="M12.95 10.707l.707-.707L8 4.343 6.586 5.757 10.828 10l-4.242 4.243L8 15.657l4.95-4.95z" />
</svg>
<input
type="text"
name="title"
id="title"
placeholder="Einscheidung"
autocomplete="off"
v-model="item.topic"
@keyup.prevent
/>
</summary>
<QuillEditor
id="top"
theme="snow"
placeholder="Entscheidung Inhalt..."
style="height: 250px; max-height: 250px; min-height: 250px"
contentType="html"
:toolbar="toolbarOptions"
v-model:content="item.context"
/>
</details>
</div>
<button primary class="!w-fit" @click="createProtocolDecision">Eintrag hinzufügen</button>
</div>
</template>
<script setup lang="ts">
import { defineComponent } from "vue";
import { mapActions, mapState } from "pinia";
import { mapActions, mapState, mapWritableState } from "pinia";
import Spinner from "@/components/Spinner.vue";
import { useProtocolStore } from "@/stores/admin/protocol";
import { QuillEditor } from "@vueup/vue-quill";
@ -24,13 +62,13 @@ export default defineComponent({
protocolId: String,
},
computed: {
...mapState(useProtocolDecisionStore, ["decision", "loading"]),
...mapWritableState(useProtocolDecisionStore, ["decision", "loading"]),
},
mounted() {
this.fetchProtocolDecision();
},
methods: {
...mapActions(useProtocolDecisionStore, ["fetchProtocolDecision"]),
...mapActions(useProtocolDecisionStore, ["fetchProtocolDecision", "createProtocolDecision"]),
},
});
</script>

View file

@ -4,6 +4,61 @@
<p v-else-if="loading == 'failed'" @click="fetchProtocolVoting" class="cursor-pointer">
&#8634; laden fehlgeschlagen
</p>
<div class="flex flex-col gap-2 h-full overflow-y-auto">
<details
v-for="item in voting"
class="flex flex-col gap-2 rounded-lg w-full justify-between border border-primary overflow-hidden min-h-fit"
>
<summary class="flex flex-row gap-2 bg-primary p-2 w-full justify-between items-center cursor-pointer">
<svg
class="fill-white stroke-white opacity-75 w-4 h-4"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
>
<path d="M12.95 10.707l.707-.707L8 4.343 6.586 5.757 10.828 10l-4.242 4.243L8 15.657l4.95-4.95z" />
</svg>
<input
type="text"
name="title"
id="title"
placeholder="Einscheidung"
autocomplete="off"
v-model="item.topic"
@keyup.prevent
/>
</summary>
<QuillEditor
id="top"
theme="snow"
placeholder="Entscheidung Inhalt..."
style="height: 100px; max-height: 100px; min-height: 100px"
contentType="html"
:toolbar="toolbarOptions"
v-model:content="item.context"
/>
<div class="px-2 pb-2">
<p>Ergebnis:</p>
<div class="flex flex-row gap-2">
<div class="w-full">
<p>dafür</p>
<input type="number" v-model="item.favour" />
</div>
<div class="w-full">
<p>dagegen</p>
<input type="number" v-model="item.against" />
</div>
<div class="w-full">
<p>enthalten</p>
<input type="number" v-model="item.abstain" />
</div>
</div>
</div>
</details>
</div>
<button primary class="!w-fit" @click="createProtocolVoting">Abstimmung hinzufügen</button>
</div>
</template>
@ -30,7 +85,7 @@ export default defineComponent({
this.fetchProtocolVoting();
},
methods: {
...mapActions(useProtocolVotingStore, ["fetchProtocolVoting"]),
...mapActions(useProtocolVotingStore, ["fetchProtocolVoting", "createProtocolVoting"]),
},
});
</script>