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
This commit is contained in:
@@ -40,6 +40,7 @@ import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { setQuery, getQuery } from '@/lib/query-params';
|
||||
import { ProductCard } from './components/ProductCard';
|
||||
import { FilterBottomSheet } from './components/FilterBottomSheet';
|
||||
import { Pagination } from '@/components/Pagination';
|
||||
import { SearchBar } from './components/SearchBar';
|
||||
|
||||
const stockStatusStyle: Record<string, string> = {
|
||||
@@ -144,7 +145,7 @@ export default function Products() {
|
||||
const deleteMutation = useMutation({
|
||||
mutationFn: async (ids: number[]) => {
|
||||
const results = await Promise.allSettled(
|
||||
ids.map(id => api.del(`/products/${id}/edit`))
|
||||
ids.map(id => api.del(`/products/${id}`))
|
||||
);
|
||||
const failed = results.filter(r => r.status === 'rejected').length;
|
||||
return { total: ids.length, failed };
|
||||
@@ -481,30 +482,13 @@ export default function Products() {
|
||||
)}
|
||||
|
||||
{/* Pagination */}
|
||||
{data && data.total > perPage && (
|
||||
<div className="flex justify-between items-center pt-4">
|
||||
<div className="text-sm text-muted-foreground">
|
||||
{__('Showing')} {((page - 1) * perPage) + 1} - {Math.min(page * perPage, data.total)} {__('of')} {data.total}
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setPage(p => Math.max(1, p - 1))}
|
||||
disabled={page === 1}
|
||||
>
|
||||
{__('Previous')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setPage(p => p + 1)}
|
||||
disabled={!data || page * perPage >= data.total}
|
||||
>
|
||||
{__('Next')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{data && (
|
||||
<Pagination
|
||||
page={page}
|
||||
perPage={perPage}
|
||||
total={data.total}
|
||||
onPageChange={setPage}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Delete Dialog */}
|
||||
|
||||
Reference in New Issue
Block a user