- 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>
207 lines
7.6 KiB
JavaScript
207 lines
7.6 KiB
JavaScript
/**
|
|
* Forms Page - Form management with full table features
|
|
*/
|
|
|
|
import { __ } from '@wordpress/i18n';
|
|
import DataTable from '../components/shared/DataTable';
|
|
|
|
// 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',
|
|
label: __('ID', 'formipay'),
|
|
render: (row) => <strong>#{row.ID}</strong>
|
|
},
|
|
{
|
|
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>
|
|
<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>
|
|
</>
|
|
)
|
|
},
|
|
{
|
|
key: 'date',
|
|
label: __('Date', 'formipay'),
|
|
render: (row) => {
|
|
const date = new Date(row.date);
|
|
return (
|
|
<span style={{ whiteSpace: 'nowrap' }}>
|
|
{date.toLocaleDateString()}
|
|
<br />
|
|
<span style={{ fontSize: 'smaller', color: '#646970' }}>
|
|
{date.toLocaleTimeString()}
|
|
</span>
|
|
</span>
|
|
);
|
|
}
|
|
},
|
|
{
|
|
key: 'status',
|
|
label: __('Status', 'formipay'),
|
|
render: (row) => {
|
|
const status = row.post_status || row.status || 'unknown';
|
|
const statusLabels = {
|
|
publish: __('Published', 'formipay'),
|
|
draft: __('Draft', 'formipay'),
|
|
pending: __('Pending', 'formipay'),
|
|
};
|
|
return (
|
|
<span className={`status-label ${status}`}>
|
|
{statusLabels[status] || status}
|
|
</span>
|
|
);
|
|
}
|
|
},
|
|
{
|
|
key: 'shortcode',
|
|
label: __('Shortcode', 'formipay'),
|
|
render: (row) => (
|
|
<>
|
|
<input
|
|
className="formipay-form-shortcode"
|
|
value={`[formipay form=${row.ID}]`}
|
|
disabled
|
|
/>
|
|
<button
|
|
className="copy-shortcode"
|
|
data-copy={`[formipay form=${row.ID}]`}
|
|
onClick={(e) => {
|
|
const text = e.currentTarget.dataset.copy;
|
|
navigator.clipboard.writeText(text).then(() => {
|
|
const originalHTML = e.currentTarget.innerHTML;
|
|
e.currentTarget.innerHTML = '[Copied]';
|
|
setTimeout(() => {
|
|
e.currentTarget.innerHTML = originalHTML;
|
|
}, 2000);
|
|
|
|
Swal.fire({
|
|
icon: 'success',
|
|
title: __('Shortcode copied!', 'formipay'),
|
|
toast: true,
|
|
position: 'top-end',
|
|
showConfirmButton: false,
|
|
timer: 3000,
|
|
timerProgressBar: true,
|
|
});
|
|
});
|
|
}}
|
|
>
|
|
{__('Copy', 'formipay')}
|
|
</button>
|
|
</>
|
|
)
|
|
},
|
|
];
|
|
|
|
return (
|
|
<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')}
|
|
/>
|
|
);
|
|
}
|