feat(contacts): ContactCard + ContactsBlock-Komponente, BlockRenderer registriert
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -331,6 +331,24 @@ export interface OrganisationsBlockData {
|
||||
layout?: BlockLayout;
|
||||
}
|
||||
|
||||
export interface ContactEntry {
|
||||
_type?: "contact";
|
||||
_slug?: string;
|
||||
name?: string;
|
||||
role?: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
note?: string;
|
||||
}
|
||||
|
||||
export interface ContactsBlockData {
|
||||
_type?: "contacts_block";
|
||||
_slug?: string;
|
||||
headline?: string;
|
||||
contacts?: (string | ContactEntry)[];
|
||||
layout?: BlockLayout;
|
||||
}
|
||||
|
||||
/** Live-Strommix-Widget (_type: "live_strommix"). Editorial-Konfig + Platzierungs-Instanz.
|
||||
* Fetched live data from /api/strommix (aggregator) and renders status bucket
|
||||
* derived from `(wind_onshore + wind_offshore + solar) / load * 100`. */
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
import CalendarBlock from "./blocks/CalendarBlock.svelte";
|
||||
import CalendarCompactBlock from "./blocks/CalendarCompactBlock.svelte";
|
||||
import OrganisationsBlock from "./blocks/OrganisationsBlock.svelte";
|
||||
import ContactsBlock from "./blocks/ContactsBlock.svelte";
|
||||
import OpnFormBlock from "./blocks/OpnFormBlock.svelte";
|
||||
import InternalComponentBlock from "./blocks/InternalComponentBlock.svelte";
|
||||
import DeadlineBannerBlock from "./blocks/DeadlineBannerBlock.svelte";
|
||||
@@ -39,6 +40,7 @@
|
||||
SearchableTextBlockData,
|
||||
CalendarBlockData,
|
||||
OrganisationsBlockData,
|
||||
ContactsBlockData,
|
||||
OpnFormBlockData,
|
||||
InternalComponentBlockData,
|
||||
DeadlineBannerBlockData,
|
||||
@@ -91,6 +93,8 @@
|
||||
{/if}
|
||||
{:else if type === "organisations"}
|
||||
<OrganisationsBlock block={block as OrganisationsBlockData} />
|
||||
{:else if type === "contacts_block"}
|
||||
<ContactsBlock block={block as ContactsBlockData} />
|
||||
{:else if type === "opnform"}
|
||||
<OpnFormBlock block={block as OpnFormBlockData} />
|
||||
{:else if type === "internal_component"}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<script lang="ts">
|
||||
import Icon from "@iconify/svelte";
|
||||
import "$lib/iconify-offline";
|
||||
import type { ContactEntry } from "$lib/block-types";
|
||||
|
||||
let { contact }: { contact: ContactEntry } = $props();
|
||||
</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>
|
||||
{#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}
|
||||
{#if contact.note}
|
||||
<p class="text-[11px] text-stein-400">{contact.note}</p>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -0,0 +1,25 @@
|
||||
<script lang="ts">
|
||||
import ContactCard from "$lib/components/ContactCard.svelte";
|
||||
import type { ContactsBlockData, ContactEntry } from "$lib/block-types";
|
||||
|
||||
let { block }: { block: ContactsBlockData } = $props();
|
||||
|
||||
const contacts = $derived(
|
||||
(block.contacts ?? []).filter((c): c is ContactEntry => typeof c === "object" && c !== null)
|
||||
);
|
||||
</script>
|
||||
|
||||
{#if contacts.length > 0}
|
||||
<div>
|
||||
{#if block.headline}
|
||||
<h3 class="mb-3 text-sm font-semibold text-stein-700">{block.headline}</h3>
|
||||
{/if}
|
||||
<div class="flex flex-col gap-2 sm:flex-row sm:flex-wrap">
|
||||
{#each contacts as contact}
|
||||
<div class="sm:min-w-[220px] sm:max-w-xs">
|
||||
<ContactCard {contact} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
Reference in New Issue
Block a user