mirror of
https://github.com/BradNut/boredgame
synced 2025-09-08 17:40:22 +00:00
Comment out collection until I can fix it. Rewrite wishlist, create users with the default role, add seed roles, update the user with the default role if they don't have it. And other things.
This commit is contained in:
parent
734cee7a29
commit
48c16d7892
28 changed files with 978 additions and 612 deletions
38
package.json
38
package.json
|
|
@ -21,36 +21,36 @@
|
||||||
"seed": "ts-node --esm prisma/seed.ts"
|
"seed": "ts-node --esm prisma/seed.ts"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.34.3",
|
"@playwright/test": "^1.35.1",
|
||||||
"@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.19.0",
|
"@sveltejs/kit": "^1.20.2",
|
||||||
"@types/cookie": "^0.5.1",
|
"@types/cookie": "^0.5.1",
|
||||||
"@types/node": "^18.16.16",
|
"@types/node": "^18.16.18",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.59.7",
|
"@typescript-eslint/eslint-plugin": "^5.59.11",
|
||||||
"@typescript-eslint/parser": "^5.59.7",
|
"@typescript-eslint/parser": "^5.59.11",
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
"eslint": "^8.41.0",
|
"eslint": "^8.42.0",
|
||||||
"eslint-config-prettier": "^8.8.0",
|
"eslint-config-prettier": "^8.8.0",
|
||||||
"eslint-plugin-svelte": "^2.29.0",
|
"eslint-plugin-svelte": "^2.30.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.23",
|
"postcss": "^8.4.24",
|
||||||
"postcss-import": "^15.1.0",
|
"postcss-import": "^15.1.0",
|
||||||
"postcss-load-config": "^4.0.1",
|
"postcss-load-config": "^4.0.1",
|
||||||
"postcss-preset-env": "^8.4.2",
|
"postcss-preset-env": "^8.5.0",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
"prettier-plugin-svelte": "^2.10.0",
|
"prettier-plugin-svelte": "^2.10.1",
|
||||||
"prisma": "^4.14.1",
|
"prisma": "^4.15.0",
|
||||||
"sass": "^1.62.1",
|
"sass": "^1.63.4",
|
||||||
"svelte": "^3.59.1",
|
"svelte": "^3.59.1",
|
||||||
"svelte-check": "^2.10.3",
|
"svelte-check": "^2.10.3",
|
||||||
"svelte-preprocess": "^5.0.3",
|
"svelte-preprocess": "^5.0.4",
|
||||||
"sveltekit-superforms": "^0.8.7",
|
"sveltekit-superforms": "^1.0.0",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"tslib": "^2.5.2",
|
"tslib": "^2.5.3",
|
||||||
"typescript": "^4.9.5",
|
"typescript": "^4.9.5",
|
||||||
"vite": "^4.3.9",
|
"vite": "^4.3.9",
|
||||||
"vitest": "^0.25.3",
|
"vitest": "^0.25.3",
|
||||||
|
|
@ -64,21 +64,21 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@axiomhq/axiom-node": "^0.12.0",
|
"@axiomhq/axiom-node": "^0.12.0",
|
||||||
"@fontsource/fira-mono": "^4.5.10",
|
"@fontsource/fira-mono": "^4.5.10",
|
||||||
"@iconify-icons/line-md": "^1.2.22",
|
"@iconify-icons/line-md": "^1.2.23",
|
||||||
"@iconify-icons/mdi": "^1.2.46",
|
"@iconify-icons/mdi": "^1.2.46",
|
||||||
"@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",
|
||||||
"@lucia-auth/adapter-mysql": "^1.1.1",
|
"@lucia-auth/adapter-mysql": "^1.1.1",
|
||||||
"@lucia-auth/adapter-prisma": "^2.0.0",
|
"@lucia-auth/adapter-prisma": "^2.0.0",
|
||||||
"@lukeed/uuid": "^2.0.1",
|
"@lukeed/uuid": "^2.0.1",
|
||||||
"@prisma/client": "4.14.1",
|
"@prisma/client": "4.15.0",
|
||||||
"@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",
|
"loader": "^2.1.1",
|
||||||
"lucia-auth": "^1.7.0",
|
"lucia-auth": "^1.8.0",
|
||||||
"open-props": "^1.5.8",
|
"open-props": "^1.5.9",
|
||||||
"svelte-lazy": "^1.2.1",
|
"svelte-lazy": "^1.2.1",
|
||||||
"svelte-lazy-loader": "^1.0.0",
|
"svelte-lazy-loader": "^1.0.0",
|
||||||
"svelte-legos": "^0.2.1",
|
"svelte-legos": "^0.2.1",
|
||||||
|
|
|
||||||
848
pnpm-lock.yaml
848
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
|
@ -5,7 +5,7 @@ const config = {
|
||||||
plugins: [
|
plugins: [
|
||||||
atImport(),
|
atImport(),
|
||||||
postcssPresetEnv({
|
postcssPresetEnv({
|
||||||
stage: 3,
|
stage: 2,
|
||||||
features: {
|
features: {
|
||||||
'nesting-rules': true,
|
'nesting-rules': true,
|
||||||
'custom-media-queries': true,
|
'custom-media-queries': true,
|
||||||
|
|
|
||||||
|
|
@ -11,20 +11,39 @@ datasource db {
|
||||||
relationMode = "prisma"
|
relationMode = "prisma"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model Role {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
name String @unique
|
||||||
|
userRoles UserRole[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model UserRole {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
user AuthUser @relation(fields: [userId], references: [id])
|
||||||
|
userId String
|
||||||
|
role Role @relation(fields: [roleId], references: [id])
|
||||||
|
roleId String
|
||||||
|
|
||||||
|
@@unique([userId, roleId])
|
||||||
|
@@index([userId])
|
||||||
|
@@index([roleId])
|
||||||
|
}
|
||||||
|
|
||||||
model AuthUser {
|
model AuthUser {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
username String @unique
|
username String @unique
|
||||||
email String? @unique
|
email String? @unique
|
||||||
firstName String?
|
firstName String?
|
||||||
lastName String?
|
lastName String?
|
||||||
role Role @default(USER)
|
roles UserRole[]
|
||||||
verified Boolean @default(false)
|
verified Boolean @default(false)
|
||||||
receiveEmail Boolean @default(false)
|
receiveEmail Boolean @default(false)
|
||||||
token String? @unique
|
token String? @unique
|
||||||
collection Collection?
|
collection Collection?
|
||||||
wishlist Wishlist[]
|
wishlist Wishlist[]
|
||||||
createdAt DateTime @default(now()) @db.Timestamp(6)
|
theme String @default("system")
|
||||||
updatedAt DateTime @updatedAt @db.Timestamp(6)
|
createdAt DateTime @default(now()) @db.Timestamp(6)
|
||||||
|
updatedAt DateTime @updatedAt @db.Timestamp(6)
|
||||||
auth_session AuthSession[]
|
auth_session AuthSession[]
|
||||||
auth_key AuthKey[]
|
auth_key AuthKey[]
|
||||||
|
|
||||||
|
|
@ -54,11 +73,6 @@ model AuthKey {
|
||||||
@@map("auth_key")
|
@@map("auth_key")
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Role {
|
|
||||||
USER
|
|
||||||
ADMIN
|
|
||||||
}
|
|
||||||
|
|
||||||
model Collection {
|
model Collection {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
user_id String @unique
|
user_id String @unique
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,40 @@
|
||||||
import { PrismaClient } from '@prisma/client';
|
import { PrismaClient } from '@prisma/client';
|
||||||
import userData from '../src/lib/data.json' assert { type: 'json' };
|
// import userData from '../src/lib/data.json' assert { type: 'json' };
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
console.log(`Start seeding ...`);
|
console.log(`Start seeding ...`);
|
||||||
|
|
||||||
for (const p of userData) {
|
const existingRoles = await prisma.role.findMany();
|
||||||
const user = await prisma.user.create({
|
if (existingRoles.length === 0) {
|
||||||
data: {
|
await prisma.role.createMany({
|
||||||
firstName: p.user.firstName,
|
data: [{ name: 'admin' }, { name: 'user' }]
|
||||||
lastName: p.user.lastName,
|
|
||||||
email: p.user.email,
|
|
||||||
username: p.user.username
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
console.log(`Created user with id: ${user.id}`);
|
console.log('Roles created.');
|
||||||
|
} else {
|
||||||
|
console.log('Roles already exist. No action taken.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for (const p of userData) {
|
||||||
|
// const user = await prisma.user.create({
|
||||||
|
// data: {
|
||||||
|
// firstName: p.user.firstName,
|
||||||
|
// lastName: p.user.lastName,
|
||||||
|
// email: p.user.email,
|
||||||
|
// username: p.user.username
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// console.log(`Created user with id: ${user.id}`);
|
||||||
|
// }
|
||||||
console.log(`Seeding finished.`);
|
console.log(`Seeding finished.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
main()
|
main()
|
||||||
.then(async () => {
|
|
||||||
await prisma.$disconnect();
|
|
||||||
})
|
|
||||||
.catch(async (e) => {
|
.catch(async (e) => {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
await prisma.$disconnect();
|
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
})
|
||||||
|
.finally(async () => {
|
||||||
|
await prisma.$disconnect();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
1
src/app.d.ts
vendored
1
src/app.d.ts
vendored
|
|
@ -9,6 +9,7 @@ declare global {
|
||||||
}
|
}
|
||||||
interface Locals {
|
interface Locals {
|
||||||
auth: import('lucia-auth').AuthRequest;
|
auth: import('lucia-auth').AuthRequest;
|
||||||
|
prisma: PrismaClient;
|
||||||
user: Lucia.UserAttributes;
|
user: Lucia.UserAttributes;
|
||||||
startTimer: number;
|
startTimer: number;
|
||||||
error: string;
|
error: string;
|
||||||
|
|
|
||||||
32
src/db/roles.ts
Normal file
32
src/db/roles.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
import prisma from '$lib/prisma';
|
||||||
|
|
||||||
|
export async function add_user_to_role(userId: string, roleName: string) {
|
||||||
|
// Find the role by its name
|
||||||
|
const role = await prisma.role.findUnique({
|
||||||
|
where: {
|
||||||
|
name: roleName
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!role) {
|
||||||
|
throw new Error(`Role with name ${roleName} not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a UserRole entry linking the user and the role
|
||||||
|
const userRole = await prisma.userRole.create({
|
||||||
|
data: {
|
||||||
|
user: {
|
||||||
|
connect: {
|
||||||
|
id: userId
|
||||||
|
}
|
||||||
|
},
|
||||||
|
role: {
|
||||||
|
connect: {
|
||||||
|
id: role.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return userRole;
|
||||||
|
}
|
||||||
56
src/db/users.ts
Normal file
56
src/db/users.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
import { auth } from '$lib/server/lucia';
|
||||||
|
import prisma from '$lib/prisma';
|
||||||
|
import type { AuthUser } from '@prisma/client';
|
||||||
|
import { add_user_to_role } from './roles';
|
||||||
|
|
||||||
|
export function create_user(user: AuthUser) {
|
||||||
|
return prisma.authUser.create({
|
||||||
|
data: {
|
||||||
|
username: user.username
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function find_or_create_user(user: AuthUser) {
|
||||||
|
const existing_user = await prisma.authUser.findUnique({
|
||||||
|
where: {
|
||||||
|
username: user.username
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (existing_user) {
|
||||||
|
return existing_user;
|
||||||
|
} else {
|
||||||
|
const new_user = await create_user(user);
|
||||||
|
add_user_to_role(new_user.id, 'user');
|
||||||
|
return new_user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function find_user_with_roles(user_id: string) {
|
||||||
|
const user_with_roles = await prisma.authUser.findUnique({
|
||||||
|
where: {
|
||||||
|
id: user_id
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
roles: {
|
||||||
|
select: {
|
||||||
|
role: {
|
||||||
|
select: {
|
||||||
|
name: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!user_with_roles) {
|
||||||
|
throw new Error('User not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = {
|
||||||
|
...user_with_roles,
|
||||||
|
roles: user_with_roles.roles.map((user_role) => user_role.role.name)
|
||||||
|
};
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import { redirect, type Handle } from '@sveltejs/kit';
|
import { sequence } from '@sveltejs/kit/hooks';
|
||||||
import type { HandleServerError } from '@sveltejs/kit';
|
import { redirect, type HandleServerError, type Handle } from '@sveltejs/kit';
|
||||||
|
import { dev } from '$app/environment';
|
||||||
import { auth } from '$lib/server/lucia';
|
import { auth } from '$lib/server/lucia';
|
||||||
import log from '$lib/server/log';
|
import log from '$lib/server/log';
|
||||||
import { dev } from '$app/environment';
|
import prisma from '$lib/config/prisma';
|
||||||
|
|
||||||
export const handleError: HandleServerError = async ({ error, event }) => {
|
export const handleError: HandleServerError = async ({ error, event }) => {
|
||||||
const errorId = crypto.randomUUID();
|
const errorId = crypto.randomUUID();
|
||||||
|
|
@ -24,24 +25,28 @@ export const handleError: HandleServerError = async ({ error, event }) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const handle: Handle = async ({ event, resolve }) => {
|
// export const prismaClient: Handle = async function ({ event, resolve }) {
|
||||||
|
// event.locals.prisma = prisma;
|
||||||
|
// const response = await resolve(event);
|
||||||
|
// return response;
|
||||||
|
// };
|
||||||
|
|
||||||
|
export const authentication: Handle = async function ({ event, resolve }) {
|
||||||
const startTimer = Date.now();
|
const startTimer = Date.now();
|
||||||
event.locals.startTimer = startTimer;
|
event.locals.startTimer = startTimer;
|
||||||
|
|
||||||
event.locals.auth = auth.handleRequest(event);
|
event.locals.auth = auth.handleRequest(event);
|
||||||
console.log(JSON.stringify(event));
|
|
||||||
if (event.locals?.auth) {
|
if (event.locals?.auth) {
|
||||||
const { user } = await event.locals.auth.validateUser();
|
const { user } = await event.locals.auth.validateUser();
|
||||||
event.locals.user = user;
|
event.locals.user = user;
|
||||||
if (event.route.id?.startsWith('/(protected)')) {
|
// if (event.route.id?.startsWith('/(protected)')) {
|
||||||
if (!user) throw redirect(302, '/auth/sign-in');
|
// if (!user) throw redirect(302, '/auth/sign-in');
|
||||||
if (!user.verified) throw redirect(302, '/auth/verify/email');
|
// if (!user.verified) throw redirect(302, '/auth/verify/email');
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await resolve(event);
|
const response = await resolve(event);
|
||||||
if (!dev) {
|
|
||||||
log(response.status, event);
|
|
||||||
}
|
|
||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const handle = sequence(authentication);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { enhance } from '$app/forms';
|
import { enhance } from '$app/forms';
|
||||||
import Profile from '../preferences/profile.svelte';
|
// import Profile from '../preferences/profile.svelte';
|
||||||
import logo from './bored-game.png';
|
import logo from './bored-game.png';
|
||||||
|
|
||||||
export let user: any;
|
export let user: any;
|
||||||
|
|
@ -15,9 +15,9 @@
|
||||||
</div>
|
</div>
|
||||||
<!-- <TextSearch /> -->
|
<!-- <TextSearch /> -->
|
||||||
<nav>
|
<nav>
|
||||||
<a href="/collection" title="Go to your collection" data-sveltekit-preload-data>Collection</a>
|
|
||||||
<a href="/wishlist" title="Go to your wishlist" data-sveltekit-preload-data>Wishlist</a>
|
|
||||||
{#if user}
|
{#if user}
|
||||||
|
<a href="/collection" title="Go to your collection" data-sveltekit-preload-data>Collection</a>
|
||||||
|
<a href="/wishlist" title="Go to your wishlist" data-sveltekit-preload-data>Wishlist</a>
|
||||||
<form
|
<form
|
||||||
use:enhance
|
use:enhance
|
||||||
action="/auth/signout"
|
action="/auth/signout"
|
||||||
|
|
@ -36,11 +36,11 @@
|
||||||
<span class="flex-auto">Sign Up</span></a
|
<span class="flex-auto">Sign Up</span></a
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
<Profile />
|
<!-- <Profile /> -->
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="postcss">
|
||||||
header {
|
header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
@ -88,7 +88,6 @@
|
||||||
padding: 0 1em;
|
padding: 0 1em;
|
||||||
color: var(--heading-color);
|
color: var(--heading-color);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
/* font-size: 0.8rem; */
|
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.1em;
|
letter-spacing: 0.1em;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Validation } from 'sveltekit-superforms/index';
|
import type { SuperValidated } from 'sveltekit-superforms/index';
|
||||||
import type { SearchSchema } from '$lib/zodValidation';
|
import type { SearchSchema } from '$lib/zodValidation';
|
||||||
import { boredState } from '$lib/stores/boredState';
|
import { boredState } from '$lib/stores/boredState';
|
||||||
import { gameStore } from '$lib/stores/gameSearchStore';
|
import { gameStore } from '$lib/stores/gameSearchStore';
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
import { superForm } from 'sveltekit-superforms/client';
|
import { superForm } from 'sveltekit-superforms/client';
|
||||||
import { toast } from '../../toast/toast';
|
import { toast } from '../../toast/toast';
|
||||||
|
|
||||||
export let data: Validation<SearchSchema>;
|
export let data: SuperValidated<SearchSchema>;
|
||||||
const { enhance } = superForm(data, {
|
const { enhance } = superForm(data, {
|
||||||
onSubmit: () => {
|
onSubmit: () => {
|
||||||
gameStore.removeAll();
|
gameStore.removeAll();
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
import { applyAction, type SubmitFunction } from '$app/forms';
|
import { applyAction, type SubmitFunction } from '$app/forms';
|
||||||
import { superForm } from 'sveltekit-superforms/client';
|
import { superForm } from 'sveltekit-superforms/client';
|
||||||
import SuperDebug from 'sveltekit-superforms/client/SuperDebug.svelte';
|
import SuperDebug from 'sveltekit-superforms/client/SuperDebug.svelte';
|
||||||
import type { Validation } from 'sveltekit-superforms/index';
|
import type { SuperValidated } from 'sveltekit-superforms/index';
|
||||||
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';
|
||||||
import { boredState } from '$lib/stores/boredState';
|
import { boredState } from '$lib/stores/boredState';
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
import Pagination from '$lib/components/pagination/index.svelte';
|
import Pagination from '$lib/components/pagination/index.svelte';
|
||||||
import Game from '$lib/components/game/index.svelte';
|
import Game from '$lib/components/game/index.svelte';
|
||||||
import { ToastType, type GameType, type SavedGameType } from '$lib/types';
|
import { ToastType, type GameType, type SavedGameType } from '$lib/types';
|
||||||
import SkeletonPlaceholder from '../../SkeletonPlaceholder.svelte';
|
// import SkeletonPlaceholder from '../../SkeletonPlaceholder.svelte';
|
||||||
import RemoveCollectionDialog from '../../dialog/RemoveCollectionDialog.svelte';
|
import RemoveCollectionDialog from '../../dialog/RemoveCollectionDialog.svelte';
|
||||||
import RemoveWishlistDialog from '../../dialog/RemoveWishlistDialog.svelte';
|
import RemoveWishlistDialog from '../../dialog/RemoveWishlistDialog.svelte';
|
||||||
import type { SearchSchema } from '$lib/zodValidation';
|
import type { SearchSchema } from '$lib/zodValidation';
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
detail: GameType | SavedGameType;
|
detail: GameType | SavedGameType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export let data: Validation<SearchSchema>;
|
export let data: SuperValidated<SearchSchema>;
|
||||||
const { form, constraints, errors } = superForm(data, {
|
const { form, constraints, errors } = superForm(data, {
|
||||||
onSubmit: () => {
|
onSubmit: () => {
|
||||||
boredState.update((n) => ({ ...n, loading: true }));
|
boredState.update((n) => ({ ...n, loading: true }));
|
||||||
|
|
@ -213,11 +213,11 @@
|
||||||
<div class="games">
|
<div class="games">
|
||||||
<h1>Games Found:</h1>
|
<h1>Games Found:</h1>
|
||||||
<div class="games-list">
|
<div class="games-list">
|
||||||
{#each placeholderList as game, i}
|
<!-- {#each placeholderList as game, i}
|
||||||
<SkeletonPlaceholder
|
<SkeletonPlaceholder
|
||||||
style="width: 100%; height: 500px; border-radius: var(--borderRadius);"
|
style="width: 100%; height: 500px; border-radius: var(--borderRadius);"
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each} -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,8 @@ import prisma from '@lucia-auth/adapter-prisma';
|
||||||
import { PrismaClient } from '@prisma/client';
|
import { PrismaClient } from '@prisma/client';
|
||||||
import { dev } from '$app/environment';
|
import { dev } from '$app/environment';
|
||||||
|
|
||||||
const client = new PrismaClient();
|
|
||||||
|
|
||||||
export const auth = lucia({
|
export const auth = lucia({
|
||||||
adapter: prisma(client),
|
adapter: prisma(new PrismaClient()),
|
||||||
env: dev ? 'DEV' : 'PROD',
|
env: dev ? 'DEV' : 'PROD',
|
||||||
middleware: sveltekit(),
|
middleware: sveltekit(),
|
||||||
transformDatabaseUser: (userData) => {
|
transformDatabaseUser: (userData) => {
|
||||||
|
|
@ -23,6 +21,9 @@ export const auth = lucia({
|
||||||
receiveEmail: userData.receiveEmail,
|
receiveEmail: userData.receiveEmail,
|
||||||
token: userData.token
|
token: userData.token
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
experimental: {
|
||||||
|
debugMode: false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,13 @@ export const saved_game_schema = z.object({
|
||||||
playtime: IntegerString(z.number())
|
playtime: IntegerString(z.number())
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const list_game_request_schema = z.object({
|
||||||
|
id: z.string(),
|
||||||
|
externalId: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type ListGameSchema = typeof list_game_request_schema;
|
||||||
|
|
||||||
// https://github.com/colinhacks/zod/discussions/330
|
// https://github.com/colinhacks/zod/discussions/330
|
||||||
function IntegerString<schema extends ZodNumber | ZodOptional<ZodNumber>>(schema: schema) {
|
function IntegerString<schema extends ZodNumber | ZodOptional<ZodNumber>>(schema: schema) {
|
||||||
return z.preprocess(
|
return z.preprocess(
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@
|
||||||
<Analytics />
|
<Analytics />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if dev}
|
<!-- {#if dev}
|
||||||
<Toy
|
<Toy
|
||||||
register={{
|
register={{
|
||||||
boredState,
|
boredState,
|
||||||
|
|
@ -85,7 +85,7 @@
|
||||||
toast
|
toast
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if} -->
|
||||||
|
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<Header user={data.user} />
|
<Header user={data.user} />
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@
|
||||||
import Random from '$lib/components/random/index.svelte';
|
import Random from '$lib/components/random/index.svelte';
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
|
export let formData;
|
||||||
|
console.log('formData', formData);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
|
|
|
||||||
5
src/routes/admin/+layout.server.ts
Normal file
5
src/routes/admin/+layout.server.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { redirect } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
export const load = async function ({ locals }) {
|
||||||
|
if (!locals?.user?.role?.includes('admin')) throw redirect(302, '/');
|
||||||
|
};
|
||||||
1
src/routes/admin/+layout.svelte
Normal file
1
src/routes/admin/+layout.svelte
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<slot />
|
||||||
0
src/routes/admin/+page.svelte
Normal file
0
src/routes/admin/+page.svelte
Normal file
|
|
@ -1,7 +1,9 @@
|
||||||
import { fail, redirect } from '@sveltejs/kit';
|
import { fail, redirect } from '@sveltejs/kit';
|
||||||
import { setError, superValidate } from 'sveltekit-superforms/server';
|
import { setError, superValidate } from 'sveltekit-superforms/server';
|
||||||
import { auth } from '$lib/server/lucia';
|
import { auth } from '$lib/server/lucia';
|
||||||
|
import prisma from '$lib/prisma.js';
|
||||||
import { userSchema } from '$lib/config/zod-schemas';
|
import { userSchema } from '$lib/config/zod-schemas';
|
||||||
|
import { add_user_to_role } from '$db/roles';
|
||||||
|
|
||||||
const signInSchema = userSchema.pick({
|
const signInSchema = userSchema.pick({
|
||||||
username: true,
|
username: true,
|
||||||
|
|
@ -13,10 +15,10 @@ export const load = async (event) => {
|
||||||
if (session) {
|
if (session) {
|
||||||
throw redirect(302, '/');
|
throw redirect(302, '/');
|
||||||
}
|
}
|
||||||
// const form = await superValidate(event, signInSchema);
|
const form = await superValidate(event, signInSchema);
|
||||||
// return {
|
return {
|
||||||
// form
|
form
|
||||||
// };
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const actions = {
|
export const actions = {
|
||||||
|
|
@ -30,16 +32,32 @@ export const actions = {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adding user to the db
|
|
||||||
try {
|
try {
|
||||||
const key = await auth.useKey('username', form.data.username, form.data.password);
|
const key = await auth.useKey('username', form.data.username, form.data.password);
|
||||||
const session = await auth.createSession(key.userId);
|
const session = await auth.createSession(key.userId);
|
||||||
event.locals.auth.setSession(session);
|
event.locals.auth.setSession(session);
|
||||||
|
|
||||||
|
const user = await prisma.authUser.findUnique({
|
||||||
|
where: {
|
||||||
|
id: session.userId
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
roles: {
|
||||||
|
select: {
|
||||||
|
role: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (user && user.roles.length === 0) {
|
||||||
|
add_user_to_role(user.id, 'user');
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// TODO: need to return error message to the client
|
// TODO: need to return error message to the client
|
||||||
console.error(e);
|
console.error(e);
|
||||||
form.data.password = '';
|
form.data.password = '';
|
||||||
return setError(form, null, 'The username or password is incorrect.');
|
return setError(form, '', 'The username or password is incorrect.');
|
||||||
}
|
}
|
||||||
form.data.username = '';
|
form.data.username = '';
|
||||||
form.data.password = '';
|
form.data.password = '';
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
import { userSchema } from '$lib/config/zod-schemas.js';
|
import { userSchema } from '$lib/config/zod-schemas.js';
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
|
|
||||||
const signInSchema = userSchema.pick({ username: true, password: true });
|
const signInSchema = userSchema.pick({ username: true, password: true });
|
||||||
const { form, errors, enhance, delayed } = superForm(data.form, {
|
const { form, errors, enhance, delayed } = superForm(data.form, {
|
||||||
taintedMessage: null,
|
taintedMessage: null,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { fail, redirect } from '@sveltejs/kit';
|
||||||
import { setError, superValidate } from 'sveltekit-superforms/server';
|
import { setError, superValidate } from 'sveltekit-superforms/server';
|
||||||
import { auth } from '$lib/server/lucia';
|
import { auth } from '$lib/server/lucia';
|
||||||
import { userSchema } from '$lib/config/zod-schemas';
|
import { userSchema } from '$lib/config/zod-schemas';
|
||||||
|
import { add_user_to_role } from '$db/roles';
|
||||||
|
|
||||||
const signUpSchema = userSchema.pick({
|
const signUpSchema = userSchema.pick({
|
||||||
firstName: true,
|
firstName: true,
|
||||||
|
|
@ -55,6 +56,7 @@ export const actions = {
|
||||||
token
|
token
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
add_user_to_role(user.id, 'user');
|
||||||
|
|
||||||
console.log('User', user);
|
console.log('User', user);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,65 @@
|
||||||
import type { PageServerLoad } from "../$types";
|
// import { redirect } from '@sveltejs/kit';
|
||||||
|
// import { superValidate } from 'sveltekit-superforms/server';
|
||||||
|
// import { search_schema } from '$lib/zodValidation';
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ fetch, url }) => {
|
export const load = async ({ fetch, url, locals }) => {
|
||||||
const searchParams = Object.fromEntries(url?.searchParams);
|
// const session = await locals.auth.validate();
|
||||||
const q = searchParams?.q;
|
// if (!session) {
|
||||||
const limit = parseInt(searchParams?.limit) || 10;
|
// throw redirect(302, '/auth/signin');
|
||||||
const skip = parseInt(searchParams?.skip) || 0;
|
// }
|
||||||
|
|
||||||
|
console.log('locals load', locals);
|
||||||
|
// const searchParams = Object.fromEntries(url?.searchParams);
|
||||||
|
// const q = searchParams?.q;
|
||||||
|
// const limit = parseInt(searchParams?.limit) || 10;
|
||||||
|
// const skip = parseInt(searchParams?.skip) || 0;
|
||||||
|
|
||||||
|
// const searchData = {
|
||||||
|
// q,
|
||||||
|
// limit,
|
||||||
|
// skip
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const form = await superValidate(searchData, search_schema);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// let collection = await locals.prisma.collection.findUnique({
|
||||||
|
// where: {
|
||||||
|
// user_id: session.userId
|
||||||
|
// }
|
||||||
|
// include: {
|
||||||
|
// collectionItems: {
|
||||||
|
// where: {
|
||||||
|
// title: {
|
||||||
|
// contains: q,
|
||||||
|
// mode: 'insensitive'
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// take: limit,
|
||||||
|
// skip
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// console.log('collection', collection);
|
||||||
|
// if (!collection) {
|
||||||
|
// collection = await locals.prisma.collection.create({
|
||||||
|
// data: {
|
||||||
|
// user_id: session.userId
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
return {
|
||||||
|
// form,
|
||||||
|
// collection
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
q,
|
// form,
|
||||||
limit,
|
// collection: []
|
||||||
skip
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,78 +1,78 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { tick, onDestroy } from 'svelte';
|
// import { tick, onDestroy } from 'svelte';
|
||||||
import Game from '$lib/components/game/index.svelte';
|
// import Game from '$lib/components/game/index.svelte';
|
||||||
import { collectionStore } from '$lib/stores/collectionStore';
|
// import { collectionStore } from '$lib/stores/collectionStore';
|
||||||
import type { GameType, SavedGameType } from '$lib/types';
|
// import type { GameType, SavedGameType } from '$lib/types';
|
||||||
import { boredState } from '$lib/stores/boredState';
|
// import { boredState } from '$lib/stores/boredState';
|
||||||
import Pagination from '$lib/components/pagination/index.svelte';
|
// import Pagination from '$lib/components/pagination/index.svelte';
|
||||||
import RemoveCollectionDialog from '$lib/components/dialog/RemoveCollectionDialog.svelte';
|
// import RemoveCollectionDialog from '$lib/components/dialog/RemoveCollectionDialog.svelte';
|
||||||
import RemoveWishlistDialog from '$lib/components/dialog/RemoveWishlistDialog.svelte';
|
// import RemoveWishlistDialog from '$lib/components/dialog/RemoveWishlistDialog.svelte';
|
||||||
import { createSearchStore, searchHandler } from '$lib/stores/search';
|
// import { createSearchStore, searchHandler } from '$lib/stores/search';
|
||||||
import type { PageData } from './$types';
|
|
||||||
|
|
||||||
export let data: PageData;
|
export let data;
|
||||||
console.log(`Page data: ${JSON.stringify(data)}`)
|
console.log(`Page data: ${JSON.stringify(data)}`);
|
||||||
|
// let collectionItems = data?.collection?.collectionItems;
|
||||||
|
|
||||||
let gameToRemove: GameType | SavedGameType;
|
// let gameToRemove: GameType | SavedGameType;
|
||||||
let pageSize = 10;
|
// let pageSize = 10;
|
||||||
let page = 1;
|
// let page = 1;
|
||||||
|
|
||||||
const searchStore = createSearchStore($collectionStore);
|
// const searchStore = createSearchStore($collectionStore);
|
||||||
console.log('searchStore', $searchStore);
|
// console.log('searchStore', $searchStore);
|
||||||
|
|
||||||
const unsubscribe = searchStore.subscribe((model) => searchHandler(model));
|
// const unsubscribe = searchStore.subscribe((model) => searchHandler(model));
|
||||||
|
|
||||||
onDestroy(() => {
|
// onDestroy(() => {
|
||||||
unsubscribe();
|
// unsubscribe();
|
||||||
});
|
// });
|
||||||
|
|
||||||
$: skip = (page - 1) * pageSize;
|
// $: skip = (page - 1) * pageSize;
|
||||||
$: gamesShown = $searchStore.data.slice(skip, skip + pageSize);
|
// $: gamesShown = $searchStore.data.slice(skip, skip + pageSize);
|
||||||
$: totalItems = $searchStore.search === '' ? $collectionStore.length : $searchStore.filtered.length;
|
// $: totalItems = $searchStore.search === '' ? $collectionStore.length : $searchStore.filtered.length;
|
||||||
|
|
||||||
interface RemoveGameEvent extends Event {
|
// interface RemoveGameEvent extends Event {
|
||||||
detail: GameType | SavedGameType;
|
// detail: GameType | SavedGameType;
|
||||||
}
|
// }
|
||||||
|
|
||||||
function handleRemoveCollection(event: RemoveGameEvent) {
|
// function handleRemoveCollection(event: RemoveGameEvent) {
|
||||||
console.log('Remove collection event handler');
|
// console.log('Remove collection event handler');
|
||||||
console.log('event', event);
|
// console.log('event', event);
|
||||||
gameToRemove = event?.detail;
|
// gameToRemove = event?.detail;
|
||||||
boredState.update((n) => ({
|
// boredState.update((n) => ({
|
||||||
...n,
|
// ...n,
|
||||||
dialog: { isOpen: true, content: RemoveCollectionDialog, additionalData: gameToRemove }
|
// dialog: { isOpen: true, content: RemoveCollectionDialog, additionalData: gameToRemove }
|
||||||
}));
|
// }));
|
||||||
}
|
// }
|
||||||
|
|
||||||
function handleRemoveWishlist(event: RemoveGameEvent) {
|
// function handleRemoveWishlist(event: RemoveGameEvent) {
|
||||||
console.log('Remove wishlist event handler');
|
// console.log('Remove wishlist event handler');
|
||||||
console.log('event', event);
|
// console.log('event', event);
|
||||||
gameToRemove = event?.detail;
|
// gameToRemove = event?.detail;
|
||||||
boredState.update((n) => ({
|
// boredState.update((n) => ({
|
||||||
...n,
|
// ...n,
|
||||||
dialog: { isOpen: true, content: RemoveWishlistDialog, additionalData: gameToRemove }
|
// dialog: { isOpen: true, content: RemoveWishlistDialog, additionalData: gameToRemove }
|
||||||
}));
|
// }));
|
||||||
}
|
// }
|
||||||
|
|
||||||
async function handleNextPageEvent(event: CustomEvent) {
|
// async function handleNextPageEvent(event: CustomEvent) {
|
||||||
if (+event?.detail?.page === page + 1) {
|
// if (+event?.detail?.page === page + 1) {
|
||||||
page += 1;
|
// page += 1;
|
||||||
}
|
// }
|
||||||
await tick();
|
// await tick();
|
||||||
}
|
// }
|
||||||
|
|
||||||
async function handlePreviousPageEvent(event: CustomEvent) {
|
// async function handlePreviousPageEvent(event: CustomEvent) {
|
||||||
if (+event?.detail?.page === page - 1) {
|
// if (+event?.detail?.page === page - 1) {
|
||||||
page -= 1;
|
// page -= 1;
|
||||||
}
|
// }
|
||||||
await tick();
|
// await tick();
|
||||||
}
|
// }
|
||||||
|
|
||||||
async function handlePerPageEvent(event: CustomEvent) {
|
// async function handlePerPageEvent(event: CustomEvent) {
|
||||||
page = 1;
|
// page = 1;
|
||||||
pageSize = event.detail.pageSize;
|
// pageSize = event.detail.pageSize;
|
||||||
await tick();
|
// await tick();
|
||||||
}
|
// }
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
|
|
@ -80,9 +80,9 @@
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<h1>Your Collection</h1>
|
<h1>Your Collection</h1>
|
||||||
<input type="text" id="search" name="search" placeholder="Search Your Collection" bind:value={$searchStore.search} />
|
<!-- <input type="text" id="search" name="search" placeholder="Search Your Collection" bind:value={$searchStore.search} /> -->
|
||||||
|
|
||||||
<div class="games">
|
<!-- <div class="games">
|
||||||
<div class="games-list">
|
<div class="games-list">
|
||||||
{#if $collectionStore.length === 0}
|
{#if $collectionStore.length === 0}
|
||||||
<h2>No games in your collection</h2>
|
<h2>No games in your collection</h2>
|
||||||
|
|
@ -109,9 +109,9 @@
|
||||||
on:perPageEvent={handlePerPageEvent}
|
on:perPageEvent={handlePerPageEvent}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="postcss">
|
||||||
h1 {
|
h1 {
|
||||||
margin: 1.5rem 0rem;
|
margin: 1.5rem 0rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
||||||
|
|
@ -205,7 +205,7 @@
|
||||||
margin: 1rem;
|
margin: 1rem;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
|
|
||||||
@media (max-width: env(--medium-viewport)) {
|
@media (max-width: 700px) {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
}
|
}
|
||||||
|
|
@ -220,7 +220,7 @@
|
||||||
margin: 1rem;
|
margin: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: env(--xsmall-viewport)) {
|
@media (max-width: 500px) {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
95
src/routes/wishlist/+page.server.ts
Normal file
95
src/routes/wishlist/+page.server.ts
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
import { 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 wishlists = await prisma.wishlist.findMany({
|
||||||
|
where: {
|
||||||
|
user_id: session.userId
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
items: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (wishlists.length === 0) {
|
||||||
|
const wishlist = await prisma.wishlist.create({
|
||||||
|
data: {
|
||||||
|
user_id: session.userId,
|
||||||
|
name: 'My Wishlist'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
wishlists.push(wishlist);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
wishlists
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 session = await locals.auth.validate();
|
||||||
|
if (!session) {
|
||||||
|
throw redirect(302, '/auth/signin');
|
||||||
|
}
|
||||||
|
|
||||||
|
const game = await prisma.game.findUnique({
|
||||||
|
where: {
|
||||||
|
id: form.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// if (!game) {
|
||||||
|
// throw redirect(302, '/404');
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (game) {
|
||||||
|
const wishlist = await prisma.wishlist.create({
|
||||||
|
data: {
|
||||||
|
user_id: session.userId,
|
||||||
|
name: form.name
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,70 +1,72 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { tick, onDestroy } from 'svelte';
|
// import { tick, onDestroy } from 'svelte';
|
||||||
import Game from '$lib/components/game/index.svelte';
|
// import Game from '$lib/components/game/index.svelte';
|
||||||
import { wishlistStore } from '$lib/stores/wishlistStore';
|
// import { wishlistStore } from '$lib/stores/wishlistStore';
|
||||||
import type { GameType, SavedGameType } from '$lib/types';
|
// import type { GameType, SavedGameType } from '$lib/types';
|
||||||
import { boredState } from '$lib/stores/boredState';
|
// import { boredState } from '$lib/stores/boredState';
|
||||||
import Pagination from '$lib/components/pagination/index.svelte';
|
// import Pagination from '$lib/components/pagination/index.svelte';
|
||||||
import RemoveWishlistDialog from '$lib/components/dialog/RemoveWishlistDialog.svelte';
|
// import RemoveWishlistDialog from '$lib/components/dialog/RemoveWishlistDialog.svelte';
|
||||||
import RemoveCollectionDialog from '$lib/components/dialog/RemoveCollectionDialog.svelte';
|
// import RemoveCollectionDialog from '$lib/components/dialog/RemoveCollectionDialog.svelte';
|
||||||
import { createSearchStore, searchHandler } from '$lib/stores/search';
|
// import { createSearchStore, searchHandler } from '$lib/stores/search';
|
||||||
|
|
||||||
let gameToRemove: GameType | SavedGameType;
|
// let gameToRemove: GameType | SavedGameType;
|
||||||
let pageSize = 10;
|
// let pageSize = 10;
|
||||||
let page = 1;
|
// let page = 1;
|
||||||
|
|
||||||
const searchStore = createSearchStore($wishlistStore);
|
// const searchStore = createSearchStore($wishlistStore);
|
||||||
console.log('searchStore', $searchStore);
|
// console.log('searchStore', $searchStore);
|
||||||
|
|
||||||
const unsubscribe = searchStore.subscribe((model) => searchHandler(model));
|
// const unsubscribe = searchStore.subscribe((model) => searchHandler(model));
|
||||||
|
|
||||||
onDestroy(() => {
|
// onDestroy(() => {
|
||||||
unsubscribe();
|
// unsubscribe();
|
||||||
});
|
// });
|
||||||
|
|
||||||
$: skip = (page - 1) * pageSize;
|
// $: skip = (page - 1) * pageSize;
|
||||||
$: gamesShown = $searchStore.filtered.slice(skip, skip + pageSize);
|
// $: gamesShown = $searchStore.filtered.slice(skip, skip + pageSize);
|
||||||
$: totalItems = $searchStore.search === '' ? $wishlistStore.length : $searchStore.filtered.length;
|
// $: totalItems = $searchStore.search === '' ? $wishlistStore.length : $searchStore.filtered.length;
|
||||||
|
|
||||||
interface RemoveGameEvent extends Event {
|
// interface RemoveGameEvent extends Event {
|
||||||
detail: GameType | SavedGameType;
|
// detail: GameType | SavedGameType;
|
||||||
}
|
// }
|
||||||
|
|
||||||
function handleRemoveCollection(event: RemoveGameEvent) {
|
// function handleRemoveCollection(event: RemoveGameEvent) {
|
||||||
gameToRemove = event?.detail;
|
// gameToRemove = event?.detail;
|
||||||
boredState.update((n) => ({
|
// boredState.update((n) => ({
|
||||||
...n,
|
// ...n,
|
||||||
dialog: { isOpen: true, content: RemoveCollectionDialog, additionalData: gameToRemove }
|
// dialog: { isOpen: true, content: RemoveCollectionDialog, additionalData: gameToRemove }
|
||||||
}));
|
// }));
|
||||||
}
|
// }
|
||||||
|
|
||||||
function handleRemoveWishlist(event: RemoveGameEvent) {
|
// function handleRemoveWishlist(event: RemoveGameEvent) {
|
||||||
gameToRemove = event?.detail;
|
// gameToRemove = event?.detail;
|
||||||
boredState.update((n) => ({
|
// boredState.update((n) => ({
|
||||||
...n,
|
// ...n,
|
||||||
dialog: { isOpen: true, content: RemoveWishlistDialog, additionalData: gameToRemove }
|
// dialog: { isOpen: true, content: RemoveWishlistDialog, additionalData: gameToRemove }
|
||||||
}));
|
// }));
|
||||||
}
|
// }
|
||||||
|
|
||||||
async function handleNextPageEvent(event: CustomEvent) {
|
// async function handleNextPageEvent(event: CustomEvent) {
|
||||||
if (+event?.detail?.page === page + 1) {
|
// if (+event?.detail?.page === page + 1) {
|
||||||
page += 1;
|
// page += 1;
|
||||||
}
|
// }
|
||||||
await tick();
|
// await tick();
|
||||||
}
|
// }
|
||||||
|
|
||||||
async function handlePreviousPageEvent(event: CustomEvent) {
|
// async function handlePreviousPageEvent(event: CustomEvent) {
|
||||||
if (+event?.detail?.page === page - 1) {
|
// if (+event?.detail?.page === page - 1) {
|
||||||
page -= 1;
|
// page -= 1;
|
||||||
}
|
// }
|
||||||
await tick();
|
// await tick();
|
||||||
}
|
// }
|
||||||
|
|
||||||
async function handlePerPageEvent(event: CustomEvent) {
|
// async function handlePerPageEvent(event: CustomEvent) {
|
||||||
page = 1;
|
// page = 1;
|
||||||
pageSize = event.detail.pageSize;
|
// pageSize = event.detail.pageSize;
|
||||||
await tick();
|
// await tick();
|
||||||
}
|
// }
|
||||||
|
export let data;
|
||||||
|
const wishlists = data.wishlists || [];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
|
|
@ -72,7 +74,10 @@
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<h1>Your Wishlist</h1>
|
<h1>Your Wishlist</h1>
|
||||||
<input type="text" id="search" name="search" placeholder="Search Your Wishlist" bind:value={$searchStore.search} />
|
{#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">
|
||||||
<div class="games-list">
|
<div class="games-list">
|
||||||
|
|
@ -101,7 +106,7 @@
|
||||||
on:perPageEvent={handlePerPageEvent}
|
on:perPageEvent={handlePerPageEvent}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
h1 {
|
h1 {
|
||||||
|
|
|
||||||
|
|
@ -6,22 +6,18 @@ import { vitePreprocess } from '@sveltejs/kit/vite';
|
||||||
const config = {
|
const config = {
|
||||||
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
|
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
|
||||||
// for more information about preprocessors
|
// for more information about preprocessors
|
||||||
preprocess: [
|
preprocess: [vitePreprocess()],
|
||||||
vitePreprocess({
|
|
||||||
postcss: true
|
|
||||||
}),
|
|
||||||
preprocess({
|
|
||||||
postcss: true
|
|
||||||
})
|
|
||||||
],
|
|
||||||
vitePlugin: {
|
vitePlugin: {
|
||||||
inspector: true,
|
inspector: true,
|
||||||
},
|
},
|
||||||
kit: {
|
kit: {
|
||||||
adapter: adapter(),
|
adapter: adapter(),
|
||||||
alias: {
|
alias: {
|
||||||
|
$db: './src/db',
|
||||||
|
$assets: './src/assets',
|
||||||
$lib: './src/lib',
|
$lib: './src/lib',
|
||||||
$styles: './src/styles',
|
$styles: './src/styles',
|
||||||
|
$themes: './src/themes'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue