Vorname/Nachname für Mitarbeiter, Aufgaben-Duplizierung, Zeitstrahl im Zeitplan
- employees: name-Feld in first_name/last_name aufgeteilt (Schema, API, Formular, alle Namensanzeigen) - Aufgaben können mit neuem Start-/Enddatum dupliziert werden (POST /api/tasks/:id/duplicate übernimmt Name, Stunden, Status und Zuweisungen 1:1, nur die Termine ändern sich) - Zeitplan zeigt jetzt einen Zeitstrahl mit Kalenderwochen und Tagesnummern oberhalb der Projekt-Balken
This commit is contained in:
+6
-6
@@ -20,14 +20,14 @@ if (isNew) {
|
||||
|
||||
function seed() {
|
||||
const insertEmployee = db.prepare(
|
||||
'INSERT INTO employees (name, email, role, weekly_capacity_hours) VALUES (?, ?, ?, ?)'
|
||||
'INSERT INTO employees (first_name, last_name, email, role, weekly_capacity_hours) VALUES (?, ?, ?, ?, ?)'
|
||||
);
|
||||
const employees = [
|
||||
['Anna Keller', 'anna.keller@example.com', 'Frontend-Entwicklerin', 40],
|
||||
['Jonas Weber', 'jonas.weber@example.com', 'Backend-Entwickler', 40],
|
||||
['Mira Schulz', 'mira.schulz@example.com', 'UX-Designerin', 32],
|
||||
['Tom Fischer', 'tom.fischer@example.com', 'Projektleiter', 40],
|
||||
['Lea Hoffmann', 'lea.hoffmann@example.com', 'QA-Ingenieurin', 30],
|
||||
['Anna', 'Keller', 'anna.keller@example.com', 'Frontend-Entwicklerin', 40],
|
||||
['Jonas', 'Weber', 'jonas.weber@example.com', 'Backend-Entwickler', 40],
|
||||
['Mira', 'Schulz', 'mira.schulz@example.com', 'UX-Designerin', 32],
|
||||
['Tom', 'Fischer', 'tom.fischer@example.com', 'Projektleiter', 40],
|
||||
['Lea', 'Hoffmann', 'lea.hoffmann@example.com', 'QA-Ingenieurin', 30],
|
||||
];
|
||||
const employeeIds = employees.map((e) => insertEmployee.run(...e).lastInsertRowid);
|
||||
|
||||
|
||||
+46
-11
@@ -41,28 +41,31 @@ function parseDate(s) {
|
||||
// ---------- employees ----------
|
||||
|
||||
app.get('/api/employees', (req, res) => {
|
||||
res.json(db.prepare('SELECT * FROM employees ORDER BY name').all());
|
||||
res.json(db.prepare('SELECT * FROM employees ORDER BY last_name, first_name').all());
|
||||
});
|
||||
|
||||
app.post('/api/employees', (req, res) => {
|
||||
const { name, email, role, weekly_capacity_hours } = req.body;
|
||||
if (!name) return res.status(400).json({ error: 'name ist erforderlich' });
|
||||
const { first_name, last_name, email, role, weekly_capacity_hours } = req.body;
|
||||
if (!first_name || !last_name) {
|
||||
return res.status(400).json({ error: 'first_name und last_name sind erforderlich' });
|
||||
}
|
||||
const result = db
|
||||
.prepare(
|
||||
'INSERT INTO employees (name, email, role, weekly_capacity_hours) VALUES (?, ?, ?, ?)'
|
||||
'INSERT INTO employees (first_name, last_name, email, role, weekly_capacity_hours) VALUES (?, ?, ?, ?, ?)'
|
||||
)
|
||||
.run(name, email || null, role || null, weekly_capacity_hours ?? 40);
|
||||
.run(first_name, last_name, email || null, role || null, weekly_capacity_hours ?? 40);
|
||||
res.status(201).json(db.prepare('SELECT * FROM employees WHERE id = ?').get(result.lastInsertRowid));
|
||||
});
|
||||
|
||||
app.put('/api/employees/:id', (req, res) => {
|
||||
const { name, email, role, weekly_capacity_hours } = req.body;
|
||||
const { first_name, last_name, email, role, weekly_capacity_hours } = req.body;
|
||||
const existing = db.prepare('SELECT * FROM employees WHERE id = ?').get(req.params.id);
|
||||
if (!existing) return res.status(404).json({ error: 'nicht gefunden' });
|
||||
db.prepare(
|
||||
'UPDATE employees SET name = ?, email = ?, role = ?, weekly_capacity_hours = ? WHERE id = ?'
|
||||
'UPDATE employees SET first_name = ?, last_name = ?, email = ?, role = ?, weekly_capacity_hours = ? WHERE id = ?'
|
||||
).run(
|
||||
name ?? existing.name,
|
||||
first_name ?? existing.first_name,
|
||||
last_name ?? existing.last_name,
|
||||
email ?? existing.email,
|
||||
role ?? existing.role,
|
||||
weekly_capacity_hours ?? existing.weekly_capacity_hours,
|
||||
@@ -144,7 +147,7 @@ app.get('/api/projects', (req, res) => {
|
||||
const tasks = db.prepare('SELECT * FROM tasks ORDER BY start_date').all();
|
||||
const assignments = db
|
||||
.prepare(
|
||||
`SELECT a.*, e.name AS employee_name FROM assignments a
|
||||
`SELECT a.*, (e.first_name || ' ' || e.last_name) AS employee_name FROM assignments a
|
||||
JOIN employees e ON e.id = a.employee_id`
|
||||
)
|
||||
.all();
|
||||
@@ -231,6 +234,38 @@ app.delete('/api/tasks/:id', (req, res) => {
|
||||
res.status(204).end();
|
||||
});
|
||||
|
||||
app.post('/api/tasks/:id/duplicate', (req, res) => {
|
||||
const existing = db.prepare('SELECT * FROM tasks WHERE id = ?').get(req.params.id);
|
||||
if (!existing) return res.status(404).json({ error: 'nicht gefunden' });
|
||||
const { start_date, end_date } = req.body;
|
||||
if (!start_date || !end_date) {
|
||||
return res.status(400).json({ error: 'start_date und end_date sind erforderlich' });
|
||||
}
|
||||
const result = db
|
||||
.prepare(
|
||||
'INSERT INTO tasks (project_id, name, start_date, end_date, estimated_hours, status) VALUES (?, ?, ?, ?, ?, ?)'
|
||||
)
|
||||
.run(existing.project_id, existing.name, start_date, end_date, existing.estimated_hours, existing.status);
|
||||
const newTaskId = result.lastInsertRowid;
|
||||
|
||||
const insertAssignment = db.prepare(
|
||||
'INSERT INTO assignments (task_id, employee_id, allocated_hours_per_week) VALUES (?, ?, ?)'
|
||||
);
|
||||
const originalAssignments = db.prepare('SELECT * FROM assignments WHERE task_id = ?').all(req.params.id);
|
||||
for (const a of originalAssignments) {
|
||||
insertAssignment.run(newTaskId, a.employee_id, a.allocated_hours_per_week);
|
||||
}
|
||||
|
||||
const newTask = db.prepare('SELECT * FROM tasks WHERE id = ?').get(newTaskId);
|
||||
const newAssignments = db
|
||||
.prepare(
|
||||
`SELECT a.*, (e.first_name || ' ' || e.last_name) AS employee_name FROM assignments a
|
||||
JOIN employees e ON e.id = a.employee_id WHERE a.task_id = ?`
|
||||
)
|
||||
.all(newTaskId);
|
||||
res.status(201).json({ ...newTask, assignments: newAssignments, depends_on: [] });
|
||||
});
|
||||
|
||||
app.post('/api/tasks/:id/dependencies', (req, res) => {
|
||||
const { depends_on_task_id } = req.body;
|
||||
db.prepare(
|
||||
@@ -291,7 +326,7 @@ app.get('/api/utilization', (req, res) => {
|
||||
weeks.push({ key: isoWeekKey(weekStart), start: weekStart });
|
||||
}
|
||||
|
||||
const employees = db.prepare('SELECT * FROM employees ORDER BY name').all();
|
||||
const employees = db.prepare('SELECT * FROM employees ORDER BY last_name, first_name').all();
|
||||
const rows = db
|
||||
.prepare(
|
||||
`SELECT a.employee_id, a.allocated_hours_per_week, t.start_date, t.end_date
|
||||
@@ -320,7 +355,7 @@ app.get('/api/utilization', (req, res) => {
|
||||
weeks: weeks.map((w) => w.key),
|
||||
employees: employees.map((e) => ({
|
||||
id: e.id,
|
||||
name: e.name,
|
||||
name: `${e.first_name} ${e.last_name}`,
|
||||
weekly_capacity_hours: e.weekly_capacity_hours,
|
||||
hours: weeks.map((w) => Math.round((data[e.id][w.key] || 0) * 10) / 10),
|
||||
})),
|
||||
|
||||
+2
-1
@@ -2,7 +2,8 @@ PRAGMA foreign_keys = ON;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS employees (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
first_name TEXT NOT NULL,
|
||||
last_name TEXT NOT NULL,
|
||||
email TEXT,
|
||||
role TEXT,
|
||||
weekly_capacity_hours REAL NOT NULL DEFAULT 40
|
||||
|
||||
Reference in New Issue
Block a user