Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/app/dashboard/dashboard-list-item.component.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import {Component, Input} from '@angular/core';
import {TaskStatusEnum} from '../api/models/task-status';

export type DashboardTask = {
title: string;
subtitle: string;
abbreviation: string;
color: string;
comments: number;
status: TaskStatusEnum;
date: Date;
weight: number;
};

@Component({
Expand Down
46 changes: 41 additions & 5 deletions src/app/dashboard/f-cross-dashboard.component.html
Original file line number Diff line number Diff line change
@@ -1,15 +1,51 @@
<div class="panel-full-screen flex flex-row gap-8 px-16 overflow-x-auto">
<div *ngFor="let unit of units">
<div *ngFor="let unit of unitsProcessed; trackBy: unitIdentify">
<div class="flex flex-col h-full w-[34rem] shadow">
<div class="shrink-0 flex flex-row gap-4 w-full bg-formatif-blue p-4 rounded-t-lg">
<h3 class="m-0 flex-grow text-white truncate min-w-0">{{ unit.code }}</h3>
<div
class="shrink-0 flex flex-row gap-4 items-center w-full bg-formatif-blue p-4 rounded-t-lg"
>
<h3
class="cursor-pointer m-0 mt-2 flex-grow text-white truncate min-w-0"
uiSref="projects/dashboard"
[uiParams]="{projectId: unit.projectId, taskAbbr: ''}"
>
{{ unit.code }}
</h3>

<input
type="text"
class="h-full w-64 p-2 px-4 text-md rounded-md"
placeholder="Search tasks..."
/>
<mat-icon style="color: white">sort</mat-icon>
<mat-icon style="color: white">filter_list_alt</mat-icon>

<div class="h-[24px]">
<button aria-label="Sort options" [matMenuTriggerFor]="sortmenu">
<mat-icon style="color: white">sort</mat-icon>
</button>
<mat-menu #sortmenu="matMenu">
<div *ngFor="let mode of sortOptions">
<button mat-menu-item (click)="setSort(unit.projectId, mode)">
{{ mode }}
</button>
</div>
</mat-menu>
</div>

<div class="h-[24px]">
<button aria-label="Filter options" [matMenuTriggerFor]="filtermenu">
<mat-icon style="color: white">filter_list_alt</mat-icon>
</button>
<mat-menu #filtermenu="matMenu">
<div
mat-menu-item
*ngFor="let mode of filterOptions"
(click)="$event.stopPropagation(); toggleFilter(unit.projectId, mode)"
>
<mat-checkbox [checked]="filters.get(unit.projectId)?.includes(mode) === true" />
{{ mode }}
</div>
</mat-menu>
</div>
</div>

<div class="flex flex-col overflow-y-auto">
Expand Down
96 changes: 86 additions & 10 deletions src/app/dashboard/f-cross-dashboard.component.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import {Component, OnInit} from '@angular/core';
import {GlobalStateService} from 'src/app/projects/states/index/global-state.service';
import {Project} from '../api/models/project';
import {TaskStatus} from '../api/models/task-status';
import {TaskStatus, TaskStatusEnum} from '../api/models/task-status';
import {DashboardTask} from './dashboard-list-item.component';
import {Task} from '../api/models/task';
import {TaskDefinition} from '../api/models/task-definition';

enum Filter {
HideCompleted = 'Hide Completed',
}

enum SortMode {
Recommended = 'Recommended',
SubmissionDate = 'Due Date',
Default = 'Default',
}

const completedTypes: readonly TaskStatusEnum[] = ['complete'];

type DashboardUnit = {
projectId: number;
Expand All @@ -20,37 +31,102 @@ type DashboardUnit = {
export class CrossDashboardComponent implements OnInit {
constructor(private globalStateService: GlobalStateService) {}

units: DashboardUnit[] = [];
filterOptions = Object.values(Filter);
sortOptions = Object.values(SortMode);

private units: DashboardUnit[] = [];
unitsProcessed: DashboardUnit[] = [];

private filters = new Map<number, Filter[]>();
private sorting = new Map<number, SortMode>();

ngOnInit(): void {
this.globalStateService.onLoad(() => {
this.globalStateService.currentUserProjects.values.subscribe((projects) => {
this.units = this.mapProjects(projects);
this.processTasks();
});
});
}

mapProjects(projects: readonly Project[]): DashboardUnit[] {
unitIdentify(_index: number, item: DashboardUnit) {
return item.projectId;
}

setSort(project: number, mode: SortMode) {
this.sorting.set(project, mode);
this.processTasks();
}

toggleFilter(project: number, filter: Filter) {
let filters = this.filters.get(project) ?? [];
if (filters.includes(filter)) {
filters = filters.filter((f) => f != filter);
} else {
filters = [...filters, filter];
}
this.filters.set(project, filters);
this.processTasks();
}

private processTasks() {
this.unitsProcessed = this.units.map((unit) => ({
...unit,
tasks: unit.tasks
.filter((task) => {
const filters = this.filters.get(unit.projectId) ?? [];
if (filters.includes(Filter.HideCompleted) && completedTypes.includes(task.status)) {
return false;
} else {
return true;
}
})
.sort((a, b) => {
const sort = this.sorting.get(unit.projectId) ?? 'recommended';
if (completedTypes.includes(a.status) && !completedTypes.includes(b.status)) {
return -1;
} else if (!completedTypes.includes(a.status) && completedTypes.includes(b.status)) {
return 1;
}
switch (sort) {
case SortMode.Recommended: {
// TODO: Connect to recommender's points
return 0;
}
case SortMode.SubmissionDate:
return a.date.getTime() - b.date.getTime();
case SortMode.Default:
return a.weight - b.weight;
}
}),
}));
}

private mapProjects(projects: readonly Project[]): DashboardUnit[] {
return projects.map((project) => {
project.calcTopTasks();
const unit = project.unit;
return {
projectId: project.id,
code: unit.code,
name: unit.name,
tasks: this.mapTasks(project.tasks, unit.taskDefinitions),
tasks: this.mapTasks(project.activeTasks()),
};
});
}

mapTasks(tasks: readonly Task[], taskDefs: readonly TaskDefinition[]): DashboardTask[] {
return taskDefs.map((def) => {
const task = tasks.find((t) => t.taskDefId == def.id);
private mapTasks(tasks: readonly Task[]): DashboardTask[] {
return tasks.map((task) => {
const def = task.definition;
return {
title: def.name,
subtitle: `${def.abbreviation} - ${def.targetGradeText} Task`,
abbreviation: def.abbreviation,
color: TaskStatus.STATUS_COLORS.get(task?.status ?? 'not_started'),
comments: task?.numNewComments ?? 0,
color: TaskStatus.STATUS_COLORS.get(task.status),
comments: task.numNewComments ?? 0,
status: task.status,
date: def.targetDate,
weight: task.topWeight,
};
});
}
Expand Down
Loading