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.
Files changed (46) hide show
  1. package/dist/editor/payaza-form/PayazaFormEditor.d.ts.map +1 -1
  2. package/dist/editor/payaza-form/PayazaFormEditor.js +243 -63
  3. package/dist/editor/payaza-form/PayazaFormEditorFullPage.d.ts +3 -0
  4. package/dist/editor/payaza-form/PayazaFormEditorFullPage.d.ts.map +1 -1
  5. package/dist/editor/payaza-form/PayazaFormEditorFullPage.js +2 -1
  6. package/dist/editor/payaza-form/components/SectionField.js +1 -1
  7. package/dist/editor/payaza-form/data-merger.d.ts +6 -2
  8. package/dist/editor/payaza-form/data-merger.d.ts.map +1 -1
  9. package/dist/editor/payaza-form/data-merger.js +64 -51
  10. package/dist/editor/payaza-form/schema-bridge.d.ts.map +1 -1
  11. package/dist/editor/payaza-form/schema-bridge.js +17 -4
  12. package/dist/editor/shared/AIGenerateModal.js +1 -1
  13. package/dist/editor/shared/EditorHeader.d.ts +7 -0
  14. package/dist/editor/shared/EditorHeader.d.ts.map +1 -1
  15. package/dist/editor/shared/EditorHeader.js +45 -9
  16. package/dist/editor/shared/IframePreview.d.ts +1 -0
  17. package/dist/editor/shared/IframePreview.d.ts.map +1 -1
  18. package/dist/editor/shared/IframePreview.js +12 -2
  19. package/dist/editor/types.d.ts +12 -0
  20. package/dist/editor/types.d.ts.map +1 -1
  21. package/dist/layouts/booking/components/BookingHomePage.d.ts.map +1 -1
  22. package/dist/layouts/booking/components/BookingHomePage.js +44 -3
  23. package/dist/layouts/booking-agenda/components/BookingHomePageAgenda.d.ts.map +1 -1
  24. package/dist/layouts/booking-agenda/components/BookingHomePageAgenda.js +33 -3
  25. package/dist/layouts/clothing/components/ClothingHomePage.d.ts.map +1 -1
  26. package/dist/layouts/clothing/components/ClothingHomePage.js +26 -23
  27. package/dist/layouts/clothing-minimal/components/ClothingHomePageMinimal.d.ts.map +1 -1
  28. package/dist/layouts/clothing-minimal/components/ClothingHomePageMinimal.js +23 -2
  29. package/dist/layouts/electronics/components/ElectronicsHomePage.d.ts.map +1 -1
  30. package/dist/layouts/electronics/components/ElectronicsHomePage.js +24 -4
  31. package/dist/layouts/electronics-grid/components/ElectronicsHomePageGrid.d.ts.map +1 -1
  32. package/dist/layouts/electronics-grid/components/ElectronicsHomePageGrid.js +24 -2
  33. package/dist/layouts/food/components/FoodHomePage.d.ts.map +1 -1
  34. package/dist/layouts/food/components/FoodHomePage.js +38 -2
  35. package/dist/layouts/food-modern/components/FoodHomePageModern.d.ts.map +1 -1
  36. package/dist/layouts/food-modern/components/FoodHomePageModern.js +31 -2
  37. package/dist/layouts/motivational-speaker/components/MotivationalHomePage.d.ts.map +1 -1
  38. package/dist/layouts/motivational-speaker/components/MotivationalHomePage.js +15 -1
  39. package/dist/layouts/shared/components/StoreHeader.d.ts.map +1 -1
  40. package/dist/layouts/shared/components/StoreHeader.js +24 -7
  41. package/dist/lib/preview-data.d.ts.map +1 -1
  42. package/dist/lib/preview-data.js +11 -1
  43. package/dist/lib/store-types.d.ts +1 -0
  44. package/dist/lib/store-types.d.ts.map +1 -1
  45. package/dist/styles/index.css +53 -29
  46. 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;AAM5F,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,CAulB5D,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, loadLayoutJSONSync } from './layout-loader';
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
- export const PayazaFormEditor = ({ layoutId, initialData, inventory, onChange, onSave, onPublish, onBack, title, className = '', customWidgets, storeName, storeDescription, assets, }) => {
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
- // Load requirement (schema + defaults)
64
- const syncLayout = loadLayoutJSONSync(layoutId, inventory);
65
- let requirementLayout = syncLayout;
66
- if (!requirementLayout) {
67
- requirementLayout = await loadLayoutJSON(layoutId, inventory);
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
- if (requirementLayout && isMounted) {
70
- // Store base requirement for re-merging on page changes
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
- if (isMounted) {
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
- const response = await fetch('/api/ai/generate', {
235
- method: 'POST',
236
- headers: { 'Content-Type': 'application/json' },
237
- body: JSON.stringify({
238
- ...context,
239
- layoutId,
240
- alignmentSchema: layoutJson,
241
- existingData: data,
242
- assets: assets || context.assets
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
- const error = await response.json();
247
- throw new Error(error.error || 'Failed to generate content');
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
- const updatedStoreConfig = mergeAllPagesWithActualData(updatedLayout, data);
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
- alert(`AI Generation failed: ${error.message}`);
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
- // fieldPath from FieldRenderer is relative to the section
309
- // e.g., ['show'] for hero.show, ['sliders', 0, 'title'] for hero.sliders[0].title
310
- handleFieldChange(sectionKey, fieldPath, value);
311
- }, onArrayItemChange: (index, itemValue) => {
312
- handleArrayItemChange(sectionKey, [], index, itemValue);
313
- }, onArrayItemAdd: () => {
314
- if (sectionSchema._itemSchema) {
315
- handleArrayItemAdd(sectionKey, [], sectionSchema._itemSchema);
316
- }
317
- }, onArrayItemRemove: (index) => {
318
- handleArrayItemRemove(sectionKey, [], index);
319
- } }, sectionKey));
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;AAE7E,MAAM,WAAW,6BAA8B,SAAQ,qBAAqB;CAAG;AAE/E,eAAO,MAAM,wBAAwB,EAAE,KAAK,CAAC,EAAE,CAAC,6BAA6B,CAQ5E,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-[0_4px_20px_rgb(0,0,0,0.02)]'} ${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-[12px] font-black tracking-tight transition-colors ${isExpanded ? 'text-slate-900' : 'text-slate-700'}`, 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) => {
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): OverlayLayoutJSON;
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): 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,GACb,iBAAiB,CAwFnB;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,iBAAiB,EAC9B,WAAW,EAAE,WAAW,GACvB,WAAW,CAWb"}
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"}