@jhits/plugin-newsletter 0.0.6 → 0.0.8
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 +8 -9
- package/src/api/handler.ts +0 -693
- package/src/api/router.ts +0 -111
- package/src/index.server.ts +0 -12
- package/src/index.tsx +0 -313
- package/src/index.tsx.patch +0 -98
- package/src/init.tsx +0 -72
- package/src/lib/blocks/BlockRenderer.tsx +0 -125
- package/src/lib/email/EmailRenderer.tsx +0 -425
- package/src/lib/email/index.ts +0 -6
- package/src/lib/mappers/apiMapper.ts +0 -57
- package/src/lib/utils/blockHelpers.ts +0 -71
- package/src/lib/utils/slugify.ts +0 -43
- package/src/registry/BlockRegistry.ts +0 -53
- package/src/registry/index.ts +0 -5
- package/src/state/EditorContext.tsx +0 -279
- package/src/state/index.ts +0 -10
- package/src/state/reducer.ts +0 -561
- package/src/state/types.ts +0 -154
- package/src/types/block.ts +0 -275
- package/src/types/newsletter.ts +0 -151
- package/src/types/registry.ts +0 -14
- package/src/views/CanvasEditor/BlockWrapper.tsx +0 -143
- package/src/views/CanvasEditor/CanvasEditorView.tsx +0 -249
- package/src/views/CanvasEditor/EditorBody.tsx +0 -95
- package/src/views/CanvasEditor/EditorHeader.tsx +0 -139
- package/src/views/CanvasEditor/components/CustomBlockItem.tsx +0 -83
- package/src/views/CanvasEditor/components/EditorCanvas.tsx +0 -674
- package/src/views/CanvasEditor/components/EditorLibrary.tsx +0 -120
- package/src/views/CanvasEditor/components/EditorSidebar.tsx +0 -156
- package/src/views/CanvasEditor/components/ErrorBanner.tsx +0 -31
- package/src/views/CanvasEditor/components/LibraryItem.tsx +0 -71
- package/src/views/CanvasEditor/components/SlashCommandDetector.tsx +0 -196
- package/src/views/CanvasEditor/components/SlashCommandMenu.tsx +0 -131
- package/src/views/CanvasEditor/components/index.ts +0 -16
- package/src/views/CanvasEditor/hooks/index.ts +0 -7
- package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.ts +0 -136
- package/src/views/CanvasEditor/hooks/useNewsletterLoader.ts +0 -34
- package/src/views/CanvasEditor/hooks/useRegisteredBlocks.ts +0 -54
- package/src/views/CanvasEditor/hooks/useSlashCommand.ts +0 -106
- package/src/views/CanvasEditor/index.ts +0 -12
- package/src/views/NewsletterEditor.tsx +0 -38
- package/src/views/NewsletterManager.tsx +0 -240
- package/src/views/SettingsView.tsx +0 -216
- package/src/views/SubscribersView.tsx +0 -269
package/src/api/router.ts
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Newsletter Plugin API Router
|
|
3
|
-
* Routes API requests to appropriate handlers
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
'use server';
|
|
7
|
-
|
|
8
|
-
import { NextRequest, NextResponse } from 'next/server';
|
|
9
|
-
import { NewsletterApiConfig } from '../types/newsletter';
|
|
10
|
-
import {
|
|
11
|
-
GET_SUBSCRIBERS,
|
|
12
|
-
POST_SUBSCRIBE,
|
|
13
|
-
GET_SUBSCRIBER,
|
|
14
|
-
DELETE_SUBSCRIBER,
|
|
15
|
-
GET_SETTINGS,
|
|
16
|
-
POST_SETTINGS,
|
|
17
|
-
GET_NEWSLETTERS,
|
|
18
|
-
GET_NEWSLETTER,
|
|
19
|
-
POST_NEWSLETTER,
|
|
20
|
-
PUT_NEWSLETTER,
|
|
21
|
-
DELETE_NEWSLETTER,
|
|
22
|
-
} from './handler';
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Handle newsletter API requests
|
|
26
|
-
*/
|
|
27
|
-
export async function handleNewsletterApi(
|
|
28
|
-
req: NextRequest,
|
|
29
|
-
path: string[],
|
|
30
|
-
config: NewsletterApiConfig
|
|
31
|
-
): Promise<NextResponse> {
|
|
32
|
-
const method = req.method;
|
|
33
|
-
const route = path[0] || '';
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
// Route: /api/plugin-newsletter/subscribers
|
|
37
|
-
if (route === 'subscribers') {
|
|
38
|
-
if (path[1]) {
|
|
39
|
-
// /api/plugin-newsletter/subscribers/[email]
|
|
40
|
-
const email = decodeURIComponent(path[1]).toLowerCase();
|
|
41
|
-
if (method === 'GET') {
|
|
42
|
-
return await GET_SUBSCRIBER(req, email, config);
|
|
43
|
-
}
|
|
44
|
-
if (method === 'DELETE') {
|
|
45
|
-
return await DELETE_SUBSCRIBER(req, email, config);
|
|
46
|
-
}
|
|
47
|
-
} else {
|
|
48
|
-
// /api/plugin-newsletter/subscribers
|
|
49
|
-
if (method === 'GET') {
|
|
50
|
-
return await GET_SUBSCRIBERS(req, config);
|
|
51
|
-
}
|
|
52
|
-
if (method === 'POST') {
|
|
53
|
-
return await POST_SUBSCRIBE(req, config);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Route: /api/plugin-newsletter/settings
|
|
59
|
-
if (route === 'settings') {
|
|
60
|
-
if (method === 'GET') {
|
|
61
|
-
return await GET_SETTINGS(req, config);
|
|
62
|
-
}
|
|
63
|
-
if (method === 'POST' || method === 'PUT') {
|
|
64
|
-
return await POST_SETTINGS(req, config);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Route: /api/plugin-newsletter/newsletters
|
|
69
|
-
if (route === 'newsletters') {
|
|
70
|
-
// Special route: /api/plugin-newsletter/newsletters/new
|
|
71
|
-
if (path[1] === 'new' && method === 'POST') {
|
|
72
|
-
return await POST_NEWSLETTER(req, config);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (path[1]) {
|
|
76
|
-
// /api/plugin-newsletter/newsletters/[slug]
|
|
77
|
-
const newsletterSlug = decodeURIComponent(path[1]);
|
|
78
|
-
if (method === 'GET') {
|
|
79
|
-
return await GET_NEWSLETTER(req, newsletterSlug, config);
|
|
80
|
-
}
|
|
81
|
-
if (method === 'PUT') {
|
|
82
|
-
return await PUT_NEWSLETTER(req, newsletterSlug, config);
|
|
83
|
-
}
|
|
84
|
-
if (method === 'DELETE') {
|
|
85
|
-
return await DELETE_NEWSLETTER(req, newsletterSlug, config);
|
|
86
|
-
}
|
|
87
|
-
} else {
|
|
88
|
-
// /api/plugin-newsletter/newsletters
|
|
89
|
-
if (method === 'GET') {
|
|
90
|
-
return await GET_NEWSLETTERS(req, config);
|
|
91
|
-
}
|
|
92
|
-
if (method === 'POST') {
|
|
93
|
-
return await POST_NEWSLETTER(req, config);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Method not allowed
|
|
99
|
-
return NextResponse.json(
|
|
100
|
-
{ error: `Method ${method} not allowed for route: ${route || '/'}` },
|
|
101
|
-
{ status: 405 }
|
|
102
|
-
);
|
|
103
|
-
} catch (error: any) {
|
|
104
|
-
console.error('[NewsletterApiRouter] Error:', error);
|
|
105
|
-
return NextResponse.json(
|
|
106
|
-
{ error: error.message || 'Internal server error' },
|
|
107
|
-
{ status: 500 }
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
package/src/index.server.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import 'server-only';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Plugin Newsletter - Server-Only Entry Point
|
|
5
|
-
* This file exports only server-side API handlers
|
|
6
|
-
* Used by the dynamic plugin router via @jhits/plugin-newsletter/server
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export { handleNewsletterApi as handleApi } from './api/router';
|
|
10
|
-
export { handleNewsletterApi } from './api/router'; // Keep original export for backward compatibility
|
|
11
|
-
export type { NewsletterApiConfig } from './types/newsletter';
|
|
12
|
-
|
package/src/index.tsx
DELETED
|
@@ -1,313 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Plugin Newsletter - Client Entry Point
|
|
3
|
-
* Main newsletter management interface for the dashboard
|
|
4
|
-
* Block-Based Newsletter Management System
|
|
5
|
-
* Multi-Tenant Architecture: Accepts custom blocks from client applications
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
'use client';
|
|
9
|
-
|
|
10
|
-
import React, { useMemo, useEffect } from 'react';
|
|
11
|
-
import { EditorProvider } from './state/EditorContext';
|
|
12
|
-
import { ClientBlockDefinition } from './types/block';
|
|
13
|
-
import { SubscribersView } from './views/SubscribersView';
|
|
14
|
-
import { SettingsView } from './views/SettingsView';
|
|
15
|
-
import { NewsletterManagerView } from './views/NewsletterManager';
|
|
16
|
-
import { NewsletterEditorView } from './views/NewsletterEditor';
|
|
17
|
-
import { editorStateToAPI } from './lib/mappers/apiMapper';
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Plugin Props Interface
|
|
21
|
-
* Matches the PluginProps from @jhits/jhits-dashboard
|
|
22
|
-
*/
|
|
23
|
-
export interface PluginProps {
|
|
24
|
-
subPath: string[];
|
|
25
|
-
siteId: string;
|
|
26
|
-
locale: string;
|
|
27
|
-
/** Custom blocks from client application (optional, can also come from window.__JHITS_PLUGIN_PROPS__) */
|
|
28
|
-
customBlocks?: ClientBlockDefinition[];
|
|
29
|
-
/** Enable dark mode for content area and wrappers (default: true) */
|
|
30
|
-
darkMode?: boolean;
|
|
31
|
-
/** Background colors for the editor */
|
|
32
|
-
backgroundColors?: {
|
|
33
|
-
/** Background color for light mode (REQUIRED) */
|
|
34
|
-
light: string;
|
|
35
|
-
/** Background color for dark mode (optional) */
|
|
36
|
-
dark?: string;
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Main Router Component
|
|
42
|
-
* Handles routing within the newsletter plugin
|
|
43
|
-
*
|
|
44
|
-
* Client Handshake:
|
|
45
|
-
* - Client apps can pass customBlocks via props
|
|
46
|
-
* - Or via window.__JHITS_PLUGIN_PROPS__['plugin-newsletter'].customBlocks
|
|
47
|
-
* - The EditorProvider will automatically register these blocks
|
|
48
|
-
*/
|
|
49
|
-
export default function NewsletterPlugin(props: PluginProps) {
|
|
50
|
-
const { subPath, siteId, locale, customBlocks: propsCustomBlocks, darkMode: propsDarkMode, backgroundColors: propsBackgroundColors } = props;
|
|
51
|
-
|
|
52
|
-
// Get custom blocks from props or window global (client app injection point)
|
|
53
|
-
const customBlocks = useMemo(() => {
|
|
54
|
-
// First, try props
|
|
55
|
-
if (propsCustomBlocks && propsCustomBlocks.length > 0) {
|
|
56
|
-
return propsCustomBlocks;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Fallback to window global (for client app injection)
|
|
60
|
-
if (typeof window !== 'undefined' && (window as any).__JHITS_PLUGIN_PROPS__) {
|
|
61
|
-
const pluginProps = (window as any).__JHITS_PLUGIN_PROPS__['plugin-newsletter'];
|
|
62
|
-
if (pluginProps?.customBlocks) {
|
|
63
|
-
return pluginProps.customBlocks as ClientBlockDefinition[];
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return [];
|
|
68
|
-
}, [propsCustomBlocks]);
|
|
69
|
-
|
|
70
|
-
// Get dark mode setting from props, localStorage (dev settings), or window global
|
|
71
|
-
// Priority: localStorage (dev) > props > window global > default
|
|
72
|
-
const darkMode = useMemo(() => {
|
|
73
|
-
// First, check localStorage for dev settings (highest priority for dev)
|
|
74
|
-
if (typeof window !== 'undefined') {
|
|
75
|
-
try {
|
|
76
|
-
const saved = localStorage.getItem('__JHITS_PLUGIN_NEWSLETTER_CONFIG__');
|
|
77
|
-
if (saved) {
|
|
78
|
-
const config = JSON.parse(saved);
|
|
79
|
-
if (config.darkMode !== undefined) {
|
|
80
|
-
return config.darkMode as boolean;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
} catch (e) {
|
|
84
|
-
// Ignore localStorage errors
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Then try props
|
|
89
|
-
if (propsDarkMode !== undefined) {
|
|
90
|
-
return propsDarkMode;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Fallback to window global if prop not provided
|
|
94
|
-
if (typeof window !== 'undefined' && (window as any).__JHITS_PLUGIN_PROPS__) {
|
|
95
|
-
const pluginProps = (window as any).__JHITS_PLUGIN_PROPS__['plugin-newsletter'];
|
|
96
|
-
if (pluginProps?.darkMode !== undefined) {
|
|
97
|
-
return pluginProps.darkMode as boolean;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return true; // Default to dark mode enabled
|
|
102
|
-
}, [propsDarkMode]);
|
|
103
|
-
|
|
104
|
-
// Get background colors from props, localStorage (dev settings), or window global
|
|
105
|
-
// Priority: localStorage (dev) > props > window global
|
|
106
|
-
const backgroundColors = useMemo(() => {
|
|
107
|
-
// First, check localStorage for dev settings (highest priority for dev)
|
|
108
|
-
if (typeof window !== 'undefined') {
|
|
109
|
-
try {
|
|
110
|
-
const saved = localStorage.getItem('__JHITS_PLUGIN_NEWSLETTER_CONFIG__');
|
|
111
|
-
if (saved) {
|
|
112
|
-
const config = JSON.parse(saved);
|
|
113
|
-
if (config.backgroundColors) {
|
|
114
|
-
return config.backgroundColors;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
} catch (e) {
|
|
118
|
-
// Ignore localStorage errors
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Then try props
|
|
123
|
-
if (propsBackgroundColors) {
|
|
124
|
-
return propsBackgroundColors;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Fallback to window global
|
|
128
|
-
if (typeof window !== 'undefined' && (window as any).__JHITS_PLUGIN_PROPS__) {
|
|
129
|
-
const pluginProps = (window as any).__JHITS_PLUGIN_PROPS__['plugin-newsletter'];
|
|
130
|
-
if (pluginProps?.backgroundColors) {
|
|
131
|
-
return pluginProps.backgroundColors as { light: string; dark?: string };
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return undefined;
|
|
136
|
-
}, [propsBackgroundColors]);
|
|
137
|
-
|
|
138
|
-
const route = subPath[0] || 'newsletters';
|
|
139
|
-
|
|
140
|
-
// Listen for config updates from settings screen
|
|
141
|
-
useEffect(() => {
|
|
142
|
-
if (typeof window === 'undefined') return;
|
|
143
|
-
|
|
144
|
-
const handleConfigUpdate = () => {
|
|
145
|
-
// Reload page to apply changes (simplest way to ensure all components pick up new values)
|
|
146
|
-
window.location.reload();
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
window.addEventListener('newsletter-plugin-config-updated', handleConfigUpdate as EventListener);
|
|
150
|
-
return () => {
|
|
151
|
-
window.removeEventListener('newsletter-plugin-config-updated', handleConfigUpdate as EventListener);
|
|
152
|
-
};
|
|
153
|
-
}, []);
|
|
154
|
-
|
|
155
|
-
// Route to appropriate view
|
|
156
|
-
switch (route) {
|
|
157
|
-
case 'newsletters':
|
|
158
|
-
return <NewsletterManagerView siteId={siteId} locale={locale} />;
|
|
159
|
-
|
|
160
|
-
case 'editor':
|
|
161
|
-
const newsletterSlug = subPath[1];
|
|
162
|
-
return (
|
|
163
|
-
<EditorProvider
|
|
164
|
-
customBlocks={customBlocks}
|
|
165
|
-
darkMode={darkMode}
|
|
166
|
-
backgroundColors={backgroundColors}
|
|
167
|
-
onSave={async (state) => {
|
|
168
|
-
// Save to API - create new or update existing newsletter
|
|
169
|
-
const originalSlug = newsletterSlug || state.slug;
|
|
170
|
-
const apiData = editorStateToAPI(state);
|
|
171
|
-
|
|
172
|
-
// If we have a slug, try to update first
|
|
173
|
-
if (originalSlug) {
|
|
174
|
-
console.log('[NewsletterPlugin] Attempting to update newsletter with slug:', originalSlug);
|
|
175
|
-
const updateResponse = await fetch(`/api/plugin-newsletter/newsletters/${originalSlug}`, {
|
|
176
|
-
method: 'PUT',
|
|
177
|
-
headers: { 'Content-Type': 'application/json' },
|
|
178
|
-
credentials: 'include',
|
|
179
|
-
body: JSON.stringify(apiData),
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
if (updateResponse.ok) {
|
|
183
|
-
const result = await updateResponse.json();
|
|
184
|
-
// If the slug changed, update the URL
|
|
185
|
-
if (result.slug && result.slug !== originalSlug) {
|
|
186
|
-
window.history.replaceState(null, '', `/dashboard/newsletter/editor/${result.slug}`);
|
|
187
|
-
}
|
|
188
|
-
return result;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// If 404, newsletter doesn't exist, create a new one
|
|
192
|
-
if (updateResponse.status === 404) {
|
|
193
|
-
console.log('[NewsletterPlugin] Newsletter not found, creating new newsletter');
|
|
194
|
-
} else {
|
|
195
|
-
// Other error, throw it
|
|
196
|
-
const error = await updateResponse.json();
|
|
197
|
-
console.error('[NewsletterPlugin] Save failed:', {
|
|
198
|
-
status: updateResponse.status,
|
|
199
|
-
statusText: updateResponse.statusText,
|
|
200
|
-
error,
|
|
201
|
-
});
|
|
202
|
-
const errorMessage = error.message || error.error || 'Failed to save newsletter';
|
|
203
|
-
throw new Error(errorMessage);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Create new newsletter (either no slug or update returned 404)
|
|
208
|
-
console.log('[NewsletterPlugin] Creating new newsletter');
|
|
209
|
-
const createResponse = await fetch('/api/plugin-newsletter/newsletters/new', {
|
|
210
|
-
method: 'POST',
|
|
211
|
-
headers: { 'Content-Type': 'application/json' },
|
|
212
|
-
credentials: 'include',
|
|
213
|
-
body: JSON.stringify(apiData),
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
if (!createResponse.ok) {
|
|
217
|
-
const error = await createResponse.json();
|
|
218
|
-
console.error('[NewsletterPlugin] Create failed:', {
|
|
219
|
-
status: createResponse.status,
|
|
220
|
-
statusText: createResponse.statusText,
|
|
221
|
-
error,
|
|
222
|
-
});
|
|
223
|
-
const errorMessage = error.message || error.error || 'Failed to create newsletter';
|
|
224
|
-
throw new Error(errorMessage);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const result = await createResponse.json();
|
|
228
|
-
// Update the URL to the new newsletter's slug
|
|
229
|
-
if (result.slug) {
|
|
230
|
-
window.history.replaceState(null, '', `/dashboard/newsletter/editor/${result.slug}`);
|
|
231
|
-
}
|
|
232
|
-
return result;
|
|
233
|
-
}}
|
|
234
|
-
>
|
|
235
|
-
<NewsletterEditorView newsletterSlug={newsletterSlug} siteId={siteId} locale={locale} darkMode={darkMode} backgroundColors={backgroundColors} />
|
|
236
|
-
</EditorProvider>
|
|
237
|
-
);
|
|
238
|
-
|
|
239
|
-
case 'new':
|
|
240
|
-
return (
|
|
241
|
-
<EditorProvider
|
|
242
|
-
customBlocks={customBlocks}
|
|
243
|
-
darkMode={darkMode}
|
|
244
|
-
backgroundColors={backgroundColors}
|
|
245
|
-
onSave={async (state) => {
|
|
246
|
-
// Save to API - create new newsletter
|
|
247
|
-
const apiData = editorStateToAPI(state);
|
|
248
|
-
const response = await fetch('/api/plugin-newsletter/newsletters/new', {
|
|
249
|
-
method: 'POST',
|
|
250
|
-
headers: { 'Content-Type': 'application/json' },
|
|
251
|
-
credentials: 'include',
|
|
252
|
-
body: JSON.stringify(apiData),
|
|
253
|
-
});
|
|
254
|
-
if (!response.ok) {
|
|
255
|
-
const error = await response.json();
|
|
256
|
-
throw new Error(error.message || 'Failed to create newsletter');
|
|
257
|
-
}
|
|
258
|
-
const result = await response.json();
|
|
259
|
-
// Update the URL to the new newsletter's slug
|
|
260
|
-
if (result.slug) {
|
|
261
|
-
window.history.replaceState(null, '', `/dashboard/newsletter/editor/${result.slug}`);
|
|
262
|
-
}
|
|
263
|
-
return result;
|
|
264
|
-
}}
|
|
265
|
-
>
|
|
266
|
-
<NewsletterEditorView siteId={siteId} locale={locale} darkMode={darkMode} backgroundColors={backgroundColors} />
|
|
267
|
-
</EditorProvider>
|
|
268
|
-
);
|
|
269
|
-
|
|
270
|
-
case 'subscribers':
|
|
271
|
-
return <SubscribersView siteId={siteId} locale={locale} />;
|
|
272
|
-
|
|
273
|
-
case 'settings':
|
|
274
|
-
return <SettingsView siteId={siteId} locale={locale} />;
|
|
275
|
-
|
|
276
|
-
default:
|
|
277
|
-
return <NewsletterManagerView siteId={siteId} locale={locale} />;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Export for use as default
|
|
282
|
-
export { NewsletterPlugin as Index };
|
|
283
|
-
|
|
284
|
-
// Export types for client applications
|
|
285
|
-
export type {
|
|
286
|
-
Block,
|
|
287
|
-
BlockTypeDefinition,
|
|
288
|
-
ClientBlockDefinition,
|
|
289
|
-
RichTextFormattingConfig,
|
|
290
|
-
BlockEditProps,
|
|
291
|
-
BlockPreviewProps,
|
|
292
|
-
IBlockComponent,
|
|
293
|
-
} from './types/block';
|
|
294
|
-
|
|
295
|
-
// Export newsletter types
|
|
296
|
-
export type {
|
|
297
|
-
Newsletter,
|
|
298
|
-
NewsletterStatus,
|
|
299
|
-
NewsletterMetadata,
|
|
300
|
-
NewsletterListItem,
|
|
301
|
-
NewsletterFilterOptions,
|
|
302
|
-
} from './types/newsletter';
|
|
303
|
-
|
|
304
|
-
// Export initialization utility for easy setup
|
|
305
|
-
export { initNewsletterPlugin } from './init';
|
|
306
|
-
export type { NewsletterPluginConfig } from './init';
|
|
307
|
-
|
|
308
|
-
// Export editor state management
|
|
309
|
-
export { EditorProvider, useEditor } from './state/EditorContext';
|
|
310
|
-
export type { EditorProviderProps, EditorState, EditorContextValue } from './state';
|
|
311
|
-
|
|
312
|
-
// Export block registry
|
|
313
|
-
export { blockRegistry } from './registry';
|
package/src/index.tsx.patch
DELETED
|
@@ -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
|
-
>
|
package/src/init.tsx
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Newsletter Plugin Initialization Utility
|
|
3
|
-
*
|
|
4
|
-
* Simple function to initialize the newsletter plugin with client configuration.
|
|
5
|
-
* Call this once in your app (e.g., in a script tag or root layout) to configure
|
|
6
|
-
* the newsletter plugin without needing a React component.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```typescript
|
|
10
|
-
* import { initNewsletterPlugin } from '@jhits/plugin-newsletter/init';
|
|
11
|
-
* import { newsletterConfig } from '@/plugins/newsletter-config';
|
|
12
|
-
*
|
|
13
|
-
* // Call once when your app loads
|
|
14
|
-
* initNewsletterPlugin(newsletterConfig);
|
|
15
|
-
* ```
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
'use client';
|
|
19
|
-
|
|
20
|
-
import React from 'react';
|
|
21
|
-
import type { ClientBlockDefinition } from './types/block';
|
|
22
|
-
|
|
23
|
-
export interface NewsletterPluginConfig {
|
|
24
|
-
/** Custom blocks available in the editor */
|
|
25
|
-
customBlocks?: ClientBlockDefinition[];
|
|
26
|
-
/** Dark mode setting for the editor content area and wrappers (default: true) */
|
|
27
|
-
darkMode?: boolean;
|
|
28
|
-
/** Background colors for the editor */
|
|
29
|
-
backgroundColors?: {
|
|
30
|
-
/** Background color for light mode (REQUIRED) - CSS color value (hex, rgb, or named color) */
|
|
31
|
-
light: string;
|
|
32
|
-
/** Background color for dark mode (optional) - CSS color value (hex, rgb, or named color) */
|
|
33
|
-
dark?: string;
|
|
34
|
-
};
|
|
35
|
-
/** Email configuration */
|
|
36
|
-
emailConfig?: {
|
|
37
|
-
/** Logo URL to display in email header (absolute URL) */
|
|
38
|
-
logoUrl?: string;
|
|
39
|
-
/** Logo alt text */
|
|
40
|
-
logoAlt?: string;
|
|
41
|
-
/** Footer text for emails */
|
|
42
|
-
footerText?: string;
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Initialize the newsletter plugin with client configuration
|
|
48
|
-
*
|
|
49
|
-
* This function sets up the window global that the plugin reads from automatically.
|
|
50
|
-
* Call this once when your app loads, before the plugin is rendered.
|
|
51
|
-
*
|
|
52
|
-
* @param config - Newsletter plugin configuration (customBlocks, darkMode, etc.)
|
|
53
|
-
*/
|
|
54
|
-
export function initNewsletterPlugin(config: NewsletterPluginConfig): void {
|
|
55
|
-
if (typeof window === 'undefined') {
|
|
56
|
-
// Server-side: no-op
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Initialize the global plugin props object if it doesn't exist
|
|
61
|
-
if (!(window as any).__JHITS_PLUGIN_PROPS__) {
|
|
62
|
-
(window as any).__JHITS_PLUGIN_PROPS__ = {};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Set newsletter plugin configuration
|
|
66
|
-
(window as any).__JHITS_PLUGIN_PROPS__['plugin-newsletter'] = {
|
|
67
|
-
customBlocks: config.customBlocks || [],
|
|
68
|
-
darkMode: config.darkMode !== undefined ? config.darkMode : true, // Default to true
|
|
69
|
-
backgroundColors: config.backgroundColors || undefined,
|
|
70
|
-
emailConfig: config.emailConfig || undefined,
|
|
71
|
-
};
|
|
72
|
-
}
|