Dieser Commit ist enthalten in:
Claude Project Manager
2025-12-28 21:36:45 +00:00
Commit ab1e5be9a9
146 geänderte Dateien mit 65525 neuen und 0 gelöschten Zeilen

332
frontend/js/undo.js Normale Datei
Datei anzeigen

@ -0,0 +1,332 @@
/**
* TASKMATE - Undo/Redo Module
* ===========================
* Undo and redo functionality
*/
import store from './store.js';
import api from './api.js';
class UndoManager {
constructor() {
this.bindEvents();
}
bindEvents() {
window.addEventListener('undo:perform', () => this.undo());
window.addEventListener('redo:perform', () => this.redo());
}
// =====================
// UNDO/REDO
// =====================
async undo() {
if (!store.canUndo()) {
this.showInfo('Nichts zum Rückgängigmachen');
return;
}
const action = store.popUndo();
if (!action) return;
try {
await this.executeUndo(action);
this.showSuccess('Rückgängig gemacht');
} catch (error) {
console.error('Undo failed:', error);
this.showError('Rückgängig fehlgeschlagen');
// Re-push to undo stack
store.pushUndo(action);
}
}
async redo() {
if (!store.canRedo()) {
this.showInfo('Nichts zum Wiederholen');
return;
}
const action = store.popRedo();
if (!action) return;
try {
await this.executeRedo(action);
this.showSuccess('Wiederholt');
} catch (error) {
console.error('Redo failed:', error);
this.showError('Wiederholen fehlgeschlagen');
}
}
// =====================
// ACTION EXECUTION
// =====================
async executeUndo(action) {
const projectId = store.get('currentProjectId');
switch (action.type) {
case 'DELETE_TASK':
// Restore deleted task
const restoredTask = await api.createTask(projectId, {
...action.task,
id: undefined // Let server assign new ID
});
store.addTask(restoredTask);
break;
case 'CREATE_TASK':
// Delete created task
await api.deleteTask(projectId, action.taskId);
store.removeTask(action.taskId);
break;
case 'UPDATE_TASK':
// Restore previous values
await api.updateTask(projectId, action.taskId, action.previousData);
store.updateTask(action.taskId, action.previousData);
break;
case 'MOVE_TASK':
// Move back to original position
await api.moveTask(projectId, action.taskId, action.fromColumnId, action.fromPosition);
store.moveTask(action.taskId, action.fromColumnId, action.fromPosition);
break;
case 'DELETE_COLUMN':
// Restore deleted column (without tasks - they're gone)
const restoredColumn = await api.createColumn(projectId, action.column);
store.addColumn(restoredColumn);
break;
case 'CREATE_COLUMN':
// Delete created column
await api.deleteColumn(projectId, action.columnId);
store.removeColumn(action.columnId);
break;
case 'UPDATE_COLUMN':
// Restore previous values
await api.updateColumn(projectId, action.columnId, action.previousData);
store.updateColumn(action.columnId, action.previousData);
break;
case 'BULK_DELETE':
// Restore all deleted tasks
for (const task of action.tasks) {
const restored = await api.createTask(projectId, {
...task,
id: undefined
});
store.addTask(restored);
}
break;
case 'BULK_MOVE':
// Move all tasks back
for (const item of action.items) {
await api.moveTask(projectId, item.taskId, item.fromColumnId, item.fromPosition);
store.moveTask(item.taskId, item.fromColumnId, item.fromPosition);
}
break;
case 'BULK_UPDATE':
// Restore all previous values
for (const item of action.items) {
await api.updateTask(projectId, item.taskId, item.previousData);
store.updateTask(item.taskId, item.previousData);
}
break;
default:
console.warn('Unknown undo action type:', action.type);
}
}
async executeRedo(action) {
const projectId = store.get('currentProjectId');
switch (action.type) {
case 'DELETE_TASK':
// Re-delete the task
await api.deleteTask(projectId, action.task.id);
store.removeTask(action.task.id);
break;
case 'CREATE_TASK':
// Re-create the task
const recreatedTask = await api.createTask(projectId, action.taskData);
store.addTask(recreatedTask);
break;
case 'UPDATE_TASK':
// Re-apply the update
await api.updateTask(projectId, action.taskId, action.newData);
store.updateTask(action.taskId, action.newData);
break;
case 'MOVE_TASK':
// Re-move to new position
await api.moveTask(projectId, action.taskId, action.toColumnId, action.toPosition);
store.moveTask(action.taskId, action.toColumnId, action.toPosition);
break;
case 'DELETE_COLUMN':
// Re-delete the column
await api.deleteColumn(projectId, action.column.id);
store.removeColumn(action.column.id);
break;
case 'CREATE_COLUMN':
// Re-create the column
const recreatedColumn = await api.createColumn(projectId, action.columnData);
store.addColumn(recreatedColumn);
break;
case 'UPDATE_COLUMN':
// Re-apply the update
await api.updateColumn(projectId, action.columnId, action.newData);
store.updateColumn(action.columnId, action.newData);
break;
case 'BULK_DELETE':
// Re-delete all tasks
for (const task of action.tasks) {
await api.deleteTask(projectId, task.id);
store.removeTask(task.id);
}
break;
case 'BULK_MOVE':
// Re-move all tasks
for (const item of action.items) {
await api.moveTask(projectId, item.taskId, item.toColumnId, item.toPosition);
store.moveTask(item.taskId, item.toColumnId, item.toPosition);
}
break;
case 'BULK_UPDATE':
// Re-apply all updates
for (const item of action.items) {
await api.updateTask(projectId, item.taskId, item.newData);
store.updateTask(item.taskId, item.newData);
}
break;
default:
console.warn('Unknown redo action type:', action.type);
}
}
// =====================
// ACTION RECORDING
// =====================
recordTaskDelete(task) {
store.pushUndo({
type: 'DELETE_TASK',
task: { ...task }
});
}
recordTaskCreate(taskId, taskData) {
store.pushUndo({
type: 'CREATE_TASK',
taskId,
taskData
});
}
recordTaskUpdate(taskId, previousData, newData) {
store.pushUndo({
type: 'UPDATE_TASK',
taskId,
previousData,
newData
});
}
recordTaskMove(taskId, fromColumnId, fromPosition, toColumnId, toPosition) {
store.pushUndo({
type: 'MOVE_TASK',
taskId,
fromColumnId,
fromPosition,
toColumnId,
toPosition
});
}
recordColumnDelete(column) {
store.pushUndo({
type: 'DELETE_COLUMN',
column: { ...column }
});
}
recordColumnCreate(columnId, columnData) {
store.pushUndo({
type: 'CREATE_COLUMN',
columnId,
columnData
});
}
recordColumnUpdate(columnId, previousData, newData) {
store.pushUndo({
type: 'UPDATE_COLUMN',
columnId,
previousData,
newData
});
}
recordBulkDelete(tasks) {
store.pushUndo({
type: 'BULK_DELETE',
tasks: tasks.map(t => ({ ...t }))
});
}
recordBulkMove(items) {
store.pushUndo({
type: 'BULK_MOVE',
items: [...items]
});
}
recordBulkUpdate(items) {
store.pushUndo({
type: 'BULK_UPDATE',
items: [...items]
});
}
// =====================
// HELPERS
// =====================
showSuccess(message) {
window.dispatchEvent(new CustomEvent('toast:show', {
detail: { message, type: 'success' }
}));
}
showError(message) {
window.dispatchEvent(new CustomEvent('toast:show', {
detail: { message, type: 'error' }
}));
}
showInfo(message) {
window.dispatchEvent(new CustomEvent('toast:show', {
detail: { message, type: 'info' }
}));
}
}
// Create and export singleton
const undoManager = new UndoManager();
export default undoManager;