mirror of
https://github.com/BradNut/boredgame
synced 2025-09-08 17:40:22 +00:00
Updating dependencies, fixing search, fixing wishlist add and remove, fixing game page, not showing buttons on list of games, and adding helper components.
This commit is contained in:
parent
c4a141a1ff
commit
bdfc1dfd3f
28 changed files with 988 additions and 748 deletions
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"cSpell.words": ["iconify", "kickstarter", "msrp"]
|
||||
"cSpell.words": ["iconify", "kickstarter", "lucide", "msrp"]
|
||||
}
|
||||
|
|
|
|||
34
package.json
34
package.json
|
|
@ -22,18 +22,18 @@
|
|||
"seed": "ts-node --esm prisma/seed.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@melt-ui/pp": "^0.0.7",
|
||||
"@playwright/test": "^1.36.2",
|
||||
"@melt-ui/pp": "^0.1.2",
|
||||
"@playwright/test": "^1.37.0",
|
||||
"@sveltejs/adapter-auto": "^1.0.3",
|
||||
"@sveltejs/adapter-vercel": "^1.0.6",
|
||||
"@sveltejs/kit": "^1.22.4",
|
||||
"@sveltejs/kit": "^1.22.5",
|
||||
"@types/cookie": "^0.5.1",
|
||||
"@types/node": "^18.17.1",
|
||||
"@types/node": "^18.17.5",
|
||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||
"@typescript-eslint/parser": "^5.62.0",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"eslint": "^8.46.0",
|
||||
"eslint-config-prettier": "^8.9.0",
|
||||
"autoprefixer": "^10.4.15",
|
||||
"eslint": "^8.47.0",
|
||||
"eslint-config-prettier": "^8.10.0",
|
||||
"eslint-plugin-svelte": "^2.32.4",
|
||||
"just-clone": "^6.2.0",
|
||||
"just-debounce-it": "^3.2.0",
|
||||
|
|
@ -44,18 +44,18 @@
|
|||
"prettier": "^2.8.8",
|
||||
"prettier-plugin-svelte": "^2.10.1",
|
||||
"prisma": "^5.1.1",
|
||||
"sass": "^1.64.2",
|
||||
"svelte": "^4.1.2",
|
||||
"svelte-check": "^3.4.6",
|
||||
"sass": "^1.65.1",
|
||||
"svelte": "^4.2.0",
|
||||
"svelte-check": "^3.5.0",
|
||||
"svelte-preprocess": "^5.0.4",
|
||||
"svelte-sequential-preprocessor": "^2.0.1",
|
||||
"sveltekit-flash-message": "^2.1.0",
|
||||
"sveltekit-superforms": "^1.5.0",
|
||||
"sveltekit-flash-message": "^2.1.3",
|
||||
"sveltekit-superforms": "^1.5.1",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"ts-node": "^10.9.1",
|
||||
"tslib": "^2.6.1",
|
||||
"typescript": "^5.1.6",
|
||||
"vite": "^4.4.8",
|
||||
"vite": "^4.4.9",
|
||||
"vitest": "^0.25.3",
|
||||
"zod": "^3.21.4"
|
||||
},
|
||||
|
|
@ -70,9 +70,9 @@
|
|||
"@iconify-icons/line-md": "^1.2.23",
|
||||
"@iconify-icons/mdi": "^1.2.47",
|
||||
"@lucia-auth/adapter-mysql": "^2.0.0",
|
||||
"@lucia-auth/adapter-prisma": "^3.0.0",
|
||||
"@lucia-auth/adapter-prisma": "^3.0.1",
|
||||
"@lukeed/uuid": "^2.0.1",
|
||||
"@melt-ui/svelte": "^0.27.3",
|
||||
"@melt-ui/svelte": "^0.34.6",
|
||||
"@prisma/client": "5.1.1",
|
||||
"@types/feather-icons": "^4.29.1",
|
||||
"class-variance-authority": "^0.6.1",
|
||||
|
|
@ -82,9 +82,9 @@
|
|||
"iconify-icon": "^1.0.8",
|
||||
"just-kebab-case": "^4.2.0",
|
||||
"loader": "^2.1.1",
|
||||
"lucia": "^2.0.0",
|
||||
"lucia": "^2.3.0",
|
||||
"lucide-svelte": "^0.256.1",
|
||||
"open-props": "^1.5.10",
|
||||
"open-props": "^1.5.11",
|
||||
"radix-svelte": "^0.8.0",
|
||||
"svelte-french-toast": "^1.2.0",
|
||||
"svelte-lazy": "^1.2.1",
|
||||
|
|
|
|||
457
pnpm-lock.yaml
457
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
|
@ -46,7 +46,8 @@ model User {
|
|||
receiveEmail Boolean @default(false)
|
||||
token String? @unique
|
||||
collection Collection?
|
||||
wishlist Wishlist[]
|
||||
wishlist Wishlist?
|
||||
list List[]
|
||||
theme String @default("system")
|
||||
created_at DateTime @default(now()) @db.Timestamp(6)
|
||||
updated_at DateTime @updatedAt @db.Timestamp(6)
|
||||
|
|
@ -101,7 +102,6 @@ model CollectionItem {
|
|||
|
||||
model Wishlist {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
user_id String @unique
|
||||
user User @relation(references: [id], fields: [user_id])
|
||||
items WishlistItem[]
|
||||
|
|
@ -123,6 +123,30 @@ model WishlistItem {
|
|||
@@map("wishlist_items")
|
||||
}
|
||||
|
||||
model List {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
user_id String @unique
|
||||
user User @relation(references: [id], fields: [user_id])
|
||||
items ListItem[]
|
||||
|
||||
@@index([user_id])
|
||||
@@map("lists")
|
||||
}
|
||||
|
||||
model ListItem {
|
||||
id String @id @default(cuid())
|
||||
list_id String @unique
|
||||
list List @relation(references: [id], fields: [list_id], onDelete: Cascade)
|
||||
game_id String @unique
|
||||
game Game @relation(references: [id], fields: [game_id])
|
||||
created_at DateTime @default(now()) @db.Timestamp(6)
|
||||
updated_at DateTime @updatedAt @db.Timestamp(6)
|
||||
|
||||
@@index([game_id, list_id])
|
||||
@@map("list_items")
|
||||
}
|
||||
|
||||
model Game {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
|
|
@ -152,6 +176,7 @@ model Game {
|
|||
expansions Expansion[]
|
||||
collection_items CollectionItem[]
|
||||
wishlist_items WishlistItem[]
|
||||
list_items ListItem[]
|
||||
external_id String @unique
|
||||
created_at DateTime @default(now()) @db.Timestamp(6)
|
||||
updated_at DateTime @updatedAt @db.Timestamp(6)
|
||||
|
|
|
|||
0
src/lib/apis/collection.ts
Normal file
0
src/lib/apis/collection.ts
Normal file
0
src/lib/apis/wishlist.ts
Normal file
0
src/lib/apis/wishlist.ts
Normal file
138
src/lib/components/AddToList.svelte
Normal file
138
src/lib/components/AddToList.svelte
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
<script lang="ts">
|
||||
import { enhance } from "$app/forms";
|
||||
import { fly } from "svelte/transition";
|
||||
import { createSelect, melt } from "@melt-ui/svelte";
|
||||
import { Check, ChevronDown, MinusCircle, PlusCircle } from "lucide-svelte";
|
||||
import Button from "./ui/button/Button.svelte";
|
||||
import type { Collection, Wishlist } from "@prisma/client";
|
||||
|
||||
export let game_id: string;
|
||||
export let collection: Collection;
|
||||
export let wishlist: Wishlist;
|
||||
export let in_wishlist = false;
|
||||
export let in_collection = false;
|
||||
export let lists = [];
|
||||
|
||||
const handleChange = ({ curr, next }) => {
|
||||
console.log({ curr, next });
|
||||
return next;
|
||||
}
|
||||
|
||||
const {
|
||||
elements: { trigger, menu, option, label, group, groupLabel },
|
||||
states: { valueLabel, open },
|
||||
helpers: { isSelected },
|
||||
} = createSelect({
|
||||
forceVisible: true,
|
||||
onValueChange: handleChange
|
||||
});
|
||||
|
||||
console.log({ in_collection, in_wishlist });
|
||||
|
||||
let options: Record<string, string> = {};
|
||||
let list_of_lists = [];
|
||||
if (!in_collection) {
|
||||
options[collection.id] = 'Add to collection';
|
||||
}
|
||||
if (!in_wishlist) {
|
||||
options[wishlist.id] = 'Add to wishlist';
|
||||
}
|
||||
lists.forEach((list) => {
|
||||
if (!list?.in_list) {
|
||||
options[list.id] = list.name;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if in_wishlist}
|
||||
<form method="POST" action={`/wishlist?/remove`} use:enhance>
|
||||
<input type="hidden" name="id" value={game_id} />
|
||||
<Button variant="destructive" type="submit">
|
||||
<MinusCircle class="square-5" /> Remove from wishlist
|
||||
</Button>
|
||||
</form>
|
||||
{:else}
|
||||
<form method="POST" action='/wishlist?/add' use:enhance>
|
||||
<input type="hidden" name="id" value={game_id} />
|
||||
<Button variant="destructive" type="submit">
|
||||
<PlusCircle class="square-5" /> Add to wishlist
|
||||
</Button>
|
||||
</form>
|
||||
{/if}
|
||||
|
||||
{#if in_collection}
|
||||
<form method="POST" action='/collection?/remove' use:enhance>
|
||||
<input type="hidden" name="id" value={game_id} />
|
||||
<Button variant="destructive" type="submit">
|
||||
<MinusCircle class="square-5" /> Remove from collection
|
||||
</Button>
|
||||
</form>
|
||||
{:else}
|
||||
<form method="POST" action='/collection?/add' use:enhance>
|
||||
<input type="hidden" name="id" value={game_id} />
|
||||
<Button variant="destructive" type="submit">
|
||||
<PlusCircle class="square-5" /> Add to collection
|
||||
</Button>
|
||||
</form>
|
||||
{/if}
|
||||
|
||||
<div class="flex flex-col gap-1">
|
||||
<!-- svelte-ignore a11y-label-has-associated-control - $label contains the 'for' attribute -->
|
||||
<button
|
||||
class="flex h-10 min-w-[220px] items-center justify-between rounded-md bg-white px-3 py-2 text-black-500 transition-opacity hover:opacity-90"
|
||||
use:melt={$trigger}
|
||||
aria-label="Wishlist"
|
||||
>
|
||||
{$valueLabel || 'Add to...'}
|
||||
<ChevronDown class="square-5" />
|
||||
</button>
|
||||
{#if $open}
|
||||
<div
|
||||
class="z-10 flex max-h-[360px] flex-col overflow-y-auto rounded-md bg-white p-1 focus:!ring-0"
|
||||
use:melt={$menu}
|
||||
transition:fly={{ duration: 150, y: -5 }}
|
||||
>
|
||||
{#each Object.entries(options) as [key, value]}
|
||||
<div
|
||||
class="flex relative cursor-pointer rounded-md py-1 pl-8 pr-4 text-neutral-800 focus:z-10 focus:text-purple-700 data-[highlighted]:bg-purple-50 data-[selected]:bg-purple-100 data-[highlighted]:text-purple-900 data-[selected]:text-purple-900"
|
||||
use:melt={$option({ value: key, label: value })}
|
||||
>
|
||||
{value}
|
||||
{#if $isSelected(key)}
|
||||
<div class="check">
|
||||
<Check class="square-4" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- <CardFooter>
|
||||
<div class="grid gap-2 place-items-center">
|
||||
<form method="POST" action={`/collection?/${existsInCollection ? 'remove' : 'add'}`}>
|
||||
<input type="hidden" name="id" value={game.id} />
|
||||
<Button variant={existsInCollection ? 'destructive' : 'default'}>
|
||||
{collectionText}
|
||||
{#if existsInCollection}
|
||||
<iconify-icon class="ml-2" icon={minusCircle} width="24" height="24" />
|
||||
{:else}
|
||||
<iconify-icon class="ml-2" icon={plusCircle} width="24" height="24" />
|
||||
{/if}
|
||||
</Button>
|
||||
</form>
|
||||
<!-- Add dropdown for wishlist add -->
|
||||
<!--- <form method="POST" action={`/wishlist?/${existsInWishlist ? 'remove' : 'add'}`}>
|
||||
<input type="hidden" name="id" value={game.id} />
|
||||
<Button variant={existsInWishlist ? 'destructive' : 'default'}>
|
||||
{wishlistText}
|
||||
{#if existsInWishlist}
|
||||
<iconify-icon class="ml-2" icon={minusCircle} width="24" height="24" />
|
||||
{:else}
|
||||
<iconify-icon class="ml-2" icon={plusCircle} width="24" height="24" />
|
||||
{/if}
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
</CardFooter> -->
|
||||
0
src/lib/components/WishlistButton.svelte
Normal file
0
src/lib/components/WishlistButton.svelte
Normal file
|
|
@ -1,89 +1,18 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { Image } from 'svelte-lazy-loader';
|
||||
import { fade } from 'svelte/transition';
|
||||
import plusCircle from '@iconify-icons/line-md/plus-circle';
|
||||
import minusCircle from '@iconify-icons/line-md/minus-circle';
|
||||
import { superForm } from 'sveltekit-superforms/client';
|
||||
// import Button from '$lib/components/button/index.svelte';
|
||||
import type { GameType, SavedGameType } from '$lib/types';
|
||||
import { collectionStore } from '$lib/stores/collectionStore';
|
||||
import { wishlistStore } from '$lib/stores/wishlistStore';
|
||||
import { addToCollection, removeFromCollection } from '$lib/util/manipulateCollection';
|
||||
import { addToWishlist } from '$lib/util/manipulateWishlist';
|
||||
import { browser } from '$app/environment';
|
||||
import { binarySearchOnStore } from '$lib/util/binarySearchOnStore';
|
||||
import { convertToSavedGame } from '$lib/util/gameMapper';
|
||||
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '$components/ui/card';
|
||||
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '$components/ui/card';
|
||||
import { Button } from '$components/ui/button';
|
||||
import type { SuperValidated } from 'sveltekit-superforms';
|
||||
import type { ListGameSchema } from '$lib/zodValidation';
|
||||
import type { CollectionItem } from '@prisma/client';
|
||||
|
||||
// export let data: SuperValidated<ListGameSchema>;
|
||||
export let game: GameType | CollectionItem;
|
||||
export let detailed: boolean = false;
|
||||
|
||||
// const { form, errors, enhance, delayed } = superForm(data);
|
||||
// data={modifyListForm}
|
||||
|
||||
// const dispatch = createEventDispatcher();
|
||||
|
||||
// function removeGameFromWishlist() {
|
||||
// dispatch('handleRemoveWishlist', game);
|
||||
// }
|
||||
|
||||
// function removeGameFromCollection() {
|
||||
// dispatch('handleRemoveCollection', game);
|
||||
// }
|
||||
|
||||
function onCollectionClick() {
|
||||
if (existsInCollection) {
|
||||
removeGameFromCollection();
|
||||
} else {
|
||||
let index = binarySearchOnStore($collectionStore, convertToSavedGame(game), 'en');
|
||||
addToCollection(game, index);
|
||||
if (browser) {
|
||||
localStorage.collection = JSON.stringify($collectionStore);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onWishlistClick() {
|
||||
if (existsInWishlist) {
|
||||
removeGameFromWishlist();
|
||||
} else {
|
||||
addToWishlist(game);
|
||||
if (browser) {
|
||||
localStorage.wishlist = JSON.stringify($wishlistStore);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// function lazy(img: HTMLImageElement) {
|
||||
// function loaded() {
|
||||
// img.classList.add('loaded');
|
||||
// img.classList.remove('loading');
|
||||
// }
|
||||
// if (img.complete) {
|
||||
// loaded();
|
||||
// } else {
|
||||
// img.classList.add('loading');
|
||||
// img.onload = () => loaded();
|
||||
// }
|
||||
// }
|
||||
|
||||
// $: existsInCollection = $collectionStore.find((item: SavedGameType) => item.id === game.id);
|
||||
// $: existsInWishlist = $wishlistStore.find((item: SavedGameType) => item.id === game.id);
|
||||
$: existsInCollection = game.in_collection;
|
||||
$: existsInWishlist = game.in_wishlist;
|
||||
$: collectionText = existsInCollection ? 'Remove from collection' : 'Add to collection';
|
||||
$: wishlistText = existsInWishlist ? 'Remove from wishlist' : 'Add to wishlist';
|
||||
</script>
|
||||
|
||||
<article class="grid grid-template-cols-2 gap-4" transition:fade|global>
|
||||
|
|
@ -113,32 +42,6 @@
|
|||
</div>
|
||||
</a>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<div class="grid gap-2 place-items-center">
|
||||
<form method="POST" action={`/collection?/${existsInCollection ? 'remove' : 'add'}`}>
|
||||
<input type="hidden" name="id" value={game.id} />
|
||||
<Button variant={existsInCollection ? 'destructive' : 'default'} on:click={onCollectionClick}>
|
||||
{collectionText}
|
||||
{#if existsInCollection}
|
||||
<iconify-icon class="ml-2" icon={minusCircle} width="24" height="24" />
|
||||
{:else}
|
||||
<iconify-icon class="ml-2" icon={plusCircle} width="24" height="24" />
|
||||
{/if}
|
||||
</Button>
|
||||
</form>
|
||||
<form method="POST" action={`/wishlist?/${existsInWishlist ? 'remove' : 'add'}`}>
|
||||
<input type="hidden" name="id" value={game.id} />
|
||||
<Button variant={existsInWishlist ? 'destructive' : 'default'} on:click={onWishlistClick}>
|
||||
{wishlistText}
|
||||
{#if existsInWishlist}
|
||||
<iconify-icon class="ml-2" icon={minusCircle} width="24" height="24" />
|
||||
{:else}
|
||||
<iconify-icon class="ml-2" icon={plusCircle} width="24" height="24" />
|
||||
{/if}
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</article>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,32 +1,21 @@
|
|||
<script lang="ts">
|
||||
import { tick } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { applyAction, type SubmitFunction } from '$app/forms';
|
||||
import { fade, fly } from 'svelte/transition';
|
||||
import { superForm } from 'sveltekit-superforms/client';
|
||||
import SuperDebug from 'sveltekit-superforms/client/SuperDebug.svelte';
|
||||
import type { SuperValidated } from 'sveltekit-superforms/index';
|
||||
// import { Disclosure, DisclosureButton, DisclosurePanel } from '@rgossiaux/svelte-headlessui';
|
||||
// import { ChevronRightIcon } from '@rgossiaux/svelte-heroicons/solid';
|
||||
import { boredState } from '$lib/stores/boredState';
|
||||
import AdvancedSearch from '$lib/components/search/advancedSearch/index.svelte';
|
||||
import { xl, md, sm } from '$lib/stores/mediaQueryStore';
|
||||
import { gameStore } from '$lib/stores/gameSearchStore';
|
||||
import { toast } from '../../toast/toast';
|
||||
import Pagination from '$lib/components/pagination/index.svelte';
|
||||
import Game from '$lib/components/game/index.svelte';
|
||||
import { ToastType, type GameType, type SavedGameType } from '$lib/types';
|
||||
// import SkeletonPlaceholder from '../../SkeletonPlaceholder.svelte';
|
||||
import RemoveCollectionDialog from '../../dialog/RemoveCollectionDialog.svelte';
|
||||
import RemoveWishlistDialog from '../../dialog/RemoveWishlistDialog.svelte';
|
||||
import type { ListGameSchema, SearchSchema } from '$lib/zodValidation';
|
||||
import { type GameType, type SavedGameType } from '$lib/types';
|
||||
import type { SearchSchema } from '$lib/zodValidation';
|
||||
import { Label } from '$components/ui/label';
|
||||
import { Input } from '$components/ui/input';
|
||||
import { Button } from '$components/ui/button';
|
||||
|
||||
interface RemoveGameEvent extends Event {
|
||||
detail: GameType | SavedGameType;
|
||||
}
|
||||
|
||||
export let data;
|
||||
console.log("text search data", data);
|
||||
export let showButton: boolean = false;
|
||||
|
|
@ -34,7 +23,6 @@
|
|||
|
||||
const { games, totalCount } = data?.searchData;
|
||||
const { form, errors, enhance, constraints, message }: SuperValidated<SearchSchema> = superForm(data.form);
|
||||
// const { form: modifyListForm, errors: listErrors, constraints: listConstraints, enhance: listEnhance, message: listMessage } : SuperValidated<ListGameSchema> = superForm(data.modifyListForm);
|
||||
|
||||
let gameToRemove: GameType | SavedGameType;
|
||||
let numberOfGameSkeleton = 1;
|
||||
|
|
@ -47,7 +35,6 @@
|
|||
let name = form?.name || '';
|
||||
let disclosureOpen = $errors.length > 0 || false;
|
||||
|
||||
// $: skip = (page - 1) * pageSize;
|
||||
$: showPagination = totalCount > pageSize;
|
||||
|
||||
if ($xl) {
|
||||
|
|
@ -60,12 +47,6 @@
|
|||
numberOfGameSkeleton = 1;
|
||||
}
|
||||
|
||||
// let placeholderList = [...Array(numberOfGameSkeleton).keys()];
|
||||
|
||||
// if (form?.error) {
|
||||
// disclosureOpen = true;
|
||||
// }
|
||||
|
||||
async function handleNextPageEvent(event: CustomEvent) {
|
||||
if (+event?.detail?.page === page + 1) {
|
||||
page += 1;
|
||||
|
|
@ -89,54 +70,6 @@
|
|||
submitButton.click();
|
||||
}
|
||||
|
||||
// function handleRemoveCollection(event: RemoveGameEvent) {
|
||||
// gameToRemove = event?.detail;
|
||||
// boredState.update((n) => ({
|
||||
// ...n,
|
||||
// dialog: { isOpen: true, content: RemoveCollectionDialog, additionalData: gameToRemove }
|
||||
// }));
|
||||
// }
|
||||
|
||||
// function handleRemoveWishlist(event: RemoveGameEvent) {
|
||||
// gameToRemove = event?.detail;
|
||||
// boredState.update((n) => ({
|
||||
// ...n,
|
||||
// dialog: { isOpen: true, content: RemoveWishlistDialog, additionalData: gameToRemove }
|
||||
// }));
|
||||
// }
|
||||
|
||||
// const submitSearch: SubmitFunction = ({ form, data, action, cancel }) => {
|
||||
// const { name } = Object.fromEntries(data);
|
||||
// if (!disclosureOpen && name?.length === 0) {
|
||||
// toast.send('Please enter a search term', {
|
||||
// duration: 3000,
|
||||
// type: ToastType.ERROR,
|
||||
// dismissible: true
|
||||
// });
|
||||
// cancel();
|
||||
// return;
|
||||
// }
|
||||
|
||||
// gameStore.removeAll();
|
||||
// boredState.update((n) => ({ ...n, loading: true }));
|
||||
// return async ({ result }) => {
|
||||
// boredState.update((n) => ({ ...n, loading: false }));
|
||||
// // `result` is an `ActionResult` object
|
||||
// if (result.type === 'error') {
|
||||
// toast.send('Error!', { duration: 3000, type: ToastType.ERROR, dismissible: true });
|
||||
// await applyAction(result);
|
||||
// } else if (result.type === 'success') {
|
||||
// gameStore.removeAll();
|
||||
// gameStore.addAll(result?.data?.searchData?.games);
|
||||
// totalItems = result?.data?.searchData?.totalCount;
|
||||
// // toast.send('Success!', { duration: 3000, type: ToastType.INFO, dismissible: true });
|
||||
// await applyAction(result);
|
||||
// } else {
|
||||
// await applyAction(result);
|
||||
// }
|
||||
// };
|
||||
// };
|
||||
|
||||
const dev = process.env.NODE_ENV !== 'production';
|
||||
|
||||
// TODO: Keep all Pagination Values on back and forth browser
|
||||
|
|
@ -147,7 +80,7 @@
|
|||
<SuperDebug data={$form} />
|
||||
{/if}
|
||||
|
||||
<form id="search-form" action="/search" method="GET">
|
||||
<form id="search-form" action="/search" method="GET" use:enhance>
|
||||
<div class="search">
|
||||
<fieldset class="text-search" aria-busy={submitting} disabled={submitting}>
|
||||
<Label for="label">Search</Label>
|
||||
|
|
|
|||
|
|
@ -265,6 +265,11 @@ const searchResultSchema = z.object({
|
|||
count: z.number()
|
||||
});
|
||||
|
||||
export const WishlistSchema = z.object({
|
||||
name: z.string(),
|
||||
id: z.string()
|
||||
});
|
||||
|
||||
// export const game_raw_schema_json = zodToJsonSchema(game_schema, {
|
||||
// $refStrategy: 'none',
|
||||
// });
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { fail, redirect } from '@sveltejs/kit';
|
||||
import { setError, superValidate } from 'sveltekit-superforms/server';
|
||||
import type { PageServerLoad } from '../$types.js';
|
||||
import type { PageServerLoad } from '../../$types.js';
|
||||
import prisma from '$lib/prisma.js';
|
||||
import { modifyListGameSchema, type ListGame } from '$lib/config/zod-schemas.js';
|
||||
import type { CollectionItemWithGame } from '$lib/types.js';
|
||||
|
|
@ -127,7 +127,7 @@ export const actions = {
|
|||
}
|
||||
|
||||
if (game) {
|
||||
const wishlist = await prisma.wishlist.create({
|
||||
const wishlist = await prisma.collectionItem.create({
|
||||
data: {
|
||||
user_id: session.userId,
|
||||
name: form.name
|
||||
|
|
|
|||
35
src/routes/(protected)/list/+layout.server.ts
Normal file
35
src/routes/(protected)/list/+layout.server.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import prisma from '$lib/prisma.js';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
|
||||
export async function load({ params, locals }) {
|
||||
const session = await locals.auth.validate();
|
||||
if (!session) {
|
||||
throw redirect(302, '/auth/signin');
|
||||
}
|
||||
|
||||
try {
|
||||
let wishlists = await prisma.wishlist.findMany({
|
||||
where: {
|
||||
user_id: session.userId
|
||||
}
|
||||
});
|
||||
|
||||
if (wishlists.length === 0) {
|
||||
const wishlist = await prisma.wishlist.create({
|
||||
data: {
|
||||
user_id: session.userId
|
||||
}
|
||||
});
|
||||
wishlists.push(wishlist);
|
||||
}
|
||||
|
||||
return {
|
||||
wishlists
|
||||
};
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
return {
|
||||
wishlists: []
|
||||
};
|
||||
}
|
||||
41
src/routes/(protected)/list/+layout.svelte
Normal file
41
src/routes/(protected)/list/+layout.svelte
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<script lang="ts">
|
||||
import Transition from '$lib/components/transition/index.svelte';
|
||||
|
||||
export let data;
|
||||
const wishlists = data.wishlists || [];
|
||||
</script>
|
||||
|
||||
|
||||
<aside class="wishlists">
|
||||
<h2>Your Wishlists</h2>
|
||||
<div class="separator"></div>
|
||||
{#each wishlists as wishlist}
|
||||
<h2 class="wishlist"><a href={`/wishlist/${wishlist.id}`}>{wishlist.name}</a></h2>
|
||||
{/each}
|
||||
</aside>
|
||||
<div class="content">
|
||||
<Transition url={data.url} transition={{ type: 'page' }}>
|
||||
<slot />
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
|
||||
<style lang="postcss">
|
||||
.wishlists {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
.separator {
|
||||
margin: 5px 0;
|
||||
height: 1px;
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgba(0, 0, 0, 1);
|
||||
background-color: rgba(0, 0, 0, var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.wishlist {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
</style>
|
||||
126
src/routes/(protected)/list/[id]/+page.server.ts
Normal file
126
src/routes/(protected)/list/[id]/+page.server.ts
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
import { fail, redirect } from '@sveltejs/kit';
|
||||
import { superValidate } from 'sveltekit-superforms/server';
|
||||
import prisma from '$lib/prisma.js';
|
||||
import { list_game_request_schema } from '$lib/zodValidation';
|
||||
|
||||
export async function load({ params, locals }) {
|
||||
const session = await locals.auth.validate();
|
||||
if (!session) {
|
||||
throw redirect(302, '/auth/signin');
|
||||
}
|
||||
|
||||
try {
|
||||
let wishlist = await prisma.wishlist.findUnique({
|
||||
where: {
|
||||
id: params.id,
|
||||
AND: {
|
||||
user_id: session.userId
|
||||
}
|
||||
},
|
||||
include: {
|
||||
items: {
|
||||
include: {
|
||||
game: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
thumb_url: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
wishlist
|
||||
};
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
export const actions = {
|
||||
// Add game to a wishlist
|
||||
add: async (event) => {
|
||||
const { params, locals, request } = event;
|
||||
const form = await superValidate(event, modifyListGameSchema);
|
||||
|
||||
const session = await locals.auth.validate();
|
||||
if (!session) {
|
||||
throw redirect(302, '/auth/signin');
|
||||
}
|
||||
|
||||
let game = await prisma.game.findUnique({
|
||||
where: {
|
||||
id: form.id
|
||||
}
|
||||
});
|
||||
|
||||
if (!game) {
|
||||
// game = await prisma.game.create({
|
||||
// data: {
|
||||
// name: form.name
|
||||
// }
|
||||
// });
|
||||
return fail(400, {
|
||||
message: 'Game not found'
|
||||
});
|
||||
}
|
||||
|
||||
const wishlist = await prisma.wishlist.findUnique({
|
||||
where: {
|
||||
id: params.id
|
||||
}
|
||||
});
|
||||
|
||||
if (wishlist?.user_id !== session.userId) {
|
||||
return fail(401, {
|
||||
message: 'Unauthorized'
|
||||
});
|
||||
}
|
||||
|
||||
if (!wishlist) {
|
||||
throw redirect(302, '/404');
|
||||
}
|
||||
|
||||
const wishlistItem = await prisma.wishlistItem.create({
|
||||
data: {
|
||||
game_id: game.id,
|
||||
wishlist_id: wishlist.id
|
||||
}
|
||||
});
|
||||
|
||||
if (!wishlistItem) {
|
||||
return fail(500, {
|
||||
message: 'Something went wrong'
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
form
|
||||
};
|
||||
},
|
||||
// Create new wishlist
|
||||
create: async ({ params, locals, request }) => {
|
||||
const session = await locals.auth.validate();
|
||||
if (!session) {
|
||||
throw redirect(302, '/auth/signin');
|
||||
}
|
||||
},
|
||||
// Delete a wishlist
|
||||
delete: async ({ params, locals, request }) => {
|
||||
const session = await locals.auth.validate();
|
||||
if (!session) {
|
||||
throw redirect(302, '/auth/signin');
|
||||
}
|
||||
},
|
||||
// Remove game from a wishlist
|
||||
remove: async ({ params, locals, request }) => {
|
||||
const session = await locals.auth.validate();
|
||||
if (!session) {
|
||||
throw redirect(302, '/auth/signin');
|
||||
}
|
||||
}
|
||||
};
|
||||
24
src/routes/(protected)/list/[id]/+page.svelte
Normal file
24
src/routes/(protected)/list/[id]/+page.svelte
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<script lang="ts">
|
||||
import Game from '$lib/components/game/index.svelte';
|
||||
|
||||
export let data;
|
||||
console.log('data', data);
|
||||
const wishlist = data.wishlist;
|
||||
const gamesItems = wishlist?.items;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{`Wishlist ${wishlist.name} | Bored Game`}</title>
|
||||
</svelte:head>
|
||||
|
||||
<h1>{wishlist.name}</h1>
|
||||
<h2>Games on your wishlist:</h2>
|
||||
<div class="games-list">
|
||||
{#if gamesItems.length > 0}
|
||||
{#each gamesItems as game (game.id)}
|
||||
<Game {game} />
|
||||
{/each}
|
||||
{:else}
|
||||
<h2>Sorry no games found!</h2>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
import { redirect } from '@sveltejs/kit';
|
||||
import { error, redirect } from '@sveltejs/kit';
|
||||
import { superValidate } from 'sveltekit-superforms/server';
|
||||
import prisma from '$lib/prisma.js';
|
||||
import { list_game_request_schema } from '$lib/zodValidation';
|
||||
import { modifyListGameSchema } from '$lib/config/zod-schemas.js';
|
||||
|
||||
export async function load({ params, locals }) {
|
||||
const session = await locals.auth.validate();
|
||||
|
|
@ -9,40 +10,50 @@ export async function load({ params, locals }) {
|
|||
throw redirect(302, '/auth/signin');
|
||||
}
|
||||
|
||||
console.log('Wishlist load User id', session.user);
|
||||
|
||||
try {
|
||||
let wishlists = await prisma.wishlist.findMany({
|
||||
let wishlist = await prisma.wishlist.findUnique({
|
||||
where: {
|
||||
user_id: session.userId
|
||||
user_id: session?.user?.userId
|
||||
},
|
||||
include: {
|
||||
items: true
|
||||
items: {
|
||||
include: {
|
||||
game: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
thumb_url: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (wishlists.length === 0) {
|
||||
const wishlist = await prisma.wishlist.create({
|
||||
data: {
|
||||
user_id: session.userId,
|
||||
name: 'My Wishlist'
|
||||
}
|
||||
});
|
||||
wishlists.push(wishlist);
|
||||
if (!wishlist) {
|
||||
throw redirect(302, '/404');
|
||||
}
|
||||
|
||||
console.log('wishlist', wishlist);
|
||||
|
||||
return {
|
||||
wishlists
|
||||
games: wishlist?.items
|
||||
};
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
redirect(302, '/404');
|
||||
}
|
||||
|
||||
export const actions = {
|
||||
// Add game to a wishlist
|
||||
add: async (event) => {
|
||||
const { params, locals, request } = event;
|
||||
const form = await superValidate(event, list_game_request_schema);
|
||||
const form = await superValidate(event, modifyListGameSchema);
|
||||
|
||||
try {
|
||||
const session = await locals.auth.validate();
|
||||
if (!session) {
|
||||
throw redirect(302, '/auth/signin');
|
||||
|
|
@ -50,24 +61,36 @@ export const actions = {
|
|||
|
||||
let game = await prisma.game.findUnique({
|
||||
where: {
|
||||
id: form.id
|
||||
id: form.data.id
|
||||
}
|
||||
});
|
||||
|
||||
if (!game) {
|
||||
game = await prisma.game.create({
|
||||
data: {
|
||||
name: form.name
|
||||
}
|
||||
});
|
||||
// game = await prisma.game.create({
|
||||
// data: {
|
||||
// name: form.name
|
||||
// }
|
||||
// });
|
||||
console.log('game not found');
|
||||
throw redirect(302, '/404');
|
||||
}
|
||||
|
||||
if (game) {
|
||||
const wishlist = await prisma.wishlist.create({
|
||||
const wishlist = await prisma.wishlist.findUnique({
|
||||
where: {
|
||||
user_id: session.user.userId
|
||||
}
|
||||
});
|
||||
|
||||
if (!wishlist) {
|
||||
console.log('Wishlist not found');
|
||||
return error(404, 'Wishlist not found');
|
||||
}
|
||||
|
||||
await prisma.wishlistItem.create({
|
||||
data: {
|
||||
user_id: session.userId,
|
||||
name: form.name
|
||||
game_id: game.id,
|
||||
wishlist_id: wishlist.id
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -75,6 +98,10 @@ export const actions = {
|
|||
return {
|
||||
form
|
||||
};
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return error(500, 'Something went wrong');
|
||||
}
|
||||
},
|
||||
// Create new wishlist
|
||||
create: async ({ params, locals, request }) => {
|
||||
|
|
@ -91,10 +118,58 @@ export const actions = {
|
|||
}
|
||||
},
|
||||
// Remove game from a wishlist
|
||||
remove: async ({ params, locals, request }) => {
|
||||
remove: async (event) => {
|
||||
const { params, locals, request } = event;
|
||||
const form = await superValidate(event, modifyListGameSchema);
|
||||
|
||||
try {
|
||||
const session = await locals.auth.validate();
|
||||
if (!session) {
|
||||
throw redirect(302, '/auth/signin');
|
||||
}
|
||||
|
||||
let game = await prisma.game.findUnique({
|
||||
where: {
|
||||
id: form.data.id
|
||||
}
|
||||
});
|
||||
|
||||
if (!game) {
|
||||
// game = await prisma.game.create({
|
||||
// data: {
|
||||
// name: form.name
|
||||
// }
|
||||
// });
|
||||
console.log('game not found');
|
||||
throw redirect(302, '/404');
|
||||
}
|
||||
|
||||
if (game) {
|
||||
const wishlist = await prisma.wishlist.findUnique({
|
||||
where: {
|
||||
user_id: session.user.userId
|
||||
}
|
||||
});
|
||||
|
||||
if (!wishlist) {
|
||||
console.log('Wishlist not found');
|
||||
return error(404, 'Wishlist not found');
|
||||
}
|
||||
|
||||
await prisma.wishlistItem.delete({
|
||||
where: {
|
||||
wishlist_id: wishlist.id,
|
||||
game_id: game.id
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
form
|
||||
};
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return error(500, 'Something went wrong');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,134 +1,22 @@
|
|||
<script lang="ts">
|
||||
// import { tick, onDestroy } from 'svelte';
|
||||
// import Game from '$lib/components/game/index.svelte';
|
||||
// import { wishlistStore } from '$lib/stores/wishlistStore';
|
||||
// import type { GameType, SavedGameType } from '$lib/types';
|
||||
// import { boredState } from '$lib/stores/boredState';
|
||||
// import Pagination from '$lib/components/pagination/index.svelte';
|
||||
// import RemoveWishlistDialog from '$lib/components/dialog/RemoveWishlistDialog.svelte';
|
||||
// import RemoveCollectionDialog from '$lib/components/dialog/RemoveCollectionDialog.svelte';
|
||||
// import { createSearchStore, searchHandler } from '$lib/stores/search';
|
||||
import Game from '$lib/components/game/index.svelte';
|
||||
|
||||
// let gameToRemove: GameType | SavedGameType;
|
||||
// let pageSize = 10;
|
||||
// let page = 1;
|
||||
|
||||
// const searchStore = createSearchStore($wishlistStore);
|
||||
// console.log('searchStore', $searchStore);
|
||||
|
||||
// const unsubscribe = searchStore.subscribe((model) => searchHandler(model));
|
||||
|
||||
// onDestroy(() => {
|
||||
// unsubscribe();
|
||||
// });
|
||||
|
||||
// $: skip = (page - 1) * pageSize;
|
||||
// $: gamesShown = $searchStore.filtered.slice(skip, skip + pageSize);
|
||||
// $: totalItems = $searchStore.search === '' ? $wishlistStore.length : $searchStore.filtered.length;
|
||||
|
||||
// interface RemoveGameEvent extends Event {
|
||||
// detail: GameType | SavedGameType;
|
||||
// }
|
||||
|
||||
// function handleRemoveCollection(event: RemoveGameEvent) {
|
||||
// gameToRemove = event?.detail;
|
||||
// boredState.update((n) => ({
|
||||
// ...n,
|
||||
// dialog: { isOpen: true, content: RemoveCollectionDialog, additionalData: gameToRemove }
|
||||
// }));
|
||||
// }
|
||||
|
||||
// function handleRemoveWishlist(event: RemoveGameEvent) {
|
||||
// gameToRemove = event?.detail;
|
||||
// boredState.update((n) => ({
|
||||
// ...n,
|
||||
// dialog: { isOpen: true, content: RemoveWishlistDialog, additionalData: gameToRemove }
|
||||
// }));
|
||||
// }
|
||||
|
||||
// async function handleNextPageEvent(event: CustomEvent) {
|
||||
// if (+event?.detail?.page === page + 1) {
|
||||
// page += 1;
|
||||
// }
|
||||
// await tick();
|
||||
// }
|
||||
|
||||
// async function handlePreviousPageEvent(event: CustomEvent) {
|
||||
// if (+event?.detail?.page === page - 1) {
|
||||
// page -= 1;
|
||||
// }
|
||||
// await tick();
|
||||
// }
|
||||
|
||||
// async function handlePerPageEvent(event: CustomEvent) {
|
||||
// page = 1;
|
||||
// pageSize = event.detail.pageSize;
|
||||
// await tick();
|
||||
// }
|
||||
export let data;
|
||||
const wishlists = data.wishlists || [];
|
||||
console.log('data', data);
|
||||
const games = data.games;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Your Wishlist | Bored Game</title>
|
||||
<title>{`Your Wishlist | Bored Game`}</title>
|
||||
</svelte:head>
|
||||
|
||||
<h1>Your Wishlist</h1>
|
||||
{#each wishlists as wishlist}
|
||||
<h2>{wishlist.name}</h2>
|
||||
{/each}
|
||||
<!-- <input type="text" id="search" name="search" placeholder="Search Your Wishlist" bind:value={$searchStore.search} />
|
||||
|
||||
<div class="games">
|
||||
<div class="games-list">
|
||||
{#if $wishlistStore.length === 0}
|
||||
<h2>No games in your wishlist</h2>
|
||||
{:else}
|
||||
{#each gamesShown as game}
|
||||
<Game
|
||||
on:handleRemoveWishlist={handleRemoveWishlist}
|
||||
on:handleRemoveCollection={handleRemoveCollection}
|
||||
{game}
|
||||
/>
|
||||
<h2>Games on your wishlist:</h2>
|
||||
<div class="games-list">
|
||||
{#if games.length > 0}
|
||||
{#each games as game (game.id)}
|
||||
<Game {game} />
|
||||
{/each}
|
||||
{:else}
|
||||
<h2>Sorry no games found!</h2>
|
||||
{/if}
|
||||
</div>
|
||||
{#if $wishlistStore.length !== 0}
|
||||
<Pagination
|
||||
{pageSize}
|
||||
{page}
|
||||
{totalItems}
|
||||
forwardText="Next"
|
||||
backwardText="Prev"
|
||||
pageSizes={[10, 25, 50, 100]}
|
||||
on:nextPageEvent={handleNextPageEvent}
|
||||
on:previousPageEvent={handlePreviousPageEvent}
|
||||
on:perPageEvent={handlePerPageEvent}
|
||||
/>
|
||||
{/if}
|
||||
</div> -->
|
||||
|
||||
<style lang="scss">
|
||||
h1 {
|
||||
margin: 1.5rem 0rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.games {
|
||||
margin: 2rem 0rem;
|
||||
}
|
||||
|
||||
.games-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(200px, 1fr));
|
||||
gap: 2rem;
|
||||
|
||||
@media (max-width: 800px) {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
@media (max-width: 550px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</div>
|
||||
|
|
@ -11,7 +11,6 @@
|
|||
import Header from '$lib/components/header/index.svelte';
|
||||
import Footer from '$lib/components/footer.svelte';
|
||||
import Loading from '$lib/components/loading.svelte';
|
||||
import Transition from '$lib/components/transition/index.svelte';
|
||||
import Portal from '$lib/Portal.svelte';
|
||||
import { boredState } from '$lib/stores/boredState';
|
||||
import { collectionStore } from '$lib/stores/collectionStore';
|
||||
|
|
@ -123,9 +122,9 @@
|
|||
<Header user={data.user} />
|
||||
|
||||
<main>
|
||||
<Transition url={data.url} transition={{ type: 'page' }}>
|
||||
<!-- <Transition url={data.url} transition={{ type: 'page' }}> -->
|
||||
<slot />
|
||||
</Transition>
|
||||
<!-- </Transition> -->
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
|
|
|
|||
33
src/routes/api/wishlist/+server.ts
Normal file
33
src/routes/api/wishlist/+server.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { error } from '@sveltejs/kit';
|
||||
|
||||
// Fetch collection for user
|
||||
// export function GET({ url, locals }) {
|
||||
// if (!locals.user) {
|
||||
// throw error(401, {});
|
||||
// }
|
||||
// const
|
||||
// const searchParams = Object.fromEntries(url.searchParams);
|
||||
// const q = searchParams?.q;
|
||||
// const limit = parseInt(searchParams?.limit) || 10;
|
||||
// const skip = parseInt(searchParams?.skip) || 0;
|
||||
|
||||
// return new Response();
|
||||
// }
|
||||
|
||||
// // Create a new collection for user
|
||||
// export function POST({ url }) {
|
||||
// const searchParams = Object.fromEntries(url.searchParams);
|
||||
// const q = searchParams?.q;
|
||||
// const limit = parseInt(searchParams?.limit) || 10;
|
||||
// const skip = parseInt(searchParams?.skip) || 0;
|
||||
// }
|
||||
|
||||
// // Update or Create a collection
|
||||
// export function PUT({ url }) {
|
||||
// const searchParams = Object.fromEntries(url.searchParams);
|
||||
// const q = searchParams?.q;
|
||||
// const limit = parseInt(searchParams?.limit) || 10;
|
||||
// const skip = parseInt(searchParams?.skip) || 0;
|
||||
// }
|
||||
|
||||
//
|
||||
0
src/routes/api/wishlist/[id]/items/+server.ts
Normal file
0
src/routes/api/wishlist/[id]/items/+server.ts
Normal file
74
src/routes/api/wishlist/[id]/search/+server.ts
Normal file
74
src/routes/api/wishlist/[id]/search/+server.ts
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import { error, json } from '@sveltejs/kit';
|
||||
import prisma from '$lib/prisma.js';
|
||||
import type { CollectionItemWithGame } from '$lib/types.js';
|
||||
|
||||
// Search a user's collection
|
||||
export async function GET({ url, locals, params }) {
|
||||
const searchParams = Object.fromEntries(url.searchParams);
|
||||
const q = searchParams?.q || '';
|
||||
const limit = parseInt(searchParams?.limit) || 10;
|
||||
const skip = parseInt(searchParams?.skip) || 0;
|
||||
const order = searchParams?.order || 'asc';
|
||||
const sort = searchParams?.sort || 'name';
|
||||
const collection_id = params.id;
|
||||
const session = await locals.auth.validate();
|
||||
console.log('url', url);
|
||||
console.log('username', locals?.user?.id);
|
||||
|
||||
if (!session) {
|
||||
throw error(401, { message: 'Unauthorized' });
|
||||
}
|
||||
|
||||
let collection = await prisma.collection.findUnique({
|
||||
where: {
|
||||
user_id: locals.user.userId
|
||||
}
|
||||
});
|
||||
console.log('collection', collection);
|
||||
|
||||
if (!collection) {
|
||||
console.log('Collection was not found');
|
||||
throw error(404, { message: 'Collection was not found' });
|
||||
}
|
||||
|
||||
try {
|
||||
const orderBy = { [sort]: order };
|
||||
let collection_items: CollectionItemWithGame[] = await prisma.collectionItem.findMany({
|
||||
where: {
|
||||
collection_id,
|
||||
AND: [
|
||||
{
|
||||
game: {
|
||||
name: {
|
||||
contains: q
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
orderBy: [
|
||||
{
|
||||
game: {
|
||||
...orderBy
|
||||
}
|
||||
}
|
||||
],
|
||||
include: {
|
||||
game: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
thumb_url: true
|
||||
}
|
||||
}
|
||||
},
|
||||
skip,
|
||||
take: limit
|
||||
});
|
||||
|
||||
return json(collection_items);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
throw error(500, { message: 'Something went wrong' });
|
||||
}
|
||||
}
|
||||
|
|
@ -71,12 +71,10 @@ export const actions = {
|
|||
user_id: user.id
|
||||
},
|
||||
create: {
|
||||
user_id: user.id,
|
||||
name: 'My Wishlist'
|
||||
user_id: user.id
|
||||
},
|
||||
update: {
|
||||
user_id: user.id,
|
||||
name: 'My Wishlist'
|
||||
user_id: user.id
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,8 +89,7 @@ export const actions = {
|
|||
});
|
||||
await prisma.wishlist.create({
|
||||
data: {
|
||||
user_id: user.userId,
|
||||
name: 'My Wishlist'
|
||||
user_id: user.userId
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -12,9 +12,44 @@ export const load = async ({ params, setHeaders, locals }) => {
|
|||
}
|
||||
});
|
||||
console.log('found game', game);
|
||||
|
||||
if (!game) {
|
||||
throw error(404, 'not found');
|
||||
}
|
||||
|
||||
const wishlist = await prisma.wishlist.findUnique({
|
||||
where: {
|
||||
user_id: user.userId
|
||||
},
|
||||
include: {
|
||||
items: {
|
||||
where: {
|
||||
game_id: game.id
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const collection = await prisma.collection.findUnique({
|
||||
where: {
|
||||
user_id: user.userId
|
||||
},
|
||||
include: {
|
||||
items: {
|
||||
where: {
|
||||
game_id: game.id
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
game,
|
||||
user
|
||||
user,
|
||||
in_wishlist: wishlist?.items?.length !== 0 || false,
|
||||
in_collection: collection?.items?.length !== 0 || false,
|
||||
wishlist,
|
||||
collection
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
|
|
|||
|
|
@ -3,20 +3,14 @@
|
|||
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 type { GameType, SavedGameType } from '$lib/types';
|
||||
import type { SavedGameType } from '$lib/types';
|
||||
import { collectionStore } from '$lib/stores/collectionStore';
|
||||
import { wishlistStore } from '$lib/stores/wishlistStore';
|
||||
import RemoveCollectionDialog from '$lib/components/dialog/RemoveCollectionDialog.svelte';
|
||||
import { addToCollection } from '$lib/util/manipulateCollection';
|
||||
import type { PageData } from './$types';
|
||||
import { boredState } from '$lib/stores/boredState';
|
||||
import { browser } from '$app/environment';
|
||||
import LinkWithIcon from '$lib/components/LinkWithIcon.svelte';
|
||||
import { addToWishlist } from '$lib/util/manipulateWishlist';
|
||||
import RemoveWishlistDialog from '$lib/components/dialog/RemoveWishlistDialog.svelte';
|
||||
import { binarySearchOnStore } from '$lib/util/binarySearchOnStore';
|
||||
import { convertToSavedGame } from '$lib/util/gameMapper';
|
||||
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);
|
||||
|
|
@ -25,56 +19,11 @@
|
|||
|
||||
export let data: PageData;
|
||||
console.log('data', data);
|
||||
// let game: GameType;
|
||||
$: ({ game, user } = data);
|
||||
// let game = data?.game;
|
||||
// export let game: GameType = data?.game;
|
||||
|
||||
$: ({ game, user, wishlist, collection, in_collection, in_wishlist } = data);
|
||||
|
||||
let seeMore: boolean = false;
|
||||
console.log('game', game);
|
||||
// let firstParagraphEnd = 0;
|
||||
// if (game?.description?.indexOf('</p>') > 0) {
|
||||
// firstParagraphEnd = game?.description?.indexOf('</p>') + 4;
|
||||
// } else if (game?.description?.indexOf('</ p>') > 0) {
|
||||
// firstParagraphEnd = game?.description?.indexOf('</ p>') + 5;
|
||||
// }
|
||||
|
||||
function onCollectionClick() {
|
||||
if (existsInCollection) {
|
||||
removeFromCollection();
|
||||
} else {
|
||||
let index = binarySearchOnStore($collectionStore, convertToSavedGame(game), 'en');
|
||||
console.log(`Binary index: ${index}`)
|
||||
addToCollection(game, index);
|
||||
if (browser) {
|
||||
localStorage.collection = JSON.stringify($collectionStore);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onWishlistClick() {
|
||||
if (existsInWishlist) {
|
||||
removeFromWishList();
|
||||
} else {
|
||||
addToWishlist(game);
|
||||
if (browser) {
|
||||
localStorage.wishlist = JSON.stringify($wishlistStore);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeFromCollection() {
|
||||
boredState.update((n) => ({
|
||||
...n,
|
||||
dialog: { isOpen: true, content: RemoveCollectionDialog, additionalData: game }
|
||||
}));
|
||||
}
|
||||
|
||||
function removeFromWishList() {
|
||||
boredState.update((n) => ({
|
||||
...n,
|
||||
dialog: { isOpen: true, content: RemoveWishlistDialog, additionalData: game }
|
||||
}));
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
|
|
@ -86,7 +35,11 @@
|
|||
<section class="game">
|
||||
<div>
|
||||
<a class="thumbnail" href={game.url}>
|
||||
{#if game?.thumb_url}
|
||||
<Image src={game.thumb_url} alt={`Image of ${game.name}`} />
|
||||
{:else}
|
||||
<Dices />
|
||||
{/if}
|
||||
<!-- <img src={game.image_url} alt={`Image of ${game.name}`} /> -->
|
||||
</a>
|
||||
</div>
|
||||
|
|
@ -110,24 +63,7 @@
|
|||
</LinkWithIcon>
|
||||
</div>
|
||||
{#if user?.username}
|
||||
<div style="display: grid; gap: 1.5rem; place-content: center;">
|
||||
<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 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>
|
||||
<AddToList {in_collection} {in_wishlist} game_id={game.id} {wishlist} {collection} />
|
||||
{:else}
|
||||
<span>
|
||||
<Button href="/auth/signup">Sign Up</Button> or <Button href="/auth/signin">Sign In</Button> to add to a list.
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import type { PageServerLoad } from '../$types.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, locals, eventFetch) {
|
||||
async function searchForGames(urlQueryParams: SearchQuery, eventFetch) {
|
||||
try {
|
||||
console.log('urlQueryParams search games', urlQueryParams);
|
||||
// let games = await prisma.game.findMany({
|
||||
|
|
@ -104,8 +104,8 @@ async function searchForGames(urlQueryParams: SearchQuery, locals, eventFetch) {
|
|||
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}`;
|
||||
game.players = `${game.min_players} - ${game.max_players}`;
|
||||
game.playtime = `${game.min_playtime} - ${game.max_playtime}`;
|
||||
}
|
||||
const boredGame = mapAPIGameToBoredGame(game);
|
||||
createOrUpdateGame(boredGame);
|
||||
|
|
@ -114,51 +114,6 @@ async function searchForGames(urlQueryParams: SearchQuery, locals, eventFetch) {
|
|||
}
|
||||
}
|
||||
|
||||
if (locals?.user) {
|
||||
const game_ids = games.map((game) => game.id);
|
||||
console.log('game_ids', game_ids);
|
||||
const collections = await prisma.collection.findMany({
|
||||
where: {
|
||||
user_id: locals.user.id
|
||||
},
|
||||
include: {
|
||||
items: {
|
||||
where: {
|
||||
game_id: {
|
||||
in: game_ids
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
console.log('collections', collections);
|
||||
const wishlists = await prisma.wishlist.findMany({
|
||||
where: {
|
||||
user_id: locals.user.id
|
||||
},
|
||||
include: {
|
||||
items: {
|
||||
where: {
|
||||
id: {
|
||||
in: game_ids
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// console.log('wishlist_items', wishlist_items);
|
||||
for (const game of games) {
|
||||
console.log(
|
||||
'Checking collection',
|
||||
collections.findIndex((item) => item.items.some((i) => i.game_id === game.id))
|
||||
);
|
||||
game.in_collection =
|
||||
collections.findIndex((item) => item.items.some((i) => i.game_id === game.id)) === 0;
|
||||
game.in_wishlist =
|
||||
wishlists.findIndex((item) => item.items.some((i) => i.game_id === game.id)) === 0;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
totalCount,
|
||||
games
|
||||
|
|
@ -290,7 +245,7 @@ export const load: PageServerLoad = async ({ params, locals, request, fetch, url
|
|||
};
|
||||
|
||||
// fields: ('id,name,min_age,min_players,max_players,thumb_url,min_playtime,max_playtime,min_age,description');
|
||||
|
||||
try {
|
||||
if (form.data?.minAge) {
|
||||
if (form.data?.exactMinAge) {
|
||||
queryParams.min_age = form.data?.minAge;
|
||||
|
|
@ -320,13 +275,25 @@ export const load: PageServerLoad = async ({ params, locals, request, fetch, url
|
|||
}
|
||||
|
||||
const urlQueryParams = new URLSearchParams(newQueryParams);
|
||||
const searchData = await searchForGames(urlQueryParams, locals, fetch);
|
||||
const searchData = await searchForGames(urlQueryParams, fetch);
|
||||
// console.log('searchData', searchData);
|
||||
|
||||
return {
|
||||
form,
|
||||
// modifyListForm,
|
||||
searchData
|
||||
};
|
||||
} catch (e) {
|
||||
console.log(`Error searching board games ${e}`);
|
||||
}
|
||||
return {
|
||||
form,
|
||||
searchData: {
|
||||
totalCount: 0,
|
||||
games: []
|
||||
},
|
||||
wishlists: []
|
||||
};
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
|
|
|
|||
|
|
@ -28,4 +28,5 @@ const config = {
|
|||
componentPath: './src/lib/components/ui'
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
|
|
|||
Loading…
Reference in a new issue