@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
|
@@ -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
|
+
}
|