@tapni/auth 1.0.72 → 1.0.74
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/README.md +11 -0
- package/dist/.vite/manifest.json +26 -26
- package/dist/{Apps-Cz5rdOWu.js → Apps-Ca-YMuAn.js} +4 -4
- package/dist/{CustomApp-C2_MFR0o.js → CustomApp-C6PBysID.js} +6 -6
- package/dist/{QR-BnhhTtpP.js → QR-DWC2Q2sk.js} +3 -3
- package/dist/TapniAuth.es.js +1 -1
- package/dist/TapniAuth.umd.js +24 -19
- package/dist/{install-Ct-hQgCR.js → install-C1jLtDf_.js} +5887 -4589
- package/dist/style.css +1 -1
- package/package.json +3 -2
- package/src/services/AuthService.js +3 -0
- package/src/store/auth.js +13 -25
- package/src/views/Billing.vue +632 -47
package/src/views/Billing.vue
CHANGED
|
@@ -36,57 +36,177 @@
|
|
|
36
36
|
<p class="center-text">{{ssoLang[appLanguage].billing_p }}</p>
|
|
37
37
|
|
|
38
38
|
<div class="full-top">
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
<section class="billing-section">
|
|
40
|
+
<div class="section-heading">
|
|
41
|
+
<h3 class="section-title">Subscriptions</h3>
|
|
42
|
+
</div>
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
44
|
+
<div v-if="subscriptions.length === 0" class="no-subscriptions center-text full-top">
|
|
45
|
+
<p class="gray-text">There are no active subscriptions at this point.</p>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div v-else>
|
|
49
|
+
<div v-for="sub in subscriptions" :key="sub.subscriptionId" class="subscription-card half-bottom">
|
|
50
|
+
<div class="subscription-header">
|
|
51
|
+
<div class="subscription-info">
|
|
52
|
+
<h3 class="subscription-name">{{ sub.subscriptionName }}</h3>
|
|
53
|
+
<span class="subscription-status" :class="getStatusClass(sub.status)">
|
|
54
|
+
{{ getStatusText(sub.status) }}
|
|
55
|
+
</span>
|
|
56
|
+
</div>
|
|
53
57
|
</div>
|
|
54
|
-
</div>
|
|
55
58
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
<div class="subscription-details">
|
|
60
|
+
<div class="detail-row">
|
|
61
|
+
<span class="detail-label">Amount:</span>
|
|
62
|
+
<span class="detail-value">{{ formatCurrency(sub.amount, sub.currency) }}</span>
|
|
63
|
+
</div>
|
|
64
|
+
<div class="detail-row">
|
|
65
|
+
<span class="detail-label">Billing:</span>
|
|
66
|
+
<span class="detail-value">{{ formatInterval(sub.interval) }}</span>
|
|
67
|
+
</div>
|
|
68
|
+
<div class="detail-row">
|
|
69
|
+
<span class="detail-label">Licenses:</span>
|
|
70
|
+
<span class="detail-value">{{ sub.licenses }}</span>
|
|
71
|
+
</div>
|
|
72
|
+
<div v-if="sub.isTrial" class="detail-row">
|
|
73
|
+
<span class="detail-label">Trial Ends:</span>
|
|
74
|
+
<span class="detail-value">{{ formatDate(sub.trialEnd) }}</span>
|
|
75
|
+
</div>
|
|
76
|
+
<div v-else-if="sub.endDate" class="detail-row">
|
|
77
|
+
<span class="detail-label">Next Billing:</span>
|
|
78
|
+
<span class="detail-value">{{ formatDate(sub.endDate) }}</span>
|
|
79
|
+
</div>
|
|
60
80
|
</div>
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
<
|
|
81
|
+
|
|
82
|
+
<div class="subscription-actions">
|
|
83
|
+
<button
|
|
84
|
+
v-if="sub.status === 'canceled' || sub.status === 'expired'"
|
|
85
|
+
@click="resubscribe(sub)"
|
|
86
|
+
class="resubscribe-button"
|
|
87
|
+
:disabled="loading"
|
|
88
|
+
>
|
|
89
|
+
{{ loading ? 'Processing...' : 'Re-Subscribe' }}
|
|
90
|
+
</button>
|
|
91
|
+
<button
|
|
92
|
+
v-if="sub.status === 'past_due' || sub.status === 'unpaid'"
|
|
93
|
+
@click="retryPayment(sub)"
|
|
94
|
+
class="pay-now-button"
|
|
95
|
+
:disabled="loading"
|
|
96
|
+
>
|
|
97
|
+
{{ loading ? 'Processing...' : 'Pay Now' }}
|
|
98
|
+
</button>
|
|
99
|
+
<button
|
|
100
|
+
v-if="sub.status !== 'canceled' && sub.status !== 'expired' && sub.status !== 'past_due' && sub.status !== 'unpaid'"
|
|
101
|
+
@click="openCancelModal(sub)"
|
|
102
|
+
class="cancel-button"
|
|
103
|
+
:disabled="loading"
|
|
104
|
+
>
|
|
105
|
+
{{ loading ? 'Processing...' : 'Cancel Subscription' }}
|
|
106
|
+
</button>
|
|
64
107
|
</div>
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
</section>
|
|
111
|
+
|
|
112
|
+
<section class="billing-section orders-section">
|
|
113
|
+
<div class="section-heading">
|
|
114
|
+
<h3 class="section-title">Orders</h3>
|
|
115
|
+
<span v-if="orders.length" class="section-caption">{{ orders.length }} found</span>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<div v-if="ordersLoading" class="no-subscriptions center-text full-top">
|
|
119
|
+
<p class="gray-text">Loading your orders...</p>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<div v-else-if="orders.length === 0" class="no-subscriptions center-text full-top">
|
|
123
|
+
<p class="gray-text">No orders found for this account yet.</p>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<div v-else>
|
|
127
|
+
<div v-for="order in orders" :key="order.id" class="subscription-card order-card half-bottom">
|
|
128
|
+
<div class="subscription-header order-header">
|
|
129
|
+
<div class="subscription-info">
|
|
130
|
+
<h3 class="subscription-name">{{ order.orderNumber || order.orderId }}</h3>
|
|
131
|
+
<div class="order-meta">
|
|
132
|
+
<span class="order-date">{{ formatDateValue(order.createdAt) }}</span>
|
|
133
|
+
<span class="order-source">{{ formatSource(order.source) }}</span>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
<div class="order-badges">
|
|
138
|
+
<span class="subscription-status" :class="getOrderStateClass(order.status)">
|
|
139
|
+
{{ formatOrderState(order.status, order.isDraft, order.canceled) }}
|
|
140
|
+
</span>
|
|
141
|
+
<span v-if="order.paymentStatus" class="subscription-status" :class="getPaymentStatusClass(order.paymentStatus)">
|
|
142
|
+
{{ formatPaymentStatus(order.paymentStatus) }}
|
|
143
|
+
</span>
|
|
144
|
+
<span v-if="order.fulfillmentStatus" class="subscription-status" :class="getFulfillmentStatusClass(order.fulfillmentStatus)">
|
|
145
|
+
{{ formatFulfillmentStatus(order.fulfillmentStatus) }}
|
|
146
|
+
</span>
|
|
147
|
+
</div>
|
|
68
148
|
</div>
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
<
|
|
149
|
+
|
|
150
|
+
<div class="subscription-details order-grid">
|
|
151
|
+
<div class="detail-row">
|
|
152
|
+
<span class="detail-label">Total:</span>
|
|
153
|
+
<span class="detail-value">{{ formatCurrency(order.total, order.currency) }}</span>
|
|
154
|
+
</div>
|
|
155
|
+
<div class="detail-row">
|
|
156
|
+
<span class="detail-label">Subtotal:</span>
|
|
157
|
+
<span class="detail-value">{{ formatCurrency(order.subtotal, order.currency) }}</span>
|
|
158
|
+
</div>
|
|
159
|
+
<div class="detail-row">
|
|
160
|
+
<span class="detail-label">Discount:</span>
|
|
161
|
+
<span class="detail-value">{{ formatSignedCurrency(order.discountAmount, order.currency) }}</span>
|
|
162
|
+
</div>
|
|
163
|
+
<div class="detail-row">
|
|
164
|
+
<span class="detail-label">Tax:</span>
|
|
165
|
+
<span class="detail-value">{{ formatCurrency(order.taxAmount, order.currency) }}</span>
|
|
166
|
+
</div>
|
|
167
|
+
<div class="detail-row">
|
|
168
|
+
<span class="detail-label">Shipping:</span>
|
|
169
|
+
<span class="detail-value">{{ order.shippingMethod || 'Not specified' }}</span>
|
|
170
|
+
</div>
|
|
171
|
+
<div class="detail-row">
|
|
172
|
+
<span class="detail-label">Invoice:</span>
|
|
173
|
+
<span class="detail-value">{{ order.invoiceStatus || 'Not available' }}</span>
|
|
174
|
+
</div>
|
|
72
175
|
</div>
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
<
|
|
176
|
+
|
|
177
|
+
<div v-if="order.items?.length" class="order-items">
|
|
178
|
+
<h4 class="subsection-title">Items</h4>
|
|
179
|
+
<div v-for="(item, index) in order.items" :key="`${order.id}-${index}`" class="order-item-row">
|
|
180
|
+
<span class="detail-label">{{ item.name || item.sku || 'Item' }} x{{ item.quantity }}</span>
|
|
181
|
+
<span class="detail-value">{{ formatCurrency(item.price * item.quantity, order.currency) }}</span>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
<div class="order-addresses" v-if="order.billingAddress || order.shippingAddress">
|
|
186
|
+
<div v-if="order.billingAddress" class="address-card">
|
|
187
|
+
<h4 class="subsection-title">Billing Address</h4>
|
|
188
|
+
<p class="address-line">{{ formatAddress(order.billingAddress) }}</p>
|
|
189
|
+
</div>
|
|
190
|
+
<div v-if="order.shippingAddress" class="address-card">
|
|
191
|
+
<h4 class="subsection-title">Shipping Address</h4>
|
|
192
|
+
<p class="address-line">{{ formatAddress(order.shippingAddress) }}</p>
|
|
193
|
+
</div>
|
|
76
194
|
</div>
|
|
77
|
-
</div>
|
|
78
195
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
:
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
196
|
+
<div class="subscription-actions order-actions">
|
|
197
|
+
<a v-if="order.invoiceUrl" :href="order.invoiceUrl" target="_blank" rel="noopener noreferrer" class="resubscribe-button button-link">
|
|
198
|
+
View Invoice
|
|
199
|
+
</a>
|
|
200
|
+
<a v-if="order.designerUrl" :href="order.designerUrl" target="_blank" rel="noopener noreferrer" class="pay-now-button button-link">
|
|
201
|
+
Open Order
|
|
202
|
+
</a>
|
|
203
|
+
<a v-if="order.orderStatusUrl" :href="order.orderStatusUrl" target="_blank" rel="noopener noreferrer" class="cancel-button button-link">
|
|
204
|
+
Order Status
|
|
205
|
+
</a>
|
|
206
|
+
</div>
|
|
87
207
|
</div>
|
|
88
208
|
</div>
|
|
89
|
-
</
|
|
209
|
+
</section>
|
|
90
210
|
</div>
|
|
91
211
|
</div>
|
|
92
212
|
|
|
@@ -136,6 +256,7 @@
|
|
|
136
256
|
import AuthMixin from "../mixins/auth.mixin";
|
|
137
257
|
import {EventBus} from "@/store/event-bus.js";
|
|
138
258
|
import api from "@/services/Api.js";
|
|
259
|
+
import AuthService from "@/services/AuthService.js";
|
|
139
260
|
|
|
140
261
|
export default {
|
|
141
262
|
name: "AuthBilling",
|
|
@@ -150,6 +271,8 @@ export default {
|
|
|
150
271
|
return {
|
|
151
272
|
loading: false,
|
|
152
273
|
subscriptions: [],
|
|
274
|
+
orders: [],
|
|
275
|
+
ordersLoading: false,
|
|
153
276
|
showCancelModal: false,
|
|
154
277
|
selectedSubscription: null,
|
|
155
278
|
cancelFeedback: ''
|
|
@@ -157,13 +280,17 @@ export default {
|
|
|
157
280
|
},
|
|
158
281
|
async mounted() {
|
|
159
282
|
if (!this.isLoggedIn) this.$router.push('/login');
|
|
160
|
-
await this.
|
|
161
|
-
this.loadSubscriptions();
|
|
283
|
+
await this.refreshBillingData();
|
|
162
284
|
},
|
|
163
285
|
methods: {
|
|
164
286
|
close () {
|
|
165
287
|
EventBus.$emit('ssoEvent', {name: 'toggleAuthModal', data: true})
|
|
166
288
|
},
|
|
289
|
+
async refreshBillingData() {
|
|
290
|
+
await this.getAccountSettings();
|
|
291
|
+
this.loadSubscriptions();
|
|
292
|
+
await this.loadOrders();
|
|
293
|
+
},
|
|
167
294
|
loadSubscriptions() {
|
|
168
295
|
// Extract subscriptions from account.billing
|
|
169
296
|
if (this.account.billing) {
|
|
@@ -178,6 +305,26 @@ export default {
|
|
|
178
305
|
}
|
|
179
306
|
|
|
180
307
|
this.subscriptions = subs;
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
this.subscriptions = [];
|
|
312
|
+
},
|
|
313
|
+
async loadOrders() {
|
|
314
|
+
this.ordersLoading = true;
|
|
315
|
+
|
|
316
|
+
try {
|
|
317
|
+
const response = await AuthService.getAccountOrders({ limit: 25 });
|
|
318
|
+
this.orders = response?.data?.data || [];
|
|
319
|
+
} catch (error) {
|
|
320
|
+
console.error('Error loading account orders:', error);
|
|
321
|
+
this.orders = [];
|
|
322
|
+
EventBus.$emit('showToast', {
|
|
323
|
+
type: 'error',
|
|
324
|
+
message: error.response?.data?.error || 'Failed to load orders.'
|
|
325
|
+
});
|
|
326
|
+
} finally {
|
|
327
|
+
this.ordersLoading = false;
|
|
181
328
|
}
|
|
182
329
|
},
|
|
183
330
|
formatCurrency(amount, currency) {
|
|
@@ -187,7 +334,11 @@ export default {
|
|
|
187
334
|
'GBP': '£'
|
|
188
335
|
};
|
|
189
336
|
const symbol = currencySymbols[currency?.toUpperCase()] || currency || '';
|
|
190
|
-
return `${symbol}${amount
|
|
337
|
+
return `${symbol}${Number(amount || 0).toFixed(2)}`;
|
|
338
|
+
},
|
|
339
|
+
formatSignedCurrency(amount, currency) {
|
|
340
|
+
const numericAmount = Number(amount || 0);
|
|
341
|
+
return numericAmount > 0 ? `-${this.formatCurrency(numericAmount, currency)}` : this.formatCurrency(numericAmount, currency);
|
|
191
342
|
},
|
|
192
343
|
formatInterval(interval) {
|
|
193
344
|
return interval ? `per ${interval}` : '';
|
|
@@ -197,19 +348,71 @@ export default {
|
|
|
197
348
|
const date = new Date(timestamp * 1000);
|
|
198
349
|
return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' });
|
|
199
350
|
},
|
|
351
|
+
formatDateValue(value) {
|
|
352
|
+
if (!value) return '';
|
|
353
|
+
const date = new Date(value);
|
|
354
|
+
if (Number.isNaN(date.getTime())) return '';
|
|
355
|
+
return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' });
|
|
356
|
+
},
|
|
357
|
+
formatSource(source) {
|
|
358
|
+
if (!source) return 'Unknown source';
|
|
359
|
+
if (source.includes('myshopify.com')) return 'Shopify';
|
|
360
|
+
if (source === 'tapstack.erp') return 'Tapstack';
|
|
361
|
+
return source;
|
|
362
|
+
},
|
|
200
363
|
getStatusText(status) {
|
|
201
364
|
const statusMap = {
|
|
202
365
|
'trialing': 'Trial',
|
|
203
366
|
'active': 'Active',
|
|
204
367
|
'past_due': 'Past Due',
|
|
205
368
|
'canceled': 'Canceled',
|
|
206
|
-
'unpaid': 'Unpaid'
|
|
369
|
+
'unpaid': 'Unpaid',
|
|
370
|
+
'expired': 'Expired'
|
|
207
371
|
};
|
|
208
372
|
return statusMap[status] || status;
|
|
209
373
|
},
|
|
210
374
|
getStatusClass(status) {
|
|
211
375
|
return `status-${status}`;
|
|
212
376
|
},
|
|
377
|
+
getOrderStateClass(status) {
|
|
378
|
+
return `status-${String(status || 'created').toLowerCase().replace(/_/g, '-')}`;
|
|
379
|
+
},
|
|
380
|
+
getPaymentStatusClass(status) {
|
|
381
|
+
return `status-${String(status || '').toLowerCase().replace(/_/g, '-')}`;
|
|
382
|
+
},
|
|
383
|
+
getFulfillmentStatusClass(status) {
|
|
384
|
+
return `status-${String(status || '').toLowerCase().replace(/_/g, '-')}`;
|
|
385
|
+
},
|
|
386
|
+
formatOrderState(status, isDraft, canceled) {
|
|
387
|
+
if (canceled) return 'Canceled';
|
|
388
|
+
if (isDraft) return 'Draft';
|
|
389
|
+
return this.formatStatusLabel(status || 'created');
|
|
390
|
+
},
|
|
391
|
+
formatPaymentStatus(status) {
|
|
392
|
+
return this.formatStatusLabel(status);
|
|
393
|
+
},
|
|
394
|
+
formatFulfillmentStatus(status) {
|
|
395
|
+
return this.formatStatusLabel(status);
|
|
396
|
+
},
|
|
397
|
+
formatStatusLabel(status) {
|
|
398
|
+
return String(status || '')
|
|
399
|
+
.replace(/_/g, ' ')
|
|
400
|
+
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
401
|
+
},
|
|
402
|
+
formatAddress(address) {
|
|
403
|
+
if (!address) return '';
|
|
404
|
+
return [
|
|
405
|
+
address.name,
|
|
406
|
+
address.email,
|
|
407
|
+
address.company,
|
|
408
|
+
address.line1,
|
|
409
|
+
address.line2,
|
|
410
|
+
[address.postalCode, address.city].filter(Boolean).join(' '),
|
|
411
|
+
address.state,
|
|
412
|
+
address.country,
|
|
413
|
+
address.phone
|
|
414
|
+
].filter(Boolean).join(', ');
|
|
415
|
+
},
|
|
213
416
|
openCancelModal(subscription) {
|
|
214
417
|
this.selectedSubscription = subscription;
|
|
215
418
|
this.showCancelModal = true;
|
|
@@ -257,8 +460,7 @@ export default {
|
|
|
257
460
|
});
|
|
258
461
|
|
|
259
462
|
// Refresh account settings to get updated billing info
|
|
260
|
-
await this.
|
|
261
|
-
this.loadSubscriptions();
|
|
463
|
+
await this.refreshBillingData();
|
|
262
464
|
|
|
263
465
|
// Close modal
|
|
264
466
|
this.closeCancelModal();
|
|
@@ -274,11 +476,394 @@ export default {
|
|
|
274
476
|
} finally {
|
|
275
477
|
this.loading = false;
|
|
276
478
|
}
|
|
479
|
+
},
|
|
480
|
+
async resubscribe(sub) {
|
|
481
|
+
if (!sub?.subscriptionId) return;
|
|
482
|
+
if (sub.paymentGateway === 'revenuecat') {
|
|
483
|
+
EventBus.$emit('ssoEvent', {
|
|
484
|
+
name: 'resubscribe',
|
|
485
|
+
data: { subscriptionId: sub.subscriptionId, paymentGateway: sub.paymentGateway }
|
|
486
|
+
});
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
this.loading = true;
|
|
490
|
+
try {
|
|
491
|
+
const response = await api(false, 'v2').post('checkout/resubscribe/' + sub.subscriptionId);
|
|
492
|
+
if (response.data.success) {
|
|
493
|
+
EventBus.$emit('showToast', { type: 'success', message: response.data.message || 'Subscription reactivated successfully' });
|
|
494
|
+
await this.refreshBillingData();
|
|
495
|
+
if (response.data.clientSecret) {
|
|
496
|
+
EventBus.$emit('ssoEvent', { name: 'resubscribePaymentRequired', data: { clientSecret: response.data.clientSecret } });
|
|
497
|
+
}
|
|
498
|
+
} else {
|
|
499
|
+
throw new Error(response.data.message || 'Failed to resubscribe');
|
|
500
|
+
}
|
|
501
|
+
} catch (error) {
|
|
502
|
+
EventBus.$emit('showToast', {
|
|
503
|
+
type: 'error',
|
|
504
|
+
message: error.response?.data?.error || error.response?.data?.message || 'Failed to resubscribe. Please try again.'
|
|
505
|
+
});
|
|
506
|
+
} finally {
|
|
507
|
+
this.loading = false;
|
|
508
|
+
}
|
|
509
|
+
},
|
|
510
|
+
async retryPayment(sub) {
|
|
511
|
+
if (!sub?.subscriptionId) return;
|
|
512
|
+
this.loading = true;
|
|
513
|
+
try {
|
|
514
|
+
const response = await api(false, 'v2').post('checkout/retry-payment/' + sub.subscriptionId);
|
|
515
|
+
const data = response.data;
|
|
516
|
+
if (data.paid) {
|
|
517
|
+
EventBus.$emit('showToast', { type: 'success', message: data.message || 'Payment successful' });
|
|
518
|
+
await this.refreshBillingData();
|
|
519
|
+
} else if (data.hostedInvoiceUrl) {
|
|
520
|
+
window.open(data.hostedInvoiceUrl, '_blank', 'noopener,noreferrer');
|
|
521
|
+
EventBus.$emit('showToast', { type: 'info', message: data.message || 'Please complete payment in the opened window.' });
|
|
522
|
+
} else {
|
|
523
|
+
throw new Error(data.message || 'Payment could not be processed');
|
|
524
|
+
}
|
|
525
|
+
} catch (error) {
|
|
526
|
+
EventBus.$emit('showToast', {
|
|
527
|
+
type: 'error',
|
|
528
|
+
message: error.response?.data?.error || error.response?.data?.message || 'Failed to retry payment. Please try again.'
|
|
529
|
+
});
|
|
530
|
+
} finally {
|
|
531
|
+
this.loading = false;
|
|
532
|
+
}
|
|
277
533
|
}
|
|
278
534
|
}
|
|
279
535
|
};
|
|
280
536
|
</script>
|
|
281
537
|
|
|
282
|
-
<style>
|
|
538
|
+
<style scoped>
|
|
539
|
+
.billing-section {
|
|
540
|
+
margin-top: 24px;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
.section-heading {
|
|
544
|
+
display: flex;
|
|
545
|
+
align-items: center;
|
|
546
|
+
justify-content: space-between;
|
|
547
|
+
margin-bottom: 14px;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
.section-title {
|
|
551
|
+
margin: 0;
|
|
552
|
+
font-size: 18px;
|
|
553
|
+
font-weight: 600;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
.section-caption {
|
|
557
|
+
color: #7d7d7d;
|
|
558
|
+
font-size: 13px;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
.subscription-card {
|
|
562
|
+
background: #ffffff;
|
|
563
|
+
border: 1px solid #ececec;
|
|
564
|
+
border-radius: 18px;
|
|
565
|
+
padding: 18px;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
.subscription-header {
|
|
569
|
+
display: flex;
|
|
570
|
+
justify-content: space-between;
|
|
571
|
+
gap: 16px;
|
|
572
|
+
align-items: flex-start;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
.subscription-info {
|
|
576
|
+
display: flex;
|
|
577
|
+
flex-direction: column;
|
|
578
|
+
gap: 8px;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
.subscription-name {
|
|
582
|
+
margin: 0;
|
|
583
|
+
font-size: 18px;
|
|
584
|
+
font-weight: 600;
|
|
585
|
+
color: #111111;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
.subscription-status {
|
|
589
|
+
display: inline-flex;
|
|
590
|
+
align-items: center;
|
|
591
|
+
width: fit-content;
|
|
592
|
+
padding: 6px 10px;
|
|
593
|
+
border-radius: 999px;
|
|
594
|
+
font-size: 12px;
|
|
595
|
+
font-weight: 600;
|
|
596
|
+
background: #f1f5f9;
|
|
597
|
+
color: #334155;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
.subscription-details {
|
|
601
|
+
margin-top: 16px;
|
|
602
|
+
display: grid;
|
|
603
|
+
gap: 10px;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
.detail-row {
|
|
607
|
+
display: flex;
|
|
608
|
+
justify-content: space-between;
|
|
609
|
+
gap: 16px;
|
|
610
|
+
align-items: flex-start;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
.detail-label {
|
|
614
|
+
color: #6b7280;
|
|
615
|
+
font-size: 14px;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
.detail-value {
|
|
619
|
+
color: #111111;
|
|
620
|
+
font-size: 14px;
|
|
621
|
+
text-align: right;
|
|
622
|
+
word-break: break-word;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
.subscription-actions {
|
|
626
|
+
margin-top: 18px;
|
|
627
|
+
display: flex;
|
|
628
|
+
flex-wrap: wrap;
|
|
629
|
+
gap: 10px;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
.cancel-button,
|
|
633
|
+
.resubscribe-button,
|
|
634
|
+
.pay-now-button,
|
|
635
|
+
.button-secondary,
|
|
636
|
+
.button-danger {
|
|
637
|
+
border: 0;
|
|
638
|
+
border-radius: 999px;
|
|
639
|
+
padding: 10px 16px;
|
|
640
|
+
font-size: 14px;
|
|
641
|
+
font-weight: 600;
|
|
642
|
+
cursor: pointer;
|
|
643
|
+
transition: opacity 0.2s ease;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
.cancel-button:disabled,
|
|
647
|
+
.resubscribe-button:disabled,
|
|
648
|
+
.pay-now-button:disabled,
|
|
649
|
+
.button-secondary:disabled,
|
|
650
|
+
.button-danger:disabled {
|
|
651
|
+
opacity: 0.6;
|
|
652
|
+
cursor: not-allowed;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
.cancel-button,
|
|
656
|
+
.button-secondary {
|
|
657
|
+
background: #111111;
|
|
658
|
+
color: #ffffff;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
.resubscribe-button {
|
|
662
|
+
background: #111111;
|
|
663
|
+
color: #ffffff;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
.pay-now-button,
|
|
667
|
+
.button-danger {
|
|
668
|
+
background: #0f766e;
|
|
669
|
+
color: #ffffff;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
.button-link {
|
|
673
|
+
text-decoration: none;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
.order-card {
|
|
677
|
+
background: #fafafa;
|
|
678
|
+
}
|
|
283
679
|
|
|
680
|
+
.order-header {
|
|
681
|
+
align-items: center;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
.order-meta {
|
|
685
|
+
display: flex;
|
|
686
|
+
flex-wrap: wrap;
|
|
687
|
+
gap: 8px;
|
|
688
|
+
color: #6b7280;
|
|
689
|
+
font-size: 13px;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
.order-source {
|
|
693
|
+
text-transform: capitalize;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
.order-badges {
|
|
697
|
+
display: flex;
|
|
698
|
+
flex-wrap: wrap;
|
|
699
|
+
justify-content: flex-end;
|
|
700
|
+
gap: 8px;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
.order-grid {
|
|
704
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
.order-items,
|
|
708
|
+
.order-addresses {
|
|
709
|
+
margin-top: 18px;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
.subsection-title {
|
|
713
|
+
margin: 0 0 10px 0;
|
|
714
|
+
font-size: 14px;
|
|
715
|
+
font-weight: 600;
|
|
716
|
+
color: #111111;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
.order-item-row {
|
|
720
|
+
display: flex;
|
|
721
|
+
justify-content: space-between;
|
|
722
|
+
gap: 16px;
|
|
723
|
+
padding: 8px 0;
|
|
724
|
+
border-top: 1px solid #ececec;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
.order-item-row:first-of-type {
|
|
728
|
+
border-top: 0;
|
|
729
|
+
padding-top: 0;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
.order-addresses {
|
|
733
|
+
display: grid;
|
|
734
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
735
|
+
gap: 14px;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
.address-card {
|
|
739
|
+
background: #ffffff;
|
|
740
|
+
border: 1px solid #ececec;
|
|
741
|
+
border-radius: 14px;
|
|
742
|
+
padding: 14px;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
.address-line {
|
|
746
|
+
margin: 0;
|
|
747
|
+
color: #374151;
|
|
748
|
+
font-size: 14px;
|
|
749
|
+
line-height: 1.5;
|
|
750
|
+
word-break: break-word;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
.modal-overlay {
|
|
754
|
+
position: fixed;
|
|
755
|
+
inset: 0;
|
|
756
|
+
background: rgba(17, 17, 17, 0.5);
|
|
757
|
+
display: flex;
|
|
758
|
+
align-items: center;
|
|
759
|
+
justify-content: center;
|
|
760
|
+
z-index: 20;
|
|
761
|
+
padding: 20px;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
.modal-content {
|
|
765
|
+
width: min(100%, 520px);
|
|
766
|
+
background: #ffffff;
|
|
767
|
+
border-radius: 20px;
|
|
768
|
+
padding: 20px;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
.modal-header,
|
|
772
|
+
.modal-footer {
|
|
773
|
+
display: flex;
|
|
774
|
+
align-items: center;
|
|
775
|
+
justify-content: space-between;
|
|
776
|
+
gap: 12px;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
.modal-header {
|
|
780
|
+
margin-bottom: 16px;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
.modal-body {
|
|
784
|
+
display: grid;
|
|
785
|
+
gap: 16px;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
.modal-text,
|
|
789
|
+
.feedback-label {
|
|
790
|
+
color: #374151;
|
|
791
|
+
font-size: 14px;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
.feedback-textarea {
|
|
795
|
+
width: 100%;
|
|
796
|
+
border: 1px solid #d1d5db;
|
|
797
|
+
border-radius: 14px;
|
|
798
|
+
padding: 12px;
|
|
799
|
+
font: inherit;
|
|
800
|
+
resize: vertical;
|
|
801
|
+
min-height: 110px;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
.close-button {
|
|
805
|
+
background: transparent;
|
|
806
|
+
border: 0;
|
|
807
|
+
font-size: 26px;
|
|
808
|
+
line-height: 1;
|
|
809
|
+
cursor: pointer;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
.status-active,
|
|
813
|
+
.status-paid,
|
|
814
|
+
.status-success,
|
|
815
|
+
.status-created,
|
|
816
|
+
.status-submitted,
|
|
817
|
+
.status-fulfilled {
|
|
818
|
+
background: #dcfce7;
|
|
819
|
+
color: #166534;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
.status-trialing,
|
|
823
|
+
.status-open,
|
|
824
|
+
.status-unfulfilled,
|
|
825
|
+
.status-processing,
|
|
826
|
+
.status-draft {
|
|
827
|
+
background: #fef3c7;
|
|
828
|
+
color: #92400e;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
.status-canceled,
|
|
832
|
+
.status-cancelled,
|
|
833
|
+
.status-expired,
|
|
834
|
+
.status-unpaid,
|
|
835
|
+
.status-past-due,
|
|
836
|
+
.status-void,
|
|
837
|
+
.status-failure,
|
|
838
|
+
.status-failed {
|
|
839
|
+
background: #fee2e2;
|
|
840
|
+
color: #991b1b;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
@media (max-width: 720px) {
|
|
844
|
+
.subscription-header,
|
|
845
|
+
.modal-header,
|
|
846
|
+
.modal-footer {
|
|
847
|
+
flex-direction: column;
|
|
848
|
+
align-items: stretch;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
.order-badges {
|
|
852
|
+
justify-content: flex-start;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
.order-grid,
|
|
856
|
+
.order-addresses {
|
|
857
|
+
grid-template-columns: 1fr;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
.detail-row,
|
|
861
|
+
.order-item-row {
|
|
862
|
+
flex-direction: column;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
.detail-value {
|
|
866
|
+
text-align: left;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
284
869
|
</style>
|