@ -1,150 +1,87 @@ |
|||
import { Component, Input, Output, EventEmitter } from '@angular/core'; |
|||
import { CommonModule } from '@angular/common'; |
|||
import { MatExpansionModule } from '@angular/material/expansion'; |
|||
import { MatFormFieldModule } from '@angular/material/form-field'; |
|||
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; |
|||
} |
|||
import { MatCardModule } from '@angular/material/card'; |
|||
import { GamePlan } from '../../../core/models/plan.model'; |
|||
|
|||
@Component({ |
|||
selector: 'app-plan-game', |
|||
standalone: true, |
|||
imports: [ |
|||
CommonModule, |
|||
MatExpansionModule, |
|||
MatFormFieldModule, |
|||
MatInputModule, |
|||
FormsModule, |
|||
PlanPokemonComponent |
|||
MatCardModule |
|||
], |
|||
template: ` |
|||
<mat-expansion-panel> |
|||
<mat-expansion-panel-header> |
|||
<mat-panel-title> |
|||
{{ game.game_name }} |
|||
</mat-panel-title> |
|||
<mat-panel-description> |
|||
Catch: {{ getTotalCatchCount() }} |
|||
</mat-panel-description> |
|||
</mat-expansion-panel-header> |
|||
|
|||
<div class="search-container"> |
|||
<mat-form-field appearance="outline" class="search-field"> |
|||
<mat-label>Search Pokémon</mat-label> |
|||
<input matInput [(ngModel)]="searchTerm" (input)="filterPokemon()"> |
|||
</mat-form-field> |
|||
</div> |
|||
|
|||
<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> |
|||
<mat-card |
|||
class="game-card" |
|||
[class.selected]="isSelected" |
|||
(click)="onSelect()"> |
|||
<img |
|||
[src]="getGameBoxArt()" |
|||
[alt]="game.game_name" |
|||
class="game-image" |
|||
> |
|||
<mat-card-content> |
|||
<h3>{{ game.game_name }}</h3> |
|||
<p class="catch-count"> |
|||
Pokémon to catch: {{ getTotalCatchCount() }} |
|||
</p> |
|||
</mat-card-content> |
|||
</mat-card> |
|||
`,
|
|||
styles: [` |
|||
.search-container { |
|||
margin-bottom: 16px; |
|||
.game-card { |
|||
width: 200px; |
|||
cursor: pointer; |
|||
transition: transform 0.2s, box-shadow 0.2s; |
|||
} |
|||
|
|||
.search-field { |
|||
width: 100%; |
|||
.game-card:hover { |
|||
transform: translateY(-4px); |
|||
box-shadow: 0 4px 8px rgba(0,0,0,0.2); |
|||
} |
|||
|
|||
.game-sections { |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 24px; |
|||
.game-card.selected { |
|||
border: 2px solid #4CAF50; |
|||
transform: translateY(-4px); |
|||
} |
|||
|
|||
.completed-section { |
|||
border-top: 2px solid #eee; |
|||
padding-top: 16px; |
|||
.game-image { |
|||
width: 100%; |
|||
height: 160px; |
|||
object-fit: cover; |
|||
} |
|||
|
|||
.section-title { |
|||
color: #4CAF50; |
|||
margin: 0 0 16px 0; |
|||
mat-card-content { |
|||
padding: 16px; |
|||
} |
|||
|
|||
h3 { |
|||
margin: 0; |
|||
font-size: 1.2em; |
|||
color: #333; |
|||
} |
|||
|
|||
.catch-count { |
|||
margin: 8px 0 0; |
|||
color: #666; |
|||
} |
|||
`]
|
|||
}) |
|||
export class PlanGameComponent { |
|||
@Input() game!: GamePlan; |
|||
@Output() statusUpdate = new EventEmitter<StatusUpdateEvent>(); |
|||
|
|||
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); |
|||
} |
|||
@Input() isSelected = false; |
|||
@Output() gameSelect = new EventEmitter<GamePlan>(); |
|||
|
|||
getTotalCatchCount(): number { |
|||
return this.game.pokemon.reduce((sum, pokemon) => sum + pokemon.catch_count, 0); |
|||
} |
|||
|
|||
onPokemonStatusUpdate(event: {pfic: string, caught: boolean}) { |
|||
this.statusUpdate.emit(event); |
|||
this.filterPokemon(); // Refresh the lists
|
|||
getGameBoxArt(): string { |
|||
// You'll need to implement this to return the correct box art URL
|
|||
return `/assets/images/games/${this.game.game_name.toLowerCase().replace(' ', '-')}.png`; |
|||
} |
|||
|
|||
updateGameTotal(change: number) { |
|||
const currentTotal = this.getTotalCatchCount(); |
|||
// 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); |
|||
} |
|||
onSelect() { |
|||
this.gameSelect.emit(this.game); |
|||
} |
|||
} |
|||
|
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 |