@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.
Files changed (76) 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/send-newsletter.d.ts.map +1 -1
  4. package/dist/api/handlers/send-newsletter.js +54 -6
  5. package/dist/api/handlers/settings.d.ts.map +1 -1
  6. package/dist/api/handlers/settings.js +51 -1
  7. package/dist/index.d.ts +27 -10
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +15 -122
  10. package/dist/lib/blocks/BlockRenderer.d.ts.map +1 -1
  11. package/dist/lib/blocks/BlockRenderer.js +14 -2
  12. package/dist/lib/email/EmailRenderer.d.ts +1 -0
  13. package/dist/lib/email/EmailRenderer.d.ts.map +1 -1
  14. package/dist/lib/email/EmailRenderer.js +31 -19
  15. package/dist/lib/utils/config-resolver.d.ts +33 -0
  16. package/dist/lib/utils/config-resolver.d.ts.map +1 -0
  17. package/dist/lib/utils/config-resolver.js +47 -0
  18. package/dist/registry/BlockRegistry.d.ts +9 -1
  19. package/dist/registry/BlockRegistry.d.ts.map +1 -1
  20. package/dist/registry/BlockRegistry.js +126 -8
  21. package/dist/state/EditorContext.d.ts +11 -1
  22. package/dist/state/EditorContext.d.ts.map +1 -1
  23. package/dist/state/EditorContext.js +23 -5
  24. package/dist/state/types.d.ts +12 -0
  25. package/dist/state/types.d.ts.map +1 -1
  26. package/dist/types/block.d.ts +9 -0
  27. package/dist/types/block.d.ts.map +1 -1
  28. package/dist/types/newsletter.d.ts +2 -0
  29. package/dist/types/newsletter.d.ts.map +1 -1
  30. package/dist/views/CanvasEditor/BlockWrapper.d.ts.map +1 -1
  31. package/dist/views/CanvasEditor/BlockWrapper.js +24 -3
  32. package/dist/views/CanvasEditor/CanvasEditorView.d.ts.map +1 -1
  33. package/dist/views/CanvasEditor/CanvasEditorView.js +77 -17
  34. package/dist/views/CanvasEditor/EditorBody.d.ts.map +1 -1
  35. package/dist/views/CanvasEditor/EditorBody.js +1 -1
  36. package/dist/views/CanvasEditor/components/EditorCanvas.d.ts.map +1 -1
  37. package/dist/views/CanvasEditor/components/EditorCanvas.js +158 -100
  38. package/dist/views/CanvasEditor/components/EditorSidebar.d.ts +3 -1
  39. package/dist/views/CanvasEditor/components/EditorSidebar.d.ts.map +1 -1
  40. package/dist/views/CanvasEditor/components/EditorSidebar.js +3 -3
  41. package/dist/views/CanvasEditor/hooks/useRegisteredBlocks.d.ts +1 -1
  42. package/dist/views/CanvasEditor/hooks/useRegisteredBlocks.d.ts.map +1 -1
  43. package/dist/views/CanvasEditor/hooks/useRegisteredBlocks.js +6 -40
  44. package/dist/views/components/DomainPromptModal.d.ts +13 -0
  45. package/dist/views/components/DomainPromptModal.d.ts.map +1 -0
  46. package/dist/views/components/DomainPromptModal.js +58 -0
  47. package/dist/views/components/SendNewsletterModal.d.ts.map +1 -1
  48. package/dist/views/components/SendNewsletterModal.js +91 -22
  49. package/dist/views/components/SmtpSettingsModal.d.ts.map +1 -1
  50. package/dist/views/components/SmtpSettingsModal.js +10 -0
  51. package/dist/views/components/TestEmailModal.d.ts.map +1 -1
  52. package/dist/views/components/TestEmailModal.js +86 -17
  53. package/package.json +53 -9
  54. package/src/api/email-utils.ts +53 -4
  55. package/src/api/handlers/send-newsletter.ts +65 -6
  56. package/src/api/handlers/settings.ts +60 -2
  57. package/src/index.tsx +49 -155
  58. package/src/lib/blocks/BlockRenderer.tsx +16 -2
  59. package/src/lib/email/EmailRenderer.tsx +31 -20
  60. package/src/lib/utils/config-resolver.ts +71 -0
  61. package/src/registry/BlockRegistry.tsx +255 -0
  62. package/src/state/EditorContext.tsx +43 -8
  63. package/src/state/types.ts +16 -0
  64. package/src/types/block.ts +10 -0
  65. package/src/types/newsletter.ts +3 -0
  66. package/src/views/CanvasEditor/BlockWrapper.tsx +27 -2
  67. package/src/views/CanvasEditor/CanvasEditorView.tsx +142 -61
  68. package/src/views/CanvasEditor/EditorBody.tsx +17 -13
  69. package/src/views/CanvasEditor/components/EditorCanvas.tsx +178 -115
  70. package/src/views/CanvasEditor/components/EditorSidebar.tsx +57 -2
  71. package/src/views/CanvasEditor/hooks/useRegisteredBlocks.ts +6 -45
  72. package/src/views/components/DomainPromptModal.tsx +160 -0
  73. package/src/views/components/SendNewsletterModal.tsx +270 -184
  74. package/src/views/components/SmtpSettingsModal.tsx +11 -0
  75. package/src/views/components/TestEmailModal.tsx +235 -149
  76. package/src/registry/BlockRegistry.ts +0 -53
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Domain Prompt Modal Component
3
+ * Non-technical modal to ask for the website domain when it's missing in dev mode.
4
+ */
5
+
6
+ 'use client';
7
+
8
+ import React, { useState } from 'react';
9
+ import { X, Globe, Save, RefreshCw, CheckCircle2, Info } from 'lucide-react';
10
+
11
+ interface DomainPromptModalProps {
12
+ isOpen: boolean;
13
+ onClose: () => void;
14
+ onSave: (domain: string) => Promise<void>;
15
+ language: string;
16
+ }
17
+
18
+ export function DomainPromptModal({ isOpen, onClose, onSave, language }: DomainPromptModalProps) {
19
+ const [domain, setDomain] = useState('');
20
+ const [isSaving, setIsSaving] = useState(false);
21
+ const [isSuccess, setIsSuccess] = useState(false);
22
+ const [error, setError] = useState<string | null>(null);
23
+
24
+ const languageNames: Record<string, string> = {
25
+ en: 'English',
26
+ nl: 'Dutch',
27
+ sv: 'Swedish',
28
+ };
29
+
30
+ const handleSave = async () => {
31
+ if (!domain.trim()) {
32
+ setError('Please enter a website address');
33
+ return;
34
+ }
35
+
36
+ // Basic URL validation/cleanup
37
+ let cleanDomain = domain.trim().toLowerCase();
38
+ if (cleanDomain.startsWith('http://')) cleanDomain = cleanDomain.replace('http://', '');
39
+ if (cleanDomain.startsWith('https://')) cleanDomain = cleanDomain.replace('https://', '');
40
+ if (cleanDomain.endsWith('/')) cleanDomain = cleanDomain.slice(0, -1);
41
+
42
+ try {
43
+ setIsSaving(true);
44
+ setError(null);
45
+ await onSave(cleanDomain);
46
+ setIsSuccess(true);
47
+ setTimeout(() => {
48
+ onClose();
49
+ }, 1500);
50
+ } catch (err: any) {
51
+ setError(err.message || 'Failed to save domain configuration');
52
+ } finally {
53
+ setIsSaving(false);
54
+ }
55
+ };
56
+
57
+ if (!isOpen) return null;
58
+
59
+ return (
60
+ <div className="fixed inset-0 z-[60] flex items-center justify-center p-4">
61
+ {/* Backdrop */}
62
+ <div
63
+ className="absolute inset-0 bg-black/60 backdrop-blur-md"
64
+ onClick={() => !isSaving && onClose()}
65
+ />
66
+
67
+ {/* Modal */}
68
+ <div className="relative w-full max-w-lg bg-white dark:bg-neutral-900 rounded-[2.5rem] border border-dashboard-border shadow-2xl overflow-hidden animate-in fade-in zoom-in duration-300">
69
+ {/* Header */}
70
+ <div className="px-10 pt-10 pb-6 text-center">
71
+ <div className="w-20 h-20 bg-primary/10 rounded-[2rem] flex items-center justify-center mx-auto mb-6">
72
+ <Globe size={40} className="text-primary" />
73
+ </div>
74
+ <h2 className="text-3xl font-black text-dashboard-text uppercase tracking-tighter leading-none mb-2">
75
+ Website Connection<span className="text-primary">.</span>
76
+ </h2>
77
+ <p className="text-sm text-neutral-500 dark:text-neutral-400 font-medium">
78
+ We need to know where your <span className="font-bold text-dashboard-text">{languageNames[language] || language}</span> website is hosted to ensure links work correctly.
79
+ </p>
80
+ </div>
81
+
82
+ {/* Content */}
83
+ <div className="px-10 pb-10 space-y-6">
84
+ <div className="bg-blue-50/50 dark:bg-blue-900/10 border border-blue-100 dark:border-blue-900/20 rounded-2xl p-4 flex gap-3">
85
+ <Info size={18} className="text-blue-500 flex-shrink-0 mt-0.5" />
86
+ <p className="text-xs text-blue-700 dark:text-blue-300 leading-relaxed">
87
+ Since you are in development mode, we couldn't automatically find the domain for this language. Please provide it below.
88
+ </p>
89
+ </div>
90
+
91
+ <div>
92
+ <label className="text-[10px] font-black text-dashboard-text-secondary uppercase tracking-[0.2em] block mb-3 ml-1">
93
+ Your {languageNames[language] || language} Website URL
94
+ </label>
95
+ <div className="relative group">
96
+ <div className="absolute left-4 top-1/2 -translate-y-1/2 text-neutral-400 group-focus-within:text-primary transition-colors font-mono text-sm">
97
+ https://
98
+ </div>
99
+ <input
100
+ type="text"
101
+ value={domain}
102
+ onChange={(e) => {
103
+ setDomain(e.target.value);
104
+ setError(null);
105
+ }}
106
+ onKeyDown={(e) => e.key === 'Enter' && handleSave()}
107
+ placeholder="www.yourwebsite.com"
108
+ className="w-full pl-20 pr-6 py-4 bg-dashboard-bg border border-dashboard-border rounded-2xl outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all text-dashboard-text font-medium"
109
+ autoFocus
110
+ />
111
+ </div>
112
+ {error && (
113
+ <p className="text-[10px] text-red-500 font-bold uppercase tracking-widest mt-3 ml-1">
114
+ {error}
115
+ </p>
116
+ )}
117
+ </div>
118
+
119
+ <div className="flex gap-3 pt-2">
120
+ <button
121
+ onClick={onClose}
122
+ disabled={isSaving}
123
+ className="flex-1 px-6 py-4 rounded-full text-[10px] font-black uppercase tracking-widest transition-all bg-dashboard-bg border border-dashboard-border text-dashboard-text hover:bg-dashboard-border active:scale-95"
124
+ >
125
+ Skip for now
126
+ </button>
127
+ <button
128
+ onClick={handleSave}
129
+ disabled={isSaving || isSuccess}
130
+ className={`flex-[2] inline-flex items-center justify-center gap-2 px-6 py-4 rounded-full text-[10px] font-black uppercase tracking-widest transition-all shadow-lg active:scale-95 ${
131
+ isSaving
132
+ ? 'bg-neutral-400 text-white cursor-not-allowed'
133
+ : isSuccess
134
+ ? 'bg-green-600 text-white'
135
+ : 'bg-primary text-white hover:shadow-primary/20'
136
+ }`}
137
+ >
138
+ {isSaving ? (
139
+ <>
140
+ <RefreshCw className="w-4 h-4 animate-spin" />
141
+ Connecting...
142
+ </>
143
+ ) : isSuccess ? (
144
+ <>
145
+ <CheckCircle2 className="w-4 h-4" />
146
+ Saved!
147
+ </>
148
+ ) : (
149
+ <>
150
+ <Save className="w-4 h-4" />
151
+ Link Website
152
+ </>
153
+ )}
154
+ </button>
155
+ </div>
156
+ </div>
157
+ </div>
158
+ </div>
159
+ );
160
+ }