Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export const PageManagementDialog: React.FC<PageManagementDialogProps> = ({ open
const [editingPageId, setEditingPageId] = useState<string | null>(null);
const [editingPageName, setEditingPageName] = useState('');
const [error, setError] = useState<string | null>(null);
const [autoSaving, setAutoSaving] = useState<string | null>(null); // Track which page is being auto-saved

// Reset state when dialog opens
React.useEffect(() => {
Expand All @@ -67,6 +68,7 @@ export const PageManagementDialog: React.FC<PageManagementDialogProps> = ({ open
setEditingPageId(null);
setEditingPageName('');
setError(null);
setAutoSaving(null);
}
}, [open]);

Expand Down Expand Up @@ -135,12 +137,40 @@ export const PageManagementDialog: React.FC<PageManagementDialogProps> = ({ open

// Handle publishing/unpublishing a page
const handlePublishPage = async (pageId: string, publish: boolean) => {
console.log('πŸš€ handlePublishPage called:', { pageId, publish, currentPageId: currentPage?.id });

try {
await publishPage(pageId, publish);
setError(null);

// Auto-save if publishing the current page (always save to ensure latest changes are published)
if (publish && currentPage && pageId === currentPage.id) {
console.log('πŸ”„ Auto-saving page before publishing...', { pageId, currentPageId: currentPage.id });
setAutoSaving(pageId);

try {
console.log('πŸ’Ύ Calling savePage...');
await savePage(pageId);
console.log('βœ… Auto-save completed successfully');
} catch (saveErr) {
console.error('❌ Auto-save failed:', saveErr);
setAutoSaving(null);
setError('Failed to auto-save page before publishing. Please save manually first.');
return; // Don't proceed with publishing if auto-save fails
}

setAutoSaving(null);
} else {
console.log('⏭️ Skipping auto-save:', { publish, hasCurrentPage: !!currentPage, isCurrentPage: pageId === currentPage?.id });
}

// Proceed with publish/unpublish
console.log('πŸ“€ Calling publishPage...');
await publishPage(pageId, publish);
console.log('βœ… Publish completed successfully');
} catch (err) {
console.error('❌ Publish failed:', err);
setAutoSaving(null);
setError(`Failed to ${publish ? 'publish' : 'unpublish'} page`);
console.error(err);
}
};

Expand Down Expand Up @@ -297,6 +327,9 @@ export const PageManagementDialog: React.FC<PageManagementDialogProps> = ({ open
) : (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
{page.name}
{autoSaving === page.id && (
<Chip size="small" label="Saving before publish..." color="info" />
)}
{page.is_local && (
<Chip size="small" label="Local" color="warning" />
)}
Expand All @@ -312,7 +345,7 @@ export const PageManagementDialog: React.FC<PageManagementDialogProps> = ({ open
Route: {page.route || 'None'}
</Typography>
<Typography variant="caption" component="div">
Last Updated: {formatDate(page.updated_at as string)}
Last Updated: {formatDate((page as any).updated_at)}
</Typography>
{page.backup_date && (
<Typography variant="caption" component="div">
Expand Down Expand Up @@ -367,23 +400,30 @@ export const PageManagementDialog: React.FC<PageManagementDialogProps> = ({ open

{/* Publish/Unpublish Button */}
{!page.is_local && (
<Tooltip title={page.is_published ? "Unpublish" : "Publish"}>
<IconButton
edge="end"
aria-label={page.is_published ? "unpublish" : "publish"}
onClick={(e) => {
e.stopPropagation();
handlePublishPage(page.id, !page.is_published);
}}
size="small"
color={page.is_published ? "success" : "primary"}
>
{page.is_published ? (
<UnpublishedIcon fontSize="small" />
) : (
<PublishIcon fontSize="small" />
)}
</IconButton>
<Tooltip title={
autoSaving === page.id
? "Saving current changes before publish..."
: page.is_published ? "Unpublish" : "Publish"
}>
<span>
<IconButton
edge="end"
aria-label={page.is_published ? "unpublish" : "publish"}
onClick={(e) => {
e.stopPropagation();
handlePublishPage(page.id, !page.is_published);
}}
size="small"
color={page.is_published ? "success" : "primary"}
disabled={autoSaving === page.id}
>
{page.is_published ? (
<UnpublishedIcon fontSize="small" />
) : (
<PublishIcon fontSize="small" />
)}
</IconButton>
</span>
</Tooltip>
)}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,21 @@ interface PageManagementDialogAdapterProps {
* @param props The component props
* @returns The page management dialog adapter component
*/
export const PageManagementDialog: React.FC<PageManagementDialogAdapterProps> = ({
open,
onClose
export const PageManagementDialog: React.FC<PageManagementDialogAdapterProps> = ({
open,
onClose
}) => {
const {
const {
currentPage,
publishPage,
savePage,
backupPage,
restorePage,
updatePage
} = usePluginStudio();

console.log('πŸ”§ PageManagementDialogAdapter rendered:', { open, hasCurrentPage: !!currentPage });
console.log('πŸ”§ PageManagementDialogAdapter context:', { currentPageId: currentPage?.id, currentPageName: currentPage?.name });

// Only render the dialog if we have a current page
if (!currentPage) {
Expand All @@ -31,7 +35,33 @@ export const PageManagementDialog: React.FC<PageManagementDialogAdapterProps> =

// Handle publishing a page
const handlePublish = async (pageId: string, publish: boolean) => {
await publishPage(pageId, publish);
console.log('πŸš€ PageManagementDialogAdapter handlePublish called:', { pageId, publish, currentPageId: currentPage?.id });

try {
// Auto-save if publishing the current page (always save to ensure latest changes are published)
if (publish && currentPage && pageId === currentPage.id) {
console.log('πŸ”„ Auto-saving page before publishing...', { pageId, currentPageId: currentPage.id });

try {
console.log('πŸ’Ύ Calling savePage...');
await savePage(pageId);
console.log('βœ… Auto-save completed successfully');
} catch (saveErr) {
console.error('❌ Auto-save failed:', saveErr);
throw new Error('Failed to auto-save page before publishing. Please save manually first.');
}
} else {
console.log('⏭️ Skipping auto-save:', { publish, hasCurrentPage: !!currentPage, isCurrentPage: pageId === currentPage?.id });
}

// Proceed with publish/unpublish
console.log('πŸ“€ Calling publishPage...');
await publishPage(pageId, publish);
console.log('βœ… Publish completed successfully');
} catch (err) {
console.error('❌ Publish failed:', err);
throw err;
}
};

// Handle backing up a page
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,19 +124,19 @@ export const GridToolbar: React.FC<GridToolbarProps> = ({ onSave }) => {
}}>
{/* Left Section: Basic Actions and Page Management Controls */}
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, flex: '1 0 auto' }}>
{/* Page Selector - Always visible */}
<PageSelector
pages={pages}
currentPage={currentPage}
onPageChange={setCurrentPage}
onCreatePage={createPage}
onDeletePage={deletePage}
onRenamePage={renamePage}
onSavePage={savePage}
/>

{currentPage ? (
<>
{/* Create Page Button */}
<Tooltip title="Create New Page">
<IconButton
onClick={() => createPage('New Page')}
size="small"
sx={{ color: 'primary.main' }}
>
<AddIcon />
</IconButton>
</Tooltip>

{/* Save Page Button */}
<Tooltip title="Save Page">
<IconButton
Expand Down Expand Up @@ -190,17 +190,6 @@ export const GridToolbar: React.FC<GridToolbarProps> = ({ onSave }) => {
</span>
</Tooltip>

{/* Page Selector */}
<PageSelector
pages={pages}
currentPage={currentPage}
onPageChange={setCurrentPage}
onCreatePage={createPage}
onDeletePage={deletePage}
onRenamePage={renamePage}
onSavePage={savePage}
/>

{/* Page Management and Route Management */}
{currentPage.is_local !== true && (
<>
Expand Down Expand Up @@ -252,17 +241,8 @@ export const GridToolbar: React.FC<GridToolbarProps> = ({ onSave }) => {
)}
</>
) : (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Tooltip title="Create New Page">
<IconButton
onClick={() => createPage('New Page')}
size="small"
sx={{ color: 'primary.main' }}
>
<AddIcon />
</IconButton>
</Tooltip>
<Box sx={{ ml: 1 }}>No pages available. Create a new page to get started.</Box>
<Box sx={{ ml: 1, color: 'text.secondary' }}>
No pages available. Use the + button to create a new page.
</Box>
)}
</Box>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/features/plugin-studio/hooks/page/usePages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -337,8 +337,8 @@ export const usePages = () => {
// Normal case - update existing page
// Create deep clones to avoid reference issues
const content = {
layouts: JSON.parse(JSON.stringify(currentPage.layouts)),
modules: JSON.parse(JSON.stringify(currentPage.modules))
layouts: JSON.parse(JSON.stringify(currentPage.layouts || {})),
modules: JSON.parse(JSON.stringify(currentPage.modules || {}))
};

console.log('savePage - Saving content to backend:', content);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useEffect, useCallback, useMemo } from 'react';
import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
import { Box, useTheme } from '@mui/material';
import { UnifiedPageRenderer } from '../components/UnifiedPageRenderer';
import { ResponsiveContainer } from '../components/ResponsiveContainer';
Expand Down Expand Up @@ -76,6 +76,9 @@ export const PluginStudioAdapter: React.FC<PluginStudioAdapterProps> = ({
const [conversionError, setConversionError] = useState<Error | null>(null);
const [isConverting, setIsConverting] = useState(false);

// Safeguard to prevent infinite loops during module updates
const isUpdatingModulesRef = useRef(false);

// Performance tracking
const [performanceMetrics, setPerformanceMetrics] = useState<{
conversionTime?: number;
Expand Down Expand Up @@ -415,9 +418,11 @@ export const PluginStudioAdapter: React.FC<PluginStudioAdapterProps> = ({

/**
* Watch for changes in page modules and update converted data
* Fixed: Removed convertedPageData from dependency array to prevent infinite loop
* Added safeguard to prevent recursive updates
*/
useEffect(() => {
if (!page || !convertedPageData) return;
if (!page || !convertedPageData || isUpdatingModulesRef.current) return;

// Check if modules have changed
const currentModulesHash = JSON.stringify(page.modules);
Expand All @@ -428,15 +433,18 @@ export const PluginStudioAdapter: React.FC<PluginStudioAdapterProps> = ({

if (currentModulesHash !== convertedModulesHash) {
console.log('[PluginStudioAdapter] Modules changed, updating converted data');
isUpdatingModulesRef.current = true;
try {
const updated = convertPageData(page, layouts || { desktop: [], tablet: [], mobile: [] });
setConvertedPageData(updated);
} catch (error) {
console.error('[PluginStudioAdapter] Module update failed:', error);
setConversionError(error as Error);
} finally {
isUpdatingModulesRef.current = false;
}
}
}, [page?.modules, convertPageData, convertedPageData, layouts]);
}, [page?.modules, convertPageData, layouts]);

/**
* Determine render mode based on preview state
Expand Down