update
This commit is contained in:
1 parent
db77c1f985
commit
a90044b0a7
21 files changed
+1111
-253
No files matched your search
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { computed, reactive, ref } from "vue";
|
||||
import { computed, reactive, ref } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
items: { type: Array, default: () => [] },
|
||||
@@ -7,111 +7,111 @@ const props = defineProps({
|
||||
host: Boolean,
|
||||
// Ohne gewählten Namen bleibt der Reiter zum Mitlesen da
|
||||
angemeldet: Boolean,
|
||||
});
|
||||
})
|
||||
|
||||
const emit = defineEmits(["changed"]);
|
||||
const emit = defineEmits(['changed'])
|
||||
|
||||
const entwurf = ref("");
|
||||
const entwurf = ref('')
|
||||
|
||||
// Der Host darf jeden Wunsch anfassen, alle anderen nur die eigenen
|
||||
const bearbeitet = ref(null);
|
||||
const bearbeitung = ref("");
|
||||
const bearbeitet = ref(null)
|
||||
const bearbeitung = ref('')
|
||||
|
||||
function darfAendern(item) {
|
||||
return props.host || item.mine;
|
||||
return props.host || item.mine
|
||||
}
|
||||
|
||||
function bearbeiten(item) {
|
||||
bearbeitet.value = item.id;
|
||||
bearbeitung.value = item.text;
|
||||
bearbeitet.value = item.id
|
||||
bearbeitung.value = item.text
|
||||
}
|
||||
|
||||
async function speichern() {
|
||||
const text = bearbeitung.value.trim();
|
||||
const text = bearbeitung.value.trim()
|
||||
if (text) {
|
||||
await fetch(`/api/features/${bearbeitet.value}`, {
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ text }),
|
||||
});
|
||||
emit("changed");
|
||||
})
|
||||
emit('changed')
|
||||
}
|
||||
bearbeitet.value = null;
|
||||
bearbeitet.value = null
|
||||
}
|
||||
|
||||
// Die Liste kommt vom Server schon sortiert: offene nach Stimmen, dann
|
||||
// die erledigten. Hier nur noch in zwei Abschnitte teilen.
|
||||
const offene = computed(() => props.items.filter((w) => !w.done_at));
|
||||
const erledigte = computed(() => props.items.filter((w) => w.done_at));
|
||||
const offene = computed(() => props.items.filter((w) => !w.done_at))
|
||||
const erledigte = computed(() => props.items.filter((w) => w.done_at))
|
||||
|
||||
async function anlegen() {
|
||||
if (!entwurf.value.trim()) return;
|
||||
await fetch("/api/features", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
if (!entwurf.value.trim()) return
|
||||
await fetch('/api/features', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ text: entwurf.value }),
|
||||
});
|
||||
entwurf.value = "";
|
||||
emit("changed");
|
||||
})
|
||||
entwurf.value = ''
|
||||
emit('changed')
|
||||
}
|
||||
|
||||
async function loeschen(item) {
|
||||
await fetch(`/api/features/${item.id}`, { method: "DELETE" });
|
||||
emit("changed");
|
||||
await fetch(`/api/features/${item.id}`, { method: 'DELETE' })
|
||||
emit('changed')
|
||||
}
|
||||
|
||||
async function abstimmen(item) {
|
||||
await fetch(`/api/features/${item.id}/vote`, { method: "POST" });
|
||||
emit("changed");
|
||||
await fetch(`/api/features/${item.id}/vote`, { method: 'POST' })
|
||||
emit('changed')
|
||||
}
|
||||
|
||||
async function erledigen(item, done) {
|
||||
await fetch(`/api/features/${item.id}/done`, {
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ done }),
|
||||
});
|
||||
emit("changed");
|
||||
})
|
||||
emit('changed')
|
||||
}
|
||||
|
||||
// Ergänzungen: aufgeklappt je Wunsch, mit eigenem Entwurf
|
||||
const aufgeklappt = reactive(new Set());
|
||||
const notizEntwurf = reactive({});
|
||||
const aufgeklappt = reactive(new Set())
|
||||
const notizEntwurf = reactive({})
|
||||
|
||||
function umschalten(id) {
|
||||
if (aufgeklappt.has(id)) aufgeklappt.delete(id);
|
||||
else aufgeklappt.add(id);
|
||||
if (aufgeklappt.has(id)) aufgeklappt.delete(id)
|
||||
else aufgeklappt.add(id)
|
||||
}
|
||||
|
||||
function ergaenzungsLabel(item) {
|
||||
const n = item.notes.length;
|
||||
if (!n) return "Ergänzen";
|
||||
return n === 1 ? "1 Ergänzung" : `${n} Ergänzungen`;
|
||||
const n = item.notes.length
|
||||
if (!n) return 'Ergänzen'
|
||||
return n === 1 ? '1 Ergänzung' : `${n} Ergänzungen`
|
||||
}
|
||||
|
||||
async function anmerken(item) {
|
||||
const text = (notizEntwurf[item.id] || "").trim();
|
||||
if (!text) return;
|
||||
const text = (notizEntwurf[item.id] || '').trim()
|
||||
if (!text) return
|
||||
await fetch(`/api/features/${item.id}/notes`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ text }),
|
||||
});
|
||||
notizEntwurf[item.id] = "";
|
||||
emit("changed");
|
||||
})
|
||||
notizEntwurf[item.id] = ''
|
||||
emit('changed')
|
||||
}
|
||||
|
||||
async function notizLoeschen(notiz) {
|
||||
await fetch(`/api/feature-notes/${notiz.id}`, { method: "DELETE" });
|
||||
emit("changed");
|
||||
await fetch(`/api/feature-notes/${notiz.id}`, { method: 'DELETE' })
|
||||
emit('changed')
|
||||
}
|
||||
|
||||
function datum(iso) {
|
||||
return new Date(iso).toLocaleDateString("de-DE", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
});
|
||||
return new Date(iso).toLocaleDateString('de-DE', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -129,9 +129,7 @@ function datum(iso) {
|
||||
placeholder="Was fehlt dir auf der Seite?"
|
||||
@keydown.ctrl.enter="anlegen"
|
||||
></textarea>
|
||||
<button type="button" :disabled="!entwurf.trim()" @click="anlegen">
|
||||
Eintragen
|
||||
</button>
|
||||
<button type="button" :disabled="!entwurf.trim()" @click="anlegen">Eintragen</button>
|
||||
</div>
|
||||
|
||||
<section
|
||||
@@ -172,14 +170,8 @@ function datum(iso) {
|
||||
@keydown.esc="bearbeitet = null"
|
||||
></textarea>
|
||||
<div class="zeile">
|
||||
<button type="button" class="klein" @click="speichern">
|
||||
Speichern
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="klein"
|
||||
@click="bearbeitet = null"
|
||||
>
|
||||
<button type="button" class="klein" @click="speichern">Speichern</button>
|
||||
<button type="button" class="klein" @click="bearbeitet = null">
|
||||
Abbrechen
|
||||
</button>
|
||||
</div>
|
||||
@@ -188,9 +180,7 @@ function datum(iso) {
|
||||
<template v-else>
|
||||
<p class="text">{{ w.text }}</p>
|
||||
<p class="meta">
|
||||
<span :class="{ eigen: w.mine }">{{
|
||||
w.name || "Anonym"
|
||||
}}</span>
|
||||
<span :class="{ eigen: w.mine }">{{ w.name || 'Anonym' }}</span>
|
||||
<span class="punkt">·</span>{{ datum(w.created_at) }}
|
||||
<template v-if="w.done_at">
|
||||
<span class="punkt">·</span>erledigt
|
||||
@@ -210,9 +200,7 @@ function datum(iso) {
|
||||
|
||||
<div v-if="aufgeklappt.has(w.id)" class="notizen">
|
||||
<p v-for="n in w.notes" :key="n.id" class="notiz">
|
||||
<span class="wer" :class="{ eigen: n.mine }">{{
|
||||
n.name || "Anonym"
|
||||
}}</span>
|
||||
<span class="wer" :class="{ eigen: n.mine }">{{ n.name || 'Anonym' }}</span>
|
||||
<span class="notiz-text">{{ n.text }}</span>
|
||||
<button
|
||||
v-if="host"
|
||||
@@ -250,12 +238,10 @@ function datum(iso) {
|
||||
v-if="host"
|
||||
type="button"
|
||||
class="weg"
|
||||
:title="
|
||||
w.done_at ? 'Wieder öffnen' : 'Als erledigt markieren'
|
||||
"
|
||||
:title="w.done_at ? 'Wieder öffnen' : 'Als erledigt markieren'"
|
||||
@click="erledigen(w, !w.done_at)"
|
||||
>
|
||||
{{ w.done_at ? "↺" : "✓" }}
|
||||
{{ w.done_at ? '↺' : '✓' }}
|
||||
</button>
|
||||
<button
|
||||
v-if="darfAendern(w) && bearbeitet !== w.id"
|
||||
@@ -288,8 +274,8 @@ function datum(iso) {
|
||||
.spalten {
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
"neu erledigt"
|
||||
"offen erledigt";
|
||||
'neu erledigt'
|
||||
'offen erledigt';
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-rows: auto 1fr;
|
||||
gap: 0 2.5rem;
|
||||
@@ -558,9 +544,9 @@ h3 {
|
||||
@media (max-width: 900px) {
|
||||
.spalten {
|
||||
grid-template-areas:
|
||||
"neu"
|
||||
"offen"
|
||||
"erledigt";
|
||||
'neu'
|
||||
'offen'
|
||||
'erledigt';
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user