feat: migrate Coupons, Access, Customers, Licenses pages to full DataTable
Update all admin listing pages to use the full-featured DataTable component: Coupons page: - All columns (ID, code, type, amount, usages, date limit, status) - Status filter (All, Active, Inactive) - Inline actions and bulk delete - Add New modal Access page: - All columns (ID, title, product, status, date) - Status filter (All, Published, Draft) - Inline actions and bulk delete - Add New modal Customers page: - All columns (ID, name, email, phone, total orders, date) - Read-only (no selection or inline actions) - Search and pagination Licenses page: - All columns (ID, license key, product, order, email, status) - Status labels (Active, Inactive, Expired) - Read-only (no selection or inline actions) - Search and pagination Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1 +1 @@
|
|||||||
<?php return array('dependencies' => array('react', 'wp-components', 'wp-element', 'wp-i18n', 'wp-icons/build/arrow-left', 'wp-icons/build/bell', 'wp-icons/build/eye-closed', 'wp-icons/build/eye-opened', 'wp-icons/build/list', 'wp-icons/build/message', 'wp-icons/build/minus', 'wp-icons/build/plus', 'wp-icons/build/trash', 'wp-icons/build/visible'), 'version' => '164b137f81dde0451242');
|
<?php return array('dependencies' => array('react', 'wp-components', 'wp-element', 'wp-i18n', 'wp-icons/build/arrow-left', 'wp-icons/build/bell', 'wp-icons/build/eye-closed', 'wp-icons/build/eye-opened', 'wp-icons/build/list', 'wp-icons/build/message', 'wp-icons/build/minus', 'wp-icons/build/plus', 'wp-icons/build/trash', 'wp-icons/build/visible'), 'version' => 'ea8676d843297ad5e987');
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
BIN
node_modules/.cache/babel-loader/0ad392e4a022a30eb82054a0147079d607e5a8c6e67cbdc541cb3eac6504933d.json.gz
generated
vendored
Normal file
BIN
node_modules/.cache/babel-loader/0ad392e4a022a30eb82054a0147079d607e5a8c6e67cbdc541cb3eac6504933d.json.gz
generated
vendored
Normal file
Binary file not shown.
BIN
node_modules/.cache/babel-loader/0b7882e19c8bdddd6808bd0b54fcd61374b225048ab0d451abbe333d09fab460.json.gz
generated
vendored
Normal file
BIN
node_modules/.cache/babel-loader/0b7882e19c8bdddd6808bd0b54fcd61374b225048ab0d451abbe333d09fab460.json.gz
generated
vendored
Normal file
Binary file not shown.
BIN
node_modules/.cache/babel-loader/5a27f19ce1f1ca46b061d1feeb1df31e799ee018b9105f438b90f6d82a19ca75.json.gz
generated
vendored
Normal file
BIN
node_modules/.cache/babel-loader/5a27f19ce1f1ca46b061d1feeb1df31e799ee018b9105f438b90f6d82a19ca75.json.gz
generated
vendored
Normal file
Binary file not shown.
BIN
node_modules/.cache/babel-loader/6c8d197c6aef81da583f60809805cf20d20be3ba21f3d898ef1239e5e2e77ee8.json.gz
generated
vendored
Normal file
BIN
node_modules/.cache/babel-loader/6c8d197c6aef81da583f60809805cf20d20be3ba21f3d898ef1239e5e2e77ee8.json.gz
generated
vendored
Normal file
Binary file not shown.
BIN
node_modules/.cache/babel-loader/757e005cd7d8064797f4acf3ee23694f9833484004d3ebd442de956e40f29c90.json.gz
generated
vendored
Normal file
BIN
node_modules/.cache/babel-loader/757e005cd7d8064797f4acf3ee23694f9833484004d3ebd442de956e40f29c90.json.gz
generated
vendored
Normal file
Binary file not shown.
BIN
node_modules/.cache/babel-loader/81bc671315573b551e0bfafda03cf846121966f836ee2a2fe60307338dc66c9a.json.gz
generated
vendored
Normal file
BIN
node_modules/.cache/babel-loader/81bc671315573b551e0bfafda03cf846121966f836ee2a2fe60307338dc66c9a.json.gz
generated
vendored
Normal file
Binary file not shown.
BIN
node_modules/.cache/babel-loader/8ff0b9507ffef823cde6831d73d204ad9f7cebce4fc4fb80b7669ec46448a356.json.gz
generated
vendored
Normal file
BIN
node_modules/.cache/babel-loader/8ff0b9507ffef823cde6831d73d204ad9f7cebce4fc4fb80b7669ec46448a356.json.gz
generated
vendored
Normal file
Binary file not shown.
BIN
node_modules/.cache/babel-loader/bf45c8260791bbd05d8126d6c8542df5948d308c8405ee75e2830902d6ede1e6.json.gz
generated
vendored
Normal file
BIN
node_modules/.cache/babel-loader/bf45c8260791bbd05d8126d6c8542df5948d308c8405ee75e2830902d6ede1e6.json.gz
generated
vendored
Normal file
Binary file not shown.
BIN
node_modules/.cache/babel-loader/d91b2b36e48ca7c0ebb556e15f0c5897c5192cc26b2eba9a88137a56f5241cad.json.gz
generated
vendored
Normal file
BIN
node_modules/.cache/babel-loader/d91b2b36e48ca7c0ebb556e15f0c5897c5192cc26b2eba9a88137a56f5241cad.json.gz
generated
vendored
Normal file
Binary file not shown.
@@ -1,43 +1,17 @@
|
|||||||
/**
|
/**
|
||||||
* Access Page - Access items management
|
* Access Page - Access items management with full table features
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { useState, useCallback, useEffect } from '@wordpress/element';
|
|
||||||
import { Button } from '@wordpress/components';
|
|
||||||
import { accessApi } from '../api/client';
|
|
||||||
import DataTable from '../components/shared/DataTable';
|
import DataTable from '../components/shared/DataTable';
|
||||||
import './AdminPages.css';
|
import './AdminPages.css';
|
||||||
|
|
||||||
export default function AccessPage({ initialData }) {
|
export default function AccessPage() {
|
||||||
const [items, setItems] = useState([]);
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
|
|
||||||
const loadItems = useCallback(() => {
|
|
||||||
setLoading(true);
|
|
||||||
accessApi.list()
|
|
||||||
.then(result => {
|
|
||||||
// Handle both WordPress format and direct format
|
|
||||||
const items = result.data?.results || result.results || result.data || [];
|
|
||||||
setItems(items);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Load access items error:', error);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setLoading(false);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
loadItems();
|
|
||||||
}, [loadItems]);
|
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
key: 'id',
|
key: 'ID',
|
||||||
label: __('ID', 'formipay'),
|
label: __('ID', 'formipay'),
|
||||||
render: (row) => <strong>#{row.id}</strong>
|
render: (row) => <strong>#{row.ID}</strong>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'title',
|
key: 'title',
|
||||||
@@ -57,7 +31,7 @@ export default function AccessPage({ initialData }) {
|
|||||||
draft: __('Draft', 'formipay'),
|
draft: __('Draft', 'formipay'),
|
||||||
}[status] || status;
|
}[status] || status;
|
||||||
return (
|
return (
|
||||||
<span className={`status-badge status-${status}`}>
|
<span className={`status-label ${status}`}>
|
||||||
{statusLabel}
|
{statusLabel}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
@@ -78,15 +52,33 @@ export default function AccessPage({ initialData }) {
|
|||||||
<div className="formipay-page-access">
|
<div className="formipay-page-access">
|
||||||
<div className="formipay-page-header">
|
<div className="formipay-page-header">
|
||||||
<h1>{ __('Access Items', 'formipay') }</h1>
|
<h1>{ __('Access Items', 'formipay') }</h1>
|
||||||
<Button variant="primary" href={window.formipayAdmin?.siteUrl + '/wp-admin/post-new.php?post_type=formipay-access'}>
|
|
||||||
{ __('+ Add New Item', 'formipay') }
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={items}
|
ajaxUrl={window.formipayAdmin?.ajaxUrl || '/wp-admin/admin-ajax.php'}
|
||||||
loading={loading}
|
nonce={window.formipayAdmin?.nonce || ''}
|
||||||
|
tableAction="formipay-tabledata-access-items"
|
||||||
|
deleteAction="formipay-delete-access-item"
|
||||||
|
duplicateAction="formipay-duplicate-access-item"
|
||||||
|
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 Item', 'formipay'),
|
||||||
|
action: 'formipay-create-access-item-post',
|
||||||
|
},
|
||||||
|
bulkDelete: {
|
||||||
|
action: 'formipay-bulk-delete-access-item',
|
||||||
|
},
|
||||||
|
inline: true,
|
||||||
|
}}
|
||||||
emptyMessage={__('No access items found', 'formipay')}
|
emptyMessage={__('No access items found', 'formipay')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,43 +1,17 @@
|
|||||||
/**
|
/**
|
||||||
* Coupons Page - Coupon management
|
* Coupons Page - Coupon management with full table features
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { useState, useCallback, useEffect } from '@wordpress/element';
|
|
||||||
import { Button } from '@wordpress/components';
|
|
||||||
import { couponsApi } from '../api/client';
|
|
||||||
import DataTable from '../components/shared/DataTable';
|
import DataTable from '../components/shared/DataTable';
|
||||||
import './AdminPages.css';
|
import './AdminPages.css';
|
||||||
|
|
||||||
export default function CouponsPage({ initialData }) {
|
export default function CouponsPage() {
|
||||||
const [coupons, setCoupons] = useState([]);
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
|
|
||||||
const loadCoupons = useCallback(() => {
|
|
||||||
setLoading(true);
|
|
||||||
couponsApi.list()
|
|
||||||
.then(result => {
|
|
||||||
// Handle both WordPress format and direct format
|
|
||||||
const coupons = result.data?.results || result.results || result.data || [];
|
|
||||||
setCoupons(coupons);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Load coupons error:', error);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setLoading(false);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
loadCoupons();
|
|
||||||
}, [loadCoupons]);
|
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
key: 'id',
|
key: 'ID',
|
||||||
label: __('ID', 'formipay'),
|
label: __('ID', 'formipay'),
|
||||||
render: (row) => <strong>#{row.id}</strong>
|
render: (row) => <strong>#{row.ID}</strong>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'code',
|
key: 'code',
|
||||||
@@ -55,18 +29,36 @@ export default function CouponsPage({ initialData }) {
|
|||||||
{
|
{
|
||||||
key: 'amount',
|
key: 'amount',
|
||||||
label: __('Amount', 'formipay'),
|
label: __('Amount', 'formipay'),
|
||||||
|
render: (row) => {
|
||||||
|
// Multi-currency display would go here
|
||||||
|
return row.amount || '-';
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'usages',
|
key: 'usages',
|
||||||
label: __('Usages', 'formipay'),
|
label: __('Usages', 'formipay'),
|
||||||
render: (row) => row.usage_count || row.usages || 0
|
render: (row) => row.usage_count || row.usages || 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'date_limit',
|
||||||
|
label: __('Date Limit', 'formipay'),
|
||||||
|
render: (row) => {
|
||||||
|
const dateLimit = row.date_limit;
|
||||||
|
if (!dateLimit || dateLimit === 'none') return __('Unlimited', 'formipay');
|
||||||
|
return new Date(dateLimit).toLocaleDateString();
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'status',
|
key: 'status',
|
||||||
label: __('Status', 'formipay'),
|
label: __('Status', 'formipay'),
|
||||||
render: (row) => {
|
render: (row) => {
|
||||||
const status = row.post_status || row.status || 'unknown';
|
const status = row.post_status || row.status || 'unknown';
|
||||||
return status === 'publish' ? __('Active', 'formipay') : __('Inactive', 'formipay');
|
const isActive = row.active === 'on' || status === 'publish';
|
||||||
|
return (
|
||||||
|
<span className={`status-label ${isActive ? 'publish' : 'draft'}`}>
|
||||||
|
{isActive ? __('Active', 'formipay') : __('Inactive', 'formipay')}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -75,26 +67,33 @@ export default function CouponsPage({ initialData }) {
|
|||||||
<div className="formipay-page-coupons">
|
<div className="formipay-page-coupons">
|
||||||
<div className="formipay-page-header">
|
<div className="formipay-page-header">
|
||||||
<h1>{ __('Coupons', 'formipay') }</h1>
|
<h1>{ __('Coupons', 'formipay') }</h1>
|
||||||
<Button
|
|
||||||
variant="primary"
|
|
||||||
onClick={() => {
|
|
||||||
// Open create modal - for now just prompt
|
|
||||||
const code = prompt(__('Enter coupon code:', 'formipay'));
|
|
||||||
if (code) {
|
|
||||||
couponsApi.create({ code })
|
|
||||||
.then(() => loadCoupons())
|
|
||||||
.catch(console.error);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{ __('+ Add New Coupon', 'formipay') }
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={coupons}
|
ajaxUrl={window.formipayAdmin?.ajaxUrl || '/wp-admin/admin-ajax.php'}
|
||||||
loading={loading}
|
nonce={window.formipayAdmin?.nonce || ''}
|
||||||
|
tableAction="formipay-tabledata-coupons"
|
||||||
|
deleteAction="formipay-delete-coupon"
|
||||||
|
duplicateAction="formipay-duplicate-coupon"
|
||||||
|
filterOptions={{
|
||||||
|
key: 'status',
|
||||||
|
options: [
|
||||||
|
{ value: 'all', label: __('All', 'formipay') },
|
||||||
|
{ value: 'active', label: __('Active', 'formipay') },
|
||||||
|
{ value: 'inactive', label: __('Inactive', 'formipay') },
|
||||||
|
]
|
||||||
|
}}
|
||||||
|
actions={{
|
||||||
|
addNew: {
|
||||||
|
label: __('+ Add New Coupon', 'formipay'),
|
||||||
|
action: 'formipay-create-coupon-post',
|
||||||
|
},
|
||||||
|
bulkDelete: {
|
||||||
|
action: 'formipay-bulk-delete-coupon',
|
||||||
|
},
|
||||||
|
inline: true,
|
||||||
|
}}
|
||||||
emptyMessage={__('No coupons found', 'formipay')}
|
emptyMessage={__('No coupons found', 'formipay')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,37 +1,12 @@
|
|||||||
/**
|
/**
|
||||||
* Customers Page - Customer list
|
* Customers Page - Customer list with full table features
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { useState, useCallback, useEffect } from '@wordpress/element';
|
|
||||||
import { customersApi } from '../api/client';
|
|
||||||
import DataTable from '../components/shared/DataTable';
|
import DataTable from '../components/shared/DataTable';
|
||||||
import './AdminPages.css';
|
import './AdminPages.css';
|
||||||
|
|
||||||
export default function CustomersPage({ initialData }) {
|
export default function CustomersPage() {
|
||||||
const [customers, setCustomers] = useState([]);
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
|
|
||||||
const loadCustomers = useCallback(() => {
|
|
||||||
setLoading(true);
|
|
||||||
customersApi.list()
|
|
||||||
.then(result => {
|
|
||||||
// Handle both WordPress format and direct format
|
|
||||||
const customers = result.data?.results || result.results || result.data || [];
|
|
||||||
setCustomers(customers);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Load customers error:', error);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setLoading(false);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
loadCustomers();
|
|
||||||
}, [loadCustomers]);
|
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
key: 'id',
|
key: 'id',
|
||||||
@@ -76,8 +51,11 @@ export default function CustomersPage({ initialData }) {
|
|||||||
|
|
||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={customers}
|
ajaxUrl={window.formipayAdmin?.ajaxUrl || '/wp-admin/admin-ajax.php'}
|
||||||
loading={loading}
|
nonce={window.formipayAdmin?.nonce || ''}
|
||||||
|
tableAction="formipay-tabledata-customers"
|
||||||
|
selectable={false}
|
||||||
|
inline={false}
|
||||||
emptyMessage={__('No customers found', 'formipay')}
|
emptyMessage={__('No customers found', 'formipay')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,46 +1,12 @@
|
|||||||
/**
|
/**
|
||||||
* Licenses Page - License management
|
* Licenses Page - License management with full table features
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { useState, useCallback, useEffect } from '@wordpress/element';
|
|
||||||
import DataTable from '../components/shared/DataTable';
|
import DataTable from '../components/shared/DataTable';
|
||||||
import './AdminPages.css';
|
import './AdminPages.css';
|
||||||
|
|
||||||
export default function LicensesPage({ initialData }) {
|
export default function LicensesPage() {
|
||||||
const [licenses, setLicenses] = useState([]);
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
|
|
||||||
const loadLicenses = useCallback(() => {
|
|
||||||
setLoading(true);
|
|
||||||
// Licenses API might use a different endpoint
|
|
||||||
fetch(window.formipayAdmin?.ajaxUrl || '/wp-admin/admin-ajax.php', {
|
|
||||||
method: 'POST',
|
|
||||||
credentials: 'same-origin',
|
|
||||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
||||||
body: new URLSearchParams({
|
|
||||||
action: 'formipay-tabledata-licenses',
|
|
||||||
_wpnonce: window.formipayAdmin?.nonce || '',
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(result => {
|
|
||||||
// Handle both WordPress format and direct format
|
|
||||||
const licenses = result.data?.results || result.results || result.data || [];
|
|
||||||
setLicenses(licenses);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Load licenses error:', error);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setLoading(false);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
loadLicenses();
|
|
||||||
}, [loadLicenses]);
|
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
key: 'id',
|
key: 'id',
|
||||||
@@ -76,25 +42,31 @@ export default function LicensesPage({ initialData }) {
|
|||||||
inactive: __('Inactive', 'formipay'),
|
inactive: __('Inactive', 'formipay'),
|
||||||
expired: __('Expired', 'formipay'),
|
expired: __('Expired', 'formipay'),
|
||||||
};
|
};
|
||||||
return statusLabels[status] || status;
|
const statusClass = status === 'active' ? 'publish' : status === 'expired' ? 'pending' : 'draft';
|
||||||
|
return (
|
||||||
|
<span className={`status-label ${statusClass}`}>
|
||||||
|
{statusLabels[status] || status}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="formipay-page-licenses">
|
<div className="formipay-page-licenses">
|
||||||
<div className="formipay-page-licenses">
|
<div className="formipay-page-header">
|
||||||
<div className="formipay-page-header">
|
<h1>{ __('Licenses', 'formipay') }</h1>
|
||||||
<h1>{ __('Licenses', 'formipay') }</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<DataTable
|
|
||||||
columns={columns}
|
|
||||||
data={licenses}
|
|
||||||
loading={loading}
|
|
||||||
emptyMessage={__('No licenses found', 'formipay')}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<DataTable
|
||||||
|
columns={columns}
|
||||||
|
ajaxUrl={window.formipayAdmin?.ajaxUrl || '/wp-admin/admin-ajax.php'}
|
||||||
|
nonce={window.formipayAdmin?.nonce || ''}
|
||||||
|
tableAction="formipay-tabledata-licenses"
|
||||||
|
selectable={false}
|
||||||
|
inline={false}
|
||||||
|
emptyMessage={__('No licenses found', 'formipay')}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user