data browser wip
This commit is contained in:
@@ -14,6 +14,24 @@
|
||||
import { Input } from '#lib/components/ui/input/index.js';
|
||||
import type { PageProps } from './$types';
|
||||
let { data, form }: PageProps = $props();
|
||||
let tableFilter = $state('');
|
||||
const schemas = $derived.by(() => {
|
||||
const q = tableFilter.trim().toLowerCase();
|
||||
const groups = new Map<string, typeof data.tables>();
|
||||
for (const t of data.tables) {
|
||||
if (q && !`${t.schema}.${t.name} ${t.displayName}`.toLowerCase().includes(q)) continue;
|
||||
groups.set(t.schema, [...(groups.get(t.schema) ?? []), t]);
|
||||
}
|
||||
// lists first, then alphabetical
|
||||
return [...groups].sort(([a], [b]) => (a === 'lists' ? -1 : b === 'lists' ? 1 : a.localeCompare(b)));
|
||||
});
|
||||
const totalRows = $derived(data.tables.reduce((n, t) => n + (t.rows ?? 0), 0));
|
||||
const badge = (access: string, relkind: string) =>
|
||||
access === 'open'
|
||||
? { text: 'editable', cls: 'border-emerald-600/40 text-emerald-500' }
|
||||
: access === 'core'
|
||||
? { text: 'app table', cls: 'border-amber-500/40 text-amber-500' }
|
||||
: { text: relkind === 'v' || relkind === 'm' ? 'view' : 'read-only', cls: 'border-[var(--site-border)] text-[var(--site-muted)]' };
|
||||
let uploadError = $state('');
|
||||
let uploading = $state(false);
|
||||
const uploadPicture: SubmitFunction = async ({ formData, cancel }) => {
|
||||
@@ -137,4 +155,51 @@
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
{#if data.isOwner && data.tables.length}
|
||||
<section id="database" class="mt-16">
|
||||
<div class="mb-4 flex flex-wrap items-end justify-between gap-4">
|
||||
<div>
|
||||
<h2 class="text-2xl font-semibold tracking-tight">database</h2>
|
||||
<p class="mt-1 text-sm text-[var(--site-muted)]">
|
||||
{data.tables.length} tables in {new Set(data.tables.map((t) => t.schema)).size} schemas, ~{totalRows.toLocaleString('en-GB')} rows. Only you can see this. Lists are editable; app tables are read-only until you unlock them; a new list is a new table.
|
||||
</p>
|
||||
</div>
|
||||
<input
|
||||
type="search"
|
||||
bind:value={tableFilter}
|
||||
placeholder="find a table"
|
||||
aria-label="Find a table"
|
||||
class="w-56 rounded border border-[var(--site-border)] bg-[var(--site-bg)] px-3 py-1.5 text-sm"
|
||||
/>
|
||||
</div>
|
||||
{#each schemas as [schema, tables] (schema)}
|
||||
<details open class="border-t border-[var(--site-border)] py-3" id="schema-{schema}">
|
||||
<summary class="cursor-pointer text-lg font-medium">
|
||||
{schema}
|
||||
<span class="ml-2 text-sm font-normal text-[var(--site-muted)]">{tables.length} {tables.length === 1 ? 'table' : 'tables'}</span>
|
||||
</summary>
|
||||
<ul class="mt-2 grid gap-x-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{#each tables as t (t.name)}
|
||||
{@const b = badge(t.access, t.relkind)}
|
||||
<li>
|
||||
<a
|
||||
href={resolve('/lists/db/[schema]/[table]', { schema: t.schema, table: t.name })}
|
||||
class="flex items-center gap-2 rounded px-2 py-1.5 hover:bg-[var(--site-hover)]"
|
||||
>
|
||||
<span class="min-w-0 flex-1 truncate" title={t.description ?? t.displayName}>{t.name}</span>
|
||||
<span class={['rounded-full border px-1.5 text-[10px]', b.cls]}>{b.text}</span>
|
||||
<span class="w-16 text-right text-xs text-[var(--site-muted)] tabular-nums">
|
||||
{t.rows === null ? '–' : `${t.approximate ? '~' : ''}${t.rows.toLocaleString('en-GB')}`}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</details>
|
||||
{:else}
|
||||
<p class="text-[var(--site-muted)]">no tables match</p>
|
||||
{/each}
|
||||
</section>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user