feat: initialize React admin build pipeline (F2.1-F2.6)
- Add package.json with @wordpress/scripts and React dependencies - Configure webpack for admin bundle output - Create src/admin directory structure (api, components, pages) - Implement API client with nonce handling (ajaxRequest, apiRequest) - Add API methods for orders, customers, products, forms, coupons, licenses - Create React App component with page routing - Add placeholder page components for all admin sections - Create ReactAdmin PHP class to manage asset enqueuing - Register ReactAdmin singleton in main plugin file - Bump version to 2.0.0 Build: Run 'npm install && npm run build' to generate assets
This commit is contained in:
128
src/admin/api/client.js
Normal file
128
src/admin/api/client.js
Normal file
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* API client for Formipay admin interface
|
||||
* Handles nonce-secured requests to WordPress admin-ajax.php
|
||||
*/
|
||||
|
||||
export const API_BASE = window.formipayAdmin?.ajaxUrl || '/wp-admin/admin-ajax.php';
|
||||
export const NONCE = window.formipayAdmin?.nonce || '';
|
||||
export const REST_BASE = window.formipayAdmin?.restUrl || '/wp-json/formipay/v1';
|
||||
|
||||
/**
|
||||
* Generic AJAX request handler with nonce
|
||||
*/
|
||||
export async function ajaxRequest(action, data = {}) {
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append('action', action);
|
||||
formData.append('_wpnonce', NONCE);
|
||||
|
||||
Object.keys(data).forEach(key => {
|
||||
if (typeof data[key] === 'object') {
|
||||
formData.append(key, JSON.stringify(data[key]));
|
||||
} else {
|
||||
formData.append(key, data[key]);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await fetch(API_BASE, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
body: formData,
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (!response.ok || result.success === false) {
|
||||
throw new Error(result.message || 'Request failed');
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error(`AJAX error [${action}]:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API request handler
|
||||
*/
|
||||
export async function apiRequest(endpoint, options = {}) {
|
||||
const url = `${REST_BASE}${endpoint}`;
|
||||
|
||||
const defaultOptions = {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-WP-Nonce': NONCE,
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch(url, { ...defaultOptions, ...options });
|
||||
const result = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(result.message || 'API request failed');
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error(`API error [${endpoint}]:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Orders API
|
||||
*/
|
||||
export const ordersApi = {
|
||||
list: (params = {}) => ajaxRequest('formipay_tabledata_orders', params),
|
||||
get: (orderId) => ajaxRequest('formipay_get_order', { order_id: orderId }),
|
||||
updateStatus: (orderId, status) => ajaxRequest('formipay_update_order_status', {
|
||||
order_id: orderId,
|
||||
status,
|
||||
}),
|
||||
delete: (orderIds) => ajaxRequest('formipay_bulk_delete_order', { ids: orderIds }),
|
||||
};
|
||||
|
||||
/**
|
||||
* Customers API
|
||||
*/
|
||||
export const customersApi = {
|
||||
list: (params = {}) => ajaxRequest('formipay_tabledata_customers', params),
|
||||
get: (customerId) => ajaxRequest('formipay_get_customer', { customer_id: customerId }),
|
||||
};
|
||||
|
||||
/**
|
||||
* Products API
|
||||
*/
|
||||
export const productsApi = {
|
||||
list: (params = {}) => ajaxRequest('formipay_tabledata_products', params),
|
||||
get: (productId) => ajaxRequest('formipay_get_product', { post_id: productId }),
|
||||
getVariables: (productId) => ajaxRequest('get_product_variables', { post_id: productId }),
|
||||
};
|
||||
|
||||
/**
|
||||
* Forms API
|
||||
*/
|
||||
export const formsApi = {
|
||||
list: (params = {}) => ajaxRequest('formipay_tabledata_forms', params),
|
||||
get: (formId) => ajaxRequest('formipay_get_form', { post_id: formId }),
|
||||
};
|
||||
|
||||
/**
|
||||
* Coupons API
|
||||
*/
|
||||
export const couponsApi = {
|
||||
list: (params = {}) => ajaxRequest('formipay_tabledata_coupons', params),
|
||||
check: (code, formId) => ajaxRequest('formipay_check_coupon', { code, form_id: formId }),
|
||||
};
|
||||
|
||||
/**
|
||||
* License API
|
||||
*/
|
||||
export const licenseApi = {
|
||||
verify: (key) => ajaxRequest('formipay_verify_license', { license_key: key }),
|
||||
activate: (key) => ajaxRequest('formipay_activate_license', { license_key: key }),
|
||||
deactivate: (key) => ajaxRequest('formipay_deactivate_license', { license_key: key }),
|
||||
};
|
||||
39
src/admin/components/App.js
Normal file
39
src/admin/components/App.js
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* App Component - Main admin application shell
|
||||
*/
|
||||
|
||||
import OrdersPage from '../pages/Orders';
|
||||
import CustomersPage from '../pages/Customers';
|
||||
import ProductsPage from '../pages/Products';
|
||||
import FormsPage from '../pages/Forms';
|
||||
import CouponsPage from '../pages/Coupons';
|
||||
import AccessPage from '../pages/Access';
|
||||
import LicensesPage from '../pages/Licenses';
|
||||
|
||||
const pageComponents = {
|
||||
orders: OrdersPage,
|
||||
customers: CustomersPage,
|
||||
products: ProductsPage,
|
||||
forms: FormsPage,
|
||||
coupons: CouponsPage,
|
||||
access: AccessPage,
|
||||
licenses: LicensesPage,
|
||||
};
|
||||
|
||||
export default function App({ page, initialData }) {
|
||||
const PageComponent = pageComponents[page];
|
||||
|
||||
if (!PageComponent) {
|
||||
return (
|
||||
<div className="formipay-error">
|
||||
<p>Unknown page: {page}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="formipay-admin-wrap">
|
||||
<PageComponent initialData={initialData} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
28
src/admin/index.js
Normal file
28
src/admin/index.js
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Formipay Admin - React Application Entry Point
|
||||
*/
|
||||
|
||||
import { render } from '@wordpress/element';
|
||||
import App from './components/App';
|
||||
|
||||
// Mount the React app to all available mount points
|
||||
const mountApps = () => {
|
||||
const mountPoints = document.querySelectorAll('[data-formipay-mount]');
|
||||
|
||||
mountPoints.forEach((mountPoint) => {
|
||||
const page = mountPoint.dataset.formipayMount;
|
||||
const initialData = window.formipayAdmin?.[page] || {};
|
||||
|
||||
render(
|
||||
<App page={page} initialData={initialData} />,
|
||||
mountPoint
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
// Initialize when DOM is ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', mountApps);
|
||||
} else {
|
||||
mountApps();
|
||||
}
|
||||
14
src/admin/pages/Access.js
Normal file
14
src/admin/pages/Access.js
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Access Page - Placeholder
|
||||
*/
|
||||
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
export default function AccessPage({ initialData }) {
|
||||
return (
|
||||
<div className="formipay-page-formipay-page">
|
||||
<h1>{ __('Access', 'formipay') }</h1>
|
||||
<p>{ __('Page content coming soon...', 'formipay') }</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
14
src/admin/pages/Coupons.js
Normal file
14
src/admin/pages/Coupons.js
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Coupons Page - Placeholder
|
||||
*/
|
||||
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
export default function CouponsPage({ initialData }) {
|
||||
return (
|
||||
<div className="formipay-page-formipay-page">
|
||||
<h1>{ __('Coupons', 'formipay') }</h1>
|
||||
<p>{ __('Page content coming soon...', 'formipay') }</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
14
src/admin/pages/Customers.js
Normal file
14
src/admin/pages/Customers.js
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Customers Page - Placeholder
|
||||
*/
|
||||
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
export default function CustomersPage({ initialData }) {
|
||||
return (
|
||||
<div className="formipay-page-formipay-page">
|
||||
<h1>{ __('Customers', 'formipay') }</h1>
|
||||
<p>{ __('Page content coming soon...', 'formipay') }</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
14
src/admin/pages/Forms.js
Normal file
14
src/admin/pages/Forms.js
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Forms Page - Placeholder
|
||||
*/
|
||||
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
export default function FormsPage({ initialData }) {
|
||||
return (
|
||||
<div className="formipay-page-formipay-page">
|
||||
<h1>{ __('Forms', 'formipay') }</h1>
|
||||
<p>{ __('Page content coming soon...', 'formipay') }</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
14
src/admin/pages/Licenses.js
Normal file
14
src/admin/pages/Licenses.js
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Licenses Page - Placeholder
|
||||
*/
|
||||
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
export default function LicensesPage({ initialData }) {
|
||||
return (
|
||||
<div className="formipay-page-formipay-page">
|
||||
<h1>{ __('Licenses', 'formipay') }</h1>
|
||||
<p>{ __('Page content coming soon...', 'formipay') }</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
14
src/admin/pages/Orders.js
Normal file
14
src/admin/pages/Orders.js
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Orders Page - Placeholder
|
||||
*/
|
||||
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
export default function OrdersPage({ initialData }) {
|
||||
return (
|
||||
<div className="formipay-page-formipay-page">
|
||||
<h1>{ __('Orders', 'formipay') }</h1>
|
||||
<p>{ __('Page content coming soon...', 'formipay') }</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
14
src/admin/pages/Products.js
Normal file
14
src/admin/pages/Products.js
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Products Page - Placeholder
|
||||
*/
|
||||
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
export default function ProductsPage({ initialData }) {
|
||||
return (
|
||||
<div className="formipay-page-formipay-page">
|
||||
<h1>{ __('Products', 'formipay') }</h1>
|
||||
<p>{ __('Page content coming soon...', 'formipay') }</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user