Enhance user routing and layout management. Added support for user-specific routes, including dynamic user ID handling and updated component imports. Improved type definitions and user data mapping in hooks for better user experience.
This commit is contained in:
@@ -1,9 +1,15 @@
|
||||
<script lang="ts">
|
||||
import { browser } from '$app/environment';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { sites, siteSlugs } from '$lib/sites';
|
||||
import { sites, siteSlugs, type SiteSlug } from '$lib/sites';
|
||||
|
||||
let { data }: { data: Record<string, unknown> } = $props();
|
||||
let { data }: { data: App.PageData } = $props();
|
||||
|
||||
const visibleSlugs = $derived(
|
||||
data.user
|
||||
? (siteSlugs.filter((slug) => data.user && data.user[slug] === true) as SiteSlug[])
|
||||
: []
|
||||
);
|
||||
</script>
|
||||
|
||||
{#if browser}
|
||||
@@ -18,7 +24,7 @@
|
||||
<main class="flex min-h-0 flex-1 flex-col items-center justify-center gap-4 p-4 sm:gap-6 sm:p-6">
|
||||
<h1 class="text-2xl font-semibold">Base</h1>
|
||||
<div class="flex w-full max-w-xs flex-col gap-3">
|
||||
{#each siteSlugs as slug}
|
||||
{#each visibleSlugs as slug}
|
||||
{@const site = sites[slug]}
|
||||
<a href="/{slug}" class="block">
|
||||
<Button class="h-16 w-full min-h-16" variant="outline">{site.title}</Button>
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { sites, siteSlugs } from '$lib/sites';
|
||||
import { sites, siteSlugs, type SiteSlug } from '$lib/sites';
|
||||
|
||||
let { data: _data }: { data: Record<string, unknown> } = $props();
|
||||
// `_data` is accepted to mirror Stackq's pattern (unused for now).
|
||||
let { data }: { data: App.PageData } = $props();
|
||||
|
||||
const visibleSlugs = $derived(
|
||||
data.user
|
||||
? (siteSlugs.filter((slug) => data.user && data.user[slug] === true) as SiteSlug[])
|
||||
: []
|
||||
);
|
||||
</script>
|
||||
|
||||
<main class="flex min-h-0 flex-1 flex-col items-center justify-center gap-4 p-4 sm:gap-6 sm:p-6">
|
||||
<h1 class="w-full max-w-xs shrink-0 text-center text-2xl font-semibold">Base</h1>
|
||||
<div class="flex w-full max-w-xs shrink-0 flex-col gap-3">
|
||||
{#each siteSlugs as slug}
|
||||
{#each visibleSlugs as slug}
|
||||
{@const site = sites[slug]}
|
||||
<a href="/{slug}" class="block">
|
||||
<Button class="h-16 min-h-16 w-full" variant="outline">
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { mapUserRecord } from '$lib/user-from-record';
|
||||
|
||||
export const load: PageServerLoad = async ({ locals }) => {
|
||||
const list = await locals.pb.collection('users').getFullList();
|
||||
const users = list.map((r) => mapUserRecord(r as Record<string, unknown>));
|
||||
return { users };
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
<script lang="ts">
|
||||
import * as Card from '$lib/components/ui/card';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import type { UserFromRecord } from '$lib/user-from-record';
|
||||
|
||||
let { data }: { data: { users: UserFromRecord[] } } = $props();
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Users — Base</title>
|
||||
</svelte:head>
|
||||
|
||||
<main class="flex min-h-0 flex-1 flex-col gap-4 p-4 sm:gap-6 sm:p-6">
|
||||
<h1 class="text-2xl font-semibold">Users</h1>
|
||||
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{#each data.users as user (user.id)}
|
||||
<Card.Root class="flex flex-col">
|
||||
<Card.Header class="flex flex-row items-center gap-3">
|
||||
<div class="flex size-10 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground text-sm font-medium">
|
||||
{user.name
|
||||
? String(user.name).slice(0, 2).toUpperCase()
|
||||
: user.email
|
||||
? String(user.email).slice(0, 2).toUpperCase()
|
||||
: '?'}
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<Card.Title class="truncate">{user.name ?? user.email ?? user.id}</Card.Title>
|
||||
{#if user.email && user.name}
|
||||
<Card.Description class="truncate text-xs">{user.email}</Card.Description>
|
||||
{/if}
|
||||
</div>
|
||||
</Card.Header>
|
||||
<Card.Content class="flex-1">
|
||||
<p class="text-muted-foreground text-sm">
|
||||
Prism {user.prism ? '✓' : '—'} · TasGrid {user.tasgrid ? '✓' : '—'} · Stackq {user.stackq ? '✓' : '—'} · HRM {user.hrm ? '✓' : '—'}
|
||||
</p>
|
||||
</Card.Content>
|
||||
<Card.Footer>
|
||||
<a href="/users/{user.id}" class="block">
|
||||
<Button variant="outline" class="w-full">View</Button>
|
||||
</a>
|
||||
</Card.Footer>
|
||||
</Card.Root>
|
||||
{/each}
|
||||
</div>
|
||||
</main>
|
||||
@@ -0,0 +1,14 @@
|
||||
import { error } from '@sveltejs/kit';
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { mapUserRecord } from '$lib/user-from-record';
|
||||
|
||||
export const load: PageServerLoad = async ({ params, locals }) => {
|
||||
const id = params.id;
|
||||
if (!id) throw error(404, 'User not found');
|
||||
try {
|
||||
const record = (await locals.pb.collection('users').getOne(id)) as Record<string, unknown>;
|
||||
return { user: mapUserRecord(record) };
|
||||
} catch {
|
||||
throw error(404, 'User not found');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
<script lang="ts">
|
||||
import * as Card from '$lib/components/ui/card';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import type { UserFromRecord } from '$lib/user-from-record';
|
||||
|
||||
let { data }: { data: { user: UserFromRecord } } = $props();
|
||||
const user = data.user;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{user.name ?? user.email ?? 'User'} — Base</title>
|
||||
</svelte:head>
|
||||
|
||||
<main class="flex min-h-0 flex-1 flex-col gap-4 p-4 sm:gap-6 sm:p-6">
|
||||
<a href="/users" class="self-start">
|
||||
<Button variant="ghost" size="sm">← Users</Button>
|
||||
</a>
|
||||
<Card.Root class="max-w-md">
|
||||
<Card.Header>
|
||||
<Card.Title>{user.name ?? user.email ?? user.id}</Card.Title>
|
||||
{#if user.email}
|
||||
<Card.Description>{user.email}</Card.Description>
|
||||
{/if}
|
||||
</Card.Header>
|
||||
<Card.Content class="flex flex-col gap-2">
|
||||
<p class="text-muted-foreground text-sm">
|
||||
<span class="font-medium text-foreground">Access:</span><br />
|
||||
Prism {user.prism ? '✓' : '—'} · TasGrid {user.tasgrid ? '✓' : '—'} · Stackq {user.stackq ? '✓' : '—'} · HRM {user.hrm ? '✓' : '—'}
|
||||
</p>
|
||||
<p class="text-muted-foreground text-sm">
|
||||
<span class="font-medium text-foreground">Dark mode:</span> {user.darkmode ? 'On' : 'Off'}
|
||||
</p>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
</main>
|
||||
Reference in New Issue
Block a user