2024-02-24 11:16:21 +00:00
|
|
|
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";
|
2024-02-24 11:16:21 +00:00
|
|
|
|
|
|
|
|
interface SchemaField {
|
|
|
|
|
name: string;
|
2024-02-24 17:38:37 +00:00
|
|
|
shortName: string;
|
2024-02-24 11:16:21 +00:00
|
|
|
category: 'user' | 'score' | 'beatmap';
|
|
|
|
|
type: 'number' | 'string' | 'flag' | 'grade' | 'boolean';
|
|
|
|
|
active: boolean;
|
|
|
|
|
description: string;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-24 17:21:27 +00:00
|
|
|
interface SchemaResponse {
|
|
|
|
|
fields: SchemaField[];
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-24 11:16:21 +00:00
|
|
|
interface SearchResponse {
|
2024-02-25 01:00:45 +00:00
|
|
|
scores: any[];
|
2024-02-24 13:59:17 +00:00
|
|
|
pagination: SearchPagination;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface SearchPagination {
|
|
|
|
|
currentPage: number;
|
|
|
|
|
pageSize: number;
|
|
|
|
|
totalResults: number;
|
|
|
|
|
totalPages: number;
|
2024-02-24 11:16:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface Sorting {
|
|
|
|
|
field: string;
|
|
|
|
|
order: 'ASC' | 'DESC';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@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
|
2024-02-24 11:16:21 +00:00
|
|
|
],
|
|
|
|
|
templateUrl: './search.component.html',
|
|
|
|
|
styleUrl: './search.component.css'
|
|
|
|
|
})
|
|
|
|
|
export class SearchComponent implements OnInit {
|
|
|
|
|
|
2024-02-24 17:21:27 +00:00
|
|
|
constructor(private httpClient: HttpClient,
|
|
|
|
|
public downloadFilesService: DownloadFilesService) { }
|
2024-02-24 11:16:21 +00:00
|
|
|
|
2024-02-24 16:58:10 +00:00
|
|
|
isError = false;
|
2024-02-24 13:59:17 +00:00
|
|
|
isLoading = false;
|
2024-02-24 19:31:15 +00:00
|
|
|
isLoadingSchema = true;
|
2024-02-24 11:16:21 +00:00
|
|
|
response: SearchResponse | null = null;
|
|
|
|
|
|
2024-02-24 17:21:27 +00:00
|
|
|
fields: SchemaField[] = [];
|
2024-02-24 11:16:21 +00:00
|
|
|
|
|
|
|
|
sortingOrder: Sorting | null = null;
|
|
|
|
|
queries: Query[] | null = null;
|
|
|
|
|
|
|
|
|
|
ngOnInit(): void {
|
2024-02-24 19:31:15 +00:00
|
|
|
this.isLoadingSchema = true;
|
2024-02-25 01:00:45 +00:00
|
|
|
this.httpClient.get<SchemaResponse>(`${environment.apiUrl}/search/schema`,).subscribe({
|
2024-02-24 17:21:27 +00:00
|
|
|
next: (response) => {
|
|
|
|
|
this.fields = response.fields;
|
|
|
|
|
this.loadPreviousFromLocalStorage();
|
2024-02-24 19:31:15 +00:00
|
|
|
this.isLoadingSchema = false;
|
2024-02-24 17:21:27 +00:00
|
|
|
},
|
|
|
|
|
error: () => {
|
|
|
|
|
alert('Error fetching schema');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private loadPreviousFromLocalStorage() {
|
2024-02-24 18:29:10 +00:00
|
|
|
const storedQueries = localStorage.getItem('search_settings');
|
2024-02-24 11:16:21 +00:00
|
|
|
if (storedQueries) {
|
2024-02-24 18:29:10 +00:00
|
|
|
let queries1 = JSON.parse(storedQueries);
|
|
|
|
|
this.queries = queries1.queries;
|
|
|
|
|
this.sortingOrder = queries1.sortingOrder;
|
2024-02-24 11:16:21 +00:00
|
|
|
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 11:16:21 +00:00
|
|
|
}
|
|
|
|
|
});
|
2024-02-24 18:29:10 +00:00
|
|
|
} else {
|
|
|
|
|
this.queries = [];
|
|
|
|
|
this.sortingOrder = {
|
|
|
|
|
field: 'user_id',
|
|
|
|
|
order: 'ASC'
|
|
|
|
|
};
|
2024-02-24 11:16:21 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-24 16:58:10 +00:00
|
|
|
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();
|
2024-02-24 16:58:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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();
|
2024-02-24 16:58:10 +00:00
|
|
|
}
|
|
|
|
|
|
2024-02-24 11:16:21 +00:00
|
|
|
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 11:16:21 +00:00
|
|
|
}
|
|
|
|
|
|
2024-02-24 13:59:17 +00:00
|
|
|
search(pageNumber: number = 1): void {
|
|
|
|
|
this.isLoading = true;
|
2024-02-24 16:58:10 +00:00
|
|
|
this.isError = false;
|
|
|
|
|
this.response = null;
|
|
|
|
|
|
2024-02-24 11:16:21 +00:00
|
|
|
const body = {
|
|
|
|
|
queries: this.queries,
|
2024-02-24 13:59:17 +00:00
|
|
|
sorting: this.sortingOrder,
|
|
|
|
|
page: pageNumber
|
2024-02-24 11:16:21 +00:00
|
|
|
}
|
2024-02-24 16:58:10 +00:00
|
|
|
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: () => {
|
2024-02-24 16:58:10 +00:00
|
|
|
this.isError = true;
|
|
|
|
|
this.isLoading = false;
|
|
|
|
|
}
|
|
|
|
|
});
|
2024-02-24 18:29:10 +00:00
|
|
|
this.saveSettingsToLocalStorage();
|
2024-02-24 11:16:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add this method to the SearchComponent class
|
2024-02-25 01:00:45 +00:00
|
|
|
getValue(entry: any, columnName: string): any {
|
|
|
|
|
return entry[columnName as keyof any];
|
2024-02-24 11:16:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected readonly countryCodeToFlag = countryCodeToFlag;
|
2024-02-24 13:59:17 +00:00
|
|
|
protected readonly Math = Math;
|
2024-02-24 18:29:10 +00:00
|
|
|
|
2024-02-24 11:16:21 +00:00
|
|
|
}
|