@jhits/plugin-newsletter 0.0.11 → 0.0.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jhits/plugin-newsletter",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "description": "Newsletter management and email delivery plugin for the JHITS ecosystem",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -30,7 +30,7 @@
30
30
  "mongodb": "^7.1.0",
31
31
  "next-auth": "^4.24.13",
32
32
  "nodemailer": "^8.0.1",
33
- "@jhits/plugin-core": "0.0.9"
33
+ "@jhits/plugin-core": "0.0.10"
34
34
  },
35
35
  "peerDependencies": {
36
36
  "next": ">=15.0.0",
@@ -51,9 +51,7 @@
51
51
  "files": [
52
52
  "src",
53
53
  "dist",
54
- "package.json",
55
- "templates",
56
- "data"
54
+ "package.json"
57
55
  ],
58
56
  "scripts": {
59
57
  "build": "tsc"
@@ -48,6 +48,7 @@ export async function GET_NEWSLETTERS(
48
48
 
49
49
  const filter = {
50
50
  ...query,
51
+ type: { $ne: 'welcome_email' },
51
52
  $or: [
52
53
  { hidden: { $exists: false } },
53
54
  { hidden: { $eq: false } }
@@ -25,7 +25,7 @@ export interface EditorProviderProps {
25
25
  /** Initial state (optional) */
26
26
  initialState?: Partial<EditorState>;
27
27
  /** Callback when save is triggered */
28
- onSave?: (state: EditorState) => Promise<void>;
28
+ onSave?: (state: EditorState, extraData?: { language?: string }) => Promise<void>;
29
29
  /**
30
30
  * Custom blocks from client application
31
31
  * These blocks will be registered in the BlockRegistry on mount
@@ -178,9 +178,9 @@ export function EditorProvider({
178
178
  dispatch({ type: 'RESET_EDITOR' });
179
179
  }, []);
180
180
 
181
- const save = useCallback(async () => {
181
+ const save = useCallback(async (extraData?: { language?: string }) => {
182
182
  if (onSave) {
183
- await onSave(stateRef.current);
183
+ await onSave(stateRef.current, extraData);
184
184
  dispatch({ type: 'MARK_CLEAN' });
185
185
  }
186
186
  }, [onSave]);
@@ -218,7 +218,7 @@ export function CanvasEditorView({ newsletterId, darkMode, backgroundColors: pro
218
218
  setIsSaving(true);
219
219
  setSaveError(null);
220
220
  try {
221
- await helpers.save();
221
+ await helpers.save(isWelcomeEmail ? { language: currentLanguage } : undefined);
222
222
  setIsSaving(false);
223
223
  } catch (error: any) {
224
224
  console.error('[CanvasEditorView] Save error:', error);
@@ -1,12 +0,0 @@
1
- {
2
- "testEmail": {
3
- "title": "SMTP Configuration Verified",
4
- "greeting": "Hello,",
5
- "description": "This is a test email to verify that your SMTP configuration is working correctly.",
6
- "successTitle": "Success!",
7
- "successMessage": "Your SMTP settings are configured correctly.",
8
- "recipientLabel": "Email sent to:",
9
- "footerText": "This is an automated test email from",
10
- "ignoreText": "If you did not expect to receive this email, you can safely ignore it."
11
- }
12
- }
@@ -1,12 +0,0 @@
1
- {
2
- "testEmail": {
3
- "title": "SMTP-configuratie geverifieerd",
4
- "greeting": "Hallo,",
5
- "description": "Dit is een test e-mail om te verifiëren dat je SMTP-configuratie correct werkt.",
6
- "successTitle": "Gelukt!",
7
- "successMessage": "Je SMTP-instellingen zijn correct geconfigureerd.",
8
- "recipientLabel": "E-mail verzonden naar:",
9
- "footerText": "Dit is een automatische test e-mail van",
10
- "ignoreText": "Als je deze e-mail niet verwacht, kun je hem negeren."
11
- }
12
- }
@@ -1,12 +0,0 @@
1
- {
2
- "testEmail": {
3
- "title": "SMTP-konfiguration verifierad",
4
- "greeting": "Hej,",
5
- "description": "Detta är ett testmeddelande för att verifiera att din SMTP-konfiguration fungerar korrekt.",
6
- "successTitle": "Framgång!",
7
- "successMessage": "Dina SMTP-inställningar är korrekt konfigurerade.",
8
- "recipientLabel": "E-post skickat till:",
9
- "footerText": "Detta är ett automatiskt testmeddelande från",
10
- "ignoreText": "Om du inte förväntade dig detta e-postmeddelande kan du ignorera det."
11
- }
12
- }
@@ -1,98 +0,0 @@
1
- --- a/packages/plugin-newsletter/src/index.tsx
2
- +++ b/packages/plugin-newsletter/src/index.tsx
3
- @@ -165,7 +165,50 @@ export default function NewsletterPlugin(props: PluginProps) {
4
- onSave={async (state) => {
5
- - // Save to API - update existing newsletter
6
- + // Save to API - create new or update existing newsletter
7
- const originalSlug = newsletterSlug || state.slug;
8
- - if (!originalSlug) {
9
- - throw new Error('Cannot save: no newsletter identifier available. Please reload the page.');
10
- - }
11
- - console.log('[NewsletterPlugin] Saving newsletter with slug:', originalSlug);
12
- const apiData = editorStateToAPI(state);
13
- - const response = await fetch(`/api/plugin-newsletter/newsletters/${originalSlug}`, {
14
- - method: 'PUT',
15
- - headers: { 'Content-Type': 'application/json' },
16
- - credentials: 'include',
17
- - body: JSON.stringify(apiData),
18
- - });
19
- - if (!response.ok) {
20
- - const error = await response.json();
21
- - console.error('[NewsletterPlugin] Save failed:', {
22
- - status: response.status,
23
- - statusText: response.statusText,
24
- - error,
25
- - });
26
- - const errorMessage = error.message || error.error || 'Failed to save newsletter';
27
- - throw new Error(errorMessage);
28
- - }
29
- - const result = await response.json();
30
- - // If the slug changed, update the URL
31
- - if (result.slug && result.slug !== originalSlug) {
32
- - window.history.replaceState(null, '', `/dashboard/newsletter/editor/${result.slug}`);
33
- - }
34
- - return result;
35
- +
36
- + // If we have a slug, try to update first
37
- + if (originalSlug) {
38
- + console.log('[NewsletterPlugin] Attempting to update newsletter with slug:', originalSlug);
39
- + const updateResponse = await fetch(`/api/plugin-newsletter/newsletters/${originalSlug}`, {
40
- + method: 'PUT',
41
- + headers: { 'Content-Type': 'application/json' },
42
- + credentials: 'include',
43
- + body: JSON.stringify(apiData),
44
- + });
45
- +
46
- + if (updateResponse.ok) {
47
- + const result = await updateResponse.json();
48
- + // If the slug changed, update the URL
49
- + if (result.slug && result.slug !== originalSlug) {
50
- + window.history.replaceState(null, '', `/dashboard/newsletter/editor/${result.slug}`);
51
- + }
52
- + return result;
53
- + }
54
- +
55
- + // If 404, newsletter doesn't exist, create a new one
56
- + if (updateResponse.status === 404) {
57
- + console.log('[NewsletterPlugin] Newsletter not found, creating new newsletter');
58
- + } else {
59
- + // Other error, throw it
60
- + const error = await updateResponse.json();
61
- + console.error('[NewsletterPlugin] Save failed:', {
62
- + status: updateResponse.status,
63
- + statusText: updateResponse.statusText,
64
- + error,
65
- + });
66
- + const errorMessage = error.message || error.error || 'Failed to save newsletter';
67
- + throw new Error(errorMessage);
68
- + }
69
- + }
70
- +
71
- + // Create new newsletter (either no slug or update returned 404)
72
- + console.log('[NewsletterPlugin] Creating new newsletter');
73
- + const createResponse = await fetch('/api/plugin-newsletter/newsletters/new', {
74
- + method: 'POST',
75
- + headers: { 'Content-Type': 'application/json' },
76
- + credentials: 'include',
77
- + body: JSON.stringify(apiData),
78
- + });
79
- +
80
- + if (!createResponse.ok) {
81
- + const error = await createResponse.json();
82
- + console.error('[NewsletterPlugin] Create failed:', {
83
- + status: createResponse.status,
84
- + statusText: createResponse.statusText,
85
- + error,
86
- + });
87
- + const errorMessage = error.message || error.error || 'Failed to create newsletter';
88
- + throw new Error(errorMessage);
89
- + }
90
- +
91
- + const result = await createResponse.json();
92
- + // Update the URL to the new newsletter's slug
93
- + if (result.slug) {
94
- + window.history.replaceState(null, '', `/dashboard/newsletter/editor/${result.slug}`);
95
- + }
96
- + return result;
97
- }}
98
- >
Binary file
@@ -1,221 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
-
4
- <head>
5
- <meta charset="utf-8">
6
- <style>
7
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap');
8
-
9
- body {
10
- background-color: #fafaf9;
11
- margin: 0;
12
- padding: 0;
13
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
14
- }
15
-
16
- .container {
17
- max-width: 520px;
18
- margin: 40px auto;
19
- background-color: #ffffff;
20
- border-radius: 24px;
21
- border: 1px solid #e5e5e5;
22
- overflow: hidden;
23
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
24
- }
25
-
26
- .header {
27
- padding: 32px 40px 28px 40px;
28
- text-align: center;
29
- background: linear-gradient(135deg, #5b21b6 0%, #7c3aed 100%);
30
- }
31
-
32
- .logo-container {
33
- display: inline-flex;
34
- align-items: center;
35
- }
36
-
37
- .logo-frame {
38
- width: 66px;
39
- height: 66px;
40
- background: rgba(255, 255, 255, 0.15);
41
- border: 1px solid rgba(255, 255, 255, 0.3);
42
- border-radius: 14px;
43
- display: flex;
44
- align-items: center;
45
- justify-content: center;
46
- }
47
-
48
- .logo-img {
49
- width: 66px;
50
- height: 66px;
51
- }
52
-
53
- .logo-text {
54
- text-align: left;
55
- margin-left: 12px;
56
- }
57
-
58
- .logo-jhits {
59
- font-size: 36px;
60
- font-weight: 700;
61
- color: white;
62
- line-height: 1;
63
- letter-spacing: 0.02em;
64
- }
65
-
66
- .logo-sub {
67
- display: flex;
68
- align-items: center;
69
- gap: 8px;
70
- margin-top: 6px;
71
- }
72
-
73
- .logo-platform {
74
- font-size: 10px;
75
- font-weight: 700;
76
- letter-spacing: 0.35em;
77
- color: rgba(255, 255, 255, 0.6);
78
- text-transform: uppercase;
79
- }
80
-
81
- .logo-v2 {
82
- font-size: 10px;
83
- font-weight: 700;
84
- font-style: italic;
85
- color: #c4b5fd;
86
- text-transform: uppercase;
87
- }
88
-
89
- .content {
90
- padding: 32px 40px 40px 40px;
91
- color: #18181b;
92
- line-height: 1.6;
93
- font-size: 14px;
94
- }
95
-
96
- .footer {
97
- padding: 24px 40px;
98
- text-align: center;
99
- font-size: 11px;
100
- color: #71717a;
101
- border-top: 1px solid #f4f4f5;
102
- background-color: #fafaf9;
103
- }
104
-
105
- h1 {
106
- font-weight: 800;
107
- font-size: 24px;
108
- margin-bottom: 20px;
109
- color: #18181b;
110
- text-align: center;
111
- letter-spacing: -0.025em;
112
- }
113
-
114
- .highlight {
115
- background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
116
- border: 1px solid #22c55e;
117
- border-radius: 12px;
118
- padding: 20px;
119
- margin: 24px 0;
120
- text-align: center;
121
- }
122
-
123
- .highlight-icon {
124
- font-size: 32px;
125
- margin-bottom: 8px;
126
- }
127
-
128
- .highlight-text {
129
- font-size: 14px;
130
- color: #166534;
131
- font-weight: 600;
132
- }
133
-
134
- .badge {
135
- display: inline-block;
136
- background: linear-gradient(135deg, #5b21b6 0%, #7c3aed 100%);
137
- color: white;
138
- font-size: 10px;
139
- font-weight: 800;
140
- text-transform: uppercase;
141
- letter-spacing: 0.1em;
142
- padding: 6px 12px;
143
- border-radius: 6px;
144
- margin-bottom: 20px;
145
- }
146
-
147
- .recipient {
148
- background-color: #f4f4f5;
149
- padding: 8px 16px;
150
- border-radius: 8px;
151
- font-family: monospace;
152
- font-size: 13px;
153
- color: #5b21b6;
154
- }
155
-
156
- .divider {
157
- height: 1px;
158
- background: linear-gradient(to right, transparent, #e5e5e5, transparent);
159
- margin: 24px 0;
160
- }
161
- </style>
162
- </head>
163
-
164
- <body>
165
- <div class="container">
166
- <div class="header">
167
- <div class="logo-container">
168
- <div class="logo-frame">
169
- <img src="cid:logo" alt="JHITS" class="logo-img">
170
- </div>
171
- <div class="logo-text">
172
- <div class="logo-jhits">JHITS</div>
173
- <div class="logo-sub">
174
- <span class="logo-platform">Platform</span>
175
- <span class="logo-v2">V2</span>
176
- </div>
177
- </div>
178
- </div>
179
- </div>
180
- <div class="content">
181
- <div style="text-align: center;">
182
- <span class="badge">Test Email</span>
183
- </div>
184
-
185
- <h1>{{title}}</h1>
186
-
187
- <p>{{greeting}}</p>
188
-
189
- <p>{{description}}</p>
190
-
191
- <div class="highlight">
192
- <div class="highlight-icon">✓</div>
193
- <div class="highlight-text">
194
- {{successTitle}} {{successMessage}}
195
- </div>
196
- </div>
197
-
198
- <p>{{recipientLabel}}</p>
199
- <div style="text-align: center;">
200
- <span class="recipient">{{recipientEmail}}</span>
201
- </div>
202
-
203
- <div class="divider"></div>
204
-
205
- <p style="text-align: center; font-size: 12px; color: #71717a;">
206
- {{footerText}}<br />
207
- <strong>{{fromName}}</strong>
208
- </p>
209
-
210
- <p style="text-align: center; font-size: 11px; color: #a1a1aa; margin-top: 20px;">
211
- {{ignoreText}}
212
- </p>
213
- </div>
214
- <div class="footer">
215
- © {{currentYear}} {{fromName}}. All rights reserved.<br />
216
- <span style="color: #5b21b6; font-weight: 600;">Powered by JHITS</span>
217
- </div>
218
- </div>
219
- </body>
220
-
221
- </html>
@@ -1,35 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8">
5
- <style>
6
- body { background-color: #faf9f6; margin: 0; padding: 0; font-family: 'Georgia', serif; }
7
- .container { max-width: 600px; margin: 20px auto; background-color: #ffffff; border-radius: 40px; border: 1px solid #1a2e260d; overflow: hidden; }
8
- .header { padding: 40px 0 20px 0; text-align: center; }
9
- .logo { width: 180px; height: auto; }
10
- .content { padding: 0 50px 40px 50px; color: #1a2e26; line-height: 1.8; font-size: 15px; }
11
- .footer { padding: 40px 50px; text-align: center; font-family: sans-serif; font-size: 10px; color: #a1a1aa; letter-spacing: 1px; border-top: 1px solid #faf9f6; }
12
- h1 { font-weight: normal; font-style: italic; font-size: 30px; margin-bottom: 30px; color: #1a2e26; text-align: center; }
13
- .divider { height: 1px; width: 40px; background-color: #1a2e2620; margin: 30px auto; }
14
- </style>
15
- </head>
16
- <body>
17
- <div class="container">
18
- <div class="header">
19
- <img src="cid:botanics-logo" alt="{{fromName}}" class="logo">
20
- </div>
21
- <div class="content">
22
- <h1>{{title}}</h1>
23
- <div class="divider"></div>
24
- <div>{{{message}}}</div>
25
- </div>
26
- <div class="footer">
27
- <strong>{{fromName}}</strong> &copy; {{currentYear}}<br/>
28
- {{tagline}}<br/><br/>
29
- <a href="{{unsubscribeUrl}}" style="color: #a1a1aa; text-decoration: underline;">
30
- {{unsubscribeText}}
31
- </a>
32
- </div>
33
- </div>
34
- </body>
35
- </html>