mirror of
https://github.com/BradNut/boredgame
synced 2025-09-08 17:40:22 +00:00
refactor: 🎨 Updating and trying out barbon components
This commit is contained in:
parent
8c0778f1fa
commit
d2da1297f3
19 changed files with 1422 additions and 251 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"useTabs": true,
|
"useTabs": false,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"trailingComma": "none",
|
"trailingComma": "none",
|
||||||
"printWidth": 100
|
"printWidth": 100
|
||||||
}
|
}
|
||||||
|
|
@ -12,12 +12,15 @@
|
||||||
"format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. ."
|
"format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.21.0",
|
||||||
"@sveltejs/adapter-auto": "next",
|
"@sveltejs/adapter-auto": "next",
|
||||||
"@sveltejs/kit": "next",
|
"@sveltejs/kit": "next",
|
||||||
"@types/cookie": "^0.5.0",
|
"@types/cookie": "^0.5.0",
|
||||||
"@types/node": "^17.0.24",
|
"@types/node": "^17.0.25",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.19.0",
|
"@typescript-eslint/eslint-plugin": "^5.20.0",
|
||||||
"@typescript-eslint/parser": "^5.19.0",
|
"@typescript-eslint/parser": "^5.20.0",
|
||||||
|
"carbon-components-svelte": "^0.63.0",
|
||||||
|
"carbon-icons-svelte": "^11.0.1",
|
||||||
"eslint": "^8.13.0",
|
"eslint": "^8.13.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"eslint-plugin-svelte3": "^3.4.1",
|
"eslint-plugin-svelte3": "^3.4.1",
|
||||||
|
|
|
||||||
10
playwright.config.ts
Normal file
10
playwright.config.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import type { PlaywrightTestConfig } from '@playwright/test';
|
||||||
|
|
||||||
|
const config: PlaywrightTestConfig = {
|
||||||
|
webServer: {
|
||||||
|
command: 'npm run build && npm run preview',
|
||||||
|
port: 3000
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
1259
pnpm-lock.yaml
1259
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
20
src/app.css
20
src/app.css
|
|
@ -20,12 +20,10 @@ body {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background-color: var(--primary-color);
|
background-color: var(--primary-color);
|
||||||
background: linear-gradient(
|
background: linear-gradient(180deg,
|
||||||
180deg,
|
var(--primary-color) 0%,
|
||||||
var(--primary-color) 0%,
|
var(--secondary-color) 10.45%,
|
||||||
var(--secondary-color) 10.45%,
|
var(--tertiary-color) 41.35%);
|
||||||
var(--tertiary-color) 41.35%
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body::before {
|
body::before {
|
||||||
|
|
@ -36,11 +34,9 @@ body::before {
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 10vw;
|
left: 10vw;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
background: radial-gradient(
|
background: radial-gradient(50% 50% at 50% 50%,
|
||||||
50% 50% at 50% 50%,
|
var(--pure-white) 0%,
|
||||||
var(--pure-white) 0%,
|
rgba(255, 255, 255, 0) 100%);
|
||||||
rgba(255, 255, 255, 0) 100%
|
|
||||||
);
|
|
||||||
opacity: 0.05;
|
opacity: 0.05;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,4 +100,4 @@ button:focus:not(:focus-visible) {
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 2.4rem;
|
font-size: 2.4rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
15
src/app.d.ts
vendored
Normal file
15
src/app.d.ts
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
/// <reference types="@sveltejs/kit" />
|
||||||
|
|
||||||
|
// See https://kit.svelte.dev/docs/types#app
|
||||||
|
// for information about these interfaces
|
||||||
|
declare namespace App {
|
||||||
|
interface Locals {
|
||||||
|
userid: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// interface Platform {}
|
||||||
|
|
||||||
|
// interface Session {}
|
||||||
|
|
||||||
|
// interface Stuff {}
|
||||||
|
}
|
||||||
1
src/global.d.ts
vendored
1
src/global.d.ts
vendored
|
|
@ -1 +0,0 @@
|
||||||
/// <reference types="@sveltejs/kit" />
|
|
||||||
|
|
@ -50,6 +50,7 @@
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border: 0;
|
border: 0;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
touch-action: manipulation;
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { invalidate } from '$app/navigation';
|
||||||
|
|
||||||
// this action (https://svelte.dev/tutorial/actions) allows us to
|
// this action (https://svelte.dev/tutorial/actions) allows us to
|
||||||
// progressively enhance a <form> that already works without JS
|
// progressively enhance a <form> that already works without JS
|
||||||
export function enhance(
|
export function enhance(
|
||||||
|
|
@ -7,43 +9,65 @@ export function enhance(
|
||||||
error,
|
error,
|
||||||
result
|
result
|
||||||
}: {
|
}: {
|
||||||
pending?: (data: FormData, form: HTMLFormElement) => void;
|
pending?: ({ data, form }: { data: FormData; form: HTMLFormElement }) => void;
|
||||||
error?: (res: Response | null, error: Error | null, form: HTMLFormElement) => void;
|
error?: ({
|
||||||
result: (res: Response, form: HTMLFormElement) => void;
|
data,
|
||||||
}
|
form,
|
||||||
): { destroy: () => void } {
|
response,
|
||||||
|
error
|
||||||
|
}: {
|
||||||
|
data: FormData;
|
||||||
|
form: HTMLFormElement;
|
||||||
|
response: Response | null;
|
||||||
|
error: Error | null;
|
||||||
|
}) => void;
|
||||||
|
result?: ({
|
||||||
|
data,
|
||||||
|
form,
|
||||||
|
response
|
||||||
|
}: {
|
||||||
|
data: FormData;
|
||||||
|
response: Response;
|
||||||
|
form: HTMLFormElement;
|
||||||
|
}) => void;
|
||||||
|
} = {}
|
||||||
|
) {
|
||||||
let current_token: unknown;
|
let current_token: unknown;
|
||||||
|
|
||||||
async function handle_submit(e: Event) {
|
async function handle_submit(e: SubmitEvent) {
|
||||||
const token = (current_token = {});
|
const token = (current_token = {});
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const body = new FormData(form);
|
const data = new FormData(form);
|
||||||
|
|
||||||
if (pending) pending(body, form);
|
if (pending) pending({ data, form });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(form.action, {
|
const response = await fetch(form.action, {
|
||||||
method: form.method,
|
method: form.method,
|
||||||
headers: {
|
headers: {
|
||||||
accept: 'application/json'
|
accept: 'application/json'
|
||||||
},
|
},
|
||||||
body
|
body: data
|
||||||
});
|
});
|
||||||
|
|
||||||
if (token !== current_token) return;
|
if (token !== current_token) return;
|
||||||
|
|
||||||
if (res.ok) {
|
if (response.ok) {
|
||||||
result(res, form);
|
if (result) result({ data, form, response });
|
||||||
|
|
||||||
|
const url = new URL(form.action);
|
||||||
|
url.search = url.hash = '';
|
||||||
|
invalidate(url.href);
|
||||||
} else if (error) {
|
} else if (error) {
|
||||||
error(res, null, form);
|
error({ data, form, error: null, response });
|
||||||
} else {
|
} else {
|
||||||
console.error(await res.text());
|
console.error(await response.text());
|
||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e: unknown) {
|
||||||
if (error) {
|
if (error && e instanceof Error) {
|
||||||
error(null, e, form);
|
error({ data, form, error: e, response: null });
|
||||||
} else {
|
} else {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,14 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import {
|
||||||
|
Theme,
|
||||||
|
RadioButtonGroup,
|
||||||
|
RadioButton,
|
||||||
|
} from "carbon-components-svelte";
|
||||||
|
import type { CarbonTheme } from "carbon-components-svelte/types/Theme/Theme.svelte";
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import logo from './svelte-logo.svg';
|
import logo from './svelte-logo.svg';
|
||||||
|
|
||||||
|
let theme: CarbonTheme = "white";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
|
|
@ -11,9 +19,6 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<svg viewBox="0 0 2 3" aria-hidden="true">
|
|
||||||
<path d="M0,0 L1,2 C1.5,3 1.5,3 2,3 L2,0 Z" />
|
|
||||||
</svg>
|
|
||||||
<ul>
|
<ul>
|
||||||
<li class:active={$page.url.pathname === '/'}><a sveltekit:prefetch href="/">Home</a></li>
|
<li class:active={$page.url.pathname === '/'}><a sveltekit:prefetch href="/">Home</a></li>
|
||||||
<li class:active={$page.url.pathname === '/about'}>
|
<li class:active={$page.url.pathname === '/about'}>
|
||||||
|
|
@ -23,14 +28,18 @@
|
||||||
<a sveltekit:prefetch href="/todos">Todos</a>
|
<a sveltekit:prefetch href="/todos">Todos</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<svg viewBox="0 0 2 3" aria-hidden="true">
|
<Theme
|
||||||
<path d="M0,0 L0,3 C0.5,3 0.5,3 1,2 L2,0 Z" />
|
render="toggle"
|
||||||
</svg>
|
toggle={{
|
||||||
|
themes: ['white','g100'],
|
||||||
|
hideLabel: true,
|
||||||
|
size: 'sm'
|
||||||
|
}}
|
||||||
|
bind:theme
|
||||||
|
persist
|
||||||
|
persistKey="__carbon-theme"
|
||||||
|
/>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="corner">
|
|
||||||
<!-- TODO put something else here? github link? -->
|
|
||||||
</div>
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
||||||
7
src/lib/types.d.ts
vendored
7
src/lib/types.d.ts
vendored
|
|
@ -1,7 +0,0 @@
|
||||||
/**
|
|
||||||
* Can be made globally available by placing this
|
|
||||||
* inside `global.d.ts` and removing `export` keyword
|
|
||||||
*/
|
|
||||||
export interface Locals {
|
|
||||||
userid: string;
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Header from '$lib/header/Header.svelte';
|
import Header from '$lib/header/Header.svelte';
|
||||||
|
import "carbon-components-svelte/css/all.css";
|
||||||
import '../app.css';
|
import '../app.css';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
import { api } from './_api';
|
|
||||||
import type { RequestHandler } from '@sveltejs/kit';
|
|
||||||
import type { Locals } from '$lib/types';
|
|
||||||
|
|
||||||
// PATCH /todos/:uid.json
|
|
||||||
export const patch: RequestHandler<Locals> = async (event) => {
|
|
||||||
const data = await event.request.formData();
|
|
||||||
|
|
||||||
return api(event, `todos/${event.locals.userid}/${event.params.uid}`, {
|
|
||||||
text: data.get('text'),
|
|
||||||
done: data.has('done') ? !!data.get('done') : undefined
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// DELETE /todos/:uid.json
|
|
||||||
export const del: RequestHandler<Locals> = async (event) => {
|
|
||||||
return api(event, `todos/${event.locals.userid}/${event.params.uid}`);
|
|
||||||
};
|
|
||||||
|
|
@ -1,56 +1,22 @@
|
||||||
import type { EndpointOutput, RequestEvent } from '@sveltejs/kit';
|
|
||||||
import type { Locals } from '$lib/types';
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This module is used by the /todos.json and /todos/[uid].json
|
This module is used by the /todos endpoint to
|
||||||
endpoints to make calls to api.svelte.dev, which stores todos
|
make calls to api.svelte.dev, which stores todos
|
||||||
for each user. The leading underscore indicates that this is
|
for each user. The leading underscore indicates that this is
|
||||||
a private module, _not_ an endpoint — visiting /todos/_api
|
a private module, _not_ an endpoint — visiting /todos/_api
|
||||||
will net you a 404 response.
|
will net you a 404 response.
|
||||||
|
|
||||||
(The data on the todo app will expire periodically; no
|
(The data on the todo app will expire periodically; no
|
||||||
guarantees are made. Don't use it to organise your life.)
|
guarantees are made. Don't use it to organize your life.)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const base = 'https://api.svelte.dev';
|
const base = 'https://api.svelte.dev';
|
||||||
|
|
||||||
export async function api(
|
export function api(method: string, resource: string, data?: Record<string, unknown>) {
|
||||||
event: RequestEvent<Locals>,
|
return fetch(`${base}/${resource}`, {
|
||||||
resource: string,
|
method,
|
||||||
data?: Record<string, unknown>
|
|
||||||
): Promise<EndpointOutput> {
|
|
||||||
// user must have a cookie set
|
|
||||||
if (!event.locals.userid) {
|
|
||||||
return { status: 401 };
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await fetch(`${base}/${resource}`, {
|
|
||||||
method: event.request.method,
|
|
||||||
headers: {
|
headers: {
|
||||||
'content-type': 'application/json'
|
'content-type': 'application/json'
|
||||||
},
|
},
|
||||||
body: data && JSON.stringify(data)
|
body: data && JSON.stringify(data)
|
||||||
});
|
});
|
||||||
|
|
||||||
// if the request came from a <form> submission, the browser's default
|
|
||||||
// behaviour is to show the URL corresponding to the form's "action"
|
|
||||||
// attribute. in those cases, we want to redirect them back to the
|
|
||||||
// /todos page, rather than showing the response
|
|
||||||
if (
|
|
||||||
res.ok &&
|
|
||||||
event.request.method !== 'GET' &&
|
|
||||||
event.request.headers.get('accept') !== 'application/json'
|
|
||||||
) {
|
|
||||||
return {
|
|
||||||
status: 303,
|
|
||||||
headers: {
|
|
||||||
location: '/todos'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
status: res.status,
|
|
||||||
body: await res.json()
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
import { api } from './_api';
|
|
||||||
import type { RequestHandler } from '@sveltejs/kit';
|
|
||||||
import type { Locals } from '$lib/types';
|
|
||||||
|
|
||||||
// GET /todos.json
|
|
||||||
export const get: RequestHandler<Locals> = async (event) => {
|
|
||||||
// event.locals.userid comes from src/hooks.js
|
|
||||||
const response = await api(event, `todos/${event.locals.userid}`);
|
|
||||||
|
|
||||||
if (response.status === 404) {
|
|
||||||
// user hasn't created a todo list.
|
|
||||||
// start with an empty array
|
|
||||||
return { body: [] };
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
};
|
|
||||||
|
|
||||||
// POST /todos.json
|
|
||||||
export const post: RequestHandler<Locals> = async (event) => {
|
|
||||||
const data = await event.request.formData();
|
|
||||||
|
|
||||||
const response = await api(event, `todos/${event.locals.userid}`, {
|
|
||||||
// because index.svelte posts a FormData object,
|
|
||||||
// request.body is _also_ a (readonly) FormData
|
|
||||||
// object, which allows us to get form data
|
|
||||||
// with the `body.get(key)` method
|
|
||||||
text: data.get('text')
|
|
||||||
});
|
|
||||||
|
|
||||||
return response;
|
|
||||||
};
|
|
||||||
|
|
@ -1,28 +1,5 @@
|
||||||
<script context="module" lang="ts">
|
|
||||||
import { enhance } from '$lib/form';
|
|
||||||
import type { Load } from '@sveltejs/kit';
|
|
||||||
|
|
||||||
// see https://kit.svelte.dev/docs#loading
|
|
||||||
export const load: Load = async ({ fetch }) => {
|
|
||||||
const res = await fetch('/todos.json');
|
|
||||||
|
|
||||||
if (res.ok) {
|
|
||||||
const todos = await res.json();
|
|
||||||
|
|
||||||
return {
|
|
||||||
props: { todos }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const { message } = await res.json();
|
|
||||||
|
|
||||||
return {
|
|
||||||
error: new Error(message)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { enhance } from '$lib/form';
|
||||||
import { scale } from 'svelte/transition';
|
import { scale } from 'svelte/transition';
|
||||||
import { flip } from 'svelte/animate';
|
import { flip } from 'svelte/animate';
|
||||||
|
|
||||||
|
|
@ -35,15 +12,6 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
export let todos: Todo[];
|
export let todos: Todo[];
|
||||||
|
|
||||||
async function patch(res: Response) {
|
|
||||||
const todo = await res.json();
|
|
||||||
|
|
||||||
todos = todos.map((t) => {
|
|
||||||
if (t.uid === todo.uid) return todo;
|
|
||||||
return t;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
|
|
@ -55,13 +23,10 @@
|
||||||
|
|
||||||
<form
|
<form
|
||||||
class="new"
|
class="new"
|
||||||
action="/todos.json"
|
action="/todos"
|
||||||
method="post"
|
method="post"
|
||||||
use:enhance={{
|
use:enhance={{
|
||||||
result: async (res, form) => {
|
result: async ({ form }) => {
|
||||||
const created = await res.json();
|
|
||||||
todos = [...todos, created];
|
|
||||||
|
|
||||||
form.reset();
|
form.reset();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|
@ -77,41 +42,33 @@
|
||||||
animate:flip={{ duration: 200 }}
|
animate:flip={{ duration: 200 }}
|
||||||
>
|
>
|
||||||
<form
|
<form
|
||||||
action="/todos/{todo.uid}.json?_method=PATCH"
|
action="/todos?_method=PATCH"
|
||||||
method="post"
|
method="post"
|
||||||
use:enhance={{
|
use:enhance={{
|
||||||
pending: (data) => {
|
pending: ({ data }) => {
|
||||||
todo.done = !!data.get('done');
|
todo.done = !!data.get('done');
|
||||||
},
|
}
|
||||||
result: patch
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<input type="hidden" name="uid" value={todo.uid} />
|
||||||
<input type="hidden" name="done" value={todo.done ? '' : 'true'} />
|
<input type="hidden" name="done" value={todo.done ? '' : 'true'} />
|
||||||
<button class="toggle" aria-label="Mark todo as {todo.done ? 'not done' : 'done'}" />
|
<button class="toggle" aria-label="Mark todo as {todo.done ? 'not done' : 'done'}" />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form
|
<form class="text" action="/todos?_method=PATCH" method="post" use:enhance>
|
||||||
class="text"
|
<input type="hidden" name="uid" value={todo.uid} />
|
||||||
action="/todos/{todo.uid}.json?_method=PATCH"
|
|
||||||
method="post"
|
|
||||||
use:enhance={{
|
|
||||||
result: patch
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<input aria-label="Edit todo" type="text" name="text" value={todo.text} />
|
<input aria-label="Edit todo" type="text" name="text" value={todo.text} />
|
||||||
<button class="save" aria-label="Save todo" />
|
<button class="save" aria-label="Save todo" />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form
|
<form
|
||||||
action="/todos/{todo.uid}.json?_method=DELETE"
|
action="/todos?_method=DELETE"
|
||||||
method="post"
|
method="post"
|
||||||
use:enhance={{
|
use:enhance={{
|
||||||
pending: () => (todo.pending_delete = true),
|
pending: () => (todo.pending_delete = true)
|
||||||
result: () => {
|
|
||||||
todos = todos.filter((t) => t.uid !== todo.uid);
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<input type="hidden" name="uid" value={todo.uid} />
|
||||||
<button class="delete" aria-label="Delete todo" disabled={todo.pending_delete} />
|
<button class="delete" aria-label="Delete todo" disabled={todo.pending_delete} />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -167,7 +124,7 @@
|
||||||
.done {
|
.done {
|
||||||
transform: none;
|
transform: none;
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
filter: drop-shadow(0px 0px 1px rgba(0, 0, 0, 0.1));
|
filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.1));
|
||||||
}
|
}
|
||||||
|
|
||||||
form.text {
|
form.text {
|
||||||
|
|
|
||||||
67
src/routes/todos/index.ts
Normal file
67
src/routes/todos/index.ts
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
import { api } from './_api';
|
||||||
|
import type { RequestHandler } from './index';
|
||||||
|
|
||||||
|
export const get: RequestHandler = async ({ locals }) => {
|
||||||
|
// locals.userid comes from src/hooks.js
|
||||||
|
const response = await api('get', `todos/${locals.userid}`);
|
||||||
|
|
||||||
|
if (response.status === 404) {
|
||||||
|
// user hasn't created a todo list.
|
||||||
|
// start with an empty array
|
||||||
|
return {
|
||||||
|
body: {
|
||||||
|
todos: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.status === 200) {
|
||||||
|
return {
|
||||||
|
body: {
|
||||||
|
todos: await response.json()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: response.status
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const post: RequestHandler = async ({ request, locals }) => {
|
||||||
|
const form = await request.formData();
|
||||||
|
|
||||||
|
await api('post', `todos/${locals.userid}`, {
|
||||||
|
text: form.get('text')
|
||||||
|
});
|
||||||
|
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
|
||||||
|
// If the user has JavaScript disabled, the URL will change to
|
||||||
|
// include the method override unless we redirect back to /todos
|
||||||
|
const redirect = {
|
||||||
|
status: 303,
|
||||||
|
headers: {
|
||||||
|
location: '/todos'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const patch: RequestHandler = async ({ request, locals }) => {
|
||||||
|
const form = await request.formData();
|
||||||
|
|
||||||
|
await api('patch', `todos/${locals.userid}/${form.get('uid')}`, {
|
||||||
|
text: form.has('text') ? form.get('text') : undefined,
|
||||||
|
done: form.has('done') ? !!form.get('done') : undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
return redirect;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const del: RequestHandler = async ({ request, locals }) => {
|
||||||
|
const form = await request.formData();
|
||||||
|
|
||||||
|
await api('delete', `todos/${locals.userid}/${form.get('uid')}`);
|
||||||
|
|
||||||
|
return redirect;
|
||||||
|
};
|
||||||
6
tests/test.ts
Normal file
6
tests/test.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { expect, test } from '@playwright/test';
|
||||||
|
|
||||||
|
test('about page has expected h1', async ({ page }) => {
|
||||||
|
await page.goto('/about');
|
||||||
|
expect(await page.textContent('h1')).toBe('About this app');
|
||||||
|
});
|
||||||
|
|
@ -1,11 +1,20 @@
|
||||||
{
|
{
|
||||||
"extends": "./.svelte-kit/tsconfig.json",
|
"extends": "./.svelte-kit/tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": ".",
|
"allowJs": true,
|
||||||
"paths": {
|
"checkJs": true,
|
||||||
"$root/*": [
|
"esModuleInterop": true,
|
||||||
"./src/*"
|
"forceConsistentCasingInFileNames": true,
|
||||||
]
|
"lib": [
|
||||||
}
|
"es2020",
|
||||||
|
"DOM"
|
||||||
|
],
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"module": "es2020",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"strict": true,
|
||||||
|
"target": "es2020",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in a new issue