mirror of
https://github.com/BradNut/boredgame
synced 2025-09-08 17:40:22 +00:00
Updating types, adding BGG types, change mapper for additional fields, and large update to the creation of data (mechanics, categories, expansions, etc.) on game load from id path.
This commit is contained in:
parent
4c75d1f863
commit
66c9ef5c93
10 changed files with 697 additions and 276 deletions
|
|
@ -93,18 +93,37 @@ export type CategoryType = {
|
|||
|
||||
export type PublisherType = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type DesignerType = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type ArtistType = {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export type ExpansionType = {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export type BGGLinkType =
|
||||
| 'boardgamecategory'
|
||||
| 'boardgamemechanic'
|
||||
| 'boardgameexpansion'
|
||||
| 'boardgameartist'
|
||||
| 'boardgamepublisher';
|
||||
|
||||
export type BGGLink = {
|
||||
id: number;
|
||||
type: BGGLinkType;
|
||||
value: string;
|
||||
};
|
||||
|
||||
export type GameType = {
|
||||
id: string;
|
||||
handle: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
url: string;
|
||||
edit_url: string;
|
||||
thumb_url: string;
|
||||
|
|
@ -122,16 +141,17 @@ export type GameType = {
|
|||
primary_designer: DesignerType;
|
||||
designers: DesignerType[];
|
||||
developers: String[];
|
||||
artists: String[];
|
||||
artists: ArtistType[];
|
||||
expansions: ExpansionType[];
|
||||
min_players: number;
|
||||
max_players: number;
|
||||
min_playtime: number;
|
||||
max_playtime: number;
|
||||
min_age: number;
|
||||
description: string;
|
||||
description_preview: string;
|
||||
players: string;
|
||||
playtime: string;
|
||||
playtime: number;
|
||||
external_id: number;
|
||||
};
|
||||
|
||||
export type SearchQuery = {
|
||||
|
|
@ -140,7 +160,7 @@ export type SearchQuery = {
|
|||
ids?: string[];
|
||||
list_id?: string;
|
||||
random?: boolean;
|
||||
name?: string;
|
||||
q?: string;
|
||||
exact?: boolean;
|
||||
designer?: string;
|
||||
publisher?: string;
|
||||
|
|
|
|||
415
src/lib/utils/dbUtils.ts
Normal file
415
src/lib/utils/dbUtils.ts
Normal file
|
|
@ -0,0 +1,415 @@
|
|||
import prisma from "$lib/prisma";
|
||||
import type { GameType } from "$lib/types";
|
||||
import type { Game } from "@prisma/client";
|
||||
import type { BggThingDto } from "boardgamegeekclient/dist/esm/dto";
|
||||
import type { BggLinkDto } from "boardgamegeekclient/dist/esm/dto/concrete/subdto";
|
||||
import kebabCase from "just-kebab-case";
|
||||
import { mapAPIGameToBoredGame } from "./gameMapper";
|
||||
|
||||
export async function createArtist(externalArtist: BggLinkDto) {
|
||||
try {
|
||||
let dbArtist = await prisma.artist.findFirst({
|
||||
where: {
|
||||
external_id: externalArtist.id
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
slug: true,
|
||||
external_id: true
|
||||
}
|
||||
});
|
||||
if (dbArtist) {
|
||||
return dbArtist;
|
||||
}
|
||||
console.log('Creating artist', JSON.stringify(externalArtist, null, 2));
|
||||
let artist = await prisma.artist.create({
|
||||
data: {
|
||||
name: externalArtist.value,
|
||||
external_id: externalArtist.id,
|
||||
slug: kebabCase(externalArtist.value)
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
slug: true,
|
||||
external_id: true
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Created artist', JSON.stringify(artist, null, 2));
|
||||
return artist;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
throw new Error('Something went wrong creating Artist');
|
||||
}
|
||||
}
|
||||
|
||||
export async function createDesigner(externalDesigner: BggLinkDto) {
|
||||
try {
|
||||
let dbDesigner = await prisma.designer.findFirst({
|
||||
where: {
|
||||
external_id: externalDesigner.id
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
slug: true,
|
||||
external_id: true
|
||||
}
|
||||
});
|
||||
if (dbDesigner) {
|
||||
return dbDesigner;
|
||||
}
|
||||
console.log('Creating designer', JSON.stringify(externalDesigner, null, 2));
|
||||
let designer = await prisma.designer.create({
|
||||
data: {
|
||||
name: externalDesigner.value,
|
||||
external_id: externalDesigner.id,
|
||||
slug: kebabCase(externalDesigner.value)
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
slug: true,
|
||||
external_id: true
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Created designer', JSON.stringify(designer, null, 2));
|
||||
return designer;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
throw new Error('Something went wrong creating Designer');
|
||||
}
|
||||
}
|
||||
|
||||
export async function createPublisher(externalPublisher: BggLinkDto) {
|
||||
try {
|
||||
let dbPublisher = await prisma.publisher.findFirst({
|
||||
where: {
|
||||
external_id: externalPublisher.id
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
slug: true,
|
||||
external_id: true
|
||||
}
|
||||
});
|
||||
if (dbPublisher) {
|
||||
return dbPublisher;
|
||||
}
|
||||
console.log('Creating publisher', JSON.stringify(externalPublisher, null, 2));
|
||||
let publisher = await prisma.publisher.create({
|
||||
data: {
|
||||
name: externalPublisher.value,
|
||||
external_id: externalPublisher.id,
|
||||
slug: kebabCase(externalPublisher.value)
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
slug: true,
|
||||
external_id: true
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Created publisher', JSON.stringify(publisher, null, 2));
|
||||
return publisher;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
throw new Error('Something went wrong creating Publisher');
|
||||
}
|
||||
}
|
||||
|
||||
export async function createCategory(externalCategory: BggLinkDto) {
|
||||
try {
|
||||
let dbCategory = await prisma.category.findFirst({
|
||||
where: {
|
||||
external_id: externalCategory.id
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
slug: true,
|
||||
external_id: true
|
||||
}
|
||||
});
|
||||
if (dbCategory) {
|
||||
return dbCategory;
|
||||
}
|
||||
console.log('Creating category', JSON.stringify(externalCategory, null, 2));
|
||||
let category = await prisma.category.create({
|
||||
data: {
|
||||
name: externalCategory.value,
|
||||
external_id: externalCategory.id,
|
||||
slug: kebabCase(externalCategory.value)
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
slug: true,
|
||||
external_id: true
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Created category', JSON.stringify(category, null, 2));
|
||||
|
||||
return category;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
throw new Error('Something went wrong creating Category');
|
||||
}
|
||||
}
|
||||
|
||||
export async function createMechanic(externalMechanic: BggLinkDto) {
|
||||
try {
|
||||
let dbMechanic = await prisma.mechanic.findFirst({
|
||||
where: {
|
||||
external_id: externalMechanic.id
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
slug: true,
|
||||
external_id: true
|
||||
}
|
||||
});
|
||||
if (dbMechanic) {
|
||||
return dbMechanic;
|
||||
}
|
||||
console.log('Creating mechanic', JSON.stringify(externalMechanic, null, 2));
|
||||
let mechanic = await prisma.mechanic.upsert({
|
||||
where: {
|
||||
external_id: externalMechanic.id
|
||||
},
|
||||
create: {
|
||||
name: externalMechanic.value,
|
||||
external_id: externalMechanic.id,
|
||||
slug: kebabCase(externalMechanic.value)
|
||||
},
|
||||
update: {
|
||||
name: externalMechanic.value,
|
||||
slug: kebabCase(externalMechanic.value)
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Created mechanic', JSON.stringify(mechanic, null, 2));
|
||||
|
||||
return mechanic;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
throw new Error('Something went wrong creating Mechanic');
|
||||
}
|
||||
}
|
||||
|
||||
export async function createExpansion(game: Game, externalExpansion: BggLinkDto, gameIsExpansion: boolean, eventFetch: Function) {
|
||||
try {
|
||||
let dbExpansionGame = await prisma.game.findUnique({
|
||||
where: {
|
||||
external_id: externalExpansion.id
|
||||
}
|
||||
});
|
||||
|
||||
if (!dbExpansionGame) {
|
||||
const externalGameResponse = await eventFetch(
|
||||
`/api/external/game/${externalExpansion.id}?simplified=true`
|
||||
);
|
||||
if (externalGameResponse.ok) {
|
||||
const externalGame = await externalGameResponse.json();
|
||||
console.log('externalGame', externalGame);
|
||||
let boredGame = mapAPIGameToBoredGame(externalGame);
|
||||
dbExpansionGame = await createOrUpdateGameMinimal(boredGame);
|
||||
} else {
|
||||
throw new Error(`${gameIsExpansion ? 'Base game' : 'Expansion game'} not found and failed to create.`);
|
||||
}
|
||||
}
|
||||
|
||||
let dbExpansion;
|
||||
let baseGameId;
|
||||
let gameId;
|
||||
if (gameIsExpansion) {
|
||||
console.log('External expansion is expansion. Looking for base game', JSON.stringify(game, null, 2));
|
||||
dbExpansion = await prisma.expansion.findFirst({
|
||||
where: {
|
||||
game_id: dbExpansionGame.id
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
base_game_id: true,
|
||||
game_id: true
|
||||
}
|
||||
});
|
||||
baseGameId = game.id;
|
||||
gameId = dbExpansionGame.id;
|
||||
} else {
|
||||
console.log('External Expansion is base game. Looking for expansion', JSON.stringify(game, null, 2));
|
||||
dbExpansion = await prisma.expansion.findFirst({
|
||||
where: {
|
||||
base_game_id: dbExpansionGame.id
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
base_game_id: true,
|
||||
game_id: true
|
||||
}
|
||||
});
|
||||
baseGameId = dbExpansionGame.id;
|
||||
gameId = game.id;
|
||||
}
|
||||
|
||||
if (dbExpansion) {
|
||||
console.log('Expansion already exists', JSON.stringify(dbExpansion, null, 2));
|
||||
return dbExpansion;
|
||||
}
|
||||
|
||||
console.log(`Creating expansion. baseGameId: ${baseGameId}, gameId: ${gameId}`);
|
||||
let expansion = await prisma.expansion.create({
|
||||
data: {
|
||||
base_game_id: baseGameId,
|
||||
game_id: gameId
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Created expansion', JSON.stringify(expansion, null, 2));
|
||||
|
||||
return expansion;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
throw new Error('Something went wrong creating Expansion');
|
||||
}
|
||||
}
|
||||
|
||||
export async function createOrUpdateGameMinimal(game: GameType) {
|
||||
console.log('Creating or updating minimal game data', JSON.stringify(game, null, 2));
|
||||
const externalUrl = `https://boardgamegeek.com/boardgame/${game.external_id}`;
|
||||
return await prisma.game.upsert({
|
||||
where: {
|
||||
external_id: game.external_id
|
||||
},
|
||||
create: {
|
||||
name: game.name,
|
||||
slug: kebabCase(game.name),
|
||||
description: game.description,
|
||||
external_id: game.external_id,
|
||||
url: externalUrl,
|
||||
thumb_url: game.thumb_url,
|
||||
image_url: game.image_url,
|
||||
min_age: game.min_age || 0,
|
||||
min_players: game.min_players || 0,
|
||||
max_players: game.max_players || 0,
|
||||
min_playtime: game.min_playtime || 0,
|
||||
max_playtime: game.max_playtime || 0,
|
||||
year_published: game.year_published || 0
|
||||
},
|
||||
update: {
|
||||
name: game.name,
|
||||
slug: kebabCase(game.name),
|
||||
description: game.description,
|
||||
external_id: game.external_id,
|
||||
url: externalUrl,
|
||||
thumb_url: game.thumb_url,
|
||||
image_url: game.image_url,
|
||||
min_age: game.min_age || 0,
|
||||
min_players: game.min_players || 0,
|
||||
max_players: game.max_players || 0,
|
||||
min_playtime: game.min_playtime || 0,
|
||||
max_playtime: game.max_playtime || 0,
|
||||
year_published: game.year_published || 0
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function createOrUpdateGame(game: GameType) {
|
||||
console.log('Creating or updating game', JSON.stringify(game, null, 2));
|
||||
const categoryIds = game.categories;
|
||||
const mechanicIds = game.mechanics;
|
||||
const publisherIds = game.publishers;
|
||||
const designerIds = game.designers;
|
||||
const artistIds = game.artists;
|
||||
const expansionIds = game.expansions;
|
||||
const externalUrl = `https://boardgamegeek.com/boardgame/${game.external_id}`;
|
||||
console.log('categoryIds', categoryIds);
|
||||
console.log('mechanicIds', mechanicIds);
|
||||
return await prisma.game.upsert({
|
||||
include: {
|
||||
mechanics: true,
|
||||
publishers: true,
|
||||
designers: true,
|
||||
artists: true,
|
||||
expansions: true
|
||||
},
|
||||
where: {
|
||||
external_id: game.external_id
|
||||
},
|
||||
create: {
|
||||
name: game.name,
|
||||
slug: kebabCase(game.name),
|
||||
description: game.description,
|
||||
external_id: game.external_id,
|
||||
url: externalUrl,
|
||||
thumb_url: game.thumb_url,
|
||||
image_url: game.image_url,
|
||||
min_age: game.min_age || 0,
|
||||
min_players: game.min_players || 0,
|
||||
max_players: game.max_players || 0,
|
||||
min_playtime: game.min_playtime || 0,
|
||||
max_playtime: game.max_playtime || 0,
|
||||
year_published: game.year_published || 0,
|
||||
last_sync_at: new Date(),
|
||||
categories: {
|
||||
connect: categoryIds
|
||||
},
|
||||
mechanics: {
|
||||
connect: mechanicIds
|
||||
},
|
||||
publishers: {
|
||||
connect: publisherIds
|
||||
},
|
||||
designers: {
|
||||
connect: designerIds
|
||||
},
|
||||
artists: {
|
||||
connect: artistIds
|
||||
},
|
||||
expansions: {
|
||||
connect: expansionIds
|
||||
}
|
||||
},
|
||||
update: {
|
||||
name: game.name,
|
||||
slug: kebabCase(game.name),
|
||||
description: game.description,
|
||||
external_id: game.external_id,
|
||||
url: externalUrl,
|
||||
thumb_url: game.thumb_url,
|
||||
image_url: game.image_url,
|
||||
min_age: game.min_age || 0,
|
||||
min_players: game.min_players || 0,
|
||||
max_players: game.max_players || 0,
|
||||
min_playtime: game.min_playtime || 0,
|
||||
max_playtime: game.max_playtime || 0,
|
||||
year_published: game.year_published || 0,
|
||||
last_sync_at: new Date(),
|
||||
categories: {
|
||||
connect: categoryIds
|
||||
},
|
||||
mechanics: {
|
||||
connect: mechanicIds
|
||||
},
|
||||
publishers: {
|
||||
connect: publisherIds
|
||||
},
|
||||
designers: {
|
||||
connect: designerIds
|
||||
},
|
||||
artists: {
|
||||
connect: artistIds
|
||||
},
|
||||
expansions: {
|
||||
connect: expansionIds
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import type { GameType, SavedGameType } from '$lib/types';
|
||||
import kebabCase from 'just-kebab-case';
|
||||
|
||||
export function convertToSavedGame(game: GameType | SavedGameType): SavedGameType {
|
||||
return {
|
||||
|
|
@ -41,56 +42,21 @@ export function mapSavedGameToGame(game: SavedGameType): GameType {
|
|||
};
|
||||
}
|
||||
|
||||
// TODO: Type API response
|
||||
export function mapAPIGameToBoredGame(game: any): GameType {
|
||||
const {
|
||||
id,
|
||||
handle,
|
||||
name,
|
||||
url,
|
||||
thumb_url,
|
||||
image_url,
|
||||
year_published,
|
||||
categories,
|
||||
mechanics,
|
||||
primary_designer,
|
||||
designers,
|
||||
primary_publisher,
|
||||
publishers,
|
||||
artists,
|
||||
min_players,
|
||||
max_players,
|
||||
min_playtime,
|
||||
max_playtime,
|
||||
min_age,
|
||||
description,
|
||||
description_preview,
|
||||
players,
|
||||
playtime
|
||||
} = game;
|
||||
export function mapAPIGameToBoredGame(game: GameType): GameType {
|
||||
// TODO: Fix types
|
||||
return {
|
||||
id,
|
||||
handle,
|
||||
name,
|
||||
url,
|
||||
thumb_url,
|
||||
image_url,
|
||||
year_published,
|
||||
categories,
|
||||
mechanics,
|
||||
primary_designer,
|
||||
designers,
|
||||
primary_publisher,
|
||||
publishers,
|
||||
artists,
|
||||
min_players,
|
||||
max_players,
|
||||
min_playtime,
|
||||
max_playtime,
|
||||
min_age,
|
||||
description,
|
||||
description_preview,
|
||||
players,
|
||||
playtime
|
||||
external_id: game.external_id,
|
||||
name: game.name,
|
||||
slug: kebabCase(game.name),
|
||||
thumb_url: game.thumbnail,
|
||||
image_url: game.image,
|
||||
year_published: game.year_published,
|
||||
min_players: game.min_players,
|
||||
max_players: game.max_players,
|
||||
min_playtime: game.min_playtime,
|
||||
max_playtime: game.max_playtime,
|
||||
min_age: game.min_age,
|
||||
description: game.description,
|
||||
playtime: game.playing_time
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,24 @@
|
|||
import { error } from '@sveltejs/kit';
|
||||
import prisma from '$lib/prisma.js';
|
||||
import type { GameType } from '$lib/types.js';
|
||||
import { createArtist, createCategory, createDesigner, createExpansion, createMechanic, createOrUpdateGame, createPublisher } from '$lib/utils/dbUtils.js';
|
||||
import { mapAPIGameToBoredGame } from '$lib/utils/gameMapper.js';
|
||||
import type { Game } from '@prisma/client';
|
||||
|
||||
export const load = async ({ params, setHeaders, locals }) => {
|
||||
export const load = async ({ params, setHeaders, locals, fetch }) => {
|
||||
try {
|
||||
const { user } = locals;
|
||||
const { id } = params;
|
||||
const game = await prisma.game.findUnique({
|
||||
where: {
|
||||
id
|
||||
},
|
||||
include: {
|
||||
artists: true,
|
||||
designers: true,
|
||||
publishers: true,
|
||||
mechanics: true,
|
||||
categories: true
|
||||
}
|
||||
});
|
||||
console.log('found game', game);
|
||||
|
|
@ -16,6 +27,11 @@ export const load = async ({ params, setHeaders, locals }) => {
|
|||
throw error(404, 'not found');
|
||||
}
|
||||
|
||||
const currentDate = new Date();
|
||||
if (game.last_sync_at === null || currentDate.getDate() - game.last_sync_at.getDate() > 7 * 24 * 60 * 60 * 1000) {
|
||||
await syncGameAndConnectedData(game, fetch);
|
||||
}
|
||||
|
||||
let wishlist;
|
||||
let collection;
|
||||
if (user) {
|
||||
|
|
@ -60,3 +76,63 @@ export const load = async ({ params, setHeaders, locals }) => {
|
|||
|
||||
throw error(404, 'not found');
|
||||
};
|
||||
|
||||
async function syncGameAndConnectedData(game: Game, eventFetch: Function) {
|
||||
console.log(
|
||||
`Retrieving full external game details for external id: ${game.external_id} with name ${game.name}`
|
||||
);
|
||||
const externalGameResponse = await eventFetch(`/api/external/game/${game.external_id}`);
|
||||
if (externalGameResponse.ok) {
|
||||
const externalGame = await externalGameResponse.json();
|
||||
console.log('externalGame', externalGame);
|
||||
let categories = [];
|
||||
let mechanics = [];
|
||||
let artists = [];
|
||||
let designers = [];
|
||||
let publishers = [];
|
||||
let expansions = [];
|
||||
for (const externalCategory of externalGame.categories) {
|
||||
const category = await createCategory(externalCategory);
|
||||
categories.push({
|
||||
id: category.id
|
||||
});
|
||||
}
|
||||
for (const externalMechanic of externalGame.mechanics) {
|
||||
const mechanic = await createMechanic(externalMechanic);
|
||||
mechanics.push({ id: mechanic.id });
|
||||
}
|
||||
for (const externalArtist of externalGame.artists) {
|
||||
const artist = await createArtist(externalArtist);
|
||||
artists.push({ id: artist.id });
|
||||
}
|
||||
for (const externalDesigner of externalGame.designers) {
|
||||
const designer = await createDesigner(externalDesigner);
|
||||
designers.push({ id: designer.id });
|
||||
}
|
||||
for (const externalPublisher of externalGame.publishers) {
|
||||
const publisher = await createPublisher(externalPublisher);
|
||||
publishers.push({ id: publisher.id });
|
||||
}
|
||||
|
||||
for (const externalExpansion of externalGame.expansions) {
|
||||
let expansion;
|
||||
console.log('Inbound?', externalExpansion.inbound);
|
||||
if (externalExpansion?.inbound === true) {
|
||||
expansion = await createExpansion(game, externalExpansion, false, eventFetch);
|
||||
} else {
|
||||
expansion = await createExpansion(game, externalExpansion, true, eventFetch);
|
||||
}
|
||||
expansions.push({ id: expansion.id });
|
||||
}
|
||||
|
||||
let boredGame = mapAPIGameToBoredGame(externalGame);
|
||||
|
||||
boredGame.categories = categories;
|
||||
boredGame.mechanics = mechanics;
|
||||
boredGame.designers = designers;
|
||||
boredGame.artists = artists;
|
||||
boredGame.publishers = publishers;
|
||||
boredGame.expansions = expansions;
|
||||
return createOrUpdateGame(boredGame);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,13 @@
|
|||
<script lang="ts">
|
||||
import { fade, fly } from 'svelte/transition';
|
||||
import { Image } from 'svelte-lazy-loader';
|
||||
import minusCircle from '@iconify-icons/line-md/minus-circle';
|
||||
import plusCircle from '@iconify-icons/line-md/plus-circle';
|
||||
import { Dices, ExternalLinkIcon, MinusIcon, PlusIcon } from 'lucide-svelte';
|
||||
import type { SavedGameType } from '$lib/types';
|
||||
import { collectionStore } from '$lib/stores/collectionStore';
|
||||
import { wishlistStore } from '$lib/stores/wishlistStore';
|
||||
import type { PageData } from './$types';
|
||||
import LinkWithIcon from '$lib/components/LinkWithIcon.svelte';
|
||||
import { Button } from '$components/ui/button';
|
||||
import AddToList from '$components/AddToList.svelte';
|
||||
import { Dices } from 'lucide-svelte';
|
||||
|
||||
$: existsInCollection = $collectionStore.find((item: SavedGameType) => item.id === game.id);
|
||||
$: existsInWishlist = $wishlistStore.find((item: SavedGameType) => item.id === game.id);
|
||||
|
|
@ -23,31 +20,28 @@
|
|||
$: ({ game, user, wishlist, collection, in_collection, in_wishlist } = data);
|
||||
|
||||
let seeMore: boolean = false;
|
||||
console.log('game', game);
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{game?.name} | Bored Game</title>
|
||||
</svelte:head>
|
||||
|
||||
<h2>{game?.name}</h2>
|
||||
<h2>{game?.name}
|
||||
{#if game?.year_published}
|
||||
({game?.year_published})
|
||||
{/if}
|
||||
</h2>
|
||||
|
||||
<section class="game">
|
||||
<div>
|
||||
<a class="thumbnail" href={game.url}>
|
||||
{#if game?.thumb_url && game?.name}
|
||||
<Image src={game.thumb_url} alt={`Image of ${game.name}`} />
|
||||
{#if game?.image_url && game?.name}
|
||||
<Image src={game.image_url} alt={`Image of ${game.name}`} />
|
||||
{:else}
|
||||
<Dices />
|
||||
{/if}
|
||||
<!-- <img src={game.image_url} alt={`Image of ${game.name}`} /> -->
|
||||
</a>
|
||||
</div>
|
||||
<div style="display: grid; place-items: center; gap: 3rem;">
|
||||
<div class="details">
|
||||
{#if game?.year_published}
|
||||
<p>Year: {game?.year_published}</p>
|
||||
{/if}
|
||||
{#if game?.min_players && game?.max_players}
|
||||
<p>Players: {game.min_players} - {game.max_players}</p>
|
||||
{/if}
|
||||
|
|
@ -57,10 +51,12 @@
|
|||
{#if game?.min_age}
|
||||
<p>Minimum Age: {game.min_age}</p>
|
||||
{/if}
|
||||
<LinkWithIcon external ariaLabel={`Board Game Atlas Link for ${game.name}`} url={game.url}>
|
||||
Board Game Atlas
|
||||
<!-- <ExternalLinkIcon width="24" height="24" /> -->
|
||||
</LinkWithIcon>
|
||||
{#if game?.min_playtime && game?.max_playtime}
|
||||
<p>Playtime: {game.min_playtime} - {game.max_playtime} minutes</p>
|
||||
{/if}
|
||||
<Button class="text-secondary-foreground p-0" variant="link" href={game.url} title="View on BoardGameGeek" target="_blank">
|
||||
View on BoardGameGeek <ExternalLinkIcon class="mr-2 h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
{#if user?.username}
|
||||
<AddToList {in_collection} {in_wishlist} game_id={game.id} {wishlist} {collection} />
|
||||
|
|
@ -71,34 +67,19 @@
|
|||
{/if}
|
||||
</div>
|
||||
</section>
|
||||
{#if game?.description_preview}
|
||||
<section class="description" class:show={seeMore} class:hide={!seeMore} style="margin-top: 2rem;">
|
||||
{@html game?.description}
|
||||
</section>
|
||||
<button class="btn button-icon" type="button" on:click={() => (seeMore = !seeMore)}
|
||||
>See
|
||||
{#if !seeMore}
|
||||
<section class="description" style="margin-top: 2rem;" in:fly|global={{ opacity: 0, x: 100 }} out:fly|global={{ opacity: 0, x: -100 }}>
|
||||
{`${game?.description_preview.substring(0, 250)}...`}
|
||||
</section>
|
||||
More
|
||||
<PlusIcon width="24" height="24" />
|
||||
{:else}
|
||||
Less
|
||||
<MinusIcon width="24" height="24" />
|
||||
{/if}
|
||||
{#if seeMore}
|
||||
<div class="overflow-description" in:fly|global={{ opacity: 0, x: 100 }} out:fade|global>
|
||||
{@html game?.description}
|
||||
</div>
|
||||
{/if}
|
||||
<button class="btn button-icon" type="button" on:click={() => (seeMore = !seeMore)}
|
||||
>See
|
||||
{#if !seeMore}
|
||||
More
|
||||
<!-- <PlusIcon width="24" height="24" /> -->
|
||||
{:else}
|
||||
Less
|
||||
<!-- <MinusIcon width="24" height="24" /> -->
|
||||
{/if}
|
||||
</button>
|
||||
{:else}
|
||||
<section class="description">
|
||||
<span>
|
||||
{@html game?.description}
|
||||
</span>
|
||||
</section>
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
<style lang="scss">
|
||||
h2 {
|
||||
|
|
@ -147,13 +128,11 @@
|
|||
}
|
||||
|
||||
.details {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 0.5rem;
|
||||
place-content: center;
|
||||
p {
|
||||
margin: 1rem;
|
||||
}
|
||||
display: flex;
|
||||
/* grid-template-columns: 1fr 1fr; */
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
/* align-items: center; */
|
||||
|
||||
@media (max-width: 500px) {
|
||||
grid-template-columns: 1fr;
|
||||
|
|
@ -166,6 +145,15 @@
|
|||
gap: 1.5rem;
|
||||
margin: 1rem;
|
||||
line-height: 1.75em;
|
||||
|
||||
&.show {
|
||||
max-height: auto;
|
||||
}
|
||||
|
||||
&.hide {
|
||||
max-height: 300px;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.overflow-description {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,17 @@ import type { GameType, SearchQuery } from '$lib/types';
|
|||
import { mapAPIGameToBoredGame } from '$lib/utils/gameMapper.js';
|
||||
import { search_schema } from '$lib/zodValidation';
|
||||
import type { PageServerLoad } from '../$types.js';
|
||||
import { BggClient } from 'boardgamegeekclient';
|
||||
import type { BggThingDto } from 'boardgamegeekclient/dist/esm/dto/index.js';
|
||||
import type { BggLinkDto } from 'boardgamegeekclient/dist/esm/dto/concrete/subdto/BggLinkDto.js';
|
||||
import {
|
||||
createArtist,
|
||||
createCategory,
|
||||
createDesigner,
|
||||
createMechanic,
|
||||
createOrUpdateGame,
|
||||
createOrUpdateGameMinimal,
|
||||
createPublisher
|
||||
} from '$lib/utils/dbUtils.js';
|
||||
// import { listGameSchema } from '$lib/config/zod-schemas.js';
|
||||
|
||||
/**
|
||||
|
|
@ -23,46 +32,19 @@ import type { BggThingDto } from 'boardgamegeekclient/dist/esm/dto/index.js';
|
|||
* an array of all the games fetched. If any error occurred during the operation, it returns an object with totalCount as 0 and games as empty array.
|
||||
* @throws will throw an error if the response received from fetching games operation is not OK (200).
|
||||
*/
|
||||
async function searchForGames(urlQueryParams: SearchQuery, eventFetch) {
|
||||
async function searchForGames(urlQueryParams: SearchQuery, eventFetch: Function) {
|
||||
try {
|
||||
console.log('urlQueryParams search games', urlQueryParams);
|
||||
// let games = await prisma.game.findMany({
|
||||
// where: {
|
||||
// name: {
|
||||
// search: urlQueryParams?.name
|
||||
// },
|
||||
// min_players: {
|
||||
// gte: urlQueryParams?.min_players || 0
|
||||
// },
|
||||
// max_players: {
|
||||
// lte: urlQueryParams?.max_players || 100
|
||||
// },
|
||||
// min_playtime: {
|
||||
// gte: urlQueryParams?.min_playtime || 0
|
||||
// },
|
||||
// max_playtime: {
|
||||
// lte: urlQueryParams?.max_playtime || 5000
|
||||
// },
|
||||
// min_age: {
|
||||
// gte: urlQueryParams?.min_age || 0
|
||||
// }
|
||||
// },
|
||||
// skip: urlQueryParams?.skip,
|
||||
// take: urlQueryParams?.limit,
|
||||
// orderBy: {
|
||||
// name: 'asc'
|
||||
// }
|
||||
// });
|
||||
|
||||
const headers: HeadersInit = new Headers();
|
||||
headers.set('Content-Type', 'application/json');
|
||||
const requestInit: RequestInit = {
|
||||
method: 'GET',
|
||||
headers
|
||||
};
|
||||
const response = await eventFetch(
|
||||
`/api/game/search${urlQueryParams ? `?${urlQueryParams}` : ''}`,
|
||||
requestInit
|
||||
);
|
||||
const url = `/api/game/search${urlQueryParams ? `?${urlQueryParams}` : ''}`;
|
||||
console.log('Calling internal api', url);
|
||||
const response = await eventFetch(url, requestInit);
|
||||
console.log('response from internal api', response);
|
||||
|
||||
if (!response.ok) {
|
||||
|
|
@ -70,18 +52,21 @@ async function searchForGames(urlQueryParams: SearchQuery, eventFetch) {
|
|||
throw error(response.status);
|
||||
}
|
||||
|
||||
// const games: GameType[] = [];
|
||||
// let totalCount = 0;
|
||||
let games = [];
|
||||
if (response.ok) {
|
||||
games = await response.json();
|
||||
}
|
||||
|
||||
console.log('games from DB', games);
|
||||
|
||||
const gameNameSearch = urlQueryParams.get('q');
|
||||
let totalCount = games?.length || 0;
|
||||
|
||||
if (totalCount === 0) {
|
||||
console.log('No games found in DB for', urlQueryParams.get('name'));
|
||||
if (
|
||||
totalCount === 0 ||
|
||||
!games.find((game: GameType) => game.slug === kebabCase(gameNameSearch))
|
||||
) {
|
||||
console.log('No games found in DB for', gameNameSearch);
|
||||
|
||||
const externalResponse = await eventFetch(
|
||||
`/api/external/search${urlQueryParams ? `?${urlQueryParams}` : ''}`,
|
||||
|
|
@ -95,23 +80,26 @@ async function searchForGames(urlQueryParams: SearchQuery, eventFetch) {
|
|||
throw error(response.status);
|
||||
}
|
||||
|
||||
// // const games: GameType[] = [];
|
||||
// // let totalCount = 0;
|
||||
if (externalResponse.ok) {
|
||||
const gameResponse = await externalResponse.json();
|
||||
console.log('response from external api', gameResponse);
|
||||
const gameList: BggThingDto[] = gameResponse?.games;
|
||||
totalCount = gameResponse?.totalCount;
|
||||
console.log('totalCount', totalCount);
|
||||
gameList.forEach((game) => {
|
||||
if (game?.min_players && game?.max_players) {
|
||||
game.players = `${game.min_players} - ${game.max_players}`;
|
||||
game.playtime = `${game.min_playtime} - ${game.max_playtime}`;
|
||||
for (const game of gameList) {
|
||||
console.log(
|
||||
`Retrieving simplified external game details for id: ${game.id} with name ${game.name}`
|
||||
);
|
||||
const externalGameResponse = await eventFetch(
|
||||
`/api/external/game/${game.id}?simplified=true`
|
||||
);
|
||||
if (externalGameResponse.ok) {
|
||||
const externalGame = await externalGameResponse.json();
|
||||
console.log('externalGame', externalGame);
|
||||
let boredGame = mapAPIGameToBoredGame(externalGame);
|
||||
games.push(createOrUpdateGameMinimal(boredGame));
|
||||
}
|
||||
const boredGame = mapAPIGameToBoredGame(game);
|
||||
createOrUpdateGame(boredGame);
|
||||
games.push(boredGame);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -121,6 +109,7 @@ async function searchForGames(urlQueryParams: SearchQuery, eventFetch) {
|
|||
};
|
||||
} catch (e) {
|
||||
console.log(`Error searching board games ${e}`);
|
||||
// throw error(500, { message: 'Something went wrong' });
|
||||
}
|
||||
return {
|
||||
totalCount: 0,
|
||||
|
|
@ -128,98 +117,7 @@ async function searchForGames(urlQueryParams: SearchQuery, eventFetch) {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronous function createOrUpdateGame is used to create or update a game using the given game information.
|
||||
*
|
||||
* @async
|
||||
* @function createOrUpdateGame
|
||||
* @param {GameType} game - An object that holds the details about a game. It should contain required information like name, description,
|
||||
* id (both internal and external), thumbnail URL, minimum age, minimum and maximum number of players, minimum and maximum play time,
|
||||
* year of publication and primary publisher information including the publisher's name and ID, categories and mechanics related to the game.
|
||||
*
|
||||
* @returns {Promise<Object>} The return is a Promise that resolves with the data of the game that was created or updated.
|
||||
*/
|
||||
async function createOrUpdateGame(game: GameType) {
|
||||
const categoryIds = game.categories.map((category) => ({
|
||||
external_id: category.id
|
||||
}));
|
||||
const mechanicIds = game.mechanics.map((mechanic) => ({
|
||||
external_id: mechanic.id
|
||||
}));
|
||||
console.log('categoryIds', categoryIds);
|
||||
console.log('mechanicIds', mechanicIds);
|
||||
return await prisma.game.upsert({
|
||||
where: {
|
||||
external_id: game.id
|
||||
},
|
||||
create: {
|
||||
name: game.name,
|
||||
slug: kebabCase(game.name),
|
||||
description: game.description,
|
||||
description_preview: game.description_preview,
|
||||
external_id: game.id,
|
||||
thumb_url: game.thumb_url,
|
||||
min_age: game.min_age,
|
||||
min_players: game.min_players,
|
||||
max_players: game.max_players,
|
||||
min_playtime: game.min_playtime,
|
||||
max_playtime: game.max_playtime,
|
||||
year_published: game.year_published,
|
||||
primary_publisher: {
|
||||
connectOrCreate: {
|
||||
where: {
|
||||
external_id: game.primary_publisher.id
|
||||
},
|
||||
create: {
|
||||
external_id: game.primary_publisher.id,
|
||||
name: game.primary_publisher.name,
|
||||
slug: kebabCase(game.primary_publisher.name)
|
||||
}
|
||||
}
|
||||
},
|
||||
categories: {
|
||||
connect: categoryIds
|
||||
},
|
||||
mechanics: {
|
||||
connect: mechanicIds
|
||||
}
|
||||
},
|
||||
update: {
|
||||
name: game.name,
|
||||
slug: kebabCase(game.name),
|
||||
description: game.description,
|
||||
description_preview: game.description_preview,
|
||||
external_id: game.id,
|
||||
thumb_url: game.thumb_url,
|
||||
min_age: game.min_age,
|
||||
min_players: game.min_players,
|
||||
max_players: game.max_players,
|
||||
min_playtime: game.min_playtime,
|
||||
max_playtime: game.max_playtime,
|
||||
year_published: game.year_published,
|
||||
primary_publisher: {
|
||||
connectOrCreate: {
|
||||
where: {
|
||||
external_id: game.primary_publisher.id
|
||||
},
|
||||
create: {
|
||||
external_id: game.primary_publisher.id,
|
||||
name: game.primary_publisher.name,
|
||||
slug: kebabCase(game.primary_publisher.name)
|
||||
}
|
||||
}
|
||||
},
|
||||
categories: {
|
||||
connect: categoryIds
|
||||
},
|
||||
mechanics: {
|
||||
connect: mechanicIds
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export const load: PageServerLoad = async ({ params, locals, request, fetch, url }) => {
|
||||
export const load: PageServerLoad = async ({ fetch, url }) => {
|
||||
const defaults = {
|
||||
limit: 10,
|
||||
skip: 0,
|
||||
|
|
@ -236,11 +134,9 @@ export const load: PageServerLoad = async ({ params, locals, request, fetch, url
|
|||
// const modifyListForm = await superValidate(listGameSchema);
|
||||
|
||||
const queryParams: SearchQuery = {
|
||||
order_by: 'rank',
|
||||
ascending: false,
|
||||
limit: form.data?.limit,
|
||||
skip: form.data?.skip,
|
||||
name: form.data?.q
|
||||
q: form.data?.q
|
||||
};
|
||||
|
||||
// fields: ('id,name,min_age,min_players,max_players,thumb_url,min_playtime,max_playtime,min_age,description');
|
||||
|
|
@ -275,7 +171,6 @@ export const load: PageServerLoad = async ({ params, locals, request, fetch, url
|
|||
|
||||
const urlQueryParams = new URLSearchParams(newQueryParams);
|
||||
const searchData = await searchForGames(urlQueryParams, fetch);
|
||||
// console.log('searchData', searchData);
|
||||
|
||||
return {
|
||||
form,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
<script lang="ts">
|
||||
import { superForm } from 'sveltekit-superforms/client';
|
||||
import { gameStore } from '$lib/stores/gameSearchStore';
|
||||
import TextSearch from '$lib/components/search/textSearch/index.svelte';
|
||||
|
||||
export let data;
|
||||
|
|
|
|||
73
src/routes/api/external/game/[id]/+server.ts
vendored
73
src/routes/api/external/game/[id]/+server.ts
vendored
|
|
@ -1,7 +1,11 @@
|
|||
import { HttpError_1, error, json } from '@sveltejs/kit';
|
||||
import { error, json } from '@sveltejs/kit';
|
||||
import { decode } from 'html-entities';
|
||||
import { BggClient } from 'boardgamegeekclient';
|
||||
|
||||
export async function GET({ url, locals, params }) {
|
||||
export async function GET({ params, url }) {
|
||||
const searchParams = Object.fromEntries(url.searchParams);
|
||||
console.log('searchParams external game get', searchParams);
|
||||
const simplified = 'true' === searchParams?.simplified || false;
|
||||
const game_id = Number(params.id).valueOf();
|
||||
|
||||
// TODO: Debounce excessive calls and possibly throttle
|
||||
|
|
@ -9,7 +13,6 @@ export async function GET({ url, locals, params }) {
|
|||
throw error(400, { message: 'Invalid game id' });
|
||||
}
|
||||
|
||||
console.log('Searching for', game_id);
|
||||
const client = BggClient.Create();
|
||||
const response = await client.thing.query({
|
||||
id: game_id
|
||||
|
|
@ -21,11 +24,61 @@ export async function GET({ url, locals, params }) {
|
|||
|
||||
const result = response[0];
|
||||
|
||||
if (simplified) {
|
||||
return json({
|
||||
external_id: result.id,
|
||||
name: decode(result.name)?.trim(),
|
||||
type: result.type,
|
||||
description: decode(result.description)?.trim(),
|
||||
thumbnail: result.thumbnail,
|
||||
image: result.image,
|
||||
year_published: result.yearpublished,
|
||||
min_players: result.minplayers,
|
||||
max_players: result.maxplayers,
|
||||
playing_time: result.playingtime,
|
||||
min_playtime: result.minplaytime,
|
||||
max_playtime: result.maxplaytime,
|
||||
min_age: result.minage
|
||||
});
|
||||
}
|
||||
|
||||
const links = result.links;
|
||||
let categories = [];
|
||||
let mechanics = [];
|
||||
let expansions = [];
|
||||
let publishers = [];
|
||||
let artists = [];
|
||||
let designers = [];
|
||||
for (const link of links) {
|
||||
const linkData = {
|
||||
...link,
|
||||
value: decode(link.value)?.trim()
|
||||
}
|
||||
if (link.type === 'boardgamecategory') {
|
||||
categories.push(linkData);
|
||||
}
|
||||
if (link.type === 'boardgamemechanic') {
|
||||
mechanics.push(linkData);
|
||||
}
|
||||
if (link.type === 'boardgameexpansion') {
|
||||
expansions.push(linkData);
|
||||
}
|
||||
if (link.type === 'boardgameartist') {
|
||||
artists.push(linkData);
|
||||
}
|
||||
if (link.type === 'boardgamepublisher') {
|
||||
publishers.push(linkData);
|
||||
}
|
||||
if (link.type === 'boardgamedesigner') {
|
||||
designers.push(linkData);
|
||||
}
|
||||
}
|
||||
|
||||
const apiResponse = {
|
||||
external_id: result.id,
|
||||
name: result.name,
|
||||
name: decode(result.name)?.trim(),
|
||||
type: result.type,
|
||||
description: result.description,
|
||||
description: decode(result.description)?.trim(),
|
||||
thumbnail: result.thumbnail,
|
||||
image: result.image,
|
||||
year_published: result.yearpublished,
|
||||
|
|
@ -34,10 +87,14 @@ export async function GET({ url, locals, params }) {
|
|||
playing_time: result.playingtime,
|
||||
min_playtime: result.minplaytime,
|
||||
max_playtime: result.maxplaytime,
|
||||
min_age: result.minage
|
||||
min_age: result.minage,
|
||||
categories,
|
||||
mechanics,
|
||||
expansions,
|
||||
publishers,
|
||||
artists,
|
||||
designers
|
||||
};
|
||||
|
||||
console.log('Response from BGG', JSON.stringify(result, null, 2));
|
||||
|
||||
return json(apiResponse);
|
||||
}
|
||||
|
|
|
|||
9
src/routes/api/external/search/+server.ts
vendored
9
src/routes/api/external/search/+server.ts
vendored
|
|
@ -4,10 +4,10 @@ import { BggClient } from 'boardgamegeekclient';
|
|||
import type { ISearchRequest } from 'boardgamegeekclient/dist/esm/request/index.js';
|
||||
import { search_schema } from '$lib/zodValidation.js';
|
||||
|
||||
export async function GET({ url, locals, params }) {
|
||||
export async function GET({ url }) {
|
||||
const searchParams = Object.fromEntries(url.searchParams);
|
||||
console.log('searchParams external', searchParams);
|
||||
const name = searchParams?.name || '';
|
||||
const q = searchParams?.q || '';
|
||||
const exact = parseInt(searchParams.exact) || 0;
|
||||
const limit = parseInt(searchParams?.limit) || 10;
|
||||
const skip = parseInt(searchParams?.skip) || 0;
|
||||
|
|
@ -15,7 +15,7 @@ export async function GET({ url, locals, params }) {
|
|||
// TODO: Debounce and throttle
|
||||
try {
|
||||
search_schema.parse({
|
||||
q: name,
|
||||
q,
|
||||
limit,
|
||||
skip
|
||||
});
|
||||
|
|
@ -30,13 +30,14 @@ export async function GET({ url, locals, params }) {
|
|||
|
||||
const client = BggClient.Create();
|
||||
const request: ISearchRequest = {
|
||||
query: name,
|
||||
query: q,
|
||||
exact,
|
||||
type: ['boardgame', 'boardgameaccessory', 'boardgameexpansion']
|
||||
};
|
||||
const response = await client.search.query(request);
|
||||
|
||||
if (!response || response.length === 0 || response[0]?.total === 0) {
|
||||
console.log('No results found in external search', response);
|
||||
throw error(404, { message: 'No results found in external search' });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,31 +3,34 @@ import { Prisma } from '@prisma/client';
|
|||
import prisma from '$lib/prisma.js';
|
||||
|
||||
// Search a user's collection
|
||||
export const GET = async ({ url, locals, params, request }) => {
|
||||
export const GET = async ({ url }) => {
|
||||
const searchParams = Object.fromEntries(url.searchParams);
|
||||
const q = searchParams?.q || '';
|
||||
const limit = parseInt(searchParams?.limit) || 10;
|
||||
const skip = parseInt(searchParams?.skip) || 0;
|
||||
const order: Prisma.SortOrder = <Prisma.SortOrder>searchParams?.order || 'asc';
|
||||
const sort = searchParams?.sort || 'name';
|
||||
console.log('url', url);
|
||||
const order: Prisma.SortOrder = <Prisma.SortOrder>searchParams?.order || 'desc';
|
||||
console.log(`q: ${q}, limit: ${limit}, skip: ${skip}, order: ${order}`);
|
||||
// const sort : Prisma.GameOrderByRelevanceFieldEnum = <Prisma.GameOrderByRelevanceFieldEnum>searchParams?.sort || 'name';
|
||||
// console.log('url', url);
|
||||
|
||||
try {
|
||||
const orderBy = { [sort]: order };
|
||||
let games = await prisma.game.findMany({
|
||||
where: {
|
||||
name: {
|
||||
contains: q
|
||||
search: q
|
||||
}
|
||||
},
|
||||
orderBy: [
|
||||
{
|
||||
...orderBy
|
||||
orderBy: {
|
||||
_relevance: {
|
||||
fields: ['name'],
|
||||
search: q,
|
||||
sort: order
|
||||
}
|
||||
],
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
slug: true,
|
||||
thumb_url: true
|
||||
},
|
||||
skip,
|
||||
|
|
@ -38,6 +41,8 @@ export const GET = async ({ url, locals, params, request }) => {
|
|||
throw error(404, { message: 'No games found' });
|
||||
}
|
||||
|
||||
console.log('Games found in Search API', JSON.stringify(games, null, 2));
|
||||
|
||||
return json(games);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
|
|
|||
Loading…
Reference in a new issue