@jhits/plugin-newsletter 0.0.15 → 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.
Files changed (90) hide show
  1. package/dist/api/email-utils.d.ts.map +1 -1
  2. package/dist/api/email-utils.js +45 -4
  3. package/dist/api/handlers/newsletters.d.ts.map +1 -1
  4. package/dist/api/handlers/newsletters.js +33 -16
  5. package/dist/api/handlers/send-newsletter.d.ts.map +1 -1
  6. package/dist/api/handlers/send-newsletter.js +54 -6
  7. package/dist/api/handlers/settings.d.ts.map +1 -1
  8. package/dist/api/handlers/settings.js +51 -1
  9. package/dist/index.d.ts +27 -10
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +15 -122
  12. package/dist/lib/blocks/BlockRenderer.d.ts.map +1 -1
  13. package/dist/lib/blocks/BlockRenderer.js +14 -2
  14. package/dist/lib/email/EmailRenderer.d.ts +1 -0
  15. package/dist/lib/email/EmailRenderer.d.ts.map +1 -1
  16. package/dist/lib/email/EmailRenderer.js +31 -19
  17. package/dist/lib/utils/config-resolver.d.ts +33 -0
  18. package/dist/lib/utils/config-resolver.d.ts.map +1 -0
  19. package/dist/lib/utils/config-resolver.js +47 -0
  20. package/dist/registry/BlockRegistry.d.ts +9 -1
  21. package/dist/registry/BlockRegistry.d.ts.map +1 -1
  22. package/dist/registry/BlockRegistry.js +126 -8
  23. package/dist/state/EditorContext.d.ts +11 -1
  24. package/dist/state/EditorContext.d.ts.map +1 -1
  25. package/dist/state/EditorContext.js +23 -5
  26. package/dist/state/types.d.ts +12 -0
  27. package/dist/state/types.d.ts.map +1 -1
  28. package/dist/types/block.d.ts +9 -0
  29. package/dist/types/block.d.ts.map +1 -1
  30. package/dist/types/newsletter.d.ts +4 -0
  31. package/dist/types/newsletter.d.ts.map +1 -1
  32. package/dist/views/CanvasEditor/BlockWrapper.d.ts.map +1 -1
  33. package/dist/views/CanvasEditor/BlockWrapper.js +24 -3
  34. package/dist/views/CanvasEditor/CanvasEditorView.d.ts.map +1 -1
  35. package/dist/views/CanvasEditor/CanvasEditorView.js +77 -17
  36. package/dist/views/CanvasEditor/EditorBody.d.ts.map +1 -1
  37. package/dist/views/CanvasEditor/EditorBody.js +1 -1
  38. package/dist/views/CanvasEditor/components/EditorCanvas.d.ts.map +1 -1
  39. package/dist/views/CanvasEditor/components/EditorCanvas.js +158 -100
  40. package/dist/views/CanvasEditor/components/EditorSidebar.d.ts +3 -1
  41. package/dist/views/CanvasEditor/components/EditorSidebar.d.ts.map +1 -1
  42. package/dist/views/CanvasEditor/components/EditorSidebar.js +3 -3
  43. package/dist/views/CanvasEditor/hooks/useRegisteredBlocks.d.ts +1 -1
  44. package/dist/views/CanvasEditor/hooks/useRegisteredBlocks.d.ts.map +1 -1
  45. package/dist/views/CanvasEditor/hooks/useRegisteredBlocks.js +6 -40
  46. package/dist/views/NewsletterManager.d.ts.map +1 -1
  47. package/dist/views/NewsletterManager.js +87 -5
  48. package/dist/views/components/DomainPromptModal.d.ts +13 -0
  49. package/dist/views/components/DomainPromptModal.d.ts.map +1 -0
  50. package/dist/views/components/DomainPromptModal.js +58 -0
  51. package/dist/views/components/NewsletterCard.d.ts +16 -0
  52. package/dist/views/components/NewsletterCard.d.ts.map +1 -0
  53. package/dist/views/components/NewsletterCard.js +94 -0
  54. package/dist/views/components/NewsletterGrid.d.ts +16 -0
  55. package/dist/views/components/NewsletterGrid.d.ts.map +1 -0
  56. package/dist/views/components/NewsletterGrid.js +13 -0
  57. package/dist/views/components/SendNewsletterModal.d.ts.map +1 -1
  58. package/dist/views/components/SendNewsletterModal.js +91 -22
  59. package/dist/views/components/SmtpSettingsModal.d.ts.map +1 -1
  60. package/dist/views/components/SmtpSettingsModal.js +10 -0
  61. package/dist/views/components/TestEmailModal.d.ts.map +1 -1
  62. package/dist/views/components/TestEmailModal.js +86 -17
  63. package/package.json +53 -9
  64. package/src/api/email-utils.ts +53 -4
  65. package/src/api/handlers/newsletters.ts +40 -20
  66. package/src/api/handlers/send-newsletter.ts +65 -6
  67. package/src/api/handlers/settings.ts +60 -2
  68. package/src/index.tsx +49 -155
  69. package/src/lib/blocks/BlockRenderer.tsx +16 -2
  70. package/src/lib/email/EmailRenderer.tsx +31 -20
  71. package/src/lib/utils/config-resolver.ts +71 -0
  72. package/src/registry/BlockRegistry.tsx +255 -0
  73. package/src/state/EditorContext.tsx +43 -8
  74. package/src/state/types.ts +16 -0
  75. package/src/types/block.ts +10 -0
  76. package/src/types/newsletter.ts +5 -0
  77. package/src/views/CanvasEditor/BlockWrapper.tsx +27 -2
  78. package/src/views/CanvasEditor/CanvasEditorView.tsx +142 -61
  79. package/src/views/CanvasEditor/EditorBody.tsx +17 -13
  80. package/src/views/CanvasEditor/components/EditorCanvas.tsx +178 -115
  81. package/src/views/CanvasEditor/components/EditorSidebar.tsx +57 -2
  82. package/src/views/CanvasEditor/hooks/useRegisteredBlocks.ts +6 -45
  83. package/src/views/NewsletterManager.tsx +164 -6
  84. package/src/views/components/DomainPromptModal.tsx +160 -0
  85. package/src/views/components/NewsletterCard.tsx +212 -0
  86. package/src/views/components/NewsletterGrid.tsx +48 -0
  87. package/src/views/components/SendNewsletterModal.tsx +270 -184
  88. package/src/views/components/SmtpSettingsModal.tsx +11 -0
  89. package/src/views/components/TestEmailModal.tsx +235 -149
  90. package/src/registry/BlockRegistry.ts +0 -53
@@ -6,6 +6,7 @@
6
6
 
7
7
  import React, { useState, useEffect } from 'react';
8
8
  import { X, Send, RefreshCw, CheckCircle2, AlertCircle, Mail, Globe, Sparkles, MailCheck } from 'lucide-react';
9
+ import { DomainPromptModal } from './DomainPromptModal';
9
10
 
10
11
  interface TestEmailModalProps {
11
12
  isOpen: boolean;
@@ -29,10 +30,15 @@ export function TestEmailModal({ isOpen, onClose }: TestEmailModalProps) {
29
30
  const [isSending, setIsSending] = useState(false);
30
31
  const [sendSuccess, setSendSuccess] = useState(false);
31
32
  const [sendError, setSendError] = useState<string | null>(null);
33
+
34
+ // Domain prompt state
35
+ const [showDomainPrompt, setShowDomainPrompt] = useState(false);
36
+ const [isCheckingDomain, setIsCheckingDomain] = useState(false);
32
37
 
33
38
  useEffect(() => {
34
39
  if (isOpen) {
35
40
  fetchWelcomeEmailStatus();
41
+ setShowDomainPrompt(false);
36
42
  }
37
43
  }, [isOpen]);
38
44
 
@@ -53,7 +59,7 @@ export function TestEmailModal({ isOpen, onClose }: TestEmailModalProps) {
53
59
  }
54
60
  };
55
61
 
56
- const handleSend = async () => {
62
+ const handleSend = async (skipDomainCheck = false) => {
57
63
  if (!email.trim()) {
58
64
  setSendError('Please enter an email address');
59
65
  return;
@@ -65,6 +71,33 @@ export function TestEmailModal({ isOpen, onClose }: TestEmailModalProps) {
65
71
  return;
66
72
  }
67
73
 
74
+ // Check if we need to prompt for domain first (usually helpful in dev mode)
75
+ if (!skipDomainCheck) {
76
+ try {
77
+ setIsCheckingDomain(true);
78
+ // Check if this language has a domain configured
79
+ const response = await fetch('/api/plugin-website/settings');
80
+ const siteConfig = await response.json();
81
+
82
+ const hasDomain = siteConfig.domainLocaleConfig?.some((c: any) =>
83
+ c.locale === language &&
84
+ c.domain &&
85
+ c.domain !== 'undefined' &&
86
+ c.domain.trim() !== ''
87
+ );
88
+
89
+ if (!hasDomain) {
90
+ setShowDomainPrompt(true);
91
+ setIsCheckingDomain(false);
92
+ return; // Stop here, wait for domain prompt
93
+ }
94
+ } catch (error) {
95
+ console.warn('Failed to check domain configuration:', error);
96
+ } finally {
97
+ setIsCheckingDomain(false);
98
+ }
99
+ }
100
+
68
101
  try {
69
102
  setIsSending(true);
70
103
  setSendError(null);
@@ -79,6 +112,12 @@ export function TestEmailModal({ isOpen, onClose }: TestEmailModalProps) {
79
112
  const data = await response.json();
80
113
 
81
114
  if (!response.ok) {
115
+ // If server detects missing domain, trigger prompt
116
+ if (data.code === 'DOMAIN_MISSING') {
117
+ setShowDomainPrompt(true);
118
+ setIsSending(false);
119
+ return;
120
+ }
82
121
  throw new Error(data.error || 'Failed to send test email');
83
122
  }
84
123
 
@@ -96,173 +135,220 @@ export function TestEmailModal({ isOpen, onClose }: TestEmailModalProps) {
96
135
  }
97
136
  };
98
137
 
138
+ const handleSaveDomain = async (domain: string) => {
139
+ try {
140
+ // Get current site config
141
+ const response = await fetch('/api/plugin-website/settings');
142
+ const siteConfig = await response.json();
143
+
144
+ const currentConfigs = siteConfig.domainLocaleConfig || [];
145
+ const newConfigs = [...currentConfigs];
146
+
147
+ const existingIndex = newConfigs.findIndex((c: any) => c.locale === language);
148
+ if (existingIndex >= 0) {
149
+ newConfigs[existingIndex] = { ...newConfigs[existingIndex], domain };
150
+ } else {
151
+ newConfigs.push({ locale: language, domain });
152
+ }
153
+
154
+ // Save updated site config
155
+ const saveResponse = await fetch('/api/plugin-website/settings', {
156
+ method: 'POST',
157
+ headers: { 'Content-Type': 'application/json' },
158
+ body: JSON.stringify({
159
+ ...siteConfig,
160
+ domainLocaleConfig: newConfigs
161
+ }),
162
+ });
163
+
164
+ if (!saveResponse.ok) throw new Error('Failed to save website settings');
165
+
166
+ // Re-trigger send with domain check skipped
167
+ setShowDomainPrompt(false);
168
+ handleSend(true);
169
+ } catch (error) {
170
+ console.error('Failed to save domain:', error);
171
+ throw error;
172
+ }
173
+ };
174
+
99
175
  if (!isOpen) return null;
100
176
 
101
177
  return (
102
- <div className="fixed inset-0 z-50 flex items-center justify-center">
103
- {/* Backdrop */}
104
- <div
105
- className="absolute inset-0 bg-black/50 backdrop-blur-sm"
106
- onClick={() => !isSending && onClose()}
107
- />
108
-
109
- {/* Modal */}
110
- <div className="relative w-full max-w-md mx-4 bg-white dark:bg-neutral-900 rounded-3xl border border-dashboard-border shadow-2xl overflow-hidden">
111
- {/* Header */}
112
- <div className="flex items-center justify-between px-8 py-6 border-b border-dashboard-border">
113
- <div className="flex items-center gap-3">
114
- <div className="w-10 h-10 rounded-2xl bg-primary/10 flex items-center justify-center">
115
- <Mail className="w-5 h-5 text-primary" />
116
- </div>
117
- <div>
118
- <h2 className="text-xl font-black text-dashboard-text uppercase tracking-tight">
119
- Test Email
120
- </h2>
121
- <p className="text-xs text-dashboard-text-secondary">
122
- Send a test email to verify SMTP
123
- </p>
124
- </div>
125
- </div>
126
- <button
127
- onClick={() => !isSending && onClose()}
128
- disabled={isSending}
129
- className="p-2 rounded-full hover:bg-dashboard-border transition-colors"
130
- >
131
- <X size={20} className="text-dashboard-text-secondary" />
132
- </button>
133
- </div>
134
-
135
- {/* Content */}
136
- <div className="p-8">
137
- <div className="space-y-4">
138
- {/* Email Type Toggle */}
139
- <div>
140
- <label className="text-xs font-bold text-dashboard-text-secondary uppercase tracking-widest block mb-2">
141
- Email Template
142
- </label>
143
- <div className="flex gap-2">
144
- <button
145
- onClick={() => setEmailType('test')}
146
- className={`flex-1 flex items-center justify-center gap-2 px-4 py-3 rounded-xl text-xs font-bold uppercase tracking-widest transition-colors ${
147
- emailType === 'test'
148
- ? 'bg-primary text-white'
149
- : 'bg-dashboard-bg border border-dashboard-border text-dashboard-text hover:bg-dashboard-border'
150
- }`}
151
- >
152
- <Mail size={14} />
153
- Test
154
- </button>
155
- <button
156
- onClick={() => setEmailType('welcome')}
157
- disabled={!welcomeEmailConfigured}
158
- className={`flex-1 flex items-center justify-center gap-2 px-4 py-3 rounded-xl text-xs font-bold uppercase tracking-widest transition-colors ${
159
- emailType === 'welcome'
160
- ? 'bg-primary text-white'
161
- : welcomeEmailConfigured
162
- ? 'bg-dashboard-bg border border-dashboard-border text-dashboard-text hover:bg-dashboard-border'
163
- : 'bg-dashboard-bg border border-dashboard-border text-dashboard-text-secondary opacity-50 cursor-not-allowed'
164
- }`}
165
- >
166
- <Sparkles size={14} />
167
- Welcome
168
- {!welcomeEmailConfigured && (
169
- <span className="text-[9px] normal-case opacity-70">(Not configured)</span>
170
- )}
171
- </button>
178
+ <>
179
+ <div className="fixed inset-0 z-50 flex items-center justify-center">
180
+ {/* Backdrop */}
181
+ <div
182
+ className="absolute inset-0 bg-black/50 backdrop-blur-sm"
183
+ onClick={() => !isSending && onClose()}
184
+ />
185
+
186
+ {/* Modal */}
187
+ <div className="relative w-full max-w-md mx-4 bg-white dark:bg-neutral-900 rounded-3xl border border-dashboard-border shadow-2xl overflow-hidden">
188
+ {/* Header */}
189
+ <div className="flex items-center justify-between px-8 py-6 border-b border-dashboard-border">
190
+ <div className="flex items-center gap-3">
191
+ <div className="w-10 h-10 rounded-2xl bg-primary/10 flex items-center justify-center">
192
+ <Mail className="w-5 h-5 text-primary" />
193
+ </div>
194
+ <div>
195
+ <h2 className="text-xl font-black text-dashboard-text uppercase tracking-tight">
196
+ Test Email
197
+ </h2>
198
+ <p className="text-xs text-dashboard-text-secondary">
199
+ Send a test email to verify SMTP
200
+ </p>
172
201
  </div>
173
202
  </div>
203
+ <button
204
+ onClick={() => !isSending && onClose()}
205
+ disabled={isSending}
206
+ className="p-2 rounded-full hover:bg-dashboard-border transition-colors"
207
+ >
208
+ <X size={20} className="text-dashboard-text-secondary" />
209
+ </button>
210
+ </div>
174
211
 
175
- <div>
176
- <label className="text-xs font-bold text-dashboard-text-secondary uppercase tracking-widest block mb-2">
177
- Recipient Email
178
- </label>
179
- <input
180
- type="email"
181
- value={email}
182
- onChange={(e) => {
183
- setEmail(e.target.value);
184
- setSendError(null);
185
- }}
186
- onKeyDown={(e) => e.key === 'Enter' && handleSend()}
187
- placeholder="your@email.com"
188
- className="w-full px-4 py-3 bg-dashboard-bg border border-dashboard-border rounded-xl outline-none focus:ring-2 focus:ring-primary transition-colors text-dashboard-text"
189
- />
190
- </div>
191
-
192
- <div>
193
- <label className="text-xs font-bold text-dashboard-text-secondary uppercase tracking-widest block mb-2">
194
- <Globe size={12} className="inline mr-1" />
195
- Language
196
- </label>
197
- <div className="flex gap-2">
198
- {languages.map((lang) => (
212
+ {/* Content */}
213
+ <div className="p-8">
214
+ <div className="space-y-4">
215
+ {/* Email Type Toggle */}
216
+ <div>
217
+ <label className="text-xs font-bold text-dashboard-text-secondary uppercase tracking-widest block mb-2">
218
+ Email Template
219
+ </label>
220
+ <div className="flex gap-2">
199
221
  <button
200
- key={lang.code}
201
- onClick={() => setLanguage(lang.code)}
202
- className={`px-4 py-2 rounded-xl text-xs font-bold uppercase tracking-widest transition-colors ${
203
- language === lang.code
222
+ onClick={() => setEmailType('test')}
223
+ className={`flex-1 flex items-center justify-center gap-2 px-4 py-3 rounded-xl text-xs font-bold uppercase tracking-widest transition-colors ${
224
+ emailType === 'test'
204
225
  ? 'bg-primary text-white'
205
226
  : 'bg-dashboard-bg border border-dashboard-border text-dashboard-text hover:bg-dashboard-border'
206
227
  }`}
207
228
  >
208
- {lang.label}
229
+ <Mail size={14} />
230
+ Test
231
+ </button>
232
+ <button
233
+ onClick={() => setEmailType('welcome')}
234
+ disabled={!welcomeEmailConfigured}
235
+ className={`flex-1 flex items-center justify-center gap-2 px-4 py-3 rounded-xl text-xs font-bold uppercase tracking-widest transition-colors ${
236
+ emailType === 'welcome'
237
+ ? 'bg-primary text-white'
238
+ : welcomeEmailConfigured
239
+ ? 'bg-dashboard-bg border border-dashboard-border text-dashboard-text hover:bg-dashboard-border'
240
+ : 'bg-dashboard-bg border border-dashboard-border text-dashboard-text-secondary opacity-50 cursor-not-allowed'
241
+ }`}
242
+ >
243
+ <Sparkles size={14} />
244
+ Welcome
245
+ {!welcomeEmailConfigured && (
246
+ <span className="text-[9px] normal-case opacity-70">(Not configured)</span>
247
+ )}
209
248
  </button>
210
- ))}
249
+ </div>
250
+ </div>
251
+
252
+ <div>
253
+ <label className="text-xs font-bold text-dashboard-text-secondary uppercase tracking-widest block mb-2">
254
+ Recipient Email
255
+ </label>
256
+ <input
257
+ type="email"
258
+ value={email}
259
+ onChange={(e) => {
260
+ setEmail(e.target.value);
261
+ setSendError(null);
262
+ }}
263
+ onKeyDown={(e) => e.key === 'Enter' && handleSend()}
264
+ placeholder="your@email.com"
265
+ className="w-full px-4 py-3 bg-dashboard-bg border border-dashboard-border rounded-xl outline-none focus:ring-2 focus:ring-primary transition-colors text-dashboard-text"
266
+ />
211
267
  </div>
212
- </div>
213
268
 
214
- {sendError && (
215
- <div className="flex items-center gap-2 text-red-500 text-sm">
216
- <AlertCircle size={16} />
217
- {sendError}
269
+ <div>
270
+ <label className="text-xs font-bold text-dashboard-text-secondary uppercase tracking-widest block mb-2">
271
+ <Globe size={12} className="inline mr-1" />
272
+ Language
273
+ </label>
274
+ <div className="flex gap-2">
275
+ {languages.map((lang) => (
276
+ <button
277
+ key={lang.code}
278
+ onClick={() => setLanguage(lang.code)}
279
+ className={`px-4 py-2 rounded-xl text-xs font-bold uppercase tracking-widest transition-colors ${
280
+ language === lang.code
281
+ ? 'bg-primary text-white'
282
+ : 'bg-dashboard-bg border border-dashboard-border text-dashboard-text hover:bg-dashboard-border'
283
+ }`}
284
+ >
285
+ {lang.label}
286
+ </button>
287
+ ))}
288
+ </div>
218
289
  </div>
219
- )}
220
290
 
221
- <p className="text-xs text-dashboard-text-secondary">
222
- A test email will be sent to this address using your configured SMTP settings.
223
- </p>
291
+ {sendError && (
292
+ <div className="flex items-center gap-2 text-red-500 text-sm">
293
+ <AlertCircle size={16} />
294
+ {sendError}
295
+ </div>
296
+ )}
297
+
298
+ <p className="text-xs text-dashboard-text-secondary">
299
+ A test email will be sent to this address using your configured SMTP settings.
300
+ </p>
301
+ </div>
224
302
  </div>
225
- </div>
226
303
 
227
- {/* Footer */}
228
- <div className="flex items-center justify-end gap-3 px-8 py-6 border-t border-dashboard-border bg-dashboard-bg/50">
229
- <button
230
- onClick={onClose}
231
- disabled={isSending}
232
- className="px-6 py-3 rounded-full text-[10px] font-black uppercase tracking-widest transition-colors bg-dashboard-bg border border-dashboard-border text-dashboard-text hover:bg-dashboard-border"
233
- >
234
- Cancel
235
- </button>
236
- <button
237
- onClick={handleSend}
238
- disabled={isSending || sendSuccess}
239
- className={`inline-flex items-center gap-2 px-6 py-3 rounded-full text-[10px] font-black uppercase tracking-widest transition-colors shadow-lg ${
240
- isSending
241
- ? 'bg-neutral-400 text-white cursor-not-allowed'
242
- : sendSuccess
243
- ? 'bg-green-600 text-white'
244
- : 'bg-primary text-white hover:bg-primary/90'
245
- }`}
246
- >
247
- {isSending ? (
248
- <>
249
- <RefreshCw className="w-4 h-4 animate-spin" />
250
- Sending...
251
- </>
252
- ) : sendSuccess ? (
253
- <>
254
- <CheckCircle2 className="w-4 h-4" />
255
- Sent!
256
- </>
257
- ) : (
258
- <>
259
- <Send className="w-4 h-4" />
260
- Send Test
261
- </>
262
- )}
263
- </button>
304
+ {/* Footer */}
305
+ <div className="flex items-center justify-end gap-3 px-8 py-6 border-t border-dashboard-border bg-dashboard-bg/50">
306
+ <button
307
+ onClick={onClose}
308
+ disabled={isSending}
309
+ className="px-6 py-3 rounded-full text-[10px] font-black uppercase tracking-widest transition-colors bg-dashboard-bg border border-dashboard-border text-dashboard-text hover:bg-dashboard-border"
310
+ >
311
+ Cancel
312
+ </button>
313
+ <button
314
+ onClick={() => handleSend()}
315
+ disabled={isSending || sendSuccess || isCheckingDomain}
316
+ className={`inline-flex items-center gap-2 px-6 py-3 rounded-full text-[10px] font-black uppercase tracking-widest transition-colors shadow-lg ${
317
+ isSending || isCheckingDomain
318
+ ? 'bg-neutral-400 text-white cursor-not-allowed'
319
+ : sendSuccess
320
+ ? 'bg-green-600 text-white'
321
+ : 'bg-primary text-white hover:bg-primary/90'
322
+ }`}
323
+ >
324
+ {isSending || isCheckingDomain ? (
325
+ <>
326
+ <RefreshCw className="w-4 h-4 animate-spin" />
327
+ {isCheckingDomain ? 'Checking...' : 'Sending...'}
328
+ </>
329
+ ) : sendSuccess ? (
330
+ <>
331
+ <CheckCircle2 className="w-4 h-4" />
332
+ Sent!
333
+ </>
334
+ ) : (
335
+ <>
336
+ <Send className="w-4 h-4" />
337
+ Send Test
338
+ </>
339
+ )}
340
+ </button>
341
+ </div>
264
342
  </div>
265
343
  </div>
266
- </div>
344
+
345
+ {/* Domain Prompt Modal */}
346
+ <DomainPromptModal
347
+ isOpen={showDomainPrompt}
348
+ onClose={() => setShowDomainPrompt(false)}
349
+ onSave={handleSaveDomain}
350
+ language={language}
351
+ />
352
+ </>
267
353
  );
268
354
  }
@@ -1,53 +0,0 @@
1
- /**
2
- * Block Registry for Newsletter Plugin
3
- * Dynamic registry for all block types in system
4
- * Multi-Tenant Architecture: Blocks are provided by client applications
5
- *
6
- * The registry is a singleton that starts empty and is populated by client apps
7
- */
8
-
9
- import {
10
- BlockTypeDefinition,
11
- ClientBlockDefinition
12
- } from '../types/block';
13
-
14
- // Local interface to avoid import issues
15
- interface IBlockRegistry {
16
- register(definition: BlockTypeDefinition): void;
17
- get(type: string): BlockTypeDefinition | undefined;
18
- getAll(): BlockTypeDefinition[];
19
- has(type: string): boolean;
20
- clear(): void;
21
- }
22
-
23
- /**
24
- * Block Registry Implementation
25
- * Singleton that manages all block types in the system
26
- */
27
- class BlockRegistryImpl implements IBlockRegistry {
28
- private blocks: Map<string, BlockTypeDefinition> = new Map();
29
-
30
- register(definition: BlockTypeDefinition): void {
31
- this.blocks.set(definition.type, definition);
32
- }
33
-
34
- get(type: string): BlockTypeDefinition | undefined {
35
- return this.blocks.get(type);
36
- }
37
-
38
- getAll(): BlockTypeDefinition[] {
39
- return Array.from(this.blocks.values());
40
- }
41
-
42
- has(type: string): boolean {
43
- return this.blocks.has(type);
44
- }
45
-
46
- clear(): void {
47
- this.blocks.clear();
48
- }
49
- }
50
-
51
- // Export singleton instance
52
- export const BlockRegistry = new BlockRegistryImpl();
53
- export const blockRegistry = BlockRegistry;