@tapni/auth 1.0.4 → 1.0.6-3.dev
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 +2 -0
- package/dist/.vite/manifest.json +23 -40
- package/dist/.well-known/assetlinks.json +10 -12
- package/dist/.well-known/microsoft-identity-association.json +5 -5
- package/dist/Apps-XNA4_3B4.js +109 -0
- package/dist/Billing-Br0-fHed.js +256 -0
- package/dist/CustomApp-CrlbYDOm.js +83 -0
- package/dist/QR-Bvqb60-E.js +41 -0
- package/dist/TapniAuth.es.js +1 -1
- package/dist/TapniAuth.umd.js +49 -23
- package/dist/{install-BzPu9V_c.js → install-7FOVy8Ol.js} +6328 -4767
- package/dist/site.webmanifest +11 -1
- package/dist/style.css +1 -1
- package/dist/{web-IFGkBi0t.js → web-UrTMimK1.js} +2 -2
- package/package.json +65 -55
- package/src/.prettierrc.json +16 -0
- package/src/App.vue +329 -259
- package/src/eslint.config.js +15 -0
- package/src/index.js +4 -0
- package/src/install.js +9 -10
- package/src/main.js +54 -58
- package/src/mixins/apple.mixin.js +56 -54
- package/src/mixins/auth.mixin.js +3 -2
- package/src/mixins/global.mixin.js +3 -3
- package/src/mixins/google.mixin.js +53 -54
- package/src/mixins/microsoft.mixin.js +2 -8
- package/src/mixins/okta.mixin.js +2 -2
- package/src/mixins/qr-auth.mixin.js +111 -107
- package/src/mixins/saml.mixin.js +82 -45
- package/src/router/index.js +6 -6
- package/src/routes.js +7 -1
- package/src/services/Api.js +56 -58
- package/src/services/AuthService.js +13 -9
- package/src/services/CompanyService.js +10 -10
- package/src/services/DeviceService.js +3 -3
- package/src/services/MetadataService.js +10 -0
- package/src/services/UserService.js +48 -45
- package/src/services/UtilService.js +317 -225
- package/src/store/auth.js +485 -549
- package/src/store/constants.js +2 -2
- package/src/store/event-bus.js +22 -22
- package/src/store/locales/cn.js +476 -458
- package/src/store/locales/de.js +478 -517
- package/src/store/locales/en.js +454 -512
- package/src/store/locales/es.js +477 -524
- package/src/store/locales/fr.js +477 -516
- package/src/store/locales/it.js +477 -514
- package/src/store/locales/ja.js +488 -0
- package/src/store/locales/kr.js +477 -491
- package/src/store/locales/lang.js +51 -43
- package/src/store/locales/pt.js +488 -0
- package/src/store/locales/sr.js +477 -492
- package/src/store/locales/tr.js +477 -487
- package/src/store/store.js +6 -6
- package/src/views/Account.vue +36 -8
- package/src/views/Apps.vue +17 -30
- package/src/views/Billing.vue +538 -0
- package/src/views/Callback.vue +36 -33
- package/src/views/CustomApp.vue +14 -39
- package/src/views/General.vue +151 -184
- package/src/views/Login.vue +173 -161
- package/src/views/Register.vue +2 -12
- package/src/views/Reset.vue +132 -135
- package/src/views/Security.vue +13 -7
- package/src/views/Verify.vue +153 -151
- package/src/views/Welcome.vue +85 -71
- package/dist/Account-zep_QUuI.js +0 -153
- package/dist/Apps-B9XB7Z7q.js +0 -112
- package/dist/CustomApp-BzUsyycz.js +0 -112
- package/dist/General-DkcdjPYQ.js +0 -477
- package/dist/QR-PW6cAG5j.js +0 -41
- package/dist/index.css +0 -193
- package/dist/web-AXRKjAOB.js +0 -92
- package/src/components/DELETE_Language.vue +0 -168
- package/src/components/DELETE_LinkIcon.vue +0 -288
- package/src/components/DELETE_ModalOverlay.vue +0 -68
- package/src/components/DELETE_OTP.vue +0 -105
- package/src/components/SSO.vue +0 -126
- package/src/components/SSOPick.vue +0 -166
- package/src/mixins/DELETE_mfa-auth.mixin.js +0 -53
- package/src/mixins/facebook.mixin.js +0 -78
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="page-login content-boxed content-boxed-padding">
|
|
3
|
+
<a v-if="isModal" @click="close" class="color-black pull-right pointer" style="margin-top: 0px; position: absolute; right: -20px; text-align: right;">
|
|
4
|
+
<i class="font-17 color-black">
|
|
5
|
+
<img src="https://cdn.tapni.co/icons/down-arrow.png" class="responsive-image" style="width: 35%;"/>
|
|
6
|
+
</i>
|
|
7
|
+
</a>
|
|
8
|
+
<div class="subheaderContainer full-top">
|
|
9
|
+
<!-- Back Button -->
|
|
10
|
+
<router-link v-if="!isModal" to="/account" class="button gray-button pointer left-button">
|
|
11
|
+
<img
|
|
12
|
+
:src="getIcon('arrow-gray-right.svg')"
|
|
13
|
+
height="20"
|
|
14
|
+
width="20"
|
|
15
|
+
class="btn-icon"
|
|
16
|
+
style="rotate: 180deg;"
|
|
17
|
+
/>
|
|
18
|
+
</router-link>
|
|
19
|
+
|
|
20
|
+
<!-- Centered Text -->
|
|
21
|
+
<h2 class="center-text" style="flex-grow: 1; text-align: center; margin: 0; font-weight: 600;">
|
|
22
|
+
{{ssoLang[appLanguage].billing }}
|
|
23
|
+
</h2>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<div class="container left-text">
|
|
27
|
+
<p class="center-text">{{ssoLang[appLanguage].billing_p }}</p>
|
|
28
|
+
|
|
29
|
+
<div class="full-top">
|
|
30
|
+
<!-- No subscriptions message -->
|
|
31
|
+
<div v-if="subscriptions.length === 0" class="no-subscriptions center-text full-top">
|
|
32
|
+
<p class="gray-text">There are no active subscriptions at this point.</p>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<!-- Subscriptions list -->
|
|
36
|
+
<div v-else>
|
|
37
|
+
<div v-for="sub in subscriptions" :key="sub.subscriptionId" class="subscription-card half-bottom">
|
|
38
|
+
<div class="subscription-header">
|
|
39
|
+
<div class="subscription-info">
|
|
40
|
+
<h3 class="subscription-name">{{ sub.subscriptionName }}</h3>
|
|
41
|
+
<span class="subscription-status" :class="getStatusClass(sub.status)">
|
|
42
|
+
{{ getStatusText(sub.status) }}
|
|
43
|
+
</span>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<div class="subscription-details">
|
|
48
|
+
<div class="detail-row">
|
|
49
|
+
<span class="detail-label">Amount:</span>
|
|
50
|
+
<span class="detail-value">{{ formatCurrency(sub.amount, sub.currency) }}</span>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="detail-row">
|
|
53
|
+
<span class="detail-label">Billing:</span>
|
|
54
|
+
<span class="detail-value">{{ formatInterval(sub.interval) }}</span>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="detail-row">
|
|
57
|
+
<span class="detail-label">Licenses:</span>
|
|
58
|
+
<span class="detail-value">{{ sub.licenses }}</span>
|
|
59
|
+
</div>
|
|
60
|
+
<div v-if="sub.isTrial" class="detail-row">
|
|
61
|
+
<span class="detail-label">Trial Ends:</span>
|
|
62
|
+
<span class="detail-value">{{ formatDate(sub.trialEnd) }}</span>
|
|
63
|
+
</div>
|
|
64
|
+
<div v-else-if="sub.endDate" class="detail-row">
|
|
65
|
+
<span class="detail-label">Next Billing:</span>
|
|
66
|
+
<span class="detail-value">{{ formatDate(sub.endDate) }}</span>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<div class="subscription-actions" v-if="sub.status !== 'canceled'">
|
|
71
|
+
<button
|
|
72
|
+
@click="openCancelModal(sub)"
|
|
73
|
+
class="cancel-button"
|
|
74
|
+
:disabled="loading"
|
|
75
|
+
>
|
|
76
|
+
{{ loading ? 'Processing...' : 'Cancel Subscription' }}
|
|
77
|
+
</button>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<!-- Cancel Confirmation Modal -->
|
|
85
|
+
<div v-if="showCancelModal" class="modal-overlay" @click.self="closeCancelModal">
|
|
86
|
+
<div class="modal-content">
|
|
87
|
+
<div class="modal-header">
|
|
88
|
+
<h3>Cancel Subscription</h3>
|
|
89
|
+
<button @click="closeCancelModal" class="close-button">×</button>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<div class="modal-body">
|
|
93
|
+
<p class="modal-text">
|
|
94
|
+
Are you sure you want to cancel your subscription to <b>{{ selectedSubscription?.subscriptionName }}</b>?
|
|
95
|
+
</p>
|
|
96
|
+
|
|
97
|
+
<div class="feedback-section">
|
|
98
|
+
<label class="feedback-label">We'd love to know why you're canceling (optional):</label>
|
|
99
|
+
<textarea
|
|
100
|
+
v-model="cancelFeedback"
|
|
101
|
+
class="feedback-textarea"
|
|
102
|
+
placeholder="Your feedback helps us improve our service..."
|
|
103
|
+
rows="4"
|
|
104
|
+
></textarea>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<div class="modal-footer">
|
|
109
|
+
<button @click="closeCancelModal" class="button-secondary">
|
|
110
|
+
Keep Subscription
|
|
111
|
+
</button>
|
|
112
|
+
<button
|
|
113
|
+
@click="confirmCancel"
|
|
114
|
+
class="button-danger"
|
|
115
|
+
:disabled="loading"
|
|
116
|
+
>
|
|
117
|
+
{{ loading ? 'Canceling...' : 'Confirm Cancellation' }}
|
|
118
|
+
</button>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
</template>
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
<script>
|
|
127
|
+
import AuthMixin from "../mixins/auth.mixin";
|
|
128
|
+
import {EventBus} from "@/store/event-bus.js";
|
|
129
|
+
import api from "@/services/Api.js";
|
|
130
|
+
|
|
131
|
+
export default {
|
|
132
|
+
name: "AuthBilling",
|
|
133
|
+
mixins: [AuthMixin],
|
|
134
|
+
props: {
|
|
135
|
+
payload: {
|
|
136
|
+
type: Object,
|
|
137
|
+
default: {}
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
data () {
|
|
141
|
+
return {
|
|
142
|
+
loading: false,
|
|
143
|
+
subscriptions: [],
|
|
144
|
+
showCancelModal: false,
|
|
145
|
+
selectedSubscription: null,
|
|
146
|
+
cancelFeedback: ''
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
async mounted() {
|
|
150
|
+
if (!this.isLoggedIn) this.$router.push('/login');
|
|
151
|
+
await this.getAccountSettings();
|
|
152
|
+
this.loadSubscriptions();
|
|
153
|
+
},
|
|
154
|
+
methods: {
|
|
155
|
+
close () {
|
|
156
|
+
EventBus.$emit('ssoEvent', {name: 'toggleAuthModal', data: true})
|
|
157
|
+
},
|
|
158
|
+
loadSubscriptions() {
|
|
159
|
+
// Extract subscriptions from account.billing
|
|
160
|
+
if (this.account.billing) {
|
|
161
|
+
const billing = this.account.billing;
|
|
162
|
+
const subs = [];
|
|
163
|
+
|
|
164
|
+
// Loop through billing object to find subscription objects
|
|
165
|
+
for (const key in billing) {
|
|
166
|
+
if (key !== 'region' && key !== 'currency' && key !== 'paymentProfiles' && typeof billing[key] === 'object' && billing[key].subscriptionId) {
|
|
167
|
+
subs.push(billing[key]);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
this.subscriptions = subs;
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
formatCurrency(amount, currency) {
|
|
175
|
+
const currencySymbols = {
|
|
176
|
+
'EUR': '€',
|
|
177
|
+
'USD': '$',
|
|
178
|
+
'GBP': '£'
|
|
179
|
+
};
|
|
180
|
+
const symbol = currencySymbols[currency?.toUpperCase()] || currency || '';
|
|
181
|
+
return `${symbol}${amount?.toFixed(2) || '0.00'}`;
|
|
182
|
+
},
|
|
183
|
+
formatInterval(interval) {
|
|
184
|
+
return interval ? `per ${interval}` : '';
|
|
185
|
+
},
|
|
186
|
+
formatDate(timestamp) {
|
|
187
|
+
if (!timestamp) return '';
|
|
188
|
+
const date = new Date(timestamp * 1000);
|
|
189
|
+
return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' });
|
|
190
|
+
},
|
|
191
|
+
getStatusText(status) {
|
|
192
|
+
const statusMap = {
|
|
193
|
+
'trialing': 'Trial',
|
|
194
|
+
'active': 'Active',
|
|
195
|
+
'past_due': 'Past Due',
|
|
196
|
+
'canceled': 'Canceled',
|
|
197
|
+
'unpaid': 'Unpaid'
|
|
198
|
+
};
|
|
199
|
+
return statusMap[status] || status;
|
|
200
|
+
},
|
|
201
|
+
getStatusClass(status) {
|
|
202
|
+
return `status-${status}`;
|
|
203
|
+
},
|
|
204
|
+
openCancelModal(subscription) {
|
|
205
|
+
this.selectedSubscription = subscription;
|
|
206
|
+
this.showCancelModal = true;
|
|
207
|
+
this.cancelFeedback = '';
|
|
208
|
+
},
|
|
209
|
+
closeCancelModal() {
|
|
210
|
+
this.showCancelModal = false;
|
|
211
|
+
this.selectedSubscription = null;
|
|
212
|
+
this.cancelFeedback = '';
|
|
213
|
+
},
|
|
214
|
+
async confirmCancel() {
|
|
215
|
+
if (!this.selectedSubscription) return;
|
|
216
|
+
|
|
217
|
+
// Check if this is a RevenueCat in-app purchase subscription
|
|
218
|
+
if (this.selectedSubscription.paymentGateway === 'revenuecat') {
|
|
219
|
+
// Fire event to redirect user to RevenueCat management URL
|
|
220
|
+
EventBus.$emit('ssoEvent', {
|
|
221
|
+
name: 'subscriptionCancelled',
|
|
222
|
+
data: {
|
|
223
|
+
subscriptionId: this.selectedSubscription.subscriptionId,
|
|
224
|
+
paymentGateway: this.selectedSubscription.paymentGateway,
|
|
225
|
+
feedback: this.cancelFeedback
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// Close the modal
|
|
230
|
+
this.closeCancelModal();
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// For other payment gateways (like Stripe), proceed with API cancellation
|
|
235
|
+
this.loading = true;
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
// Send cancel request to backend
|
|
239
|
+
const response = await api(false, 'v2').post('checkout/cancel/' + this.selectedSubscription.subscriptionId, {
|
|
240
|
+
feedback: this.cancelFeedback
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
if (response.data.success) {
|
|
244
|
+
// Show success message
|
|
245
|
+
EventBus.$emit('showToast', {
|
|
246
|
+
type: 'success',
|
|
247
|
+
message: 'Subscription canceled successfully'
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// Refresh account settings to get updated billing info
|
|
251
|
+
await this.getAccountSettings();
|
|
252
|
+
this.loadSubscriptions();
|
|
253
|
+
|
|
254
|
+
// Close modal
|
|
255
|
+
this.closeCancelModal();
|
|
256
|
+
} else {
|
|
257
|
+
throw new Error(response.data.message || 'Failed to cancel subscription');
|
|
258
|
+
}
|
|
259
|
+
} catch (error) {
|
|
260
|
+
console.error('Error canceling subscription:', error);
|
|
261
|
+
EventBus.$emit('showToast', {
|
|
262
|
+
type: 'error',
|
|
263
|
+
message: error.response?.data?.message || 'Failed to cancel subscription. Please try again.'
|
|
264
|
+
});
|
|
265
|
+
} finally {
|
|
266
|
+
this.loading = false;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
</script>
|
|
272
|
+
|
|
273
|
+
<style scoped>
|
|
274
|
+
.withBackground {
|
|
275
|
+
height: 50px;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.no-subscriptions {
|
|
279
|
+
padding: 40px 20px;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.gray-text {
|
|
283
|
+
color: #666;
|
|
284
|
+
font-size: 16px;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.subscription-card {
|
|
288
|
+
background: #fff;
|
|
289
|
+
border: 1px solid #e0e0e0;
|
|
290
|
+
border-radius: 12px;
|
|
291
|
+
padding: 20px;
|
|
292
|
+
margin-bottom: 16px;
|
|
293
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.subscription-header {
|
|
297
|
+
display: flex;
|
|
298
|
+
justify-content: space-between;
|
|
299
|
+
align-items: flex-start;
|
|
300
|
+
margin-bottom: 16px;
|
|
301
|
+
padding-bottom: 16px;
|
|
302
|
+
border-bottom: 1px solid #f0f0f0;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.subscription-info {
|
|
306
|
+
flex: 1;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.subscription-name {
|
|
310
|
+
font-size: 20px;
|
|
311
|
+
font-weight: 600;
|
|
312
|
+
margin: 0 0 8px 0;
|
|
313
|
+
color: #000;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.subscription-status {
|
|
317
|
+
display: inline-block;
|
|
318
|
+
padding: 4px 12px;
|
|
319
|
+
border-radius: 12px;
|
|
320
|
+
font-size: 12px;
|
|
321
|
+
font-weight: 600;
|
|
322
|
+
text-transform: uppercase;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.status-trialing {
|
|
326
|
+
background: #e3f2fd;
|
|
327
|
+
color: #1976d2;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.status-active {
|
|
331
|
+
background: #e8f5e9;
|
|
332
|
+
color: #388e3c;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.status-past_due {
|
|
336
|
+
background: #fff3e0;
|
|
337
|
+
color: #f57c00;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.status-canceled {
|
|
341
|
+
background: #ffebee;
|
|
342
|
+
color: #d32f2f;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.subscription-details {
|
|
346
|
+
margin-bottom: 16px;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.detail-row {
|
|
350
|
+
display: flex;
|
|
351
|
+
justify-content: space-between;
|
|
352
|
+
padding: 8px 0;
|
|
353
|
+
font-size: 15px;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
.detail-label {
|
|
357
|
+
color: #666;
|
|
358
|
+
font-weight: 500;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.detail-value {
|
|
362
|
+
color: #000;
|
|
363
|
+
font-weight: 600;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.subscription-actions {
|
|
367
|
+
display: flex;
|
|
368
|
+
justify-content: flex-end;
|
|
369
|
+
padding-top: 16px;
|
|
370
|
+
border-top: 1px solid #f0f0f0;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.cancel-button {
|
|
374
|
+
padding: 10px 20px;
|
|
375
|
+
background: #fff;
|
|
376
|
+
color: #d32f2f;
|
|
377
|
+
border: 1px solid #d32f2f;
|
|
378
|
+
border-radius: 8px;
|
|
379
|
+
font-size: 14px;
|
|
380
|
+
font-weight: 600;
|
|
381
|
+
cursor: pointer;
|
|
382
|
+
transition: all 0.2s;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.cancel-button:hover:not(:disabled) {
|
|
386
|
+
background: #d32f2f;
|
|
387
|
+
color: #fff;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.cancel-button:disabled {
|
|
391
|
+
opacity: 0.5;
|
|
392
|
+
cursor: not-allowed;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/* Modal Styles */
|
|
396
|
+
.modal-overlay {
|
|
397
|
+
position: fixed;
|
|
398
|
+
top: 0;
|
|
399
|
+
left: 0;
|
|
400
|
+
right: 0;
|
|
401
|
+
bottom: 0;
|
|
402
|
+
background: rgba(0, 0, 0, 0.5);
|
|
403
|
+
display: flex;
|
|
404
|
+
align-items: center;
|
|
405
|
+
justify-content: center;
|
|
406
|
+
z-index: 9999;
|
|
407
|
+
padding: 20px;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.modal-content {
|
|
411
|
+
background: #fff;
|
|
412
|
+
border-radius: 16px;
|
|
413
|
+
width: 100%;
|
|
414
|
+
max-width: 500px;
|
|
415
|
+
max-height: 90vh;
|
|
416
|
+
overflow-y: auto;
|
|
417
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.modal-header {
|
|
421
|
+
display: flex;
|
|
422
|
+
justify-content: space-between;
|
|
423
|
+
align-items: center;
|
|
424
|
+
padding: 24px;
|
|
425
|
+
border-bottom: 1px solid #e0e0e0;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.modal-header h3 {
|
|
429
|
+
margin: 0;
|
|
430
|
+
font-size: 20px;
|
|
431
|
+
font-weight: 600;
|
|
432
|
+
color: #000;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.close-button {
|
|
436
|
+
background: none;
|
|
437
|
+
border: none;
|
|
438
|
+
font-size: 28px;
|
|
439
|
+
color: #666;
|
|
440
|
+
cursor: pointer;
|
|
441
|
+
padding: 0;
|
|
442
|
+
width: 32px;
|
|
443
|
+
height: 32px;
|
|
444
|
+
display: flex;
|
|
445
|
+
align-items: center;
|
|
446
|
+
justify-content: center;
|
|
447
|
+
border-radius: 50%;
|
|
448
|
+
transition: background 0.2s;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.close-button:hover {
|
|
452
|
+
background: #f0f0f0;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.modal-body {
|
|
456
|
+
padding: 24px;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.modal-text {
|
|
460
|
+
font-size: 16px;
|
|
461
|
+
line-height: 1.5;
|
|
462
|
+
color: #333;
|
|
463
|
+
margin-bottom: 24px;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.feedback-section {
|
|
467
|
+
margin-top: 20px;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.feedback-label {
|
|
471
|
+
display: block;
|
|
472
|
+
font-size: 14px;
|
|
473
|
+
font-weight: 600;
|
|
474
|
+
color: #333;
|
|
475
|
+
margin-bottom: 8px;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
.feedback-textarea {
|
|
479
|
+
width: 100%;
|
|
480
|
+
padding: 12px;
|
|
481
|
+
border: 1px solid #e0e0e0;
|
|
482
|
+
border-radius: 8px;
|
|
483
|
+
font-size: 14px;
|
|
484
|
+
font-family: inherit;
|
|
485
|
+
resize: vertical;
|
|
486
|
+
transition: border-color 0.2s;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.feedback-textarea:focus {
|
|
490
|
+
outline: none;
|
|
491
|
+
border-color: #1976d2;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.modal-footer {
|
|
495
|
+
display: flex;
|
|
496
|
+
gap: 12px;
|
|
497
|
+
padding: 20px 24px;
|
|
498
|
+
border-top: 1px solid #e0e0e0;
|
|
499
|
+
justify-content: flex-end;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.button-secondary {
|
|
503
|
+
padding: 12px 24px;
|
|
504
|
+
background: #fff;
|
|
505
|
+
color: #333;
|
|
506
|
+
border: 1px solid #e0e0e0;
|
|
507
|
+
border-radius: 8px;
|
|
508
|
+
font-size: 14px;
|
|
509
|
+
font-weight: 600;
|
|
510
|
+
cursor: pointer;
|
|
511
|
+
transition: all 0.2s;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
.button-secondary:hover {
|
|
515
|
+
background: #f5f5f5;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
.button-danger {
|
|
519
|
+
padding: 12px 24px;
|
|
520
|
+
background: #d32f2f;
|
|
521
|
+
color: #fff;
|
|
522
|
+
border: none;
|
|
523
|
+
border-radius: 8px;
|
|
524
|
+
font-size: 14px;
|
|
525
|
+
font-weight: 600;
|
|
526
|
+
cursor: pointer;
|
|
527
|
+
transition: all 0.2s;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
.button-danger:hover:not(:disabled) {
|
|
531
|
+
background: #b71c1c;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
.button-danger:disabled {
|
|
535
|
+
opacity: 0.5;
|
|
536
|
+
cursor: not-allowed;
|
|
537
|
+
}
|
|
538
|
+
</style>
|
package/src/views/Callback.vue
CHANGED
|
@@ -1,46 +1,49 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
<div class="page-login content-boxed content-boxed-padding">
|
|
3
|
+
<h5 v-if="($route.query.platform === 'android' || $route.query.platform === 'ios') && !isNative" style="text-align: center; width: 80%; margin: 0 auto; margin-top: 260px">
|
|
4
|
+
{{ ssoLang[this.appLanguage].you_will_be_redirected }}<br />
|
|
5
|
+
{{ ssoLang[this.appLanguage].if_redirect_not_directly }}, <span @click="inAppRedirect" style="color: blue; cursor: pointer">{{ ssoLang[this.appLanguage].click_here }}</span
|
|
6
|
+
>.
|
|
7
|
+
</h5>
|
|
8
|
+
<h4 v-else style="text-align: center; width: 80%; margin: 0 auto; margin-top: 260px">{{ ssoLang[this.appLanguage].please_wait }}</h4>
|
|
9
|
+
</div>
|
|
8
10
|
</template>
|
|
9
11
|
|
|
10
12
|
<script>
|
|
11
13
|
/* eslint-disable */
|
|
12
|
-
import OktaMixin from
|
|
13
|
-
import SamlMixin from
|
|
14
|
-
import AuthMixin from
|
|
14
|
+
import OktaMixin from '../mixins/okta.mixin';
|
|
15
|
+
import SamlMixin from '../mixins/saml.mixin';
|
|
16
|
+
import AuthMixin from '../mixins/auth.mixin';
|
|
15
17
|
import { sanitizeUrl } from '@braintree/sanitize-url';
|
|
16
18
|
export default {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
name: 'AuthCallback',
|
|
20
|
+
mixins: [OktaMixin, SamlMixin, AuthMixin],
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
data() {
|
|
23
|
+
return {};
|
|
24
|
+
},
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
async mounted() {
|
|
27
|
-
if(this.$route.path === '/callback/auth') {
|
|
28
|
-
this.exchangeAuthCode({
|
|
29
|
-
code: this.$route.query.code
|
|
30
|
-
})
|
|
31
|
-
}
|
|
32
|
-
if(this.$route.path === '/callback/okta') return await this.handleOktaRedirect();
|
|
33
|
-
if(this.$route.path === '/callback/saml') return await this.handleSamlRedirect();
|
|
34
|
-
},
|
|
35
|
-
methods: {
|
|
36
|
-
inAppRedirect() {
|
|
37
|
-
let url = sanitizeUrl(location.href);
|
|
38
|
-
url = url.replace('http', 'tapni')
|
|
39
|
-
url = url.replace('https', 'tapni')
|
|
40
|
-
return window.open(url);
|
|
26
|
+
async mounted() {
|
|
27
|
+
if (this.$route.path === '/callback/redirect') {
|
|
28
|
+
return location.href = atob(this.$route.query.uri);
|
|
41
29
|
}
|
|
42
|
-
|
|
43
|
-
|
|
30
|
+
if (this.$route.path === '/callback/auth') {
|
|
31
|
+
this.exchangeAuthCode({
|
|
32
|
+
code: this.$route.query.code,
|
|
33
|
+
code_verifier: this.$route.query.code_verifier
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
if (this.$route.path === '/callback/okta') return await this.handleOktaRedirect();
|
|
37
|
+
if (this.$route.path === '/callback/saml') return await this.handleSamlRedirect();
|
|
38
|
+
},
|
|
39
|
+
methods: {
|
|
40
|
+
inAppRedirect() {
|
|
41
|
+
let url = sanitizeUrl(location.href);
|
|
42
|
+
url = url.replace('http', 'tapni');
|
|
43
|
+
url = url.replace('https', 'tapni');
|
|
44
|
+
return window.open(url);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
44
47
|
};
|
|
45
48
|
</script>
|
|
46
49
|
|
package/src/views/CustomApp.vue
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
</a>
|
|
8
8
|
<div class="subheaderContainer full-top">
|
|
9
9
|
<!-- Back Button -->
|
|
10
|
-
<router-link to="/
|
|
10
|
+
<router-link v-if="!isModal" to="/apps" class="button gray-button pointer left-button">
|
|
11
11
|
<img
|
|
12
12
|
:src="getIcon('arrow-gray-right.svg')"
|
|
13
13
|
height="20"
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
<!-- Centered Text -->
|
|
21
21
|
<h2 class="center-text" style="flex-grow: 1; text-align: center; margin: 0; font-weight: 600;">
|
|
22
|
-
{{
|
|
22
|
+
{{ app.t_apps_name }}
|
|
23
23
|
</h2>
|
|
24
24
|
</div>
|
|
25
25
|
|
|
@@ -27,43 +27,7 @@
|
|
|
27
27
|
<p class="center-text">{{ssoLang[appLanguage].apps_p }}</p>
|
|
28
28
|
|
|
29
29
|
<div class="full-top">
|
|
30
|
-
<h3 v-if="false" class="full-top bold small-bottom">{{ssoLang[appLanguage].apps_my}}</h3>
|
|
31
30
|
|
|
32
|
-
<router-link to="/apps/tapni" class="settingRow">
|
|
33
|
-
<div class="firstRow">
|
|
34
|
-
<img
|
|
35
|
-
:src="getIcon('tapni-logo-small-black.png')"
|
|
36
|
-
class="withBackground"
|
|
37
|
-
/>
|
|
38
|
-
<h4>Tapni</h4>
|
|
39
|
-
|
|
40
|
-
<img :src="getIcon('arrow-gray-right.svg')" />
|
|
41
|
-
</div>
|
|
42
|
-
</router-link>
|
|
43
|
-
|
|
44
|
-
<router-link to="/apps/mailsign" class="settingRow half-top">
|
|
45
|
-
<div class="firstRow">
|
|
46
|
-
<img
|
|
47
|
-
:src="getIcon('app-logo-mailsign.svg')"
|
|
48
|
-
class="withBackground"
|
|
49
|
-
/>
|
|
50
|
-
<h4>MailSign</h4>
|
|
51
|
-
|
|
52
|
-
<img :src="getIcon('arrow-gray-right.svg')" />
|
|
53
|
-
</div>
|
|
54
|
-
</router-link>
|
|
55
|
-
|
|
56
|
-
<router-link v-if="false" to="/apps/qrcodetoolkit" class="settingRow half-top">
|
|
57
|
-
<div class="firstRow">
|
|
58
|
-
<img
|
|
59
|
-
:src="getIcon('app-logo-qrcodetoolkit.svg')"
|
|
60
|
-
class="withBackground"
|
|
61
|
-
/>
|
|
62
|
-
<h4>QRCode Toolkit</h4>
|
|
63
|
-
|
|
64
|
-
<img :src="getIcon('arrow-gray-right.svg')" />
|
|
65
|
-
</div>
|
|
66
|
-
</router-link>
|
|
67
31
|
</div>
|
|
68
32
|
</div>
|
|
69
33
|
</div>
|
|
@@ -73,9 +37,10 @@
|
|
|
73
37
|
<script>
|
|
74
38
|
import AuthMixin from "../mixins/auth.mixin";
|
|
75
39
|
import {EventBus} from "@/store/event-bus.js";
|
|
40
|
+
import AuthService from "@/services/AuthService.js";
|
|
76
41
|
|
|
77
42
|
export default {
|
|
78
|
-
name: "
|
|
43
|
+
name: "AuthCustomApp",
|
|
79
44
|
mixins: [AuthMixin],
|
|
80
45
|
props: {
|
|
81
46
|
payload: {
|
|
@@ -86,10 +51,20 @@ export default {
|
|
|
86
51
|
data () {
|
|
87
52
|
return {
|
|
88
53
|
loading: false,
|
|
54
|
+
app: {}
|
|
89
55
|
}
|
|
90
56
|
},
|
|
91
57
|
async mounted() {
|
|
92
58
|
if (!this.isLoggedIn) this.$router.push('/login');
|
|
59
|
+
|
|
60
|
+
const response = await AuthService.getRecordById({
|
|
61
|
+
id: this.$route.params.app,
|
|
62
|
+
query: {
|
|
63
|
+
fields: ['t_apps_name', 't_apps_icon', 't_apps_url'],
|
|
64
|
+
relations: []
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
this.app = response.data.record;
|
|
93
68
|
},
|
|
94
69
|
methods: {
|
|
95
70
|
close () {
|