payload-plugin-newsletter 0.16.10 → 0.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/dist/collections.cjs +73 -31
- package/dist/collections.cjs.map +1 -1
- package/dist/collections.js +73 -31
- package/dist/collections.js.map +1 -1
- package/dist/components.cjs +295 -275
- package/dist/components.cjs.map +1 -1
- package/dist/components.js +305 -285
- package/dist/components.js.map +1 -1
- package/dist/index.cjs +118 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +118 -33
- package/dist/index.js.map +1 -1
- package/dist/types.d.cts +7 -0
- package/dist/types.d.ts +7 -0
- package/dist/utils.cjs +64 -28
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +1 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +64 -28
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
package/dist/components.js
CHANGED
|
@@ -917,62 +917,73 @@ async function convertToEmailSafeHtml(editorState, options) {
|
|
|
917
917
|
if (!editorState) {
|
|
918
918
|
return "";
|
|
919
919
|
}
|
|
920
|
-
const rawHtml = await lexicalToEmailHtml(editorState, options?.mediaUrl);
|
|
920
|
+
const rawHtml = await lexicalToEmailHtml(editorState, options?.mediaUrl, options?.customBlockConverter);
|
|
921
921
|
const sanitizedHtml = DOMPurify.sanitize(rawHtml, EMAIL_SAFE_CONFIG);
|
|
922
922
|
if (options?.wrapInTemplate) {
|
|
923
923
|
return wrapInEmailTemplate(sanitizedHtml, options.preheader);
|
|
924
924
|
}
|
|
925
925
|
return sanitizedHtml;
|
|
926
926
|
}
|
|
927
|
-
async function lexicalToEmailHtml(editorState, mediaUrl) {
|
|
927
|
+
async function lexicalToEmailHtml(editorState, mediaUrl, customBlockConverter) {
|
|
928
928
|
const { root } = editorState;
|
|
929
929
|
if (!root || !root.children) {
|
|
930
930
|
return "";
|
|
931
931
|
}
|
|
932
|
-
const
|
|
933
|
-
|
|
932
|
+
const htmlParts = await Promise.all(
|
|
933
|
+
root.children.map((node) => convertNode(node, mediaUrl, customBlockConverter))
|
|
934
|
+
);
|
|
935
|
+
return htmlParts.join("");
|
|
934
936
|
}
|
|
935
|
-
function convertNode(node, mediaUrl) {
|
|
937
|
+
async function convertNode(node, mediaUrl, customBlockConverter) {
|
|
936
938
|
switch (node.type) {
|
|
937
939
|
case "paragraph":
|
|
938
|
-
return convertParagraph(node, mediaUrl);
|
|
940
|
+
return convertParagraph(node, mediaUrl, customBlockConverter);
|
|
939
941
|
case "heading":
|
|
940
|
-
return convertHeading(node, mediaUrl);
|
|
942
|
+
return convertHeading(node, mediaUrl, customBlockConverter);
|
|
941
943
|
case "list":
|
|
942
|
-
return convertList(node, mediaUrl);
|
|
944
|
+
return convertList(node, mediaUrl, customBlockConverter);
|
|
943
945
|
case "listitem":
|
|
944
|
-
return convertListItem(node, mediaUrl);
|
|
946
|
+
return convertListItem(node, mediaUrl, customBlockConverter);
|
|
945
947
|
case "blockquote":
|
|
946
|
-
return convertBlockquote(node, mediaUrl);
|
|
948
|
+
return convertBlockquote(node, mediaUrl, customBlockConverter);
|
|
947
949
|
case "text":
|
|
948
950
|
return convertText(node);
|
|
949
951
|
case "link":
|
|
950
|
-
return convertLink(node, mediaUrl);
|
|
952
|
+
return convertLink(node, mediaUrl, customBlockConverter);
|
|
951
953
|
case "linebreak":
|
|
952
954
|
return "<br>";
|
|
953
955
|
case "upload":
|
|
954
956
|
return convertUpload(node, mediaUrl);
|
|
955
957
|
case "block":
|
|
956
|
-
return convertBlock(node, mediaUrl);
|
|
958
|
+
return await convertBlock(node, mediaUrl, customBlockConverter);
|
|
957
959
|
default:
|
|
958
960
|
if (node.children) {
|
|
959
|
-
|
|
961
|
+
const childParts = await Promise.all(
|
|
962
|
+
node.children.map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
963
|
+
);
|
|
964
|
+
return childParts.join("");
|
|
960
965
|
}
|
|
961
966
|
return "";
|
|
962
967
|
}
|
|
963
968
|
}
|
|
964
|
-
function convertParagraph(node, mediaUrl) {
|
|
969
|
+
async function convertParagraph(node, mediaUrl, customBlockConverter) {
|
|
965
970
|
const align = getAlignment(node.format);
|
|
966
|
-
const
|
|
971
|
+
const childParts = await Promise.all(
|
|
972
|
+
(node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
973
|
+
);
|
|
974
|
+
const children = childParts.join("");
|
|
967
975
|
if (!children.trim()) {
|
|
968
976
|
return '<p style="margin: 0 0 16px 0; min-height: 1em;"> </p>';
|
|
969
977
|
}
|
|
970
978
|
return `<p style="margin: 0 0 16px 0; text-align: ${align};">${children}</p>`;
|
|
971
979
|
}
|
|
972
|
-
function convertHeading(node, mediaUrl) {
|
|
980
|
+
async function convertHeading(node, mediaUrl, customBlockConverter) {
|
|
973
981
|
const tag = node.tag || "h1";
|
|
974
982
|
const align = getAlignment(node.format);
|
|
975
|
-
const
|
|
983
|
+
const childParts = await Promise.all(
|
|
984
|
+
(node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
985
|
+
);
|
|
986
|
+
const children = childParts.join("");
|
|
976
987
|
const styles = {
|
|
977
988
|
h1: "font-size: 32px; font-weight: 700; margin: 0 0 24px 0; line-height: 1.2;",
|
|
978
989
|
h2: "font-size: 24px; font-weight: 600; margin: 0 0 16px 0; line-height: 1.3;",
|
|
@@ -981,18 +992,27 @@ function convertHeading(node, mediaUrl) {
|
|
|
981
992
|
const style = `${styles[tag] || styles.h3} text-align: ${align};`;
|
|
982
993
|
return `<${tag} style="${style}">${children}</${tag}>`;
|
|
983
994
|
}
|
|
984
|
-
function convertList(node, mediaUrl) {
|
|
995
|
+
async function convertList(node, mediaUrl, customBlockConverter) {
|
|
985
996
|
const tag = node.listType === "number" ? "ol" : "ul";
|
|
986
|
-
const
|
|
997
|
+
const childParts = await Promise.all(
|
|
998
|
+
(node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
999
|
+
);
|
|
1000
|
+
const children = childParts.join("");
|
|
987
1001
|
const style = tag === "ul" ? "margin: 0 0 16px 0; padding-left: 24px; list-style-type: disc;" : "margin: 0 0 16px 0; padding-left: 24px; list-style-type: decimal;";
|
|
988
1002
|
return `<${tag} style="${style}">${children}</${tag}>`;
|
|
989
1003
|
}
|
|
990
|
-
function convertListItem(node, mediaUrl) {
|
|
991
|
-
const
|
|
1004
|
+
async function convertListItem(node, mediaUrl, customBlockConverter) {
|
|
1005
|
+
const childParts = await Promise.all(
|
|
1006
|
+
(node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
1007
|
+
);
|
|
1008
|
+
const children = childParts.join("");
|
|
992
1009
|
return `<li style="margin: 0 0 8px 0;">${children}</li>`;
|
|
993
1010
|
}
|
|
994
|
-
function convertBlockquote(node, mediaUrl) {
|
|
995
|
-
const
|
|
1011
|
+
async function convertBlockquote(node, mediaUrl, customBlockConverter) {
|
|
1012
|
+
const childParts = await Promise.all(
|
|
1013
|
+
(node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
1014
|
+
);
|
|
1015
|
+
const children = childParts.join("");
|
|
996
1016
|
const style = "margin: 0 0 16px 0; padding-left: 16px; border-left: 4px solid #e5e7eb; color: #6b7280;";
|
|
997
1017
|
return `<blockquote style="${style}">${children}</blockquote>`;
|
|
998
1018
|
}
|
|
@@ -1012,8 +1032,11 @@ function convertText(node) {
|
|
|
1012
1032
|
}
|
|
1013
1033
|
return text;
|
|
1014
1034
|
}
|
|
1015
|
-
function convertLink(node, mediaUrl) {
|
|
1016
|
-
const
|
|
1035
|
+
async function convertLink(node, mediaUrl, customBlockConverter) {
|
|
1036
|
+
const childParts = await Promise.all(
|
|
1037
|
+
(node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
1038
|
+
);
|
|
1039
|
+
const children = childParts.join("");
|
|
1017
1040
|
const url = node.fields?.url || "#";
|
|
1018
1041
|
const newTab = node.fields?.newTab ?? false;
|
|
1019
1042
|
const targetAttr = newTab ? ' target="_blank"' : "";
|
|
@@ -1044,8 +1067,18 @@ function convertUpload(node, mediaUrl) {
|
|
|
1044
1067
|
}
|
|
1045
1068
|
return `<div style="margin: 0 0 16px 0; text-align: center;">${imgHtml}</div>`;
|
|
1046
1069
|
}
|
|
1047
|
-
function convertBlock(node, mediaUrl) {
|
|
1048
|
-
const blockType = node.fields?.blockName;
|
|
1070
|
+
async function convertBlock(node, mediaUrl, customBlockConverter) {
|
|
1071
|
+
const blockType = node.fields?.blockName || node.blockName;
|
|
1072
|
+
if (customBlockConverter) {
|
|
1073
|
+
try {
|
|
1074
|
+
const customHtml = await customBlockConverter(node, mediaUrl);
|
|
1075
|
+
if (customHtml) {
|
|
1076
|
+
return customHtml;
|
|
1077
|
+
}
|
|
1078
|
+
} catch (error) {
|
|
1079
|
+
console.error(`Custom block converter error for ${blockType}:`, error);
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1049
1082
|
switch (blockType) {
|
|
1050
1083
|
case "button":
|
|
1051
1084
|
return convertButtonBlock(node.fields);
|
|
@@ -1053,7 +1086,10 @@ function convertBlock(node, mediaUrl) {
|
|
|
1053
1086
|
return convertDividerBlock(node.fields);
|
|
1054
1087
|
default:
|
|
1055
1088
|
if (node.children) {
|
|
1056
|
-
|
|
1089
|
+
const childParts = await Promise.all(
|
|
1090
|
+
node.children.map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
1091
|
+
);
|
|
1092
|
+
return childParts.join("");
|
|
1057
1093
|
}
|
|
1058
1094
|
return "";
|
|
1059
1095
|
}
|
|
@@ -1687,218 +1723,11 @@ var BroadcastEditor = (props) => {
|
|
|
1687
1723
|
};
|
|
1688
1724
|
|
|
1689
1725
|
// src/components/Broadcasts/BroadcastInlinePreview.tsx
|
|
1690
|
-
import { useState as
|
|
1726
|
+
import { useState as useState8, useCallback as useCallback3 } from "react";
|
|
1691
1727
|
import { useFormFields as useFormFields3 } from "@payloadcms/ui";
|
|
1692
1728
|
|
|
1693
|
-
// src/utils/contentTransformer.ts
|
|
1694
|
-
async function transformContentForPreview(lexicalState, options = {}) {
|
|
1695
|
-
const html = await convertToEmailSafeHtml(lexicalState, {
|
|
1696
|
-
mediaUrl: options.mediaUrl
|
|
1697
|
-
});
|
|
1698
|
-
const processedHtml = processCustomBlocks(html);
|
|
1699
|
-
return processedHtml;
|
|
1700
|
-
}
|
|
1701
|
-
function processCustomBlocks(html) {
|
|
1702
|
-
return html;
|
|
1703
|
-
}
|
|
1704
|
-
|
|
1705
|
-
// src/email-templates/DefaultBroadcastTemplate.tsx
|
|
1706
|
-
import {
|
|
1707
|
-
Body,
|
|
1708
|
-
Container,
|
|
1709
|
-
Head,
|
|
1710
|
-
Hr,
|
|
1711
|
-
Html,
|
|
1712
|
-
Link,
|
|
1713
|
-
Preview,
|
|
1714
|
-
Section,
|
|
1715
|
-
Text
|
|
1716
|
-
} from "@react-email/components";
|
|
1717
|
-
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1718
|
-
var DefaultBroadcastTemplate = ({
|
|
1719
|
-
subject,
|
|
1720
|
-
preheader,
|
|
1721
|
-
content
|
|
1722
|
-
}) => {
|
|
1723
|
-
return /* @__PURE__ */ jsxs7(Html, { children: [
|
|
1724
|
-
/* @__PURE__ */ jsx7(Head, {}),
|
|
1725
|
-
/* @__PURE__ */ jsx7(Preview, { children: preheader || subject }),
|
|
1726
|
-
/* @__PURE__ */ jsx7(Body, { style: main, children: /* @__PURE__ */ jsxs7(Container, { style: container, children: [
|
|
1727
|
-
/* @__PURE__ */ jsx7(Section, { style: contentSection, children: /* @__PURE__ */ jsx7("div", { dangerouslySetInnerHTML: { __html: content } }) }),
|
|
1728
|
-
/* @__PURE__ */ jsx7(Hr, { style: divider }),
|
|
1729
|
-
/* @__PURE__ */ jsxs7(Section, { style: footer, children: [
|
|
1730
|
-
/* @__PURE__ */ jsx7(Text, { style: footerText, children: "You're receiving this email because you subscribed to our newsletter." }),
|
|
1731
|
-
/* @__PURE__ */ jsx7(Text, { style: footerText, children: /* @__PURE__ */ jsx7(Link, { href: "{{unsubscribe_url}}", style: footerLink, children: "Unsubscribe" }) })
|
|
1732
|
-
] })
|
|
1733
|
-
] }) })
|
|
1734
|
-
] });
|
|
1735
|
-
};
|
|
1736
|
-
var main = {
|
|
1737
|
-
backgroundColor: "#ffffff",
|
|
1738
|
-
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif'
|
|
1739
|
-
};
|
|
1740
|
-
var container = {
|
|
1741
|
-
margin: "0 auto",
|
|
1742
|
-
padding: "40px 20px",
|
|
1743
|
-
maxWidth: "600px"
|
|
1744
|
-
};
|
|
1745
|
-
var contentSection = {
|
|
1746
|
-
fontSize: "16px",
|
|
1747
|
-
lineHeight: "1.6",
|
|
1748
|
-
color: "#374151"
|
|
1749
|
-
};
|
|
1750
|
-
var divider = {
|
|
1751
|
-
borderColor: "#e5e7eb",
|
|
1752
|
-
margin: "40px 0 20px"
|
|
1753
|
-
};
|
|
1754
|
-
var footer = {
|
|
1755
|
-
textAlign: "center"
|
|
1756
|
-
};
|
|
1757
|
-
var footerText = {
|
|
1758
|
-
fontSize: "14px",
|
|
1759
|
-
lineHeight: "1.5",
|
|
1760
|
-
color: "#6b7280",
|
|
1761
|
-
margin: "0 0 10px"
|
|
1762
|
-
};
|
|
1763
|
-
var footerLink = {
|
|
1764
|
-
color: "#6b7280",
|
|
1765
|
-
textDecoration: "underline"
|
|
1766
|
-
};
|
|
1767
|
-
|
|
1768
|
-
// src/utils/templateLoader.ts
|
|
1769
|
-
var TemplateLoader = class {
|
|
1770
|
-
constructor() {
|
|
1771
|
-
this.loadAttempted = false;
|
|
1772
|
-
this.defaultTemplate = DefaultBroadcastTemplate;
|
|
1773
|
-
}
|
|
1774
|
-
async loadTemplate() {
|
|
1775
|
-
if (!this.loadAttempted) {
|
|
1776
|
-
this.loadAttempted = true;
|
|
1777
|
-
await this.attemptCustomTemplateLoad();
|
|
1778
|
-
}
|
|
1779
|
-
return this.customTemplate || this.defaultTemplate;
|
|
1780
|
-
}
|
|
1781
|
-
async attemptCustomTemplateLoad() {
|
|
1782
|
-
try {
|
|
1783
|
-
const customTemplatePath = `${process.cwd()}/email-templates/broadcast-template`;
|
|
1784
|
-
const module = await import(
|
|
1785
|
-
/* @vite-ignore */
|
|
1786
|
-
/* webpackIgnore: true */
|
|
1787
|
-
customTemplatePath
|
|
1788
|
-
).catch(() => null);
|
|
1789
|
-
if (module) {
|
|
1790
|
-
this.customTemplate = module.default || module.BroadcastTemplate;
|
|
1791
|
-
}
|
|
1792
|
-
} catch {
|
|
1793
|
-
}
|
|
1794
|
-
}
|
|
1795
|
-
// Reset for testing
|
|
1796
|
-
reset() {
|
|
1797
|
-
this.customTemplate = void 0;
|
|
1798
|
-
this.loadAttempted = false;
|
|
1799
|
-
}
|
|
1800
|
-
};
|
|
1801
|
-
var templateLoader = new TemplateLoader();
|
|
1802
|
-
async function loadTemplate() {
|
|
1803
|
-
return templateLoader.loadTemplate();
|
|
1804
|
-
}
|
|
1805
|
-
|
|
1806
|
-
// src/components/Broadcasts/EmailRenderer.tsx
|
|
1807
|
-
import { useEffect as useEffect5, useState as useState8, useCallback as useCallback3, useRef as useRef2 } from "react";
|
|
1808
|
-
import { render } from "@react-email/render";
|
|
1809
|
-
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1810
|
-
var EmailRenderer = ({
|
|
1811
|
-
template,
|
|
1812
|
-
data,
|
|
1813
|
-
device = "desktop",
|
|
1814
|
-
onRender
|
|
1815
|
-
}) => {
|
|
1816
|
-
const [renderedHtml, setRenderedHtml] = useState8("");
|
|
1817
|
-
const [error, setError] = useState8(null);
|
|
1818
|
-
const iframeRef = useRef2(null);
|
|
1819
|
-
const renderEmail = useCallback3(async () => {
|
|
1820
|
-
try {
|
|
1821
|
-
const TemplateComponent = template;
|
|
1822
|
-
const element = /* @__PURE__ */ jsx8(TemplateComponent, { ...data });
|
|
1823
|
-
const html = await render(element, {
|
|
1824
|
-
pretty: true
|
|
1825
|
-
});
|
|
1826
|
-
setRenderedHtml(html);
|
|
1827
|
-
onRender?.(html);
|
|
1828
|
-
setError(null);
|
|
1829
|
-
} catch (err) {
|
|
1830
|
-
setError(err);
|
|
1831
|
-
console.error("Failed to render email template:", err);
|
|
1832
|
-
}
|
|
1833
|
-
}, [template, data, onRender]);
|
|
1834
|
-
useEffect5(() => {
|
|
1835
|
-
renderEmail();
|
|
1836
|
-
}, [renderEmail]);
|
|
1837
|
-
useEffect5(() => {
|
|
1838
|
-
if (iframeRef.current && renderedHtml) {
|
|
1839
|
-
const iframe = iframeRef.current;
|
|
1840
|
-
const doc = iframe.contentDocument || iframe.contentWindow?.document;
|
|
1841
|
-
if (doc) {
|
|
1842
|
-
doc.open();
|
|
1843
|
-
doc.write(renderedHtml);
|
|
1844
|
-
doc.close();
|
|
1845
|
-
}
|
|
1846
|
-
}
|
|
1847
|
-
}, [renderedHtml]);
|
|
1848
|
-
const containerStyle = {
|
|
1849
|
-
width: "100%",
|
|
1850
|
-
height: "100%",
|
|
1851
|
-
display: "flex",
|
|
1852
|
-
alignItems: "flex-start",
|
|
1853
|
-
justifyContent: "center",
|
|
1854
|
-
overflow: "auto",
|
|
1855
|
-
padding: "2rem",
|
|
1856
|
-
boxSizing: "border-box"
|
|
1857
|
-
};
|
|
1858
|
-
const iframeStyle = {
|
|
1859
|
-
width: device === "mobile" ? "375px" : "600px",
|
|
1860
|
-
height: "100%",
|
|
1861
|
-
minHeight: "600px",
|
|
1862
|
-
background: "white",
|
|
1863
|
-
boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
|
|
1864
|
-
borderRadius: device === "mobile" ? "20px" : "8px",
|
|
1865
|
-
border: "none",
|
|
1866
|
-
display: "block"
|
|
1867
|
-
};
|
|
1868
|
-
const errorStyle = {
|
|
1869
|
-
background: "white",
|
|
1870
|
-
border: "1px solid #ef4444",
|
|
1871
|
-
borderRadius: "4px",
|
|
1872
|
-
padding: "2rem",
|
|
1873
|
-
maxWidth: "500px"
|
|
1874
|
-
};
|
|
1875
|
-
if (error) {
|
|
1876
|
-
return /* @__PURE__ */ jsxs8("div", { style: errorStyle, children: [
|
|
1877
|
-
/* @__PURE__ */ jsx8("h3", { style: { color: "#ef4444", margin: "0 0 1rem" }, children: "Template Render Error" }),
|
|
1878
|
-
/* @__PURE__ */ jsx8("pre", { style: {
|
|
1879
|
-
background: "#f9fafb",
|
|
1880
|
-
padding: "1rem",
|
|
1881
|
-
borderRadius: "4px",
|
|
1882
|
-
overflowX: "auto",
|
|
1883
|
-
fontSize: "12px",
|
|
1884
|
-
color: "#374151",
|
|
1885
|
-
margin: 0
|
|
1886
|
-
}, children: error.message })
|
|
1887
|
-
] });
|
|
1888
|
-
}
|
|
1889
|
-
return /* @__PURE__ */ jsx8("div", { style: containerStyle, children: /* @__PURE__ */ jsx8(
|
|
1890
|
-
"iframe",
|
|
1891
|
-
{
|
|
1892
|
-
ref: iframeRef,
|
|
1893
|
-
style: iframeStyle,
|
|
1894
|
-
sandbox: "allow-same-origin",
|
|
1895
|
-
title: "Email Preview"
|
|
1896
|
-
}
|
|
1897
|
-
) });
|
|
1898
|
-
};
|
|
1899
|
-
|
|
1900
1729
|
// src/components/Broadcasts/PreviewControls.tsx
|
|
1901
|
-
import { jsx as
|
|
1730
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1902
1731
|
var PreviewControls = ({
|
|
1903
1732
|
onUpdate,
|
|
1904
1733
|
device,
|
|
@@ -1940,8 +1769,8 @@ var PreviewControls = ({
|
|
|
1940
1769
|
cursor: "pointer",
|
|
1941
1770
|
fontSize: "14px"
|
|
1942
1771
|
});
|
|
1943
|
-
return /* @__PURE__ */
|
|
1944
|
-
/* @__PURE__ */
|
|
1772
|
+
return /* @__PURE__ */ jsxs7("div", { style: controlsStyle, children: [
|
|
1773
|
+
/* @__PURE__ */ jsx7(
|
|
1945
1774
|
"button",
|
|
1946
1775
|
{
|
|
1947
1776
|
style: updateButtonStyle,
|
|
@@ -1950,33 +1779,33 @@ var PreviewControls = ({
|
|
|
1950
1779
|
children: isLoading ? "Updating..." : "Update Preview"
|
|
1951
1780
|
}
|
|
1952
1781
|
),
|
|
1953
|
-
/* @__PURE__ */
|
|
1954
|
-
/* @__PURE__ */
|
|
1782
|
+
/* @__PURE__ */ jsxs7("div", { style: deviceSelectorStyle, children: [
|
|
1783
|
+
/* @__PURE__ */ jsxs7(
|
|
1955
1784
|
"button",
|
|
1956
1785
|
{
|
|
1957
1786
|
style: deviceButtonStyle(device === "desktop"),
|
|
1958
1787
|
onClick: () => onDeviceChange("desktop"),
|
|
1959
1788
|
"aria-label": "Desktop view",
|
|
1960
1789
|
children: [
|
|
1961
|
-
/* @__PURE__ */
|
|
1962
|
-
/* @__PURE__ */
|
|
1963
|
-
/* @__PURE__ */
|
|
1964
|
-
/* @__PURE__ */
|
|
1790
|
+
/* @__PURE__ */ jsxs7("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
1791
|
+
/* @__PURE__ */ jsx7("rect", { x: "2", y: "3", width: "20", height: "14", rx: "2", ry: "2" }),
|
|
1792
|
+
/* @__PURE__ */ jsx7("line", { x1: "8", y1: "21", x2: "16", y2: "21" }),
|
|
1793
|
+
/* @__PURE__ */ jsx7("line", { x1: "12", y1: "17", x2: "12", y2: "21" })
|
|
1965
1794
|
] }),
|
|
1966
1795
|
"Desktop"
|
|
1967
1796
|
]
|
|
1968
1797
|
}
|
|
1969
1798
|
),
|
|
1970
|
-
/* @__PURE__ */
|
|
1799
|
+
/* @__PURE__ */ jsxs7(
|
|
1971
1800
|
"button",
|
|
1972
1801
|
{
|
|
1973
1802
|
style: deviceButtonStyle(device === "mobile"),
|
|
1974
1803
|
onClick: () => onDeviceChange("mobile"),
|
|
1975
1804
|
"aria-label": "Mobile view",
|
|
1976
1805
|
children: [
|
|
1977
|
-
/* @__PURE__ */
|
|
1978
|
-
/* @__PURE__ */
|
|
1979
|
-
/* @__PURE__ */
|
|
1806
|
+
/* @__PURE__ */ jsxs7("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
1807
|
+
/* @__PURE__ */ jsx7("rect", { x: "5", y: "2", width: "14", height: "20", rx: "2", ry: "2" }),
|
|
1808
|
+
/* @__PURE__ */ jsx7("line", { x1: "12", y1: "18", x2: "12", y2: "18" })
|
|
1980
1809
|
] }),
|
|
1981
1810
|
"Mobile"
|
|
1982
1811
|
]
|
|
@@ -1987,19 +1816,19 @@ var PreviewControls = ({
|
|
|
1987
1816
|
};
|
|
1988
1817
|
|
|
1989
1818
|
// src/components/Broadcasts/BroadcastInlinePreview.tsx
|
|
1990
|
-
import { Fragment as Fragment2, jsx as
|
|
1819
|
+
import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1991
1820
|
var BroadcastInlinePreview = () => {
|
|
1992
|
-
const [device, setDevice] =
|
|
1993
|
-
const [isLoading, setIsLoading] =
|
|
1994
|
-
const [showPreview, setShowPreview] =
|
|
1995
|
-
const [
|
|
1996
|
-
const [error, setError] =
|
|
1821
|
+
const [device, setDevice] = useState8("desktop");
|
|
1822
|
+
const [isLoading, setIsLoading] = useState8(false);
|
|
1823
|
+
const [showPreview, setShowPreview] = useState8(false);
|
|
1824
|
+
const [previewHtml, setPreviewHtml] = useState8(null);
|
|
1825
|
+
const [error, setError] = useState8(null);
|
|
1997
1826
|
const fields = useFormFields3(([fields2]) => ({
|
|
1998
1827
|
subject: fields2["subject"]?.value,
|
|
1999
1828
|
preheader: fields2["contentSection.preheader"]?.value,
|
|
2000
1829
|
content: fields2["contentSection.content"]?.value
|
|
2001
1830
|
}));
|
|
2002
|
-
const updatePreview =
|
|
1831
|
+
const updatePreview = useCallback3(async () => {
|
|
2003
1832
|
if (!fields.content) {
|
|
2004
1833
|
setError(new Error("Please add some content before previewing"));
|
|
2005
1834
|
return;
|
|
@@ -2007,18 +1836,22 @@ var BroadcastInlinePreview = () => {
|
|
|
2007
1836
|
setIsLoading(true);
|
|
2008
1837
|
setError(null);
|
|
2009
1838
|
try {
|
|
2010
|
-
const
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
}
|
|
1839
|
+
const response = await fetch("/api/broadcasts/preview", {
|
|
1840
|
+
method: "POST",
|
|
1841
|
+
headers: {
|
|
1842
|
+
"Content-Type": "application/json"
|
|
1843
|
+
},
|
|
1844
|
+
body: JSON.stringify({
|
|
1845
|
+
content: fields.content,
|
|
1846
|
+
preheader: fields.preheader,
|
|
1847
|
+
subject: fields.subject
|
|
1848
|
+
})
|
|
2021
1849
|
});
|
|
1850
|
+
const data = await response.json();
|
|
1851
|
+
if (!response.ok || !data.success) {
|
|
1852
|
+
throw new Error(data.error || "Failed to generate preview");
|
|
1853
|
+
}
|
|
1854
|
+
setPreviewHtml(data.preview.html);
|
|
2022
1855
|
setShowPreview(true);
|
|
2023
1856
|
} catch (err) {
|
|
2024
1857
|
setError(err);
|
|
@@ -2070,10 +1903,10 @@ var BroadcastInlinePreview = () => {
|
|
|
2070
1903
|
fontSize: "14px",
|
|
2071
1904
|
fontWeight: 500
|
|
2072
1905
|
};
|
|
2073
|
-
return /* @__PURE__ */
|
|
2074
|
-
/* @__PURE__ */
|
|
2075
|
-
/* @__PURE__ */
|
|
2076
|
-
/* @__PURE__ */
|
|
1906
|
+
return /* @__PURE__ */ jsxs8("div", { style: containerStyle, children: [
|
|
1907
|
+
/* @__PURE__ */ jsxs8("div", { style: headerStyle, children: [
|
|
1908
|
+
/* @__PURE__ */ jsx8("h3", { style: titleStyle, children: "Email Preview" }),
|
|
1909
|
+
/* @__PURE__ */ jsx8(
|
|
2077
1910
|
"button",
|
|
2078
1911
|
{
|
|
2079
1912
|
onClick: () => showPreview ? setShowPreview(false) : updatePreview(),
|
|
@@ -2083,9 +1916,9 @@ var BroadcastInlinePreview = () => {
|
|
|
2083
1916
|
}
|
|
2084
1917
|
)
|
|
2085
1918
|
] }),
|
|
2086
|
-
showPreview && /* @__PURE__ */
|
|
2087
|
-
/* @__PURE__ */
|
|
2088
|
-
/* @__PURE__ */
|
|
1919
|
+
showPreview && /* @__PURE__ */ jsx8("div", { style: previewContainerStyle, children: error ? /* @__PURE__ */ jsxs8("div", { style: errorStyle, children: [
|
|
1920
|
+
/* @__PURE__ */ jsx8("p", { style: { color: "#ef4444", margin: "0 0 1rem" }, children: error.message }),
|
|
1921
|
+
/* @__PURE__ */ jsx8(
|
|
2089
1922
|
"button",
|
|
2090
1923
|
{
|
|
2091
1924
|
onClick: updatePreview,
|
|
@@ -2100,8 +1933,8 @@ var BroadcastInlinePreview = () => {
|
|
|
2100
1933
|
children: "Retry"
|
|
2101
1934
|
}
|
|
2102
1935
|
)
|
|
2103
|
-
] }) :
|
|
2104
|
-
/* @__PURE__ */
|
|
1936
|
+
] }) : previewHtml ? /* @__PURE__ */ jsxs8(Fragment2, { children: [
|
|
1937
|
+
/* @__PURE__ */ jsx8(
|
|
2105
1938
|
PreviewControls,
|
|
2106
1939
|
{
|
|
2107
1940
|
onUpdate: updatePreview,
|
|
@@ -2110,12 +1943,42 @@ var BroadcastInlinePreview = () => {
|
|
|
2110
1943
|
isLoading
|
|
2111
1944
|
}
|
|
2112
1945
|
),
|
|
2113
|
-
/* @__PURE__ */
|
|
2114
|
-
|
|
1946
|
+
/* @__PURE__ */ jsx8(
|
|
1947
|
+
"div",
|
|
2115
1948
|
{
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
1949
|
+
style: {
|
|
1950
|
+
flex: 1,
|
|
1951
|
+
padding: device === "mobile" ? "1rem" : "2rem",
|
|
1952
|
+
display: "flex",
|
|
1953
|
+
justifyContent: "center",
|
|
1954
|
+
overflow: "auto"
|
|
1955
|
+
},
|
|
1956
|
+
children: /* @__PURE__ */ jsx8(
|
|
1957
|
+
"div",
|
|
1958
|
+
{
|
|
1959
|
+
style: {
|
|
1960
|
+
width: device === "mobile" ? "375px" : "600px",
|
|
1961
|
+
maxWidth: "100%",
|
|
1962
|
+
background: "white",
|
|
1963
|
+
boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
|
|
1964
|
+
borderRadius: "8px",
|
|
1965
|
+
overflow: "hidden"
|
|
1966
|
+
},
|
|
1967
|
+
children: /* @__PURE__ */ jsx8(
|
|
1968
|
+
"iframe",
|
|
1969
|
+
{
|
|
1970
|
+
srcDoc: previewHtml,
|
|
1971
|
+
style: {
|
|
1972
|
+
width: "100%",
|
|
1973
|
+
height: "100%",
|
|
1974
|
+
minHeight: "600px",
|
|
1975
|
+
border: "none"
|
|
1976
|
+
},
|
|
1977
|
+
title: "Email Preview"
|
|
1978
|
+
}
|
|
1979
|
+
)
|
|
1980
|
+
}
|
|
1981
|
+
)
|
|
2119
1982
|
}
|
|
2120
1983
|
)
|
|
2121
1984
|
] }) : null })
|
|
@@ -2123,9 +1986,9 @@ var BroadcastInlinePreview = () => {
|
|
|
2123
1986
|
};
|
|
2124
1987
|
|
|
2125
1988
|
// src/components/Broadcasts/BroadcastPreviewField.tsx
|
|
2126
|
-
import { jsx as
|
|
1989
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
2127
1990
|
var BroadcastPreviewField = () => {
|
|
2128
|
-
return /* @__PURE__ */
|
|
1991
|
+
return /* @__PURE__ */ jsx9("div", { style: {
|
|
2129
1992
|
padding: "1rem",
|
|
2130
1993
|
background: "#f9fafb",
|
|
2131
1994
|
borderRadius: "4px",
|
|
@@ -2134,8 +1997,102 @@ var BroadcastPreviewField = () => {
|
|
|
2134
1997
|
}, children: "Email preview is available inline below the content editor." });
|
|
2135
1998
|
};
|
|
2136
1999
|
|
|
2000
|
+
// src/components/Broadcasts/EmailRenderer.tsx
|
|
2001
|
+
import { useEffect as useEffect5, useState as useState9, useCallback as useCallback4, useRef as useRef2 } from "react";
|
|
2002
|
+
import { render } from "@react-email/render";
|
|
2003
|
+
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2004
|
+
var EmailRenderer = ({
|
|
2005
|
+
template,
|
|
2006
|
+
data,
|
|
2007
|
+
device = "desktop",
|
|
2008
|
+
onRender
|
|
2009
|
+
}) => {
|
|
2010
|
+
const [renderedHtml, setRenderedHtml] = useState9("");
|
|
2011
|
+
const [error, setError] = useState9(null);
|
|
2012
|
+
const iframeRef = useRef2(null);
|
|
2013
|
+
const renderEmail = useCallback4(async () => {
|
|
2014
|
+
try {
|
|
2015
|
+
const TemplateComponent = template;
|
|
2016
|
+
const element = /* @__PURE__ */ jsx10(TemplateComponent, { ...data });
|
|
2017
|
+
const html = await render(element, {
|
|
2018
|
+
pretty: true
|
|
2019
|
+
});
|
|
2020
|
+
setRenderedHtml(html);
|
|
2021
|
+
onRender?.(html);
|
|
2022
|
+
setError(null);
|
|
2023
|
+
} catch (err) {
|
|
2024
|
+
setError(err);
|
|
2025
|
+
console.error("Failed to render email template:", err);
|
|
2026
|
+
}
|
|
2027
|
+
}, [template, data, onRender]);
|
|
2028
|
+
useEffect5(() => {
|
|
2029
|
+
renderEmail();
|
|
2030
|
+
}, [renderEmail]);
|
|
2031
|
+
useEffect5(() => {
|
|
2032
|
+
if (iframeRef.current && renderedHtml) {
|
|
2033
|
+
const iframe = iframeRef.current;
|
|
2034
|
+
const doc = iframe.contentDocument || iframe.contentWindow?.document;
|
|
2035
|
+
if (doc) {
|
|
2036
|
+
doc.open();
|
|
2037
|
+
doc.write(renderedHtml);
|
|
2038
|
+
doc.close();
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
}, [renderedHtml]);
|
|
2042
|
+
const containerStyle = {
|
|
2043
|
+
width: "100%",
|
|
2044
|
+
height: "100%",
|
|
2045
|
+
display: "flex",
|
|
2046
|
+
alignItems: "flex-start",
|
|
2047
|
+
justifyContent: "center",
|
|
2048
|
+
overflow: "auto",
|
|
2049
|
+
padding: "2rem",
|
|
2050
|
+
boxSizing: "border-box"
|
|
2051
|
+
};
|
|
2052
|
+
const iframeStyle = {
|
|
2053
|
+
width: device === "mobile" ? "375px" : "600px",
|
|
2054
|
+
height: "100%",
|
|
2055
|
+
minHeight: "600px",
|
|
2056
|
+
background: "white",
|
|
2057
|
+
boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
|
|
2058
|
+
borderRadius: device === "mobile" ? "20px" : "8px",
|
|
2059
|
+
border: "none",
|
|
2060
|
+
display: "block"
|
|
2061
|
+
};
|
|
2062
|
+
const errorStyle = {
|
|
2063
|
+
background: "white",
|
|
2064
|
+
border: "1px solid #ef4444",
|
|
2065
|
+
borderRadius: "4px",
|
|
2066
|
+
padding: "2rem",
|
|
2067
|
+
maxWidth: "500px"
|
|
2068
|
+
};
|
|
2069
|
+
if (error) {
|
|
2070
|
+
return /* @__PURE__ */ jsxs9("div", { style: errorStyle, children: [
|
|
2071
|
+
/* @__PURE__ */ jsx10("h3", { style: { color: "#ef4444", margin: "0 0 1rem" }, children: "Template Render Error" }),
|
|
2072
|
+
/* @__PURE__ */ jsx10("pre", { style: {
|
|
2073
|
+
background: "#f9fafb",
|
|
2074
|
+
padding: "1rem",
|
|
2075
|
+
borderRadius: "4px",
|
|
2076
|
+
overflowX: "auto",
|
|
2077
|
+
fontSize: "12px",
|
|
2078
|
+
color: "#374151",
|
|
2079
|
+
margin: 0
|
|
2080
|
+
}, children: error.message })
|
|
2081
|
+
] });
|
|
2082
|
+
}
|
|
2083
|
+
return /* @__PURE__ */ jsx10("div", { style: containerStyle, children: /* @__PURE__ */ jsx10(
|
|
2084
|
+
"iframe",
|
|
2085
|
+
{
|
|
2086
|
+
ref: iframeRef,
|
|
2087
|
+
style: iframeStyle,
|
|
2088
|
+
sandbox: "allow-same-origin",
|
|
2089
|
+
title: "Email Preview"
|
|
2090
|
+
}
|
|
2091
|
+
) });
|
|
2092
|
+
};
|
|
2093
|
+
|
|
2137
2094
|
// src/components/Broadcasts/StatusBadge.tsx
|
|
2138
|
-
import { jsx as
|
|
2095
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
2139
2096
|
var statusConfig = {
|
|
2140
2097
|
["draft" /* DRAFT */]: {
|
|
2141
2098
|
label: "Draft",
|
|
@@ -2183,7 +2140,7 @@ var statusConfig = {
|
|
|
2183
2140
|
var StatusBadge = ({ cellData }) => {
|
|
2184
2141
|
const status = cellData;
|
|
2185
2142
|
const config = statusConfig[status] || statusConfig["draft" /* DRAFT */];
|
|
2186
|
-
return /* @__PURE__ */
|
|
2143
|
+
return /* @__PURE__ */ jsx11(
|
|
2187
2144
|
"span",
|
|
2188
2145
|
{
|
|
2189
2146
|
style: {
|
|
@@ -2205,6 +2162,69 @@ var StatusBadge = ({ cellData }) => {
|
|
|
2205
2162
|
var EmptyField = () => {
|
|
2206
2163
|
return null;
|
|
2207
2164
|
};
|
|
2165
|
+
|
|
2166
|
+
// src/email-templates/DefaultBroadcastTemplate.tsx
|
|
2167
|
+
import {
|
|
2168
|
+
Body,
|
|
2169
|
+
Container,
|
|
2170
|
+
Head,
|
|
2171
|
+
Hr,
|
|
2172
|
+
Html,
|
|
2173
|
+
Link,
|
|
2174
|
+
Preview,
|
|
2175
|
+
Section,
|
|
2176
|
+
Text
|
|
2177
|
+
} from "@react-email/components";
|
|
2178
|
+
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2179
|
+
var DefaultBroadcastTemplate = ({
|
|
2180
|
+
subject,
|
|
2181
|
+
preheader,
|
|
2182
|
+
content
|
|
2183
|
+
}) => {
|
|
2184
|
+
return /* @__PURE__ */ jsxs10(Html, { children: [
|
|
2185
|
+
/* @__PURE__ */ jsx12(Head, {}),
|
|
2186
|
+
/* @__PURE__ */ jsx12(Preview, { children: preheader || subject }),
|
|
2187
|
+
/* @__PURE__ */ jsx12(Body, { style: main, children: /* @__PURE__ */ jsxs10(Container, { style: container, children: [
|
|
2188
|
+
/* @__PURE__ */ jsx12(Section, { style: contentSection, children: /* @__PURE__ */ jsx12("div", { dangerouslySetInnerHTML: { __html: content } }) }),
|
|
2189
|
+
/* @__PURE__ */ jsx12(Hr, { style: divider }),
|
|
2190
|
+
/* @__PURE__ */ jsxs10(Section, { style: footer, children: [
|
|
2191
|
+
/* @__PURE__ */ jsx12(Text, { style: footerText, children: "You're receiving this email because you subscribed to our newsletter." }),
|
|
2192
|
+
/* @__PURE__ */ jsx12(Text, { style: footerText, children: /* @__PURE__ */ jsx12(Link, { href: "{{unsubscribe_url}}", style: footerLink, children: "Unsubscribe" }) })
|
|
2193
|
+
] })
|
|
2194
|
+
] }) })
|
|
2195
|
+
] });
|
|
2196
|
+
};
|
|
2197
|
+
var main = {
|
|
2198
|
+
backgroundColor: "#ffffff",
|
|
2199
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif'
|
|
2200
|
+
};
|
|
2201
|
+
var container = {
|
|
2202
|
+
margin: "0 auto",
|
|
2203
|
+
padding: "40px 20px",
|
|
2204
|
+
maxWidth: "600px"
|
|
2205
|
+
};
|
|
2206
|
+
var contentSection = {
|
|
2207
|
+
fontSize: "16px",
|
|
2208
|
+
lineHeight: "1.6",
|
|
2209
|
+
color: "#374151"
|
|
2210
|
+
};
|
|
2211
|
+
var divider = {
|
|
2212
|
+
borderColor: "#e5e7eb",
|
|
2213
|
+
margin: "40px 0 20px"
|
|
2214
|
+
};
|
|
2215
|
+
var footer = {
|
|
2216
|
+
textAlign: "center"
|
|
2217
|
+
};
|
|
2218
|
+
var footerText = {
|
|
2219
|
+
fontSize: "14px",
|
|
2220
|
+
lineHeight: "1.5",
|
|
2221
|
+
color: "#6b7280",
|
|
2222
|
+
margin: "0 0 10px"
|
|
2223
|
+
};
|
|
2224
|
+
var footerLink = {
|
|
2225
|
+
color: "#6b7280",
|
|
2226
|
+
textDecoration: "underline"
|
|
2227
|
+
};
|
|
2208
2228
|
export {
|
|
2209
2229
|
BroadcastEditor,
|
|
2210
2230
|
BroadcastInlinePreview,
|