feat: plakat redesign phase 4 (chat window, contact, footer, cleanup) (#77)
- AI chat moves into a window/sheet, opened from a new speech-bubble card on the desk; it now respects the chatbot consent and asks before sending anything to n8n (previously it ignored the consent) - Contact as red poster section with big mail address and the form on a paper card; validation errors are finally shown inline (aria-invalid, role=alert) - Footer as ink strip with dk0 wordmark, legal links and a button to reopen the privacy settings - "About me" in the header opens the about window on the home page - Remove the old About section and its parts (BentoChat, ActivityFeed, CurrentlyReading, ReadBooks), the unused ChatWidget, Projects and BentoGrid, their tests, wrappers, loaders, types and texts - scripts/directus-cleanup-redesign.mjs: dry run by default, --apply archives hobbies, tech stack categories and old content pages and deletes four unused message overrides after a full JSON backup; run only after the redesign is live on production Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -155,7 +155,9 @@ function BoardItem({ obj, labels, spot, z, tableRef, onFront, register }: BoardI
|
||||
const openWindow = useOpenWindow();
|
||||
const elRef = useRef<HTMLDivElement | null>(null);
|
||||
const openIt = () => openWindow?.(windowFor(obj), elRef.current);
|
||||
const radius = { sticker: "rounded-full", photo: "rounded-md", book: "rounded-[6px_14px_14px_6px]" }[obj.kind as string] ?? "rounded-2xl";
|
||||
const radius =
|
||||
{ sticker: "rounded-full", photo: "rounded-md", book: "rounded-[6px_14px_14px_6px]", chat: "rounded-[22px_22px_22px_6px]" }[obj.kind as string] ??
|
||||
"rounded-2xl";
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
|
||||
@@ -85,6 +85,15 @@ export default function ObjectFace({ obj, labels }: ObjectFaceProps) {
|
||||
</div>
|
||||
);
|
||||
|
||||
case "chat":
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col justify-between rounded-[22px_22px_22px_6px] border-2 border-ink bg-ink p-4 text-paper">
|
||||
<span className="font-mono text-xs uppercase tracking-wide text-signal">KI · n8n</span>
|
||||
<span className="font-display text-[1.7rem] font-extrabold leading-[0.95] tracking-[-0.03em]">{labels.chatCard}</span>
|
||||
<span className="font-mono text-xs opacity-70">{labels.chatCardHint} ↗</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
case "sticker":
|
||||
return (
|
||||
<div className="grid h-full w-full place-items-center rounded-full border-2 border-ink bg-signal p-6 text-center font-display text-xl font-extrabold leading-[0.98] tracking-[-0.02em] text-ink">
|
||||
|
||||
@@ -45,6 +45,8 @@ export default async function TischSection({ locale }: TischSectionProps) {
|
||||
bookLabel: t("bookLabel"),
|
||||
bookFallback: t("bookFallback"),
|
||||
photoCaption: t("photoCaption"),
|
||||
chatCard: t("chatCard"),
|
||||
chatCardHint: t("chatCardHint"),
|
||||
sticker: t("sticker"),
|
||||
};
|
||||
|
||||
@@ -73,6 +75,7 @@ export default async function TischSection({ locale }: TischSectionProps) {
|
||||
{ kind: "server", id: "server" },
|
||||
{ kind: "book", id: "book", href: `/${locale}/books` },
|
||||
{ kind: "photo", id: "photo" },
|
||||
{ kind: "chat", id: "chat" },
|
||||
{ kind: "sticker", id: "sticker" },
|
||||
];
|
||||
// Im Stapel wäre der runde Sticker eine Ellipse und wiederholt nur den Claim
|
||||
|
||||
@@ -37,12 +37,17 @@ export interface TischPhoto {
|
||||
id: "photo";
|
||||
}
|
||||
|
||||
export interface TischChat {
|
||||
kind: "chat";
|
||||
id: "chat";
|
||||
}
|
||||
|
||||
export interface TischSticker {
|
||||
kind: "sticker";
|
||||
id: "sticker";
|
||||
}
|
||||
|
||||
export type TischObject = TischProject | TischTicket | TischServer | TischBook | TischPhoto | TischSticker;
|
||||
export type TischObject = TischProject | TischTicket | TischServer | TischBook | TischPhoto | TischChat | TischSticker;
|
||||
|
||||
export interface TischLabels {
|
||||
hintBoard: string;
|
||||
@@ -63,6 +68,8 @@ export interface TischLabels {
|
||||
bookLabel: string;
|
||||
bookFallback: string;
|
||||
photoCaption: string;
|
||||
chatCard: string;
|
||||
chatCardHint: string;
|
||||
sticker: string;
|
||||
}
|
||||
|
||||
@@ -73,6 +80,7 @@ export const BOARD_SIZE: Record<TischObject["kind"], { w: number; h: number }> =
|
||||
server: { w: 270, h: 290 },
|
||||
book: { w: 176, h: 262 },
|
||||
photo: { w: 210, h: 250 },
|
||||
chat: { w: 250, h: 170 },
|
||||
sticker: { w: 180, h: 180 },
|
||||
};
|
||||
|
||||
@@ -89,6 +97,7 @@ export const BOARD_SPOTS: { x: number; y: number; r: number }[] = [
|
||||
{ x: 0.76, y: 450, r: -7 },
|
||||
{ x: 0.42, y: 800, r: 7 },
|
||||
{ x: 0.12, y: 800, r: -4 },
|
||||
{ x: 0.66, y: 790, r: 4 },
|
||||
];
|
||||
|
||||
// Farbfelder für Projektkarten, abwechselnd hell und dunkel
|
||||
|
||||
@@ -15,6 +15,8 @@ export function windowFor(obj: TischObject): WindowRequest {
|
||||
return { kind: "read" };
|
||||
case "photo":
|
||||
return { kind: "about" };
|
||||
case "chat":
|
||||
return { kind: "chat" };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +33,8 @@ export function labelFor(obj: TischObject, labels: TischLabels): string {
|
||||
return labels.bookLabel;
|
||||
case "photo":
|
||||
return labels.photoCaption;
|
||||
case "chat":
|
||||
return labels.chatCard;
|
||||
case "sticker":
|
||||
return labels.sticker;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user