nise/nise-frontend/src/app/search/search.component.ts

269 lines
6.8 KiB
TypeScript
Raw Normal View History

import {Component, OnInit} from '@angular/core';
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
import {DecimalPipe, JsonPipe, NgForOf, NgIf} from "@angular/common";
import {HttpClient} from "@angular/common/http";
import {environment} from "../../environments/environment";
import {countryCodeToFlag} from "../format";
import {OsuGradeComponent} from "../../corelib/components/osu-grade/osu-grade.component";
import {Field, Query, QueryBuilderComponent} from "../../corelib/components/query-builder/query-builder.component";
2024-02-24 13:59:17 +00:00
import {RouterLink} from "@angular/router";
import {CalculatePageRangePipe} from "../../corelib/calculate-page-range.pipe";
import {DownloadFilesService} from "../../corelib/service/download-files.service";
interface SchemaField {
name: string;
2024-02-24 17:38:37 +00:00
shortName: string;
category: 'user' | 'score' | 'beatmap';
type: 'number' | 'string' | 'flag' | 'grade' | 'boolean';
active: boolean;
description: string;
}
interface SchemaResponse {
fields: SchemaField[];
}
interface SearchResponse {
scores: SearchResponseEntry[];
2024-02-24 13:59:17 +00:00
pagination: SearchPagination;
}
interface SearchPagination {
currentPage: number;
pageSize: number;
totalResults: number;
totalPages: number;
}
interface Sorting {
field: string;
order: 'ASC' | 'DESC';
}
interface SearchResponseEntry {
// User fields
user_id?: number;
user_username?: string;
user_join_date?: string;
user_country?: string;
user_country_rank?: number;
user_rank?: number;
user_pp_raw?: number;
user_accuracy?: number;
user_playcount?: number;
user_total_score?: number;
user_ranked_score?: number;
user_seconds_played?: number;
user_count_300?: number;
user_count_100?: number;
user_count_50?: number;
user_count_miss?: number;
// Score fields
replay_id?: number;
date?: string;
beatmap_id?: number;
pp?: number;
frametime?: number;
ur?: number;
// Beatmap fields
beatmap_artist?: string;
beatmap_beatmapset_id?: number;
beatmap_creator?: string;
beatmap_source?: string;
beatmap_star_rating?: number;
beatmap_title?: string;
beatmap_version?: string;
}
@Component({
selector: 'app-search',
standalone: true,
imports: [
ReactiveFormsModule,
NgForOf,
FormsModule,
JsonPipe,
NgIf,
DecimalPipe,
OsuGradeComponent,
2024-02-24 13:59:17 +00:00
QueryBuilderComponent,
RouterLink,
CalculatePageRangePipe
],
templateUrl: './search.component.html',
styleUrl: './search.component.css'
})
export class SearchComponent implements OnInit {
constructor(private httpClient: HttpClient,
public downloadFilesService: DownloadFilesService) { }
isError = false;
2024-02-24 13:59:17 +00:00
isLoading = false;
isLoadingSchema = true;
response: SearchResponse | null = null;
fields: SchemaField[] = [];
sortingOrder: Sorting | null = null;
queries: Query[] | null = null;
ngOnInit(): void {
this.isLoadingSchema = true;
2024-02-24 17:59:55 +00:00
this.httpClient.get<SchemaResponse>(`${environment.apiUrl}/search`,).subscribe({
next: (response) => {
this.fields = response.fields;
this.loadPreviousFromLocalStorage();
this.isLoadingSchema = false;
},
error: () => {
alert('Error fetching schema');
}
});
}
private loadPreviousFromLocalStorage() {
2024-02-24 18:29:10 +00:00
const storedQueries = localStorage.getItem('search_settings');
if (storedQueries) {
2024-02-24 18:29:10 +00:00
let queries1 = JSON.parse(storedQueries);
this.queries = queries1.queries;
this.sortingOrder = queries1.sortingOrder;
this.fields.forEach(field => {
2024-02-24 18:29:10 +00:00
if (queries1.columns.hasOwnProperty(field.name)) {
field.active = queries1.columns[field.name];
}
});
2024-02-24 18:29:10 +00:00
} else {
this.queries = [];
this.sortingOrder = {
field: 'user_id',
order: 'ASC'
};
}
}
deselectEntireFieldCategory(categoryName: string): void {
this.fields.forEach(field => {
if (field.category === categoryName) {
field.active = false;
}
});
2024-02-24 18:29:10 +00:00
this.saveSettingsToLocalStorage();
}
selectEntireFieldCategory(categoryName: string): void {
this.fields.forEach(field => {
if (field.category === categoryName) {
field.active = true;
}
});
2024-02-24 18:29:10 +00:00
this.saveSettingsToLocalStorage();
}
mapSchemaFieldsToFields(): Field[] {
return this.fields.map(field => {
return {
name: field.name,
type: field.type,
};
});
}
2024-02-24 18:29:10 +00:00
private serializeSettings() {
return {
queries: this.queries,
sortingOrder: this.sortingOrder,
columns: this.getColumnSettings()
};
}
private getColumnSettings() {
return this.fields.reduce<{ [key: string]: boolean }>((acc, field) => {
acc[field.name] = field.active;
return acc;
}, {});
}
saveSettingsToLocalStorage(): void {
const settings = this.serializeSettings();
localStorage.setItem('search_settings', JSON.stringify(settings));
2024-02-24 13:59:17 +00:00
}
exportSettings(): void {
2024-02-24 18:29:10 +00:00
const settings = this.serializeSettings();
this.downloadFilesService.downloadJSON(settings as any, 'nise-settings');
}
importSettings(json: any): void {
if (this.verifySchema(json)) {
this.queries = json.queries;
this.sortingOrder = json.sortingOrder;
this.fields.forEach(field => {
if (json.columns.hasOwnProperty(field.name)) {
field.active = json.columns[field.name];
}
});
}
2024-02-24 17:38:37 +00:00
}
getColumns(): string[] {
return this.fields.map(field => field.name);
2024-02-24 13:59:17 +00:00
}
2024-02-24 18:29:10 +00:00
uploadSettingsFile(event: any): void {
2024-02-24 13:59:17 +00:00
const file = event.target.files[0];
if (file) {
const fileReader = new FileReader();
fileReader.onload = (e) => {
try {
const json = JSON.parse(fileReader.result as string);
2024-02-24 18:29:10 +00:00
this.importSettings(json);
2024-02-24 13:59:17 +00:00
} catch (error) {
console.error('Error parsing JSON', error);
}
};
fileReader.readAsText(file);
}
}
verifySchema(json: any): boolean {
// TODO: Implement schema verification logic here
2024-02-24 18:29:10 +00:00
return 'queries' in json && 'sortingOrder' in json && 'columns' in json;
}
2024-02-24 13:59:17 +00:00
search(pageNumber: number = 1): void {
this.isLoading = true;
this.isError = false;
this.response = null;
const body = {
queries: this.queries,
2024-02-24 13:59:17 +00:00
sorting: this.sortingOrder,
page: pageNumber
}
this.httpClient.post<SearchResponse>(`${environment.apiUrl}/search`, body)
.subscribe({
next: (response) => {
this.response = response;
this.isLoading = false;
},
2024-02-24 18:29:10 +00:00
error: () => {
this.isError = true;
this.isLoading = false;
}
});
2024-02-24 18:29:10 +00:00
this.saveSettingsToLocalStorage();
}
// Add this method to the SearchComponent class
getValue(entry: SearchResponseEntry, columnName: string): any {
return entry[columnName as keyof SearchResponseEntry];
}
protected readonly countryCodeToFlag = countryCodeToFlag;
2024-02-24 13:59:17 +00:00
protected readonly Math = Math;
2024-02-24 18:29:10 +00:00
}