Files
WooNooW/customer-spa/src/layouts/LayoutWrapper.tsx
Dwindi Ramadhana 396ca25be4 feat: Page Editor v1.0 - canonical schema, SSR parity, and migration
Major improvements to WooNooW Page Editor system:

Schema & Architecture:
- Canonical section schema with unified sectionSchema.ts
- Normalized feature-grid to use items (not features)
- Standardized default values across all section types
- Schema versioning with automatic migration on read

Backend (PHP):
- Enhanced PlaceholderRenderer with typed output contracts
- Added fallback behavior for empty/invalid dynamic sources
- Added caching support for post data resolution
- New SchemaMigration class for backward compatibility
- New Features class for feature flags
- Enhanced PageSSR with full style support
- Removed controller-level special-casing for related_posts

Frontend (Admin SPA):
- Updated CanvasRenderer with schema-aware transformation
- Enhanced InspectorPanel with canonical schema metadata
- Added new section renderers

Frontend (Customer SPA):
- New section components: BentoCategoryGrid, MarqueeBanner, ProductCarousel, ShoppableImage
- Updated FeatureGridSection for items prop contract

Testing:
- Add PHP tests: SchemaMigrationTest, PlaceholderRendererTest, PageSSRTest
- Add TypeScript tests: schema-integration, feature-grid-regression
- Add parity tests for React vs SSR content matching
- Add CI script: check-schema-drift.mjs
- Add VERIFICATION_CHECKLIST.md

Documentation:
- RELEASE_NOTES-v1.0.md with full release notes
- docs/PAGE_EDITOR_SECTION_SCHEMA_V1.md
- docs/PAGE_EDITOR_SSR_COVERAGE_AUDIT.md
2026-05-30 13:02:08 +07:00

62 lines
2.1 KiB
TypeScript

import React, { ReactNode } from 'react';
import { useLocation } from 'react-router-dom';
import { useCheckoutSettings, useThankYouSettings } from '../hooks/useAppearanceSettings';
import { MinimalHeader } from '../components/Layout/MinimalHeader';
import { MinimalFooter } from '../components/Layout/MinimalFooter';
import { useTheme } from '../contexts/ThemeContext';
interface LayoutWrapperProps {
children: ReactNode;
header: ReactNode;
footer: ReactNode;
}
export function LayoutWrapper({ children, header, footer }: LayoutWrapperProps) {
const location = useLocation();
const checkoutSettings = useCheckoutSettings();
const thankYouSettings = useThankYouSettings();
const { colorMode } = useTheme();
// Determine visibility settings based on current route
let headerVisibility = 'show';
let footerVisibility = 'show';
let backgroundColor = '';
if (location.pathname === '/checkout') {
headerVisibility = checkoutSettings.layout.header_visibility || 'minimal';
footerVisibility = checkoutSettings.layout.footer_visibility || 'minimal';
backgroundColor = checkoutSettings.layout.background_color || '';
} else if (location.pathname.startsWith('/order-received/')) {
headerVisibility = thankYouSettings.headerVisibility || 'show';
footerVisibility = thankYouSettings.footerVisibility || 'minimal';
backgroundColor = thankYouSettings.backgroundColor || '';
}
// Render appropriate header
const renderHeader = () => {
if (headerVisibility === 'hide') return null;
if (headerVisibility === 'minimal') return <MinimalHeader />;
return header;
};
// Render appropriate footer
const renderFooter = () => {
if (footerVisibility === 'hide') return null;
if (footerVisibility === 'minimal') return <MinimalFooter />;
return footer;
};
return (
<div
className="layout-wrapper min-h-screen flex flex-col"
style={backgroundColor && colorMode !== 'dark' ? { backgroundColor } : undefined}
>
{renderHeader()}
<main className="flex-1">
{children}
</main>
{renderFooter()}
</div>
);
}