PluginProbe ʕ •ᴥ•ʔ
GiveWP – Donation Plugin and Fundraising Platform / 4.8.0
GiveWP – Donation Plugin and Fundraising Platform v4.8.0
4.16.3 4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 2.3.2 2.30.0 2.31.0 2.31.1 2.32.0 2.33.0 2.33.1 2.33.2 2.33.3 2.33.4 2.33.5 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7 2.5.0 2.5.1 2.5.10 2.5.11 2.5.12 2.5.13 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.5.8 2.5.9 2.6.0 2.6.1 2.6.2 2.6.3 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.8.0 2.8.1 2.9.0 2.9.1 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.10.0 3.11.0 3.12.0 3.12.1 3.12.2 3.12.3 3.13.0 3.14.0 3.14.1 3.14.2 3.15.0 3.15.1 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.17.0 3.17.1 3.17.2 3.18.0 3.19.0 3.19.1 3.19.2 3.19.3 3.19.4 3.2.0 3.2.1 3.2.2 3.20.0 3.21.0 3.21.1 3.22.0 3.22.1 3.22.2 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.6.0 3.6.1 3.6.2 3.7.0 3.8.0 3.9.0 4.0.0 4.1.0 4.1.1 4.10.0 4.10.1 4.11.0 4.12.0 4.13.0 4.13.1 4.13.2 4.14.0 4.14.1 4.14.2 4.14.3 4.14.4 4.14.5 4.14.6 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.1 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 trunk 1.9.0 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.2 2.11.3 2.12.0 2.12.1 2.12.2 2.12.3 2.13.0 2.13.1 2.13.2 2.13.3 2.13.4 2.14.0 2.15.0 2.16.0 2.16.1 2.17.0 2.17.1 2.17.3 2.18.0 2.18.1 2.19.1 2.19.2 2.19.3 2.19.4 2.19.5 2.19.6 2.19.7 2.19.8 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.20.0 2.20.1 2.20.2 2.21.0 2.21.1 2.21.2 2.21.3 2.21.4 2.22.0 2.22.1 2.22.2 2.22.3 2.23.0 2.23.1 2.23.2 2.24.0 2.24.1 2.24.2 2.25.0 2.25.1 2.25.2 2.25.3 2.26.0 2.27.0 2.27.1 2.27.2 2.27.3 2.28.0 2.29.0 2.29.1 2.29.2
give / src / Subscriptions / resources / admin / components / SubscriptionDetailsPage / index.tsx
give / src / Subscriptions / resources / admin / components / SubscriptionDetailsPage Last commit date
Tabs 10 months ago components 10 months ago SubscriptionDetailsPage.module.scss 10 months ago index.tsx 10 months ago slots.ts 10 months ago
index.tsx
219 lines
1 /**
2 * External Dependencies
3 */
4 import { useState } from 'react';
5 import cx from 'classnames';
6 import { useFormContext } from 'react-hook-form';
7
8 /**
9 * WordPress Dependencies
10 */
11 import { __, _n, sprintf } from '@wordpress/i18n';
12
13 /**
14 * Internal Dependencies
15 */
16 import { CancelIcon, TrashIcon } from '@givewp/components/AdminDetailsPage/Icons';
17 import AdminDetailsPage from '@givewp/components/AdminDetailsPage';
18 import ConfirmationDialog from '@givewp/components/AdminDetailsPage/ConfirmationDialog';
19 import { getSubscriptionOptionsWindowData, useSubscriptionEntityRecord } from '@givewp/subscriptions/utils';
20 import tabDefinitions from './Tabs/definitions';
21 import { useSubscriptionAmounts } from '@givewp/subscriptions/hooks';
22 import { useDispatch } from '@wordpress/data';
23 import { store as coreDataStore } from '@wordpress/core-data';
24 import useSubscriptionSync from '@givewp/subscriptions/hooks/useSubscriptionSync';
25 import SubscriptionSyncList from '../SubscriptionSyncList';
26 import styles from './SubscriptionDetailsPage.module.scss';
27 import CancelSubscriptionDialog from './components/CancelSubscriptionDialog';
28
29 const { subscriptionStatuses } = getSubscriptionOptionsWindowData();
30
31 /**
32 * @since 4.8.0
33 */
34 const StatusBadge = ({ status, isTest }: { status: string, isTest: boolean }) => {
35 const statusMap = subscriptionStatuses;
36
37 if (!statusMap[status]) {
38 return null;
39 }
40
41 return (
42 <>
43 <div className={`${styles.statusBadge} ${styles[`statusBadge--${status}`]}`}>
44 {statusMap[status]}
45 </div>
46 {isTest && (
47 <div className={`${styles.statusBadge} ${styles.testBadge}`}>
48 {__('Test Subscription', 'give')}
49 </div>
50 )}
51 </>
52 );
53 };
54
55 /**
56 * @since 4.8.0
57 */
58 export default function SubscriptionDetailsPage() {
59 const { adminUrl, subscriptionsAdminUrl } = getSubscriptionOptionsWindowData();
60 const [showConfirmationDialog, setShowConfirmationDialog] = useState<string | null>(null);
61 const [hasSyncBeenPerformed, setHasSyncBeenPerformed] = useState(false);
62
63 // Get subscription ID from URL parameters
64 const urlParams = new URLSearchParams(window.location.search);
65 const subscriptionId = urlParams.get('id');
66
67 const { record: subscription } = useSubscriptionEntityRecord(subscriptionId ? parseInt(subscriptionId) : undefined);
68 const { formatter } = useSubscriptionAmounts(subscription);
69 const { deleteEntityRecord } = useDispatch(coreDataStore);
70 const { syncSubscription, isLoading, hasResolved, syncResult } = useSubscriptionSync();
71 const subscriptionCanSync = subscription?.gateway?.canSync;
72
73 const PageTitle = () => {
74 if (subscription?.amount?.value == null) {
75 return null;
76 }
77 const periodsLabels = {
78 day: _n('day', 'days', subscription?.frequency, 'give'),
79 week: _n('week', 'weeks', subscription?.frequency, 'give'),
80 month: _n('month', 'months', subscription?.frequency, 'give'),
81 quarter: _n('quarter', 'quarters', subscription?.frequency, 'give'),
82 year: _n('year', 'years', subscription?.frequency, 'give'),
83 };
84
85 const period = [
86 __('every', 'give'),
87 subscription?.frequency > 1 ? subscription?.frequency : '',
88 periodsLabels[subscription?.period],
89 ].filter(Boolean).join(' ');
90
91 return (
92 <>
93 {formatter.format(subscription?.amount?.value)} <span className={styles.period}>{period}</span>
94 </>
95 );
96 };
97
98 function SecondaryActionButton({ className }: { className: string }) {
99 return (
100 <button
101 type="button"
102 className={className}
103 onClick={() => {
104 setShowConfirmationDialog('sync');
105 setHasSyncBeenPerformed(false);
106 }}
107 >
108 {__('Sync subscription', 'give')}
109 </button>
110 );
111 }
112
113 const ContextMenuItems = ({ className }: { className: string }) => {
114 return (
115 <>
116 {subscription?.status !== 'cancelled' ? (
117 <a
118 href="#"
119 className={cx(className, styles.archive)}
120 onClick={() => setShowConfirmationDialog('cancel')}
121 >
122 <CancelIcon /> {__('Cancel subscription', 'give')}
123 </a>
124 ) : (
125 <a
126 href="#"
127 className={cx(className, styles.archive)}
128 onClick={() => setShowConfirmationDialog('delete')}
129 >
130 <TrashIcon /> {__('Trash subscription', 'give')}
131 </a>
132 )}
133 </>
134 );
135 };
136
137 /**
138 * @since 4.8.0
139 */
140 const handleDelete = async () => {
141 try {
142 await deleteEntityRecord('givewp', 'subscription', subscription?.id, { force: false })
143 window.location.href = subscriptionsAdminUrl;
144 } catch (error) {
145 setShowConfirmationDialog(null);
146 }
147 };
148
149 /**
150 * @since 4.8.0
151 */
152 const handleSyncSubscription = async () => {
153 try {
154 await syncSubscription(subscription);
155 setHasSyncBeenPerformed(true);
156 console.log('Sync result:', syncResult);
157 } catch (error) {
158 console.error('Sync failed:', error);
159 setShowConfirmationDialog(null);
160 }
161 };
162
163 return (
164 <AdminDetailsPage
165 objectId={subscription?.id}
166 objectType="subscription"
167 objectTypePlural="subscriptions"
168 useObjectEntityRecord={useSubscriptionEntityRecord}
169 tabDefinitions={tabDefinitions}
170 breadcrumbUrl={`${adminUrl}edit.php?post_type=give_forms&page=give-subscriptions`}
171 breadcrumbTitle={subscription?.id && sprintf('#%s', subscription?.id)}
172 pageTitle={<PageTitle />}
173 SecondaryActionButton={subscriptionCanSync && SecondaryActionButton}
174 StatusBadge={() => <StatusBadge status={subscription?.status} isTest={subscription?.mode === 'test'} />}
175 ContextMenuItems={ContextMenuItems}
176 >
177 <CancelSubscriptionDialog
178 subscription={subscription}
179 showConfirmationDialog={showConfirmationDialog}
180 setShowConfirmationDialog={setShowConfirmationDialog}
181 />
182 <ConfirmationDialog
183 title={__('Move subscription to trash', 'give')}
184 actionLabel={__('Trash Subscription', 'give')}
185 isOpen={showConfirmationDialog === 'delete'}
186 handleClose={() => setShowConfirmationDialog(null)}
187 handleConfirm={handleDelete}
188 >
189 {__('Are you sure you want to move this subscription to the trash? You can restore it later if needed.', 'give')}
190 </ConfirmationDialog>
191 <ConfirmationDialog
192 variant={isLoading ? 'syncing' : null}
193 spinner={'arc'}
194 isConfirming={isLoading}
195 title={__('Sync subscription details', 'give')}
196 actionLabel={isLoading ? __('Syncing', 'give') : !hasSyncBeenPerformed ? __('Proceed to sync', 'give') : __('Resync', 'give')}
197 showCancelButton={false}
198 isOpen={showConfirmationDialog === 'sync'}
199 handleClose={() => {
200 setShowConfirmationDialog(null);
201 }}
202 handleConfirm={handleSyncSubscription}
203 footer={
204 hasSyncBeenPerformed && hasResolved && syncResult?.notice && (
205 <div className={styles.syncModalFooter}>
206 <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
207 <path fillRule="evenodd" clipRule="evenodd" d="M10 .832a9.167 9.167 0 1 0 0 18.333A9.167 9.167 0 0 0 10 .832zm0 5a.833.833 0 1 0 0 1.667h.008a.833.833 0 0 0 0-1.667H10zm.833 4.167a.833.833 0 0 0-1.666 0v3.333a.833.833 0 1 0 1.666 0V9.999z" fill="#0C7FF2"/>
208 </svg>
209 {syncResult?.notice}
210 </div>
211 )
212 }
213 >
214 {hasSyncBeenPerformed && hasResolved ? <SubscriptionSyncList syncResult={syncResult} /> : __('This will update the subscription details using the most recent data from the gateway. However, no changes will be made to existing payments.', 'give')}
215 </ConfirmationDialog>
216 </AdminDetailsPage>
217 );
218 }
219