Aufgaben-Python/courses/javascript/16_master_abschlussprojekt_web_app/musterloesung.js

154 lines
4.1 KiB
JavaScript

/**
* 🌐 MASTER 08: INTERAKTIVE TASK-APP (MUSTERLÖSUNG) 🌐
* ====================================================
*/
/**
* @typedef {'high' | 'medium' | 'low'} TaskPriority
* @typedef {'all' | 'active' | 'completed'} FilterStatus
*
* @typedef {Object} Task
* @property {number} id
* @property {string} title
* @property {TaskPriority} priority
* @property {boolean} completed
* @property {string | null} dueDate
* @property {string} createdAt
*
* @typedef {Object} TaskStatistics
* @property {number} total
* @property {number} active
* @property {number} completed
* @property {number} highPriorityCount
*/
class TaskManager {
constructor(initialTasks = []) {
/** @type {Task[]} */
this.tasks = [];
this.nextId = 1;
if (Array.isArray(initialTasks)) {
for (const t of initialTasks) {
if (t && typeof t === "object") {
const task = {
id: typeof t.id === "number" ? t.id : this.nextId++,
title: typeof t.title === "string" ? t.title.trim() : "Ohne Titel",
priority: ["high", "medium", "low"].includes(t.priority) ? t.priority : "medium",
completed: Boolean(t.completed),
dueDate: (typeof t.dueDate === "string" && t.dueDate.trim().length > 0) ? t.dueDate.trim() : null,
createdAt: t.createdAt || new Date().toISOString()
};
if (task.id >= this.nextId) {
this.nextId = task.id + 1;
}
this.tasks.push(task);
}
}
}
}
/**
* 🎯 TEILZIEL 1: addTask(title, priority = 'medium', dueDate = null)
*/
addTask(title, priority = "medium", dueDate = null) {
if (typeof title !== "string" || title.trim().length === 0) {
throw new Error("Aufgabentitel darf nicht leer sein");
}
const validPriority = ["high", "medium", "low"].includes(priority) ? priority : "medium";
const normalizedDueDate = (typeof dueDate === "string" && dueDate.trim().length > 0) ? dueDate.trim() : null;
const task = {
id: this.nextId++,
title: title.trim(),
priority: validPriority,
completed: false,
dueDate: normalizedDueDate,
createdAt: new Date().toISOString()
};
this.tasks.push(task);
return task;
}
/**
* 🎯 TEILZIEL 2: toggleTask(id)
*/
toggleTask(id) {
const numId = Number(id);
const task = this.tasks.find(t => t.id === numId);
if (!task) {
return null;
}
task.completed = !task.completed;
return task;
}
/**
* 🎯 TEILZIEL 3: deleteTask(id)
*/
deleteTask(id) {
const numId = Number(id);
const index = this.tasks.findIndex(t => t.id === numId);
if (index === -1) {
return false;
}
this.tasks.splice(index, 1);
return true;
}
/**
* 🎯 TEILZIEL 4: getFilteredTasks(filterStatus = 'all', searchQuery = '')
*/
getFilteredTasks(filterStatus = "all", searchQuery = "") {
let result = [...this.tasks];
if (filterStatus === "active") {
result = result.filter(t => !t.completed);
} else if (filterStatus === "completed") {
result = result.filter(t => t.completed);
}
if (typeof searchQuery === "string" && searchQuery.trim().length > 0) {
const query = searchQuery.trim().toLowerCase();
result = result.filter(t => t.title.toLowerCase().includes(query));
}
return result;
}
/**
* 🎯 TEILZIEL 5: getStatistics()
*/
getStatistics() {
const total = this.tasks.length;
const completed = this.tasks.filter(t => t.completed).length;
const active = total - completed;
const highPriorityCount = this.tasks.filter(t => t.priority === "high").length;
return {
total,
active,
completed,
highPriorityCount
};
}
/**
* Hilfsmethode: Task nach ID suchen
*/
getTaskById(id) {
const numId = Number(id);
return this.tasks.find(t => t.id === numId) || null;
}
/**
* Hilfsmethode: Alle erledigten Aufgaben auf einmal löschen
*/
clearCompleted() {
const initialCount = this.tasks.length;
this.tasks = this.tasks.filter(t => !t.completed);
return initialCount - this.tasks.length;
}
}