-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Boards board #41
Boards board #41
Changes from 1 commit
d1d86c3
3d5cb5e
d00274b
d780099
3b4bd1b
6283bb1
4d47a21
5d5632e
57d9ccd
9ef08d9
b42f7b7
d69230d
5265927
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,40 +1,58 @@ | ||
<h1>Board</h1> | ||
<div class="flex flex-col gap-[16px]"> | ||
<h1 class="text-3xl">{{ modelService.sprintName }}</h1> | ||
|
||
<div class="w-400 max-w-full mr-0 mb-25 inline-block align-top ml-[24px] min-w-[300px]"> | ||
<h2 class="text-xl font-semibold mb-4">To do</h2> | ||
<div class="flex items-center gap-4"> | ||
<mat-form-field subscriptSizing="dynamic"> | ||
<mat-label>Search</mat-label> | ||
<input #filterField matInput type="text" (input)="updateFilterString(filterField.value)" /> | ||
</mat-form-field> | ||
|
||
<div cdkDropList #todoList="cdkDropList" [cdkDropListData]="todo" [cdkDropListConnectedTo]="[doneList, inprogressList]" | ||
class="border border-solid border-gray-900 min-h-[200px] container-background rounded-md overflow-hidden block" | ||
(cdkDropListDropped)="drop2($event)"> | ||
@for (item of todo; track item) { | ||
<app-taskcard cdkDrag [content]="item.content" [taskid]="item.taskid" [assigneeAvatarUrl]="item.assigneeAvatar" [assigneeName]="item.assigneeName"> | ||
</app-taskcard> | ||
} | ||
<div class="flex flex-col gap-[1px]"> | ||
<span class="text-xs">Group by</span> | ||
<button mat-raised-button [matMenuTriggerFor]="groupingMenu" color="primary"> | ||
<mat-icon>expand_more</mat-icon> | ||
{{ taskGrouping }} | ||
</button> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<div class="w-400 max-w-full mb-25 inline-block align-top ml-[24px] min-w-[300px]"> | ||
<h2 class="text-xl font-semibold mb-4">Done</h2> | ||
<div class="flex flex-col gap-[8px]"> | ||
<columnset> | ||
<h2 todo class="font-bold m-auto p-1">TODO ({{ modelService.todoSize() }}/{{ modelService.allTasksSize() }})</h2> | ||
<h2 inprogress class="font-bold m-auto p-1">INPROGRESS ({{ modelService.inprogressSize() }}/{{ modelService.allTasksSize() }})</h2> | ||
<h2 done class="font-bold m-auto p-1">DONE ({{ modelService.doneSize() }}/{{ modelService.allTasksSize() }})</h2> | ||
</columnset> | ||
|
||
<div cdkDropList #inprogressList="cdkDropList" [cdkDropListData]="inprogress" [cdkDropListConnectedTo]="[doneList, todoList]" | ||
class="border border-solid border-gray-900 min-h-[200px] container-background rounded-md overflow-hidden block" | ||
(cdkDropListDropped)="drop2($event)"> | ||
@for (item of inprogress; track item) { | ||
<app-taskcard cdkDrag [content]="item.content" [taskid]="item.taskid" [assigneeAvatarUrl]="item.assigneeAvatar" [assigneeName]="item.assigneeName"> | ||
</app-taskcard> | ||
} | ||
</div> | ||
</div> | ||
|
||
<div class="w-400 max-w-full mb-25 inline-block align-top ml-[24px] min-w-[300px]"> | ||
<h2 class="text-xl font-semibold mb-4">In Progress</h2> | ||
@for (slice of slicesModelService.slices; track slice) { | ||
<div> | ||
@if (taskGrouping == TaskGroupingEnum.NONE) { | ||
<slice [sliceDescriptor]="slice"></slice> | ||
} @else if(taskGrouping == TaskGroupingEnum.BY_ASSIGNEE) { | ||
<button mat-button (click)="toggleHidden(sliceWrapper)"> | ||
<mat-icon> | ||
{{ isHidden(sliceWrapper) ? "chevron_right" : "expand_more" }} | ||
</mat-icon> | ||
<div class="flex items-center space-x-1"> | ||
<img class="w-5 h-5 rounded-full" src="{{ slice.metadata.avatarUrl }}" /> | ||
<span> | ||
{{ slice.metadata.firstName + " " + slice.metadata.familyName }} | ||
</span> | ||
</div> | ||
</button> | ||
|
||
<div cdkDropList #doneList="cdkDropList" [cdkDropListData]="done" [cdkDropListConnectedTo]="[todoList, inprogressList]" | ||
class="border border-solid border-gray-900 min-h-[200px] container-background rounded-md overflow-hidden block" | ||
(cdkDropListDropped)="drop2($event)"> | ||
@for (item of done; track item) { | ||
<app-taskcard cdkDrag [content]="item.content" [taskid]="item.taskid" [assigneeAvatarUrl]="item.assigneeAvatar" [assigneeName]="item.assigneeName"> | ||
</app-taskcard> | ||
<div #sliceWrapper> | ||
<slice [sliceDescriptor]="slice"></slice> | ||
</div> | ||
} | ||
</div> | ||
} | ||
</div> | ||
</div> | ||
|
||
<mat-menu #groupingMenu="matMenu"> | ||
@for (taskGrouping of TASK_GROUPINGS; track taskGrouping) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. błąd z typowaniem |
||
<button mat-menu-item (click)="updateTaskGrouping(taskGrouping)"> | ||
{{ taskGrouping }} | ||
</button> | ||
} | ||
</mat-menu> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,84 +1,120 @@ | ||
import { Component } from '@angular/core'; | ||
import { | ||
CdkDragDrop, | ||
moveItemInArray, | ||
transferArrayItem, | ||
CdkDrag, | ||
CdkDropList, | ||
} from '@angular/cdk/drag-drop'; | ||
import { TaskcardComponent } from './taskcard/taskcard.component'; | ||
import { Component, OnInit } from '@angular/core'; | ||
import { CommonModule } from '@angular/common'; | ||
import { MatInputModule } from '@angular/material/input'; | ||
import { MatButtonModule } from '@angular/material/button'; | ||
import { MatMenuModule } from '@angular/material/menu'; | ||
import { SlicesModelService, TaskChangedColumnEvent, TaskChangedGroupEvent } from './slice/slices_model.service'; | ||
import { SliceService } from './slice/slice.service'; | ||
import { SliceComponent } from './slice/slice.component'; | ||
import { Assignee, SAMPLE_ASSIGNEES, SAMPLE_TASKS, Task } from './model/model'; | ||
import { ModelService } from './model/model.service'; | ||
import { ColumnSetLayout } from './layout.component'; | ||
import { MatIconModule } from '@angular/material/icon'; | ||
|
||
@Component({ | ||
selector: 'app-board', | ||
standalone: true, | ||
imports: [CdkDropList, CdkDrag, TaskcardComponent], | ||
imports: [ | ||
CommonModule, | ||
MatInputModule, | ||
MatMenuModule, | ||
MatButtonModule, | ||
SliceComponent, | ||
MatIconModule, | ||
ColumnSetLayout, | ||
], | ||
providers: [ | ||
ModelService, | ||
SlicesModelService, | ||
SliceService, | ||
], | ||
templateUrl: './board.component.html', | ||
}) | ||
export class BoardComponent { | ||
export class BoardComponent implements OnInit { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Poprawić return type w całej klasie |
||
|
||
todo: Task[] = [ | ||
{ | ||
content: "Buy groceries", | ||
taskid: "t1", | ||
assigneeAvatar: "/assets/corn-icons/icon-72x72.png", | ||
assigneeName: "John Doe" | ||
}, | ||
{ | ||
content: "Read a book", | ||
taskid: "t2", | ||
assigneeAvatar: "/assets/corn-icons/icon-72x72.png", | ||
assigneeName: "Jane Doe" | ||
}, | ||
{ | ||
content: "Write code", | ||
taskid: "t3", | ||
assigneeAvatar: "/assets/corn-icons/icon-72x72.png", | ||
assigneeName: "Alice Smith" | ||
} | ||
]; | ||
|
||
inprogress: Task[] = [ | ||
{ | ||
content: "Design a website", | ||
taskid: "t4", | ||
assigneeAvatar: "/assets/corn-icons/icon-72x72.png", | ||
assigneeName: "Bob Johnson" | ||
protected filterString = ""; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. brak typowania |
||
protected taskGrouping = TaskGrouping.BY_ASSIGNEE; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. brak typowania |
||
|
||
constructor( | ||
protected readonly modelService: ModelService, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. jeżeli service jest używany tylko w tej klasie to powinien być |
||
protected readonly slicesModelService: SlicesModelService, | ||
) { } | ||
|
||
ngOnInit() { | ||
this.modelService.modelChangeHandler = () => { | ||
this.slicesModelService.rebuildSlices(); | ||
} | ||
]; | ||
|
||
done: Task[] = [ | ||
{ | ||
content: "Finish project", | ||
taskid: "t5", | ||
assigneeAvatar: "/assets/corn-icons/icon-72x72.png", | ||
assigneeName: "Charlie Brown" | ||
}, | ||
{ | ||
content: "Submit report", | ||
taskid: "t6", | ||
assigneeAvatar: "/assets/corn-icons/icon-72x72.png", | ||
assigneeName: "Diana Miller" | ||
this.modelService.assignees = Object.values(SAMPLE_ASSIGNEES); | ||
this.modelService.todo = SAMPLE_TASKS.TODO; | ||
this.modelService.inprogress = SAMPLE_TASKS.INPROGRESS; | ||
this.modelService.done = SAMPLE_TASKS.DONE; | ||
this.slicesModelService.groupChangedHandler = (event: TaskChangedGroupEvent) => { | ||
if (this.taskGrouping == TaskGrouping.BY_ASSIGNEE && | ||
event.sourceGroupMetadata != event.destinationGroupMetadata | ||
) { | ||
this.modelService.setAssigneeForTask(event.task, event.destinationGroupMetadata); | ||
} | ||
} | ||
]; | ||
|
||
drop2(event: CdkDragDrop<Task[]>) { | ||
if (event.previousContainer === event.container) { | ||
moveItemInArray(event.container.data, event.previousIndex, event.currentIndex); | ||
} else { | ||
transferArrayItem( | ||
event.previousContainer.data, | ||
event.container.data, | ||
event.previousIndex, | ||
event.currentIndex, | ||
this.slicesModelService.columnChangedHandler = (event: TaskChangedColumnEvent) => { | ||
this.modelService.moveTaskToArray(event.task, | ||
event.sourceColumn, event.sourceColumnIndex, | ||
event.destinationColumn, event.destinationColumnIndex | ||
); | ||
} | ||
this.updateFilterString(this.filterString); | ||
this.updateTaskGrouping(this.taskGrouping); | ||
} | ||
|
||
protected updateFilterString(filterString: string) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. brak typowania |
||
filterString = filterString.toLowerCase(); | ||
this.filterString = filterString; | ||
const stringPredicate = (s: string) => s.toLowerCase().includes(filterString); | ||
this.slicesModelService.filter = (t: Task) => { | ||
return stringPredicate(t.content) | ||
|| stringPredicate(t.assignee.firstName) | ||
|| stringPredicate(t.assignee.firstName + " " + t.assignee.familyName) | ||
|| stringPredicate(t.assignee.familyName) | ||
|| stringPredicate(t.taskid); | ||
}; | ||
this.slicesModelService.rebuildSlices(); | ||
} | ||
|
||
protected updateTaskGrouping(taskGrouping: TaskGrouping) { | ||
this.taskGrouping = taskGrouping; | ||
this.slicesModelService.grouper = this.GROUPERS[taskGrouping]; | ||
this.slicesModelService.rebuildSlices(); | ||
} | ||
|
||
protected toggleHidden(element: any) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. czemu typ |
||
element.hidden = !element.hidden; | ||
} | ||
|
||
protected isHidden(element: any) { | ||
return element.hidden; | ||
} | ||
|
||
protected readonly TaskGroupingEnum = TaskGrouping; | ||
protected readonly TASK_GROUPINGS = Object.values(TaskGrouping); | ||
protected readonly GROUPERS = { | ||
[TaskGrouping.NONE]: (ungrouped: Task[]) => { | ||
return [{ metadata: null, group: ungrouped }] | ||
}, | ||
[TaskGrouping.BY_ASSIGNEE]: (ungrouped: Task[]) => { | ||
return this.modelService.assignees.map((a: Assignee) => { | ||
return { | ||
metadata: a, | ||
group: ungrouped.filter((t: Task) => { | ||
return t.assignee.firstName == a.firstName | ||
&& t.assignee.familyName == a.familyName | ||
}) | ||
}; | ||
}); | ||
}, | ||
}; | ||
|
||
} | ||
|
||
interface Task { | ||
content: string; | ||
taskid: string; | ||
assigneeAvatar: string; | ||
assigneeName: string; | ||
} | ||
enum TaskGrouping { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. czemu ten |
||
NONE = "None", | ||
BY_ASSIGNEE = "By assignee", | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<div [matMenuTriggerFor]="instance" (click)="update()"> | ||
<ng-content></ng-content> | ||
</div> | ||
|
||
<mat-menu #instance> | ||
<mat-form-field (click)="$event.stopPropagation()" subscriptSizing="dynamic"> | ||
<mat-label>Search members</mat-label> | ||
<input matInput #filterStringInput type="text" (click)="$event.stopPropagation()" (input)="update()" /> | ||
</mat-form-field> | ||
|
||
<mat-divider></mat-divider> | ||
|
||
@for(item of filteredAssignees; track item) { | ||
<button mat-menu-item (click)="assigneeChanged(item)"> | ||
<div class="flex items-center space-x-3"> | ||
<ng-container *ngTemplateOutlet="zerowidthspace"></ng-container> | ||
<img class="w-6 h-6 rounded-full" src="{{ item.avatarUrl }}" title="{{ item.firstName + ' ' + item.familyName }}" /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. brak |
||
<span> | ||
{{ item.firstName + " " + item.familyName }} | ||
</span> | ||
</div> | ||
</button> | ||
} | ||
</mat-menu> | ||
|
||
<ng-template #zerowidthspace></ng-template> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { Input } from '@angular/core'; | ||
import { Assignee, Task } from '../model/model'; | ||
import { MatMenuModule } from '@angular/material/menu'; | ||
import { Component, ViewChild } from '@angular/core'; | ||
import { FormControl } from '@angular/forms'; | ||
import { CommonModule } from '@angular/common'; | ||
import { MatInputModule } from '@angular/material/input'; | ||
import { MatButtonModule } from '@angular/material/button'; | ||
import { MatDividerModule } from '@angular/material/divider'; | ||
import { ModelService } from '../model/model.service'; | ||
|
||
@Component({ | ||
selector: 'change-assignee-menu', | ||
standalone: true, | ||
imports: [ | ||
CommonModule, | ||
MatMenuModule, | ||
MatInputModule, | ||
MatButtonModule, | ||
MatDividerModule, | ||
], | ||
templateUrl: './change_assignee_menu.component.html', | ||
}) | ||
export class ChangeAssigneeMenuComponent { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. dodać typowanie metod |
||
|
||
protected myControl = new FormControl(''); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. unused variable |
||
protected filteredAssignees: Assignee[] = []; | ||
|
||
@Input() associatedTask?: Task; | ||
|
||
@ViewChild('filterStringInput') private input?: any; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. czemu |
||
|
||
constructor( | ||
protected readonly modelService: ModelService, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
) { } | ||
|
||
protected update() { | ||
if (!this.input) | ||
return; | ||
const filterString = this.input.nativeElement.value.toLowerCase(); | ||
this.filteredAssignees = this.modelService.assignees.filter(a => | ||
a.firstName.toLowerCase().includes(filterString) || | ||
a.familyName.toLowerCase().includes(filterString) || | ||
(a.firstName + " " + a.familyName).toLowerCase().includes(filterString) | ||
) | ||
} | ||
|
||
protected assigneeChanged(assignee: Assignee) { | ||
if (!this.associatedTask) | ||
return; | ||
this.modelService.setAssigneeForTask(this.associatedTask, assignee); | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
brak
alt
attribute