Files
LivingDexTracker/src/lib/repositories/CombinedDataRepository.ts
T
2026-01-16 21:11:21 +00:00

223 lines
6.4 KiB
TypeScript

import { type PokedexEntry, type PokedexEntryDB } from '$lib/models/PokedexEntry';
import { type CatchRecord, type CatchRecordDB } from '$lib/models/CatchRecord';
import { type CombinedData } from '$lib/models/CombinedData';
import type { SupabaseClient } from '@supabase/supabase-js';
class CombinedDataRepository {
constructor(
private supabase: SupabaseClient,
private userId: string | null,
private pokedexId: string | null
) {}
// Transform Supabase data to match frontend expectations (minimal transformation)
private transformPokedexEntry(entry: PokedexEntryDB): PokedexEntry {
return {
_id: entry.id.toString(),
pokedexNumber: entry.pokedexNumber,
pokemon: entry.pokemon,
form: entry.form || '',
canGigantamax: entry.canGigantamax,
regionToCatchIn: entry.regionToCatchIn || '',
gamesToCatchIn: entry.gamesToCatchIn || [],
regionToEvolveIn: entry.regionToEvolveIn || '',
evolutionInformation: entry.evolutionInformation || '',
catchInformation: entry.catchInformation || []
// Note: Regional dex numbers are stored in separate regional_dex_numbers table
};
}
private transformCatchRecord(record: CatchRecordDB): CatchRecord {
return {
_id: record.id,
userId: record.userId,
// Backwards-compatible field name; DB column is pokemonId
pokedexEntryId: record.pokemonId.toString(),
pokedexId: record.pokedexId,
haveToEvolve: record.haveToEvolve,
caught: record.caught,
inHome: record.inHome,
hasGigantamaxed: record.hasGigantamaxed,
personalNotes: record.personalNotes
};
}
async findAllCombinedData(
userId: string,
enableForms: boolean = true,
region: string = '',
game: string = ''
): Promise<CombinedData[]> {
let query = this.supabase.from('pokedex_entries').select('*');
// Apply filters
if (!enableForms) {
// Filter to only base forms (form is NULL) OR Unown "A" (since Unown has no base form)
query = query.or('form.is.null,and(pokemon.eq.Unown,form.eq.A)');
}
if (region) {
query = query.eq('regionToCatchIn', region);
}
if (game) {
query = query.contains('gamesToCatchIn', [game]);
}
// Always order by national dex number
// Note: Regional dex ordering would require joining with regional_dex_numbers table
query = query.order('pokedexNumber', { ascending: true });
const { data: entries, error: entriesError } = await query;
if (entriesError) {
console.error('Error finding combined data:', entriesError);
return [];
}
if (!entries || entries.length === 0) {
return [];
}
// Get catch records for all entries
let catchRecords: CatchRecordDB[] = [];
if (userId && this.pokedexId) {
const entryIds = entries.map((entry) => entry.id);
const { data: records, error: recordsError } = await this.supabase
.from('catch_records')
.select('*')
.eq('userId', userId)
.eq('pokedexId', this.pokedexId)
.in('pokemonId', entryIds);
if (!recordsError && records) {
catchRecords = records;
}
}
// Combine the data exactly like master branch
const combinedData = entries.map((entry) => {
const userCatchRecord = catchRecords.find((record) => record.pokemonId === entry.id) || null;
const transformedEntry = this.transformPokedexEntry(entry);
const transformedCatchRecord = userCatchRecord
? this.transformCatchRecord(userCatchRecord)
: null;
return {
pokedexEntry: transformedEntry,
catchRecord: transformedCatchRecord
};
});
// Box placement is calculated dynamically on the frontend based on the current sort order.
return combinedData;
}
async findCombinedData(
userId: string,
page: number = 1,
limit: number = 20,
enableForms: boolean = true,
region: string = '',
game: string = ''
): Promise<CombinedData[]> {
const from = (page - 1) * limit;
const to = from + limit - 1;
let query = this.supabase.from('pokedex_entries').select('*').range(from, to);
// Apply filters
if (!enableForms) {
// Filter to only base forms (form is NULL) OR Unown "A" (since Unown has no base form)
query = query.or('form.is.null,and(pokemon.eq.Unown,form.eq.A)');
}
if (region) {
query = query.eq('regionToCatchIn', region);
}
if (game) {
query = query.contains('gamesToCatchIn', [game]);
}
// Always order by national dex number
// Note: Regional dex ordering would require joining with regional_dex_numbers table
query = query.order('pokedexNumber', { ascending: true });
const { data: entries, error: entriesError } = await query;
if (entriesError) {
console.error('Error finding paginated combined data:', entriesError);
return [];
}
if (!entries || entries.length === 0) {
return [];
}
// Get catch records for these entries
let catchRecords: CatchRecordDB[] = [];
if (userId && this.pokedexId) {
const entryIds = entries.map((entry) => entry.id);
const { data: records, error: recordsError } = await this.supabase
.from('catch_records')
.select('*')
.eq('userId', userId)
.eq('pokedexId', this.pokedexId)
.in('pokemonId', entryIds);
if (!recordsError && records) {
catchRecords = records;
}
}
// Combine the data exactly like master branch
const combinedData = entries.map((entry) => {
const userCatchRecord = catchRecords.find((record) => record.pokemonId === entry.id) || null;
const transformedEntry = this.transformPokedexEntry(entry);
const transformedCatchRecord = userCatchRecord
? this.transformCatchRecord(userCatchRecord)
: null;
return {
pokedexEntry: transformedEntry,
catchRecord: transformedCatchRecord
};
});
// Box placement is calculated dynamically on the frontend based on the current sort order.
return combinedData;
}
async countCombinedData(enableForms: boolean, region: string, game: string): Promise<number> {
let query = this.supabase.from('pokedex_entries').select('id', { count: 'exact', head: true });
// Apply same filters as in findCombinedData
if (!enableForms) {
// Filter to only base forms (form is NULL) OR Unown "A" (since Unown has no base form)
query = query.or('form.is.null,and(pokemon.eq.Unown,form.eq.A)');
}
if (region) {
query = query.eq('regionToCatchIn', region);
}
if (game) {
query = query.contains('gamesToCatchIn', [game]);
}
const { count, error } = await query;
if (error) {
console.error('Error counting combined data:', error);
return 0;
}
return count || 0;
}
}
export default CombinedDataRepository;