149 lines
6.3 KiB
TypeScript
149 lines
6.3 KiB
TypeScript
import { useEffect, type ReactNode } from "react";
|
||
import { ArrowBigLeft, ArrowBigLeftDash, ArrowBigRight, ArrowBigRightDash } from "lucide-react";
|
||
import { Button, MessageDisplayPanel, type MessageDisplayAttachment } from "@govoplan/core-webui";
|
||
|
||
// Campaign review/template/mock previews need recipient navigation, review notes,
|
||
// raw MIME inspection and attachment grouping. Generic mailbox reading uses
|
||
// core MessageDisplayPanel instead.
|
||
export type CampaignMessagePreviewAttachment = MessageDisplayAttachment & {
|
||
label?: string | null;
|
||
};
|
||
|
||
export type CampaignMessagePreviewMetaItem = {
|
||
label: string;
|
||
value: ReactNode;
|
||
};
|
||
|
||
export type CampaignMessagePreviewNavigation = {
|
||
index: number;
|
||
total: number;
|
||
onFirst: () => void;
|
||
onPrevious: () => void;
|
||
onNext: () => void;
|
||
onLast: () => void;
|
||
};
|
||
|
||
export type CampaignMessagePreviewOverlayProps = {
|
||
title?: string;
|
||
subject?: string | null;
|
||
bodyMode?: "text" | "html";
|
||
text?: string | null;
|
||
html?: string | null;
|
||
recipientLabel?: ReactNode;
|
||
recipientNote?: ReactNode;
|
||
metaItems?: CampaignMessagePreviewMetaItem[];
|
||
attachments?: CampaignMessagePreviewAttachment[];
|
||
raw?: string | null;
|
||
rawLabel?: string;
|
||
navigation?: CampaignMessagePreviewNavigation;
|
||
closeLabel?: string;
|
||
onClose: () => void;
|
||
};
|
||
|
||
export default function CampaignMessagePreviewOverlay({
|
||
title = "i18n:govoplan-campaign.message_preview.58de1450",
|
||
subject,
|
||
bodyMode = "text",
|
||
text,
|
||
html,
|
||
recipientLabel,
|
||
recipientNote,
|
||
metaItems = [],
|
||
attachments = [],
|
||
raw,
|
||
rawLabel = "i18n:govoplan-campaign.raw_mime.82c612d4",
|
||
navigation,
|
||
closeLabel = "i18n:govoplan-campaign.close.bbfa773e",
|
||
onClose
|
||
}: CampaignMessagePreviewOverlayProps) {
|
||
const shownSubject = subject?.trim() || "i18n:govoplan-campaign.no_subject.7b4e8035";
|
||
const fields = metaItems.map((item) => ({ label: item.label, value: item.value }));
|
||
|
||
useEffect(() => {
|
||
const handleKeyDown = (event: KeyboardEvent) => {
|
||
if (isEditableTarget(event.target)) return;
|
||
if (event.key === "Escape") {
|
||
event.preventDefault();
|
||
onClose();
|
||
return;
|
||
}
|
||
if (!navigation) return;
|
||
if (event.key === "ArrowLeft") {
|
||
event.preventDefault();
|
||
if (navigation.index > 0) navigation.onPrevious();
|
||
} else if (event.key === "ArrowRight") {
|
||
event.preventDefault();
|
||
if (navigation.index < navigation.total - 1) navigation.onNext();
|
||
} else if (event.key === "Home") {
|
||
event.preventDefault();
|
||
if (navigation.index > 0) navigation.onFirst();
|
||
} else if (event.key === "End") {
|
||
event.preventDefault();
|
||
if (navigation.index < navigation.total - 1) navigation.onLast();
|
||
}
|
||
};
|
||
|
||
window.addEventListener("keydown", handleKeyDown);
|
||
return () => window.removeEventListener("keydown", handleKeyDown);
|
||
}, [navigation, onClose]);
|
||
|
||
return (
|
||
<div className="overlay-backdrop" role="dialog" aria-modal="true" aria-labelledby="message-preview-title">
|
||
<div className="modal-panel template-preview-modal message-preview-modal">
|
||
<header className="modal-header">
|
||
<h2 id="message-preview-title">{title}</h2>
|
||
<button className="modal-close" onClick={onClose}>×</button>
|
||
</header>
|
||
<div className="modal-body">
|
||
{(recipientLabel || recipientNote || navigation) &&
|
||
<div className="template-preview-toolbar">
|
||
<div>
|
||
{recipientLabel && <strong>{recipientLabel}</strong>}
|
||
{recipientNote && <p className="muted small-note">{recipientNote}</p>}
|
||
</div>
|
||
{navigation &&
|
||
<div className="button-row compact-actions template-preview-nav" aria-label="i18n:govoplan-campaign.preview_message_navigation.d28a8dc0">
|
||
<button type="button" className="version-arrow" onClick={navigation.onFirst} disabled={navigation.index <= 0} title="i18n:govoplan-campaign.first_message.ffc124fd" aria-label="i18n:govoplan-campaign.first_message.ffc124fd"><ArrowBigLeftDash aria-hidden="true" /></button>
|
||
<button type="button" className="version-arrow" onClick={navigation.onPrevious} disabled={navigation.index <= 0} title="i18n:govoplan-campaign.previous_message.93261bd8" aria-label="i18n:govoplan-campaign.previous_message.93261bd8"><ArrowBigLeft aria-hidden="true" /></button>
|
||
<span className="template-preview-count">{navigation.index + 1} / {navigation.total}</span>
|
||
<button type="button" className="version-arrow" onClick={navigation.onNext} disabled={navigation.index >= navigation.total - 1} title="i18n:govoplan-campaign.next_message.e3960a5d" aria-label="i18n:govoplan-campaign.next_message.e3960a5d"><ArrowBigRight aria-hidden="true" /></button>
|
||
<button type="button" className="version-arrow" onClick={navigation.onLast} disabled={navigation.index >= navigation.total - 1} title="i18n:govoplan-campaign.last_message.83741110" aria-label="i18n:govoplan-campaign.last_message.83741110"><ArrowBigRightDash aria-hidden="true" /></button>
|
||
</div>
|
||
}
|
||
</div>
|
||
}
|
||
|
||
<MessageDisplayPanel
|
||
title={shownSubject}
|
||
fields={fields}
|
||
bodyText={text}
|
||
bodyHtml={html}
|
||
preferredBodyMode={bodyMode}
|
||
deriveTextFromHtml={false}
|
||
attachments={attachments}
|
||
emptyText="i18n:govoplan-campaign.no_message_content_is_available.54e8e7e6" />
|
||
|
||
|
||
{raw &&
|
||
<details className="message-preview-raw">
|
||
<summary>{rawLabel}</summary>
|
||
<pre className="mock-message-raw">{raw}</pre>
|
||
</details>
|
||
}
|
||
</div>
|
||
<footer className="modal-footer"><Button variant="primary" onClick={onClose}>{closeLabel}</Button></footer>
|
||
</div>
|
||
</div>);
|
||
|
||
}
|
||
|
||
function isEditableTarget(target: EventTarget | null): boolean {
|
||
if (!(target instanceof HTMLElement)) return false;
|
||
return target.isContentEditable || ["INPUT", "SELECT", "TEXTAREA"].includes(target.tagName);
|
||
}
|
||
|
||
|
||
export type MessagePreviewAttachment = CampaignMessagePreviewAttachment;
|
||
export type MessagePreviewMetaItem = CampaignMessagePreviewMetaItem;
|
||
export type MessagePreviewNavigation = CampaignMessagePreviewNavigation;
|
||
export type MessagePreviewOverlayProps = CampaignMessagePreviewOverlayProps; |