strapi-plugin-email-designer-v5
Version:
Strapi Email Designer (v5)
954 lines (953 loc) • 43.4 kB
JavaScript
;
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const jsxRuntime = require("react/jsx-runtime");
const admin = require("@strapi/strapi/admin");
const reactRouterDom = require("react-router-dom");
const strapiAdmin = require("@strapi/admin/strapi-admin");
const designSystem = require("@strapi/design-system");
const icons = require("@strapi/icons");
const lodash = require("lodash");
const react = require("react");
const reactEmailEditor = require("react-email-editor");
const striptags = require("striptags");
const styled = require("styled-components");
const destr = require("destr");
const reactIntl = require("react-intl");
const index = require("./index-DSvb0BFI.js");
const dayjs = require("dayjs");
const symbols = require("@strapi/icons/symbols");
const bs = require("react-icons/bs");
const fa6 = require("react-icons/fa6");
const lu = require("react-icons/lu");
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
const striptags__default = /* @__PURE__ */ _interopDefault(striptags);
const styled__default = /* @__PURE__ */ _interopDefault(styled);
const destr__default = /* @__PURE__ */ _interopDefault(destr);
const dayjs__default = /* @__PURE__ */ _interopDefault(dayjs);
const getTranslation = (id) => `${index.PLUGIN_ID}.${id}`;
const useTr = () => {
const { formatMessage } = reactIntl.useIntl();
return (key) => formatMessage({ id: getTranslation(key) });
};
const ImportSingleDesign = ({ emailEditorRef }) => {
const translate = useTr();
const { toggleNotification } = admin.useNotification();
const hiddenInput = react.useRef(null);
const [showModal, setShowModal] = react.useState(false);
const [template, setTemplate] = react.useState();
const handleFileChange = (event) => {
if (!event) return;
const files = event.target.files;
if (!files || files.length === 0) return;
const file = files[0];
if (!file) return;
const fr = new FileReader();
fr.onload = async () => {
setTemplate(destr__default.default(fr.result));
setShowModal(true);
if (hiddenInput.current) hiddenInput.current.value = "";
};
fr.readAsText(file);
};
const handleLoadSingleDesign = () => {
if (emailEditorRef?.current) {
try {
emailEditorRef.current?.editor?.loadDesign(template);
toggleNotification({
type: "success",
title: translate("success"),
message: translate("success.single.message")
});
} catch (error) {
toggleNotification({
type: "danger",
title: translate("error"),
message: error.message
});
}
}
};
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
/* @__PURE__ */ jsxRuntime.jsx(
"input",
{
type: "file",
multiple: false,
onChange: handleFileChange,
accept: ".json",
hidden: true,
ref: hiddenInput
}
),
/* @__PURE__ */ jsxRuntime.jsx(
designSystem.Button,
{
startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.CloudUpload, {}),
onClick: () => hiddenInput.current?.click(),
style: { marginTop: "19px", height: "38px", width: "100%" },
variant: "tertiary",
children: translate("import")
}
),
/* @__PURE__ */ jsxRuntime.jsx(
designSystem.Dialog.Root,
{
open: showModal,
onOpenChange: () => {
setShowModal((s) => !s);
},
children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: translate("confirm.title") }),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { children: translate("confirm.singleImport") }),
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { fullWidth: true, variant: "tertiary", children: translate("cancel") }) }),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Action, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: () => handleLoadSingleDesign(), fullWidth: true, variant: "success-light", children: translate("confirm") }) })
] })
] })
}
)
] });
};
const getUrl = (to) => to ? `/plugins/${index.PLUGIN_ID}/${to}` : `/plugins/${index.PLUGIN_ID}`;
const standardEmailRegistrationTemplate = {
counters: { u_row: 2, u_content_text: 1, u_content_image: 1, u_column: 2 },
body: {
values: {
backgroundColor: "#ffffff",
linkStyle: {
body: true,
linkHoverColor: "#0000ee",
linkHoverUnderline: true,
linkColor: "#0000ee",
linkUnderline: true
},
contentWidth: "500px",
backgroundImage: { repeat: false, center: true, fullWidth: true, url: "", cover: false },
contentAlign: "center",
textColor: "#000000",
_meta: { htmlID: "u_body", htmlClassNames: "u_body" },
fontFamily: { label: "Arial", value: "arial,helvetica,sans-serif" },
preheaderText: ""
},
rows: [
{
cells: [1],
values: {
backgroundImage: { cover: false, url: "", repeat: false, fullWidth: true, center: true },
hideDesktop: false,
selectable: true,
columnsBackgroundColor: "",
hideable: true,
backgroundColor: "",
padding: "0px",
columns: false,
_meta: { htmlID: "u_row_2", htmlClassNames: "u_row" },
deletable: true,
displayCondition: null,
duplicatable: true,
draggable: true
},
columns: [
{
contents: [
{
values: {
hideDesktop: false,
duplicatable: true,
deletable: true,
linkStyle: {
linkHoverUnderline: true,
linkColor: "#0000ee",
inherit: true,
linkUnderline: true,
linkHoverColor: "#0000ee"
},
hideable: true,
lineHeight: "140%",
draggable: true,
containerPadding: "10px",
text: '<p style="font-size: 14px; line-height: 140%; text-align: center;"><span style="font-size: 14px; line-height: 19.6px;">__PLACEHOLDER__</span></p>',
_meta: { htmlID: "u_content_text_1", htmlClassNames: "u_content_text" },
textAlign: "left",
selectable: true
},
type: "text"
}
],
values: {
border: {},
_meta: { htmlClassNames: "u_column", htmlID: "u_column_2" },
backgroundColor: "",
padding: "0px"
}
}
]
}
]
},
schemaVersion: 6
};
const { post, get, del } = admin.getFetchClient();
const DATE_FORMAT = "MMM DD, YYYY [at] h:mmA";
const getTemplatesData = async () => {
const { data } = await get(`/${index.pluginName}/templates`);
data.forEach((template) => {
template.createdAt = dayjs__default.default(template.createdAt).format(DATE_FORMAT);
template.updatedAt = dayjs__default.default(template.updatedAt).format(DATE_FORMAT);
});
return data;
};
const getFullEditorConfig = async () => {
const { data } = await get(`/${index.pluginName}/config`);
return data;
};
const getTemplateById = async (id) => {
const { data } = await get(`/${index.pluginName}/templates/${id}`);
return data;
};
const getCoreTemplate = async (coreEmailType) => {
const { data } = await get(`/${index.pluginName}/core/${coreEmailType}`);
return data;
};
const createTemplate = async (templateId, data) => {
const { data: response } = await post(`/${index.pluginName}/templates/${templateId}`, data);
return response;
};
const updateCoreTemplate = async (coreEmailType, data) => {
const { data: response } = await post(`/${index.pluginName}/core/${coreEmailType}`, data);
return response;
};
const duplicateTemplate = async (id) => {
const { data } = await post(`/${index.pluginName}/templates/duplicate/${id}`);
return data;
};
const deleteTemplate = async (id) => {
await del(`/${index.pluginName}/templates/${id}`);
};
const downloadTemplate = async (id, type) => {
const { data } = await get(`/${index.pluginName}/download/${id}`, {
params: { type },
headers: {
Accept: "application/octet-stream"
}
});
const blob = new Blob([data], { type: type === "json" ? "application/json" : "text/html" });
const downloadUrl = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = downloadUrl;
const fileName = `template-${id}.${type}`;
link.setAttribute("download", fileName);
document.body.appendChild(link);
link.click();
link.remove();
window.URL.revokeObjectURL(downloadUrl);
};
const __DEV__ = process.env.NODE_ENV === "development";
function shallowIsEqual(object1, object2) {
const keys1 = Object.keys(object1);
const keys2 = Object.keys(object2);
if (keys1.length !== keys2.length) {
if (__DEV__) console.log("shallowIsEqual: keys1.length !== keys2.length");
return false;
}
for (const key of keys1) {
if (object1[key] !== object2[key]) {
if (__DEV__) console.log(`shallowIsEqual: ${key}`, object1[key], " !== ", object2[key]);
return false;
}
}
return true;
}
const DesignerContainer = styled__default.default.div`
padding: 18px 30px;
min-height: 100vh;
display: flex;
flex-direction: column;
gap: 10px;
`;
const Header = styled__default.default.div`
display: flex;
flex-shrink: 0;
width: 100%;
height: 60px;
align-items: center;
gap: 10px;
`;
const Designer = ({ isCore = false }) => {
const emailEditorRef = react.useRef(null);
const navigate = reactRouterDom.useNavigate();
const translate = useTr();
const { templateId, coreEmailType } = reactRouterDom.useParams();
const [templateData, setTemplateData] = react.useState();
const [errorRefId, setErrorRefId] = react.useState("");
const [bodyText, setBodyText] = react.useState("");
const [mode, setMode] = react.useState("html");
const [serverConfigLoaded, setServerConfigLoaded] = react.useState(false);
const [editorOptions, setEditorOptions] = react.useState();
const { toggleNotification } = admin.useNotification();
const saveDesign = async () => {
if (!coreEmailType && !templateData?.templateReferenceId) {
toggleNotification({
type: "danger",
title: translate("error.noReferenceId.title"),
message: translate("error.noReferenceId.message")
});
setErrorRefId("Required");
return;
}
setErrorRefId("");
let design, html, response;
try {
await new Promise((resolve) => {
emailEditorRef.current?.editor?.exportHtml((data) => {
({ design, html } = data);
resolve();
});
});
} catch (error) {
console.log(error);
return;
}
try {
if (templateId) {
response = await createTemplate(templateId, {
name: templateData?.name || translate("noName"),
templateReferenceId: templateData?.templateReferenceId,
subject: templateData?.subject || "",
design,
bodyText,
bodyHtml: html
});
} else if (coreEmailType) {
response = await updateCoreTemplate(coreEmailType, {
subject: templateData?.subject || "",
design,
message: html,
bodyText
});
}
toggleNotification({
type: "success",
title: translate("success"),
message: translate("success.message")
});
if (templateId === "new") navigate(`/plugins/${index.PLUGIN_ID}/design/${response?.id}`);
} catch (err) {
console.error(err);
const errorMessage = err?.response?.data?.error?.message;
if (errorMessage) {
toggleNotification({
type: "danger",
title: translate("error"),
message: errorMessage
});
} else {
toggleNotification({
type: "danger",
title: translate("error"),
message: errorMessage
});
}
}
};
const onLoadHandler = react.useCallback(() => {
setTimeout(() => {
if (templateData) emailEditorRef.current?.editor?.loadDesign(templateData.design);
}, 500);
}, []);
const init = async () => {
if (!templateId && !coreEmailType || coreEmailType && !["user-address-confirmation", "reset-password"].includes(coreEmailType) || templateId === "new")
return;
let _templateData = {};
if (templateId) {
_templateData = await getTemplateById(templateId);
} else if (coreEmailType) {
_templateData = await getCoreTemplate(coreEmailType);
}
if (coreEmailType && lodash.isEmpty(_templateData.design)) {
let _message = _templateData.message || "";
if (_templateData.message && _templateData.message.match(/\<body/)) {
const parser = new DOMParser();
const parsedDocument = parser.parseFromString(_message, "text/html");
_message = parsedDocument.body.innerText;
}
_message = striptags__default.default(_message, ["a", "img", "strong", "b", "i", "%", "%="]).replace(/"/g, "'").replace(/<%|<%/g, "{{").replace(/%>|%>/g, "}}").replace(/\n/g, "<br />");
_templateData.design = JSON.parse(
JSON.stringify(standardEmailRegistrationTemplate).replace("__PLACEHOLDER__", _message)
);
}
setTemplateData(_templateData);
setBodyText(_templateData.bodyText || "");
};
react.useEffect(() => {
getFullEditorConfig().then((config) => {
setEditorOptions(config);
setServerConfigLoaded(true);
});
return () => {
emailEditorRef.current?.editor?.destroy();
};
}, []);
react.useEffect(() => {
init();
}, [templateId, coreEmailType]);
react.useEffect(() => {
setTimeout(() => {
if (emailEditorRef.current?.editor && templateData?.design) {
emailEditorRef.current.editor.loadDesign(templateData.design);
}
}, 600);
}, [templateData]);
return /* @__PURE__ */ jsxRuntime.jsxs(strapiAdmin.Page.Main, { children: [
/* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Page.Title, { children: translate("page.design.title") }),
/* @__PURE__ */ jsxRuntime.jsxs(DesignerContainer, { children: [
/* @__PURE__ */ jsxRuntime.jsxs(Header, { children: [
/* @__PURE__ */ jsxRuntime.jsx(
designSystem.IconButton,
{
style: { marginTop: "19px", padding: "10px" },
label: translate("goBack"),
onClick: () => navigate({ pathname: getUrl() }),
children: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowLeft, {})
}
),
!isCore && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: "100%", maxWidth: "150px" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { required: true, error: errorRefId, children: [
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: translate("input.label.templateReferenceId") }),
/* @__PURE__ */ jsxRuntime.jsx(
designSystem.Field.Input,
{
onChange: (e) => setTemplateData((state) => ({
...state || {},
templateReferenceId: e.target.value === "" ? "" : isFinite(parseInt(e.target.value)) ? parseInt(e.target.value) : state?.templateReferenceId ?? ""
})),
value: templateData?.templateReferenceId ?? "",
type: "number",
placeholder: translate("input.placeholder.templateReferenceId")
}
),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, {})
] }) }),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { disabled: isCore, required: true, children: [
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: translate("input.label.templateName") }),
/* @__PURE__ */ jsxRuntime.jsx(
designSystem.Field.Input,
{
disabled: isCore,
value: isCore && coreEmailType ? translate(coreEmailType) : templateData?.name || "",
onChange: (e) => {
setTemplateData((state) => ({ ...state, name: e.target.value }));
},
placeholder: translate("input.placeholder.templateName")
}
),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, {})
] }) }),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { required: true, children: [
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: translate("input.label.subject") }),
/* @__PURE__ */ jsxRuntime.jsx(
designSystem.Field.Input,
{
onChange: (value) => {
setTemplateData((state) => ({ ...state, subject: value.target.value }));
},
value: templateData?.subject || "",
placeholder: translate("input.placeholder.subject")
}
),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, {})
] }) }),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: "100%", maxWidth: "100px" }, children: /* @__PURE__ */ jsxRuntime.jsx(ImportSingleDesign, { emailEditorRef }) }),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: "100%", maxWidth: "100px" }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: () => saveDesign(), style: { marginTop: "19px", height: "38px", width: "100%" }, children: translate("save") }) })
] }),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1, display: "flex", height: "calc(100dvh - 80px)" }, children: /* @__PURE__ */ jsxRuntime.jsxs(
designSystem.Tabs.Root,
{
value: mode,
onValueChange: (selected) => {
setMode(selected);
if (selected === "html") {
init();
}
},
children: [
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tabs.List, { "aria-label": "Switch between the html and text design", children: [
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Trigger, { value: "html", children: translate("designer.tab.html") }),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Trigger, { value: "text", children: translate("designer.tab.text") })
] }),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Content, { style: { height: "calc(100vh - 160px)" }, value: "html", children: /* @__PURE__ */ jsxRuntime.jsx(
designSystem.Box,
{
style: {
minHeight: "540px",
height: "100%"
},
children: serverConfigLoaded && /* @__PURE__ */ jsxRuntime.jsx(react.StrictMode, { children: /* @__PURE__ */ jsxRuntime.jsx(
reactEmailEditor.EmailEditor,
{
options: editorOptions,
minHeight: "100%",
ref: emailEditorRef,
onLoad: onLoadHandler
}
) })
}
) }),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Content, { style: { height: "calc(100vh - 160px)", padding: "20px" }, value: "text", children: /* @__PURE__ */ jsxRuntime.jsx(
designSystem.Textarea,
{
onChange: (e) => setBodyText(e.target.value),
value: bodyText,
style: { resize: "vertical" }
}
) })
]
}
) })
] })
] });
};
const Designer$1 = react.memo(Designer, shallowIsEqual);
const CoreEmailTable = () => {
const { formatMessage } = reactIntl.useIntl();
const navigate = reactRouterDom.useNavigate();
const headers = [
{ name: formatMessage({ id: getTranslation("table.header.coreEmailType") }), value: "name" }
];
const coreEmailTypes = [
{
coreEmailType: "user-address-confirmation",
name: formatMessage({ id: getTranslation("emailTypes.user-address-confirmation") })
},
{
coreEmailType: "reset-password",
name: formatMessage({ id: getTranslation("emailTypes.reset-password") })
}
];
return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 3, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: headers.length + 1, rowCount: coreEmailTypes.length, children: [
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
headers.map((header) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { style: { fontWeight: "bold" }, variant: "sigma", children: header.name }) }, header.value)),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.VisuallyHidden, { children: "Actions" }) })
] }) }),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: coreEmailTypes.map((coreEmailType, idx) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { style: { fontWeight: "bold" }, textColor: "neutral800", children: coreEmailType.name }) }),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "end", gap: "8px", children: /* @__PURE__ */ jsxRuntime.jsx(
designSystem.IconButton,
{
onClick: () => navigate({ pathname: getUrl(`core/${coreEmailType.coreEmailType}`) }),
label: formatMessage({ id: getTranslation("table.action.edit") }),
children: /* @__PURE__ */ jsxRuntime.jsx(icons.Pencil, {})
}
) }) })
] }, idx)) })
] }) });
};
const GlobalLoader = ({ loading }) => {
const { formatMessage } = reactIntl.useIntl();
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: loading && /* @__PURE__ */ jsxRuntime.jsxs(
"div",
{
style: {
position: "fixed",
top: 0,
left: 0,
width: "100%",
height: "100%",
zIndex: 999,
backgroundColor: "rgba(0, 0, 0, 0.4)",
display: "flex",
justifyContent: "center",
alignItems: "center",
flexDirection: "column",
backdropFilter: "blur(5px)"
},
children: [
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, {}),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", style: { marginTop: "20px" }, children: formatMessage({ id: getTranslation("pleaseWait") }) })
]
}
) });
};
const ImportExportActions = ({ data = [], reload, handleTemplatesExport }) => {
const { formatMessage } = reactIntl.useIntl();
const { toggleNotification } = admin.useNotification();
const emailTemplatesFileSelect = react.useRef(null);
const [importConfirmationModal, setImportConfirmationModal] = react.useState(false);
const [importedTemplates, setImportedTemplates] = react.useState([]);
const [importLoading, setImportLoading] = react.useState(false);
const handleFileChange = (event) => {
if (!event) return;
const files = event.target.files;
if (!files || files.length === 0) return;
const file = files[0];
if (!file) return;
const fr = new FileReader();
fr.onload = async () => {
const content = destr.destr(fr.result);
setImportedTemplates(content);
setImportConfirmationModal(true);
};
fr.readAsText(file);
};
const handleTemplatesImport = async () => {
try {
setImportLoading(true);
let _importedTemplates = [];
for (const template of importedTemplates) {
const response = await createTemplate(template.id, {
...template,
createdAt: dayjs__default.default().toDate(),
updatedAt: dayjs__default.default().toDate(),
import: true
});
if (!lodash.isEmpty(response)) _importedTemplates.push(response);
}
await reload();
toggleNotification({
type: "success",
title: formatMessage({ id: getTranslation("success") }),
message: formatMessage({ id: getTranslation("success.importingTemplates") })
});
} catch (error) {
console.error("💬 :: handleTemplatesImport :: Error", error);
toggleNotification({
type: "danger",
title: formatMessage({ id: getTranslation("error") }),
message: formatMessage({ id: getTranslation("error.importingTemplates") })
});
} finally {
setImportConfirmationModal(false);
setImportLoading(false);
setImportedTemplates([]);
}
};
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
/* @__PURE__ */ jsxRuntime.jsx(GlobalLoader, { loading: importLoading }),
/* @__PURE__ */ jsxRuntime.jsx(
designSystem.Dialog.Root,
{
open: importConfirmationModal,
onOpenChange: () => {
setImportConfirmationModal((s) => !s);
if (emailTemplatesFileSelect.current) {
emailTemplatesFileSelect.current.value = "";
}
},
children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: formatMessage({ id: getTranslation("confirm.title") }) }),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { icon: /* @__PURE__ */ jsxRuntime.jsx(icons.CloudUpload, { width: 50, height: 50 }), children: formatMessage({ id: getTranslation("confirm.import.message") }) }),
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { disabled: importLoading, fullWidth: true, variant: "tertiary", children: "Cancel" }) }),
/* @__PURE__ */ jsxRuntime.jsx(
designSystem.Button,
{
loading: importLoading,
disabled: importLoading,
onClick: () => handleTemplatesImport(),
fullWidth: true,
variant: "success-light",
children: "Yes, import"
}
)
] })
] })
}
),
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { style: { padding: "30px 0px 10px" }, gap: "14px", justifyContent: "end", children: [
data?.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: () => handleTemplatesExport(), color: "success", startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Download, {}), children: formatMessage({ id: getTranslation("designer.exportTemplates") }) }),
/* @__PURE__ */ jsxRuntime.jsx(
designSystem.Button,
{
onClick: () => {
emailTemplatesFileSelect?.current?.click();
},
startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.CloudUpload, {}),
children: formatMessage({ id: getTranslation("designer.importTemplates") })
}
)
] }),
/* @__PURE__ */ jsxRuntime.jsx("input", { accept: ".json", hidden: true, type: "file", ref: emailTemplatesFileSelect, onChange: handleFileChange })
] });
};
const CustomEmailTable = ({ data = [], reload }) => {
const { formatMessage } = reactIntl.useIntl();
const navigate = reactRouterDom.useNavigate();
const { toggleNotification } = admin.useNotification();
const [duplicateConfirmationModal, setDuplicateConfirmationModal] = react.useState(false);
const [duplicateId, setDuplicateId] = react.useState();
const [deleteId, setDeleteId] = react.useState();
const [showDeleteModal, setShowDeleteModal] = react.useState(false);
const handleTemplateDuplication = react.useCallback(async () => {
if (!duplicateId) return;
try {
const response = await duplicateTemplate(duplicateId);
toggleNotification({
type: "success",
title: formatMessage({ id: getTranslation("success") }),
message: formatMessage({ id: getTranslation("success.duplicate") })
});
navigate({ pathname: getUrl(`design/${response.id}`) });
} catch (error) {
toggleNotification({
type: "danger",
title: formatMessage({ id: getTranslation("error") }),
message: formatMessage({ id: getTranslation("error.duplicate") })
});
}
}, [duplicateConfirmationModal]);
const handleDelete = react.useCallback(async () => {
if (!deleteId) return;
try {
await deleteTemplate(deleteId);
await reload();
toggleNotification({
type: "success",
title: formatMessage({ id: getTranslation("success") }),
message: formatMessage({ id: getTranslation("success.delete") })
});
} catch (error) {
toggleNotification({
type: "danger",
title: formatMessage({ id: getTranslation("error") }),
message: formatMessage({ id: getTranslation("error.delete") })
});
}
}, [deleteId]);
const handleTemplatesExport = async () => {
const templates = await getTemplatesData();
const dataStr = `data:text/json;charset=utf-8,${encodeURIComponent(JSON.stringify(templates))}`;
let a = document.createElement("a");
a.href = dataStr;
a.download = `email_templates_${dayjs__default.default().unix()}.json`;
a.click();
};
const emailTemplatesHeaders = [
{ name: formatMessage({ id: getTranslation("table.header.name") }), value: "name" },
{
name: formatMessage({ id: getTranslation("table.header.templateReferenceId") }),
value: "templateReferenceId"
},
{ name: formatMessage({ id: getTranslation("table.header.createdAt") }), value: "createdAt" }
];
if (data.length === 0) {
return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 8, children: [
/* @__PURE__ */ jsxRuntime.jsx(
designSystem.EmptyStateLayout,
{
shadow: "none",
icon: /* @__PURE__ */ jsxRuntime.jsx(symbols.EmptyPictures, { width: "210px" }),
content: formatMessage({ id: getTranslation("customTable.noDesigns") }),
action: /* @__PURE__ */ jsxRuntime.jsx(
designSystem.Button,
{
variant: "secondary",
startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
onClick: () => navigate({ pathname: getUrl(`design/new`) }),
children: formatMessage({ id: getTranslation("customTable.addDesign") })
}
)
}
),
/* @__PURE__ */ jsxRuntime.jsx(ImportExportActions, { data, reload, handleTemplatesExport })
] });
}
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
/* @__PURE__ */ jsxRuntime.jsx(
designSystem.Dialog.Root,
{
open: duplicateConfirmationModal,
onOpenChange: () => setDuplicateConfirmationModal((s) => !s),
children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: formatMessage({ id: getTranslation("confirm.title") }) }),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { icon: /* @__PURE__ */ jsxRuntime.jsx(lu.LuCopyCheck, { size: "30px" }), children: formatMessage({ id: getTranslation("confirm.duplicate.message") }) }),
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: () => setDuplicateId(void 0), fullWidth: true, variant: "tertiary", children: "Cancel" }) }),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Action, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: () => handleTemplateDuplication(), fullWidth: true, variant: "success-light", children: "Yes, duplicate" }) })
] })
] })
}
),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: showDeleteModal, onOpenChange: () => setShowDeleteModal((s) => !s), children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: formatMessage({ id: getTranslation("confirm.title") }) }),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { icon: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { width: "30px" }), children: formatMessage({ id: getTranslation("confirm.delete.message") }) }),
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: () => setDeleteId(void 0), fullWidth: true, variant: "tertiary", children: "Cancel" }) }),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Action, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: () => handleDelete(), fullWidth: true, variant: "danger-light", children: "Yes, delete" }) })
] })
] }) }),
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 3, children: [
/* @__PURE__ */ jsxRuntime.jsxs(
designSystem.Table,
{
colCount: emailTemplatesHeaders.length + 1,
rowCount: data.length,
footer: /* @__PURE__ */ jsxRuntime.jsx(
designSystem.TFooter,
{
style: { cursor: "pointer" },
icon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
onClick: () => navigate({ pathname: getUrl(`design/new`) }),
children: formatMessage({ id: getTranslation("customTable.addDesign") })
}
),
children: [
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
emailTemplatesHeaders.map((header) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { style: { fontWeight: "bold" }, variant: "sigma", children: header.name }) }, header.name)),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.VisuallyHidden, { children: "Actions" }) })
] }) }),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: data.map((entry, idx) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { style: { fontWeight: "bold" }, textColor: "neutral800", children: entry.name }) }),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral800", children: entry.templateReferenceId }) }),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral800", children: entry.createdAt }) }),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: "10px", justifyContent: "end", children: [
/* @__PURE__ */ jsxRuntime.jsx(
designSystem.IconButton,
{
label: formatMessage({ id: getTranslation("tooltip.edit") }),
onClick: () => navigate({ pathname: getUrl(`design/${entry.id}`) }),
children: /* @__PURE__ */ jsxRuntime.jsx(icons.Pencil, {})
}
),
/* @__PURE__ */ jsxRuntime.jsx(
designSystem.IconButton,
{
label: formatMessage({ id: getTranslation("tooltip.downloadHtml") }),
onClick: () => downloadTemplate(entry.id, "html"),
children: /* @__PURE__ */ jsxRuntime.jsx(bs.BsFiletypeHtml, { size: 16 })
}
),
/* @__PURE__ */ jsxRuntime.jsx(
designSystem.IconButton,
{
label: formatMessage({ id: getTranslation("tooltip.downloadDesign") }),
onClick: () => downloadTemplate(entry.id, "json"),
children: /* @__PURE__ */ jsxRuntime.jsx(bs.BsFiletypeJson, { size: 16 })
}
),
/* @__PURE__ */ jsxRuntime.jsx(
designSystem.IconButton,
{
label: formatMessage({ id: getTranslation("tooltip.duplicate") }),
onClick: () => {
setDuplicateId(entry.id);
setDuplicateConfirmationModal(true);
},
children: /* @__PURE__ */ jsxRuntime.jsx(icons.Duplicate, {})
}
),
/* @__PURE__ */ jsxRuntime.jsx(
designSystem.IconButton,
{
label: formatMessage({ id: getTranslation("tooltip.copyTemplateId") }),
onClick: () => {
navigator.clipboard.writeText(`${entry.templateReferenceId}`).then(
() => {
toggleNotification({
type: "success",
title: formatMessage({ id: getTranslation("success") }),
message: formatMessage({ id: getTranslation("success.copyTemplateId") })
});
},
(err) => {
console.error("Could not copy text: ", err);
}
);
},
children: /* @__PURE__ */ jsxRuntime.jsx(fa6.FaHashtag, { size: 16 })
}
),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingLeft: 1, children: /* @__PURE__ */ jsxRuntime.jsx(
designSystem.IconButton,
{
label: formatMessage({ id: getTranslation("tooltip.delete") }),
onClick: () => {
setDeleteId(entry.id);
setShowDeleteModal(true);
},
children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
}
) })
] }) })
] }, idx)) })
]
}
),
/* @__PURE__ */ jsxRuntime.jsx(ImportExportActions, { data, reload, handleTemplatesExport })
] })
] });
};
const HomePage = () => {
const navigate = reactRouterDom.useNavigate();
const translate = useTr();
const [emailTemplates, setEmailTemplates] = react.useState([]);
const [activeTab, setActiveTab] = react.useState("customEmailTemplates");
const { toggleNotification } = strapiAdmin.useNotification();
const init = react.useCallback(async () => {
const data = await getTemplatesData();
setEmailTemplates(data);
}, []);
react.useEffect(() => {
init().catch(() => {
toggleNotification({
type: "danger",
title: translate("error"),
message: translate("error.loadingTemplates")
});
});
}, []);
return /* @__PURE__ */ jsxRuntime.jsxs(strapiAdmin.Page.Main, { children: [
/* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Page.Title, { children: translate("page.title") }),
/* @__PURE__ */ jsxRuntime.jsx(
strapiAdmin.Layouts.Header,
{
id: "title",
title: translate("page.title"),
subtitle: translate("page.subTitle"),
primaryAction: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { label: translate("page.home.cta.tooltip"), children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: () => navigate({ pathname: getUrl(`design/new`) }), children: translate("page.home.cta") }) })
}
),
/* @__PURE__ */ jsxRuntime.jsxs(strapiAdmin.Layouts.Content, { children: [
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Divider, { style: { marginBottom: "50px" } }),
/* @__PURE__ */ jsxRuntime.jsxs(
designSystem.Tabs.Root,
{
value: activeTab,
onValueChange: (selected) => {
setActiveTab(selected);
},
children: [
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tabs.List, { "aria-label": "Switch between custom email designs & core email designs", children: [
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Trigger, { value: "customEmailTemplates", children: translate("emailTypes.custom.tab.label") }),
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Trigger, { value: "coreEmailTemplates", children: translate("emailTypes.core.tab.label") })
] }),
/* @__PURE__ */ jsxRuntime.jsx(
designSystem.Tabs.Content,
{
style: { borderBottomRightRadius: "6px", borderBottomLeftRadius: "6px" },
value: "customEmailTemplates",
children: /* @__PURE__ */ jsxRuntime.jsx(CustomEmailTable, { reload: init, data: emailTemplates })
}
),
/* @__PURE__ */ jsxRuntime.jsx(
designSystem.Tabs.Content,
{
style: { borderBottomRightRadius: "6px", borderBottomLeftRadius: "6px" },
value: "coreEmailTemplates",
children: /* @__PURE__ */ jsxRuntime.jsx(CoreEmailTable, {})
}
)
]
}
),
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { paddingBottom: "20px" } })
] })
] });
};
const App = () => {
return /* @__PURE__ */ jsxRuntime.jsxs(reactRouterDom.Routes, { children: [
/* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { index: true, element: /* @__PURE__ */ jsxRuntime.jsx(HomePage, {}) }),
/* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: "design/:templateId", element: /* @__PURE__ */ jsxRuntime.jsx(Designer$1, {}) }),
/* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: "core/:coreEmailType", element: /* @__PURE__ */ jsxRuntime.jsx(Designer$1, { isCore: true }) }),
/* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: "*", element: /* @__PURE__ */ jsxRuntime.jsx(admin.Page.Error, {}) })
] });
};
exports.App = App;