Initial commit: Ressourcenplanung-App
Vue 3 + Vite Frontend und Node/Express + SQLite Backend für Projekt-, Aufgaben-, Mitarbeiter- und Teamverwaltung inkl. Gantt-Zeitplan und Auslastungs-Heatmap.
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
<script setup>
|
||||
import { reactive } from 'vue';
|
||||
import { store } from './store.js';
|
||||
import { formatDate } from './helpers.js';
|
||||
|
||||
const newProject = reactive({ name: '', description: '', start_date: '', end_date: '', color: '#4f46e5' });
|
||||
const newTask = reactive({});
|
||||
const newAssignment = 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 });
|
||||
}
|
||||
</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>{{ formatDate(task.end_date) }}</td>
|
||||
<td>{{ task.estimated_hours }}</td>
|
||||
<td>{{ task.status }}</td>
|
||||
<td>
|
||||
<span v-for="a in task.assignments" :key="a.id" class="tag">
|
||||
{{ a.employee_name }} ({{ a.allocated_hours_per_week }}h)
|
||||
</span>
|
||||
</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="'assign-' + task.id" class="form-row">
|
||||
<span style="width: 180px">{{ task.name }}:</span>
|
||||
<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">{{ e.name }}</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>
|
||||
<button class="btn danger" @click="store.deleteTask(project.id, task.id)">Aufgabe löschen</button>
|
||||
</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>
|
||||
Reference in New Issue
Block a user