feat: Adressbuch-Block mit Suche, Org- und Buchstabenfilter
- AdressbuchBlock: listet alle Kontakte alphabetisch mit Suche, Org-Filter-Chips und A–Z-Buchstabenfilter; /adressbuch-Seite - ContactCard: neues Design passend zu OrganisationsCard (Badges, Links) - ContactsBlock: Karussell mit snap-scroll, volle Breite - block-types, blog-utils, cms: AdressbuchBlockData, resolveAdressbuchBlocks, getContacts; BlockRenderer registriert adressbuch - Kontakt-Seite: Link auf /adressbuch statt eingebettetem Block Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,27 +4,54 @@
|
||||
import type { ContactEntry } from "$lib/block-types";
|
||||
|
||||
let { contact }: { contact: ContactEntry } = $props();
|
||||
|
||||
const orgNames = $derived(
|
||||
(contact.organisations ?? [])
|
||||
.filter((o): o is { _slug?: string; name?: string } => typeof o === "object" && o !== null)
|
||||
.map((o) => o.name)
|
||||
.filter(Boolean) as string[]
|
||||
);
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-1.5 rounded-lg border border-stein-200 bg-white px-4 py-3">
|
||||
<div>
|
||||
<span class="text-sm font-semibold text-stein-900">{contact.name ?? ""}</span>
|
||||
{#if contact.role}
|
||||
<span class="ml-1.5 text-xs text-stein-500">{contact.role}</span>
|
||||
{/if}
|
||||
<div
|
||||
class="not-prose card-surface relative flex h-full min-w-0 flex-col gap-2 overflow-hidden rounded-sm p-4 transition-[transform,box-shadow,border-color] duration-200 hover:-translate-y-0.5 hover:border-wald-300 hover:shadow-md"
|
||||
>
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<p class="truncate text-sm font-medium leading-tight text-stein-900">{contact.name ?? ""}</p>
|
||||
|
||||
{#if contact.role || orgNames.length > 0}
|
||||
<div class="flex flex-wrap gap-1">
|
||||
{#if contact.role}
|
||||
<span class="shrink-0 rounded-full border border-stein-200 bg-stein-100 px-1.5 py-0 text-[0.65rem] font-medium text-stein-600">
|
||||
{contact.role}
|
||||
</span>
|
||||
{/if}
|
||||
{#each orgNames as org}
|
||||
<span class="shrink-0 rounded-full border border-wald-200 bg-wald-50 px-1.5 py-0 text-[0.65rem] font-medium text-wald-700">
|
||||
{org}
|
||||
</span>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if contact.phone}
|
||||
<a href="tel:{contact.phone.replace(/\s/g, '')}" class="inline-flex items-center gap-1.5 text-xs text-stein-700 no-underline hover:text-wald-700">
|
||||
<Icon icon="mdi:phone-outline" class="size-3.5 shrink-0 text-stein-400" />
|
||||
{contact.phone}
|
||||
</a>
|
||||
{/if}
|
||||
{#if contact.email}
|
||||
<a href="mailto:{contact.email}" class="inline-flex items-center gap-1.5 text-xs text-stein-700 no-underline hover:text-wald-700">
|
||||
<Icon icon="mdi:email-outline" class="size-3.5 shrink-0 text-stein-400" />
|
||||
{contact.email}
|
||||
</a>
|
||||
|
||||
{#if contact.phone || contact.email}
|
||||
<div class="flex flex-col gap-1">
|
||||
{#if contact.phone}
|
||||
<a href="tel:{contact.phone.replace(/\s/g, '')}" class="inline-flex items-center gap-1.5 text-xs text-stein-600 no-underline hover:text-wald-700">
|
||||
<Icon icon="mdi:phone-outline" class="size-3.5 shrink-0 text-stein-400" />
|
||||
<span class="truncate">{contact.phone}</span>
|
||||
</a>
|
||||
{/if}
|
||||
{#if contact.email}
|
||||
<a href="mailto:{contact.email}" class="inline-flex items-center gap-1.5 text-xs text-stein-600 no-underline hover:text-wald-700">
|
||||
<Icon icon="mdi:email-outline" class="size-3.5 shrink-0 text-stein-400" />
|
||||
<span class="truncate">{contact.email}</span>
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if contact.note}
|
||||
<p class="text-[11px] text-stein-400">{contact.note}</p>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user