@progalaxyelabs/ngx-stonescriptphp-client 1.24.2 → 1.24.4
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.
|
@@ -2241,6 +2241,7 @@ class TenantLoginComponent {
|
|
|
2241
2241
|
autoSelectSingleTenant = true;
|
|
2242
2242
|
prefillEmail; // Email to prefill (for account linking flow)
|
|
2243
2243
|
allowTenantCreation = true;
|
|
2244
|
+
otpIdentifierTypes = ['email', 'phone']; // Allowed OTP identifier types
|
|
2244
2245
|
// Tenant Selector Labels
|
|
2245
2246
|
tenantSelectorTitle = 'Select Organization';
|
|
2246
2247
|
tenantSelectorDescription = 'Choose which organization you want to access:';
|
|
@@ -2270,6 +2271,8 @@ class TenantLoginComponent {
|
|
|
2270
2271
|
otpStep = 'identifier';
|
|
2271
2272
|
otpIdentifier = '';
|
|
2272
2273
|
otpIdentifierHint = '';
|
|
2274
|
+
otpIdentifierError = '';
|
|
2275
|
+
otpIdentifierErrorColor = 'red';
|
|
2273
2276
|
otpNormalizedIdentifier = ''; // E.164 for phone, as-is for email
|
|
2274
2277
|
otpMaskedIdentifier = '';
|
|
2275
2278
|
otpDigits = ['', '', '', '', '', ''];
|
|
@@ -2528,12 +2531,12 @@ class TenantLoginComponent {
|
|
|
2528
2531
|
async onOtpSend() {
|
|
2529
2532
|
const raw = this.otpIdentifier.trim();
|
|
2530
2533
|
if (!raw) {
|
|
2531
|
-
this.error =
|
|
2534
|
+
this.error = `Please enter your ${this.otpIdentifierName}`;
|
|
2532
2535
|
return;
|
|
2533
2536
|
}
|
|
2534
2537
|
const type = this.detectIdentifierType(raw);
|
|
2535
2538
|
if (!type) {
|
|
2536
|
-
this.error =
|
|
2539
|
+
this.error = `Please enter a valid ${this.otpIdentifierName}`;
|
|
2537
2540
|
return;
|
|
2538
2541
|
}
|
|
2539
2542
|
// Normalize: auto-prepend +91 for 10-digit Indian numbers
|
|
@@ -2546,6 +2549,7 @@ class TenantLoginComponent {
|
|
|
2546
2549
|
}
|
|
2547
2550
|
this.loading = true;
|
|
2548
2551
|
this.error = '';
|
|
2552
|
+
this.otpIdentifierError = '';
|
|
2549
2553
|
try {
|
|
2550
2554
|
const result = await this.auth.sendOtp(this.otpNormalizedIdentifier);
|
|
2551
2555
|
if (!result.success) {
|
|
@@ -2580,7 +2584,26 @@ class TenantLoginComponent {
|
|
|
2580
2584
|
try {
|
|
2581
2585
|
const verifyResult = await this.auth.verifyOtp(this.otpNormalizedIdentifier, code);
|
|
2582
2586
|
if (!verifyResult.success) {
|
|
2583
|
-
|
|
2587
|
+
switch (verifyResult.error) {
|
|
2588
|
+
case 'otp_expired':
|
|
2589
|
+
this.resetToIdentifierStep('Your code has expired. Please request a new one.', 'orange');
|
|
2590
|
+
break;
|
|
2591
|
+
case 'otp_invalid': {
|
|
2592
|
+
const attempts = verifyResult.remaining_attempts;
|
|
2593
|
+
this.error = attempts !== undefined
|
|
2594
|
+
? `Invalid code. ${attempts} attempt${attempts === 1 ? '' : 's'} remaining.`
|
|
2595
|
+
: 'Invalid code. Please try again.';
|
|
2596
|
+
break;
|
|
2597
|
+
}
|
|
2598
|
+
case 'otp_rate_limited':
|
|
2599
|
+
this.resetToIdentifierStep('Too many attempts. Please try again later.', 'red');
|
|
2600
|
+
break;
|
|
2601
|
+
case 'otp_not_found':
|
|
2602
|
+
this.resetToIdentifierStep('No code found. Please request a new one.', 'orange');
|
|
2603
|
+
break;
|
|
2604
|
+
default:
|
|
2605
|
+
this.error = verifyResult.message || 'Invalid code. Please try again.';
|
|
2606
|
+
}
|
|
2584
2607
|
return;
|
|
2585
2608
|
}
|
|
2586
2609
|
this.otpVerifiedToken = verifyResult.verified_token;
|
|
@@ -2656,11 +2679,26 @@ class TenantLoginComponent {
|
|
|
2656
2679
|
/** Go back to identifier entry */
|
|
2657
2680
|
onOtpBack(event) {
|
|
2658
2681
|
event.preventDefault();
|
|
2682
|
+
this.otpStep = 'identifier';
|
|
2683
|
+
this.otpDigits = ['', '', '', '', '', ''];
|
|
2684
|
+
this.otpVerifiedToken = '';
|
|
2685
|
+
this.error = '';
|
|
2686
|
+
this.otpIdentifierError = '';
|
|
2687
|
+
this.clearResendTimer();
|
|
2688
|
+
}
|
|
2689
|
+
/**
|
|
2690
|
+
* Reset to the identifier entry step (step 1) with an error message.
|
|
2691
|
+
* Used when OTP verification fails with a code that requires re-sending
|
|
2692
|
+
* (expired, rate-limited, not-found). Keeps the identifier pre-filled.
|
|
2693
|
+
*/
|
|
2694
|
+
resetToIdentifierStep(message, color) {
|
|
2659
2695
|
this.otpStep = 'identifier';
|
|
2660
2696
|
this.otpDigits = ['', '', '', '', '', ''];
|
|
2661
2697
|
this.otpVerifiedToken = '';
|
|
2662
2698
|
this.error = '';
|
|
2663
2699
|
this.clearResendTimer();
|
|
2700
|
+
this.otpIdentifierError = message;
|
|
2701
|
+
this.otpIdentifierErrorColor = color;
|
|
2664
2702
|
}
|
|
2665
2703
|
// ── OTP digit input handling ─────────────────────────────────────────────
|
|
2666
2704
|
onOtpDigitInput(event, index) {
|
|
@@ -2705,15 +2743,46 @@ class TenantLoginComponent {
|
|
|
2705
2743
|
get otpCode() {
|
|
2706
2744
|
return this.otpDigits.join('');
|
|
2707
2745
|
}
|
|
2746
|
+
get otpPlaceholderText() {
|
|
2747
|
+
const allowsEmail = this.otpIdentifierTypes.includes('email');
|
|
2748
|
+
const allowsPhone = this.otpIdentifierTypes.includes('phone');
|
|
2749
|
+
if (allowsEmail && allowsPhone) {
|
|
2750
|
+
return 'Enter Email or Phone Number';
|
|
2751
|
+
}
|
|
2752
|
+
else if (allowsEmail) {
|
|
2753
|
+
return 'Enter Email';
|
|
2754
|
+
}
|
|
2755
|
+
else if (allowsPhone) {
|
|
2756
|
+
return 'Enter Phone Number';
|
|
2757
|
+
}
|
|
2758
|
+
return 'Enter Identifier';
|
|
2759
|
+
}
|
|
2760
|
+
get otpIdentifierName() {
|
|
2761
|
+
const allowsEmail = this.otpIdentifierTypes.includes('email');
|
|
2762
|
+
const allowsPhone = this.otpIdentifierTypes.includes('phone');
|
|
2763
|
+
if (allowsEmail && allowsPhone) {
|
|
2764
|
+
return 'email or phone number';
|
|
2765
|
+
}
|
|
2766
|
+
else if (allowsEmail) {
|
|
2767
|
+
return 'email';
|
|
2768
|
+
}
|
|
2769
|
+
else if (allowsPhone) {
|
|
2770
|
+
return 'phone number';
|
|
2771
|
+
}
|
|
2772
|
+
return 'identifier';
|
|
2773
|
+
}
|
|
2708
2774
|
// ── OTP helpers ──────────────────────────────────────────────────────────
|
|
2709
2775
|
detectIdentifierType(value) {
|
|
2776
|
+
const allowsEmail = this.otpIdentifierTypes.includes('email');
|
|
2777
|
+
const allowsPhone = this.otpIdentifierTypes.includes('phone');
|
|
2710
2778
|
if (value.includes('@')) {
|
|
2711
2779
|
// Basic email validation
|
|
2712
|
-
|
|
2780
|
+
const isValidEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
|
|
2781
|
+
return (isValidEmail && allowsEmail) ? 'email' : null;
|
|
2713
2782
|
}
|
|
2714
2783
|
const digits = value.replace(/\D/g, '');
|
|
2715
2784
|
if (digits.length >= 10 && digits.length <= 15) {
|
|
2716
|
-
return 'phone';
|
|
2785
|
+
return allowsPhone ? 'phone' : null;
|
|
2717
2786
|
}
|
|
2718
2787
|
return null;
|
|
2719
2788
|
}
|
|
@@ -2775,7 +2844,7 @@ class TenantLoginComponent {
|
|
|
2775
2844
|
this.createTenant.emit();
|
|
2776
2845
|
}
|
|
2777
2846
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: TenantLoginComponent, deps: [{ token: AuthService }, { token: ProviderRegistryService }], target: i0.ɵɵFactoryTarget.Component });
|
|
2778
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: TenantLoginComponent, isStandalone: true, selector: "lib-tenant-login", inputs: { title: "title", providers: "providers", showTenantSelector: "showTenantSelector", autoSelectSingleTenant: "autoSelectSingleTenant", prefillEmail: "prefillEmail", allowTenantCreation: "allowTenantCreation", tenantSelectorTitle: "tenantSelectorTitle", tenantSelectorDescription: "tenantSelectorDescription", continueButtonText: "continueButtonText", registerLinkText: "registerLinkText", registerLinkAction: "registerLinkAction", createTenantLinkText: "createTenantLinkText", createTenantLinkAction: "createTenantLinkAction" }, outputs: { tenantSelected: "tenantSelected", needsOnboarding: "needsOnboarding", createTenant: "createTenant" }, viewQueries: [{ propertyName: "otpInputs", predicate: ["otpInput"], descendants: true }], ngImport: i0, template: `
|
|
2847
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: TenantLoginComponent, isStandalone: true, selector: "lib-tenant-login", inputs: { title: "title", providers: "providers", showTenantSelector: "showTenantSelector", autoSelectSingleTenant: "autoSelectSingleTenant", prefillEmail: "prefillEmail", allowTenantCreation: "allowTenantCreation", otpIdentifierTypes: "otpIdentifierTypes", tenantSelectorTitle: "tenantSelectorTitle", tenantSelectorDescription: "tenantSelectorDescription", continueButtonText: "continueButtonText", registerLinkText: "registerLinkText", registerLinkAction: "registerLinkAction", createTenantLinkText: "createTenantLinkText", createTenantLinkAction: "createTenantLinkAction" }, outputs: { tenantSelected: "tenantSelected", needsOnboarding: "needsOnboarding", createTenant: "createTenant" }, viewQueries: [{ propertyName: "otpInputs", predicate: ["otpInput"], descendants: true }], ngImport: i0, template: `
|
|
2779
2848
|
<div class="tenant-login-dialog">
|
|
2780
2849
|
@if (!showingTenantSelector) {
|
|
2781
2850
|
<!-- Step 1: Authentication -->
|
|
@@ -2786,11 +2855,16 @@ class TenantLoginComponent {
|
|
|
2786
2855
|
<!-- OTP Step 1: Identifier entry -->
|
|
2787
2856
|
@if (otpStep === 'identifier') {
|
|
2788
2857
|
<form (ngSubmit)="onOtpSend()" class="otp-form">
|
|
2858
|
+
@if (otpIdentifierError) {
|
|
2859
|
+
<div [class]="'otp-identifier-error otp-identifier-error--' + otpIdentifierErrorColor">
|
|
2860
|
+
{{ otpIdentifierError }}
|
|
2861
|
+
</div>
|
|
2862
|
+
}
|
|
2789
2863
|
<div class="form-group">
|
|
2790
2864
|
<input
|
|
2791
2865
|
[(ngModel)]="otpIdentifier"
|
|
2792
2866
|
name="otpIdentifier"
|
|
2793
|
-
placeholder="
|
|
2867
|
+
[placeholder]="otpPlaceholderText"
|
|
2794
2868
|
type="text"
|
|
2795
2869
|
required
|
|
2796
2870
|
autocomplete="email tel"
|
|
@@ -2852,7 +2926,7 @@ class TenantLoginComponent {
|
|
|
2852
2926
|
</div>
|
|
2853
2927
|
<div class="otp-back">
|
|
2854
2928
|
<a href="#" (click)="onOtpBack($event)">
|
|
2855
|
-
Use a different
|
|
2929
|
+
Use a different {{ otpIdentifierName }}
|
|
2856
2930
|
</a>
|
|
2857
2931
|
</div>
|
|
2858
2932
|
</div>
|
|
@@ -3052,7 +3126,7 @@ class TenantLoginComponent {
|
|
|
3052
3126
|
</div>
|
|
3053
3127
|
}
|
|
3054
3128
|
</div>
|
|
3055
|
-
`, isInline: true, styles: [".tenant-login-dialog{padding:24px;max-width:450px;position:relative}.login-title{margin:0 0 24px;font-size:24px;font-weight:500;text-align:center}.welcome-message{margin-bottom:16px;padding:12px;background:#e8f5e9;border-radius:4px;text-align:center;font-size:14px;color:#2e7d32}.selector-description{margin-bottom:20px;font-size:14px;color:#666;text-align:center}.email-form,.otp-form,.form-group{margin-bottom:16px}.password-group{position:relative}.form-control{width:100%;padding:12px;border:1px solid #ddd;border-radius:4px;font-size:14px;box-sizing:border-box}.password-input{padding-right:45px}.password-toggle{position:absolute;right:8px;top:50%;transform:translateY(-50%);background:none;border:none;cursor:pointer;font-size:18px;padding:8px;line-height:1;opacity:.6;transition:opacity .2s}.password-toggle:hover{opacity:1}.password-toggle:focus{outline:2px solid #4285f4;outline-offset:2px;border-radius:4px}.form-control:focus{outline:none;border-color:#4285f4}.field-hint{margin-top:4px;font-size:12px;color:#888}.btn{padding:12px 24px;border:none;border-radius:4px;font-size:14px;font-weight:500;cursor:pointer;transition:background-color .2s}.btn:disabled{opacity:.6;cursor:not-allowed}.btn-block{width:100%}.btn-primary{background-color:#4285f4;color:#fff}.btn-primary:hover:not(:disabled){background-color:#357ae8}.divider{margin:16px 0;text-align:center;position:relative}.divider:before{content:\"\";position:absolute;top:50%;left:0;right:0;height:1px;background:#ddd}.divider span{background:#fff;padding:0 12px;position:relative;color:#666;font-size:12px}.oauth-buttons{display:flex;flex-direction:column;gap:12px}.btn-oauth{width:100%;background:#fff;color:#333;border:1px solid #ddd;display:flex;align-items:center;justify-content:center;gap:8px}.btn-oauth:hover:not(:disabled){background:#f8f8f8}.btn-google{border-color:#4285f4}.btn-linkedin{border-color:#0077b5}.btn-apple{border-color:#000}.btn-microsoft{border-color:#00a4ef}.btn-github{border-color:#333}.btn-zoho{background-color:#f0483e;color:#fff;border:1px solid #d63b32}.btn-zoho:hover{background-color:#d63b32}.oauth-icon{font-size:18px}.switch-method{margin-top:12px;text-align:center;font-size:14px}.switch-method a{color:#4285f4;text-decoration:none}.switch-method a:hover{text-decoration:underline}.otp-subtitle{text-align:center;font-size:14px;color:#555;margin-bottom:20px}.otp-digits{display:flex;justify-content:center;gap:8px;margin-bottom:20px}.otp-digit-input{width:44px;height:52px;text-align:center;font-size:22px;font-weight:600;border:2px solid #ddd;border-radius:8px;outline:none;transition:border-color .2s;box-sizing:border-box}.otp-digit-input:focus{border-color:#4285f4}.otp-actions{margin-bottom:12px}.otp-resend{text-align:center;font-size:14px;margin-bottom:8px}.resend-timer{color:#888}.resend-link{color:#4285f4;text-decoration:none;cursor:pointer}.resend-link:hover{text-decoration:underline}.otp-back{text-align:center;font-size:13px}.otp-back a{color:#888;text-decoration:none}.otp-back a:hover{text-decoration:underline;color:#4285f4}.otp-register-section .otp-subtitle{color:#2e7d32}.tenant-list{margin-bottom:20px;display:flex;flex-direction:column;gap:12px}.tenant-item{display:flex;align-items:flex-start;gap:12px;padding:16px;border:2px solid #e0e0e0;border-radius:6px;cursor:pointer;transition:all .2s}.tenant-item:hover{border-color:#4285f4;background:#f8f9ff}.tenant-item.selected{border-color:#4285f4;background:#e8f0fe}.tenant-radio{flex-shrink:0;padding-top:2px}.tenant-radio input[type=radio]{width:18px;height:18px;cursor:pointer}.tenant-info{flex:1}.tenant-name{font-size:16px;font-weight:500;color:#333;margin-bottom:4px}.tenant-meta{font-size:13px;color:#666}.tenant-role{font-weight:500;color:#4285f4}.tenant-separator{margin:0 6px}.error-message{margin-top:16px;padding:12px;background:#fee;color:#c33;border-radius:4px;font-size:14px}.loading-overlay{position:absolute;inset:0;background:#fffc;display:flex;align-items:center;justify-content:center}.spinner{width:40px;height:40px;border:4px solid #f3f3f3;border-top:4px solid #4285f4;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.register-link{margin-top:16px;text-align:center;font-size:14px;color:#666}.register-link a{color:#4285f4;text-decoration:none}.register-link a:hover{text-decoration:underline}.create-tenant-link{margin-top:16px;padding-top:16px;border-top:1px solid #e0e0e0;text-align:center;font-size:14px;color:#666}.create-tenant-link a{color:#4285f4;text-decoration:none;font-weight:500}.create-tenant-link a:hover{text-decoration:underline}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i4.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i4.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }] });
|
|
3129
|
+
`, isInline: true, styles: [".tenant-login-dialog{padding:24px;max-width:450px;position:relative}.login-title{margin:0 0 24px;font-size:24px;font-weight:500;text-align:center}.welcome-message{margin-bottom:16px;padding:12px;background:#e8f5e9;border-radius:4px;text-align:center;font-size:14px;color:#2e7d32}.selector-description{margin-bottom:20px;font-size:14px;color:#666;text-align:center}.email-form,.otp-form,.form-group{margin-bottom:16px}.password-group{position:relative}.form-control{width:100%;padding:12px;border:1px solid #ddd;border-radius:4px;font-size:14px;box-sizing:border-box}.password-input{padding-right:45px}.password-toggle{position:absolute;right:8px;top:50%;transform:translateY(-50%);background:none;border:none;cursor:pointer;font-size:18px;padding:8px;line-height:1;opacity:.6;transition:opacity .2s}.password-toggle:hover{opacity:1}.password-toggle:focus{outline:2px solid #4285f4;outline-offset:2px;border-radius:4px}.form-control:focus{outline:none;border-color:#4285f4}.field-hint{margin-top:4px;font-size:12px;color:#888}.btn{padding:12px 24px;border:none;border-radius:4px;font-size:14px;font-weight:500;cursor:pointer;transition:background-color .2s}.btn:disabled{opacity:.6;cursor:not-allowed}.btn-block{width:100%}.btn-primary{background-color:#4285f4;color:#fff}.btn-primary:hover:not(:disabled){background-color:#357ae8}.divider{margin:16px 0;text-align:center;position:relative}.divider:before{content:\"\";position:absolute;top:50%;left:0;right:0;height:1px;background:#ddd}.divider span{background:#fff;padding:0 12px;position:relative;color:#666;font-size:12px}.oauth-buttons{display:flex;flex-direction:column;gap:12px}.btn-oauth{width:100%;background:#fff;color:#333;border:1px solid #ddd;display:flex;align-items:center;justify-content:center;gap:8px}.btn-oauth:hover:not(:disabled){background:#f8f8f8}.btn-google{border-color:#4285f4}.btn-linkedin{border-color:#0077b5}.btn-apple{border-color:#000}.btn-microsoft{border-color:#00a4ef}.btn-github{border-color:#333}.btn-zoho{background-color:#f0483e;color:#fff;border:1px solid #d63b32}.btn-zoho:hover{background-color:#d63b32}.oauth-icon{font-size:18px}.switch-method{margin-top:12px;text-align:center;font-size:14px}.switch-method a{color:#4285f4;text-decoration:none}.switch-method a:hover{text-decoration:underline}.otp-subtitle{text-align:center;font-size:14px;color:#555;margin-bottom:20px}.otp-digits{display:flex;justify-content:center;gap:8px;margin-bottom:20px}.otp-digit-input{width:44px;height:52px;text-align:center;font-size:22px;font-weight:600;border:2px solid #ddd;border-radius:8px;outline:none;transition:border-color .2s;box-sizing:border-box}.otp-digit-input:focus{border-color:#4285f4}.otp-actions{margin-bottom:12px}.otp-resend{text-align:center;font-size:14px;margin-bottom:8px}.resend-timer{color:#888}.resend-link{color:#4285f4;text-decoration:none;cursor:pointer}.resend-link:hover{text-decoration:underline}.otp-back{text-align:center;font-size:13px}.otp-back a{color:#888;text-decoration:none}.otp-back a:hover{text-decoration:underline;color:#4285f4}.otp-register-section .otp-subtitle{color:#2e7d32}.tenant-list{margin-bottom:20px;display:flex;flex-direction:column;gap:12px}.tenant-item{display:flex;align-items:flex-start;gap:12px;padding:16px;border:2px solid #e0e0e0;border-radius:6px;cursor:pointer;transition:all .2s}.tenant-item:hover{border-color:#4285f4;background:#f8f9ff}.tenant-item.selected{border-color:#4285f4;background:#e8f0fe}.tenant-radio{flex-shrink:0;padding-top:2px}.tenant-radio input[type=radio]{width:18px;height:18px;cursor:pointer}.tenant-info{flex:1}.tenant-name{font-size:16px;font-weight:500;color:#333;margin-bottom:4px}.tenant-meta{font-size:13px;color:#666}.tenant-role{font-weight:500;color:#4285f4}.tenant-separator{margin:0 6px}.error-message{margin-top:16px;padding:12px;background:#fee;color:#c33;border-radius:4px;font-size:14px}.otp-identifier-error{margin-bottom:12px;padding:10px 12px;border-radius:4px;font-size:14px}.otp-identifier-error--red{background:#fee;color:#c33}.otp-identifier-error--orange{background:#fff3e0;color:#e65100}.loading-overlay{position:absolute;inset:0;background:#fffc;display:flex;align-items:center;justify-content:center}.spinner{width:40px;height:40px;border:4px solid #f3f3f3;border-top:4px solid #4285f4;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.register-link{margin-top:16px;text-align:center;font-size:14px;color:#666}.register-link a{color:#4285f4;text-decoration:none}.register-link a:hover{text-decoration:underline}.create-tenant-link{margin-top:16px;padding-top:16px;border-top:1px solid #e0e0e0;text-align:center;font-size:14px;color:#666}.create-tenant-link a{color:#4285f4;text-decoration:none;font-weight:500}.create-tenant-link a:hover{text-decoration:underline}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i4.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i4.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }] });
|
|
3056
3130
|
}
|
|
3057
3131
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: TenantLoginComponent, decorators: [{
|
|
3058
3132
|
type: Component,
|
|
@@ -3067,11 +3141,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3067
3141
|
<!-- OTP Step 1: Identifier entry -->
|
|
3068
3142
|
@if (otpStep === 'identifier') {
|
|
3069
3143
|
<form (ngSubmit)="onOtpSend()" class="otp-form">
|
|
3144
|
+
@if (otpIdentifierError) {
|
|
3145
|
+
<div [class]="'otp-identifier-error otp-identifier-error--' + otpIdentifierErrorColor">
|
|
3146
|
+
{{ otpIdentifierError }}
|
|
3147
|
+
</div>
|
|
3148
|
+
}
|
|
3070
3149
|
<div class="form-group">
|
|
3071
3150
|
<input
|
|
3072
3151
|
[(ngModel)]="otpIdentifier"
|
|
3073
3152
|
name="otpIdentifier"
|
|
3074
|
-
placeholder="
|
|
3153
|
+
[placeholder]="otpPlaceholderText"
|
|
3075
3154
|
type="text"
|
|
3076
3155
|
required
|
|
3077
3156
|
autocomplete="email tel"
|
|
@@ -3133,7 +3212,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3133
3212
|
</div>
|
|
3134
3213
|
<div class="otp-back">
|
|
3135
3214
|
<a href="#" (click)="onOtpBack($event)">
|
|
3136
|
-
Use a different
|
|
3215
|
+
Use a different {{ otpIdentifierName }}
|
|
3137
3216
|
</a>
|
|
3138
3217
|
</div>
|
|
3139
3218
|
</div>
|
|
@@ -3333,7 +3412,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3333
3412
|
</div>
|
|
3334
3413
|
}
|
|
3335
3414
|
</div>
|
|
3336
|
-
`, styles: [".tenant-login-dialog{padding:24px;max-width:450px;position:relative}.login-title{margin:0 0 24px;font-size:24px;font-weight:500;text-align:center}.welcome-message{margin-bottom:16px;padding:12px;background:#e8f5e9;border-radius:4px;text-align:center;font-size:14px;color:#2e7d32}.selector-description{margin-bottom:20px;font-size:14px;color:#666;text-align:center}.email-form,.otp-form,.form-group{margin-bottom:16px}.password-group{position:relative}.form-control{width:100%;padding:12px;border:1px solid #ddd;border-radius:4px;font-size:14px;box-sizing:border-box}.password-input{padding-right:45px}.password-toggle{position:absolute;right:8px;top:50%;transform:translateY(-50%);background:none;border:none;cursor:pointer;font-size:18px;padding:8px;line-height:1;opacity:.6;transition:opacity .2s}.password-toggle:hover{opacity:1}.password-toggle:focus{outline:2px solid #4285f4;outline-offset:2px;border-radius:4px}.form-control:focus{outline:none;border-color:#4285f4}.field-hint{margin-top:4px;font-size:12px;color:#888}.btn{padding:12px 24px;border:none;border-radius:4px;font-size:14px;font-weight:500;cursor:pointer;transition:background-color .2s}.btn:disabled{opacity:.6;cursor:not-allowed}.btn-block{width:100%}.btn-primary{background-color:#4285f4;color:#fff}.btn-primary:hover:not(:disabled){background-color:#357ae8}.divider{margin:16px 0;text-align:center;position:relative}.divider:before{content:\"\";position:absolute;top:50%;left:0;right:0;height:1px;background:#ddd}.divider span{background:#fff;padding:0 12px;position:relative;color:#666;font-size:12px}.oauth-buttons{display:flex;flex-direction:column;gap:12px}.btn-oauth{width:100%;background:#fff;color:#333;border:1px solid #ddd;display:flex;align-items:center;justify-content:center;gap:8px}.btn-oauth:hover:not(:disabled){background:#f8f8f8}.btn-google{border-color:#4285f4}.btn-linkedin{border-color:#0077b5}.btn-apple{border-color:#000}.btn-microsoft{border-color:#00a4ef}.btn-github{border-color:#333}.btn-zoho{background-color:#f0483e;color:#fff;border:1px solid #d63b32}.btn-zoho:hover{background-color:#d63b32}.oauth-icon{font-size:18px}.switch-method{margin-top:12px;text-align:center;font-size:14px}.switch-method a{color:#4285f4;text-decoration:none}.switch-method a:hover{text-decoration:underline}.otp-subtitle{text-align:center;font-size:14px;color:#555;margin-bottom:20px}.otp-digits{display:flex;justify-content:center;gap:8px;margin-bottom:20px}.otp-digit-input{width:44px;height:52px;text-align:center;font-size:22px;font-weight:600;border:2px solid #ddd;border-radius:8px;outline:none;transition:border-color .2s;box-sizing:border-box}.otp-digit-input:focus{border-color:#4285f4}.otp-actions{margin-bottom:12px}.otp-resend{text-align:center;font-size:14px;margin-bottom:8px}.resend-timer{color:#888}.resend-link{color:#4285f4;text-decoration:none;cursor:pointer}.resend-link:hover{text-decoration:underline}.otp-back{text-align:center;font-size:13px}.otp-back a{color:#888;text-decoration:none}.otp-back a:hover{text-decoration:underline;color:#4285f4}.otp-register-section .otp-subtitle{color:#2e7d32}.tenant-list{margin-bottom:20px;display:flex;flex-direction:column;gap:12px}.tenant-item{display:flex;align-items:flex-start;gap:12px;padding:16px;border:2px solid #e0e0e0;border-radius:6px;cursor:pointer;transition:all .2s}.tenant-item:hover{border-color:#4285f4;background:#f8f9ff}.tenant-item.selected{border-color:#4285f4;background:#e8f0fe}.tenant-radio{flex-shrink:0;padding-top:2px}.tenant-radio input[type=radio]{width:18px;height:18px;cursor:pointer}.tenant-info{flex:1}.tenant-name{font-size:16px;font-weight:500;color:#333;margin-bottom:4px}.tenant-meta{font-size:13px;color:#666}.tenant-role{font-weight:500;color:#4285f4}.tenant-separator{margin:0 6px}.error-message{margin-top:16px;padding:12px;background:#fee;color:#c33;border-radius:4px;font-size:14px}.loading-overlay{position:absolute;inset:0;background:#fffc;display:flex;align-items:center;justify-content:center}.spinner{width:40px;height:40px;border:4px solid #f3f3f3;border-top:4px solid #4285f4;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.register-link{margin-top:16px;text-align:center;font-size:14px;color:#666}.register-link a{color:#4285f4;text-decoration:none}.register-link a:hover{text-decoration:underline}.create-tenant-link{margin-top:16px;padding-top:16px;border-top:1px solid #e0e0e0;text-align:center;font-size:14px;color:#666}.create-tenant-link a{color:#4285f4;text-decoration:none;font-weight:500}.create-tenant-link a:hover{text-decoration:underline}\n"] }]
|
|
3415
|
+
`, styles: [".tenant-login-dialog{padding:24px;max-width:450px;position:relative}.login-title{margin:0 0 24px;font-size:24px;font-weight:500;text-align:center}.welcome-message{margin-bottom:16px;padding:12px;background:#e8f5e9;border-radius:4px;text-align:center;font-size:14px;color:#2e7d32}.selector-description{margin-bottom:20px;font-size:14px;color:#666;text-align:center}.email-form,.otp-form,.form-group{margin-bottom:16px}.password-group{position:relative}.form-control{width:100%;padding:12px;border:1px solid #ddd;border-radius:4px;font-size:14px;box-sizing:border-box}.password-input{padding-right:45px}.password-toggle{position:absolute;right:8px;top:50%;transform:translateY(-50%);background:none;border:none;cursor:pointer;font-size:18px;padding:8px;line-height:1;opacity:.6;transition:opacity .2s}.password-toggle:hover{opacity:1}.password-toggle:focus{outline:2px solid #4285f4;outline-offset:2px;border-radius:4px}.form-control:focus{outline:none;border-color:#4285f4}.field-hint{margin-top:4px;font-size:12px;color:#888}.btn{padding:12px 24px;border:none;border-radius:4px;font-size:14px;font-weight:500;cursor:pointer;transition:background-color .2s}.btn:disabled{opacity:.6;cursor:not-allowed}.btn-block{width:100%}.btn-primary{background-color:#4285f4;color:#fff}.btn-primary:hover:not(:disabled){background-color:#357ae8}.divider{margin:16px 0;text-align:center;position:relative}.divider:before{content:\"\";position:absolute;top:50%;left:0;right:0;height:1px;background:#ddd}.divider span{background:#fff;padding:0 12px;position:relative;color:#666;font-size:12px}.oauth-buttons{display:flex;flex-direction:column;gap:12px}.btn-oauth{width:100%;background:#fff;color:#333;border:1px solid #ddd;display:flex;align-items:center;justify-content:center;gap:8px}.btn-oauth:hover:not(:disabled){background:#f8f8f8}.btn-google{border-color:#4285f4}.btn-linkedin{border-color:#0077b5}.btn-apple{border-color:#000}.btn-microsoft{border-color:#00a4ef}.btn-github{border-color:#333}.btn-zoho{background-color:#f0483e;color:#fff;border:1px solid #d63b32}.btn-zoho:hover{background-color:#d63b32}.oauth-icon{font-size:18px}.switch-method{margin-top:12px;text-align:center;font-size:14px}.switch-method a{color:#4285f4;text-decoration:none}.switch-method a:hover{text-decoration:underline}.otp-subtitle{text-align:center;font-size:14px;color:#555;margin-bottom:20px}.otp-digits{display:flex;justify-content:center;gap:8px;margin-bottom:20px}.otp-digit-input{width:44px;height:52px;text-align:center;font-size:22px;font-weight:600;border:2px solid #ddd;border-radius:8px;outline:none;transition:border-color .2s;box-sizing:border-box}.otp-digit-input:focus{border-color:#4285f4}.otp-actions{margin-bottom:12px}.otp-resend{text-align:center;font-size:14px;margin-bottom:8px}.resend-timer{color:#888}.resend-link{color:#4285f4;text-decoration:none;cursor:pointer}.resend-link:hover{text-decoration:underline}.otp-back{text-align:center;font-size:13px}.otp-back a{color:#888;text-decoration:none}.otp-back a:hover{text-decoration:underline;color:#4285f4}.otp-register-section .otp-subtitle{color:#2e7d32}.tenant-list{margin-bottom:20px;display:flex;flex-direction:column;gap:12px}.tenant-item{display:flex;align-items:flex-start;gap:12px;padding:16px;border:2px solid #e0e0e0;border-radius:6px;cursor:pointer;transition:all .2s}.tenant-item:hover{border-color:#4285f4;background:#f8f9ff}.tenant-item.selected{border-color:#4285f4;background:#e8f0fe}.tenant-radio{flex-shrink:0;padding-top:2px}.tenant-radio input[type=radio]{width:18px;height:18px;cursor:pointer}.tenant-info{flex:1}.tenant-name{font-size:16px;font-weight:500;color:#333;margin-bottom:4px}.tenant-meta{font-size:13px;color:#666}.tenant-role{font-weight:500;color:#4285f4}.tenant-separator{margin:0 6px}.error-message{margin-top:16px;padding:12px;background:#fee;color:#c33;border-radius:4px;font-size:14px}.otp-identifier-error{margin-bottom:12px;padding:10px 12px;border-radius:4px;font-size:14px}.otp-identifier-error--red{background:#fee;color:#c33}.otp-identifier-error--orange{background:#fff3e0;color:#e65100}.loading-overlay{position:absolute;inset:0;background:#fffc;display:flex;align-items:center;justify-content:center}.spinner{width:40px;height:40px;border:4px solid #f3f3f3;border-top:4px solid #4285f4;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.register-link{margin-top:16px;text-align:center;font-size:14px;color:#666}.register-link a{color:#4285f4;text-decoration:none}.register-link a:hover{text-decoration:underline}.create-tenant-link{margin-top:16px;padding-top:16px;border-top:1px solid #e0e0e0;text-align:center;font-size:14px;color:#666}.create-tenant-link a{color:#4285f4;text-decoration:none;font-weight:500}.create-tenant-link a:hover{text-decoration:underline}\n"] }]
|
|
3337
3416
|
}], ctorParameters: () => [{ type: AuthService }, { type: ProviderRegistryService }], propDecorators: { otpInputs: [{
|
|
3338
3417
|
type: ViewChildren,
|
|
3339
3418
|
args: ['otpInput']
|
|
@@ -3349,6 +3428,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3349
3428
|
type: Input
|
|
3350
3429
|
}], allowTenantCreation: [{
|
|
3351
3430
|
type: Input
|
|
3431
|
+
}], otpIdentifierTypes: [{
|
|
3432
|
+
type: Input
|
|
3352
3433
|
}], tenantSelectorTitle: [{
|
|
3353
3434
|
type: Input
|
|
3354
3435
|
}], tenantSelectorDescription: [{
|
|
@@ -3817,7 +3898,7 @@ class AuthPageComponent {
|
|
|
3817
3898
|
}
|
|
3818
3899
|
</div>
|
|
3819
3900
|
</div>
|
|
3820
|
-
`, isInline: true, styles: [".auth-container{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px;background:linear-gradient(135deg,#667eea,#764ba2)}.auth-card{background:#fff;border-radius:12px;box-shadow:0 10px 40px #0000001a;padding:40px;width:100%;max-width:480px}.logo{display:block;max-width:200px;max-height:80px;margin:0 auto 24px}.app-name{margin:0 0 12px;font-size:28px;font-weight:600;text-align:center;color:#1a202c}.subtitle{margin:0 0 32px;font-size:16px;text-align:center;color:#718096}:host ::ng-deep .tenant-login-dialog,:host ::ng-deep .register-dialog{padding:0;max-width:none}:host ::ng-deep .login-title,:host ::ng-deep .register-title{display:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: TenantLoginComponent, selector: "lib-tenant-login", inputs: ["title", "providers", "showTenantSelector", "autoSelectSingleTenant", "prefillEmail", "allowTenantCreation", "tenantSelectorTitle", "tenantSelectorDescription", "continueButtonText", "registerLinkText", "registerLinkAction", "createTenantLinkText", "createTenantLinkAction"], outputs: ["tenantSelected", "needsOnboarding", "createTenant"] }, { kind: "component", type: RegisterComponent, selector: "lib-register", outputs: ["navigateToLogin"] }] });
|
|
3901
|
+
`, isInline: true, styles: [".auth-container{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px;background:linear-gradient(135deg,#667eea,#764ba2)}.auth-card{background:#fff;border-radius:12px;box-shadow:0 10px 40px #0000001a;padding:40px;width:100%;max-width:480px}.logo{display:block;max-width:200px;max-height:80px;margin:0 auto 24px}.app-name{margin:0 0 12px;font-size:28px;font-weight:600;text-align:center;color:#1a202c}.subtitle{margin:0 0 32px;font-size:16px;text-align:center;color:#718096}:host ::ng-deep .tenant-login-dialog,:host ::ng-deep .register-dialog{padding:0;max-width:none}:host ::ng-deep .login-title,:host ::ng-deep .register-title{display:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: TenantLoginComponent, selector: "lib-tenant-login", inputs: ["title", "providers", "showTenantSelector", "autoSelectSingleTenant", "prefillEmail", "allowTenantCreation", "otpIdentifierTypes", "tenantSelectorTitle", "tenantSelectorDescription", "continueButtonText", "registerLinkText", "registerLinkAction", "createTenantLinkText", "createTenantLinkAction"], outputs: ["tenantSelected", "needsOnboarding", "createTenant"] }, { kind: "component", type: RegisterComponent, selector: "lib-register", outputs: ["navigateToLogin"] }] });
|
|
3821
3902
|
}
|
|
3822
3903
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: AuthPageComponent, decorators: [{
|
|
3823
3904
|
type: Component,
|
|
@@ -5094,7 +5175,7 @@ class TenantLoginDialogComponent {
|
|
|
5094
5175
|
(createTenant)="onCreateTenant()">
|
|
5095
5176
|
</lib-tenant-login>
|
|
5096
5177
|
</div>
|
|
5097
|
-
`, isInline: true, styles: [".dialog-wrapper{padding:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: TenantLoginComponent, selector: "lib-tenant-login", inputs: ["title", "providers", "showTenantSelector", "autoSelectSingleTenant", "prefillEmail", "allowTenantCreation", "tenantSelectorTitle", "tenantSelectorDescription", "continueButtonText", "registerLinkText", "registerLinkAction", "createTenantLinkText", "createTenantLinkAction"], outputs: ["tenantSelected", "needsOnboarding", "createTenant"] }] });
|
|
5178
|
+
`, isInline: true, styles: [".dialog-wrapper{padding:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: TenantLoginComponent, selector: "lib-tenant-login", inputs: ["title", "providers", "showTenantSelector", "autoSelectSingleTenant", "prefillEmail", "allowTenantCreation", "otpIdentifierTypes", "tenantSelectorTitle", "tenantSelectorDescription", "continueButtonText", "registerLinkText", "registerLinkAction", "createTenantLinkText", "createTenantLinkAction"], outputs: ["tenantSelected", "needsOnboarding", "createTenant"] }] });
|
|
5098
5179
|
}
|
|
5099
5180
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: TenantLoginDialogComponent, decorators: [{
|
|
5100
5181
|
type: Component,
|