feat: add WPCFTO-inspired design system and React navigation

- Add WPCFTO-inspired design system CSS (colors, spacing, typography)
- Add reusable React components matching WPCFTO visual language
- Implement client-side navigation with hash-based routing
- Add NavigationMenu component for on-page navigation (no SSR)
- Update WordPress submenu highlighting based on current React page
- Add Refresh button to DataTable toolbar
- Fix icon imports in VariationPricingTable
- Remove page headers from all table pages (consistent UX)
- Convert Orders page to use DataTable for consistency

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
dwindown
2026-04-19 05:58:44 +07:00
parent 96ea79600a
commit 057611ef40
19 changed files with 2158 additions and 214 deletions

View File

@@ -4,12 +4,60 @@
import { __ } from '@wordpress/i18n';
import DataTable from '../components/shared/DataTable';
import './AdminPages.css';
// SweetAlert2 is loaded via WordPress (global scope)
const Swal = window.Swal;
export default function FormsPage() {
const ajaxUrl = window.formipayAdmin?.ajaxUrl || '/wp-admin/admin-ajax.php';
const nonce = window.formipayAdmin?.nonce || '';
const handleDelete = async (id) => {
const result = await Swal.fire({
icon: 'info',
html: __('Do you want to delete this item?', 'formipay'),
showCancelButton: true,
confirmButtonText: __('Delete Permanently', 'formipay'),
cancelButtonText: __('Cancel', 'formipay'),
});
if (result.isConfirmed) {
await fetch(`${ajaxUrl}?action=formipay-delete-form`, {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
id,
_wpnonce: nonce,
}),
});
window.location.reload();
}
};
const handleDuplicate = async (id) => {
const result = await Swal.fire({
icon: 'info',
html: __('Do you want to duplicate this item?', 'formipay'),
showCancelButton: true,
confirmButtonText: __('Confirm', 'formipay'),
cancelButtonText: __('Cancel', 'formipay'),
});
if (result.isConfirmed) {
await fetch(`${ajaxUrl}?action=formipay-duplicate-form`, {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
id,
_wpnonce: nonce,
}),
});
window.location.reload();
}
};
const columns = [
{
key: 'ID',
@@ -20,9 +68,36 @@ export default function FormsPage() {
key: 'title',
label: __('Title', 'formipay'),
render: (row) => (
<a href={`${window.formipayAdmin?.siteUrl || ''}/wp-admin/post.php?post=${row.ID}&action=edit`}>
<strong>{row.title}</strong>
</a>
<>
<a href={`${window.formipayAdmin?.siteUrl || ''}/wp-admin/post.php?post=${row.ID}&action=edit`}>
<strong>{row.title}</strong>
</a>
<span className="row-actions">
<a href={`${window.formipayAdmin?.siteUrl || ''}/wp-admin/post.php?post=${row.ID}&action=edit`}>{__('edit', 'formipay')}</a>
{' | '}
<button
className="button-link delete"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
handleDelete(row.ID);
}}
>
{__('delete', 'formipay')}
</button>
{' | '}
<button
className="button-link duplicate"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
handleDuplicate(row.ID);
}}
>
{__('duplicate', 'formipay')}
</button>
</span>
</>
)
},
{
@@ -75,7 +150,7 @@ export default function FormsPage() {
const text = e.currentTarget.dataset.copy;
navigator.clipboard.writeText(text).then(() => {
const originalHTML = e.currentTarget.innerHTML;
e.currentTarget.innerHTML = 'Copied';
e.currentTarget.innerHTML = '[Copied]';
setTimeout(() => {
e.currentTarget.innerHTML = originalHTML;
}, 2000);
@@ -92,7 +167,7 @@ export default function FormsPage() {
});
}}
>
📋 {__('Copy', 'formipay')}
{__('Copy', 'formipay')}
</button>
</>
)
@@ -100,38 +175,32 @@ export default function FormsPage() {
];
return (
<div className="formipay-page-forms">
<div className="formipay-page-header">
<h1>{ __('Forms', 'formipay') }</h1>
</div>
<DataTable
columns={columns}
ajaxUrl={window.formipayAdmin?.ajaxUrl || '/wp-admin/admin-ajax.php'}
nonce={window.formipayAdmin?.nonce || ''}
tableAction="formipay-tabledata-forms"
deleteAction="formipay-delete-form"
duplicateAction="formipay-duplicate-form"
filterOptions={{
key: 'post_status',
options: [
{ value: 'all', label: __('All', 'formipay') },
{ value: 'publish', label: __('Published', 'formipay') },
{ value: 'draft', label: __('Draft', 'formipay') },
]
}}
actions={{
addNew: {
label: __('+ Add New Form', 'formipay'),
action: 'formipay-create-form-post',
},
bulkDelete: {
action: 'formipay-bulk-delete-form',
},
inline: true,
}}
emptyMessage={__('No forms found', 'formipay')}
/>
</div>
<DataTable
columns={columns}
ajaxUrl={window.formipayAdmin?.ajaxUrl || '/wp-admin/admin-ajax.php'}
nonce={window.formipayAdmin?.nonce || ''}
tableAction="formipay-tabledata-forms"
deleteAction="formipay-delete-form"
duplicateAction="formipay-duplicate-form"
filterOptions={{
key: 'post_status',
options: [
{ value: 'all', label: __('All', 'formipay') },
{ value: 'publish', label: __('Published', 'formipay') },
{ value: 'draft', label: __('Draft', 'formipay') },
]
}}
actions={{
addNew: {
label: __('+ Add New Form', 'formipay'),
action: 'formipay-create-form-post',
},
bulkDelete: {
action: 'formipay-bulk-delete-form',
},
inline: true,
}}
emptyMessage={__('No forms found', 'formipay')}
/>
);
}