@progalaxyelabs/ngx-stonescriptphp-client 1.6.0 → 1.8.2

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.
@@ -1,9 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { Injectable, Inject, NgModule, EventEmitter, Output, Input, Component, Optional } from '@angular/core';
3
3
  import { BehaviorSubject } from 'rxjs';
4
- import * as i2$1 from '@angular/common';
4
+ import * as i3 from '@angular/common';
5
5
  import { CommonModule } from '@angular/common';
6
- import * as i2 from '@angular/forms';
6
+ import * as i4 from '@angular/forms';
7
7
  import { FormsModule } from '@angular/forms';
8
8
 
9
9
  class ApiResponse {
@@ -196,6 +196,13 @@ class MyEnvironmentModel {
196
196
  };
197
197
  apiServer = { host: '' };
198
198
  chatServer = { host: '' };
199
+ /**
200
+ * Accounts/Authentication service server configuration
201
+ * Use this when auth is on a different server than the API
202
+ * Replaces the deprecated accountsUrl string with structured config
203
+ * @example { host: 'https://accounts.progalaxyelabs.com' }
204
+ */
205
+ accountsServer;
199
206
  /**
200
207
  * Files service server configuration
201
208
  * Used by FilesService for file upload/download operations
@@ -214,6 +221,19 @@ class MyEnvironmentModel {
214
221
  csrfTokenCookieName: 'csrf_token',
215
222
  csrfHeaderName: 'X-CSRF-Token'
216
223
  };
224
+ /**
225
+ * Custom OAuth provider configurations.
226
+ * Register additional OAuth providers beyond the built-in ones
227
+ * (google, linkedin, apple, microsoft, github, zoho).
228
+ * @example
229
+ * ```typescript
230
+ * customProviders: {
231
+ * okta: { label: 'Sign in with Okta', cssClass: 'btn-okta', buttonStyle: { borderColor: '#007dc1' } },
232
+ * keycloak: { label: 'Sign in with Keycloak', icon: '🔑' }
233
+ * }
234
+ * ```
235
+ */
236
+ customProviders;
217
237
  /**
218
238
  * Branding configuration for auth components
219
239
  * Allows platforms to customize login/register pages without creating wrappers
@@ -270,7 +290,7 @@ class ApiConnectionService {
270
290
  signinStatus;
271
291
  environment;
272
292
  csrf;
273
- host = ''; // contains trailing slash
293
+ host = ''; // base URL without trailing slash
274
294
  accessToken = '';
275
295
  authConfig;
276
296
  constructor(tokens, signinStatus, environment, csrf) {
@@ -334,7 +354,7 @@ class ApiConnectionService {
334
354
  return new ApiResponse('error');
335
355
  }
336
356
  async get(endpoint, queryParamsObj) {
337
- const url = this.host + endpoint.replace(/^\/+/, '') + this.buildQueryString(queryParamsObj);
357
+ const url = this.host + endpoint + this.buildQueryString(queryParamsObj);
338
358
  const fetchOptions = {
339
359
  mode: 'cors',
340
360
  redirect: 'error'
@@ -342,7 +362,7 @@ class ApiConnectionService {
342
362
  return this.request(url, fetchOptions, null);
343
363
  }
344
364
  async post(pathWithQueryParams, data) {
345
- const url = this.host + pathWithQueryParams.replace(/^\/+/, '');
365
+ const url = this.host + pathWithQueryParams;
346
366
  const fetchOptions = {
347
367
  method: 'POST',
348
368
  mode: 'cors',
@@ -355,7 +375,7 @@ class ApiConnectionService {
355
375
  return this.request(url, fetchOptions, data);
356
376
  }
357
377
  async put(pathWithQueryParams, data) {
358
- const url = this.host + pathWithQueryParams.replace(/^\/+/, '');
378
+ const url = this.host + pathWithQueryParams;
359
379
  const fetchOptions = {
360
380
  method: 'PUT',
361
381
  mode: 'cors',
@@ -368,7 +388,7 @@ class ApiConnectionService {
368
388
  return this.request(url, fetchOptions, data);
369
389
  }
370
390
  async patch(pathWithQueryParams, data) {
371
- const url = this.host + pathWithQueryParams.replace(/^\/+/, '');
391
+ const url = this.host + pathWithQueryParams;
372
392
  const fetchOptions = {
373
393
  method: 'PATCH',
374
394
  mode: 'cors',
@@ -381,7 +401,7 @@ class ApiConnectionService {
381
401
  return this.request(url, fetchOptions, data);
382
402
  }
383
403
  async delete(endpoint, queryParamsObj) {
384
- const url = this.host + endpoint.replace(/^\/+/, '') + this.buildQueryString(queryParamsObj);
404
+ const url = this.host + endpoint + this.buildQueryString(queryParamsObj);
385
405
  const fetchOptions = {
386
406
  method: 'DELETE',
387
407
  mode: 'cors',
@@ -447,7 +467,11 @@ class ApiConnectionService {
447
467
  */
448
468
  async refreshAccessTokenCookieMode() {
449
469
  try {
450
- const refreshTokenUrl = this.host + this.authConfig.refreshEndpoint.replace(/^\/+/, '');
470
+ // Determine auth server: accountsServer.host > accountsUrl > apiServer.host
471
+ const authHost = this.environment.accountsServer?.host
472
+ || this.environment.accountsUrl
473
+ || this.host;
474
+ const refreshTokenUrl = authHost + this.authConfig.refreshEndpoint;
451
475
  const headers = {
452
476
  'Content-Type': 'application/json'
453
477
  };
@@ -503,7 +527,11 @@ class ApiConnectionService {
503
527
  if (!refreshToken) {
504
528
  return false;
505
529
  }
506
- const refreshTokenUrl = this.host + this.authConfig.refreshEndpoint.replace(/^\/+/, '');
530
+ // Determine auth server: accountsServer.host > accountsUrl > apiServer.host
531
+ const authHost = this.environment.accountsServer?.host
532
+ || this.environment.accountsUrl
533
+ || this.host;
534
+ const refreshTokenUrl = authHost + this.authConfig.refreshEndpoint;
507
535
  let refreshTokenResponse = await fetch(refreshTokenUrl, {
508
536
  method: 'POST',
509
537
  mode: 'cors',
@@ -563,7 +591,7 @@ class ApiConnectionService {
563
591
  */
564
592
  async uploadDrawing(formData) {
565
593
  const uploadHost = this.environment.uploadServer?.host || this.host;
566
- const url = uploadHost + 'upload/drawing';
594
+ const url = uploadHost + '/upload/drawing';
567
595
  const fetchOptions = {
568
596
  method: 'POST',
569
597
  mode: 'cors',
@@ -578,7 +606,7 @@ class ApiConnectionService {
578
606
  */
579
607
  async uploadImage(formData) {
580
608
  const uploadHost = this.environment.uploadServer?.host || this.host;
581
- const url = uploadHost + 'upload/image';
609
+ const url = uploadHost + '/upload/image';
582
610
  const fetchOptions = {
583
611
  method: 'POST',
584
612
  mode: 'cors',
@@ -1762,6 +1790,188 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
1762
1790
  }]
1763
1791
  }], ctorParameters: () => [{ type: TokenService }, { type: SigninStatusService }, { type: MyEnvironmentModel }, { type: CsrfService }] });
1764
1792
 
1793
+ /**
1794
+ * Default configurations for built-in OAuth providers.
1795
+ * These serve as fallback values when no custom configuration is registered.
1796
+ */
1797
+ const BUILT_IN_PROVIDERS = {
1798
+ google: {
1799
+ label: 'Google',
1800
+ cssClass: 'btn-google',
1801
+ buttonStyle: { borderColor: '#4285f4' }
1802
+ },
1803
+ linkedin: {
1804
+ label: 'LinkedIn',
1805
+ cssClass: 'btn-linkedin',
1806
+ buttonStyle: { borderColor: '#0077b5' }
1807
+ },
1808
+ apple: {
1809
+ label: 'Apple',
1810
+ cssClass: 'btn-apple',
1811
+ buttonStyle: { borderColor: '#000' }
1812
+ },
1813
+ microsoft: {
1814
+ label: 'Microsoft',
1815
+ cssClass: 'btn-microsoft',
1816
+ buttonStyle: { borderColor: '#00a4ef' }
1817
+ },
1818
+ github: {
1819
+ label: 'GitHub',
1820
+ cssClass: 'btn-github',
1821
+ buttonStyle: { borderColor: '#333' }
1822
+ },
1823
+ zoho: {
1824
+ label: 'Zoho',
1825
+ icon: '🔶',
1826
+ cssClass: 'btn-zoho',
1827
+ buttonStyle: {
1828
+ borderColor: '#d63b32',
1829
+ backgroundColor: '#f0483e',
1830
+ color: '#ffffff'
1831
+ }
1832
+ },
1833
+ emailPassword: {
1834
+ label: 'Email',
1835
+ cssClass: 'btn-email'
1836
+ }
1837
+ };
1838
+ /**
1839
+ * Service for managing OAuth provider configurations.
1840
+ *
1841
+ * Provides a central registry for both built-in and custom OAuth providers.
1842
+ * Custom providers can be registered either through the environment configuration
1843
+ * (customProviders field) or programmatically via registerProvider/registerProviders.
1844
+ *
1845
+ * Custom registrations take precedence over built-in defaults.
1846
+ * Unknown providers receive an auto-generated fallback configuration.
1847
+ */
1848
+ class ProviderRegistryService {
1849
+ environment;
1850
+ customProviders = new Map();
1851
+ constructor(environment) {
1852
+ this.environment = environment;
1853
+ // Seed from environment customProviders if present
1854
+ if (this.environment.customProviders) {
1855
+ for (const [id, config] of Object.entries(this.environment.customProviders)) {
1856
+ this.customProviders.set(id, config);
1857
+ }
1858
+ }
1859
+ }
1860
+ /**
1861
+ * Register a custom OAuth provider configuration.
1862
+ * If a provider with the same id already exists, it will be overwritten.
1863
+ * @param id - Provider identifier (e.g., 'okta', 'auth0')
1864
+ * @param config - Provider display configuration
1865
+ */
1866
+ registerProvider(id, config) {
1867
+ this.customProviders.set(id, config);
1868
+ }
1869
+ /**
1870
+ * Register multiple custom OAuth provider configurations at once.
1871
+ * @param providers - Record of provider id to configuration
1872
+ */
1873
+ registerProviders(providers) {
1874
+ for (const [id, config] of Object.entries(providers)) {
1875
+ this.customProviders.set(id, config);
1876
+ }
1877
+ }
1878
+ /**
1879
+ * Get the full configuration for a provider.
1880
+ * Resolution order: custom registration > built-in default > auto-generated fallback.
1881
+ * @param provider - Provider identifier
1882
+ */
1883
+ getProviderConfig(provider) {
1884
+ // 1. Check custom registrations first
1885
+ const custom = this.customProviders.get(provider);
1886
+ if (custom) {
1887
+ return custom;
1888
+ }
1889
+ // 2. Check built-in providers
1890
+ const builtIn = BUILT_IN_PROVIDERS[provider];
1891
+ if (builtIn) {
1892
+ return builtIn;
1893
+ }
1894
+ // 3. Auto-generated fallback for unknown providers
1895
+ const displayName = provider.charAt(0).toUpperCase() + provider.slice(1);
1896
+ return {
1897
+ label: displayName,
1898
+ cssClass: `btn-${provider}`
1899
+ };
1900
+ }
1901
+ /**
1902
+ * Get the display label for a provider, formatted for sign-in context.
1903
+ * @param provider - Provider identifier
1904
+ * @returns Label like "Sign in with Google"
1905
+ */
1906
+ getLabel(provider) {
1907
+ const config = this.getProviderConfig(provider);
1908
+ if (provider === 'emailPassword') {
1909
+ return `Sign in with ${config.label}`;
1910
+ }
1911
+ return `Sign in with ${config.label}`;
1912
+ }
1913
+ /**
1914
+ * Get the display label for a provider, formatted for sign-up context.
1915
+ * @param provider - Provider identifier
1916
+ * @returns Label like "Sign up with Google"
1917
+ */
1918
+ getSignupLabel(provider) {
1919
+ const config = this.getProviderConfig(provider);
1920
+ return `Sign up with ${config.label}`;
1921
+ }
1922
+ /**
1923
+ * Get the icon for a provider, if configured.
1924
+ * @param provider - Provider identifier
1925
+ * @returns Icon string or undefined
1926
+ */
1927
+ getIcon(provider) {
1928
+ const config = this.getProviderConfig(provider);
1929
+ return config.icon;
1930
+ }
1931
+ /**
1932
+ * Get the CSS class for a provider button.
1933
+ * @param provider - Provider identifier
1934
+ * @returns CSS class string (e.g., "btn-google")
1935
+ */
1936
+ getCssClass(provider) {
1937
+ const config = this.getProviderConfig(provider);
1938
+ return config.cssClass || `btn-${provider}`;
1939
+ }
1940
+ /**
1941
+ * Get inline button styles for a provider, if configured.
1942
+ * @param provider - Provider identifier
1943
+ * @returns Style object for ngStyle binding, or null if no custom styles
1944
+ */
1945
+ getButtonStyle(provider) {
1946
+ const config = this.getProviderConfig(provider);
1947
+ if (!config.buttonStyle) {
1948
+ return null;
1949
+ }
1950
+ const styles = {};
1951
+ if (config.buttonStyle.borderColor) {
1952
+ styles['border-color'] = config.buttonStyle.borderColor;
1953
+ }
1954
+ if (config.buttonStyle.backgroundColor) {
1955
+ styles['background-color'] = config.buttonStyle.backgroundColor;
1956
+ }
1957
+ if (config.buttonStyle.color) {
1958
+ styles['color'] = config.buttonStyle.color;
1959
+ }
1960
+ return Object.keys(styles).length > 0 ? styles : null;
1961
+ }
1962
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ProviderRegistryService, deps: [{ token: MyEnvironmentModel }], target: i0.ɵɵFactoryTarget.Injectable });
1963
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ProviderRegistryService, providedIn: 'root' });
1964
+ }
1965
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ProviderRegistryService, decorators: [{
1966
+ type: Injectable,
1967
+ args: [{
1968
+ providedIn: 'root'
1969
+ }]
1970
+ }], ctorParameters: () => [{ type: MyEnvironmentModel, decorators: [{
1971
+ type: Inject,
1972
+ args: [MyEnvironmentModel]
1973
+ }] }] });
1974
+
1765
1975
  class NgxStoneScriptPhpClientModule {
1766
1976
  static forRoot(environment) {
1767
1977
  return {
@@ -1787,6 +1997,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
1787
1997
 
1788
1998
  class TenantLoginComponent {
1789
1999
  auth;
2000
+ providerRegistry;
1790
2001
  // Component Configuration
1791
2002
  title = 'Sign In';
1792
2003
  providers = ['google'];
@@ -1820,8 +2031,9 @@ class TenantLoginComponent {
1820
2031
  memberships = [];
1821
2032
  selectedTenantId = null;
1822
2033
  userName = '';
1823
- constructor(auth) {
2034
+ constructor(auth, providerRegistry) {
1824
2035
  this.auth = auth;
2036
+ this.providerRegistry = providerRegistry;
1825
2037
  }
1826
2038
  ngOnInit() {
1827
2039
  if (!this.providers || this.providers.length === 0) {
@@ -1843,22 +2055,16 @@ class TenantLoginComponent {
1843
2055
  return this.providers.includes(provider);
1844
2056
  }
1845
2057
  getProviderLabel(provider) {
1846
- const labels = {
1847
- google: 'Sign in with Google',
1848
- linkedin: 'Sign in with LinkedIn',
1849
- apple: 'Sign in with Apple',
1850
- microsoft: 'Sign in with Microsoft',
1851
- github: 'Sign in with GitHub',
1852
- zoho: 'Sign in with Zoho',
1853
- emailPassword: 'Sign in with Email'
1854
- };
1855
- return labels[provider];
2058
+ return this.providerRegistry.getLabel(provider);
1856
2059
  }
1857
2060
  getProviderIcon(provider) {
1858
- const icons = {
1859
- zoho: '🔶'
1860
- };
1861
- return icons[provider];
2061
+ return this.providerRegistry.getIcon(provider);
2062
+ }
2063
+ getProviderCssClass(provider) {
2064
+ return this.providerRegistry.getCssClass(provider);
2065
+ }
2066
+ getProviderButtonStyle(provider) {
2067
+ return this.providerRegistry.getButtonStyle(provider);
1862
2068
  }
1863
2069
  toggleAuthMethod(event) {
1864
2070
  event.preventDefault();
@@ -2016,7 +2222,7 @@ class TenantLoginComponent {
2016
2222
  event.preventDefault();
2017
2223
  this.createTenant.emit();
2018
2224
  }
2019
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TenantLoginComponent, deps: [{ token: AuthService }], target: i0.ɵɵFactoryTarget.Component });
2225
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TenantLoginComponent, deps: [{ token: AuthService }, { token: ProviderRegistryService }], target: i0.ɵɵFactoryTarget.Component });
2020
2226
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", 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", createTenant: "createTenant" }, ngImport: i0, template: `
2021
2227
  <div class="tenant-login-dialog">
2022
2228
  @if (!showingTenantSelector) {
@@ -2075,7 +2281,8 @@ class TenantLoginComponent {
2075
2281
  type="button"
2076
2282
  (click)="onOAuthLogin(provider)"
2077
2283
  [disabled]="loading"
2078
- class="btn btn-oauth btn-{{ provider }}">
2284
+ [class]="'btn btn-oauth ' + getProviderCssClass(provider)"
2285
+ [ngStyle]="getProviderButtonStyle(provider)">
2079
2286
  @if (getProviderIcon(provider)) {
2080
2287
  <span class="oauth-icon">
2081
2288
  {{ getProviderIcon(provider) }}
@@ -2182,7 +2389,7 @@ class TenantLoginComponent {
2182
2389
  </div>
2183
2390
  }
2184
2391
  </div>
2185
- `, 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,.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}.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}.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: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }] });
2392
+ `, 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,.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}.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}.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"] }] });
2186
2393
  }
2187
2394
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TenantLoginComponent, decorators: [{
2188
2395
  type: Component,
@@ -2244,7 +2451,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
2244
2451
  type="button"
2245
2452
  (click)="onOAuthLogin(provider)"
2246
2453
  [disabled]="loading"
2247
- class="btn btn-oauth btn-{{ provider }}">
2454
+ [class]="'btn btn-oauth ' + getProviderCssClass(provider)"
2455
+ [ngStyle]="getProviderButtonStyle(provider)">
2248
2456
  @if (getProviderIcon(provider)) {
2249
2457
  <span class="oauth-icon">
2250
2458
  {{ getProviderIcon(provider) }}
@@ -2352,7 +2560,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
2352
2560
  }
2353
2561
  </div>
2354
2562
  `, 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,.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}.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}.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"] }]
2355
- }], ctorParameters: () => [{ type: AuthService }], propDecorators: { title: [{
2563
+ }], ctorParameters: () => [{ type: AuthService }, { type: ProviderRegistryService }], propDecorators: { title: [{
2356
2564
  type: Input
2357
2565
  }], providers: [{
2358
2566
  type: Input
@@ -2616,7 +2824,7 @@ class RegisterComponent {
2616
2824
  <a href="#" (click)="onLoginClick($event)">Sign in</a>
2617
2825
  </div>
2618
2826
  </div>
2619
- `, isInline: true, styles: [".register-dialog{padding:24px;max-width:400px;position:relative}.register-title{margin:0 0 24px;font-size:24px;font-weight:500;text-align:center}.register-form,.form-group{margin-bottom:16px}.password-group{position:relative}.form-group label{display:block;margin-bottom:6px;font-size:14px;font-weight:500;color:#333}.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:38px;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}.form-hint{display:block;margin-top:4px;font-size:12px;color:#666}.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}.error-message{margin-top:16px;padding:12px;background:#fee;color:#c33;border-radius:4px;font-size:14px}.success-message{margin-top:16px;padding:12px;background:#efe;color:#3a3;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)}}.login-link{margin-top:16px;text-align:center;font-size:14px;color:#666}.login-link a{color:#4285f4;text-decoration:none}.login-link a:hover{text-decoration:underline}.account-link-prompt{background:#f8f9fa;border:2px solid #4285f4;border-radius:8px;padding:24px;margin-bottom:16px;text-align:center}.prompt-icon{font-size:48px;margin-bottom:12px}.account-link-prompt h3{margin:0 0 12px;color:#333;font-size:20px;font-weight:500}.account-link-prompt p{margin:8px 0;color:#555;font-size:14px;line-height:1.6}.prompt-actions{margin-top:20px;display:flex;flex-direction:column;gap:10px}.btn-secondary{background:#fff;color:#333;border:1px solid #ddd}.btn-secondary:hover:not(:disabled){background:#f8f9fa;border-color:#ccc}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }] });
2827
+ `, isInline: true, styles: [".register-dialog{padding:24px;max-width:400px;position:relative}.register-title{margin:0 0 24px;font-size:24px;font-weight:500;text-align:center}.register-form,.form-group{margin-bottom:16px}.password-group{position:relative}.form-group label{display:block;margin-bottom:6px;font-size:14px;font-weight:500;color:#333}.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:38px;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}.form-hint{display:block;margin-top:4px;font-size:12px;color:#666}.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}.error-message{margin-top:16px;padding:12px;background:#fee;color:#c33;border-radius:4px;font-size:14px}.success-message{margin-top:16px;padding:12px;background:#efe;color:#3a3;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)}}.login-link{margin-top:16px;text-align:center;font-size:14px;color:#666}.login-link a{color:#4285f4;text-decoration:none}.login-link a:hover{text-decoration:underline}.account-link-prompt{background:#f8f9fa;border:2px solid #4285f4;border-radius:8px;padding:24px;margin-bottom:16px;text-align:center}.prompt-icon{font-size:48px;margin-bottom:12px}.account-link-prompt h3{margin:0 0 12px;color:#333;font-size:20px;font-weight:500}.account-link-prompt p{margin:8px 0;color:#555;font-size:14px;line-height:1.6}.prompt-actions{margin-top:20px;display:flex;flex-direction:column;gap:10px}.btn-secondary{background:#fff;color:#333;border:1px solid #ddd}.btn-secondary:hover:not(:disabled){background:#f8f9fa;border-color:#ccc}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { 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.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { 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"] }] });
2620
2828
  }
2621
2829
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: RegisterComponent, decorators: [{
2622
2830
  type: Component,
@@ -2871,6 +3079,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
2871
3079
 
2872
3080
  class LoginDialogComponent {
2873
3081
  auth;
3082
+ providerRegistry;
2874
3083
  /**
2875
3084
  * REQUIRED: Which authentication providers to show in this dialog
2876
3085
  * @example ['google', 'linkedin', 'emailPassword']
@@ -2882,8 +3091,9 @@ class LoginDialogComponent {
2882
3091
  loading = false;
2883
3092
  showPassword = false;
2884
3093
  oauthProviders = [];
2885
- constructor(auth) {
3094
+ constructor(auth, providerRegistry) {
2886
3095
  this.auth = auth;
3096
+ this.providerRegistry = providerRegistry;
2887
3097
  }
2888
3098
  ngOnInit() {
2889
3099
  if (!this.providers || this.providers.length === 0) {
@@ -2898,20 +3108,16 @@ class LoginDialogComponent {
2898
3108
  return this.providers.includes(provider);
2899
3109
  }
2900
3110
  getProviderLabel(provider) {
2901
- const labels = {
2902
- google: 'Sign in with Google',
2903
- linkedin: 'Sign in with LinkedIn',
2904
- apple: 'Sign in with Apple',
2905
- microsoft: 'Sign in with Microsoft',
2906
- github: 'Sign in with GitHub',
2907
- zoho: 'Sign in with Zoho',
2908
- emailPassword: 'Sign in with Email'
2909
- };
2910
- return labels[provider];
3111
+ return this.providerRegistry.getLabel(provider);
2911
3112
  }
2912
3113
  getProviderIcon(provider) {
2913
- // Platforms can customize icons via CSS classes: .btn-google, .btn-linkedin, etc.
2914
- return undefined;
3114
+ return this.providerRegistry.getIcon(provider);
3115
+ }
3116
+ getProviderCssClass(provider) {
3117
+ return this.providerRegistry.getCssClass(provider);
3118
+ }
3119
+ getProviderButtonStyle(provider) {
3120
+ return this.providerRegistry.getButtonStyle(provider);
2915
3121
  }
2916
3122
  async onEmailLogin() {
2917
3123
  if (!this.email || !this.password) {
@@ -2957,7 +3163,7 @@ class LoginDialogComponent {
2957
3163
  // For now, just emit a console message
2958
3164
  console.log('Register clicked - platform should handle navigation');
2959
3165
  }
2960
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LoginDialogComponent, deps: [{ token: AuthService }], target: i0.ɵɵFactoryTarget.Component });
3166
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LoginDialogComponent, deps: [{ token: AuthService }, { token: ProviderRegistryService }], target: i0.ɵɵFactoryTarget.Component });
2961
3167
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: LoginDialogComponent, isStandalone: true, selector: "lib-login-dialog", inputs: { providers: "providers" }, ngImport: i0, template: `
2962
3168
  <div class="login-dialog">
2963
3169
  <h2 class="login-title">Sign In</h2>
@@ -3013,7 +3219,8 @@ class LoginDialogComponent {
3013
3219
  <button
3014
3220
  (click)="onOAuthLogin(provider)"
3015
3221
  [disabled]="loading"
3016
- class="btn btn-oauth btn-{{ provider }}">
3222
+ [class]="'btn btn-oauth ' + getProviderCssClass(provider)"
3223
+ [ngStyle]="getProviderButtonStyle(provider)">
3017
3224
  @if (getProviderIcon(provider)) {
3018
3225
  <span class="oauth-icon">
3019
3226
  {{ getProviderIcon(provider) }}
@@ -3045,7 +3252,7 @@ class LoginDialogComponent {
3045
3252
  <a href="#" (click)="onRegisterClick($event)">Sign up</a>
3046
3253
  </div>
3047
3254
  </div>
3048
- `, isInline: true, styles: [".login-dialog{padding:24px;max-width:400px;position:relative}.login-title{margin:0 0 24px;font-size:24px;font-weight:500;text-align:center}.email-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}.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}.oauth-icon{font-size:18px}.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}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }] });
3255
+ `, isInline: true, styles: [".login-dialog{padding:24px;max-width:400px;position:relative}.login-title{margin:0 0 24px;font-size:24px;font-weight:500;text-align:center}.email-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}.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}.oauth-icon{font-size:18px}.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}\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"] }] });
3049
3256
  }
3050
3257
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LoginDialogComponent, decorators: [{
3051
3258
  type: Component,
@@ -3104,7 +3311,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
3104
3311
  <button
3105
3312
  (click)="onOAuthLogin(provider)"
3106
3313
  [disabled]="loading"
3107
- class="btn btn-oauth btn-{{ provider }}">
3314
+ [class]="'btn btn-oauth ' + getProviderCssClass(provider)"
3315
+ [ngStyle]="getProviderButtonStyle(provider)">
3108
3316
  @if (getProviderIcon(provider)) {
3109
3317
  <span class="oauth-icon">
3110
3318
  {{ getProviderIcon(provider) }}
@@ -3137,12 +3345,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
3137
3345
  </div>
3138
3346
  </div>
3139
3347
  `, styles: [".login-dialog{padding:24px;max-width:400px;position:relative}.login-title{margin:0 0 24px;font-size:24px;font-weight:500;text-align:center}.email-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}.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}.oauth-icon{font-size:18px}.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}\n"] }]
3140
- }], ctorParameters: () => [{ type: AuthService }], propDecorators: { providers: [{
3348
+ }], ctorParameters: () => [{ type: AuthService }, { type: ProviderRegistryService }], propDecorators: { providers: [{
3141
3349
  type: Input
3142
3350
  }] } });
3143
3351
 
3144
3352
  class TenantRegisterComponent {
3145
3353
  auth;
3354
+ providerRegistry;
3146
3355
  // Component Configuration
3147
3356
  title = 'Create New Organization';
3148
3357
  providers = ['google'];
@@ -3187,8 +3396,9 @@ class TenantRegisterComponent {
3187
3396
  oauthProviders = [];
3188
3397
  showPassword = false;
3189
3398
  showConfirmPassword = false;
3190
- constructor(auth) {
3399
+ constructor(auth, providerRegistry) {
3191
3400
  this.auth = auth;
3401
+ this.providerRegistry = providerRegistry;
3192
3402
  }
3193
3403
  ngOnInit() {
3194
3404
  if (!this.providers || this.providers.length === 0) {
@@ -3205,19 +3415,16 @@ class TenantRegisterComponent {
3205
3415
  return this.providers.includes(provider);
3206
3416
  }
3207
3417
  getProviderLabel(provider) {
3208
- const labels = {
3209
- google: 'Sign up with Google',
3210
- linkedin: 'Sign up with LinkedIn',
3211
- apple: 'Sign up with Apple',
3212
- microsoft: 'Sign up with Microsoft',
3213
- github: 'Sign up with GitHub',
3214
- zoho: 'Sign up with Zoho',
3215
- emailPassword: 'Sign up with Email'
3216
- };
3217
- return labels[provider];
3418
+ return this.providerRegistry.getSignupLabel(provider);
3218
3419
  }
3219
3420
  getProviderIcon(provider) {
3220
- return undefined;
3421
+ return this.providerRegistry.getIcon(provider);
3422
+ }
3423
+ getProviderCssClass(provider) {
3424
+ return this.providerRegistry.getCssClass(provider);
3425
+ }
3426
+ getProviderButtonStyle(provider) {
3427
+ return this.providerRegistry.getButtonStyle(provider);
3221
3428
  }
3222
3429
  onTenantNameChange() {
3223
3430
  // Auto-generate slug from tenant name
@@ -3363,7 +3570,7 @@ class TenantRegisterComponent {
3363
3570
  event.preventDefault();
3364
3571
  this.navigateToLogin.emit();
3365
3572
  }
3366
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TenantRegisterComponent, deps: [{ token: AuthService }], target: i0.ɵɵFactoryTarget.Component });
3573
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TenantRegisterComponent, deps: [{ token: AuthService }, { token: ProviderRegistryService }], target: i0.ɵɵFactoryTarget.Component });
3367
3574
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: TenantRegisterComponent, isStandalone: true, selector: "lib-tenant-register", inputs: { title: "title", providers: "providers", requireTenantName: "requireTenantName", tenantSectionTitle: "tenantSectionTitle", tenantNameLabel: "tenantNameLabel", tenantNamePlaceholder: "tenantNamePlaceholder", tenantSlugLabel: "tenantSlugLabel", tenantSlugPlaceholder: "tenantSlugPlaceholder", urlPreviewEnabled: "urlPreviewEnabled", urlPreviewPrefix: "urlPreviewPrefix", userSectionTitle: "userSectionTitle", oauthDescription: "oauthDescription", ownershipTitle: "ownershipTitle", ownershipMessage: "ownershipMessage", submitButtonText: "submitButtonText", loginLinkText: "loginLinkText", loginLinkAction: "loginLinkAction" }, outputs: { tenantCreated: "tenantCreated", navigateToLogin: "navigateToLogin" }, ngImport: i0, template: `
3368
3575
  <div class="tenant-register-dialog">
3369
3576
  <h2 class="register-title">{{ title }}</h2>
@@ -3437,7 +3644,8 @@ class TenantRegisterComponent {
3437
3644
  type="button"
3438
3645
  (click)="onOAuthRegister(provider)"
3439
3646
  [disabled]="loading || !isFormValid()"
3440
- class="btn btn-oauth btn-{{ provider }}">
3647
+ [class]="'btn btn-oauth ' + getProviderCssClass(provider)"
3648
+ [ngStyle]="getProviderButtonStyle(provider)">
3441
3649
  @if (getProviderIcon(provider)) {
3442
3650
  <span class="oauth-icon">
3443
3651
  {{ getProviderIcon(provider) }}
@@ -3571,7 +3779,7 @@ class TenantRegisterComponent {
3571
3779
  <a href="#" (click)="onLoginClick($event)">{{ loginLinkAction }}</a>
3572
3780
  </div>
3573
3781
  </div>
3574
- `, isInline: true, styles: [".tenant-register-dialog{padding:24px;max-width:500px;position:relative}.register-title{margin:0 0 20px;font-size:24px;font-weight:500;text-align:center}.warning-box{display:flex;gap:12px;padding:16px;background:#fff3cd;border:1px solid #ffc107;border-radius:6px;margin-bottom:24px}.warning-icon{font-size:24px;line-height:1}.warning-content{flex:1}.warning-content strong{display:block;margin-bottom:4px;color:#856404;font-size:14px}.warning-content p{margin:0;color:#856404;font-size:13px;line-height:1.5}.section-header{font-size:16px;font-weight:600;margin:20px 0 12px;padding-bottom:8px;border-bottom:2px solid #e0e0e0;color:#333}.register-form,.form-group{margin-bottom:16px}.password-group{position:relative}.form-group label{display:block;margin-bottom:6px;font-size:14px;font-weight:500;color:#333}.form-control{width:100%;padding:12px;border:1px solid #ddd;border-radius:4px;font-size:14px;box-sizing:border-box;transition:border-color .2s}.password-input{padding-right:45px}.password-toggle{position:absolute;right:8px;top:38px;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}.form-control.input-error{border-color:#dc3545}.form-control.input-success{border-color:#28a745}.form-hint{display:block;margin-top:4px;font-size:12px;color:#666}.form-hint .checking{color:#666}.form-hint .available{color:#28a745;font-weight:500}.form-hint .error-text{color:#dc3545}.oauth-section{margin:16px 0}.oauth-description{margin-bottom:12px;font-size:14px;color:#666;text-align:center}.oauth-buttons{display:flex;flex-direction:column;gap:12px}.btn{padding:12px 24px;border:none;border-radius:4px;font-size:14px;font-weight:500;cursor:pointer;transition:background-color .2s,opacity .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}.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}.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}.error-message{margin-top:16px;padding:12px;background:#fee;color:#c33;border-radius:4px;font-size:14px}.success-message{margin-top:16px;padding:12px;background:#efe;color:#3a3;border-radius:4px;font-size:14px}.loading-overlay{position:absolute;inset:0;background:#fffffff2;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px}.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)}}.loading-text{margin:0;font-size:14px;color:#666}.login-link{margin-top:16px;text-align:center;font-size:14px;color:#666}.login-link a{color:#4285f4;text-decoration:none}.login-link a:hover{text-decoration:underline}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i2.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }] });
3782
+ `, isInline: true, styles: [".tenant-register-dialog{padding:24px;max-width:500px;position:relative}.register-title{margin:0 0 20px;font-size:24px;font-weight:500;text-align:center}.warning-box{display:flex;gap:12px;padding:16px;background:#fff3cd;border:1px solid #ffc107;border-radius:6px;margin-bottom:24px}.warning-icon{font-size:24px;line-height:1}.warning-content{flex:1}.warning-content strong{display:block;margin-bottom:4px;color:#856404;font-size:14px}.warning-content p{margin:0;color:#856404;font-size:13px;line-height:1.5}.section-header{font-size:16px;font-weight:600;margin:20px 0 12px;padding-bottom:8px;border-bottom:2px solid #e0e0e0;color:#333}.register-form,.form-group{margin-bottom:16px}.password-group{position:relative}.form-group label{display:block;margin-bottom:6px;font-size:14px;font-weight:500;color:#333}.form-control{width:100%;padding:12px;border:1px solid #ddd;border-radius:4px;font-size:14px;box-sizing:border-box;transition:border-color .2s}.password-input{padding-right:45px}.password-toggle{position:absolute;right:8px;top:38px;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}.form-control.input-error{border-color:#dc3545}.form-control.input-success{border-color:#28a745}.form-hint{display:block;margin-top:4px;font-size:12px;color:#666}.form-hint .checking{color:#666}.form-hint .available{color:#28a745;font-weight:500}.form-hint .error-text{color:#dc3545}.oauth-section{margin:16px 0}.oauth-description{margin-bottom:12px;font-size:14px;color:#666;text-align:center}.oauth-buttons{display:flex;flex-direction:column;gap:12px}.btn{padding:12px 24px;border:none;border-radius:4px;font-size:14px;font-weight:500;cursor:pointer;transition:background-color .2s,opacity .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}.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}.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}.error-message{margin-top:16px;padding:12px;background:#fee;color:#c33;border-radius:4px;font-size:14px}.success-message{margin-top:16px;padding:12px;background:#efe;color:#3a3;border-radius:4px;font-size:14px}.loading-overlay{position:absolute;inset:0;background:#fffffff2;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px}.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)}}.loading-text{margin:0;font-size:14px;color:#666}.login-link{margin-top:16px;text-align:center;font-size:14px;color:#666}.login-link a{color:#4285f4;text-decoration:none}.login-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.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i4.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { 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"] }] });
3575
3783
  }
3576
3784
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TenantRegisterComponent, decorators: [{
3577
3785
  type: Component,
@@ -3648,7 +3856,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
3648
3856
  type="button"
3649
3857
  (click)="onOAuthRegister(provider)"
3650
3858
  [disabled]="loading || !isFormValid()"
3651
- class="btn btn-oauth btn-{{ provider }}">
3859
+ [class]="'btn btn-oauth ' + getProviderCssClass(provider)"
3860
+ [ngStyle]="getProviderButtonStyle(provider)">
3652
3861
  @if (getProviderIcon(provider)) {
3653
3862
  <span class="oauth-icon">
3654
3863
  {{ getProviderIcon(provider) }}
@@ -3783,7 +3992,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
3783
3992
  </div>
3784
3993
  </div>
3785
3994
  `, styles: [".tenant-register-dialog{padding:24px;max-width:500px;position:relative}.register-title{margin:0 0 20px;font-size:24px;font-weight:500;text-align:center}.warning-box{display:flex;gap:12px;padding:16px;background:#fff3cd;border:1px solid #ffc107;border-radius:6px;margin-bottom:24px}.warning-icon{font-size:24px;line-height:1}.warning-content{flex:1}.warning-content strong{display:block;margin-bottom:4px;color:#856404;font-size:14px}.warning-content p{margin:0;color:#856404;font-size:13px;line-height:1.5}.section-header{font-size:16px;font-weight:600;margin:20px 0 12px;padding-bottom:8px;border-bottom:2px solid #e0e0e0;color:#333}.register-form,.form-group{margin-bottom:16px}.password-group{position:relative}.form-group label{display:block;margin-bottom:6px;font-size:14px;font-weight:500;color:#333}.form-control{width:100%;padding:12px;border:1px solid #ddd;border-radius:4px;font-size:14px;box-sizing:border-box;transition:border-color .2s}.password-input{padding-right:45px}.password-toggle{position:absolute;right:8px;top:38px;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}.form-control.input-error{border-color:#dc3545}.form-control.input-success{border-color:#28a745}.form-hint{display:block;margin-top:4px;font-size:12px;color:#666}.form-hint .checking{color:#666}.form-hint .available{color:#28a745;font-weight:500}.form-hint .error-text{color:#dc3545}.oauth-section{margin:16px 0}.oauth-description{margin-bottom:12px;font-size:14px;color:#666;text-align:center}.oauth-buttons{display:flex;flex-direction:column;gap:12px}.btn{padding:12px 24px;border:none;border-radius:4px;font-size:14px;font-weight:500;cursor:pointer;transition:background-color .2s,opacity .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}.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}.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}.error-message{margin-top:16px;padding:12px;background:#fee;color:#c33;border-radius:4px;font-size:14px}.success-message{margin-top:16px;padding:12px;background:#efe;color:#3a3;border-radius:4px;font-size:14px}.loading-overlay{position:absolute;inset:0;background:#fffffff2;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px}.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)}}.loading-text{margin:0;font-size:14px;color:#666}.login-link{margin-top:16px;text-align:center;font-size:14px;color:#666}.login-link a{color:#4285f4;text-decoration:none}.login-link a:hover{text-decoration:underline}\n"] }]
3786
- }], ctorParameters: () => [{ type: AuthService }], propDecorators: { title: [{
3995
+ }], ctorParameters: () => [{ type: AuthService }, { type: ProviderRegistryService }], propDecorators: { title: [{
3787
3996
  type: Input
3788
3997
  }], providers: [{
3789
3998
  type: Input
@@ -4049,5 +4258,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
4049
4258
  * Generated bundle index. Do not edit.
4050
4259
  */
4051
4260
 
4052
- export { ApiConnectionService, ApiResponse, AuthPageComponent, AuthService, CsrfService, DbService, FilesService, LoginDialogComponent, MyEnvironmentModel, NgxStoneScriptPhpClientModule, RegisterComponent, SigninStatusService, TenantLoginComponent, TenantLoginDialogComponent, TenantRegisterComponent, TenantRegisterDialogComponent, TokenService, VerifyStatus };
4261
+ export { ApiConnectionService, ApiResponse, AuthPageComponent, AuthService, CsrfService, DbService, FilesService, LoginDialogComponent, MyEnvironmentModel, NgxStoneScriptPhpClientModule, ProviderRegistryService, RegisterComponent, SigninStatusService, TenantLoginComponent, TenantLoginDialogComponent, TenantRegisterComponent, TenantRegisterDialogComponent, TokenService, VerifyStatus };
4053
4262
  //# sourceMappingURL=progalaxyelabs-ngx-stonescriptphp-client.mjs.map