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
89 lines
3.2 KiB
TypeScript
89 lines
3.2 KiB
TypeScript
import React, { useEffect, useRef } from 'react';
|
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
import { api } from '@/lib/api';
|
|
import { OrdersApi } from '@/lib/api/orders';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import OrderForm from '@/routes/Orders/partials/OrderForm';
|
|
import { getStoreCurrency } from '@/lib/currency';
|
|
import { showErrorToast, showSuccessToast } from '@/lib/errorHandling';
|
|
import { __, sprintf } from '@/lib/i18n';
|
|
import { usePageHeader } from '@/contexts/PageHeaderContext';
|
|
import { Button } from '@/components/ui/button';
|
|
import { useFABConfig } from '@/hooks/useFABConfig';
|
|
|
|
export default function OrdersNew() {
|
|
const nav = useNavigate();
|
|
const qc = useQueryClient();
|
|
const { setPageHeader, clearPageHeader } = usePageHeader();
|
|
const formRef = useRef<HTMLFormElement>(null);
|
|
|
|
// Hide FAB on new order page
|
|
useFABConfig('none');
|
|
|
|
// Countries from Woo (allowed + default + states)
|
|
const countriesQ = useQuery({ queryKey: ['countries'], queryFn: OrdersApi.countries });
|
|
const countriesData = React.useMemo(() => {
|
|
const list = countriesQ.data?.countries ?? [];
|
|
return list.map((c: any) => ({ code: String(c.code), name: String(c.name) }));
|
|
}, [countriesQ.data]);
|
|
|
|
// Live payment & shipping methods
|
|
const payments = useQuery({ queryKey: ['payments'], queryFn: OrdersApi.payments });
|
|
const shippings = useQuery({ queryKey: ['shippings'], queryFn: OrdersApi.shippings });
|
|
|
|
const mutate = useMutation({
|
|
mutationFn: OrdersApi.create,
|
|
onSuccess: (data) => {
|
|
qc.invalidateQueries({ queryKey: ['orders'] });
|
|
showSuccessToast(__('Order created successfully'), sprintf(__('Order #%s has been created'), data.number || data.id));
|
|
nav('/orders');
|
|
},
|
|
onError: (error: any) => {
|
|
showErrorToast(error);
|
|
},
|
|
});
|
|
|
|
// Prefer global store currency injected by PHP
|
|
const { currency: storeCurrency, symbol: storeSymbol } = getStoreCurrency();
|
|
|
|
// Set page header with back button and create button
|
|
useEffect(() => {
|
|
const actions = (
|
|
<div className="flex gap-2">
|
|
<Button size="sm" variant="ghost" onClick={() => nav('/orders')}>
|
|
{__('Back')}
|
|
</Button>
|
|
<Button
|
|
size="sm"
|
|
onClick={() => formRef.current?.requestSubmit()}
|
|
disabled={mutate.isPending}
|
|
>
|
|
{mutate.isPending ? __('Creating...') : __('Create')}
|
|
</Button>
|
|
</div>
|
|
);
|
|
setPageHeader(__('New Order'), actions);
|
|
return () => clearPageHeader();
|
|
}, [mutate.isPending, setPageHeader, clearPageHeader, nav]);
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
|
|
<OrderForm
|
|
mode="create"
|
|
currency={storeCurrency || countriesQ.data?.currency || 'USD'}
|
|
currencySymbol={storeSymbol || countriesQ.data?.currency_symbol}
|
|
countries={countriesData}
|
|
states={countriesQ.data?.states || {}}
|
|
defaultCountry={countriesQ.data?.default_country}
|
|
payments={(payments.data || [])}
|
|
shippings={(shippings.data || [])}
|
|
formRef={formRef}
|
|
hideSubmitButton={true}
|
|
onSubmit={(form) => {
|
|
mutate.mutate(form as any);
|
|
}}
|
|
/>
|
|
</div>
|
|
);
|
|
} |