app: lists on meta tables, schema-qualified SQL, typed columns
- lists read/write real lists.* tables via meta.tables/meta.columns and the SECURITY DEFINER meta functions; typed extra columns in the item forms - old numeric list URLs 308-redirect to the slug - include_recurring is boolean, osu accuracy/pp/rate numeric - raw SQL qualified with the new schemas
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { NativeSelect } from '#lib/components/ui/native-select/index.js';
|
||||
import { Input } from '#lib/components/ui/input/index.js';
|
||||
import { Textarea } from '#lib/components/ui/textarea/index.js';
|
||||
import { Checkbox } from '#lib/components/ui/checkbox/index.js';
|
||||
@@ -14,6 +13,7 @@
|
||||
import { enhance, type SubmitFunction } from '$app/forms';
|
||||
import ListPictureDropzone from '#lib/components/ListPictureDropzone.svelte';
|
||||
import ProgressBar from '#lib/components/ProgressBar.svelte';
|
||||
import ListExtraFields from '#lib/components/ListExtraFields.svelte';
|
||||
import type { PageProps } from './$types';
|
||||
let { data, form }: PageProps = $props();
|
||||
let completed = $derived(new Set(data.completedIds));
|
||||
@@ -109,23 +109,16 @@
|
||||
{#if form?.message}<p role="alert" class="text-red-700">{form.message}</p>{/if}
|
||||
{#if uploadError}<p role="alert" class="text-red-700">{uploadError}</p>{/if}
|
||||
{#if data.list.isProgress}
|
||||
{#if data.user}
|
||||
<div class="border-y border-[var(--site-border)] py-4">
|
||||
<p class="font-medium">
|
||||
{data.checked} of {data.total} checked · {data.total
|
||||
? Math.round((data.checked / data.total) * 100)
|
||||
: 0}%
|
||||
</p>
|
||||
<div class="mt-2">
|
||||
<ProgressBar value={data.checked} max={data.total} label="List progress" small />
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<p>
|
||||
<a class="underline" href={resolve(`login?returnTo=/lists/${data.list.slug}`)}>Log in</a> to save
|
||||
your progress.
|
||||
<div class="border-y border-[var(--site-border)] py-4">
|
||||
<p class="font-medium">
|
||||
{data.checked} of {data.total} checked · {data.total
|
||||
? Math.round((data.checked / data.total) * 100)
|
||||
: 0}%
|
||||
</p>
|
||||
{/if}
|
||||
<div class="mt-2">
|
||||
<ProgressBar value={data.checked} max={data.total} label="List progress" small />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if data.isAdmin && managing}
|
||||
<Collapsible.Root class="border-b border-[var(--site-border)] pb-5">
|
||||
@@ -159,12 +152,9 @@
|
||||
class="block w-full rounded border p-2"
|
||||
/></label
|
||||
>
|
||||
<label
|
||||
>Type<NativeSelect name="type" class="block w-full rounded border p-2">
|
||||
<option value="normal" selected={!data.list.isProgress}>Normal</option>
|
||||
<option value="progress" selected={data.list.isProgress}>Progress</option>
|
||||
</NativeSelect></label
|
||||
>
|
||||
<p class="self-end text-sm text-[var(--site-muted)]">
|
||||
{data.list.isProgress ? 'Progress' : 'Normal'} list (type is fixed once created)
|
||||
</p>
|
||||
<div class="sm:col-span-2">
|
||||
<ListPictureDropzone label="List picture (optional)" />
|
||||
{#if data.list.pictureUrl}<label class="mt-2 block text-sm"
|
||||
@@ -238,8 +228,21 @@
|
||||
>
|
||||
{item.description}
|
||||
</p>{/if}
|
||||
{#if data.list.columns.length}<p class="mt-1 text-sm text-[var(--site-muted)]">
|
||||
{#each data.list.columns.filter((c) => item.extras[c.name] !== null && item.extras[c.name] !== '') as column, i (column.name)}{#if i}
|
||||
·
|
||||
{/if}{column.label}: {#if column.uiType === 'url'}<a
|
||||
class="underline"
|
||||
href={String(item.extras[column.name])}
|
||||
rel="noopener noreferrer">link</a
|
||||
>{:else if column.uiType === 'bool'}{item.extras[column.name]
|
||||
? 'yes'
|
||||
: 'no'}{:else if column.uiType === 'cents'}{(
|
||||
Number(item.extras[column.name]) / 100
|
||||
).toFixed(2)}{:else}{item.extras[column.name]}{/if}{/each}
|
||||
</p>{/if}
|
||||
</div>
|
||||
{#if data.list.isProgress && data.user}
|
||||
{#if data.list.isProgress && data.isAdmin}
|
||||
<form method="POST" action="?/setCompletion" use:enhance class="shrink-0">
|
||||
<input type="hidden" name="itemId" value={item.id} />
|
||||
<input type="hidden" name="completed" value={completed.has(item.id) ? 'no' : 'yes'} />
|
||||
@@ -251,6 +254,8 @@
|
||||
{#if completed.has(item.id)}<Check size={16} aria-hidden="true" />{/if}
|
||||
</Button>
|
||||
</form>
|
||||
{:else if data.list.isProgress && completed.has(item.id)}
|
||||
<Check size={16} aria-label="Done" class="shrink-0" />
|
||||
{/if}
|
||||
{#if data.isAdmin && managing}<Button
|
||||
type="button"
|
||||
@@ -309,6 +314,7 @@
|
||||
value={editingItem.description ?? ''}
|
||||
/></label
|
||||
>
|
||||
<ListExtraFields columns={data.list.columns} values={editingItem.extras} />
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={uploading}
|
||||
@@ -373,6 +379,7 @@
|
||||
class="block w-full rounded border p-2"
|
||||
/></label
|
||||
>
|
||||
<ListExtraFields columns={data.list.columns} />
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={uploading}
|
||||
|
||||
Reference in New Issue
Block a user