payaza-storefront-layouts 1.0.38 → 1.0.40
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/editor/payaza-form/PayazaFormEditor.d.ts.map +1 -1
- package/dist/editor/payaza-form/PayazaFormEditor.js +243 -63
- package/dist/editor/payaza-form/PayazaFormEditorFullPage.d.ts +3 -0
- package/dist/editor/payaza-form/PayazaFormEditorFullPage.d.ts.map +1 -1
- package/dist/editor/payaza-form/PayazaFormEditorFullPage.js +2 -1
- package/dist/editor/payaza-form/components/SectionField.js +1 -1
- package/dist/editor/payaza-form/data-merger.d.ts +6 -2
- package/dist/editor/payaza-form/data-merger.d.ts.map +1 -1
- package/dist/editor/payaza-form/data-merger.js +64 -51
- package/dist/editor/payaza-form/schema-bridge.d.ts.map +1 -1
- package/dist/editor/payaza-form/schema-bridge.js +17 -4
- package/dist/editor/shared/AIGenerateModal.js +1 -1
- package/dist/editor/shared/EditorHeader.d.ts +7 -0
- package/dist/editor/shared/EditorHeader.d.ts.map +1 -1
- package/dist/editor/shared/EditorHeader.js +45 -9
- package/dist/editor/shared/IframePreview.d.ts +1 -0
- package/dist/editor/shared/IframePreview.d.ts.map +1 -1
- package/dist/editor/shared/IframePreview.js +12 -2
- package/dist/editor/types.d.ts +12 -0
- package/dist/editor/types.d.ts.map +1 -1
- package/dist/layouts/booking/components/BookingHomePage.d.ts.map +1 -1
- package/dist/layouts/booking/components/BookingHomePage.js +44 -3
- package/dist/layouts/booking-agenda/components/BookingHomePageAgenda.d.ts.map +1 -1
- package/dist/layouts/booking-agenda/components/BookingHomePageAgenda.js +33 -3
- package/dist/layouts/clothing/components/ClothingHomePage.d.ts.map +1 -1
- package/dist/layouts/clothing/components/ClothingHomePage.js +26 -23
- package/dist/layouts/clothing-minimal/components/ClothingHomePageMinimal.d.ts.map +1 -1
- package/dist/layouts/clothing-minimal/components/ClothingHomePageMinimal.js +23 -2
- package/dist/layouts/electronics/components/ElectronicsHomePage.d.ts.map +1 -1
- package/dist/layouts/electronics/components/ElectronicsHomePage.js +24 -4
- package/dist/layouts/electronics-grid/components/ElectronicsHomePageGrid.d.ts.map +1 -1
- package/dist/layouts/electronics-grid/components/ElectronicsHomePageGrid.js +24 -2
- package/dist/layouts/food/components/FoodHomePage.d.ts.map +1 -1
- package/dist/layouts/food/components/FoodHomePage.js +38 -2
- package/dist/layouts/food-modern/components/FoodHomePageModern.d.ts.map +1 -1
- package/dist/layouts/food-modern/components/FoodHomePageModern.js +31 -2
- package/dist/layouts/motivational-speaker/components/MotivationalHomePage.d.ts.map +1 -1
- package/dist/layouts/motivational-speaker/components/MotivationalHomePage.js +15 -1
- package/dist/layouts/shared/components/StoreHeader.d.ts.map +1 -1
- package/dist/layouts/shared/components/StoreHeader.js +24 -7
- package/dist/lib/preview-data.d.ts.map +1 -1
- package/dist/lib/preview-data.js +11 -1
- package/dist/lib/store-types.d.ts +1 -0
- package/dist/lib/store-types.d.ts.map +1 -1
- package/dist/styles/index.css +53 -29
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PayazaFormEditor.d.ts","sourceRoot":"","sources":["../../../src/editor/payaza-form/PayazaFormEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA+C,MAAM,OAAO,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAU/C,OAAO,EAA4B,eAAe,EAAgB,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"PayazaFormEditor.d.ts","sourceRoot":"","sources":["../../../src/editor/payaza-form/PayazaFormEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA+C,MAAM,OAAO,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAU/C,OAAO,EAA4B,eAAe,EAAgB,MAAM,mBAAmB,CAAC;AAO5F,MAAM,WAAW,qBAAsB,SAAQ,mBAAmB;IAChE;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;CACjD;AAED,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CA8xB5D,CAAC"}
|
|
@@ -5,7 +5,7 @@ import { IframePreview } from '../shared/IframePreview';
|
|
|
5
5
|
import { ResizableSidebar } from '../shared/ResizableSidebar';
|
|
6
6
|
import { getPageSchema } from './engine';
|
|
7
7
|
import { FieldRenderer } from './FieldRenderer';
|
|
8
|
-
import { loadLayoutJSON
|
|
8
|
+
import { loadLayoutJSON } from './layout-loader';
|
|
9
9
|
import { transformToStoreConfig, updateOverlayData, addArrayItem, removeArrayItem, updateArrayItem } from './data-transformer';
|
|
10
10
|
import { generateArrayItem } from './engine';
|
|
11
11
|
import { initializeWidgetRegistry, clearWidgets } from './widget-registry';
|
|
@@ -13,10 +13,18 @@ import { mergeRequirementWithActualData, mergeAllPagesWithActualData } from './d
|
|
|
13
13
|
import { Info, Search, X } from 'lucide-react';
|
|
14
14
|
import { useDebounce } from '../../hooks/use-debounce';
|
|
15
15
|
import { AIGenerateModal } from '../shared/AIGenerateModal';
|
|
16
|
-
|
|
16
|
+
import { useAlertModal } from '../../components/ui/alert-modal';
|
|
17
|
+
export const PayazaFormEditor = ({ layoutId, initialData, inventory, onChange, onSave, onPublish, onBack, onBackToSettings, onOpenAssets, onBackToTheme, title, className = '', customWidgets, storeName, storeDescription, assets, }) => {
|
|
17
18
|
const [activePageId, setActivePageId] = useState('home');
|
|
18
19
|
const [data, setData] = useState(initialData);
|
|
19
20
|
const dataRef = useRef(initialData);
|
|
21
|
+
// Undo/Redo State
|
|
22
|
+
const [history, setHistory] = useState([initialData]);
|
|
23
|
+
const [historyIndex, setHistoryIndex] = useState(0);
|
|
24
|
+
const isInternalChange = useRef(false);
|
|
25
|
+
const MAX_HISTORY = 50;
|
|
26
|
+
// Track last published data to disable publish button if no changes
|
|
27
|
+
const [lastPublishedData, setLastPublishedData] = useState(initialData);
|
|
20
28
|
// Debounce the data for preview to prevent excessive iframe updates and RAM spikes
|
|
21
29
|
const debouncedData = useDebounce(data, 400);
|
|
22
30
|
const [viewMode, setViewMode] = useState('desktop');
|
|
@@ -28,10 +36,79 @@ export const PayazaFormEditor = ({ layoutId, initialData, inventory, onChange, o
|
|
|
28
36
|
const [baseRequirement, setBaseRequirement] = useState(null);
|
|
29
37
|
const [loading, setLoading] = useState(true);
|
|
30
38
|
const [searchQuery, setSearchQuery] = useState('');
|
|
39
|
+
const [selectedSection, setSelectedSection] = useState(null);
|
|
40
|
+
const { showAlert } = useAlertModal();
|
|
31
41
|
// Keep dataRef in sync with data state
|
|
32
42
|
useEffect(() => {
|
|
33
43
|
dataRef.current = data;
|
|
44
|
+
// Add to history if not an internal change (undo/redo)
|
|
45
|
+
if (!isInternalChange.current) {
|
|
46
|
+
setHistory(prev => {
|
|
47
|
+
const newHistory = prev.slice(0, historyIndex + 1);
|
|
48
|
+
// Only add if data is different from the last entry
|
|
49
|
+
if (JSON.stringify(newHistory[newHistory.length - 1]) !== JSON.stringify(data)) {
|
|
50
|
+
const updatedHistory = [...newHistory, data];
|
|
51
|
+
if (updatedHistory.length > MAX_HISTORY) {
|
|
52
|
+
return updatedHistory.slice(1);
|
|
53
|
+
}
|
|
54
|
+
return updatedHistory;
|
|
55
|
+
}
|
|
56
|
+
return prev;
|
|
57
|
+
});
|
|
58
|
+
setHistoryIndex(prev => {
|
|
59
|
+
const newIndex = Math.min(historyIndex + 1, MAX_HISTORY - 1);
|
|
60
|
+
// We need to re-calculate based on what setHistory actually does
|
|
61
|
+
return historyIndex + 1;
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
isInternalChange.current = false;
|
|
34
65
|
}, [data]);
|
|
66
|
+
// Fix the history index after history update
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
if (history.length > 0 && historyIndex >= history.length) {
|
|
69
|
+
setHistoryIndex(history.length - 1);
|
|
70
|
+
}
|
|
71
|
+
}, [history]);
|
|
72
|
+
const handleUndo = () => {
|
|
73
|
+
if (historyIndex > 0) {
|
|
74
|
+
isInternalChange.current = true;
|
|
75
|
+
const prevData = history[historyIndex - 1];
|
|
76
|
+
setHistoryIndex(historyIndex - 1);
|
|
77
|
+
setData(prevData);
|
|
78
|
+
onChange?.(prevData);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const handleRedo = () => {
|
|
82
|
+
if (historyIndex < history.length - 1) {
|
|
83
|
+
isInternalChange.current = true;
|
|
84
|
+
const nextData = history[historyIndex + 1];
|
|
85
|
+
setHistoryIndex(historyIndex + 1);
|
|
86
|
+
setData(nextData);
|
|
87
|
+
onChange?.(nextData);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
// Keyboard Shortcuts
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
const handleKeyDown = (e) => {
|
|
93
|
+
// Undo: Ctrl+Z or Cmd+Z
|
|
94
|
+
if ((e.ctrlKey || e.metaKey) && e.key === 'z' && !e.shiftKey) {
|
|
95
|
+
e.preventDefault();
|
|
96
|
+
handleUndo();
|
|
97
|
+
}
|
|
98
|
+
// Redo: Ctrl+Y or Cmd+Shift+Z
|
|
99
|
+
if (((e.ctrlKey || e.metaKey) && e.key === 'y') || ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'z')) {
|
|
100
|
+
e.preventDefault();
|
|
101
|
+
handleRedo();
|
|
102
|
+
}
|
|
103
|
+
// Save: Ctrl+S or Cmd+S
|
|
104
|
+
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
|
|
105
|
+
e.preventDefault();
|
|
106
|
+
handleSave();
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
window.addEventListener('keydown', handleKeyDown);
|
|
110
|
+
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
111
|
+
}, [historyIndex, history, data]); // Dependencies ensure latest state is used
|
|
35
112
|
// Initialize custom widgets if provided
|
|
36
113
|
useEffect(() => {
|
|
37
114
|
if (customWidgets) {
|
|
@@ -60,41 +137,39 @@ export const PayazaFormEditor = ({ layoutId, initialData, inventory, onChange, o
|
|
|
60
137
|
const loadLayout = async () => {
|
|
61
138
|
setLoading(true);
|
|
62
139
|
try {
|
|
63
|
-
//
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
140
|
+
// Normalization mapping
|
|
141
|
+
const layoutIdMap = {
|
|
142
|
+
'fashion-hub': 'clothing',
|
|
143
|
+
'savory-bites': 'food',
|
|
144
|
+
'modern-eats': 'food-modern',
|
|
145
|
+
'minimal-style': 'clothing-minimal',
|
|
146
|
+
'fashion-minimal': 'clothing-minimal',
|
|
147
|
+
'urban-retreat': 'booking',
|
|
148
|
+
'agenda-book': 'booking-agenda',
|
|
149
|
+
'grid-tech': 'electronics-grid',
|
|
150
|
+
'mindset-mastery': 'motivational-speaker'
|
|
151
|
+
};
|
|
152
|
+
const rawId = String(layoutId || '').trim().toLowerCase();
|
|
153
|
+
const normalizedId = layoutIdMap[rawId] || rawId;
|
|
154
|
+
const layoutRequirement = await loadLayoutJSON(normalizedId, inventory);
|
|
155
|
+
if (!isMounted)
|
|
156
|
+
return;
|
|
157
|
+
if (layoutRequirement) {
|
|
158
|
+
setBaseRequirement(layoutRequirement);
|
|
159
|
+
// Initial merge for the active page
|
|
160
|
+
const mergedLayout = mergeRequirementWithActualData(layoutRequirement, initialData, activePageId);
|
|
161
|
+
setLayoutJson(mergedLayout);
|
|
162
|
+
// Merge with existing data to preserve all StoreConfig properties
|
|
163
|
+
const storeConfig = transformToStoreConfig(mergedLayout, activePageId, initialData);
|
|
164
|
+
setData(storeConfig);
|
|
165
|
+
// Don't call onChange here to avoid loops on initial load
|
|
68
166
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
setBaseRequirement(requirementLayout);
|
|
72
|
-
if (initialData) {
|
|
73
|
-
// CRITICAL: Merge ALL pages with actual data to ensure StoreConfig is fully populated
|
|
74
|
-
// with defaults for all pages, not just the active one.
|
|
75
|
-
// This ensures onSave/onPublish always receive a complete set of data.
|
|
76
|
-
const fullyMergedStoreConfig = mergeAllPagesWithActualData(requirementLayout, initialData);
|
|
77
|
-
// Also get the layout for the active page specifically
|
|
78
|
-
const mergedLayout = mergeRequirementWithActualData(requirementLayout, fullyMergedStoreConfig, activePageId);
|
|
79
|
-
setLayoutJson(mergedLayout);
|
|
80
|
-
if (process.env.NODE_ENV === 'development') {
|
|
81
|
-
console.log('[PayazaFormEditor] Setting fully merged StoreConfig for preview:', {
|
|
82
|
-
pageId: activePageId,
|
|
83
|
-
hasLayoutConfig: !!fullyMergedStoreConfig.layoutConfig,
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
setData(fullyMergedStoreConfig);
|
|
87
|
-
}
|
|
88
|
-
else {
|
|
89
|
-
// No actual data, use requirement as-is
|
|
90
|
-
setLayoutJson(requirementLayout);
|
|
91
|
-
}
|
|
167
|
+
else {
|
|
168
|
+
console.error(`[PayazaFormEditor] Failed to load layout requirement for: ${layoutId} (normalized: ${normalizedId})`);
|
|
92
169
|
}
|
|
93
170
|
}
|
|
94
171
|
catch (error) {
|
|
95
|
-
|
|
96
|
-
console.error('Error loading layout:', error);
|
|
97
|
-
}
|
|
172
|
+
console.error('[PayazaFormEditor] Error loading layout:', error);
|
|
98
173
|
}
|
|
99
174
|
finally {
|
|
100
175
|
if (isMounted) {
|
|
@@ -103,8 +178,23 @@ export const PayazaFormEditor = ({ layoutId, initialData, inventory, onChange, o
|
|
|
103
178
|
}
|
|
104
179
|
};
|
|
105
180
|
loadLayout();
|
|
181
|
+
const handleMessage = (event) => {
|
|
182
|
+
if (event.data?.type === 'SECTION_SELECTED') {
|
|
183
|
+
const sectionKey = event.data.section || event.data.sectionKey;
|
|
184
|
+
if (sectionKey) {
|
|
185
|
+
setSelectedSection(sectionKey);
|
|
186
|
+
// Find and scroll to the section in the sidebar
|
|
187
|
+
const element = document.querySelector(`[data-section-key="${sectionKey}"]`);
|
|
188
|
+
if (element) {
|
|
189
|
+
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
window.addEventListener('message', handleMessage);
|
|
106
195
|
return () => {
|
|
107
196
|
isMounted = false;
|
|
197
|
+
window.removeEventListener('message', handleMessage);
|
|
108
198
|
};
|
|
109
199
|
}, [layoutId, inventory, initialData]);
|
|
110
200
|
// Re-merge data when page changes
|
|
@@ -219,11 +309,15 @@ export const PayazaFormEditor = ({ layoutId, initialData, inventory, onChange, o
|
|
|
219
309
|
setIsPublishing(true);
|
|
220
310
|
try {
|
|
221
311
|
await onPublish?.(data);
|
|
312
|
+
setLastPublishedData(data);
|
|
222
313
|
}
|
|
223
314
|
finally {
|
|
224
315
|
setIsPublishing(false);
|
|
225
316
|
}
|
|
226
317
|
};
|
|
318
|
+
const hasChangesSincePublish = useMemo(() => {
|
|
319
|
+
return JSON.stringify(data) !== JSON.stringify(lastPublishedData);
|
|
320
|
+
}, [data, lastPublishedData]);
|
|
227
321
|
const handleAIGenerate = async (context) => {
|
|
228
322
|
if (!layoutJson)
|
|
229
323
|
return;
|
|
@@ -231,20 +325,41 @@ export const PayazaFormEditor = ({ layoutId, initialData, inventory, onChange, o
|
|
|
231
325
|
// Use a local variable to track if this specific request should still update state
|
|
232
326
|
let shouldUpdate = true;
|
|
233
327
|
try {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
328
|
+
let response;
|
|
329
|
+
try {
|
|
330
|
+
response = await fetch('/api/ai/generate', {
|
|
331
|
+
method: 'POST',
|
|
332
|
+
headers: { 'Content-Type': 'application/json' },
|
|
333
|
+
body: JSON.stringify({
|
|
334
|
+
...context,
|
|
335
|
+
layoutId,
|
|
336
|
+
alignmentSchema: layoutJson,
|
|
337
|
+
existingData: data,
|
|
338
|
+
assets: assets || context.assets
|
|
339
|
+
})
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
catch (fetchError) {
|
|
343
|
+
// Handle network errors, CORS errors, etc.
|
|
344
|
+
throw new Error(fetchError.message || 'Network request failed. Please check your connection and try again.');
|
|
345
|
+
}
|
|
346
|
+
const contentType = response.headers.get('content-type');
|
|
347
|
+
if (!contentType || !contentType.includes('application/json')) {
|
|
348
|
+
const text = await response.text();
|
|
349
|
+
console.error('Non-JSON response received:', text);
|
|
350
|
+
throw new Error('The server returned an invalid response. Please ensure the AI endpoint is correctly configured.');
|
|
351
|
+
}
|
|
245
352
|
if (!response.ok) {
|
|
246
|
-
|
|
247
|
-
|
|
353
|
+
let errorMessage = 'Failed to generate content';
|
|
354
|
+
try {
|
|
355
|
+
const error = await response.json();
|
|
356
|
+
errorMessage = error.error || error.message || errorMessage;
|
|
357
|
+
}
|
|
358
|
+
catch {
|
|
359
|
+
// If response is not JSON, use status text
|
|
360
|
+
errorMessage = response.statusText || `Server returned ${response.status} error`;
|
|
361
|
+
}
|
|
362
|
+
throw new Error(errorMessage);
|
|
248
363
|
}
|
|
249
364
|
const generatedData = await response.json();
|
|
250
365
|
if (!shouldUpdate)
|
|
@@ -263,7 +378,8 @@ export const PayazaFormEditor = ({ layoutId, initialData, inventory, onChange, o
|
|
|
263
378
|
setLayoutJson(updatedLayout);
|
|
264
379
|
// CRITICAL: Merge ALL pages from the updated AI layout into the StoreConfig
|
|
265
380
|
// to ensure the StoreConfig is fully updated for all pages.
|
|
266
|
-
|
|
381
|
+
// We set actualDataPrecedence to false so AI data takes precedence.
|
|
382
|
+
const updatedStoreConfig = mergeAllPagesWithActualData(updatedLayout, data, { actualDataPrecedence: false });
|
|
267
383
|
setData(updatedStoreConfig);
|
|
268
384
|
onChange?.(updatedStoreConfig);
|
|
269
385
|
// Optional: Add a small delay and a "polishing" step for specific layouts like food
|
|
@@ -280,7 +396,71 @@ export const PayazaFormEditor = ({ layoutId, initialData, inventory, onChange, o
|
|
|
280
396
|
catch (error) {
|
|
281
397
|
if (shouldUpdate) {
|
|
282
398
|
console.error('AI Generation Error:', error);
|
|
283
|
-
|
|
399
|
+
// Determine error type and message
|
|
400
|
+
let errorTitle = 'AI Generation Failed';
|
|
401
|
+
let errorMessage = 'We encountered an issue while generating content.';
|
|
402
|
+
let actionableMessage = 'Please try again. If the problem persists, you can continue editing manually.';
|
|
403
|
+
// Extract error message from various error types
|
|
404
|
+
let errorText = '';
|
|
405
|
+
if (error instanceof Error) {
|
|
406
|
+
errorText = error.message.toLowerCase();
|
|
407
|
+
}
|
|
408
|
+
else if (typeof error === 'string') {
|
|
409
|
+
errorText = error.toLowerCase();
|
|
410
|
+
}
|
|
411
|
+
else if (error && typeof error === 'object' && 'message' in error) {
|
|
412
|
+
errorText = String(error.message).toLowerCase();
|
|
413
|
+
}
|
|
414
|
+
// Handle specific error cases
|
|
415
|
+
if (errorText.includes('network') || errorText.includes('fetch') || errorText.includes('failed to fetch') ||
|
|
416
|
+
errorText.includes('ai generation fetch failed') || errorText.includes('network request failed')) {
|
|
417
|
+
errorTitle = 'Connection Problem';
|
|
418
|
+
errorMessage = 'Unable to connect to the AI service.';
|
|
419
|
+
actionableMessage = 'Please check your internet connection and try again.';
|
|
420
|
+
}
|
|
421
|
+
else if (errorText.includes('timeout') || errorText.includes('aborted')) {
|
|
422
|
+
errorTitle = 'Request Timed Out';
|
|
423
|
+
errorMessage = 'The AI service is taking longer than expected to respond.';
|
|
424
|
+
actionableMessage = 'Please try again in a moment. The service may be experiencing high demand.';
|
|
425
|
+
}
|
|
426
|
+
else if (errorText.includes('500') || errorText.includes('server error') || errorText.includes('internal server')) {
|
|
427
|
+
errorTitle = 'Service Temporarily Unavailable';
|
|
428
|
+
errorMessage = 'The AI service is experiencing technical difficulties.';
|
|
429
|
+
actionableMessage = 'Please try again in a few moments. If the issue continues, contact support.';
|
|
430
|
+
}
|
|
431
|
+
else if (errorText.includes('400') || errorText.includes('bad request')) {
|
|
432
|
+
errorTitle = 'Invalid Request';
|
|
433
|
+
errorMessage = 'There was an issue with the generation request.';
|
|
434
|
+
actionableMessage = 'Please try again with different content or contact support if the issue persists.';
|
|
435
|
+
}
|
|
436
|
+
else if (errorText.includes('api key') || errorText.includes('unauthorized') || errorText.includes('403') || errorText.includes('401')) {
|
|
437
|
+
errorTitle = 'Service Unavailable';
|
|
438
|
+
errorMessage = 'The AI service is temporarily unavailable.';
|
|
439
|
+
actionableMessage = 'Please try again later or contact support for assistance.';
|
|
440
|
+
}
|
|
441
|
+
else if (errorText.includes('quota') || errorText.includes('429') || errorText.includes('rate limit')) {
|
|
442
|
+
errorTitle = 'Service Limit Reached';
|
|
443
|
+
errorMessage = 'The AI service has reached its usage limit.';
|
|
444
|
+
actionableMessage = 'Please try again later or contact support to increase your service limits.';
|
|
445
|
+
}
|
|
446
|
+
else if (error instanceof Error) {
|
|
447
|
+
errorMessage = error.message || 'An unexpected error occurred.';
|
|
448
|
+
}
|
|
449
|
+
else if (typeof error === 'string') {
|
|
450
|
+
errorMessage = error;
|
|
451
|
+
}
|
|
452
|
+
showAlert(`${errorMessage} ${actionableMessage}`, 'error', {
|
|
453
|
+
title: errorTitle,
|
|
454
|
+
confirmText: 'Try Again',
|
|
455
|
+
cancelText: 'Continue Editing',
|
|
456
|
+
showCancel: true,
|
|
457
|
+
onConfirm: () => {
|
|
458
|
+
setIsAIModalOpen(true);
|
|
459
|
+
},
|
|
460
|
+
onCancel: () => {
|
|
461
|
+
setIsAIModalOpen(false);
|
|
462
|
+
}
|
|
463
|
+
});
|
|
284
464
|
}
|
|
285
465
|
}
|
|
286
466
|
finally {
|
|
@@ -298,25 +478,25 @@ export const PayazaFormEditor = ({ layoutId, initialData, inventory, onChange, o
|
|
|
298
478
|
if (!layoutJson || !pageSchema) {
|
|
299
479
|
return (_jsx("div", { className: "flex items-center justify-center h-full", children: _jsx("div", { className: "text-slate-400", children: "Layout not found" }) }));
|
|
300
480
|
}
|
|
301
|
-
return (_jsxs("div", { className: `flex flex-col h-full bg-white ${className} font-sans antialiased text-slate-900`, children: [_jsx(EditorHeader, { layoutName: layoutJson.layoutName || layoutId, storeName: storeName, activePage: activePageId, pages: pages, onPageSwitch: setActivePageId, onSave: handleSave, onPublish: onPublish ? handlePublish : undefined, onBack: onBack, viewMode: viewMode, onViewModeChange: setViewMode, editorType: "payaza-form", isSaving: isSaving, isPublishing: isPublishing, onGenerateAI: () => setIsAIModalOpen(true), isGeneratingAI: isGeneratingAI }), _jsx(AIGenerateModal, { isOpen: isAIModalOpen, onClose: () => setIsAIModalOpen(false), onGenerate: handleAIGenerate, initialContext: {
|
|
481
|
+
return (_jsxs("div", { className: `flex flex-col h-full bg-white ${className} font-sans antialiased text-slate-900`, children: [_jsx(EditorHeader, { layoutName: layoutJson.layoutName || layoutId, storeName: storeName, activePage: activePageId, pages: pages, onPageSwitch: setActivePageId, onSave: handleSave, onPublish: onPublish && hasChangesSincePublish ? handlePublish : undefined, onBack: onBack, onBackToSettings: onBackToSettings, onOpenAssets: onOpenAssets, onBackToTheme: onBackToTheme, onUndo: handleUndo, onRedo: handleRedo, canUndo: historyIndex > 0, canRedo: historyIndex < history.length - 1, viewMode: viewMode, onViewModeChange: setViewMode, editorType: "payaza-form", isSaving: isSaving, isPublishing: isPublishing, onGenerateAI: () => setIsAIModalOpen(true), isGeneratingAI: isGeneratingAI }), _jsx(AIGenerateModal, { isOpen: isAIModalOpen, onClose: () => setIsAIModalOpen(false), onGenerate: handleAIGenerate, initialContext: {
|
|
302
482
|
storeName: storeName || data?.name || '',
|
|
303
483
|
storeDescription: storeDescription || data?.description || '',
|
|
304
484
|
assets: assets || [],
|
|
305
|
-
}, isGenerating: isGeneratingAI }), _jsxs("div", { className: "flex flex-1 overflow-hidden", children: [_jsx("div", { className: "flex-1 overflow-hidden bg-[#f8f9fb] relative", children: _jsx(IframePreview, { layoutId: layoutId, config: data, activePageId: activePageId, viewMode: viewMode }) }), _jsx(ResizableSidebar, { initialWidth: 380, minWidth: 340, maxWidth: 500, className: "bg-white border-l border-slate-200 shadow-[-12px_0_32px_-4px_rgba(0,0,0,0.03)]", children: _jsxs("div", { className: "flex flex-col h-full bg-white relative", children: [_jsxs("div", { className: "bg-white/90 backdrop-blur-xl sticky top-0 z-30 border-b border-slate-100 px-6 pt-6 pb-4", children: [_jsxs("div", { className: "flex items-center justify-between mb-6", children: [_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("h3", { className: "text-[11px] font-black text-slate-900 uppercase tracking-[0.3em]", children: "Editor Panel" }), _jsxs("p", { className: "text-[9px] text-slate-400 font-bold uppercase tracking-widest flex items-center gap-1.5", children: [_jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-900 shadow-[0_0_8px_rgba(0,0,0,0.2)]" }), "Customising ", activePageId] })] }), _jsx("div", { className: "flex items-center gap-2", children: _jsx("div", { className: "p-2 rounded-xl bg-slate-50 text-slate-400 hover:text-slate-600 transition-all cursor-help", title: "Studio Info", children: _jsx(Info, { size: 14 }) }) })] }), _jsxs("div", { className: "relative group", children: [_jsx(Search, { className: "absolute left-4 top-1/2 -translate-y-1/2 text-slate-400 transition-colors group-focus-within:text-slate-900", size: 14, strokeWidth: 2.5 }), _jsx("input", { type: "text", placeholder: "Search properties...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), className: "w-full h-12 pl-11 pr-10 bg-slate-50/50 border border-slate-200 rounded-xl text-[12px] font-bold text-slate-700 placeholder:text-slate-400 focus:outline-none focus:ring-4 focus:ring-slate-900/5 focus:border-slate-900/20 focus:bg-white transition-all shadow-sm" }), searchQuery && (_jsx("button", { onClick: () => setSearchQuery(''), className: "absolute right-3 top-1/2 -translate-y-1/2 w-6 h-6 flex items-center justify-center bg-slate-200 text-slate-500 hover:bg-slate-300 hover:text-slate-700 rounded-full transition-all", children: _jsx(X, { size: 12, strokeWidth: 3 }) }))] })] }), _jsx("div", { className: "flex-1 payaza-form-editor overflow-y-auto custom-scrollbar bg-[#fcfcfd] px-6 py-8", children: _jsx("div", { className: "space-y-6", children: filteredSchema.length > 0 ? (filteredSchema.map(([sectionKey, sectionSchema]) => {
|
|
485
|
+
}, isGenerating: isGeneratingAI }), _jsxs("div", { className: "flex flex-1 overflow-hidden", children: [_jsx("div", { className: "flex-1 overflow-hidden bg-[#f8f9fb] relative", children: _jsx(IframePreview, { layoutId: layoutId, config: data, activePageId: activePageId, viewMode: viewMode, selectedSection: selectedSection }) }), _jsx(ResizableSidebar, { initialWidth: 380, minWidth: 340, maxWidth: 500, className: "bg-white border-l border-slate-200 shadow-[-12px_0_32px_-4px_rgba(0,0,0,0.03)]", children: _jsxs("div", { className: "flex flex-col h-full bg-white relative", children: [_jsxs("div", { className: "bg-white/90 backdrop-blur-xl sticky top-0 z-30 border-b border-slate-100 px-6 pt-6 pb-4", children: [_jsxs("div", { className: "flex items-center justify-between mb-6", children: [_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("h3", { className: "text-[11px] font-black text-slate-900 uppercase tracking-[0.3em]", children: "Editor Panel" }), _jsxs("p", { className: "text-[9px] text-slate-400 font-bold uppercase tracking-widest flex items-center gap-1.5", children: [_jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-900 shadow-[0_0_8px_rgba(0,0,0,0.2)]" }), "Customising ", activePageId] })] }), _jsx("div", { className: "flex items-center gap-2", children: _jsx("div", { className: "p-2 rounded-xl bg-slate-50 text-slate-400 hover:text-slate-600 transition-all cursor-help", title: "Studio Info", children: _jsx(Info, { size: 14 }) }) })] }), _jsxs("div", { className: "relative group", children: [_jsx(Search, { className: "absolute left-4 top-1/2 -translate-y-1/2 text-slate-400 transition-colors group-focus-within:text-slate-900", size: 14, strokeWidth: 2.5 }), _jsx("input", { type: "text", placeholder: "Search properties...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), className: "w-full h-12 pl-11 pr-10 bg-slate-50/50 border border-slate-200 rounded-xl text-[12px] font-bold text-slate-700 placeholder:text-slate-400 focus:outline-none focus:ring-4 focus:ring-slate-900/5 focus:border-slate-900/20 focus:bg-white transition-all shadow-sm" }), searchQuery && (_jsx("button", { onClick: () => setSearchQuery(''), className: "absolute right-3 top-1/2 -translate-y-1/2 w-6 h-6 flex items-center justify-center bg-slate-200 text-slate-500 hover:bg-slate-300 hover:text-slate-700 rounded-full transition-all", children: _jsx(X, { size: 12, strokeWidth: 3 }) }))] })] }), _jsx("div", { className: "flex-1 payaza-form-editor overflow-y-auto custom-scrollbar bg-[#fcfcfd] px-6 py-8", children: _jsx("div", { className: "space-y-6", children: filteredSchema.length > 0 ? (filteredSchema.map(([sectionKey, sectionSchema]) => {
|
|
306
486
|
const sectionData = pageSchema.data[sectionKey];
|
|
307
|
-
return (_jsx(FieldRenderer, { schemaField: sectionSchema, value: sectionData, path: [sectionKey], onChange: (value, fieldPath) => {
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
487
|
+
return (_jsx("div", { "data-section-key": sectionKey, className: selectedSection === sectionKey ? 'ring-2 ring-primary ring-inset rounded-xl bg-primary/5' : '', children: _jsx(FieldRenderer, { schemaField: sectionSchema, value: sectionData, path: [sectionKey], onChange: (value, fieldPath) => {
|
|
488
|
+
// fieldPath from FieldRenderer is relative to the section
|
|
489
|
+
// e.g., ['show'] for hero.show, ['sliders', 0, 'title'] for hero.sliders[0].title
|
|
490
|
+
handleFieldChange(sectionKey, fieldPath, value);
|
|
491
|
+
}, onArrayItemChange: (index, itemValue) => {
|
|
492
|
+
handleArrayItemChange(sectionKey, [], index, itemValue);
|
|
493
|
+
}, onArrayItemAdd: () => {
|
|
494
|
+
if (sectionSchema._itemSchema) {
|
|
495
|
+
handleArrayItemAdd(sectionKey, [], sectionSchema._itemSchema);
|
|
496
|
+
}
|
|
497
|
+
}, onArrayItemRemove: (index) => {
|
|
498
|
+
handleArrayItemRemove(sectionKey, [], index);
|
|
499
|
+
} }) }, sectionKey));
|
|
320
500
|
})) : (_jsxs("div", { className: "flex flex-col items-center justify-center py-24 text-center px-6 bg-slate-50/50 rounded-3xl border border-dashed border-slate-200", children: [_jsx("div", { className: "w-20 h-20 bg-white rounded-full flex items-center justify-center mb-6 shadow-xl border border-slate-50", children: _jsx(Search, { size: 28, className: "text-slate-200", strokeWidth: 1.5 }) }), _jsx("p", { className: "text-[12px] font-black text-slate-500 uppercase tracking-widest mb-2", children: "No results found" }), _jsx("p", { className: "text-[10px] text-slate-400 font-bold uppercase tracking-widest max-w-[200px] leading-relaxed", children: "Try adjusting your search to find specific properties" })] })) }) }), _jsx("div", { className: "px-8 py-6 bg-white border-t border-slate-100 shadow-[0_-10px_40px_-10px_rgba(0,0,0,0.02)]", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "w-2 h-2 rounded-full bg-green-500 shadow-[0_0_10px_rgba(34,197,94,0.5)] animate-pulse" }), _jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("p", { className: "text-[10px] font-black text-slate-900 uppercase tracking-[0.1em]", children: "Studio Status" }), _jsx("p", { className: "text-[8px] font-bold text-slate-400 uppercase tracking-widest", children: "Live Syncing Enabled" })] })] }), _jsx("div", { className: "flex items-center gap-2 px-3 py-1.5 rounded-full bg-slate-50 border border-slate-100 shadow-sm", children: _jsx("span", { className: "text-[9px] font-black text-slate-400 uppercase tracking-[0.1em]", children: "Build v1.0.32" }) })] }) })] }) })] }), _jsx("style", { dangerouslySetInnerHTML: { __html: `
|
|
321
501
|
/* Custom Scrollbar */
|
|
322
502
|
.custom-scrollbar::-webkit-scrollbar {
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { PayazaFormEditorProps } from './PayazaFormEditor';
|
|
3
3
|
export interface PayazaFormEditorFullPageProps extends PayazaFormEditorProps {
|
|
4
|
+
onBackToSettings?: () => void;
|
|
5
|
+
onOpenAssets?: () => void;
|
|
6
|
+
onBackToTheme?: () => void;
|
|
4
7
|
}
|
|
5
8
|
export declare const PayazaFormEditorFullPage: React.FC<PayazaFormEditorFullPageProps>;
|
|
6
9
|
//# sourceMappingURL=PayazaFormEditorFullPage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PayazaFormEditorFullPage.d.ts","sourceRoot":"","sources":["../../../src/editor/payaza-form/PayazaFormEditorFullPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAoB,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"PayazaFormEditorFullPage.d.ts","sourceRoot":"","sources":["../../../src/editor/payaza-form/PayazaFormEditorFullPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAoB,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAG7E,MAAM,WAAW,6BAA8B,SAAQ,qBAAqB;IAE1E,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED,eAAO,MAAM,wBAAwB,EAAE,KAAK,CAAC,EAAE,CAAC,6BAA6B,CAU5E,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { PayazaFormEditor } from './PayazaFormEditor';
|
|
3
|
+
import { AlertModalProvider } from '../../components/ui/alert-modal';
|
|
3
4
|
export const PayazaFormEditorFullPage = (props) => {
|
|
4
|
-
return (_jsx("div", { className: "fixed inset-0 z-50 flex flex-col bg-slate-50", children: _jsx("main", { className: "flex-1 overflow-hidden", children: _jsx(PayazaFormEditor, { ...props }) }) }));
|
|
5
|
+
return (_jsx(AlertModalProvider, { children: _jsx("div", { className: "fixed inset-0 z-50 flex flex-col bg-slate-50", children: _jsx("main", { className: "flex-1 overflow-hidden", children: _jsx(PayazaFormEditor, { ...props }) }) }) }));
|
|
5
6
|
};
|
|
@@ -15,7 +15,7 @@ export const SectionField = ({ label, description, children, defaultExpanded = f
|
|
|
15
15
|
const isAllHidden = !visibility.desktop && !visibility.tablet && !visibility.mobile;
|
|
16
16
|
return (_jsxs("div", { className: `mb-4 overflow-hidden rounded-2xl border transition-all duration-300 ${isExpanded
|
|
17
17
|
? 'border-slate-300 bg-white shadow-[0_8px_30px_rgb(0,0,0,0.04)] scale-[1.01]'
|
|
18
|
-
: 'border-slate-200 bg-slate-50/50 hover:bg-white hover:border-slate-300 hover:shadow-
|
|
18
|
+
: 'border-slate-200 bg-slate-50/50 hover:bg-white hover:border-slate-300/80 hover:shadow-sm'} ${isAllHidden ? 'opacity-60 grayscale' : 'opacity-100'}`, children: [_jsxs("div", { className: "flex items-center w-full relative", children: [_jsxs("button", { type: "button", onClick: () => setIsExpanded(!isExpanded), className: "flex-1 flex items-center justify-between py-5 px-6 text-left transition-colors group", children: [_jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: `text-[11px] font-bold uppercase tracking-wider transition-colors ${isExpanded ? 'text-slate-900' : 'text-slate-900'}`, children: label }), !isExpanded && (_jsxs("div", { className: "flex items-center gap-1.5 ml-1", children: [!isAllVisible && !isAllHidden && (_jsxs("div", { className: "flex items-center gap-1 p-1 bg-slate-100/50 rounded-md", children: [visibility.desktop && _jsx(Monitor, { size: 10, className: "text-slate-400" }), visibility.tablet && _jsx(Tablet, { size: 10, className: "text-slate-400" }), visibility.mobile && _jsx(Smartphone, { size: 10, className: "text-slate-400" })] })), isAllHidden && (_jsxs("div", { className: "flex items-center gap-1 px-1.5 py-0.5 bg-red-50 rounded-md", children: [_jsx(EyeOff, { size: 10, className: "text-red-400" }), _jsx("span", { className: "text-[8px] font-bold text-red-400 uppercase tracking-tighter", children: "Hidden" })] }))] }))] }), description && !isExpanded && (_jsx("span", { className: "text-[10px] text-slate-400 font-bold uppercase tracking-widest truncate max-w-[180px]", children: description }))] }), _jsx("div", { className: `transition-all duration-300 rounded-full p-1.5 ${isExpanded ? 'rotate-180 bg-slate-100 text-slate-900' : 'text-slate-300 group-hover:text-slate-500 bg-transparent'}`, children: _jsx(ChevronDown, { className: "w-4 h-4", strokeWidth: 2.5 }) })] }), !isExpanded && (_jsx("div", { className: "absolute right-[52px] top-1/2 -translate-y-1/2 flex items-center opacity-0 group-hover:opacity-100 transition-opacity pr-2 border-r border-slate-100 mr-2", children: _jsx("button", { onClick: (e) => {
|
|
19
19
|
e.stopPropagation();
|
|
20
20
|
const nextHidden = !isAllHidden;
|
|
21
21
|
setVisibility({
|
|
@@ -8,10 +8,14 @@ export declare function extractActualPageData(storeConfig: StoreConfig, pageId:
|
|
|
8
8
|
* Merge requirement schema with actual layout data
|
|
9
9
|
* Actual data takes precedence, but requirement provides structure and defaults
|
|
10
10
|
*/
|
|
11
|
-
export declare function mergeRequirementWithActualData(requirement: OverlayLayoutJSON, storeConfig: StoreConfig, pageId: string
|
|
11
|
+
export declare function mergeRequirementWithActualData(requirement: OverlayLayoutJSON, storeConfig: StoreConfig, pageId: string, options?: {
|
|
12
|
+
actualDataPrecedence?: boolean;
|
|
13
|
+
}): OverlayLayoutJSON;
|
|
12
14
|
/**
|
|
13
15
|
* Merge ALL pages in a requirement with actual data from StoreConfig
|
|
14
16
|
* This ensures the StoreConfig is fully populated with defaults for all pages
|
|
15
17
|
*/
|
|
16
|
-
export declare function mergeAllPagesWithActualData(requirement: OverlayLayoutJSON, storeConfig: StoreConfig
|
|
18
|
+
export declare function mergeAllPagesWithActualData(requirement: OverlayLayoutJSON, storeConfig: StoreConfig, options?: {
|
|
19
|
+
actualDataPrecedence?: boolean;
|
|
20
|
+
}): StoreConfig;
|
|
17
21
|
//# sourceMappingURL=data-merger.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-merger.d.ts","sourceRoot":"","sources":["../../../src/editor/payaza-form/data-merger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AA6GpD;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,iBAAiB,GAC9B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAoLrB;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAC5C,WAAW,EAAE,iBAAiB,EAC9B,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"data-merger.d.ts","sourceRoot":"","sources":["../../../src/editor/payaza-form/data-merger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AA6GpD;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,iBAAiB,GAC9B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAoLrB;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAC5C,WAAW,EAAE,iBAAiB,EAC9B,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IAAE,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAAmC,GAC3E,iBAAiB,CAoGnB;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,iBAAiB,EAC9B,WAAW,EAAE,WAAW,EACxB,OAAO,GAAE;IAAE,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAAmC,GAC3E,WAAW,CAWb"}
|