mirror of
https://github.com/BradNut/boredgame
synced 2025-09-08 17:40:22 +00:00
Refactor component names, add api for random games, and use on main page.
This commit is contained in:
parent
daa9a628d1
commit
994d1d462c
23 changed files with 197 additions and 262 deletions
|
|
@ -27,7 +27,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@melt-ui/pp": "^0.1.4",
|
||||
"@melt-ui/svelte": "^0.66.3",
|
||||
"@melt-ui/svelte": "^0.66.4",
|
||||
"@playwright/test": "^1.40.1",
|
||||
"@resvg/resvg-js": "^2.4.1",
|
||||
"@sveltejs/adapter-auto": "^3.0.0",
|
||||
|
|
|
|||
|
|
@ -105,10 +105,10 @@ dependencies:
|
|||
devDependencies:
|
||||
'@melt-ui/pp':
|
||||
specifier: ^0.1.4
|
||||
version: 0.1.4(@melt-ui/svelte@0.66.3)(svelte@4.2.8)
|
||||
version: 0.1.4(@melt-ui/svelte@0.66.4)(svelte@4.2.8)
|
||||
'@melt-ui/svelte':
|
||||
specifier: ^0.66.3
|
||||
version: 0.66.3(svelte@4.2.8)
|
||||
specifier: ^0.66.4
|
||||
version: 0.66.4(svelte@4.2.8)
|
||||
'@playwright/test':
|
||||
specifier: ^1.40.1
|
||||
version: 1.40.1
|
||||
|
|
@ -1035,14 +1035,14 @@ packages:
|
|||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@melt-ui/pp@0.1.4(@melt-ui/svelte@0.66.3)(svelte@4.2.8):
|
||||
/@melt-ui/pp@0.1.4(@melt-ui/svelte@0.66.4)(svelte@4.2.8):
|
||||
resolution: {integrity: sha512-zR+Kl3CZJPJBHW8V7YcdQCMI/dVcnW9Ct3yGbVaIywYVStVRS7F9uEDOea3xLLT2WTGodQePzPlUn53yKFu87g==}
|
||||
engines: {pnpm: '>=8.6.3'}
|
||||
peerDependencies:
|
||||
'@melt-ui/svelte': '>= 0.29.0'
|
||||
svelte: ^3.55.0 || ^4.0.0 || ^5.0.0-next.1
|
||||
dependencies:
|
||||
'@melt-ui/svelte': 0.66.3(svelte@4.2.8)
|
||||
'@melt-ui/svelte': 0.66.4(svelte@4.2.8)
|
||||
estree-walker: 3.0.3
|
||||
svelte: 4.2.8
|
||||
dev: true
|
||||
|
|
@ -1061,8 +1061,8 @@ packages:
|
|||
svelte: 4.2.8
|
||||
dev: false
|
||||
|
||||
/@melt-ui/svelte@0.66.3(svelte@4.2.8):
|
||||
resolution: {integrity: sha512-inwvI+YjvMWykK8PEYIg9sAx0sQHI29XeX9hfrdtP47mFVa61pQLqrLoADBpTSb5gO9ZzuL01agXGFfzjK1iPw==}
|
||||
/@melt-ui/svelte@0.66.4(svelte@4.2.8):
|
||||
resolution: {integrity: sha512-RYzgje5/0WQiN8YYAoeuHP7Ua/Ew2oPqBVOkMqlqRquARyiD1gP1RsfXWH637i06q+T5S+UuIiVkc4b/pbcZ4g==}
|
||||
peerDependencies:
|
||||
svelte: '>=3 <5'
|
||||
dependencies:
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ import { lucia } from '$lib/server/auth';
|
|||
Sentry.init({
|
||||
dsn: 'https://742e43279df93a3c4a4a78c12eb1f879@o4506057768632320.ingest.sentry.io/4506057770401792',
|
||||
tracesSampleRate: 1,
|
||||
environment: dev ? 'development' : 'production'
|
||||
environment: dev ? 'development' : 'production',
|
||||
enabled: !dev
|
||||
});
|
||||
|
||||
export const authentication: Handle = async function ({ event, resolve }) {
|
||||
|
|
|
|||
64
src/lib/components/Game.svelte
Normal file
64
src/lib/components/Game.svelte
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
<script lang="ts">
|
||||
import type { GameType, SavedGameType } from '$lib/types';
|
||||
import * as Card from "$lib/components/ui/card";
|
||||
import type { CollectionItem } from '@prisma/client';
|
||||
|
||||
export let game: GameType | CollectionItem;
|
||||
export let detailed: boolean = false;
|
||||
export let variant: 'default' | 'compact' = 'default';
|
||||
|
||||
// Naive and assumes description is only on our GameType at the moment
|
||||
function isGameType(game: GameType | SavedGameType): game is GameType {
|
||||
return (game as GameType).description !== undefined;
|
||||
}
|
||||
</script>
|
||||
|
||||
<article class="grid grid-template-cols-2 gap-4">
|
||||
<Card.Root class={variant === 'compact' ? 'game-card-compact' : ''}>
|
||||
<Card.Header>
|
||||
<Card.Title class="game-card-header">
|
||||
<span style:--transition-name="game-name-{game.slug}">
|
||||
{game.name}
|
||||
{#if game?.year_published}
|
||||
({game?.year_published})
|
||||
{/if}
|
||||
</span>
|
||||
</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content class={variant === 'compact' ? 'pt-6' : ''}>
|
||||
<a
|
||||
class="thumbnail"
|
||||
href={`/game/${game.id}`}
|
||||
title={`View ${game.name}`}
|
||||
data-sveltekit-preload-data
|
||||
>
|
||||
<img src={game.thumb_url} alt={`Image of ${game.name}`} loading="lazy" decoding="async" />
|
||||
<div class="game-details">
|
||||
{#if game?.players}
|
||||
<p>Players: {game.players}</p>
|
||||
<p>Time: {game.playtime} minutes</p>
|
||||
{#if isGameType(game) && game?.min_age}
|
||||
<p>Min Age: {game.min_age}</p>
|
||||
{/if}
|
||||
{#if detailed && isGameType(game) && game?.description}
|
||||
<div class="description">{@html game.description}</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</a>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
</article>
|
||||
|
||||
<style lang="postcss">
|
||||
:global(.game-card-compact) {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
|
||||
.game-card-header {
|
||||
span {
|
||||
view-transition-name: var(--transition-name);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { fade } from 'svelte/transition';
|
||||
import type { GameType, SavedGameType } from '$lib/types';
|
||||
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '$components/ui/card';
|
||||
import { Button } from '$components/ui/button';
|
||||
import type { CollectionItem } from '@prisma/client';
|
||||
|
||||
// export let data: SuperValidated<ListGameSchema>;
|
||||
export let game: GameType | CollectionItem;
|
||||
export let detailed: boolean = false;
|
||||
|
||||
// Naive and assumes description is only on our GameType at the moment
|
||||
function isGameType(game: GameType | SavedGameType): game is GameType {
|
||||
return (game as GameType).description !== undefined;
|
||||
}
|
||||
</script>
|
||||
|
||||
<article class="grid grid-template-cols-2 gap-4" transition:fade|global>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{game.name}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<a
|
||||
class="thumbnail"
|
||||
href={`/game/${game.id}`}
|
||||
title={`View ${game.name}`}
|
||||
data-sveltekit-preload-data
|
||||
>
|
||||
<img src={game.thumb_url} alt={`Image of ${game.name}`} loading="lazy" decoding="async" />
|
||||
<div class="game-details">
|
||||
{#if game?.players}
|
||||
<p>Players: {game.players}</p>
|
||||
<p>Time: {game.playtime} minutes</p>
|
||||
{#if isGameType(game) && game?.min_age}
|
||||
<p>Min Age: {game.min_age}</p>
|
||||
{/if}
|
||||
{#if detailed && isGameType(game) && game?.description}
|
||||
<div class="description">{@html game.description}</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</a>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</article>
|
||||
|
||||
<!-- <article class="game-container" transition:fade|global>
|
||||
<h2>{game.name}</h2>
|
||||
<a
|
||||
class="thumbnail"
|
||||
href={`/game/${game.id}`}
|
||||
title={`View ${game.name}`}
|
||||
data-sveltekit-preload-data
|
||||
>
|
||||
<!-- <Image src={game.thumb_url} alt={`Image of ${game.name}`} /> -->
|
||||
<!-- <img src={game.thumb_url} alt={`Image of ${game.name}`} loading="lazy" decoding="async" /> -->
|
||||
<!-- loading="lazy" decoding="async" -->
|
||||
<!-- </a> -->
|
||||
|
||||
<!-- <div class="game-details">
|
||||
{#if game?.players}
|
||||
<p>Players: {game.players}</p>
|
||||
<p>Time: {game.playtime} minutes</p>
|
||||
{#if isGameType(game) && game?.min_age}
|
||||
<p>Min Age: {game.min_age}</p>
|
||||
{/if}
|
||||
{#if detailed && isGameType(game) && game?.description}
|
||||
<div class="description">{@html game.description}</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="game-buttons">
|
||||
<Button size="md" kind={existsInCollection ? 'danger' : 'primary'} icon on:click={onCollectionClick}>
|
||||
{collectionText}
|
||||
{#if existsInCollection}
|
||||
<iconify-icon icon={minusCircle} width="24" height="24" />
|
||||
{:else}
|
||||
<iconify-icon icon={plusCircle} width="24" height="24" />
|
||||
{/if}
|
||||
</Button>
|
||||
<Button size="md" kind={existsInWishlist ? 'danger' : 'primary'} icon on:click={onWishlistClick}>
|
||||
{wishlistText}
|
||||
{#if existsInWishlist}
|
||||
<iconify-icon icon={minusCircle} width="24" height="24" />
|
||||
{:else}
|
||||
<iconify-icon icon={plusCircle} width="24" height="24" />
|
||||
{/if}
|
||||
</Button>
|
||||
</div> -->
|
||||
<!-- </article> -->
|
||||
|
||||
<style lang="scss">
|
||||
.game-container {
|
||||
display: grid;
|
||||
/* grid-template-rows: repeat(auto-fill, 1fr); */
|
||||
grid-template-rows: 0.15fr minmax(250px, 1fr) 0.2fr 0.2fr;
|
||||
place-items: center;
|
||||
text-align: center;
|
||||
|
||||
@media (max-width: 650px) {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
gap: var(--spacing-16);
|
||||
padding: var(--spacing-16) var(--spacing-16);
|
||||
transition: all 0.3s;
|
||||
border-radius: 8px;
|
||||
background-color: var(--primary);
|
||||
/* &:hover {
|
||||
background-color: hsla(222, 9%, 65%, 1);
|
||||
} */
|
||||
|
||||
/* .game-info {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
gap: 0.75rem;
|
||||
margin: 0.2rem;
|
||||
} */
|
||||
|
||||
.game-details {
|
||||
p,
|
||||
a {
|
||||
padding: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.game-buttons {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { cn } from "$lib/utils";
|
||||
|
||||
let className: string | undefined | null = undefined;
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<div
|
||||
class={cn(
|
||||
"rounded-lg border bg-card text-card-foreground shadow-sm",
|
||||
className
|
||||
)}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:focusin
|
||||
on:focusout
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { cn } from "$lib/utils";
|
||||
|
||||
let className: string | undefined | null = undefined;
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<div class={cn("p-6 pt-0", className)} {...$$restProps}>
|
||||
<slot />
|
||||
</div>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { cn } from "$lib/utils";
|
||||
|
||||
let className: string | undefined | null = undefined;
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<p class={cn("text-sm text-muted-foreground", className)} {...$$restProps}>
|
||||
<slot />
|
||||
</p>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { cn } from "$lib/utils";
|
||||
|
||||
let className: string | undefined | null = undefined;
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<div class={cn("flex items-center p-6 pt-0", className)} {...$$restProps}>
|
||||
<slot />
|
||||
</div>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { cn } from "$lib/utils";
|
||||
|
||||
let className: string | undefined | null = undefined;
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<div class={cn("flex flex-col space-y-1.5 p-6", className)} {...$$restProps}>
|
||||
<slot />
|
||||
</div>
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { cn } from "$lib/utils";
|
||||
|
||||
let className: string | undefined | null = undefined;
|
||||
export { className as class };
|
||||
|
||||
export let tag: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" = "h3";
|
||||
</script>
|
||||
|
||||
<svelte:element
|
||||
this={tag}
|
||||
class={cn("text-lg font-semibold leading-none tracking-tight", className)}
|
||||
{...$$restProps}
|
||||
>
|
||||
<slot />
|
||||
</svelte:element>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
// import { tick, onDestroy } from 'svelte';
|
||||
import Game from '$lib/components/game/index.svelte';
|
||||
import Game from '$components/Game.svelte';
|
||||
|
||||
export let data;
|
||||
console.log(`Page data: ${JSON.stringify(data)}`);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import Game from '$lib/components/game/index.svelte';
|
||||
import Game from '$components/Game.svelte';
|
||||
|
||||
export let data;
|
||||
console.log('data', data);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import Game from '$lib/components/game/index.svelte';
|
||||
import Game from '$components/Game.svelte';
|
||||
|
||||
export let data;
|
||||
console.log('data', data);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import 'iconify-icon';
|
||||
import Header from '$components/Header.svelte';
|
||||
import Footer from '$lib/components/footer.svelte';
|
||||
import Footer from '$components/Footer.svelte';
|
||||
|
||||
export let data;
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -44,15 +44,8 @@ export const load: PageServerLoad = async ({ fetch, url }) => {
|
|||
const form = await superValidate(formData, search_schema);
|
||||
console.log('form', form);
|
||||
|
||||
const count = 5;
|
||||
const ids: { id: string }[] = await prisma.$queryRaw`SELECT id FROM games ORDER BY RAND() LIMIT ${count}`;
|
||||
const randomGames: Game[] = await prisma.game.findMany({
|
||||
where: {
|
||||
id: {
|
||||
in: ids.map(id => id.id)
|
||||
}
|
||||
}
|
||||
});
|
||||
const randomGames: Game[] = await fetch('/api/game/random?limit=6').then(res => res.json());
|
||||
console.log('randomGames', randomGames);
|
||||
|
||||
return { form, metaTagsChild: metaTags, randomGames };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
// import TextSearch from '$lib/components/search/textSearch/header.svelte';
|
||||
import RandomSearch from '$lib/components/search/random/index.svelte';
|
||||
import Game from '$lib/components/game/index.svelte';
|
||||
import Game from '$components/Game.svelte';
|
||||
import logo from '$lib/assets/bored-game.png';
|
||||
|
||||
// import Random from '$lib/components/random/header.svelte';
|
||||
|
|
@ -26,13 +26,13 @@
|
|||
<!-- <TextSearch showButton advancedSearch data={data.form} /> -->
|
||||
</div>
|
||||
|
||||
<section>
|
||||
<section class="random-games">
|
||||
{#each randomGames as game (game.id)}
|
||||
<Game {game} />
|
||||
<Game variant='compact' {game} />
|
||||
{/each}
|
||||
</section>
|
||||
|
||||
<style lang="scss">
|
||||
<style lang="postcss">
|
||||
.game-search {
|
||||
display: grid;
|
||||
gap: 2rem;
|
||||
|
|
@ -43,6 +43,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
.random-games {
|
||||
margin-top: 1rem;
|
||||
display: grid;
|
||||
gap: 0.5rem;
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
}
|
||||
|
||||
.random-buttons {
|
||||
display: flex;
|
||||
place-content: space-between;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
<title>{game?.name} | Bored Game</title>
|
||||
</svelte:head>
|
||||
|
||||
<h2>{game?.name}
|
||||
<h2 style:--transition-name="game-name-{game.slug}">{game?.name}
|
||||
{#if game?.year_published}
|
||||
({game?.year_published})
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@
|
|||
import type { SuperValidated } from 'sveltekit-superforms';
|
||||
import { superForm } from 'sveltekit-superforms/client';
|
||||
import SuperDebug from 'sveltekit-superforms/client/SuperDebug.svelte';
|
||||
import { createPagination, melt } from '@melt-ui/svelte';
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-svelte';
|
||||
import { createPagination, createToolbar, melt } from '@melt-ui/svelte';
|
||||
import { ChevronLeft, ChevronRight, LayoutList, LayoutGrid } from 'lucide-svelte';
|
||||
import type { SearchSchema } from '$lib/zodValidation';
|
||||
import Game from '$lib/components/game/index.svelte';
|
||||
import Game from '$components/Game.svelte';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
|
|
@ -23,7 +23,15 @@
|
|||
$: showPagination = totalCount > pageSize;
|
||||
|
||||
const {
|
||||
elements: { root, pageTrigger, prevButton, nextButton },
|
||||
elements: { root: toolbarRoot },
|
||||
builders: { createToolbarGroup },
|
||||
} = createToolbar();
|
||||
const {
|
||||
elements: { group: listStyleGroup, item: listStyleItem },
|
||||
} = createToolbarGroup();
|
||||
|
||||
const {
|
||||
elements: { root: paginationRoot, pageTrigger, prevButton, nextButton },
|
||||
states: { pages, range }
|
||||
} = createPagination({
|
||||
count: totalCount,
|
||||
|
|
@ -31,6 +39,11 @@
|
|||
defaultPage: 1,
|
||||
siblingCount: 1
|
||||
});
|
||||
|
||||
const gameListStyle: 'grid' | 'list' = 'grid';
|
||||
function handleListStyle(event) {
|
||||
console.log(event, typeof event);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="game-search">
|
||||
|
|
@ -64,7 +77,19 @@
|
|||
</search>
|
||||
|
||||
<section class="games">
|
||||
<h1>Games Found:</h1>
|
||||
<div>
|
||||
<h1>Games Found:</h1>
|
||||
<div use:melt={$toolbarRoot}>
|
||||
<div use:melt={$listStyleGroup} class="search-toolbar">
|
||||
<button class="style-item" aria-label='list' use:melt={$listStyleItem('list')} on:m-click|preventDefault={(e) => handleListStyle(e)}>
|
||||
<LayoutList class="square-5" />
|
||||
</button>
|
||||
<button class="style-item" aria-label='grid' use:melt={$listStyleItem('grid')} on:m-click|preventDefault={(e) => handleListStyle(e)}>
|
||||
<LayoutGrid class="square-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="games-list">
|
||||
{#if totalCount > 0}
|
||||
{#each games as game (game.id)}
|
||||
|
|
@ -75,7 +100,7 @@
|
|||
{/if}
|
||||
</div>
|
||||
{#if showPagination}
|
||||
<nav use:melt={$root}>
|
||||
<nav use:melt={$paginationRoot}>
|
||||
<p class="text-center">Showing items {$range.start} - {$range.end}</p>
|
||||
<div class="buttons">
|
||||
<button use:melt={$prevButton}><ChevronLeft /></button>
|
||||
|
|
@ -156,6 +181,11 @@
|
|||
.games-list {
|
||||
display: grid;
|
||||
--listColumns: 4;
|
||||
|
||||
.list {
|
||||
--listColumns: 1;
|
||||
}
|
||||
|
||||
grid-template-columns: repeat(var(--listColumns), minmax(250px, 1fr));
|
||||
gap: 2rem;
|
||||
|
||||
|
|
@ -175,4 +205,24 @@
|
|||
--listColumns: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.search-toolbar {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
gap: 0.15rem;
|
||||
}
|
||||
|
||||
.style-item {
|
||||
padding: 0.1rem;
|
||||
border-radius: 0.375rem;
|
||||
border: 1px solid black;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
&[data-state='on'] {
|
||||
background-color: grey;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
import 'iconify-icon';
|
||||
import { page } from '$app/stores';
|
||||
import { onNavigate } from "$app/navigation";
|
||||
import Analytics from '$lib/components/analytics.svelte';
|
||||
import Analytics from '$components/Analytics.svelte';
|
||||
import Loading from '$lib/components/Loading.svelte';
|
||||
import { theme } from '$state/theme';
|
||||
import PageLoadingIndicator from '$lib/page_loading_indicator.svelte';
|
||||
|
|
@ -29,9 +29,9 @@
|
|||
...$page.data.metaTagsChild
|
||||
}
|
||||
|
||||
const flash = getFlash(page, {
|
||||
clearAfterMs: 6000
|
||||
});
|
||||
// const flash = getFlash(page, {
|
||||
// clearAfterMs: 6000
|
||||
// });
|
||||
|
||||
onMount(() => {
|
||||
// set the theme to the user's active theme
|
||||
|
|
@ -39,21 +39,21 @@
|
|||
document.querySelector('html')?.setAttribute('data-theme', $theme);
|
||||
});
|
||||
|
||||
flash.subscribe(($flash) => {
|
||||
if (!$flash) return;
|
||||
// flash.subscribe(($flash) => {
|
||||
// if (!$flash) return;
|
||||
|
||||
if ($flash.type === 'success') {
|
||||
toast.success($flash.message);
|
||||
} else {
|
||||
toast.error($flash.message, {
|
||||
duration: 5000
|
||||
});
|
||||
}
|
||||
// if ($flash.type === 'success') {
|
||||
// toast.success($flash.message);
|
||||
// } else {
|
||||
// toast.error($flash.message, {
|
||||
// duration: 5000
|
||||
// });
|
||||
// }
|
||||
|
||||
// Clearing the flash message could sometimes
|
||||
// be required here to avoid double-toasting.
|
||||
flash.set(undefined);
|
||||
});
|
||||
// // Clearing the flash message could sometimes
|
||||
// // be required here to avoid double-toasting.
|
||||
// flash.set(undefined);
|
||||
// });
|
||||
|
||||
onNavigate(async (navigation) => {
|
||||
if (!document.startViewTransition) return;
|
||||
|
|
@ -80,7 +80,7 @@
|
|||
</div>
|
||||
|
||||
<Toaster />
|
||||
<Loading />
|
||||
<!-- <Loading /> -->
|
||||
|
||||
<style lang="postcss">
|
||||
.layout {
|
||||
|
|
|
|||
31
src/routes/api/game/random/+server.ts
Normal file
31
src/routes/api/game/random/+server.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import prisma from '$lib/prisma.js';
|
||||
import type { Game } from '@prisma/client';
|
||||
import { error, json } from '@sveltejs/kit';
|
||||
|
||||
export const GET = async ({ url, locals }) => {
|
||||
const searchParams = Object.fromEntries(url.searchParams);
|
||||
const limit = parseInt(searchParams?.limit) || 1;
|
||||
|
||||
if (limit <= 0 || limit > 6) {
|
||||
error(400, { message: 'Limit must be between 1 and 6' });
|
||||
}
|
||||
|
||||
const totalGames = await prisma.game.count();
|
||||
const randomIndex = Math.floor(Math.random() * totalGames);
|
||||
const ids: { id: string }[] = await prisma.$queryRaw`
|
||||
SELECT id
|
||||
FROM games
|
||||
ORDER BY id
|
||||
LIMIT ${limit}
|
||||
OFFSET ${randomIndex}
|
||||
`;
|
||||
const randomGames: Game[] = await prisma.game.findMany({
|
||||
where: {
|
||||
id: {
|
||||
in: ids.map(id => id.id)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return json(randomGames);
|
||||
}
|
||||
Loading…
Reference in a new issue