From c4a2307530a88023d89ca57fb6b496906f489713 Mon Sep 17 00:00:00 2001 From: eneller Date: Sun, 1 Feb 2026 10:40:05 +0100 Subject: [PATCH] feat: 3 screens for rotations commit 563116331ffd2ba93f75702908338a7148cb1e7b Author: eneller Date: Sun Feb 1 10:28:19 2026 +0100 fix: edit list items commit 1946c599befd371972948c2095d5bedf902b09c9 Author: eneller Date: Sun Feb 1 10:19:16 2026 +0100 fix: minor misc ui and data commit 7c7762515b9b7123ae818799fc876736df5b9433 Author: eneller Date: Sat Jan 31 23:02:01 2026 +0100 feat: navbar commit aaa0ab063842da1f6db2ba748c95717873f748f6 Author: eneller Date: Sat Jan 31 21:05:23 2026 +0100 feat: remove players commit 4082932095ad388e2993d39e5716c1fd34529784 Author: eneller Date: Sat Jan 31 20:36:49 2026 +0100 feat: dataservice commit b6d34ce262fef26f0828fba2b96e144a4f35c9db Author: eneller Date: Sat Jan 31 17:12:37 2026 +0100 wip: rotations accordion commit 764ce43138293037813639ebd5f6fe0d226df7ed Author: eneller Date: Sat Jan 31 16:29:11 2026 +0100 refactor: basic screen commit 1bc97df0dac7503e41a0738f6bd26a94acf7445e Author: eneller Date: Sat Jan 31 15:52:06 2026 +0100 feat: deduplicated player adding commit 5408a8d9b690d8a8c4cad9520fa90090a97e67cf Author: eneller Date: Sat Jan 31 00:31:01 2026 +0100 central players list commit 665cb25d3414c01b0d68e000fcb437f16b9cb84d Author: eneller Date: Fri Jan 30 23:15:16 2026 +0100 feat: modal commit 3b8e5145c33eec3462968f8d3c343014a860a608 Author: eneller Date: Fri Jan 30 22:51:09 2026 +0100 wip: player modal commit 51414f5a99687b1f23371d1890965d6442e5f87e Author: eneller Date: Fri Jan 30 22:03:48 2026 +0100 refactor: extract randomizer to component --- angular.json | 1 + package-lock.json | 17 +++++ package.json | 1 + src/app/app.component.html | 65 ++-------------- src/app/app.component.less | 20 +++++ src/app/app.component.ts | 67 ++++------------- src/app/app.routes.ts | 16 +++- src/app/data.service.spec.ts | 16 ++++ src/app/data.service.ts | 33 ++++++++ .../modal-rotations.component.html | 73 ++++++++++++++++++ .../modal-rotations.component.less | 0 .../modal-rotations.component.spec.ts | 23 ++++++ .../modal-rotations.component.ts | 21 ++++++ src/app/model.ts | 42 +++++++++++ .../screen-basic/screen-basic.component.html | 42 +++++++++++ .../screen-basic/screen-basic.component.less | 0 .../screen-basic.component.spec.ts | 23 ++++++ .../screen-basic/screen-basic.component.ts | 49 ++++++++++++ .../screen-edit/screen-edit.component.html | 33 ++++++++ .../screen-edit/screen-edit.component.less | 0 .../screen-edit/screen-edit.component.spec.ts | 23 ++++++ src/app/screen-edit/screen-edit.component.ts | 67 +++++++++++++++++ .../screen-rotations.component.html | 75 +++++++++++++++++++ .../screen-rotations.component.less | 0 .../screen-rotations.component.spec.ts | 23 ++++++ .../screen-rotations.component.ts | 32 ++++++++ src/app/util.ts | 7 ++ 27 files changed, 659 insertions(+), 110 deletions(-) create mode 100644 src/app/data.service.spec.ts create mode 100644 src/app/data.service.ts create mode 100644 src/app/modal-rotations/modal-rotations.component.html create mode 100644 src/app/modal-rotations/modal-rotations.component.less create mode 100644 src/app/modal-rotations/modal-rotations.component.spec.ts create mode 100644 src/app/modal-rotations/modal-rotations.component.ts create mode 100644 src/app/model.ts create mode 100644 src/app/screen-basic/screen-basic.component.html create mode 100644 src/app/screen-basic/screen-basic.component.less create mode 100644 src/app/screen-basic/screen-basic.component.spec.ts create mode 100644 src/app/screen-basic/screen-basic.component.ts create mode 100644 src/app/screen-edit/screen-edit.component.html create mode 100644 src/app/screen-edit/screen-edit.component.less create mode 100644 src/app/screen-edit/screen-edit.component.spec.ts create mode 100644 src/app/screen-edit/screen-edit.component.ts create mode 100644 src/app/screen-rotations/screen-rotations.component.html create mode 100644 src/app/screen-rotations/screen-rotations.component.less create mode 100644 src/app/screen-rotations/screen-rotations.component.spec.ts create mode 100644 src/app/screen-rotations/screen-rotations.component.ts create mode 100644 src/app/util.ts diff --git a/angular.json b/angular.json index 433ccef..72fb5bd 100644 --- a/angular.json +++ b/angular.json @@ -35,6 +35,7 @@ ], "styles": [ "node_modules/bootstrap/dist/css/bootstrap.min.css", + "node_modules/bootstrap-icons/font/bootstrap-icons.min.css", "src/styles.less" ], "scripts": [], diff --git a/package-lock.json b/package-lock.json index 659f07e..757a74c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "@ng-bootstrap/ng-bootstrap": "^18.0.0", "@popperjs/core": "^2.11.8", "bootstrap": "^5.3.3", + "bootstrap-icons": "^1.13.1", "express": "^4.18.2", "rxjs": "~7.8.0", "sonarqube-scanner": "^4.3.0", @@ -6249,6 +6250,22 @@ "@popperjs/core": "^2.11.8" } }, + "node_modules/bootstrap-icons": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.13.1.tgz", + "integrity": "sha512-ijombt4v6bv5CLeXvRWKy7CuM3TRTuPEuGaGKvTV5cz65rQSY8RQ2JcHt6b90cBBAC7s8fsf2EkQDldzCoXUjw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", diff --git a/package.json b/package.json index 18cf17b..c7b133f 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "@ng-bootstrap/ng-bootstrap": "^18.0.0", "@popperjs/core": "^2.11.8", "bootstrap": "^5.3.3", + "bootstrap-icons": "^1.13.1", "express": "^4.18.2", "rxjs": "~7.8.0", "sonarqube-scanner": "^4.3.0", diff --git a/src/app/app.component.html b/src/app/app.component.html index 15b378d..f56c0d5 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,59 +1,10 @@
-
-

Please select the number of teams:

- - -
-
- - -
- -
- - - - - - - - - - - @for (team of teamsArray; track $index) { - - - - - } - -
Removed: {{ duplicateNames }}
SizeNames
{{ team.length | number }}{{ team }}
- -
+
- - + \ No newline at end of file diff --git a/src/app/app.component.less b/src/app/app.component.less index b6c917b..4217717 100644 --- a/src/app/app.component.less +++ b/src/app/app.component.less @@ -15,4 +15,24 @@ tr{ .wrap-cell{ word-break: break-all; white-space: normal; +} +.main{ + margin-bottom: 5rem; + padding: 1rem; + overflow-y: auto; +} +.custom-navbar{ + height: 3rem; + width: 100vw; + position: fixed; + bottom: 0; + display: flex; + justify-content: space-around; /* Distribute items evenly */ + z-index: 1000; +} +.custom-navitem{ + text-align: center; /* Center the content of each item */ + flex: 1; +} +.nav-link.active{ } \ No newline at end of file diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 292df5e..5d2164f 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,77 +1,40 @@ -import { Component, OnInit} from '@angular/core'; +import { Component, inject, OnInit} from '@angular/core'; import { FormsModule } from '@angular/forms'; -import { RouterOutlet, ActivatedRoute } from '@angular/router'; +import { RouterOutlet, ActivatedRoute, RouterLink } from '@angular/router'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { CommonModule } from '@angular/common'; import { filter, take } from 'rxjs'; +import { ScreenBasicComponent } from "./screen-basic/screen-basic.component"; +import { ScreenRotationsComponent } from './screen-rotations/screen-rotations.component'; +import { Player } from './model'; +import { ScreenEditComponent } from './screen-edit/screen-edit.component'; +import { DataService } from './data.service'; @Component({ selector: 'app-root', - imports: [NgbModule, RouterOutlet, CommonModule, FormsModule], + imports: [NgbModule, RouterOutlet, CommonModule, FormsModule, ScreenBasicComponent, ScreenRotationsComponent, ScreenEditComponent, RouterLink], templateUrl: './app.component.html', styleUrl: './app.component.less' }) export class AppComponent implements OnInit { title = 'vb'; - playerNamesValue = ""; - numTeamsSelectorValue = "2"; - numTeamsSelected = 2; - nTeamsValue = "4"; - teamsArray: string[][] = []; - duplicateNames: string[] = []; + data = inject(DataService); - constructor(private activatedRoute: ActivatedRoute){} + constructor(public route: ActivatedRoute){} ngOnInit(): void { - this.activatedRoute.queryParams.pipe( + this.route.queryParams.pipe( filter(params => Object.keys(params).length > 0), // Only proceed if params are not empty take(1) ).subscribe(params => { - const names = params['names']?.replaceAll(',', '\n'); - if (names) { - this.playerNamesValue = names; + if (params['names']){ + this.data.setPlayers( + params['names'].split(',').map((name: string) => new Player(name)) + ); } }); } - onButtonGenerate(textinput: string): void{ - if(this.numTeamsSelectorValue === 'n'){ - this.numTeamsSelected = Number(this.nTeamsValue); - } - else{ - this.numTeamsSelected = Number(this.numTeamsSelectorValue); - } - let nameslist = this.playerNamesValue - .split('\n') - .map(function(str){return str.trim();}) - .filter(function(str){return str}); // boolean interpretation is same as non-empty - // remove duplicates by using a Set - let namesset = new Set(nameslist); - let names = [...namesset]; - - - let teams = Array.from({ length: this.numTeamsSelected }, () => []); - let playersPerTeam = Math.floor(names.length / this.numTeamsSelected); - - let nameslen = names.length; - function* iter(list: any[]){ - let index = 0; - while(true){ - yield list[index % list.length]; - index++; - } - } - let iterator = iter(teams); - for(let i =0; i < nameslen; i++){ - let index = Math.floor(Math.random()* names.length); - let n = names[index]; - names.splice(index,1); - let team = iterator.next().value; - team.push(n); - - } - this.teamsArray = teams; - } } diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index dc39edb..482d17e 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -1,3 +1,17 @@ import { Routes } from '@angular/router'; +import { ScreenBasicComponent } from './screen-basic/screen-basic.component'; +import { ScreenEditComponent } from './screen-edit/screen-edit.component'; +import { ScreenRotationsComponent } from './screen-rotations/screen-rotations.component'; +import { AppComponent } from './app.component'; -export const routes: Routes = []; +export const routes: Routes = [ + { + path: '', + children: [ + { path: '', redirectTo: 'edit', pathMatch: 'full' }, + { path: 'edit', component: ScreenEditComponent }, + { path: 'basic', component: ScreenBasicComponent }, + { path: 'rotations', component: ScreenRotationsComponent } + ] + } +]; diff --git a/src/app/data.service.spec.ts b/src/app/data.service.spec.ts new file mode 100644 index 0000000..38e8d9e --- /dev/null +++ b/src/app/data.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { DataService } from './data.service'; + +describe('DataService', () => { + let service: DataService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(DataService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/data.service.ts b/src/app/data.service.ts new file mode 100644 index 0000000..122aa63 --- /dev/null +++ b/src/app/data.service.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@angular/core'; +import { Player } from './model'; + +@Injectable({ + providedIn: 'root' +}) +export class DataService { + private players: Player[] = []; + + setPlayers(players: Player[]){ + for (let player of players){ + this.addPlayer(player); + } + } + + addPlayer(player: Player): boolean{ + if (player.name.trim() && Player.isNew(player, this.players)){ + this.players.push(player); + return true + } + return false + } + removePlayer(player: Player){ + const index = this.players.indexOf(player); + if (index !== -1) { + this.players.splice(index, 1); + } + } + getPlayers(): Player[] { + let clone =Object.assign([],this.players); + return clone + } +} diff --git a/src/app/modal-rotations/modal-rotations.component.html b/src/app/modal-rotations/modal-rotations.component.html new file mode 100644 index 0000000..9f32e34 --- /dev/null +++ b/src/app/modal-rotations/modal-rotations.component.html @@ -0,0 +1,73 @@ + + + + diff --git a/src/app/modal-rotations/modal-rotations.component.less b/src/app/modal-rotations/modal-rotations.component.less new file mode 100644 index 0000000..e69de29 diff --git a/src/app/modal-rotations/modal-rotations.component.spec.ts b/src/app/modal-rotations/modal-rotations.component.spec.ts new file mode 100644 index 0000000..111191d --- /dev/null +++ b/src/app/modal-rotations/modal-rotations.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ModalRotationsComponent } from './modal-rotations.component'; + +describe('ModalRotationsComponent', () => { + let component: ModalRotationsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ModalRotationsComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ModalRotationsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/modal-rotations/modal-rotations.component.ts b/src/app/modal-rotations/modal-rotations.component.ts new file mode 100644 index 0000000..1530d7a --- /dev/null +++ b/src/app/modal-rotations/modal-rotations.component.ts @@ -0,0 +1,21 @@ +import { Component, Input } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { Player} from '../model'; +import { FormsModule } from '@angular/forms'; + +@Component({ + selector: 'app-modal-rotations', + imports: [FormsModule], + templateUrl: './modal-rotations.component.html', + styleUrl: './modal-rotations.component.less' +}) +export class ModalRotationsComponent { + @Input() player!: Player; + + constructor(public activeModal: NgbActiveModal) {} + + savePlayer() { + // You can emit an event or handle the save logic here + this.activeModal.close(this.player); + } +} diff --git a/src/app/model.ts b/src/app/model.ts new file mode 100644 index 0000000..2608e7f --- /dev/null +++ b/src/app/model.ts @@ -0,0 +1,42 @@ +export class Player{ + name: string; + outside: boolean; + middle: boolean; + opposite: boolean; // dia + setter: boolean; + libero: boolean; + + constructor( + name:string, + outside: boolean = false, + middle: boolean = false, + opposite: boolean = false, // dia + setter: boolean = false, + libero: boolean = false, + ){ + this.name = name; + this.outside = outside; + this.middle = middle; + this.opposite = opposite; + this.setter = setter; + this.libero = libero; + } + serialize(): string[] { + const values = 'OOOO' + return [this.name, values]; + } + + valueOf(): string{ + return this.name; + } + static deSerialize(name: string, values: string): Player{ + return new Player(name); + } + static isNew(newplayer:Player, players: Player[]): boolean { + const seen = new Set(players.map(p => p.name)); + if (seen.has(newplayer.name)){ + return false + } + return true +} +} diff --git a/src/app/screen-basic/screen-basic.component.html b/src/app/screen-basic/screen-basic.component.html new file mode 100644 index 0000000..9809c52 --- /dev/null +++ b/src/app/screen-basic/screen-basic.component.html @@ -0,0 +1,42 @@ +
+ +

Random Teams

+ +
+ +
+ + + + + + + + + + @for (team of teamsArray; track $index) { + + + + + } + +
SizeNames
{{ team.length | number }}{{ team }}
+ +
diff --git a/src/app/screen-basic/screen-basic.component.less b/src/app/screen-basic/screen-basic.component.less new file mode 100644 index 0000000..e69de29 diff --git a/src/app/screen-basic/screen-basic.component.spec.ts b/src/app/screen-basic/screen-basic.component.spec.ts new file mode 100644 index 0000000..68482f0 --- /dev/null +++ b/src/app/screen-basic/screen-basic.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ScreenBasicComponent } from './screen-basic.component'; + +describe('ScreenBasicComponent', () => { + let component: ScreenBasicComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ScreenBasicComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ScreenBasicComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/screen-basic/screen-basic.component.ts b/src/app/screen-basic/screen-basic.component.ts new file mode 100644 index 0000000..dbb0ea3 --- /dev/null +++ b/src/app/screen-basic/screen-basic.component.ts @@ -0,0 +1,49 @@ +// Original Team Generation Screen for arbitrary size and number of teams +import { Component, inject} from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { RouterOutlet, ActivatedRoute } from '@angular/router'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { CommonModule } from '@angular/common'; +import { Player } from '../model'; +import { iter } from '../util'; +import { DataService } from '../data.service'; + +@Component({ + selector: 'app-screen-basic', + imports: [NgbModule, RouterOutlet, CommonModule, FormsModule], + templateUrl: './screen-basic.component.html', + styleUrl: './screen-basic.component.less' +}) +export class ScreenBasicComponent { + data = inject(DataService); + numTeamsSelectorValue = "2"; + numTeamsSelected = 2; + nTeamsValue = "4"; + teamsArray: string[][] = []; + + onButtonGenerate(): void{ + if(this.numTeamsSelectorValue === 'n'){ + this.numTeamsSelected = Number(this.nTeamsValue); + } + else{ + this.numTeamsSelected = Number(this.numTeamsSelectorValue); + } + + let teams = Array.from({ length: this.numTeamsSelected }, () => []); + // clone array here + let localPlayers: Player[] = this.data.getPlayers(); + + let nameslen = localPlayers.length; + let iterator = iter(teams); + for(let i =0; i < nameslen; i++){ + let index = Math.floor(Math.random()* localPlayers.length); + let n = localPlayers[index]; + localPlayers.splice(index,1); + let team = iterator.next().value; + team.push(n.name); + + } + this.teamsArray = teams; + } + +} \ No newline at end of file diff --git a/src/app/screen-edit/screen-edit.component.html b/src/app/screen-edit/screen-edit.component.html new file mode 100644 index 0000000..625549e --- /dev/null +++ b/src/app/screen-edit/screen-edit.component.html @@ -0,0 +1,33 @@ +
+

Players: {{ data.getPlayers().length }}

+
+ + +
+ @if (alertMessage) { + {{ alertMessage }} + } +
+
    + @for (player of data.getPlayers(); track $index) { +
  • + {{ player.name }} + +
  • + } +
+ +
+ + +
\ No newline at end of file diff --git a/src/app/screen-edit/screen-edit.component.less b/src/app/screen-edit/screen-edit.component.less new file mode 100644 index 0000000..e69de29 diff --git a/src/app/screen-edit/screen-edit.component.spec.ts b/src/app/screen-edit/screen-edit.component.spec.ts new file mode 100644 index 0000000..bf13f40 --- /dev/null +++ b/src/app/screen-edit/screen-edit.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ScreenEditComponent } from './screen-edit.component'; + +describe('ScreenEditComponent', () => { + let component: ScreenEditComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ScreenEditComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ScreenEditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/screen-edit/screen-edit.component.ts b/src/app/screen-edit/screen-edit.component.ts new file mode 100644 index 0000000..d9a6697 --- /dev/null +++ b/src/app/screen-edit/screen-edit.component.ts @@ -0,0 +1,67 @@ +import { Component, inject, Input, ViewChild } from '@angular/core'; +import { NgbAlert, NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { Player } from '../model'; +import { ModalRotationsComponent } from '../modal-rotations/modal-rotations.component'; +import { FormsModule } from '@angular/forms'; +import { Subject } from 'rxjs/internal/Subject'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { debounceTime, tap } from 'rxjs/operators'; +import { DataService } from '../data.service'; + +@Component({ + selector: 'app-screen-edit', + imports: [FormsModule, NgbAlert], + templateUrl: './screen-edit.component.html', + styleUrl: './screen-edit.component.less' +}) +export class ScreenEditComponent { + data = inject(DataService); + @ViewChild('selfClosingAlert', { static: false })selfClosingAlert!: NgbAlert; + private _message$ = new Subject(); + newItem: string = ""; + alertMessage = ''; + + constructor(private modalService: NgbModal) { + this._message$ + .pipe( + takeUntilDestroyed(), + tap((message) => (this.alertMessage = message)), + debounceTime(5000), + ) + .subscribe(() => this.selfClosingAlert?.close()); + } + + addItem() { + const name = this.newItem.trim() + if (name) { + let newPlayer = new Player(name); + if(this.data.addPlayer(newPlayer)){ + this.newItem = ''; + return + } + else{ + this.setAlertMessage(newPlayer.name); + } + } + } + removeItem(player: Player){ + this.data.removePlayer(player); + } + + openPlayerModal(player: Player){ + const modalRef = this.modalService.open(ModalRotationsComponent); + modalRef.componentInstance.player = player; + /* + modalRef.result.then((updatedPlayer) => { + // Handle the updated player data if needed + console.log('Player updated:', updatedPlayer); + }).catch((error) => { + console.log('Modal dismissed'); + }); + */ + } + public setAlertMessage(s: string) { + this._message$.next(`"${s}" already exists. Please choose a unique name.`); + } + +} diff --git a/src/app/screen-rotations/screen-rotations.component.html b/src/app/screen-rotations/screen-rotations.component.html new file mode 100644 index 0000000..3f74f69 --- /dev/null +++ b/src/app/screen-rotations/screen-rotations.component.html @@ -0,0 +1,75 @@ +
+

Rotations

+
+
+

+ +

+
+
+ + @for (player of LiberoPlayers; track $index) { + {{ player.name }} + } + +
+
+
+
+

+ +

+
+
+ + @for (player of SetterPlayers; track $index) { + {{ player.name }} + } + +
+
+
+
+

+ +

+
+
+ + @for (player of OppositePlayers; track $index) { + {{ player.name }} + } + +
+
+
+
+

+ +

+
+
+ + @for (player of MiddlePlayers; track $index) { + {{ player.name }} + } + +
+
+
+
+

+ +

+
+
+ + @for (player of OutsidePlayers; track $index) { + {{ player.name }} + } + +
+
+
+
+
\ No newline at end of file diff --git a/src/app/screen-rotations/screen-rotations.component.less b/src/app/screen-rotations/screen-rotations.component.less new file mode 100644 index 0000000..e69de29 diff --git a/src/app/screen-rotations/screen-rotations.component.spec.ts b/src/app/screen-rotations/screen-rotations.component.spec.ts new file mode 100644 index 0000000..0998926 --- /dev/null +++ b/src/app/screen-rotations/screen-rotations.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ScreenRotationsComponent } from './screen-rotations.component'; + +describe('ScreenRotationsComponent', () => { + let component: ScreenRotationsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ScreenRotationsComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ScreenRotationsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/screen-rotations/screen-rotations.component.ts b/src/app/screen-rotations/screen-rotations.component.ts new file mode 100644 index 0000000..f7c5485 --- /dev/null +++ b/src/app/screen-rotations/screen-rotations.component.ts @@ -0,0 +1,32 @@ +// Team Generation Screen respecting volleyball roles as defined by `../model/Player` +import { Component, inject, Input } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { Player } from '../model'; +import { NgbAccordionBody, NgbAccordionCollapse, NgbAccordionHeader, NgbAccordionItem, NgbAccordionButton, NgbAccordionDirective, NgbAccordionToggle } from '@ng-bootstrap/ng-bootstrap'; +import { DataService } from '../data.service'; + +@Component({ + selector: 'app-screen-rotations', + imports: [FormsModule, NgbAccordionButton, NgbAccordionDirective, NgbAccordionItem, NgbAccordionHeader, NgbAccordionToggle, NgbAccordionBody, NgbAccordionCollapse,], + templateUrl: './screen-rotations.component.html', + styleUrl: './screen-rotations.component.less' +}) +export class ScreenRotationsComponent { + data = inject(DataService); + + get OutsidePlayers(): Player[] { + return this.data.getPlayers().filter(player => player.outside); + } + get MiddlePlayers(): Player[] { + return this.data.getPlayers().filter(player => player.middle); + } + get OppositePlayers(): Player[] { + return this.data.getPlayers().filter(player => player.opposite); + } + get SetterPlayers(): Player[] { + return this.data.getPlayers().filter(player => player.setter); + } + get LiberoPlayers(): Player[] { + return this.data.getPlayers().filter(player => player.libero); + } +} \ No newline at end of file diff --git a/src/app/util.ts b/src/app/util.ts new file mode 100644 index 0000000..22bd05b --- /dev/null +++ b/src/app/util.ts @@ -0,0 +1,7 @@ +export function* iter(list: any[]){ + let index = 0; + while(true){ + yield list[index % list.length]; + index++; + } +} \ No newline at end of file