ultimate-jekyll-manager 0.0.277 → 0.0.279
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/css/pages/account/index.scss +4 -0
- package/dist/assets/js/pages/account/sections/billing.js +111 -192
- package/dist/assets/js/pages/payment/checkout/modules/tracking.js +2 -3
- package/dist/assets/js/pages/payment/confirmation/modules/state.js +10 -10
- package/dist/assets/js/pages/status/index.js +2 -2
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/account/index.html +57 -9
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/status.html +1 -2
- package/package.json +1 -1
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
// Libraries
|
|
6
6
|
import { FormManager } from '__main_assets__/js/libs/form-manager.js';
|
|
7
7
|
import authorizedFetch from '__main_assets__/js/libs/authorized-fetch.js';
|
|
8
|
-
import { getPrerenderedIcon } from '__main_assets__/js/libs/prerendered-icons.js';
|
|
9
8
|
|
|
10
9
|
let webManager = null;
|
|
11
10
|
let appData = null;
|
|
@@ -23,6 +22,18 @@ const CANCEL_REASONS = [
|
|
|
23
22
|
'Other',
|
|
24
23
|
];
|
|
25
24
|
|
|
25
|
+
// Status display configuration
|
|
26
|
+
const STATUS_CONFIG = {
|
|
27
|
+
free: { label: 'Free', badgeClass: 'badge bg-secondary' },
|
|
28
|
+
active: { label: 'Active', badgeClass: 'badge bg-success' },
|
|
29
|
+
trialing: { label: 'Active', badgeClass: 'badge bg-success' },
|
|
30
|
+
cancelling: { label: 'Active', badgeClass: 'badge bg-success' },
|
|
31
|
+
suspended: { label: 'Suspended', badgeClass: 'badge bg-danger' },
|
|
32
|
+
cancelled: { label: 'Cancelled', badgeClass: 'badge bg-secondary' },
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const FREQUENCY_LABELS = { daily: 'day', weekly: 'week', monthly: 'month', annually: 'year' };
|
|
36
|
+
|
|
26
37
|
// Initialize billing section
|
|
27
38
|
export async function init(wm) {
|
|
28
39
|
webManager = wm;
|
|
@@ -39,11 +50,7 @@ export async function loadData(account, sharedAppData) {
|
|
|
39
50
|
appData = sharedAppData;
|
|
40
51
|
currentAccount = account;
|
|
41
52
|
|
|
42
|
-
|
|
43
|
-
updateAlerts(account);
|
|
44
|
-
updateBillingDetails(account);
|
|
45
|
-
updateActionButtons(account);
|
|
46
|
-
updateUsageInfo(account);
|
|
53
|
+
updateUI(account);
|
|
47
54
|
}
|
|
48
55
|
|
|
49
56
|
// Called when section is shown
|
|
@@ -51,196 +58,89 @@ export function onShow() {
|
|
|
51
58
|
// Nothing needed
|
|
52
59
|
}
|
|
53
60
|
|
|
54
|
-
// ───
|
|
61
|
+
// ─── UI Update ──────────────────────────────────────────────
|
|
55
62
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const productId = getProductId(subscription);
|
|
66
|
-
const isPaid = productId !== 'basic';
|
|
67
|
-
const displayName = getDisplayName(subscription);
|
|
68
|
-
const status = getEffectiveStatus(subscription, isPaid);
|
|
69
|
-
|
|
70
|
-
switch (status) {
|
|
71
|
-
case 'free': {
|
|
72
|
-
$planStatus.className = 'badge bg-secondary';
|
|
73
|
-
$planStatus.textContent = 'Free';
|
|
74
|
-
$planDescription.innerHTML = `You are currently on the <strong>${displayName}</strong> plan. Upgrade to unlock premium features.`;
|
|
75
|
-
break;
|
|
76
|
-
}
|
|
77
|
-
case 'trialing':
|
|
78
|
-
case 'cancelling':
|
|
79
|
-
case 'active': {
|
|
80
|
-
$planStatus.className = 'badge bg-success';
|
|
81
|
-
$planStatus.textContent = 'Active';
|
|
82
|
-
$planDescription.innerHTML = `You are currently on the <strong>${displayName}</strong> plan.`;
|
|
83
|
-
break;
|
|
84
|
-
}
|
|
85
|
-
case 'suspended': {
|
|
86
|
-
$planStatus.className = 'badge bg-danger';
|
|
87
|
-
$planStatus.textContent = 'Suspended';
|
|
88
|
-
$planDescription.innerHTML = `Your <strong>${displayName}</strong> subscription has been suspended and access has been revoked due to a payment issue. Please update your payment method to restore access.`;
|
|
89
|
-
break;
|
|
90
|
-
}
|
|
91
|
-
case 'cancelled': {
|
|
92
|
-
$planStatus.className = 'badge bg-secondary';
|
|
93
|
-
$planStatus.textContent = 'Cancelled';
|
|
94
|
-
$planDescription.innerHTML = `Your <strong>${displayName}</strong> subscription has ended. Resubscribe to regain access to premium features.`;
|
|
95
|
-
break;
|
|
96
|
-
}
|
|
97
|
-
default: {
|
|
98
|
-
$planStatus.className = 'badge bg-secondary';
|
|
99
|
-
$planStatus.textContent = 'Free';
|
|
100
|
-
$planDescription.innerHTML = `You are currently on the <strong>Free</strong> plan.`;
|
|
101
|
-
break;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
63
|
+
/* @dev-only:start */
|
|
64
|
+
{
|
|
65
|
+
window._billing = {
|
|
66
|
+
test: (account) => updateUI(account),
|
|
67
|
+
state: () => buildBillingState(currentAccount),
|
|
68
|
+
restore: () => { if (currentAccount) updateUI(currentAccount); },
|
|
69
|
+
};
|
|
104
70
|
}
|
|
71
|
+
/* @dev-only:end */
|
|
105
72
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const $alerts = document.getElementById('billing-alerts');
|
|
110
|
-
if (!$alerts) {
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
$alerts.innerHTML = '';
|
|
115
|
-
const subscription = account.subscription || {};
|
|
116
|
-
const productId = getProductId(subscription);
|
|
117
|
-
const isPaid = productId !== 'basic';
|
|
118
|
-
const status = getEffectiveStatus(subscription, isPaid);
|
|
119
|
-
|
|
120
|
-
// Suspended (payment issue) alert
|
|
121
|
-
if (status === 'suspended') {
|
|
122
|
-
$alerts.innerHTML += `
|
|
123
|
-
<div class="alert alert-danger d-flex align-items-start">
|
|
124
|
-
<span class="me-2 flex-shrink-0">${getPrerenderedIcon('triangle-exclamation', 'fa-md')}</span>
|
|
125
|
-
<div>
|
|
126
|
-
<strong>Payment failed</strong>
|
|
127
|
-
<p class="mb-0 small">Your payment method was declined. Please update your payment method to keep your subscription active.</p>
|
|
128
|
-
</div>
|
|
129
|
-
</div>
|
|
130
|
-
`;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Cancellation pending alert
|
|
134
|
-
if (status === 'cancelling') {
|
|
135
|
-
const cancelTimestamp = subscription.cancellation?.date?.timestampUNIX;
|
|
136
|
-
const dateStr = cancelTimestamp && cancelTimestamp > 0
|
|
137
|
-
? new Date(cancelTimestamp * 1000).toLocaleDateString()
|
|
138
|
-
: 'the end of your billing period';
|
|
139
|
-
|
|
140
|
-
$alerts.innerHTML += `
|
|
141
|
-
<div class="alert alert-warning d-flex align-items-start">
|
|
142
|
-
<span class="me-2 flex-shrink-0">${getPrerenderedIcon('clock', 'fa-md')}</span>
|
|
143
|
-
<div>
|
|
144
|
-
<strong>Cancellation scheduled</strong>
|
|
145
|
-
<p class="mb-0 small">Your subscription will end on <strong>${dateStr}</strong>. You'll continue to have full access until then. If you change your mind, you can reactivate through billing management.</p>
|
|
146
|
-
</div>
|
|
147
|
-
</div>
|
|
148
|
-
`;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Free trial alert
|
|
152
|
-
if (status === 'trialing') {
|
|
153
|
-
const trialEndUnix = subscription.trial?.expires?.timestampUNIX;
|
|
154
|
-
const endDate = trialEndUnix && trialEndUnix > 0
|
|
155
|
-
? new Date(trialEndUnix * 1000).toLocaleDateString()
|
|
156
|
-
: null;
|
|
157
|
-
|
|
158
|
-
$alerts.innerHTML += `
|
|
159
|
-
<div class="alert alert-success d-flex align-items-start">
|
|
160
|
-
<span class="me-2 flex-shrink-0">${getPrerenderedIcon('circle-check', 'fa-md')}</span>
|
|
161
|
-
<div>
|
|
162
|
-
<strong>Free trial</strong>
|
|
163
|
-
<p class="mb-0 small">You're on a free trial${endDate ? ` that ends on <strong>${endDate}</strong>` : ''}. You won't be charged until your trial ends.</p>
|
|
164
|
-
</div>
|
|
165
|
-
</div>
|
|
166
|
-
`;
|
|
167
|
-
}
|
|
73
|
+
function updateUI(account) {
|
|
74
|
+
webManager.bindings().update(buildBillingState(account));
|
|
75
|
+
updateUsageInfo(account);
|
|
168
76
|
}
|
|
169
77
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
function updateBillingDetails(account) {
|
|
173
|
-
const subscription = account.subscription || {};
|
|
174
|
-
const $details = document.getElementById('billing-details');
|
|
175
|
-
|
|
176
|
-
if (!$details) {
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
|
|
78
|
+
function buildBillingState(account) {
|
|
79
|
+
const subscription = account?.subscription || {};
|
|
180
80
|
const productId = getProductId(subscription);
|
|
181
81
|
const isPaid = productId !== 'basic';
|
|
182
82
|
const status = getEffectiveStatus(subscription, isPaid);
|
|
83
|
+
const displayName = getDisplayName(subscription);
|
|
84
|
+
const config = STATUS_CONFIG[status] || STATUS_CONFIG.free;
|
|
183
85
|
|
|
184
|
-
//
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
86
|
+
// Pre-format alert dates
|
|
87
|
+
const cancelTimestamp = subscription.cancellation?.date?.timestampUNIX;
|
|
88
|
+
const cancelDate = (cancelTimestamp && cancelTimestamp > 0)
|
|
89
|
+
? new Date(cancelTimestamp * 1000).toLocaleDateString()
|
|
90
|
+
: 'the end of your billing period';
|
|
189
91
|
|
|
92
|
+
const trialEndUnix = subscription.trial?.expires?.timestampUNIX;
|
|
93
|
+
const trialEndDate = (trialEndUnix && trialEndUnix > 0)
|
|
94
|
+
? new Date(trialEndUnix * 1000).toLocaleDateString()
|
|
95
|
+
: null;
|
|
96
|
+
|
|
97
|
+
// Pre-format billing details
|
|
190
98
|
const nextBillingUnix = subscription.expires?.timestampUNIX;
|
|
191
99
|
const amount = subscription.payment?.price;
|
|
192
100
|
const currency = appData?.payment?.currency || 'USD';
|
|
193
101
|
const frequency = subscription.payment?.frequency;
|
|
102
|
+
const hasValidBilling = nextBillingUnix && nextBillingUnix > 0 && amount;
|
|
194
103
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
104
|
+
return {
|
|
105
|
+
billing: {
|
|
106
|
+
plan: {
|
|
107
|
+
name: displayName,
|
|
108
|
+
},
|
|
109
|
+
status: {
|
|
110
|
+
label: config.label,
|
|
111
|
+
badgeClass: config.badgeClass,
|
|
112
|
+
},
|
|
113
|
+
description: {
|
|
114
|
+
free: status === 'free',
|
|
115
|
+
active: status === 'active' || status === 'trialing' || status === 'cancelling',
|
|
116
|
+
suspended: status === 'suspended',
|
|
117
|
+
cancelled: status === 'cancelled',
|
|
118
|
+
},
|
|
119
|
+
alerts: {
|
|
120
|
+
suspended: status === 'suspended',
|
|
121
|
+
cancelling: status === 'cancelling',
|
|
122
|
+
trialing: status === 'trialing',
|
|
123
|
+
cancelDate: cancelDate,
|
|
124
|
+
trialEndDate: trialEndDate || '',
|
|
125
|
+
trialHasEndDate: !!trialEndDate,
|
|
126
|
+
},
|
|
127
|
+
details: {
|
|
128
|
+
visible: isPaid && status !== 'suspended' && status !== 'cancelled' && !!hasValidBilling,
|
|
129
|
+
nextDate: hasValidBilling ? new Date(nextBillingUnix * 1000).toLocaleDateString() : '',
|
|
130
|
+
amount: hasValidBilling ? `${formatCurrency(amount, currency)} / ${FREQUENCY_LABELS[frequency] || 'month'}` : '',
|
|
131
|
+
},
|
|
132
|
+
buttons: {
|
|
133
|
+
upgrade: !isPaid || status === 'cancelled',
|
|
134
|
+
change: isPaid && status !== 'cancelled' && status !== 'suspended',
|
|
135
|
+
manage: isPaid && status !== 'cancelled',
|
|
136
|
+
cancel: isPaid && status !== 'cancelled' && status !== 'suspended',
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
};
|
|
215
140
|
}
|
|
216
141
|
|
|
217
142
|
// ─── Action Buttons ──────────────────────────────────────────
|
|
218
143
|
|
|
219
|
-
function updateActionButtons(account) {
|
|
220
|
-
// Bindings handle the primary show/hide based on product.id (basic vs paid).
|
|
221
|
-
// JS only needs to refine for cancelled/suspended edge cases.
|
|
222
|
-
const subscription = account.subscription || {};
|
|
223
|
-
const productId = getProductId(subscription);
|
|
224
|
-
const isPaid = productId !== 'basic';
|
|
225
|
-
const status = getEffectiveStatus(subscription, isPaid);
|
|
226
|
-
|
|
227
|
-
const $upgradeBtn = document.getElementById('upgrade-plan-btn');
|
|
228
|
-
const $changeBtn = document.getElementById('change-plan-btn');
|
|
229
|
-
const $manageBtn = document.getElementById('manage-billing-btn');
|
|
230
|
-
const $cancelTrigger = document.getElementById('cancel-subscription-trigger');
|
|
231
|
-
|
|
232
|
-
if (status === 'cancelled') {
|
|
233
|
-
// Cancelled: show upgrade, hide change/manage/cancel
|
|
234
|
-
if ($upgradeBtn) $upgradeBtn.removeAttribute('hidden');
|
|
235
|
-
if ($changeBtn) $changeBtn.setAttribute('hidden', '');
|
|
236
|
-
if ($manageBtn) $manageBtn.setAttribute('hidden', '');
|
|
237
|
-
if ($cancelTrigger) $cancelTrigger.setAttribute('hidden', '');
|
|
238
|
-
} else if (status === 'suspended') {
|
|
239
|
-
// Suspended: hide change, keep manage + cancel visible (bindings handle)
|
|
240
|
-
if ($changeBtn) $changeBtn.setAttribute('hidden', '');
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
144
|
function setupActionButtons() {
|
|
245
145
|
const $upgradeBtn = document.getElementById('upgrade-plan-btn');
|
|
246
146
|
const $changeBtn = document.getElementById('change-plan-btn');
|
|
@@ -343,7 +243,16 @@ function setupCancellationForm() {
|
|
|
343
243
|
throw new Error(response.message || 'Failed to cancel subscription. Please try again.');
|
|
344
244
|
}
|
|
345
245
|
|
|
346
|
-
|
|
246
|
+
// Detect trial cancellation: trial was active and trial period equals subscription period
|
|
247
|
+
const sub = currentAccount?.subscription;
|
|
248
|
+
const isTrialCancel = sub?.trial?.claimed === true
|
|
249
|
+
&& sub?.trial?.expires?.timestampUNIX === sub?.expires?.timestampUNIX;
|
|
250
|
+
|
|
251
|
+
if (isTrialCancel) {
|
|
252
|
+
cancelFormManager.showSuccess('Your trial has been cancelled. You\'ve been moved to the free plan. You can subscribe again anytime.');
|
|
253
|
+
} else {
|
|
254
|
+
cancelFormManager.showSuccess('Your subscription has been cancelled. You\'ll continue to have access until the end of your current billing period.');
|
|
255
|
+
}
|
|
347
256
|
|
|
348
257
|
// Collapse the cancel form after a short delay
|
|
349
258
|
setTimeout(() => {
|
|
@@ -352,22 +261,32 @@ function setupCancellationForm() {
|
|
|
352
261
|
const bsCollapse = bootstrap.Collapse.getInstance($accordion);
|
|
353
262
|
if (bsCollapse) bsCollapse.hide();
|
|
354
263
|
}
|
|
355
|
-
},
|
|
264
|
+
}, 1000);
|
|
356
265
|
|
|
357
266
|
// Update the UI to reflect cancellation (using backend structure)
|
|
358
|
-
if (
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
267
|
+
if (sub) {
|
|
268
|
+
if (isTrialCancel) {
|
|
269
|
+
// Trial cancellations are immediate — set status to cancelled
|
|
270
|
+
sub.status = 'cancelled';
|
|
271
|
+
sub.cancellation = {
|
|
272
|
+
pending: false,
|
|
273
|
+
date: {
|
|
274
|
+
timestamp: new Date().toISOString(),
|
|
275
|
+
timestampUNIX: Math.floor(Date.now() / 1000),
|
|
276
|
+
},
|
|
277
|
+
};
|
|
278
|
+
} else {
|
|
279
|
+
// Non-trial cancellations are pending until end of billing period
|
|
280
|
+
const expiresUnix = sub.expires?.timestampUNIX || 0;
|
|
281
|
+
sub.cancellation = {
|
|
282
|
+
pending: true,
|
|
283
|
+
date: {
|
|
284
|
+
timestamp: new Date(expiresUnix * 1000).toISOString(),
|
|
285
|
+
timestampUNIX: expiresUnix,
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
updateUI(currentAccount);
|
|
371
290
|
}
|
|
372
291
|
});
|
|
373
292
|
}
|
|
@@ -7,9 +7,8 @@ function getBasePrice(state) {
|
|
|
7
7
|
if (!product) return 0;
|
|
8
8
|
|
|
9
9
|
if (product.type === 'subscription') {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
: (product.prices?.annually?.amount || 0);
|
|
10
|
+
const entry = product.prices?.[state.frequency];
|
|
11
|
+
return (typeof entry === 'object' ? entry?.amount : entry) || 0;
|
|
13
12
|
}
|
|
14
13
|
|
|
15
14
|
return product.prices?.amount
|
|
@@ -21,16 +21,16 @@ export const state = {
|
|
|
21
21
|
// Returns a fresh object every time -- no mutation of shared references
|
|
22
22
|
export function buildBindingsState() {
|
|
23
23
|
const isSubscription = !!state.frequency;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
24
|
+
|
|
25
|
+
const FREQUENCY_MAP = {
|
|
26
|
+
daily: { cycle: 'daily', period: 'day' },
|
|
27
|
+
weekly: { cycle: 'weekly', period: 'week' },
|
|
28
|
+
monthly: { cycle: 'monthly', period: 'month' },
|
|
29
|
+
annually: { cycle: 'annually', period: 'year' },
|
|
30
|
+
};
|
|
31
|
+
const freq = FREQUENCY_MAP[state.frequency] || { cycle: '', period: '' };
|
|
32
|
+
const billingCycleText = freq.cycle;
|
|
33
|
+
const billingPeriodText = freq.period;
|
|
34
34
|
|
|
35
35
|
// Build subscription info text
|
|
36
36
|
let subscriptionInfoText = '';
|
|
@@ -345,7 +345,7 @@ function updateTimeAgo($element, timestamp) {
|
|
|
345
345
|
const totalSeconds = Math.floor(diffMs / 1000);
|
|
346
346
|
|
|
347
347
|
if (totalSeconds <= 0) {
|
|
348
|
-
$element.textContent = 'Just now';
|
|
348
|
+
$element.textContent = '(Just now)';
|
|
349
349
|
return;
|
|
350
350
|
}
|
|
351
351
|
|
|
@@ -383,7 +383,7 @@ function updateTimeAgo($element, timestamp) {
|
|
|
383
383
|
}
|
|
384
384
|
parts.push(`${seconds}s`);
|
|
385
385
|
|
|
386
|
-
$element.textContent = parts.join(' ') + ' ago';
|
|
386
|
+
$element.textContent = '(' + parts.join(' ') + ' ago)';
|
|
387
387
|
}
|
|
388
388
|
|
|
389
389
|
// Fetch status data from API (if configured)
|
|
@@ -970,30 +970,78 @@ badges:
|
|
|
970
970
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
|
971
971
|
<div>
|
|
972
972
|
<h5 class="card-title mb-1 d-flex align-items-center flex-wrap gap-2">
|
|
973
|
-
<span><span data-wm-bind="@text
|
|
974
|
-
<span
|
|
973
|
+
<span><span data-wm-bind="@text billing.plan.name" class="wm-binding-skeleton">Current</span> plan</span>
|
|
974
|
+
<span data-wm-bind="@attr class billing.status.badgeClass, @text billing.status.label" class="badge bg-secondary wm-binding-skeleton"> </span>
|
|
975
975
|
</h5>
|
|
976
|
-
<p class="card-text mb-0"
|
|
976
|
+
<p class="card-text mb-0" data-wm-bind="@show billing.description.free" hidden>
|
|
977
|
+
You are currently on the <strong data-wm-bind="@text billing.plan.name">Free</strong> plan. Upgrade to unlock premium features.
|
|
978
|
+
</p>
|
|
979
|
+
<p class="card-text mb-0" data-wm-bind="@show billing.description.active" hidden>
|
|
980
|
+
You are currently on the <strong data-wm-bind="@text billing.plan.name">Current</strong> plan.
|
|
981
|
+
</p>
|
|
982
|
+
<p class="card-text mb-0" data-wm-bind="@show billing.description.suspended" hidden>
|
|
983
|
+
Your <strong data-wm-bind="@text billing.plan.name">Current</strong> subscription has been suspended and access has been revoked due to a payment issue. Please update your payment method to restore access.
|
|
984
|
+
</p>
|
|
985
|
+
<p class="card-text mb-0" data-wm-bind="@show billing.description.cancelled" hidden>
|
|
986
|
+
Your <strong data-wm-bind="@text billing.plan.name">Current</strong> subscription has ended. Resubscribe to regain access to premium features.
|
|
987
|
+
</p>
|
|
977
988
|
</div>
|
|
978
989
|
</div>
|
|
979
990
|
|
|
980
991
|
<!-- Billing details (next billing, amount) -->
|
|
981
|
-
<div id="billing-details" class="mb-3
|
|
992
|
+
<div id="billing-details" class="mb-3" data-wm-bind="@show billing.details.visible" hidden>
|
|
993
|
+
<div class="row small text-muted">
|
|
994
|
+
<div class="col-sm-6 mb-1">
|
|
995
|
+
<strong>Next billing:</strong> <span data-wm-bind="@text billing.details.nextDate"></span>
|
|
996
|
+
</div>
|
|
997
|
+
<div class="col-sm-6 mb-1">
|
|
998
|
+
<strong>Amount:</strong> <span data-wm-bind="@text billing.details.amount"></span>
|
|
999
|
+
</div>
|
|
1000
|
+
</div>
|
|
1001
|
+
</div>
|
|
982
1002
|
|
|
983
1003
|
<!-- State-specific alerts -->
|
|
984
|
-
<div
|
|
1004
|
+
<div class="mb-3">
|
|
1005
|
+
<!-- Suspended (payment issue) alert -->
|
|
1006
|
+
<div class="alert alert-danger d-flex align-items-start" data-wm-bind="@show billing.alerts.suspended" hidden>
|
|
1007
|
+
<span class="me-2 flex-shrink-0">{% uj_icon "triangle-exclamation", "fa-md" %}</span>
|
|
1008
|
+
<div>
|
|
1009
|
+
<strong>Payment failed</strong>
|
|
1010
|
+
<p class="mb-0 small">Your payment method was declined. Please update your payment method to keep your subscription active.</p>
|
|
1011
|
+
</div>
|
|
1012
|
+
</div>
|
|
1013
|
+
|
|
1014
|
+
<!-- Cancellation pending alert -->
|
|
1015
|
+
<div class="alert alert-warning d-flex align-items-start" data-wm-bind="@show billing.alerts.cancelling" hidden>
|
|
1016
|
+
<span class="me-2 flex-shrink-0">{% uj_icon "clock", "fa-md" %}</span>
|
|
1017
|
+
<div>
|
|
1018
|
+
<strong>Cancellation scheduled</strong>
|
|
1019
|
+
<p class="mb-0 small">Your subscription will end on <strong data-wm-bind="@text billing.alerts.cancelDate"></strong>. You'll continue to have full access until then. If you change your mind, you can reactivate through billing management.</p>
|
|
1020
|
+
</div>
|
|
1021
|
+
</div>
|
|
1022
|
+
|
|
1023
|
+
<!-- Free trial alert -->
|
|
1024
|
+
<div class="alert alert-success d-flex align-items-start" data-wm-bind="@show billing.alerts.trialing" hidden>
|
|
1025
|
+
<span class="me-2 flex-shrink-0">{% uj_icon "circle-check", "fa-md" %}</span>
|
|
1026
|
+
<div>
|
|
1027
|
+
<strong>Free trial</strong>
|
|
1028
|
+
<p class="mb-0 small" data-wm-bind="@show billing.alerts.trialHasEndDate" hidden>You're on a free trial that ends on <strong data-wm-bind="@text billing.alerts.trialEndDate"></strong>. You won't be charged until your trial ends.</p>
|
|
1029
|
+
<p class="mb-0 small" data-wm-bind="@hide billing.alerts.trialHasEndDate">You're on a free trial. You won't be charged until your trial ends.</p>
|
|
1030
|
+
</div>
|
|
1031
|
+
</div>
|
|
1032
|
+
</div>
|
|
985
1033
|
|
|
986
1034
|
<!-- Action Buttons -->
|
|
987
1035
|
<div class="d-grid d-sm-flex gap-2">
|
|
988
|
-
<button id="upgrade-plan-btn" class="btn btn-success plan-action-btn" data-wm-bind="@show
|
|
1036
|
+
<button id="upgrade-plan-btn" class="btn btn-success plan-action-btn" data-wm-bind="@show billing.buttons.upgrade" hidden>
|
|
989
1037
|
{% uj_icon "crown", "me-2" %}
|
|
990
1038
|
<span class="button-text">Upgrade Plan</span>
|
|
991
1039
|
</button>
|
|
992
|
-
<button id="change-plan-btn" class="btn btn-outline-adaptive plan-action-btn" data-wm-bind="@show
|
|
1040
|
+
<button id="change-plan-btn" class="btn btn-outline-adaptive plan-action-btn" data-wm-bind="@show billing.buttons.change" hidden>
|
|
993
1041
|
{% uj_icon "arrow-right-arrow-left", "me-2" %}
|
|
994
1042
|
<span class="button-text">Change Plan</span>
|
|
995
1043
|
</button>
|
|
996
|
-
<button id="manage-billing-btn" class="btn btn-primary plan-action-btn" data-wm-bind="@show
|
|
1044
|
+
<button id="manage-billing-btn" class="btn btn-primary plan-action-btn" data-wm-bind="@show billing.buttons.manage" hidden>
|
|
997
1045
|
{% uj_icon "credit-card", "me-2" %}
|
|
998
1046
|
<span class="button-text">Manage Billing</span>
|
|
999
1047
|
</button>
|
|
@@ -1012,7 +1060,7 @@ badges:
|
|
|
1012
1060
|
</div>
|
|
1013
1061
|
|
|
1014
1062
|
<!-- Cancel Subscription (only visible for paid users) -->
|
|
1015
|
-
<div id="cancel-subscription-trigger" class="text-center" data-wm-bind="@show
|
|
1063
|
+
<div id="cancel-subscription-trigger" class="text-center" data-wm-bind="@show billing.buttons.cancel" hidden>
|
|
1016
1064
|
<button type="button" class="btn btn-link btn-sm text-muted text-decoration-underline cancel-trigger-link" data-bs-toggle="collapse" data-bs-target="#cancel-subscription-accordion" aria-expanded="false" aria-controls="cancel-subscription-accordion">
|
|
1017
1065
|
Cancel subscription
|
|
1018
1066
|
</button>
|
|
@@ -307,8 +307,7 @@ build_info:
|
|
|
307
307
|
</div>
|
|
308
308
|
<div>
|
|
309
309
|
<div class="text-muted small mb-1">Last Build</div>
|
|
310
|
-
<div id="build-time" class="
|
|
311
|
-
<div id="build-time-ago" class="text-muted small">—</div>
|
|
310
|
+
<div class="fw-semibold"><span id="build-time">—</span> <span id="build-time-ago" class="text-muted small ms-1"></span></div>
|
|
312
311
|
</div>
|
|
313
312
|
</div>
|
|
314
313
|
</div>
|