Adding superforms, image lazy loader, and image icons.

This commit is contained in:
Bradley Shellnut 2023-05-14 21:08:30 -07:00
parent b09f71244f
commit 240bf4aa9e
12 changed files with 1096 additions and 984 deletions

View file

@ -13,55 +13,59 @@
"format": "prettier --write --plugin-search-dir=. ." "format": "prettier --write --plugin-search-dir=. ."
}, },
"devDependencies": { "devDependencies": {
"@iconify-icons/line-md": "^1.2.21", "@playwright/test": "^1.33.0",
"@iconify-icons/mdi": "^1.2.44",
"@playwright/test": "^1.31.2",
"@rgossiaux/svelte-headlessui": "1.0.2", "@rgossiaux/svelte-headlessui": "1.0.2",
"@rgossiaux/svelte-heroicons": "^0.1.2", "@rgossiaux/svelte-heroicons": "^0.1.2",
"@sveltejs/adapter-auto": "^1.0.3", "@sveltejs/adapter-auto": "^1.0.3",
"@sveltejs/adapter-vercel": "^1.0.6", "@sveltejs/adapter-vercel": "^1.0.6",
"@sveltejs/kit": "^1.11.0", "@sveltejs/kit": "^1.16.3",
"@types/cookie": "^0.5.1", "@types/cookie": "^0.5.1",
"@types/node": "^18.15.0", "@types/node": "^18.16.9",
"@typescript-eslint/eslint-plugin": "^5.54.1", "@typescript-eslint/eslint-plugin": "^5.59.5",
"@typescript-eslint/parser": "^5.54.1", "@typescript-eslint/parser": "^5.59.5",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",
"eslint": "^8.36.0", "eslint": "^8.40.0",
"eslint-config-prettier": "^8.7.0", "eslint-config-prettier": "^8.8.0",
"eslint-plugin-svelte3": "^4.0.0", "eslint-plugin-svelte": "^2.28.0",
"just-clone": "^6.2.0", "just-clone": "^6.2.0",
"just-debounce-it": "^3.2.0", "just-debounce-it": "^3.2.0",
"postcss": "^8.4.21", "postcss": "^8.4.23",
"postcss-color-functional-notation": "^4.2.4", "postcss-color-functional-notation": "^4.2.4",
"postcss-custom-media": "^9.1.2", "postcss-custom-media": "^9.1.3",
"postcss-env-function": "^4.0.6", "postcss-env-function": "^4.0.6",
"postcss-import": "^15.1.0", "postcss-import": "^15.1.0",
"postcss-load-config": "^4.0.1", "postcss-load-config": "^4.0.1",
"postcss-media-minmax": "^5.0.0", "postcss-media-minmax": "^5.0.0",
"postcss-nested": "^6.0.1", "postcss-nested": "^6.0.1",
"prettier": "^2.8.4", "prettier": "^2.8.8",
"prettier-plugin-svelte": "^2.9.0", "prettier-plugin-svelte": "^2.10.0",
"sass": "^1.59.2", "sass": "^1.62.1",
"svelte": "^3.56.0", "svelte": "^3.59.1",
"svelte-check": "^2.10.3", "svelte-check": "^2.10.3",
"svelte-preprocess": "^4.10.7", "svelte-preprocess": "^4.10.7",
"sveltekit-superforms": "^0.8.6",
"tslib": "^2.5.0", "tslib": "^2.5.0",
"typescript": "^4.9.5", "typescript": "^4.9.5",
"vite": "^4.1.4", "vite": "^4.3.5",
"vitest": "^0.25.3" "vitest": "^0.25.3",
"zod": "^3.21.4"
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@fontsource/fira-mono": "^4.5.10", "@fontsource/fira-mono": "^4.5.10",
"@iconify-icons/line-md": "^1.2.22",
"@iconify-icons/mdi": "^1.2.45",
"@leveluptuts/svelte-side-menu": "^1.0.5", "@leveluptuts/svelte-side-menu": "^1.0.5",
"@leveluptuts/svelte-toy": "^2.0.3", "@leveluptuts/svelte-toy": "^2.0.3",
"@lukeed/uuid": "^2.0.0", "@lukeed/uuid": "^2.0.1",
"@types/feather-icons": "^4.29.1", "@types/feather-icons": "^4.29.1",
"cookie": "^0.5.0", "cookie": "^0.5.0",
"feather-icons": "^4.29.0", "feather-icons": "^4.29.0",
"iconify-icon": "^1.0.7", "iconify-icon": "^1.0.7",
"loader": "^2.1.1",
"open-props": "^1.5.8",
"svelte-lazy": "^1.2.1",
"svelte-lazy-loader": "^1.0.0", "svelte-lazy-loader": "^1.0.0",
"zod": "^3.21.4", "zod-to-json-schema": "^3.21.1"
"zod-to-json-schema": "^3.20.4"
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -1,16 +1,17 @@
<script lang="ts"> <script lang="ts">
import type { ActionData } from './$types';
import { boredState } from '$lib/stores/boredState'; import { boredState } from '$lib/stores/boredState';
import type { PageData } from '.svelte-kit/types/src/routes/$types';
export let data: PageData; export let form;
export let errors;
export let constraints;
console.log('advanced search data', $form);
let submitting = $boredState?.loading; let submitting = $boredState?.loading;
let minAge = +data?.minAge || 1; let minAge = +$form?.minAge || 1;
let minPlayers = +data?.minPlayers || 1; let minPlayers = +$form?.minPlayers || 1;
let maxPlayers = +data?.maxPlayers || 1; let maxPlayers = +$form?.maxPlayers || 1;
let exactMinPlayers = Boolean(data?.exactMinPlayers) || false; let exactMinPlayers = Boolean($form?.exactMinPlayers) || false;
let exactMaxPlayers = Boolean(data?.exactMaxPlayers) || false; let exactMaxPlayers = Boolean($form?.exactMaxPlayers) || false;
</script> </script>
<fieldset class="advanced-search" aria-busy={submitting} disabled={submitting}> <fieldset class="advanced-search" aria-busy={submitting} disabled={submitting}>
@ -19,10 +20,10 @@
Min Age Min Age
<input id="minAge" name="minAge" bind:value={minAge} type="number" min={1} max={120} /> <input id="minAge" name="minAge" bind:value={minAge} type="number" min={1} max={120} />
</label> </label>
{#if data?.errors?.minAge} {#if $errors?.minAge}
<div id="minPlayers-error" class="error"> <div id="minPlayers-error" class="error">
<p aria-label={`Error: ${data?.errors?.minAge}`} class="center"> <p aria-label={`Error: ${$errors?.minAge}`} class="center">
{data?.errors?.minAge} {$errors?.minAge}
</p> </p>
</div> </div>
{/if} {/if}
@ -49,10 +50,10 @@
bind:value={exactMinPlayers} bind:value={exactMinPlayers}
/> />
</label> </label>
{#if data?.errors?.minPlayers} {#if $errors?.minPlayers}
<div id="minPlayers-error" class="error"> <div id="minPlayers-error" class="error">
<p aria-label={`Error: ${data?.errors?.minPlayers}`} class="center"> <p aria-label={`Error: ${$errors?.minPlayers}`} class="center">
{data?.errors?.minPlayers} {$errors?.minPlayers}
</p> </p>
</div> </div>
{/if} {/if}
@ -79,10 +80,10 @@
bind:value={exactMaxPlayers} bind:value={exactMaxPlayers}
/> />
</label> </label>
{#if data?.error?.id === 'maxPlayers'} {#if $errors?.id === 'maxPlayers'}
<div id="maxPlayers-error" class="error"> <div id="maxPlayers-error" class="error">
<p aria-label={`Error: ${data.error.message}`} class="center"> <p aria-label={`Error: ${$errors.message}`} class="center">
Error: {data.error.message} Error: {$errors.message}
</p> </p>
</div> </div>
{/if} {/if}

View file

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { tick } from 'svelte'; import { tick } from 'svelte';
import { applyAction, enhance, type SubmitFunction } from '$app/forms'; import { applyAction, enhance, type SubmitFunction } from '$app/forms';
import type { ActionData, PageData } from './$types'; import SuperDebug from 'sveltekit-superforms/client/SuperDebug.svelte';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import { Disclosure, DisclosureButton, DisclosurePanel } from '@rgossiaux/svelte-headlessui'; import { Disclosure, DisclosureButton, DisclosurePanel } from '@rgossiaux/svelte-headlessui';
import { ChevronRightIcon } from '@rgossiaux/svelte-heroicons/solid'; import { ChevronRightIcon } from '@rgossiaux/svelte-heroicons/solid';
@ -21,11 +21,9 @@
detail: GameType | SavedGameType; detail: GameType | SavedGameType;
} }
export let data: PageData; export let form;
// console.log('search page data', data); export let errors;
export let form: ActionData; export let constraints;
// console.log('search page form', form);
const errors = data?.errors;
export let showButton: boolean = false; export let showButton: boolean = false;
export let advancedSearch: boolean = false; export let advancedSearch: boolean = false;
@ -33,13 +31,13 @@
let gameToRemove: GameType | SavedGameType; let gameToRemove: GameType | SavedGameType;
let numberOfGameSkeleton = 1; let numberOfGameSkeleton = 1;
let submitButton: HTMLElement; let submitButton: HTMLElement;
let pageSize = +data?.limit || 10; let pageSize = +form?.limit || 10;
let totalItems = +data?.totalCount || 0; let totalItems = +form?.searchData?.totalCount || 0;
let offset = +data?.skip || 0; let offset = +form?.skip || 0;
let page = Math.floor(offset / pageSize) + 1 || 1; let page = Math.floor(offset / pageSize) + 1 || 1;
let submitting = $boredState?.loading; let submitting = $boredState?.loading;
let name = data?.name || ''; let name = form?.name || '';
let disclosureOpen = errors || false; let disclosureOpen = $errors.length > 0 || false;
$: skip = (page - 1) * pageSize; $: skip = (page - 1) * pageSize;
$: showPagination = $gameStore?.length > 1; $: showPagination = $gameStore?.length > 1;
@ -121,8 +119,8 @@
await applyAction(result); await applyAction(result);
} else if (result.type === 'success') { } else if (result.type === 'success') {
gameStore.removeAll(); gameStore.removeAll();
gameStore.addAll(result?.data?.games); gameStore.addAll(result?.data?.searchData?.games);
totalItems = result?.data?.totalCount; totalItems = result?.data?.searchData?.totalCount;
// toast.send('Success!', { duration: 3000, type: ToastType.INFO, dismissible: true }); // toast.send('Success!', { duration: 3000, type: ToastType.INFO, dismissible: true });
await applyAction(result); await applyAction(result);
} else { } else {
@ -131,28 +129,36 @@
}; };
}; };
const dev = process.env.NODE_ENV !== 'production';
// TODO: Keep all Pagination Values on back and forth browser // TODO: Keep all Pagination Values on back and forth browser
// TODO: Add cache for certain number of pages so back and forth doesn't request data again // TODO: Add cache for certain number of pages so back and forth doesn't request data again
</script> </script>
<form id="search-form" action="/search" method="get" on:submit={() => { {#if dev}
<SuperDebug data={$form} />
{/if}
<form id="search-form" action="/search" method="GET" on:submit={() => {
skip = 0; skip = 0;
}}> }}>
<div class="search"> <div class="search">
<fieldset class="text-search" aria-busy={submitting} disabled={submitting}> <fieldset class="text-search" aria-busy={submitting} disabled={submitting}>
<label for="q"> <label for="q">Search</label>
Search <input
<input id="q"
id="q" name="q"
name="q" bind:value={$form.q}
bind:value={name} data-invalid={$errors?.q}
type="text" {...$constraints.q}
aria-label="Search boardgame" type="text"
placeholder="Search boardgame" aria-label="Search board games"
/> placeholder="Search board games"
</label> />
<input id="skip" type="hidden" name="skip" bind:value={skip} /> {#if $errors?.q}<span class="invalid">{$errors?.q}</span>{/if}
<input id="limit" type="hidden" name="limit" bind:value={pageSize} />
<input id="skip" type="hidden" name="skip" bind:value={$form.skip} />
<input id="limit" type="hidden" name="limit" bind:value={$form.limit} />
</fieldset> </fieldset>
{#if advancedSearch} {#if advancedSearch}
<Disclosure> <Disclosure>
@ -174,7 +180,9 @@
<!-- Using `static`, `DisclosurePanel` is always rendered, <!-- Using `static`, `DisclosurePanel` is always rendered,
and ignores the `open` state --> and ignores the `open` state -->
<DisclosurePanel static> <DisclosurePanel static>
<AdvancedSearch {data} /> {#if disclosureOpen}
<AdvancedSearch {form} {errors} {constraints} />
{/if}
</DisclosurePanel> </DisclosurePanel>
</div> </div>
{/if} {/if}

5
src/lib/config.ts Normal file
View file

@ -0,0 +1,5 @@
import { dev } from '$app/environment';
export const title = 'Bored Game';
export const description = 'Bored? Find a game! Bored Game!';
export const url = dev ? 'http://localhost:5173' : 'https://boredgame.vercel.app';

View file

@ -31,13 +31,15 @@ function IntegerString<schema extends ZodNumber | ZodOptional<ZodNumber>>(schema
export const search_schema = z export const search_schema = z
.object({ .object({
name: z.string().trim().optional(), q: z.string().trim().optional().default(''),
minAge: IntegerString(z.number().min(1).max(120).optional()), minAge: IntegerString(z.number().min(1).max(120).optional()),
minPlayers: IntegerString(z.number().min(1).max(50).optional()), minPlayers: IntegerString(z.number().min(1).max(50).optional()),
maxPlayers: IntegerString(z.number().min(1).max(50).optional()), maxPlayers: IntegerString(z.number().min(1).max(50).optional()),
exactMinAge: z.preprocess((a) => Boolean(a), z.boolean().optional()), exactMinAge: z.preprocess((a) => Boolean(a), z.boolean().optional()),
exactMinPlayers: z.preprocess((a) => Boolean(a), z.boolean().optional()), exactMinPlayers: z.preprocess((a) => Boolean(a), z.boolean().optional()),
exactMaxPlayers: z.preprocess((a) => Boolean(a), z.boolean().optional()) exactMaxPlayers: z.preprocess((a) => Boolean(a), z.boolean().optional()),
limit: z.number().min(10).max(100).default(10),
skip: z.number().min(0).default(0)
}) })
.superRefine( .superRefine(
({ minPlayers, maxPlayers, minAge, exactMinAge, exactMinPlayers, exactMaxPlayers }, ctx) => { ({ minPlayers, maxPlayers, minAge, exactMinAge, exactMinPlayers, exactMaxPlayers }, ctx) => {

View file

@ -1,8 +1,16 @@
import type { PageServerLoad, Actions } from './$types'; import { superValidate } from 'sveltekit-superforms/server';
import { search_schema } from '$lib/zodValidation';
export const actions: Actions = { export const load = async ({ fetch, url }) => {
default: async ({ request, locals }): Promise<any> => { const formData = Object.fromEntries(url?.searchParams);
// Do things in here formData.name = formData?.q;
return {}; const form = await superValidate(formData, search_schema);
} return { form };
} };
export const actions = {
default: async ({ request, locals }): Promise<any> => {
// Do things in here
return {};
}
};

View file

@ -1,13 +1,11 @@
<script lang="ts"> <script lang="ts">
import type { ActionData, PageData } from './$types'; import { superForm } from 'sveltekit-superforms/client';
import TextSearch from '$lib/components/search/textSearch/index.svelte'; import TextSearch from '$lib/components/search/textSearch/index.svelte';
import RandomSearch from '$lib/components/search/random/index.svelte'; import RandomSearch from '$lib/components/search/random/index.svelte';
import Random from '$lib/components/random/index.svelte'; import Random from '$lib/components/random/index.svelte';
export let data: PageData; export let data;
// console.log('data', data); const { form, errors, constraints } = superForm(data?.form);
export let form: ActionData;
// console.log('form', form);
</script> </script>
<svelte:head> <svelte:head>
@ -26,7 +24,7 @@
<Random /> <Random />
</div> </div>
</section> </section>
<TextSearch showButton advancedSearch {data} {form} /> <TextSearch showButton advancedSearch {form} {errors} {constraints} />
</div> </div>
<style lang="scss"> <style lang="scss">

View file

@ -1,8 +1,7 @@
import { error } from '@sveltejs/kit'; import { error } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
import { boardGameApi } from '../../api'; import { boardGameApi } from '../../api';
export const load: PageServerLoad = async ({ params, setHeaders }) => { export const load = async ({ params, setHeaders }) => {
const queryParams = { const queryParams = {
ids: `${params?.id}`, ids: `${params?.id}`,
fields: fields:

View file

@ -1,93 +1,12 @@
import type { Actions, PageServerLoad, RequestEvent } from '../$types'; import type { Actions, RequestEvent } from '../$types';
import { BOARD_GAME_ATLAS_CLIENT_ID } from '$env/static/private'; import { BOARD_GAME_ATLAS_CLIENT_ID } from '$env/static/private';
import { error } from '@sveltejs/kit'; import { error } from '@sveltejs/kit';
import type { GameType, SearchQuery } from '$root/lib/types'; import { superValidate } from 'sveltekit-superforms/server';
import { mapAPIGameToBoredGame } from '$root/lib/util/gameMapper'; import type { GameType, SearchQuery } from '$lib/types';
import { search_schema } from '$root/lib/zodValidation'; import { mapAPIGameToBoredGame } from '$lib/util/gameMapper';
import { ZodError } from 'zod'; import { search_schema } from '$lib/zodValidation';
export const load: PageServerLoad = async ({ fetch, url }) => {
const formData = Object.fromEntries(url?.searchParams);
formData.name = formData?.q;
const limit = parseInt(formData?.limit) || 10;
const skip = parseInt(formData?.skip) || 0;
const queryParams: SearchQuery = {
order_by: 'rank',
ascending: false,
limit,
skip,
client_id: BOARD_GAME_ATLAS_CLIENT_ID,
fuzzy_match: true,
name: '',
fields:
'id,name,min_age,min_players,max_players,thumb_url,min_playtime,max_playtime,min_age,description'
};
try {
console.log('Parsing Search Schema');
const { name, minAge, minPlayers, maxPlayers, exactMinAge, exactMinPlayers, exactMaxPlayers } =
search_schema.parse(formData);
if (minAge) {
if (exactMinAge) {
queryParams.min_age = minAge;
} else {
queryParams.gt_min_age = minAge === 1 ? 0 : minAge - 1;
}
}
if (minPlayers) {
if (exactMinPlayers) {
queryParams.min_players = minPlayers;
} else {
queryParams.gt_min_players = minPlayers === 1 ? 0 : minPlayers - 1;
}
}
if (maxPlayers) {
if (exactMaxPlayers) {
queryParams.max_players = maxPlayers;
} else {
queryParams.lt_max_players = maxPlayers + 1;
}
}
if (name) {
queryParams.name = name;
}
} catch (parsingError: unknown) {
let errors;
if (parsingError instanceof ZodError) {
const { fieldErrors } = parsingError.flatten();
console.log(`Errors with user input ${fieldErrors}}`);
errors = fieldErrors;
//throw error(400, { message: 'There was an error searching for games!' }); // fail(400, { data: formData, errors });
}
return {
errors,
name: formData.name,
minAge: formData.minAge,
minPlayers: formData.minPlayers,
maxPlayers: formData.maxPlayers,
exactMinPlayers: formData.exactMinPlayers,
exactMaxPlayers: formData.exactMaxPlayers,
games: [],
totalCount: 0,
limit,
skip
};
}
const newQueryParams: Record<string, string> = {};
for (const key in queryParams) {
// console.log('key', key);
// console.log('queryParams[key]', queryParams[key as keyof SearchQuery]);
newQueryParams[key] = `${queryParams[key as keyof SearchQuery]}`;
}
const urlQueryParams = new URLSearchParams(newQueryParams);
console.log('urlQueryParams', urlQueryParams);
async function searchForGames(urlQueryParams) {
try { try {
const url = `https://api.boardgameatlas.com/api/search${ const url = `https://api.boardgameatlas.com/api/search${
urlQueryParams ? `?${urlQueryParams}` : '' urlQueryParams ? `?${urlQueryParams}` : ''
@ -98,20 +17,19 @@ export const load: PageServerLoad = async ({ fetch, url }) => {
'content-type': 'application/json' 'content-type': 'application/json'
} }
}); });
// console.log('board game response', response);
if (!response.ok) { if (!response.ok) {
console.log('Status not 200', response.status); console.log('Status not 200', response.status);
throw error(response.status); throw error(response.status);
} }
if (response.status === 200) { const games: GameType[] = [];
let totalCount = 0;
if (response.ok) {
const gameResponse = await response.json(); const gameResponse = await response.json();
// console.log('gameResponse', gameResponse);
const gameList = gameResponse?.games; const gameList = gameResponse?.games;
const totalCount = gameResponse?.count; totalCount = gameResponse?.count;
console.log('totalCount', totalCount); console.log('totalCount', totalCount);
const games: GameType[] = [];
gameList.forEach((game) => { gameList.forEach((game) => {
if (game?.min_players && game?.max_players) { if (game?.min_players && game?.max_players) {
game.players = `${game.min_players}-${game.max_players}`; game.players = `${game.min_players}-${game.max_players}`;
@ -119,30 +37,75 @@ export const load: PageServerLoad = async ({ fetch, url }) => {
} }
games.push(mapAPIGameToBoredGame(game)); games.push(mapAPIGameToBoredGame(game));
}); });
// console.log('returning from search', games)
return {
name: formData.name,
minAge: formData.minAge,
minPlayers: formData.minPlayers,
maxPlayers: formData.maxPlayers,
exactMinPlayers: formData.exactMinPlayers,
exactMaxPlayers: formData.exactMaxPlayers,
games,
totalCount,
limit,
skip
};
} }
return {
totalCount,
games
};
} catch (e) { } catch (e) {
console.log(`Error searching board games ${e}`); console.log(`Error searching board games ${e}`);
} }
return { return {
games: [],
totalCount: 0, totalCount: 0,
limit, games: []
skip };
}
export const load = async ({ fetch, url }) => {
const defaults = {
limit: 10,
skip: 0
};
const searchParams = Object.fromEntries(url?.searchParams);
searchParams.limit = searchParams.limit || `${defaults.limit}`;
searchParams.skip = searchParams.skip || `${defaults.skip}`;
const form = await superValidate(searchParams, search_schema);
const queryParams: SearchQuery = {
order_by: 'rank',
ascending: false,
limit: form.data?.limit,
skip: form.data?.skip,
client_id: BOARD_GAME_ATLAS_CLIENT_ID,
fuzzy_match: true,
name: form.data?.q,
fields:
'id,name,min_age,min_players,max_players,thumb_url,min_playtime,max_playtime,min_age,description'
};
if (form.data?.minAge) {
if (form.data?.exactMinAge) {
queryParams.min_age = form.data?.minAge;
} else {
queryParams.gt_min_age = form.data?.minAge === 1 ? 0 : form.data?.minAge - 1;
}
}
if (form.data?.minPlayers) {
if (form.data?.exactMinPlayers) {
queryParams.min_players = form.data?.minPlayers;
} else {
queryParams.gt_min_players = form.data?.minPlayers === 1 ? 0 : form.data?.minPlayers - 1;
}
}
if (form.data?.maxPlayers) {
if (form.data?.exactMaxPlayers) {
queryParams.max_players = form.data?.maxPlayers;
} else {
queryParams.lt_max_players = form.data?.maxPlayers + 1;
}
}
const newQueryParams: Record<string, string> = {};
for (const key in queryParams) {
newQueryParams[key] = `${queryParams[key as keyof SearchQuery]}`;
}
const urlQueryParams = new URLSearchParams(newQueryParams);
return {
form,
searchData: await searchForGames(urlQueryParams)
}; };
}; };

View file

@ -1,22 +1,17 @@
<script lang="ts"> <script lang="ts">
import type { ActionData, PageData } from './$types'; import { superForm } from 'sveltekit-superforms/client';
import { gameStore } from '$lib/stores/gameSearchStore'; import { gameStore } from '$lib/stores/gameSearchStore';
import TextSearch from '$lib/components/search/textSearch/index.svelte'; import TextSearch from '$lib/components/search/textSearch/index.svelte';
export let data: PageData; export let data;
export let form: ActionData; const { form, errors, constraints } = superForm(data?.form);
$: if (data?.games) { $: if (data?.searchData?.games) {
gameStore.removeAll(); gameStore.removeAll();
gameStore.addAll(data?.games); gameStore.addAll(data?.searchData?.games);
}
$: if (form?.games) {
gameStore.removeAll();
gameStore.addAll(form?.games);
} }
</script> </script>
<div class="game-search"> <div class="game-search">
<TextSearch showButton advancedSearch {data} {form} /> <TextSearch showButton advancedSearch {form} {errors} {constraints} />
</div> </div>

View file

@ -1,11 +1,6 @@
import { sveltekit } from '@sveltejs/kit/vite'; import { sveltekit } from '@sveltejs/kit/vite';
import type { UserConfig } from 'vite'; import { defineConfig } from 'vite';
const config: UserConfig = { export default defineConfig({
plugins: [sveltekit()], plugins: [sveltekit()]
test: { });
include: ['src/**/*.{test,spec}.{js,ts}']
}
};
export default config;