feat: deduplicated player adding

This commit is contained in:
eneller
2026-01-31 15:52:06 +01:00
parent 5408a8d9b6
commit 1bc97df0da
9 changed files with 131 additions and 58 deletions

View File

@@ -1,5 +1,5 @@
<main class="main"> <main class="main">
<app-screen-basic [players]="players"></app-screen-basic> <app-screen-edit [players]="players"></app-screen-edit>
</main> </main>
<router-outlet /> <router-outlet />

View File

@@ -8,10 +8,11 @@ import { filter, take } from 'rxjs';
import { ScreenBasicComponent } from "./screen-basic/screen-basic.component"; import { ScreenBasicComponent } from "./screen-basic/screen-basic.component";
import { ScreenRotationsComponent } from './screen-rotations/screen-rotations.component'; import { ScreenRotationsComponent } from './screen-rotations/screen-rotations.component';
import { Player } from './model'; import { Player } from './model';
import { ScreenEditComponent } from './screen-edit/screen-edit.component';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
imports: [NgbModule, RouterOutlet, CommonModule, FormsModule, ScreenBasicComponent, ScreenRotationsComponent], imports: [NgbModule, RouterOutlet, CommonModule, FormsModule, ScreenBasicComponent, ScreenRotationsComponent, ScreenEditComponent],
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrl: './app.component.less' styleUrl: './app.component.less'
}) })

View File

@@ -25,7 +25,18 @@ export class Player{
const values = 'OOOO' const values = 'OOOO'
return [this.name, values]; return [this.name, values];
} }
valueOf(): string{
return this.name;
}
static deSerialize(name: string, values: string): Player{ static deSerialize(name: string, values: string): Player{
return new Player(name); return new Player(name);
} }
static isNew(newplayer:Player, players: Player[]): boolean {
const seen = new Set<string>(players.map(p => p.name));
if (seen.has(newplayer.name)){
return false
}
return true
}
} }

View File

@@ -0,0 +1,30 @@
<div class="justify-content-md-center">
<h1>Players: {{ players.length }}</h1>
<div class="input-group mb-3">
<input
type="text"
[(ngModel)]="newItem"
class="form-control"
placeholder="Enter a name..."
(keyup.enter)="addItem()"
/>
<button class="btn btn-primary" type="button" (click)="addItem()" >
<i class="bi bi-plus-lg"></i> Add
</button>
</div>
@if (alertMessage) {
<ngb-alert #selfClosingAlert type="danger" (closed)="alertMessage = ''">{{ alertMessage }}</ngb-alert>
}
<div class="container mt-4">
<ul class="list-group mb-3">
@for (player of players; track $index) {
<li class="list-group-item">
<a (click)="openPlayerModal(player)" style="display: block; cursor: pointer;">{{ player.name }}</a>
</li>
}
</ul>
</div>
</div>

View File

@@ -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<ScreenEditComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ScreenEditComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ScreenEditComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,63 @@
import { Component, 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';
@Component({
selector: 'app-screen-edit',
imports: [FormsModule, NgbAlert],
templateUrl: './screen-edit.component.html',
styleUrl: './screen-edit.component.less'
})
export class ScreenEditComponent {
@Input() players!: Player[];
@ViewChild('selfClosingAlert', { static: false })selfClosingAlert!: NgbAlert;
private _message$ = new Subject<string>();
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 (Player.isNew(newPlayer, this.players)){
this.players.push(newPlayer);
this.newItem = '';
}
else{
this.setAlertMessage(newPlayer.name);
}
}
}
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.`);
}
}

View File

@@ -1,30 +1 @@
<div class="row justify-content-md-center"> <p>rotations!</p>
<div class="container mt-4">
<h2>Players</h2>
<ul class="list-group mb-3">
@for (player of players; track $index) {
<li class="list-group-item">{{ player.name }}</li>
<button class="btn btn-outline-primary" (click)="openPlayerModal(player)">
<div>Edit Roles</div>
</button>
}
</ul>
<div class="input-group mb-3">
<input
type="text"
[(ngModel)]="newItem"
class="form-control"
placeholder="Enter a name..."
(keyup.enter)="addItem()"
/>
<button
class="btn btn-outline-secondary"
type="button"
(click)="addItem()"
>
<i class="bi bi-plus-lg"></i> Add
</button>
</div>
</div>
</div>

View File

@@ -2,8 +2,6 @@
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { Player } from '../model'; import { Player } from '../model';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { ModalRotationsComponent } from '../modal-rotations/modal-rotations.component';
@Component({ @Component({
selector: 'app-screen-rotations', selector: 'app-screen-rotations',
@@ -13,28 +11,4 @@ import { ModalRotationsComponent } from '../modal-rotations/modal-rotations.comp
}) })
export class ScreenRotationsComponent { export class ScreenRotationsComponent {
@Input() players!: Player[]; @Input() players!: Player[];
newItem: string = "";
constructor(private modalService: NgbModal) {}
addItem() {
if (this.newItem.trim()) {
this.players.push( new Player(this.newItem));
this.newItem = "";
}
}
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');
});
*/
}
} }