Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 119 additions & 32 deletions assets/src/js/react/frontend/listing-owner-dashboard/app.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
/**
* WordPress dependencies
*/
import apiFetch from '@wordpress/api-fetch';
import { Button, DropdownMenu, Modal } from '@wordpress/components';
import { useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { moreVertical } from '@wordpress/icons';
import { DropdownMenu } from '@wordpress/components';

/**
* External dependencies
*/
import { Column } from '@shamim-ahmed/components/build-types/gutenberg/table/types';
import { registerCrudStore, useCrudStore } from '@shamim-ahmed/data';
import { Table } from '@shamim-ahmed/dashboard';
import moment from 'moment';

Expand All @@ -23,7 +26,10 @@ import { OrderTableContainer } from './style';
import { ActionsDropdownWrapper } from './style';

const checkoutPageUrl = directorist_admin_order.checkout_page_url;
const columns: Column[] = [
const orderStoreName = 'directorist/orders';
const orderStorePath = '/directorist/v1/orders';

const baseColumns: Column[] = [
{
id: 'id',
label: __('Order Id', 'directorist'),
Expand Down Expand Up @@ -98,50 +104,131 @@ const columns: Column[] = [
);
},
},
{
id: 'actions',
label: __('Actions', 'directorist'),
render: ({ item }) => {
const controls: any[] = [];
];

if (item?.status === 'pending') {
controls.push({
title: __('Pay', 'directorist'),
onClick: () => {
window.location.href = `${checkoutPageUrl}?checkout_type=payment&order_id=${item?.id}`;
},
});
}
export default function App() {
registerCrudStore({ name: orderStoreName, path: orderStorePath });
const { updateItem } = useCrudStore({ name: orderStoreName });
const [cancelItem, setCancelItem] = useState<any>(null);
const [isCancelling, setIsCancelling] = useState(false);
const [cancelError, setCancelError] = useState('');

return (
<ActionsDropdownWrapper>
<DropdownMenu
icon={moreVertical}
label={__('Actions', 'directorist')}
controls={controls}
placement="right-end"
toggleProps={{
'aria-label': __('Package actions', 'directorist'),
}}
/>
</ActionsDropdownWrapper>
const handleCancelOrder = async () => {
if (!cancelItem?.id) return;

setIsCancelling(true);
setCancelError('');

try {
await apiFetch({
path: `/directorist/v1/orders/${cancelItem.id}/cancel`,
method: 'POST',
});
updateItem(cancelItem.id, { status: 'cancelled' });
setCancelItem(null);
} catch (error) {
setCancelError(
error?.message ||
__('Failed to cancel order. Please try again.', 'directorist')
);
} finally {
setIsCancelling(false);
}
};

const columns: Column[] = [
...baseColumns,
{
id: 'actions',
label: __('Actions', 'directorist'),
render: ({ item }) => {
if (item?.status !== 'pending') {
return <span className="directorist-table-text-light">—</span>;
}

const controls: any[] = [
{
title: __('Pay', 'directorist'),
onClick: () => {
window.location.href = `${checkoutPageUrl}?checkout_type=payment&order_id=${item?.id}`;
},
},
{
title: __('Cancel', 'directorist'),
onClick: () => {
setCancelError('');
setCancelItem(item);
},
},
];

return (
<ActionsDropdownWrapper>
<DropdownMenu
icon={moreVertical}
label={__('Actions', 'directorist')}
controls={controls}
placement="right-end"
toggleProps={{
'aria-label': __('Order actions', 'directorist'),
}}
/>
</ActionsDropdownWrapper>
);
},
},
},
];
];

export default function App() {
return (
<OrderTableContainer>
<Table
heading="Orders"
storeName="directorist/orders"
path="/directorist/v1/orders"
storeName={orderStoreName}
path={orderStorePath}
columns={columns}
create={{ status: false }}
edit={{ status: false }}
destroy={{ status: false }}
/>

{cancelItem && (
<Modal
title={__('Cancel Order', 'directorist')}
size="small"
isDismissible={!isCancelling}
onRequestClose={() => {
if (!isCancelling) setCancelItem(null);
}}
>
<p>
{__(
'Are you sure you want to cancel this pending order?',
'directorist'
)}
</p>
{cancelError && (
<p className="directorist-error__msg">{cancelError}</p>
)}
<div className="directorist-payment-action directorist-flex directorist-justify-content-between">
<Button
variant="secondary"
onClick={() => setCancelItem(null)}
disabled={isCancelling}
>
{__('Keep Order', 'directorist')}
</Button>
<Button
variant="primary"
isDestructive
isBusy={isCancelling}
disabled={isCancelling}
onClick={handleCancelOrder}
>
{__('Cancel Order', 'directorist')}
</Button>
</div>
</Modal>
)}
</OrderTableContainer>
);
}
50 changes: 49 additions & 1 deletion includes/rest-api/Version1/class-order-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,26 @@ public function register_routes() {
],
]
);

register_rest_route(
$this->namespace,
'/' . $this->rest_base . '/(?P<id>[\d]+)/cancel',
[
[
'methods' => WP_REST_Server::CREATABLE,
'callback' => [ $this, 'cancel' ],
'permission_callback' => [ $this, 'auth_permissions_check' ],
'args' => [
'id' => [
'description' => __( 'The order ID.' ),
'type' => 'integer',
'sanitize_callback' => 'absint',
'required' => true,
],
],
],
]
);
}

public function admin_index( WP_REST_Request $request ) {
Expand Down Expand Up @@ -223,6 +243,34 @@ public function update_status( WP_REST_Request $request ) {
] );
}

public function cancel( WP_REST_Request $request ) {
$repository = directorist_order_repository();
$order = $repository->get_by_id( $request->get_param( 'id' ) );

if ( ! $order ) {
return new WP_Error( 'rest_not_found', __( 'The order was not found' ), [ 'status' => 404 ] );
}

if ( (int) $order->user_id !== get_current_user_id() ) {
return new WP_Error( 'rest_forbidden', __( 'You are not authorized to cancel this order.' ), [ 'status' => 403 ] );
}

if ( OrderStatus::PENDING !== $order->status ) {
return new WP_Error( 'rest_invalid_status', __( 'Only pending orders can be cancelled.' ), [ 'status' => 400 ] );
}

$dto = $repository->to_dto( $order );
$dto->set_status( OrderStatus::CANCELLED );

$repository->update( $dto );

return rest_ensure_response(
[
'message' => esc_html__( 'Order was cancelled successfully' ),
]
);
}

protected function store_args(): array {
return [
'user_id' => [
Expand Down Expand Up @@ -268,4 +316,4 @@ protected function index_args(): array {
],
];
}
}
}
3 changes: 2 additions & 1 deletion templates/checkout/checkout.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,14 @@
<?php endif;
$submit_button_label = ( $subtotal < 1 ) ? __( 'Complete Submission', 'directorist' ) : __( 'Pay Now', 'directorist' );
$submit_button_label = apply_filters( 'directorist_checkout_submit_button_label', $submit_button_label, $checkout_type, $subtotal, $request );
$cancel_button_label = ( 'payment' === $checkout_type ) ? __( 'Not Now', 'directorist' ) : __( 'Cancel', 'directorist' );
$dashboard_page_link = \ATBDP_Permalink::get_dashboard_page_link();
?>

<p id="atbdp_checkout_errors" class="text-danger"></p>

<div class="directorist-payment-action directorist-flex directorist-justify-content-between" id="atbdp_pay_notpay_btn">
<a href="<?php echo $dashboard_page_link; ?>" class="directorist-btn directorist-btn-lg directorist-btn-light atbdp_not_now_button"><?php esc_html_e( 'Not Now', 'directorist' ); ?></a>
<a href="<?php echo $dashboard_page_link; ?>" class="directorist-btn directorist-btn-lg directorist-btn-light atbdp_not_now_button"><?php echo esc_html( $cancel_button_label ); ?></a>
<button type="submit" id="atbdp_checkout_submit_btn" class="directorist-btn directorist-btn-lg directorist-btn-payment-submit" data-loading-text="<?php esc_html_e( 'Processing...', 'directorist' ); ?>">
<span class="directorist-btn-text"><?php echo esc_html( $submit_button_label ); ?></span>
<span class="directorist-btn-spinner" style="display: none;">
Expand Down
Loading