@jhits/plugin-newsletter 0.0.16 → 0.0.17
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/dist/api/email-utils.d.ts.map +1 -1
- package/dist/api/email-utils.js +45 -4
- package/dist/api/handlers/send-newsletter.d.ts.map +1 -1
- package/dist/api/handlers/send-newsletter.js +54 -6
- package/dist/api/handlers/settings.d.ts.map +1 -1
- package/dist/api/handlers/settings.js +51 -1
- package/dist/index.d.ts +27 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -122
- package/dist/lib/blocks/BlockRenderer.d.ts.map +1 -1
- package/dist/lib/blocks/BlockRenderer.js +14 -2
- package/dist/lib/email/EmailRenderer.d.ts +1 -0
- package/dist/lib/email/EmailRenderer.d.ts.map +1 -1
- package/dist/lib/email/EmailRenderer.js +31 -19
- package/dist/lib/utils/config-resolver.d.ts +33 -0
- package/dist/lib/utils/config-resolver.d.ts.map +1 -0
- package/dist/lib/utils/config-resolver.js +47 -0
- package/dist/registry/BlockRegistry.d.ts +9 -1
- package/dist/registry/BlockRegistry.d.ts.map +1 -1
- package/dist/registry/BlockRegistry.js +126 -8
- package/dist/state/EditorContext.d.ts +11 -1
- package/dist/state/EditorContext.d.ts.map +1 -1
- package/dist/state/EditorContext.js +23 -5
- package/dist/state/types.d.ts +12 -0
- package/dist/state/types.d.ts.map +1 -1
- package/dist/types/block.d.ts +9 -0
- package/dist/types/block.d.ts.map +1 -1
- package/dist/types/newsletter.d.ts +2 -0
- package/dist/types/newsletter.d.ts.map +1 -1
- package/dist/views/CanvasEditor/BlockWrapper.d.ts.map +1 -1
- package/dist/views/CanvasEditor/BlockWrapper.js +24 -3
- package/dist/views/CanvasEditor/CanvasEditorView.d.ts.map +1 -1
- package/dist/views/CanvasEditor/CanvasEditorView.js +77 -17
- package/dist/views/CanvasEditor/EditorBody.d.ts.map +1 -1
- package/dist/views/CanvasEditor/EditorBody.js +1 -1
- package/dist/views/CanvasEditor/components/EditorCanvas.d.ts.map +1 -1
- package/dist/views/CanvasEditor/components/EditorCanvas.js +158 -100
- package/dist/views/CanvasEditor/components/EditorSidebar.d.ts +3 -1
- package/dist/views/CanvasEditor/components/EditorSidebar.d.ts.map +1 -1
- package/dist/views/CanvasEditor/components/EditorSidebar.js +3 -3
- package/dist/views/CanvasEditor/hooks/useRegisteredBlocks.d.ts +1 -1
- package/dist/views/CanvasEditor/hooks/useRegisteredBlocks.d.ts.map +1 -1
- package/dist/views/CanvasEditor/hooks/useRegisteredBlocks.js +6 -40
- package/dist/views/components/DomainPromptModal.d.ts +13 -0
- package/dist/views/components/DomainPromptModal.d.ts.map +1 -0
- package/dist/views/components/DomainPromptModal.js +58 -0
- package/dist/views/components/SendNewsletterModal.d.ts.map +1 -1
- package/dist/views/components/SendNewsletterModal.js +91 -22
- package/dist/views/components/SmtpSettingsModal.d.ts.map +1 -1
- package/dist/views/components/SmtpSettingsModal.js +10 -0
- package/dist/views/components/TestEmailModal.d.ts.map +1 -1
- package/dist/views/components/TestEmailModal.js +86 -17
- package/package.json +53 -9
- package/src/api/email-utils.ts +53 -4
- package/src/api/handlers/send-newsletter.ts +65 -6
- package/src/api/handlers/settings.ts +60 -2
- package/src/index.tsx +49 -155
- package/src/lib/blocks/BlockRenderer.tsx +16 -2
- package/src/lib/email/EmailRenderer.tsx +31 -20
- package/src/lib/utils/config-resolver.ts +71 -0
- package/src/registry/BlockRegistry.tsx +255 -0
- package/src/state/EditorContext.tsx +43 -8
- package/src/state/types.ts +16 -0
- package/src/types/block.ts +10 -0
- package/src/types/newsletter.ts +3 -0
- package/src/views/CanvasEditor/BlockWrapper.tsx +27 -2
- package/src/views/CanvasEditor/CanvasEditorView.tsx +142 -61
- package/src/views/CanvasEditor/EditorBody.tsx +17 -13
- package/src/views/CanvasEditor/components/EditorCanvas.tsx +178 -115
- package/src/views/CanvasEditor/components/EditorSidebar.tsx +57 -2
- package/src/views/CanvasEditor/hooks/useRegisteredBlocks.ts +6 -45
- package/src/views/components/DomainPromptModal.tsx +160 -0
- package/src/views/components/SendNewsletterModal.tsx +270 -184
- package/src/views/components/SmtpSettingsModal.tsx +11 -0
- package/src/views/components/TestEmailModal.tsx +235 -149
- package/src/registry/BlockRegistry.ts +0 -53
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email-utils.d.ts","sourceRoot":"","sources":["../../src/api/email-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,mBAAmB,EAAsB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"email-utils.d.ts","sourceRoot":"","sources":["../../src/api/email-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,mBAAmB,EAAsB,MAAM,qBAAqB,CAAC;AA0E9E,wBAAsB,gBAAgB,CAClC,MAAM,EAAE,mBAAmB,EAC3B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAgIf"}
|
package/dist/api/email-utils.js
CHANGED
|
@@ -21,10 +21,42 @@ async function getSmtpConfig(config) {
|
|
|
21
21
|
from: smtpConfig.from,
|
|
22
22
|
fromName: smtpConfig.fromName || '',
|
|
23
23
|
logoUrl: smtpConfig.logoUrl || '',
|
|
24
|
+
unsubscribeTranslations: smtpConfig.unsubscribeTranslations || {},
|
|
24
25
|
};
|
|
25
26
|
}
|
|
26
27
|
return null;
|
|
27
28
|
}
|
|
29
|
+
async function resolveBaseUrl(config, language) {
|
|
30
|
+
try {
|
|
31
|
+
const dbConnection = await config.getDb();
|
|
32
|
+
const db = dbConnection.db();
|
|
33
|
+
const settings = db.collection('settings');
|
|
34
|
+
// Try to get site config from plugin-website (stored in 'settings' collection with identifier 'site_config')
|
|
35
|
+
const siteConfig = await settings.findOne({ identifier: 'site_config' });
|
|
36
|
+
if (siteConfig && siteConfig.domainLocaleConfig && Array.isArray(siteConfig.domainLocaleConfig)) {
|
|
37
|
+
// Find domain for this locale
|
|
38
|
+
const localeConfig = siteConfig.domainLocaleConfig.find((c) => c.locale === language);
|
|
39
|
+
if (localeConfig && localeConfig.domain && localeConfig.domain !== 'undefined' && localeConfig.domain.trim() !== '') {
|
|
40
|
+
const domain = localeConfig.domain.trim();
|
|
41
|
+
// Add protocol if missing
|
|
42
|
+
if (!domain.startsWith('http')) {
|
|
43
|
+
const protocol = domain.includes('localhost') ? 'http' : 'https';
|
|
44
|
+
return `${protocol}://${domain}`;
|
|
45
|
+
}
|
|
46
|
+
return domain;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
console.warn('[NewsletterAPI] Failed to resolve language-specific base URL:', error);
|
|
52
|
+
}
|
|
53
|
+
// Fallback to default, carefully checking for 'undefined' string
|
|
54
|
+
const fallback = process.env.NEXT_PUBLIC_SITE_URL;
|
|
55
|
+
if (fallback && fallback !== 'undefined' && fallback.trim() !== '') {
|
|
56
|
+
return fallback;
|
|
57
|
+
}
|
|
58
|
+
return 'https://botanicsandyou.com';
|
|
59
|
+
}
|
|
28
60
|
export async function sendWelcomeEmail(config, email, language, host) {
|
|
29
61
|
const smtpConfig = await getSmtpConfig(config);
|
|
30
62
|
if (!smtpConfig)
|
|
@@ -32,9 +64,8 @@ export async function sendWelcomeEmail(config, email, language, host) {
|
|
|
32
64
|
const { blocks, metadata } = await GET_WELCOME_EMAIL_CONTENT(undefined, config, language);
|
|
33
65
|
const isDutch = language === 'nl';
|
|
34
66
|
const isSwedish = language === 'sv';
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
: config.baseUrl || 'https://bya.jorishummel.com';
|
|
67
|
+
// Resolve base URL based on language settings from plugin-website
|
|
68
|
+
const baseUrl = await resolveBaseUrl(config, language);
|
|
38
69
|
const slugs = {
|
|
39
70
|
sv: '/avmälla',
|
|
40
71
|
nl: '/afmelden',
|
|
@@ -75,11 +106,17 @@ export async function sendWelcomeEmail(config, email, language, host) {
|
|
|
75
106
|
}
|
|
76
107
|
if (blocks && blocks.length > 0) {
|
|
77
108
|
const { generateNewsletterEmailHtml } = await import('../lib/email/EmailRenderer');
|
|
109
|
+
// Get unsubscribe text (Priority: metadata manual override > global SMTP translation > hardcoded localized default)
|
|
110
|
+
const globalTranslations = smtpConfig.unsubscribeTranslations || {};
|
|
111
|
+
const hardcodedDefaults = { en: 'Unsubscribe', nl: 'Afmelden', sv: 'Avanmälan' };
|
|
112
|
+
const defaultText = globalTranslations[language] || hardcodedDefaults[language] || hardcodedDefaults.en;
|
|
113
|
+
const unsubscribeText = metadata?.unsubscribeText || defaultText;
|
|
78
114
|
html = generateNewsletterEmailHtml(blocks, { subject: metadata?.subject || '', previewText: metadata?.previewText || '' }, {
|
|
79
115
|
baseUrl,
|
|
80
116
|
locale: language,
|
|
81
117
|
logoUrl: logoSrc,
|
|
82
118
|
unsubscribeUrl,
|
|
119
|
+
unsubscribeText,
|
|
83
120
|
footerText: `© ${new Date().getFullYear()} ${smtpConfig.fromName || 'Botanics & You'}`,
|
|
84
121
|
});
|
|
85
122
|
subject = metadata?.subject || (isDutch ? 'Welkom!' : isSwedish ? 'Välkommen!' : 'Welcome!');
|
|
@@ -100,6 +137,10 @@ export async function sendWelcomeEmail(config, email, language, host) {
|
|
|
100
137
|
return;
|
|
101
138
|
}
|
|
102
139
|
}
|
|
140
|
+
// Get unsubscribe text for legacy template too
|
|
141
|
+
const globalTranslations = smtpConfig.unsubscribeTranslations || {};
|
|
142
|
+
const hardcodedDefaults = { en: 'Unsubscribe', nl: 'Afmelden', sv: 'Avanmälan' };
|
|
143
|
+
const unsubscribeText = globalTranslations[language] || hardcodedDefaults[language] || hardcodedDefaults.en;
|
|
103
144
|
const template = handlebars.compile(templateContent);
|
|
104
145
|
html = template({
|
|
105
146
|
fromName: smtpConfig.fromName || 'Botanics & You',
|
|
@@ -110,7 +151,7 @@ export async function sendWelcomeEmail(config, email, language, host) {
|
|
|
110
151
|
? 'Tack för att du är en del av vår community. Vi tror att naturen ger oss allt vi verkligen behöver.'
|
|
111
152
|
: 'Thank you for joining our community. We believe that nature provides everything we truly need.',
|
|
112
153
|
unsubscribeUrl,
|
|
113
|
-
unsubscribeText
|
|
154
|
+
unsubscribeText,
|
|
114
155
|
tagline: isDutch ? 'Natuurlijk verbonden' : isSwedish ? 'Naturligt ansluten' : 'Naturally connected',
|
|
115
156
|
currentYear: new Date().getFullYear(),
|
|
116
157
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"send-newsletter.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/send-newsletter.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"send-newsletter.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/send-newsletter.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAiF7D,wBAAsB,oBAAoB,CACtC,GAAG,EAAE,WAAW,EAChB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,mBAAmB,GAC5B,OAAO,CAAC,YAAY,CAAC,CA8OvB;AAED,wBAAsB,uBAAuB,CACzC,GAAG,EAAE,WAAW,EAChB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,mBAAmB,GAC5B,OAAO,CAAC,YAAY,CAAC,CAuCvB"}
|
|
@@ -19,6 +19,7 @@ async function getSmtpConfig(config) {
|
|
|
19
19
|
from: smtpConfig.from,
|
|
20
20
|
fromName: smtpConfig.fromName || '',
|
|
21
21
|
logoUrl: smtpConfig.logoUrl || '',
|
|
22
|
+
unsubscribeTranslations: smtpConfig.unsubscribeTranslations || {},
|
|
22
23
|
};
|
|
23
24
|
}
|
|
24
25
|
return null;
|
|
@@ -29,6 +30,37 @@ function getNewsletterFilter(idOrSlug) {
|
|
|
29
30
|
}
|
|
30
31
|
return { slug: idOrSlug };
|
|
31
32
|
}
|
|
33
|
+
async function resolveBaseUrl(config, language) {
|
|
34
|
+
try {
|
|
35
|
+
const dbConnection = await config.getDb();
|
|
36
|
+
const db = dbConnection.db();
|
|
37
|
+
const settings = db.collection('settings');
|
|
38
|
+
// Try to get site config from plugin-website (stored in 'settings' collection with identifier 'site_config')
|
|
39
|
+
const siteConfig = await settings.findOne({ identifier: 'site_config' });
|
|
40
|
+
if (siteConfig && siteConfig.domainLocaleConfig && Array.isArray(siteConfig.domainLocaleConfig)) {
|
|
41
|
+
// Find domain for this locale
|
|
42
|
+
const localeConfig = siteConfig.domainLocaleConfig.find((c) => c.locale === language);
|
|
43
|
+
if (localeConfig && localeConfig.domain && localeConfig.domain !== 'undefined' && localeConfig.domain.trim() !== '') {
|
|
44
|
+
const domain = localeConfig.domain.trim();
|
|
45
|
+
// Add protocol if missing
|
|
46
|
+
if (!domain.startsWith('http')) {
|
|
47
|
+
const protocol = domain.includes('localhost') ? 'http' : 'https';
|
|
48
|
+
return `${protocol}://${domain}`;
|
|
49
|
+
}
|
|
50
|
+
return domain;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
console.warn('[NewsletterAPI] Failed to resolve language-specific base URL:', error);
|
|
56
|
+
}
|
|
57
|
+
// Fallback to default, carefully checking for 'undefined' string
|
|
58
|
+
const fallback = process.env.NEXT_PUBLIC_SITE_URL;
|
|
59
|
+
if (fallback && fallback !== 'undefined' && fallback.trim() !== '') {
|
|
60
|
+
return fallback;
|
|
61
|
+
}
|
|
62
|
+
return 'https://botanicsandyou.com';
|
|
63
|
+
}
|
|
32
64
|
export async function POST_SEND_NEWSLETTER(req, idOrSlug, config) {
|
|
33
65
|
try {
|
|
34
66
|
const userId = await config.getUserId?.(req);
|
|
@@ -79,7 +111,15 @@ export async function POST_SEND_NEWSLETTER(req, idOrSlug, config) {
|
|
|
79
111
|
},
|
|
80
112
|
connectionTimeout: 30000,
|
|
81
113
|
});
|
|
82
|
-
|
|
114
|
+
// Resolve base URL based on language settings from plugin-website
|
|
115
|
+
const baseUrl = await resolveBaseUrl(config, language);
|
|
116
|
+
// Final sanity check - if domain is STILL undefined, stop sending and ask user for domain
|
|
117
|
+
if (baseUrl.includes('undefined')) {
|
|
118
|
+
return NextResponse.json({
|
|
119
|
+
error: 'Domain not configured for this language. Please define your website domain first.',
|
|
120
|
+
code: 'DOMAIN_MISSING'
|
|
121
|
+
}, { status: 400 });
|
|
122
|
+
}
|
|
83
123
|
let logoAttachment = undefined;
|
|
84
124
|
let logoSrc = smtpConfig.logoUrl || `${baseUrl}/logo_black.svg`;
|
|
85
125
|
if (smtpConfig.logoUrl && smtpConfig.logoUrl.startsWith('data:')) {
|
|
@@ -132,18 +172,26 @@ export async function POST_SEND_NEWSLETTER(req, idOrSlug, config) {
|
|
|
132
172
|
const subscriber = isTest ? null : await subscribers.findOne({ email });
|
|
133
173
|
const subscriberLang = subscriber?.language || language;
|
|
134
174
|
const subscriberSlug = slugs[subscriberLang] || slugs.en;
|
|
135
|
-
|
|
136
|
-
|
|
175
|
+
// Resolve correct base URL for this specific subscriber's language
|
|
176
|
+
const subscriberBaseUrl = await resolveBaseUrl(config, subscriberLang);
|
|
177
|
+
const unsubscribeUrl = `${subscriberBaseUrl}${subscriberSlug}?email=${encodeURIComponent(email)}`;
|
|
178
|
+
return { email, unsubscribeUrl, subscriberLang, subscriberBaseUrl };
|
|
137
179
|
}));
|
|
138
180
|
let successCount = 0;
|
|
139
181
|
let failedCount = 0;
|
|
140
|
-
for (const { email, unsubscribeUrl } of recipientsWithUrls) {
|
|
182
|
+
for (const { email, unsubscribeUrl, subscriberLang, subscriberBaseUrl } of recipientsWithUrls) {
|
|
141
183
|
try {
|
|
184
|
+
// Get unsubscribe text (Priority: metadata manual override > global SMTP translation > hardcoded localized default)
|
|
185
|
+
const globalTranslations = smtpConfig.unsubscribeTranslations || {};
|
|
186
|
+
const hardcodedDefaults = { en: 'Unsubscribe', nl: 'Afmelden', sv: 'Avanmälan' };
|
|
187
|
+
const defaultText = globalTranslations[subscriberLang] || hardcodedDefaults[subscriberLang] || hardcodedDefaults.en;
|
|
188
|
+
const unsubscribeText = metadata?.unsubscribeText || defaultText;
|
|
142
189
|
const html = generateNewsletterEmailHtml(blocks, { subject: metadata.subject || '', previewText: metadata.previewText || '' }, {
|
|
143
|
-
baseUrl,
|
|
144
|
-
locale:
|
|
190
|
+
baseUrl: subscriberBaseUrl,
|
|
191
|
+
locale: subscriberLang,
|
|
145
192
|
logoUrl: logoSrc,
|
|
146
193
|
unsubscribeUrl,
|
|
194
|
+
unsubscribeText,
|
|
147
195
|
footerText: `© ${new Date().getFullYear()} ${smtpConfig.fromName || 'Botanics & You'}`,
|
|
148
196
|
});
|
|
149
197
|
await transporter.sendMail({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/settings.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAsB,MAAM,wBAAwB,CAAC;AAoBjF,wBAAsB,YAAY,CAC9B,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,mBAAmB,GAC5B,OAAO,CAAC,YAAY,CAAC,CA2BvB;AAED,wBAAsB,aAAa,CAC/B,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,mBAAmB,GAC5B,OAAO,CAAC,YAAY,CAAC,CAgCvB;AAED,wBAAsB,iBAAiB,CACnC,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,mBAAmB,GAC5B,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/settings.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAsB,MAAM,wBAAwB,CAAC;AAoBjF,wBAAsB,YAAY,CAC9B,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,mBAAmB,GAC5B,OAAO,CAAC,YAAY,CAAC,CA2BvB;AAED,wBAAsB,aAAa,CAC/B,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,mBAAmB,GAC5B,OAAO,CAAC,YAAY,CAAC,CAgCvB;AAED,wBAAsB,iBAAiB,CACnC,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,mBAAmB,GAC5B,OAAO,CAAC,YAAY,CAAC,CAqDvB;AAED,wBAAsB,kBAAkB,CACpC,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,mBAAmB,GAC5B,OAAO,CAAC,YAAY,CAAC,CAiDvB;AA2DD,wBAAsB,eAAe,CACjC,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,mBAAmB,GAC5B,OAAO,CAAC,YAAY,CAAC,CAgMvB"}
|
|
@@ -86,6 +86,11 @@ export async function GET_SMTP_SETTINGS(req, config) {
|
|
|
86
86
|
fromName: '',
|
|
87
87
|
primaryLanguage: 'en',
|
|
88
88
|
logoUrl: '',
|
|
89
|
+
unsubscribeTranslations: {
|
|
90
|
+
en: 'Unsubscribe',
|
|
91
|
+
nl: 'Afmelden',
|
|
92
|
+
sv: 'Avanmälan',
|
|
93
|
+
},
|
|
89
94
|
});
|
|
90
95
|
}
|
|
91
96
|
return NextResponse.json({
|
|
@@ -97,6 +102,11 @@ export async function GET_SMTP_SETTINGS(req, config) {
|
|
|
97
102
|
fromName: smtpConfig.fromName || '',
|
|
98
103
|
primaryLanguage: smtpConfig.primaryLanguage || 'en',
|
|
99
104
|
logoUrl: smtpConfig.logoUrl || '',
|
|
105
|
+
unsubscribeTranslations: smtpConfig.unsubscribeTranslations || {
|
|
106
|
+
en: 'Unsubscribe',
|
|
107
|
+
nl: 'Afmelden',
|
|
108
|
+
sv: 'Avanmälan',
|
|
109
|
+
},
|
|
100
110
|
});
|
|
101
111
|
}
|
|
102
112
|
catch (error) {
|
|
@@ -128,6 +138,7 @@ export async function POST_SMTP_SETTINGS(req, config) {
|
|
|
128
138
|
fromName: body.fromName || '',
|
|
129
139
|
primaryLanguage: body.primaryLanguage || 'en',
|
|
130
140
|
logoUrl: body.logoUrl || '',
|
|
141
|
+
unsubscribeTranslations: body.unsubscribeTranslations || {},
|
|
131
142
|
updatedAt: new Date(),
|
|
132
143
|
updatedBy: userId,
|
|
133
144
|
},
|
|
@@ -159,6 +170,37 @@ function getSmtpConfigFromDb(config) {
|
|
|
159
170
|
return null;
|
|
160
171
|
})();
|
|
161
172
|
}
|
|
173
|
+
async function resolveBaseUrl(config, language) {
|
|
174
|
+
try {
|
|
175
|
+
const dbConnection = await config.getDb();
|
|
176
|
+
const db = dbConnection.db();
|
|
177
|
+
const settings = db.collection('settings');
|
|
178
|
+
// Try to get site config from plugin-website (stored in 'settings' collection with identifier 'site_config')
|
|
179
|
+
const siteConfig = await settings.findOne({ identifier: 'site_config' });
|
|
180
|
+
if (siteConfig && siteConfig.domainLocaleConfig && Array.isArray(siteConfig.domainLocaleConfig)) {
|
|
181
|
+
// Find domain for this locale
|
|
182
|
+
const localeConfig = siteConfig.domainLocaleConfig.find((c) => c.locale === language);
|
|
183
|
+
if (localeConfig && localeConfig.domain && localeConfig.domain !== 'undefined' && localeConfig.domain.trim() !== '') {
|
|
184
|
+
const domain = localeConfig.domain.trim();
|
|
185
|
+
// Add protocol if missing
|
|
186
|
+
if (!domain.startsWith('http')) {
|
|
187
|
+
const protocol = domain.includes('localhost') ? 'http' : 'https';
|
|
188
|
+
return `${protocol}://${domain}`;
|
|
189
|
+
}
|
|
190
|
+
return domain;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
console.warn('[NewsletterAPI] Failed to resolve language-specific base URL:', error);
|
|
196
|
+
}
|
|
197
|
+
// Fallback to default, carefully checking for 'undefined' string
|
|
198
|
+
const fallback = process.env.NEXT_PUBLIC_SITE_URL;
|
|
199
|
+
if (fallback && fallback !== 'undefined' && fallback.trim() !== '') {
|
|
200
|
+
return fallback;
|
|
201
|
+
}
|
|
202
|
+
return 'https://botanicsandyou.com';
|
|
203
|
+
}
|
|
162
204
|
export async function POST_TEST_EMAIL(req, config) {
|
|
163
205
|
try {
|
|
164
206
|
const userId = await config.getUserId?.(req);
|
|
@@ -194,7 +236,15 @@ export async function POST_TEST_EMAIL(req, config) {
|
|
|
194
236
|
const altPath = path.join(__dirname, '..', '..', '..', '..', 'templates', 'logo.png');
|
|
195
237
|
logoExists = fs.existsSync(altPath);
|
|
196
238
|
}
|
|
197
|
-
|
|
239
|
+
// Resolve base URL based on language settings from plugin-website
|
|
240
|
+
const baseUrl = await resolveBaseUrl(config, language);
|
|
241
|
+
// Final sanity check - if domain is STILL undefined, stop sending and ask user for domain
|
|
242
|
+
if (baseUrl.includes('undefined')) {
|
|
243
|
+
return NextResponse.json({
|
|
244
|
+
error: 'Domain not configured for this language. Please define your website domain first.',
|
|
245
|
+
code: 'DOMAIN_MISSING'
|
|
246
|
+
}, { status: 400 });
|
|
247
|
+
}
|
|
198
248
|
let logoAttachment = undefined;
|
|
199
249
|
let logoSrc = smtpConfig.logoUrl || `${baseUrl}/logo_black.svg`;
|
|
200
250
|
if (smtpConfig.logoUrl && smtpConfig.logoUrl.startsWith('data:')) {
|
package/dist/index.d.ts
CHANGED
|
@@ -24,23 +24,40 @@ export interface PluginProps {
|
|
|
24
24
|
/** Background color for dark mode (optional) */
|
|
25
25
|
dark?: string;
|
|
26
26
|
};
|
|
27
|
+
/** Localized strings for modular blocks in the editor */
|
|
28
|
+
translations?: Record<string, any>;
|
|
29
|
+
/** Global translations for unsubscribe text */
|
|
30
|
+
unsubscribeTranslations?: Record<string, string>;
|
|
27
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Client-facing configuration type
|
|
34
|
+
* Allows partial overrides of plugin behavior
|
|
35
|
+
*/
|
|
36
|
+
export type NewsletterPluginConfig = Partial<Omit<PluginProps, 'subPath' | 'siteId' | 'locale'>> & {
|
|
37
|
+
customBlocks?: ClientBlockDefinition[];
|
|
38
|
+
darkMode?: boolean;
|
|
39
|
+
backgroundColors?: {
|
|
40
|
+
light: string;
|
|
41
|
+
dark?: string;
|
|
42
|
+
};
|
|
43
|
+
translations?: Record<string, any>;
|
|
44
|
+
unsubscribeTranslations?: Record<string, string>;
|
|
45
|
+
emailConfig?: {
|
|
46
|
+
logoUrl?: string;
|
|
47
|
+
logoAlt?: string;
|
|
48
|
+
footerText?: string;
|
|
49
|
+
primaryColor?: string;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
28
52
|
/**
|
|
29
53
|
* Main Router Component
|
|
30
54
|
* Handles routing within the newsletter plugin
|
|
31
|
-
*
|
|
32
|
-
* Client Handshake:
|
|
33
|
-
* - Client apps can pass customBlocks via props
|
|
34
|
-
* - Or via window.__JHITS_PLUGIN_PROPS__['plugin-newsletter'].customBlocks
|
|
35
|
-
* - The EditorProvider will automatically register these blocks
|
|
36
55
|
*/
|
|
37
56
|
export default function NewsletterPlugin(props: PluginProps): import("react/jsx-runtime").JSX.Element;
|
|
38
57
|
export { NewsletterPlugin as Index };
|
|
39
|
-
export type { Block,
|
|
40
|
-
export type { Newsletter, NewsletterStatus, NewsletterMetadata, NewsletterListItem, NewsletterFilterOptions, } from './types/newsletter';
|
|
58
|
+
export type { Block, ClientBlockDefinition } from './types/block';
|
|
41
59
|
export { initNewsletterPlugin } from './init';
|
|
42
|
-
export type { NewsletterPluginConfig } from './init';
|
|
43
|
-
export { EditorProvider, useEditor } from './state/EditorContext';
|
|
44
|
-
export type { EditorProviderProps, EditorState, EditorContextValue } from './state';
|
|
45
60
|
export { blockRegistry } from './registry';
|
|
61
|
+
export { EditorProvider, useEditor } from './state/EditorContext';
|
|
62
|
+
export { BlockRenderer, BlocksRenderer } from './lib/blocks/BlockRenderer';
|
|
46
63
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAQtD;;;GAGG;AACH,MAAM,WAAW,WAAW;IACxB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,yGAAyG;IACzG,YAAY,CAAC,EAAE,qBAAqB,EAAE,CAAC;IACvC,qEAAqE;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,gBAAgB,CAAC,EAAE;QACf,iDAAiD;QACjD,KAAK,EAAE,MAAM,CAAC;QACd,gDAAgD;QAChD,IAAI,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACnC,+CAA+C;IAC/C,uBAAuB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpD;AAED;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC,CAAC,GAAG;IAC/F,YAAY,CAAC,EAAE,qBAAqB,EAAE,CAAC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB,CAAC,EAAE;QACf,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACnC,uBAAuB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjD,WAAW,CAAC,EAAE;QACV,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,YAAY,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;CACL,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,KAAK,EAAE,WAAW,2CA4K1D;AAGD,OAAO,EAAE,gBAAgB,IAAI,KAAK,EAAE,CAAC;AACrC,YAAY,EAAE,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -13,96 +13,19 @@ import { SettingsView } from './views/SettingsView';
|
|
|
13
13
|
import { NewsletterManagerView } from './views/NewsletterManager';
|
|
14
14
|
import { NewsletterEditorView } from './views/NewsletterEditor';
|
|
15
15
|
import { editorStateToAPI } from './lib/mappers/apiMapper';
|
|
16
|
+
import { resolvePluginConfig } from './lib/utils/config-resolver';
|
|
16
17
|
/**
|
|
17
18
|
* Main Router Component
|
|
18
19
|
* Handles routing within the newsletter plugin
|
|
19
|
-
*
|
|
20
|
-
* Client Handshake:
|
|
21
|
-
* - Client apps can pass customBlocks via props
|
|
22
|
-
* - Or via window.__JHITS_PLUGIN_PROPS__['plugin-newsletter'].customBlocks
|
|
23
|
-
* - The EditorProvider will automatically register these blocks
|
|
24
20
|
*/
|
|
25
21
|
export default function NewsletterPlugin(props) {
|
|
26
|
-
const { subPath, siteId, locale
|
|
27
|
-
//
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
if (propsCustomBlocks && propsCustomBlocks.length > 0) {
|
|
31
|
-
return propsCustomBlocks;
|
|
32
|
-
}
|
|
33
|
-
// Fallback to window global (for client app injection)
|
|
34
|
-
if (typeof window !== 'undefined' && window.__JHITS_PLUGIN_PROPS__) {
|
|
35
|
-
const pluginProps = window.__JHITS_PLUGIN_PROPS__['plugin-newsletter'];
|
|
36
|
-
if (pluginProps?.customBlocks) {
|
|
37
|
-
return pluginProps.customBlocks;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
return [];
|
|
41
|
-
}, [propsCustomBlocks]);
|
|
42
|
-
// Get dark mode setting from props, localStorage (dev settings), or window global
|
|
43
|
-
// Priority: localStorage (dev) > props > window global > default
|
|
44
|
-
const darkMode = useMemo(() => {
|
|
45
|
-
// First, check localStorage for dev settings (highest priority for dev)
|
|
46
|
-
if (typeof window !== 'undefined') {
|
|
47
|
-
try {
|
|
48
|
-
const saved = localStorage.getItem('__JHITS_PLUGIN_NEWSLETTER_CONFIG__');
|
|
49
|
-
if (saved) {
|
|
50
|
-
const config = JSON.parse(saved);
|
|
51
|
-
if (config.darkMode !== undefined) {
|
|
52
|
-
return config.darkMode;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
catch (e) {
|
|
57
|
-
// Ignore localStorage errors
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
// Then try props
|
|
61
|
-
if (propsDarkMode !== undefined) {
|
|
62
|
-
return propsDarkMode;
|
|
63
|
-
}
|
|
64
|
-
// Fallback to window global if prop not provided
|
|
65
|
-
if (typeof window !== 'undefined' && window.__JHITS_PLUGIN_PROPS__) {
|
|
66
|
-
const pluginProps = window.__JHITS_PLUGIN_PROPS__['plugin-newsletter'];
|
|
67
|
-
if (pluginProps?.darkMode !== undefined) {
|
|
68
|
-
return pluginProps.darkMode;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return true; // Default to dark mode enabled
|
|
72
|
-
}, [propsDarkMode]);
|
|
73
|
-
// Get background colors from props, localStorage (dev settings), or window global
|
|
74
|
-
// Priority: localStorage (dev) > props > window global
|
|
75
|
-
const backgroundColors = useMemo(() => {
|
|
76
|
-
// First, check localStorage for dev settings (highest priority for dev)
|
|
77
|
-
if (typeof window !== 'undefined') {
|
|
78
|
-
try {
|
|
79
|
-
const saved = localStorage.getItem('__JHITS_PLUGIN_NEWSLETTER_CONFIG__');
|
|
80
|
-
if (saved) {
|
|
81
|
-
const config = JSON.parse(saved);
|
|
82
|
-
if (config.backgroundColors) {
|
|
83
|
-
return config.backgroundColors;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
catch (e) {
|
|
88
|
-
// Ignore localStorage errors
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
// Then try props
|
|
92
|
-
if (propsBackgroundColors) {
|
|
93
|
-
return propsBackgroundColors;
|
|
94
|
-
}
|
|
95
|
-
// Fallback to window global
|
|
96
|
-
if (typeof window !== 'undefined' && window.__JHITS_PLUGIN_PROPS__) {
|
|
97
|
-
const pluginProps = window.__JHITS_PLUGIN_PROPS__['plugin-newsletter'];
|
|
98
|
-
if (pluginProps?.backgroundColors) {
|
|
99
|
-
return pluginProps.backgroundColors;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
return undefined;
|
|
103
|
-
}, [propsBackgroundColors]);
|
|
22
|
+
const { subPath, siteId, locale } = props;
|
|
23
|
+
// Resolve configuration from multiple sources (Props, Window, Storage)
|
|
24
|
+
const config = useMemo(() => resolvePluginConfig(props), [props]);
|
|
25
|
+
const { customBlocks, darkMode, backgroundColors, translations, emailConfig, unsubscribeTranslations } = config;
|
|
104
26
|
const route = subPath[0] || 'newsletters';
|
|
105
|
-
|
|
27
|
+
const newsletterId = subPath[1];
|
|
28
|
+
// Listen for config updates (e.g. from settings screen)
|
|
106
29
|
useEffect(() => {
|
|
107
30
|
if (typeof window === 'undefined')
|
|
108
31
|
return;
|
|
@@ -120,8 +43,7 @@ export default function NewsletterPlugin(props) {
|
|
|
120
43
|
case 'newsletters':
|
|
121
44
|
return _jsx(NewsletterManagerView, { siteId: siteId, locale: locale });
|
|
122
45
|
case 'editor':
|
|
123
|
-
|
|
124
|
-
return (_jsx(EditorProvider, { customBlocks: customBlocks, darkMode: darkMode, backgroundColors: backgroundColors, onSave: async (state, extraData) => {
|
|
46
|
+
return (_jsx(EditorProvider, { customBlocks: customBlocks, darkMode: darkMode, backgroundColors: backgroundColors, translations: translations, emailConfig: emailConfig, unsubscribeTranslations: unsubscribeTranslations, onSave: async (state, extraData) => {
|
|
125
47
|
// Save to API - create new or update existing newsletter
|
|
126
48
|
const originalId = newsletterId || state.slug;
|
|
127
49
|
const apiData = editorStateToAPI(state);
|
|
@@ -134,7 +56,6 @@ export default function NewsletterPlugin(props) {
|
|
|
134
56
|
const langParam = extraData?.language ? `?language=${extraData.language}` : '';
|
|
135
57
|
// If we have an id, try to update first
|
|
136
58
|
if (originalId) {
|
|
137
|
-
console.log('[NewsletterPlugin] Attempting to update newsletter with id:', originalId);
|
|
138
59
|
const updateResponse = await fetch(`/api/plugin-newsletter/newsletters/${originalId}${langParam}`, {
|
|
139
60
|
method: 'PUT',
|
|
140
61
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -143,31 +64,14 @@ export default function NewsletterPlugin(props) {
|
|
|
143
64
|
});
|
|
144
65
|
if (updateResponse.ok) {
|
|
145
66
|
const result = await updateResponse.json();
|
|
146
|
-
// If the id changed, update the URL
|
|
147
67
|
if (result.id && result.id !== originalId) {
|
|
148
68
|
window.history.replaceState(null, '', `/dashboard/newsletter/editor/${result.id}`);
|
|
149
69
|
}
|
|
150
70
|
return result;
|
|
151
71
|
}
|
|
152
|
-
// If 404, newsletter doesn't exist, create a new one
|
|
153
|
-
if (updateResponse.status === 404) {
|
|
154
|
-
console.log('[NewsletterPlugin] Newsletter not found, creating new newsletter');
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
157
|
-
// Other error, throw it
|
|
158
|
-
const error = await updateResponse.json();
|
|
159
|
-
console.error('[NewsletterPlugin] Save failed:', {
|
|
160
|
-
status: updateResponse.status,
|
|
161
|
-
statusText: updateResponse.statusText,
|
|
162
|
-
error,
|
|
163
|
-
});
|
|
164
|
-
const errorMessage = error.message || error.error || 'Failed to save newsletter';
|
|
165
|
-
throw new Error(errorMessage);
|
|
166
|
-
}
|
|
167
72
|
}
|
|
168
|
-
// Create new newsletter
|
|
169
|
-
|
|
170
|
-
const createResponse = await fetch('/api/plugin-newsletter/newsletters/new', {
|
|
73
|
+
// Create new newsletter
|
|
74
|
+
const createResponse = await fetch(`/api/plugin-newsletter/newsletters/new${langParam}`, {
|
|
171
75
|
method: 'POST',
|
|
172
76
|
headers: { 'Content-Type': 'application/json' },
|
|
173
77
|
credentials: 'include',
|
|
@@ -175,26 +79,18 @@ export default function NewsletterPlugin(props) {
|
|
|
175
79
|
});
|
|
176
80
|
if (!createResponse.ok) {
|
|
177
81
|
const error = await createResponse.json();
|
|
178
|
-
console.error('[NewsletterPlugin] Create failed:', {
|
|
179
|
-
status: createResponse.status,
|
|
180
|
-
statusText: createResponse.statusText,
|
|
181
|
-
error,
|
|
182
|
-
});
|
|
183
82
|
const errorMessage = error.message || error.error || 'Failed to create newsletter';
|
|
184
83
|
throw new Error(errorMessage);
|
|
185
84
|
}
|
|
186
85
|
const result = await createResponse.json();
|
|
187
|
-
// Update the URL to the new newsletter's id
|
|
188
86
|
if (result.id) {
|
|
189
87
|
window.history.replaceState(null, '', `/dashboard/newsletter/editor/${result.id}`);
|
|
190
88
|
}
|
|
191
89
|
return result;
|
|
192
90
|
}, children: _jsx(NewsletterEditorView, { newsletterId: newsletterId, siteId: siteId, locale: locale, darkMode: darkMode, backgroundColors: backgroundColors }) }));
|
|
193
91
|
case 'new':
|
|
194
|
-
return (_jsx(EditorProvider, { customBlocks: customBlocks, darkMode: darkMode, backgroundColors: backgroundColors, onSave: async (state, extraData) => {
|
|
195
|
-
// Save to API - create new newsletter
|
|
92
|
+
return (_jsx(EditorProvider, { customBlocks: customBlocks, darkMode: darkMode, backgroundColors: backgroundColors, translations: translations, emailConfig: emailConfig, unsubscribeTranslations: unsubscribeTranslations, onSave: async (state, extraData) => {
|
|
196
93
|
const apiData = editorStateToAPI(state);
|
|
197
|
-
// Include language in metadata if provided
|
|
198
94
|
if (extraData?.language) {
|
|
199
95
|
apiData.metadata = apiData.metadata || {};
|
|
200
96
|
apiData.metadata.lang = extraData.language;
|
|
@@ -210,7 +106,6 @@ export default function NewsletterPlugin(props) {
|
|
|
210
106
|
throw new Error(error.message || 'Failed to create newsletter');
|
|
211
107
|
}
|
|
212
108
|
const result = await response.json();
|
|
213
|
-
// Update the URL to the new newsletter's slug
|
|
214
109
|
if (result.slug) {
|
|
215
110
|
window.history.replaceState(null, '', `/dashboard/newsletter/editor/${result.slug}`);
|
|
216
111
|
}
|
|
@@ -219,7 +114,7 @@ export default function NewsletterPlugin(props) {
|
|
|
219
114
|
case 'subscribers':
|
|
220
115
|
return _jsx(SubscribersView, { siteId: siteId, locale: locale });
|
|
221
116
|
case 'welcome':
|
|
222
|
-
return (_jsx(EditorProvider, { customBlocks: customBlocks, darkMode: darkMode, backgroundColors: backgroundColors, isWelcomeEmail: true, onSave: async (state, extraData) => {
|
|
117
|
+
return (_jsx(EditorProvider, { customBlocks: customBlocks, darkMode: darkMode, backgroundColors: backgroundColors, translations: translations, emailConfig: emailConfig, unsubscribeTranslations: unsubscribeTranslations, isWelcomeEmail: true, onSave: async (state, extraData) => {
|
|
223
118
|
const apiData = editorStateToAPI(state);
|
|
224
119
|
const language = extraData?.language || locale || 'en';
|
|
225
120
|
const response = await fetch(`/api/plugin-newsletter/welcome-email?language=${language}`, {
|
|
@@ -240,11 +135,9 @@ export default function NewsletterPlugin(props) {
|
|
|
240
135
|
return _jsx(NewsletterManagerView, { siteId: siteId, locale: locale });
|
|
241
136
|
}
|
|
242
137
|
}
|
|
243
|
-
// Export
|
|
138
|
+
// Export symbols as needed
|
|
244
139
|
export { NewsletterPlugin as Index };
|
|
245
|
-
// Export initialization utility for easy setup
|
|
246
140
|
export { initNewsletterPlugin } from './init';
|
|
247
|
-
// Export editor state management
|
|
248
|
-
export { EditorProvider, useEditor } from './state/EditorContext';
|
|
249
|
-
// Export block registry
|
|
250
141
|
export { blockRegistry } from './registry';
|
|
142
|
+
export { EditorProvider, useEditor } from './state/EditorContext';
|
|
143
|
+
export { BlockRenderer, BlocksRenderer } from './lib/blocks/BlockRenderer';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BlockRenderer.d.ts","sourceRoot":"","sources":["../../../src/lib/blocks/BlockRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"BlockRenderer.d.ts","sourceRoot":"","sources":["../../../src/lib/blocks/BlockRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAK7D;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAC/B,sBAAsB;IACtB,KAAK,EAAE,KAAK,CAAC;IAEb,oEAAoE;IACpE,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,aAAa,CAAC,iBAAiB,CAAC,CAAC,CAAC;IAEtE,uCAAuC;IACvC,OAAO,CAAC,EAAE;QACN,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KAC1B,CAAC;CACL;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAC1B,KAAK,EACL,eAAe,EACf,OAAY,EACf,EAAE,kBAAkB,2CAiEpB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAC3B,MAAM,EACN,eAAe,EACf,OAAY,EACf,EAAE;IACC,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,aAAa,CAAC,iBAAiB,CAAC,CAAC,CAAC;IACtE,OAAO,CAAC,EAAE;QACN,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KAC1B,CAAC;CACL,2CAaA"}
|
|
@@ -8,20 +8,32 @@
|
|
|
8
8
|
'use client';
|
|
9
9
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
10
10
|
import { blockRegistry } from '../../registry/BlockRegistry';
|
|
11
|
+
import { useEditor } from '../../state/EditorContext';
|
|
11
12
|
/**
|
|
12
13
|
* Block Renderer Component
|
|
13
14
|
* Renders a single block using its Preview component from the registry
|
|
14
15
|
*/
|
|
15
16
|
export function BlockRenderer({ block, customRenderers, context = {} }) {
|
|
17
|
+
// We access the context to ensure re-render when blocks are registered
|
|
18
|
+
try {
|
|
19
|
+
useEditor();
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
// Not in editor context (e.g. preview bridge)
|
|
23
|
+
}
|
|
16
24
|
// Check for custom renderer override first
|
|
17
25
|
if (customRenderers?.has(block.type)) {
|
|
18
26
|
const CustomRenderer = customRenderers.get(block.type);
|
|
19
27
|
return _jsx(CustomRenderer, { block: block, context: context });
|
|
20
28
|
}
|
|
21
29
|
// Get block definition from registry
|
|
22
|
-
|
|
30
|
+
let definition = blockRegistry.get(block.type);
|
|
31
|
+
// If not found, try one last immediate re-check of the global window registry
|
|
32
|
+
if (!definition && typeof window !== 'undefined' && window.__JHITS_NEWSLETTER_REGISTRY__) {
|
|
33
|
+
definition = window.__JHITS_NEWSLETTER_REGISTRY__.get(block.type);
|
|
34
|
+
}
|
|
23
35
|
if (!definition) {
|
|
24
|
-
console.warn(`
|
|
36
|
+
console.warn(`[BlockRenderer] Unknown block type: ${block.type}. Registry contains:`, blockRegistry.getAll().map(b => b.type).join(', '));
|
|
25
37
|
return (_jsxs("div", { className: "p-4 border border-red-300 bg-red-50 rounded", children: [_jsxs("p", { className: "text-red-600", children: ["Unknown block type: ", block.type] }), _jsx("p", { className: "text-xs text-red-500 mt-1", children: "Make sure this block type is registered via customBlocks prop" })] }));
|
|
26
38
|
}
|
|
27
39
|
// Use the Preview component from the block definition
|
|
@@ -39,6 +39,7 @@ export declare function generateNewsletterEmailHtml(blocks: Block[], metadata: {
|
|
|
39
39
|
previewText?: string;
|
|
40
40
|
}, context: EmailRenderContext & {
|
|
41
41
|
unsubscribeUrl?: string;
|
|
42
|
+
unsubscribeText?: string;
|
|
42
43
|
footerText?: string;
|
|
43
44
|
logoUrl?: string;
|
|
44
45
|
logoAlt?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmailRenderer.d.ts","sourceRoot":"","sources":["../../../src/lib/email/EmailRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"EmailRenderer.d.ts","sourceRoot":"","sources":["../../../src/lib/email/EmailRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAG1C;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,GAAE,kBAAuB,GAAG,MAAM,CAmBzF;AAoPD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,OAAO,GAAE,kBAAuB,GAAG,MAAM,CAE7F;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CACvC,MAAM,EAAE,KAAK,EAAE,EACf,QAAQ,EAAE;IACN,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB,EACD,OAAO,EAAE,kBAAkB,GAAG;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB,GACF,MAAM,CA+GR"}
|