Files
ressourcenplanung/client/src/ProjectsPanel.vue
T
admin 4abbb50f8c 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.
2026-07-04 11:56:12 +02:00

176 lines
6.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup>
import { reactive } from 'vue';
import { store } from './store.js';
import { formatDate, fullName } from './helpers.js';
const newProject = reactive({ name: '', description: '', start_date: '', end_date: '', color: '#4f46e5' });
const newTask = reactive({});
const newAssignment = reactive({});
const newDuplicate = reactive({});
const duplicating = reactive({});
const expanded = reactive({});
async function addProject() {
if (!newProject.name) return;
await store.createProject({ ...newProject });
Object.assign(newProject, { name: '', description: '', start_date: '', end_date: '', color: '#4f46e5' });
}
function taskForm(projectId) {
if (!newTask[projectId]) newTask[projectId] = { name: '', start_date: '', end_date: '', estimated_hours: 0, status: 'geplant' };
return newTask[projectId];
}
async function addTask(projectId) {
const form = taskForm(projectId);
if (!form.name || !form.start_date || !form.end_date) return;
await store.createTask(projectId, { ...form });
Object.assign(form, { name: '', start_date: '', end_date: '', estimated_hours: 0, status: 'geplant' });
}
function assignmentForm(taskId) {
if (!newAssignment[taskId]) newAssignment[taskId] = { employee_id: '', allocated_hours_per_week: 8 };
return newAssignment[taskId];
}
async function addAssignment(projectId, taskId) {
const form = assignmentForm(taskId);
if (!form.employee_id) return;
await store.assignEmployee(projectId, taskId, Number(form.employee_id), Number(form.allocated_hours_per_week));
Object.assign(form, { employee_id: '', allocated_hours_per_week: 8 });
}
function duplicateForm(taskId) {
if (!newDuplicate[taskId]) newDuplicate[taskId] = { start_date: '', end_date: '' };
return newDuplicate[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>
<template>
<div class="panel-section">
<h2>Neues Projekt</h2>
<div class="form-row">
<input v-model="newProject.name" placeholder="Projektname" />
<input v-model="newProject.description" placeholder="Beschreibung" />
<input v-model="newProject.start_date" type="date" />
<input v-model="newProject.end_date" type="date" />
<input v-model="newProject.color" type="color" />
<button class="btn" @click="addProject">Anlegen</button>
</div>
</div>
<div class="panel-section" v-for="project in store.projects" :key="project.id">
<div style="display: flex; justify-content: space-between; align-items: center">
<h2 :style="{ color: project.color }">{{ project.name }}</h2>
<button class="btn secondary" @click="expanded[project.id] = !expanded[project.id]">
{{ expanded[project.id] ? 'Zuklappen' : 'Aufgaben verwalten' }}
</button>
</div>
<p class="muted">{{ project.description }} · {{ formatDate(project.start_date) }} {{ formatDate(project.end_date) }}</p>
<table>
<thead>
<tr>
<th>Aufgabe</th>
<th>Start</th>
<th>Ende</th>
<th>Stunden</th>
<th>Status</th>
<th>Zugewiesen</th>
</tr>
</thead>
<tbody>
<tr v-for="task in project.tasks" :key="task.id">
<td>{{ task.name }}</td>
<td>{{ formatDate(task.start_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>
<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>
</table>
<template v-if="expanded[project.id]">
<h3>Neue Aufgabe</h3>
<div class="form-row">
<input v-model="taskForm(project.id).name" placeholder="Aufgabenname" />
<input v-model="taskForm(project.id).start_date" type="date" />
<input v-model="taskForm(project.id).end_date" type="date" />
<input v-model.number="taskForm(project.id).estimated_hours" type="number" placeholder="Stunden" style="width: 90px" />
<select v-model="taskForm(project.id).status">
<option value="geplant">geplant</option>
<option value="in Arbeit">in Arbeit</option>
<option value="erledigt">erledigt</option>
</select>
<button class="btn" @click="addTask(project.id)">Hinzufügen</button>
</div>
<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">
<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"
: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>
</template>
<div style="margin-top: 12px">
<button class="btn danger" @click="store.deleteProject(project.id)">Projekt löschen</button>
</div>
</div>
<p v-if="store.projects.length === 0" class="muted">Noch keine Projekte angelegt.</p>
</template>