feat: Enhance task filtering, smart scheduling, audit logs and translations
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-12-17 14:26:54 +01:00
parent 9819d8db0b
commit 2579df0b89
32 changed files with 2219 additions and 456 deletions
+37 -3
View File
@@ -32,7 +32,9 @@ export interface IStorage {
createUserTaskAccess(access: InsertUserTaskAccess): Promise<UserTaskAccess>; // Alias
getSharedTasks(userId: string): Promise<SharedTask[]>; // Tasks shared WITH user
getSharedTask(taskId: string, userId: string): Promise<SharedTask | undefined>; // Check specific share
getUserTaskAccess(viewerId: string): Promise<UserTaskAccess[]>; // Access Viewer has to Owners
checkUserTaskAccess(ownerId: string, viewerId: string): Promise<boolean>; // Check specific access
getTaskSharedUsers(taskId: string): Promise<User[]>; // Get users a task is shared WITH
unshareTask(taskId: string, userId: string): Promise<boolean>; // Unshare specific task from user
@@ -280,10 +282,18 @@ export class MemStorage implements IStorage {
return Array.from(this.sharedTasks.values()).filter(st => st.sharedWithUserId === userId);
}
async getSharedTask(taskId: string, userId: string): Promise<SharedTask | undefined> {
return Array.from(this.sharedTasks.values()).find(st => st.taskId === taskId && st.sharedWithUserId === userId);
}
async getUserTaskAccess(viewerId: string): Promise<UserTaskAccess[]> {
return Array.from(this.userTaskAccess.values()).filter(uta => uta.viewerId === viewerId);
}
async checkUserTaskAccess(ownerId: string, viewerId: string): Promise<boolean> {
return Array.from(this.userTaskAccess.values()).some(uta => uta.ownerId === ownerId && uta.viewerId === viewerId);
}
async getTaskSharedUsers(taskId: string): Promise<User[]> {
const shares = Array.from(this.sharedTasks.values()).filter(st => st.taskId === taskId);
const users: User[] = [];
@@ -479,8 +489,13 @@ export class MemStorage implements IStorage {
estimatedDuration: insertTask.estimatedDuration || null,
parentTaskId: insertTask.parentTaskId || null,
startDate: insertTask.startDate || null,
dependencies: insertTask.dependencies || null,
userId: insertTask.userId || null // Set ownership
dependencies: insertTask.dependencies || [],
userId: insertTask.userId || null, // Allow null for system/orphaned tasks support
isRecurring: false,
recurrenceInterval: null,
recurrenceIntervalValue: 1,
recurrenceDays: [],
recurrenceEnd: null
};
this.tasks.set(id, task);
return task;
@@ -514,7 +529,8 @@ export class MemStorage implements IStorage {
id,
userId: event.userId || null,
taskId: event.taskId || null,
createdAt: new Date()
createdAt: new Date(),
details: event.details || null
};
this.xpEvents.set(id, xpEvent);
// Also update user XP
@@ -1085,10 +1101,28 @@ export class DbStorage implements IStorage {
return await this.db.select().from(schema.sharedTasks).where(eq(schema.sharedTasks.sharedWithUserId, userId));
}
async getSharedTask(taskId: string, userId: string): Promise<SharedTask | undefined> {
const [share] = await this.db.select().from(schema.sharedTasks)
.where(and(
eq(schema.sharedTasks.taskId, taskId),
eq(schema.sharedTasks.sharedWithUserId, userId)
));
return share;
}
async getUserTaskAccess(viewerId: string): Promise<UserTaskAccess[]> {
return await this.db.select().from(schema.userTaskAccess).where(eq(schema.userTaskAccess.viewerId, viewerId));
}
async checkUserTaskAccess(ownerId: string, viewerId: string): Promise<boolean> {
const [access] = await this.db.select().from(schema.userTaskAccess)
.where(and(
eq(schema.userTaskAccess.ownerId, ownerId),
eq(schema.userTaskAccess.viewerId, viewerId)
));
return !!access;
}
async getTaskSharedUsers(taskId: string): Promise<User[]> {
const shares = await this.db.select().from(schema.sharedTasks).where(eq(schema.sharedTasks.taskId, taskId));
if (shares.length === 0) return [];