Release v0.1.2
This commit is contained in:
@@ -1,22 +1,20 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { ArrowBigLeft, ArrowBigLeftDash, ArrowBigRight, ArrowBigRightDash } from "lucide-react";
|
||||
import Button from "../../../components/Button";
|
||||
import { Button, MessageDisplayPanel, type MessageDisplayAttachment } from "@govoplan/core-webui";
|
||||
|
||||
export type MessagePreviewAttachment = {
|
||||
filename?: string | null;
|
||||
// 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;
|
||||
detail?: string | null;
|
||||
contentType?: string | null;
|
||||
sizeBytes?: number | null;
|
||||
archiveGroup?: string | null;
|
||||
archiveLabel?: string | null;
|
||||
};
|
||||
|
||||
export type MessagePreviewMetaItem = {
|
||||
export type CampaignMessagePreviewMetaItem = {
|
||||
label: string;
|
||||
value: React.ReactNode;
|
||||
value: ReactNode;
|
||||
};
|
||||
|
||||
export type MessagePreviewNavigation = {
|
||||
export type CampaignMessagePreviewNavigation = {
|
||||
index: number;
|
||||
total: number;
|
||||
onFirst: () => void;
|
||||
@@ -25,24 +23,24 @@ export type MessagePreviewNavigation = {
|
||||
onLast: () => void;
|
||||
};
|
||||
|
||||
export type MessagePreviewOverlayProps = {
|
||||
export type CampaignMessagePreviewOverlayProps = {
|
||||
title?: string;
|
||||
subject?: string | null;
|
||||
bodyMode?: "text" | "html";
|
||||
text?: string | null;
|
||||
html?: string | null;
|
||||
recipientLabel?: React.ReactNode;
|
||||
recipientNote?: React.ReactNode;
|
||||
metaItems?: MessagePreviewMetaItem[];
|
||||
attachments?: MessagePreviewAttachment[];
|
||||
recipientLabel?: ReactNode;
|
||||
recipientNote?: ReactNode;
|
||||
metaItems?: CampaignMessagePreviewMetaItem[];
|
||||
attachments?: CampaignMessagePreviewAttachment[];
|
||||
raw?: string | null;
|
||||
rawLabel?: string;
|
||||
navigation?: MessagePreviewNavigation;
|
||||
navigation?: CampaignMessagePreviewNavigation;
|
||||
closeLabel?: string;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
export default function MessagePreviewOverlay({
|
||||
export default function CampaignMessagePreviewOverlay({
|
||||
title = "Message preview",
|
||||
subject,
|
||||
bodyMode = "text",
|
||||
@@ -57,10 +55,9 @@ export default function MessagePreviewOverlay({
|
||||
navigation,
|
||||
closeLabel = "Close",
|
||||
onClose
|
||||
}: MessagePreviewOverlayProps) {
|
||||
}: CampaignMessagePreviewOverlayProps) {
|
||||
const shownSubject = subject?.trim() || "No subject";
|
||||
const shownText = text?.trim() || "No plain-text body to preview.";
|
||||
const shownHtml = html?.trim() || "<p>No HTML body to preview.</p>";
|
||||
const fields = metaItems.map((item) => ({ label: item.label, value: item.value }));
|
||||
|
||||
return (
|
||||
<div className="overlay-backdrop" role="dialog" aria-modal="true" aria-labelledby="message-preview-title">
|
||||
@@ -88,24 +85,16 @@ export default function MessagePreviewOverlay({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{metaItems.length > 0 && (
|
||||
<div className="detail-grid message-preview-meta">
|
||||
{metaItems.map((item) => (
|
||||
<div key={item.label}><span className="muted small-note">{item.label}</span><strong>{item.value || "—"}</strong></div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="template-preview-box">
|
||||
<h3>{shownSubject}</h3>
|
||||
{bodyMode === "html" ? (
|
||||
<iframe className="template-preview-frame" title="Rendered HTML body preview" sandbox="" srcDoc={shownHtml} />
|
||||
) : (
|
||||
<pre>{shownText}</pre>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<MessagePreviewAttachmentBoxes attachments={attachments} />
|
||||
<MessageDisplayPanel
|
||||
title={shownSubject}
|
||||
fields={fields}
|
||||
bodyText={text}
|
||||
bodyHtml={html}
|
||||
preferredBodyMode={bodyMode}
|
||||
deriveTextFromHtml={false}
|
||||
attachments={attachments}
|
||||
emptyText="No message content is available."
|
||||
/>
|
||||
|
||||
{raw && (
|
||||
<details className="message-preview-raw">
|
||||
@@ -120,46 +109,8 @@ export default function MessagePreviewOverlay({
|
||||
);
|
||||
}
|
||||
|
||||
function MessagePreviewAttachmentBoxes({ attachments }: { attachments: MessagePreviewAttachment[] }) {
|
||||
const direct = attachments.filter((attachment) => !attachment.archiveGroup);
|
||||
const archiveGroups = new Map<string, MessagePreviewAttachment[]>();
|
||||
for (const attachment of attachments) {
|
||||
if (!attachment.archiveGroup) continue;
|
||||
const group = archiveGroups.get(attachment.archiveGroup) ?? [];
|
||||
group.push(attachment);
|
||||
archiveGroups.set(attachment.archiveGroup, group);
|
||||
}
|
||||
|
||||
function attachmentChip(attachment: MessagePreviewAttachment, index: number) {
|
||||
const filename = attachment.filename?.trim() || attachment.label?.trim() || "Unnamed attachment";
|
||||
const details = [attachment.detail, attachment.contentType, attachment.sizeBytes ? `${attachment.sizeBytes} bytes` : ""].filter(Boolean).join(" · ");
|
||||
return (
|
||||
<div className="attachment-file-chip" key={`${filename}:${index}`}>
|
||||
<strong>{filename}</strong>
|
||||
{details && <span>{details}</span>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="template-preview-attachments message-preview-attachments">
|
||||
<h3>Attachments</h3>
|
||||
{attachments.length === 0 ? (
|
||||
<p className="muted small-note">No attachments are effective for this message.</p>
|
||||
) : (
|
||||
<div className="message-preview-attachment-layout">
|
||||
{direct.length > 0 && <div className="attachment-chip-grid">{direct.map(attachmentChip)}</div>}
|
||||
{[...archiveGroups.entries()].map(([groupName, items]) => (
|
||||
<section className="attachment-zip-group" key={groupName}>
|
||||
<header>
|
||||
<strong>{items[0]?.archiveLabel || groupName}</strong>
|
||||
<span>{items.length} file{items.length === 1 ? "" : "s"} inside ZIP</span>
|
||||
</header>
|
||||
<div className="attachment-chip-grid">{items.map(attachmentChip)}</div>
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export type MessagePreviewAttachment = CampaignMessagePreviewAttachment;
|
||||
export type MessagePreviewMetaItem = CampaignMessagePreviewMetaItem;
|
||||
export type MessagePreviewNavigation = CampaignMessagePreviewNavigation;
|
||||
export type MessagePreviewOverlayProps = CampaignMessagePreviewOverlayProps;
|
||||
|
||||
Reference in New Issue
Block a user