fix: resolve container width issues, spa redirects, and appearance settings overwrite. feat: enhance order/sub details and newsletter layout

This commit is contained in:
Dwindi Ramadhana
2026-02-05 00:09:40 +07:00
parent a0b5f8496d
commit 5f08c18ec7
77 changed files with 7027 additions and 4546 deletions

View File

@@ -52,7 +52,9 @@ interface CanvasRendererProps {
onDuplicateSection: (id: string) => void;
onMoveSection: (id: string, direction: 'up' | 'down') => void;
onReorderSections: (sections: Section[]) => void;
onDeviceModeChange: (mode: 'desktop' | 'mobile') => void;
containerWidth?: 'boxed' | 'fullwidth' | 'default';
}
const SECTION_TYPES = [
@@ -84,7 +86,9 @@ export function CanvasRenderer({
onDuplicateSection,
onMoveSection,
onReorderSections,
onDeviceModeChange,
containerWidth = 'default',
}: CanvasRendererProps) {
const [hoveredSectionId, setHoveredSectionId] = useState<string | null>(null);
@@ -149,7 +153,9 @@ export function CanvasRenderer({
<div
className={cn(
'mx-auto bg-white shadow-xl rounded-lg transition-all duration-300 min-h-[500px]',
deviceMode === 'desktop' ? 'max-w-4xl' : 'max-w-sm'
deviceMode === 'mobile' ? 'max-w-sm' : (
containerWidth === 'fullwidth' ? 'max-w-full mx-4' : 'max-w-6xl'
)
)}
>
{sections.length === 0 ? (

View File

@@ -173,7 +173,7 @@ export function CanvasSection({
<Trash2 className="w-4 h-4" />
</button>
</AlertDialogTrigger>
<AlertDialogContent className="z-[60]">
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>{__('Delete this section?')}</AlertDialogTitle>
<AlertDialogDescription>

View File

@@ -16,6 +16,7 @@ import {
AccordionItem,
AccordionTrigger,
} from '@/components/ui/accordion';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Slider } from '@/components/ui/slider';
import {
@@ -51,6 +52,7 @@ interface PageItem {
title: string;
url?: string;
isSpaLanding?: boolean;
containerWidth?: 'boxed' | 'fullwidth';
}
interface InspectorPanelProps {
@@ -69,6 +71,7 @@ interface InspectorPanelProps {
onSetAsSpaLanding?: () => void;
onUnsetSpaLanding?: () => void;
onDeletePage?: () => void;
onContainerWidthChange?: (width: 'boxed' | 'fullwidth') => void;
}
// Section field configurations
@@ -191,6 +194,7 @@ export function InspectorPanel({
onSetAsSpaLanding,
onUnsetSpaLanding,
onDeletePage,
onContainerWidthChange,
}: InspectorPanelProps) {
if (isCollapsed) {
return (
@@ -273,6 +277,31 @@ export function InspectorPanel({
</div>
)}
{/* Container Width */}
{!isTemplate && page && onContainerWidthChange && (
<div className="pt-2 border-t mt-2">
<Label className="text-xs text-gray-500 uppercase tracking-wider block mb-2">{__('Container Width')}</Label>
<RadioGroup
value={page.containerWidth || 'boxed'}
onValueChange={(val: any) => onContainerWidthChange(val)}
className="gap-2"
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="boxed" id="cw-boxed" />
<Label htmlFor="cw-boxed" className="text-sm font-normal cursor-pointer">{__('Boxed')}</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="fullwidth" id="cw-full" />
<Label htmlFor="cw-full" className="text-sm font-normal cursor-pointer">{__('Full Width')}</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="default" id="cw-default" />
<Label htmlFor="cw-default" className="text-sm font-normal cursor-pointer text-gray-500">{__('Default (SPA Settings)')}</Label>
</div>
</RadioGroup>
</div>
)}
{/* Danger Zone */}
{!isTemplate && page && onDeletePage && (
<div className="pt-2 border-t mt-2">

View File

@@ -93,6 +93,12 @@ export default function AppearancePages() {
enabled: !!currentPage,
});
// Fetch global settings for defaults
const { data: globalSettings } = useQuery({
queryKey: ['appearance-settings'],
queryFn: async () => api.get('/appearance/settings'),
});
// Update store when page data loads
useEffect(() => {
if (pageData?.structure?.sections) {
@@ -106,6 +112,10 @@ export default function AppearancePages() {
if (pageData?.is_front_page !== undefined && currentPage) {
setCurrentPage({ ...currentPage, isFrontPage: !!pageData.is_front_page });
}
// Sync containerWidth
if (pageData?.container_width && currentPage) {
setCurrentPage({ ...currentPage, containerWidth: pageData.container_width });
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pageData, setSections, markAsSaved, setAvailableSources]); // Removed currentPage from dependency to avoid loop
@@ -296,7 +306,13 @@ export default function AppearancePages() {
onDuplicateSection={duplicateSection}
onMoveSection={moveSection}
onReorderSections={reorderSections}
onDeviceModeChange={setDeviceMode}
containerWidth={
currentPage?.containerWidth && currentPage.containerWidth !== 'default'
? currentPage.containerWidth
: ((globalSettings as any)?.data?.general?.container_width || 'boxed')
}
/>
) : (
<div className="flex-1 bg-gray-100 flex items-center justify-center text-gray-400">
@@ -356,6 +372,12 @@ export default function AppearancePages() {
}}
onUnsetSpaLanding={() => unsetSpaLandingMutation.mutate()}
onDeletePage={handleDeletePage}
onContainerWidthChange={(width) => {
if (currentPage) {
setCurrentPage({ ...currentPage, containerWidth: width });
markAsSaved(); // Mark as changed so save button enables
}
}}
/>
)
}

View File

@@ -62,6 +62,7 @@ export interface PageItem {
url?: string;
isFrontPage?: boolean;
isSpaLanding?: boolean;
containerWidth?: 'boxed' | 'fullwidth';
}
interface PageEditorState {
@@ -422,7 +423,10 @@ export const usePageEditorStore = create<PageEditorState>((set, get) => ({
'X-WP-Nonce': (window as any).WNW_API.nonce,
'Content-Type': 'application/json'
},
body: JSON.stringify({ sections })
body: JSON.stringify({
sections,
container_width: currentPage.containerWidth
})
});
set({