@ -1,150 +1,87 @@ |
|||||
import { Component, Input, Output, EventEmitter } from '@angular/core'; |
import { Component, Input, Output, EventEmitter } from '@angular/core'; |
||||
import { CommonModule } from '@angular/common'; |
import { CommonModule } from '@angular/common'; |
||||
import { MatExpansionModule } from '@angular/material/expansion'; |
import { MatCardModule } from '@angular/material/card'; |
||||
import { MatFormFieldModule } from '@angular/material/form-field'; |
import { GamePlan } from '../../../core/models/plan.model'; |
||||
import { MatInputModule } from '@angular/material/input'; |
|
||||
import { FormsModule } from '@angular/forms'; |
|
||||
import { PlanPokemonComponent } from '../plan-pokemon/plan-pokemon.component'; |
|
||||
import { GamePlan, PlanPokemon } from '../../../core/models/plan.model'; |
|
||||
|
|
||||
interface StatusUpdateEvent { |
|
||||
pfic?: string; |
|
||||
caught?: boolean; |
|
||||
gameId?: number; |
|
||||
total?: number; |
|
||||
} |
|
||||
|
|
||||
@Component({ |
@Component({ |
||||
selector: 'app-plan-game', |
selector: 'app-plan-game', |
||||
standalone: true, |
standalone: true, |
||||
imports: [ |
imports: [ |
||||
CommonModule, |
CommonModule, |
||||
MatExpansionModule, |
MatCardModule |
||||
MatFormFieldModule, |
|
||||
MatInputModule, |
|
||||
FormsModule, |
|
||||
PlanPokemonComponent |
|
||||
], |
], |
||||
template: ` |
template: ` |
||||
<mat-expansion-panel> |
<mat-card |
||||
<mat-expansion-panel-header> |
class="game-card" |
||||
<mat-panel-title> |
[class.selected]="isSelected" |
||||
{{ game.game_name }} |
(click)="onSelect()"> |
||||
</mat-panel-title> |
<img |
||||
<mat-panel-description> |
[src]="getGameBoxArt()" |
||||
Catch: {{ getTotalCatchCount() }} |
[alt]="game.game_name" |
||||
</mat-panel-description> |
class="game-image" |
||||
</mat-expansion-panel-header> |
> |
||||
|
<mat-card-content> |
||||
<div class="search-container"> |
<h3>{{ game.game_name }}</h3> |
||||
<mat-form-field appearance="outline" class="search-field"> |
<p class="catch-count"> |
||||
<mat-label>Search Pokémon</mat-label> |
Pokémon to catch: {{ getTotalCatchCount() }} |
||||
<input matInput [(ngModel)]="searchTerm" (input)="filterPokemon()"> |
</p> |
||||
</mat-form-field> |
</mat-card-content> |
||||
</div> |
</mat-card> |
||||
|
|
||||
<div class="game-sections"> |
|
||||
<div class="active-section"> |
|
||||
<app-plan-pokemon |
|
||||
*ngFor="let pokemon of filteredPokemon" |
|
||||
[pokemon]="pokemon" |
|
||||
(statusUpdate)="onPokemonStatusUpdate($event)" |
|
||||
></app-plan-pokemon> |
|
||||
</div> |
|
||||
|
|
||||
<div class="completed-section" *ngIf="completedPokemon.length > 0"> |
|
||||
<h3 class="section-title">Completed</h3> |
|
||||
<app-plan-pokemon |
|
||||
*ngFor="let pokemon of completedPokemon" |
|
||||
[pokemon]="pokemon" |
|
||||
(statusUpdate)="onPokemonStatusUpdate($event)" |
|
||||
></app-plan-pokemon> |
|
||||
</div> |
|
||||
</div> |
|
||||
</mat-expansion-panel> |
|
||||
`,
|
`,
|
||||
styles: [` |
styles: [` |
||||
.search-container { |
.game-card { |
||||
margin-bottom: 16px; |
width: 200px; |
||||
|
cursor: pointer; |
||||
|
transition: transform 0.2s, box-shadow 0.2s; |
||||
} |
} |
||||
|
|
||||
.search-field { |
.game-card:hover { |
||||
width: 100%; |
transform: translateY(-4px); |
||||
|
box-shadow: 0 4px 8px rgba(0,0,0,0.2); |
||||
} |
} |
||||
|
|
||||
.game-sections { |
.game-card.selected { |
||||
display: flex; |
border: 2px solid #4CAF50; |
||||
flex-direction: column; |
transform: translateY(-4px); |
||||
gap: 24px; |
|
||||
} |
} |
||||
|
|
||||
.completed-section { |
.game-image { |
||||
border-top: 2px solid #eee; |
width: 100%; |
||||
padding-top: 16px; |
height: 160px; |
||||
|
object-fit: cover; |
||||
|
} |
||||
|
|
||||
|
mat-card-content { |
||||
|
padding: 16px; |
||||
} |
} |
||||
|
|
||||
.section-title { |
h3 { |
||||
color: #4CAF50; |
margin: 0; |
||||
margin: 0 0 16px 0; |
|
||||
font-size: 1.2em; |
font-size: 1.2em; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.catch-count { |
||||
|
margin: 8px 0 0; |
||||
|
color: #666; |
||||
} |
} |
||||
`]
|
`]
|
||||
}) |
}) |
||||
export class PlanGameComponent { |
export class PlanGameComponent { |
||||
@Input() game!: GamePlan; |
@Input() game!: GamePlan; |
||||
@Output() statusUpdate = new EventEmitter<StatusUpdateEvent>(); |
@Input() isSelected = false; |
||||
|
@Output() gameSelect = new EventEmitter<GamePlan>(); |
||||
searchTerm = ''; |
|
||||
filteredPokemon: typeof this.game.pokemon = []; |
|
||||
completedPokemon: typeof this.game.pokemon = []; |
|
||||
|
|
||||
ngOnInit() { |
|
||||
this.filterPokemon(); |
|
||||
} |
|
||||
|
|
||||
filterPokemon() { |
|
||||
const term = this.searchTerm.toLowerCase(); |
|
||||
const filtered = this.game.pokemon.filter(pokemon => { |
|
||||
const nameMatch = pokemon.name.toLowerCase().includes(term); |
|
||||
const formMatch = pokemon.form_name?.toLowerCase().includes(term); |
|
||||
return nameMatch || formMatch; |
|
||||
}); |
|
||||
|
|
||||
this.completedPokemon = filtered.filter(p => p.catch_count === 0); |
|
||||
this.filteredPokemon = filtered.filter(p => p.catch_count > 0); |
|
||||
} |
|
||||
|
|
||||
getTotalCatchCount(): number { |
getTotalCatchCount(): number { |
||||
return this.game.pokemon.reduce((sum, pokemon) => sum + pokemon.catch_count, 0); |
return this.game.pokemon.reduce((sum, pokemon) => sum + pokemon.catch_count, 0); |
||||
} |
} |
||||
|
|
||||
onPokemonStatusUpdate(event: {pfic: string, caught: boolean}) { |
getGameBoxArt(): string { |
||||
this.statusUpdate.emit(event); |
// You'll need to implement this to return the correct box art URL
|
||||
this.filterPokemon(); // Refresh the lists
|
return `/assets/images/games/${this.game.game_name.toLowerCase().replace(' ', '-')}.png`; |
||||
} |
} |
||||
|
|
||||
updateGameTotal(change: number) { |
onSelect() { |
||||
const currentTotal = this.getTotalCatchCount(); |
this.gameSelect.emit(this.game); |
||||
// Emit the new total to update the header
|
|
||||
this.statusUpdate.emit({ |
|
||||
gameId: this.game.game_id, |
|
||||
total: currentTotal + change |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
moveToCompletedSection(pokemon: PlanPokemon) { |
|
||||
const index = this.filteredPokemon.findIndex(p => p.pfic === pokemon.pfic); |
|
||||
if (index > -1) { |
|
||||
const [movedPokemon] = this.filteredPokemon.splice(index, 1); |
|
||||
this.completedPokemon.push(movedPokemon); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
moveToActiveSection(pokemon: PlanPokemon) { |
|
||||
const index = this.completedPokemon.findIndex(p => p.pfic === pokemon.pfic); |
|
||||
if (index > -1) { |
|
||||
const [movedPokemon] = this.completedPokemon.splice(index, 1); |
|
||||
this.filteredPokemon.push(movedPokemon); |
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 655 KiB |
|
After Width: | Height: | Size: 578 KiB |
|
After Width: | Height: | Size: 885 KiB |
|
After Width: | Height: | Size: 918 KiB |
|
After Width: | Height: | Size: 868 KiB |
|
After Width: | Height: | Size: 511 KiB |
|
After Width: | Height: | Size: 925 KiB |
|
After Width: | Height: | Size: 973 KiB |
|
After Width: | Height: | Size: 1012 KiB |
|
After Width: | Height: | Size: 702 KiB |
|
After Width: | Height: | Size: 850 KiB |
|
After Width: | Height: | Size: 797 KiB |
|
After Width: | Height: | Size: 660 KiB |
|
After Width: | Height: | Size: 865 KiB |
|
After Width: | Height: | Size: 919 KiB |
|
After Width: | Height: | Size: 860 KiB |
|
After Width: | Height: | Size: 943 KiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 589 KiB |
|
After Width: | Height: | Size: 546 KiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 889 KiB |
|
After Width: | Height: | Size: 593 KiB |
|
After Width: | Height: | Size: 526 KiB |
|
After Width: | Height: | Size: 900 KiB |
|
After Width: | Height: | Size: 757 KiB |
|
After Width: | Height: | Size: 874 KiB |
|
After Width: | Height: | Size: 578 KiB |
|
After Width: | Height: | Size: 689 KiB |
|
After Width: | Height: | Size: 753 KiB |
|
After Width: | Height: | Size: 796 KiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 940 KiB |
|
After Width: | Height: | Size: 995 KiB |
|
After Width: | Height: | Size: 992 KiB |
|
After Width: | Height: | Size: 639 KiB |
|
After Width: | Height: | Size: 647 KiB |
|
After Width: | Height: | Size: 950 KiB |
|
After Width: | Height: | Size: 943 KiB |
|
After Width: | Height: | Size: 811 KiB |