import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatCardModule } from '@angular/material/card';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatInputModule } from '@angular/material/input';
import { MatFormFieldModule } from '@angular/material/form-field';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { PokemonService } from '../../../core/services/pokemon.service';
import { Pokemon } from '../../../core/models/pokemon.model';
import { PokemonCellComponent } from '../pokemon-cell/pokemon-cell.component';
import { PokemonDetailsComponent } from '../pokemon-details/pokemon-details.component';
import { map, startWith } from 'rxjs/operators';
import { Observable } from 'rxjs';
interface PokemonSearchResult {
pokemon: Pokemon;
boxNumber: number;
}
@Component({
selector: 'app-pokemon-carousel',
standalone: true,
imports: [
CommonModule,
MatCardModule,
MatIconModule,
MatButtonModule,
MatAutocompleteModule,
MatInputModule,
MatFormFieldModule,
ReactiveFormsModule,
PokemonCellComponent,
PokemonDetailsComponent
],
template: `
Search Pokémon
{{ result.pokemon.Name }}
({{ result.pokemon.Form }})
(Box {{ result.boxNumber + 1 }})
Box {{ (currentBoxIndex + 1).toString().padStart(3, '0') }}
Box {{ currentBoxIndex + 1 }} of {{ pokemonGroups.length }}
`,
styles: [`
.search-container {
display: flex;
justify-content: center;
}
.search-field {
width: 100%;
max-width: 400px;
}
.carousel-container {
display: flex;
align-items: center;
justify-content: center;
position: relative;
//height: calc(100vh - 200px);
padding-top: 2em;
padding-bottom: 2em;
}
.pokemon-box-container {
flex: 1;
max-width: 800px;
position: relative;
}
.pokemon-box {
background-color: white;
border: 2px solid #ccc;
border-radius: 10px;
padding: 20px;
position: relative;
width: 100%;
box-sizing: border-box;
}
.box-title {
background-color: #4CAF50;
color: white;
padding: 5px 15px;
border-radius: 15px 15px 0 0;
position: absolute;
top: -30px;
left: 20px;
font-weight: bold;
}
.pokemon-grid {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 10px;
}
.nav-button {
margin: 0 20px;
&.prev { left: 0; }
&.next { right: 0; }
&:disabled {
opacity: 0.5;
}
}
.box-navigation {
text-align: center;
padding: 10px;
font-size: 1.1em;
color: #666;
}
`],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class PokemonCarouselComponent implements OnInit {
pokemonGroups: (Pokemon | null)[][] = [];
currentBoxIndex = 0;
selectedPokemon: Pokemon | null = null;
caughtPokemon = new Set();
searchControl = new FormControl('');
filteredOptions: Observable;
get currentGroup(): (Pokemon | null)[] {
return this.pokemonGroups[this.currentBoxIndex] || [];
}
constructor(
private pokemonService: PokemonService,
private cdr: ChangeDetectorRef
) {
this.filteredOptions = this.searchControl.valueChanges.pipe(
startWith(''),
map(value => this.filterPokemon(value))
);
}
ngOnInit() {
this.loadPokemon();
}
private loadPokemon() {
this.pokemonService.getPokemonList().subscribe({
next: (groups) => {
this.pokemonGroups = groups;
this.cdr.markForCheck();
},
error: (error) => {
console.error('Error loading Pokemon:', error);
this.cdr.markForCheck();
}
});
}
nextBox() {
if (this.currentBoxIndex < this.pokemonGroups.length - 1) {
this.currentBoxIndex++;
this.cdr.markForCheck();
}
}
previousBox() {
if (this.currentBoxIndex > 0) {
this.currentBoxIndex--;
this.cdr.markForCheck();
}
}
onPokemonCaught(pfic: string) {
this.pokemonService.toggleCatch(pfic).subscribe(
response => {
if (response.status === 'caught') {
this.caughtPokemon.add(pfic);
} else {
this.caughtPokemon.delete(pfic);
}
this.cdr.markForCheck();
}
);
}
onPokemonSelected(pokemon: Pokemon) {
this.selectedPokemon = pokemon;
this.cdr.markForCheck();
}
private filterPokemon(value: string | PokemonSearchResult | null): PokemonSearchResult[] {
if (!value) return [];
const searchTerm = typeof value === 'string' ? value.toLowerCase() : '';
if (searchTerm.length < 2) return []; // Only search with 2 or more characters
const results: PokemonSearchResult[] = [];
this.pokemonGroups.forEach((group, boxIndex) => {
group.forEach(pokemon => {
if (pokemon && pokemon.Name.toLowerCase().includes(searchTerm)) {
results.push({
pokemon,
boxNumber: boxIndex
});
}
});
});
return results.slice(0, 10); // Limit to 10 results
}
displayFn(result: PokemonSearchResult): string {
return result?.pokemon?.Name || '';
}
onSearchSelect(event: any) {
const result: PokemonSearchResult = event.option.value;
this.currentBoxIndex = result.boxNumber;
this.searchControl.setValue('');
this.cdr.markForCheck();
}
}