Aufgaben-Tabelle: inline bearbeitbar, Zuweisung direkt sichtbar

Enddatum und Status lassen sich jetzt direkt in der Tabelle ändern,
Zuweisungen können entfernt werden und das Zuweisungsformular ist
direkt in der Zugewiesen-Spalte statt versteckt im Verwalten-Bereich,
damit auch bei bereits bestehenden Aufgaben nachträglich Mitarbeiter
zugewiesen werden können. Zusätzlich verhindert ein Guard doppeltes
Duplizieren durch Mehrfachklick.
This commit is contained in:
2026-07-04 11:56:12 +02:00
parent 4e0f628db7
commit 4abbb50f8c
2 changed files with 75 additions and 14 deletions
+42 -11
View File
@@ -7,6 +7,7 @@ const newProject = reactive({ name: '', description: '', start_date: '', end_dat
const newTask = reactive({});
const newAssignment = reactive({});
const newDuplicate = reactive({});
const duplicating = reactive({});
const expanded = reactive({});
async function addProject() {
@@ -45,10 +46,16 @@ function duplicateForm(taskId) {
}
async function duplicateTask(projectId, taskId) {
if (duplicating[taskId]) return;
const form = duplicateForm(taskId);
if (!form.start_date || !form.end_date) return;
duplicating[taskId] = true;
try {
await store.duplicateTask(projectId, taskId, { ...form });
Object.assign(form, { start_date: '', end_date: '' });
} finally {
duplicating[taskId] = false;
}
}
</script>
@@ -89,13 +96,41 @@ async function duplicateTask(projectId, taskId) {
<tr v-for="task in project.tasks" :key="task.id">
<td>{{ task.name }}</td>
<td>{{ formatDate(task.start_date) }}</td>
<td>{{ formatDate(task.end_date) }}</td>
<td>
<input
type="date"
:value="task.end_date"
@change="store.updateTask(project.id, task.id, { end_date: $event.target.value })"
/>
</td>
<td>{{ task.estimated_hours }}</td>
<td>{{ task.status }}</td>
<td>
<select
:value="task.status"
@change="store.updateTask(project.id, task.id, { status: $event.target.value })"
>
<option value="geplant">geplant</option>
<option value="in Arbeit">in Arbeit</option>
<option value="erledigt">erledigt</option>
</select>
</td>
<td>
<span v-for="a in task.assignments" :key="a.id" class="tag">
{{ a.employee_name }} ({{ a.allocated_hours_per_week }}h)
<button
class="tag-remove"
title="Zuweisung entfernen"
@click="store.unassignEmployee(project.id, task.id, a.id)"
>×</button>
</span>
<div class="assign-inline">
<select v-model="assignmentForm(task.id).employee_id">
<option value="" disabled>Mitarbeiter wählen</option>
<option v-for="e in store.employees" :key="e.id" :value="e.id">{{ fullName(e) }}</option>
</select>
<input v-model.number="assignmentForm(task.id).allocated_hours_per_week" type="number" style="width: 70px" placeholder="Std/Woche" />
<button class="btn secondary" @click="addAssignment(project.id, task.id)">Zuweisen</button>
</div>
</td>
</tr>
</tbody>
@@ -118,19 +153,15 @@ async function duplicateTask(projectId, taskId) {
<div v-for="task in project.tasks" :key="'manage-' + task.id" style="border: 1px solid var(--border); border-radius: 6px; padding: 8px; margin-bottom: 8px">
<strong>{{ task.name }}</strong>
<div class="form-row">
<select v-model="assignmentForm(task.id).employee_id">
<option value="" disabled>Mitarbeiter wählen</option>
<option v-for="e in store.employees" :key="e.id" :value="e.id">{{ fullName(e) }}</option>
</select>
<input v-model.number="assignmentForm(task.id).allocated_hours_per_week" type="number" style="width: 90px" placeholder="Std/Woche" />
<button class="btn secondary" @click="addAssignment(project.id, task.id)">Zuweisen</button>
</div>
<div class="form-row">
<span class="muted">Aufgabe zu anderem Zeitraum wiederholen:</span>
<input v-model="duplicateForm(task.id).start_date" type="date" />
<input v-model="duplicateForm(task.id).end_date" type="date" />
<button class="btn secondary" @click="duplicateTask(project.id, task.id)">Duplizieren</button>
<button
class="btn secondary"
:disabled="duplicating[task.id]"
@click="duplicateTask(project.id, task.id)"
>Duplizieren</button>
<button class="btn danger" @click="store.deleteTask(project.id, task.id)">Aufgabe löschen</button>
</div>
</div>
+31 -1
View File
@@ -212,7 +212,9 @@ button.btn.danger {
}
.tag {
display: inline-block;
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px 8px;
border-radius: 999px;
font-size: 12px;
@@ -221,6 +223,34 @@ button.btn.danger {
margin-right: 4px;
}
.tag-remove {
border: none;
background: none;
cursor: pointer;
color: inherit;
font-size: 13px;
line-height: 1;
padding: 0;
}
.assign-inline {
display: flex;
gap: 4px;
align-items: center;
margin-top: 6px;
}
.assign-inline select,
.assign-inline input {
padding: 4px 6px;
font-size: 12px;
}
.assign-inline button {
padding: 4px 8px;
font-size: 12px;
}
.muted {
color: var(--muted);
font-size: 13px;