Files
WooNooW/admin-spa/src/components/Pagination.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

49 lines
1.4 KiB
TypeScript

import React from 'react';
import { Button } from '@/components/ui/button';
import { __ } from '@/lib/i18n';
interface PaginationProps {
page: number;
perPage: number;
total: number;
onPageChange: (newPage: number) => void;
className?: string;
}
export function Pagination({ page, perPage, total, onPageChange, className = '' }: PaginationProps) {
if (total <= perPage) return null;
const startItem = ((page - 1) * perPage) + 1;
const endItem = Math.min(page * perPage, total);
const totalPages = Math.ceil(total / perPage);
return (
<div className={`flex flex-col sm:flex-row justify-between items-center gap-4 pt-4 ${className}`}>
<div className="text-sm text-muted-foreground order-2 sm:order-1">
{__('Showing')} {startItem} - {endItem} {__('of')} {total}
</div>
<div className="flex gap-2 order-1 sm:order-2">
<Button
variant="outline"
size="sm"
onClick={() => onPageChange(Math.max(1, page - 1))}
disabled={page <= 1}
>
{__('Previous')}
</Button>
<div className="flex items-center sm:hidden text-sm opacity-80 px-2">
{__('Page')} {page} {__('of')} {totalPages}
</div>
<Button
variant="outline"
size="sm"
onClick={() => onPageChange(page + 1)}
disabled={page >= totalPages}
>
{__('Next')}
</Button>
</div>
</div>
);
}