4abbb50f8c
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.
176 lines
6.9 KiB
Vue
176 lines
6.9 KiB
Vue
<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>
|