@progalaxyelabs/ngx-stonescriptphp-client 1.5.1 → 1.8.1
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
|
|
4
|
+
import * as i3 from '@angular/common';
|
|
5
5
|
import { CommonModule } from '@angular/common';
|
|
6
|
-
import * as
|
|
6
|
+
import * as i4 from '@angular/forms';
|
|
7
7
|
import { FormsModule } from '@angular/forms';
|
|
8
8
|
|
|
9
9
|
class ApiResponse {
|
|
@@ -196,6 +196,19 @@ 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;
|
|
206
|
+
/**
|
|
207
|
+
* Files service server configuration
|
|
208
|
+
* Used by FilesService for file upload/download operations
|
|
209
|
+
* @example { host: 'https://files.progalaxyelabs.com/api/' }
|
|
210
|
+
*/
|
|
211
|
+
filesServer;
|
|
199
212
|
/**
|
|
200
213
|
* Authentication configuration
|
|
201
214
|
* @default { mode: 'cookie', refreshEndpoint: '/auth/refresh', useCsrf: true }
|
|
@@ -208,6 +221,19 @@ class MyEnvironmentModel {
|
|
|
208
221
|
csrfTokenCookieName: 'csrf_token',
|
|
209
222
|
csrfHeaderName: 'X-CSRF-Token'
|
|
210
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;
|
|
211
237
|
/**
|
|
212
238
|
* Branding configuration for auth components
|
|
213
239
|
* Allows platforms to customize login/register pages without creating wrappers
|
|
@@ -264,7 +290,7 @@ class ApiConnectionService {
|
|
|
264
290
|
signinStatus;
|
|
265
291
|
environment;
|
|
266
292
|
csrf;
|
|
267
|
-
host = ''; //
|
|
293
|
+
host = ''; // base URL without trailing slash
|
|
268
294
|
accessToken = '';
|
|
269
295
|
authConfig;
|
|
270
296
|
constructor(tokens, signinStatus, environment, csrf) {
|
|
@@ -328,7 +354,7 @@ class ApiConnectionService {
|
|
|
328
354
|
return new ApiResponse('error');
|
|
329
355
|
}
|
|
330
356
|
async get(endpoint, queryParamsObj) {
|
|
331
|
-
const url = this.host + endpoint
|
|
357
|
+
const url = this.host + endpoint + this.buildQueryString(queryParamsObj);
|
|
332
358
|
const fetchOptions = {
|
|
333
359
|
mode: 'cors',
|
|
334
360
|
redirect: 'error'
|
|
@@ -336,7 +362,7 @@ class ApiConnectionService {
|
|
|
336
362
|
return this.request(url, fetchOptions, null);
|
|
337
363
|
}
|
|
338
364
|
async post(pathWithQueryParams, data) {
|
|
339
|
-
const url = this.host + pathWithQueryParams
|
|
365
|
+
const url = this.host + pathWithQueryParams;
|
|
340
366
|
const fetchOptions = {
|
|
341
367
|
method: 'POST',
|
|
342
368
|
mode: 'cors',
|
|
@@ -349,7 +375,7 @@ class ApiConnectionService {
|
|
|
349
375
|
return this.request(url, fetchOptions, data);
|
|
350
376
|
}
|
|
351
377
|
async put(pathWithQueryParams, data) {
|
|
352
|
-
const url = this.host + pathWithQueryParams
|
|
378
|
+
const url = this.host + pathWithQueryParams;
|
|
353
379
|
const fetchOptions = {
|
|
354
380
|
method: 'PUT',
|
|
355
381
|
mode: 'cors',
|
|
@@ -362,7 +388,7 @@ class ApiConnectionService {
|
|
|
362
388
|
return this.request(url, fetchOptions, data);
|
|
363
389
|
}
|
|
364
390
|
async patch(pathWithQueryParams, data) {
|
|
365
|
-
const url = this.host + pathWithQueryParams
|
|
391
|
+
const url = this.host + pathWithQueryParams;
|
|
366
392
|
const fetchOptions = {
|
|
367
393
|
method: 'PATCH',
|
|
368
394
|
mode: 'cors',
|
|
@@ -375,7 +401,7 @@ class ApiConnectionService {
|
|
|
375
401
|
return this.request(url, fetchOptions, data);
|
|
376
402
|
}
|
|
377
403
|
async delete(endpoint, queryParamsObj) {
|
|
378
|
-
const url = this.host + endpoint
|
|
404
|
+
const url = this.host + endpoint + this.buildQueryString(queryParamsObj);
|
|
379
405
|
const fetchOptions = {
|
|
380
406
|
method: 'DELETE',
|
|
381
407
|
mode: 'cors',
|
|
@@ -441,7 +467,11 @@ class ApiConnectionService {
|
|
|
441
467
|
*/
|
|
442
468
|
async refreshAccessTokenCookieMode() {
|
|
443
469
|
try {
|
|
444
|
-
|
|
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;
|
|
445
475
|
const headers = {
|
|
446
476
|
'Content-Type': 'application/json'
|
|
447
477
|
};
|
|
@@ -497,7 +527,11 @@ class ApiConnectionService {
|
|
|
497
527
|
if (!refreshToken) {
|
|
498
528
|
return false;
|
|
499
529
|
}
|
|
500
|
-
|
|
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;
|
|
501
535
|
let refreshTokenResponse = await fetch(refreshTokenUrl, {
|
|
502
536
|
method: 'POST',
|
|
503
537
|
mode: 'cors',
|
|
@@ -557,7 +591,7 @@ class ApiConnectionService {
|
|
|
557
591
|
*/
|
|
558
592
|
async uploadDrawing(formData) {
|
|
559
593
|
const uploadHost = this.environment.uploadServer?.host || this.host;
|
|
560
|
-
const url = uploadHost + 'upload/drawing';
|
|
594
|
+
const url = uploadHost + '/upload/drawing';
|
|
561
595
|
const fetchOptions = {
|
|
562
596
|
method: 'POST',
|
|
563
597
|
mode: 'cors',
|
|
@@ -572,7 +606,7 @@ class ApiConnectionService {
|
|
|
572
606
|
*/
|
|
573
607
|
async uploadImage(formData) {
|
|
574
608
|
const uploadHost = this.environment.uploadServer?.host || this.host;
|
|
575
|
-
const url = uploadHost + 'upload/image';
|
|
609
|
+
const url = uploadHost + '/upload/image';
|
|
576
610
|
const fetchOptions = {
|
|
577
611
|
method: 'POST',
|
|
578
612
|
mode: 'cors',
|
|
@@ -1492,6 +1526,452 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
1492
1526
|
}]
|
|
1493
1527
|
}], ctorParameters: () => [] });
|
|
1494
1528
|
|
|
1529
|
+
/**
|
|
1530
|
+
* Service for interacting with the stonescriptphp-files server.
|
|
1531
|
+
* Handles file upload, download, list, and delete operations
|
|
1532
|
+
* with automatic Bearer token injection and 401 refresh handling.
|
|
1533
|
+
*/
|
|
1534
|
+
class FilesService {
|
|
1535
|
+
tokens;
|
|
1536
|
+
signinStatus;
|
|
1537
|
+
environment;
|
|
1538
|
+
csrf;
|
|
1539
|
+
host = '';
|
|
1540
|
+
apiHost = '';
|
|
1541
|
+
authConfig;
|
|
1542
|
+
constructor(tokens, signinStatus, environment, csrf) {
|
|
1543
|
+
this.tokens = tokens;
|
|
1544
|
+
this.signinStatus = signinStatus;
|
|
1545
|
+
this.environment = environment;
|
|
1546
|
+
this.csrf = csrf;
|
|
1547
|
+
this.host = environment.filesServer?.host || '';
|
|
1548
|
+
this.apiHost = environment.apiServer.host;
|
|
1549
|
+
this.authConfig = {
|
|
1550
|
+
mode: environment.auth?.mode || 'cookie',
|
|
1551
|
+
refreshEndpoint: environment.auth?.refreshEndpoint,
|
|
1552
|
+
useCsrf: environment.auth?.useCsrf,
|
|
1553
|
+
refreshTokenCookieName: environment.auth?.refreshTokenCookieName || 'refresh_token',
|
|
1554
|
+
csrfTokenCookieName: environment.auth?.csrfTokenCookieName || 'csrf_token',
|
|
1555
|
+
csrfHeaderName: environment.auth?.csrfHeaderName || 'X-CSRF-Token'
|
|
1556
|
+
};
|
|
1557
|
+
if (!this.authConfig.refreshEndpoint) {
|
|
1558
|
+
this.authConfig.refreshEndpoint = this.authConfig.mode === 'cookie'
|
|
1559
|
+
? '/auth/refresh'
|
|
1560
|
+
: '/user/refresh_access';
|
|
1561
|
+
}
|
|
1562
|
+
if (this.authConfig.useCsrf === undefined) {
|
|
1563
|
+
this.authConfig.useCsrf = this.authConfig.mode === 'cookie';
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
/**
|
|
1567
|
+
* Check if the files server is configured.
|
|
1568
|
+
*/
|
|
1569
|
+
isConfigured() {
|
|
1570
|
+
return !!this.host;
|
|
1571
|
+
}
|
|
1572
|
+
/**
|
|
1573
|
+
* Upload a file to the files service.
|
|
1574
|
+
* Uses FormData — does NOT set Content-Type header (browser sets multipart boundary).
|
|
1575
|
+
*
|
|
1576
|
+
* @param file The File object to upload
|
|
1577
|
+
* @param entityType Optional entity type for server-side reference linking
|
|
1578
|
+
* @param entityId Optional entity ID for server-side reference linking
|
|
1579
|
+
* @returns Promise resolving to the upload result
|
|
1580
|
+
*/
|
|
1581
|
+
async upload(file, entityType, entityId) {
|
|
1582
|
+
const formData = new FormData();
|
|
1583
|
+
formData.append('file', file);
|
|
1584
|
+
if (entityType) {
|
|
1585
|
+
formData.append('entityType', entityType);
|
|
1586
|
+
}
|
|
1587
|
+
if (entityId) {
|
|
1588
|
+
formData.append('entityId', entityId);
|
|
1589
|
+
}
|
|
1590
|
+
const url = this.host + 'upload';
|
|
1591
|
+
const options = {
|
|
1592
|
+
method: 'POST',
|
|
1593
|
+
mode: 'cors',
|
|
1594
|
+
redirect: 'error',
|
|
1595
|
+
body: formData
|
|
1596
|
+
// No Content-Type header — browser sets multipart boundary automatically
|
|
1597
|
+
};
|
|
1598
|
+
const response = await this.requestWithRetry(url, options);
|
|
1599
|
+
return response.file;
|
|
1600
|
+
}
|
|
1601
|
+
/**
|
|
1602
|
+
* Download a file from the files service.
|
|
1603
|
+
* Returns a Blob suitable for URL.createObjectURL().
|
|
1604
|
+
*
|
|
1605
|
+
* @param fileId UUID of the file to download
|
|
1606
|
+
* @returns Promise resolving to the file Blob
|
|
1607
|
+
*/
|
|
1608
|
+
async download(fileId) {
|
|
1609
|
+
const url = this.host + 'files/' + fileId;
|
|
1610
|
+
const options = {
|
|
1611
|
+
method: 'GET',
|
|
1612
|
+
mode: 'cors',
|
|
1613
|
+
redirect: 'error'
|
|
1614
|
+
};
|
|
1615
|
+
this.includeAccessToken(options);
|
|
1616
|
+
let response = await fetch(url, options);
|
|
1617
|
+
if (response.status === 401) {
|
|
1618
|
+
const refreshed = await this.refreshAccessToken();
|
|
1619
|
+
if (refreshed) {
|
|
1620
|
+
this.includeAccessToken(options);
|
|
1621
|
+
response = await fetch(url, options);
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
if (response.status === 401) {
|
|
1625
|
+
this.signinStatus.signedOut();
|
|
1626
|
+
}
|
|
1627
|
+
if (!response.ok) {
|
|
1628
|
+
throw new Error(`Download failed: ${response.status} ${response.statusText}`);
|
|
1629
|
+
}
|
|
1630
|
+
return response.blob();
|
|
1631
|
+
}
|
|
1632
|
+
/**
|
|
1633
|
+
* List all files for the current user.
|
|
1634
|
+
*
|
|
1635
|
+
* @returns Promise resolving to array of file metadata
|
|
1636
|
+
*/
|
|
1637
|
+
async list() {
|
|
1638
|
+
const url = this.host + 'files';
|
|
1639
|
+
const options = {
|
|
1640
|
+
method: 'GET',
|
|
1641
|
+
mode: 'cors',
|
|
1642
|
+
redirect: 'error'
|
|
1643
|
+
};
|
|
1644
|
+
const response = await this.requestWithRetry(url, options);
|
|
1645
|
+
return response.files;
|
|
1646
|
+
}
|
|
1647
|
+
/**
|
|
1648
|
+
* Delete a file from the files service.
|
|
1649
|
+
*
|
|
1650
|
+
* @param fileId UUID of the file to delete
|
|
1651
|
+
* @returns Promise resolving to true on success
|
|
1652
|
+
*/
|
|
1653
|
+
async delete(fileId) {
|
|
1654
|
+
const url = this.host + 'files/' + fileId;
|
|
1655
|
+
const options = {
|
|
1656
|
+
method: 'DELETE',
|
|
1657
|
+
mode: 'cors',
|
|
1658
|
+
redirect: 'error'
|
|
1659
|
+
};
|
|
1660
|
+
const response = await this.requestWithRetry(url, options);
|
|
1661
|
+
return response.success;
|
|
1662
|
+
}
|
|
1663
|
+
/**
|
|
1664
|
+
* Make a request with automatic Bearer token injection and 401 retry.
|
|
1665
|
+
*/
|
|
1666
|
+
async requestWithRetry(url, options) {
|
|
1667
|
+
this.includeAccessToken(options);
|
|
1668
|
+
let response = await fetch(url, options);
|
|
1669
|
+
if (response.status === 401) {
|
|
1670
|
+
const refreshed = await this.refreshAccessToken();
|
|
1671
|
+
if (refreshed) {
|
|
1672
|
+
this.includeAccessToken(options);
|
|
1673
|
+
response = await fetch(url, options);
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
if (response.status === 401) {
|
|
1677
|
+
this.signinStatus.signedOut();
|
|
1678
|
+
}
|
|
1679
|
+
if (!response.ok) {
|
|
1680
|
+
const body = await response.json().catch(() => ({}));
|
|
1681
|
+
throw new Error(body.message || `Request failed: ${response.status}`);
|
|
1682
|
+
}
|
|
1683
|
+
return response.json();
|
|
1684
|
+
}
|
|
1685
|
+
includeAccessToken(options) {
|
|
1686
|
+
const accessToken = this.tokens.getAccessToken();
|
|
1687
|
+
if (!accessToken)
|
|
1688
|
+
return;
|
|
1689
|
+
if (!options.headers) {
|
|
1690
|
+
options.headers = {};
|
|
1691
|
+
}
|
|
1692
|
+
options.headers['Authorization'] = 'Bearer ' + accessToken;
|
|
1693
|
+
}
|
|
1694
|
+
async refreshAccessToken() {
|
|
1695
|
+
if (this.authConfig.mode === 'none') {
|
|
1696
|
+
return false;
|
|
1697
|
+
}
|
|
1698
|
+
if (this.authConfig.mode === 'cookie') {
|
|
1699
|
+
return this.refreshAccessTokenCookieMode();
|
|
1700
|
+
}
|
|
1701
|
+
else {
|
|
1702
|
+
return this.refreshAccessTokenBodyMode();
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
async refreshAccessTokenCookieMode() {
|
|
1706
|
+
try {
|
|
1707
|
+
const refreshTokenUrl = this.apiHost + this.authConfig.refreshEndpoint.replace(/^\/+/, '');
|
|
1708
|
+
const headers = {
|
|
1709
|
+
'Content-Type': 'application/json'
|
|
1710
|
+
};
|
|
1711
|
+
if (this.authConfig.useCsrf) {
|
|
1712
|
+
const csrfToken = this.csrf.getCsrfToken(this.authConfig.csrfTokenCookieName);
|
|
1713
|
+
if (!csrfToken) {
|
|
1714
|
+
return false;
|
|
1715
|
+
}
|
|
1716
|
+
headers[this.authConfig.csrfHeaderName] = csrfToken;
|
|
1717
|
+
}
|
|
1718
|
+
const response = await fetch(refreshTokenUrl, {
|
|
1719
|
+
method: 'POST',
|
|
1720
|
+
mode: 'cors',
|
|
1721
|
+
credentials: 'include',
|
|
1722
|
+
redirect: 'error',
|
|
1723
|
+
headers
|
|
1724
|
+
});
|
|
1725
|
+
if (!response.ok) {
|
|
1726
|
+
this.tokens.clear();
|
|
1727
|
+
return false;
|
|
1728
|
+
}
|
|
1729
|
+
const data = await response.json();
|
|
1730
|
+
if (!data || data.status !== 'ok') {
|
|
1731
|
+
return false;
|
|
1732
|
+
}
|
|
1733
|
+
const newAccessToken = data.data?.access_token || data.access_token;
|
|
1734
|
+
if (!newAccessToken) {
|
|
1735
|
+
return false;
|
|
1736
|
+
}
|
|
1737
|
+
this.tokens.setAccessToken(newAccessToken);
|
|
1738
|
+
return true;
|
|
1739
|
+
}
|
|
1740
|
+
catch {
|
|
1741
|
+
this.tokens.clear();
|
|
1742
|
+
return false;
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
async refreshAccessTokenBodyMode() {
|
|
1746
|
+
try {
|
|
1747
|
+
const refreshToken = this.tokens.getRefreshToken();
|
|
1748
|
+
if (!refreshToken) {
|
|
1749
|
+
return false;
|
|
1750
|
+
}
|
|
1751
|
+
const accessToken = this.tokens.getAccessToken();
|
|
1752
|
+
const refreshTokenUrl = this.apiHost + this.authConfig.refreshEndpoint.replace(/^\/+/, '');
|
|
1753
|
+
const response = await fetch(refreshTokenUrl, {
|
|
1754
|
+
method: 'POST',
|
|
1755
|
+
mode: 'cors',
|
|
1756
|
+
redirect: 'error',
|
|
1757
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1758
|
+
body: JSON.stringify({
|
|
1759
|
+
access_token: accessToken,
|
|
1760
|
+
refresh_token: refreshToken
|
|
1761
|
+
})
|
|
1762
|
+
});
|
|
1763
|
+
if (!response.ok) {
|
|
1764
|
+
this.tokens.clear();
|
|
1765
|
+
return false;
|
|
1766
|
+
}
|
|
1767
|
+
const data = await response.json();
|
|
1768
|
+
if (!data) {
|
|
1769
|
+
return false;
|
|
1770
|
+
}
|
|
1771
|
+
const newAccessToken = data.data?.access_token || data.access_token;
|
|
1772
|
+
if (!newAccessToken) {
|
|
1773
|
+
return false;
|
|
1774
|
+
}
|
|
1775
|
+
this.tokens.setTokens(newAccessToken, refreshToken);
|
|
1776
|
+
return true;
|
|
1777
|
+
}
|
|
1778
|
+
catch {
|
|
1779
|
+
this.tokens.clear();
|
|
1780
|
+
return false;
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FilesService, deps: [{ token: TokenService }, { token: SigninStatusService }, { token: MyEnvironmentModel }, { token: CsrfService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1784
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FilesService, providedIn: 'root' });
|
|
1785
|
+
}
|
|
1786
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FilesService, decorators: [{
|
|
1787
|
+
type: Injectable,
|
|
1788
|
+
args: [{
|
|
1789
|
+
providedIn: 'root'
|
|
1790
|
+
}]
|
|
1791
|
+
}], ctorParameters: () => [{ type: TokenService }, { type: SigninStatusService }, { type: MyEnvironmentModel }, { type: CsrfService }] });
|
|
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
|
+
|
|
1495
1975
|
class NgxStoneScriptPhpClientModule {
|
|
1496
1976
|
static forRoot(environment) {
|
|
1497
1977
|
return {
|
|
@@ -1517,6 +1997,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
1517
1997
|
|
|
1518
1998
|
class TenantLoginComponent {
|
|
1519
1999
|
auth;
|
|
2000
|
+
providerRegistry;
|
|
1520
2001
|
// Component Configuration
|
|
1521
2002
|
title = 'Sign In';
|
|
1522
2003
|
providers = ['google'];
|
|
@@ -1550,8 +2031,9 @@ class TenantLoginComponent {
|
|
|
1550
2031
|
memberships = [];
|
|
1551
2032
|
selectedTenantId = null;
|
|
1552
2033
|
userName = '';
|
|
1553
|
-
constructor(auth) {
|
|
2034
|
+
constructor(auth, providerRegistry) {
|
|
1554
2035
|
this.auth = auth;
|
|
2036
|
+
this.providerRegistry = providerRegistry;
|
|
1555
2037
|
}
|
|
1556
2038
|
ngOnInit() {
|
|
1557
2039
|
if (!this.providers || this.providers.length === 0) {
|
|
@@ -1573,22 +2055,16 @@ class TenantLoginComponent {
|
|
|
1573
2055
|
return this.providers.includes(provider);
|
|
1574
2056
|
}
|
|
1575
2057
|
getProviderLabel(provider) {
|
|
1576
|
-
|
|
1577
|
-
google: 'Sign in with Google',
|
|
1578
|
-
linkedin: 'Sign in with LinkedIn',
|
|
1579
|
-
apple: 'Sign in with Apple',
|
|
1580
|
-
microsoft: 'Sign in with Microsoft',
|
|
1581
|
-
github: 'Sign in with GitHub',
|
|
1582
|
-
zoho: 'Sign in with Zoho',
|
|
1583
|
-
emailPassword: 'Sign in with Email'
|
|
1584
|
-
};
|
|
1585
|
-
return labels[provider];
|
|
2058
|
+
return this.providerRegistry.getLabel(provider);
|
|
1586
2059
|
}
|
|
1587
2060
|
getProviderIcon(provider) {
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
return
|
|
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);
|
|
1592
2068
|
}
|
|
1593
2069
|
toggleAuthMethod(event) {
|
|
1594
2070
|
event.preventDefault();
|
|
@@ -1746,7 +2222,7 @@ class TenantLoginComponent {
|
|
|
1746
2222
|
event.preventDefault();
|
|
1747
2223
|
this.createTenant.emit();
|
|
1748
2224
|
}
|
|
1749
|
-
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 });
|
|
1750
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: `
|
|
1751
2227
|
<div class="tenant-login-dialog">
|
|
1752
2228
|
@if (!showingTenantSelector) {
|
|
@@ -1805,7 +2281,8 @@ class TenantLoginComponent {
|
|
|
1805
2281
|
type="button"
|
|
1806
2282
|
(click)="onOAuthLogin(provider)"
|
|
1807
2283
|
[disabled]="loading"
|
|
1808
|
-
class="btn btn-oauth
|
|
2284
|
+
[class]="'btn btn-oauth ' + getProviderCssClass(provider)"
|
|
2285
|
+
[ngStyle]="getProviderButtonStyle(provider)">
|
|
1809
2286
|
@if (getProviderIcon(provider)) {
|
|
1810
2287
|
<span class="oauth-icon">
|
|
1811
2288
|
{{ getProviderIcon(provider) }}
|
|
@@ -1912,7 +2389,7 @@ class TenantLoginComponent {
|
|
|
1912
2389
|
</div>
|
|
1913
2390
|
}
|
|
1914
2391
|
</div>
|
|
1915
|
-
`, 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:
|
|
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"] }] });
|
|
1916
2393
|
}
|
|
1917
2394
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TenantLoginComponent, decorators: [{
|
|
1918
2395
|
type: Component,
|
|
@@ -1974,7 +2451,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
1974
2451
|
type="button"
|
|
1975
2452
|
(click)="onOAuthLogin(provider)"
|
|
1976
2453
|
[disabled]="loading"
|
|
1977
|
-
class="btn btn-oauth
|
|
2454
|
+
[class]="'btn btn-oauth ' + getProviderCssClass(provider)"
|
|
2455
|
+
[ngStyle]="getProviderButtonStyle(provider)">
|
|
1978
2456
|
@if (getProviderIcon(provider)) {
|
|
1979
2457
|
<span class="oauth-icon">
|
|
1980
2458
|
{{ getProviderIcon(provider) }}
|
|
@@ -2082,7 +2560,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
2082
2560
|
}
|
|
2083
2561
|
</div>
|
|
2084
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"] }]
|
|
2085
|
-
}], ctorParameters: () => [{ type: AuthService }], propDecorators: { title: [{
|
|
2563
|
+
}], ctorParameters: () => [{ type: AuthService }, { type: ProviderRegistryService }], propDecorators: { title: [{
|
|
2086
2564
|
type: Input
|
|
2087
2565
|
}], providers: [{
|
|
2088
2566
|
type: Input
|
|
@@ -2346,7 +2824,7 @@ class RegisterComponent {
|
|
|
2346
2824
|
<a href="#" (click)="onLoginClick($event)">Sign in</a>
|
|
2347
2825
|
</div>
|
|
2348
2826
|
</div>
|
|
2349
|
-
`, 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:
|
|
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"] }] });
|
|
2350
2828
|
}
|
|
2351
2829
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: RegisterComponent, decorators: [{
|
|
2352
2830
|
type: Component,
|
|
@@ -2601,6 +3079,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
2601
3079
|
|
|
2602
3080
|
class LoginDialogComponent {
|
|
2603
3081
|
auth;
|
|
3082
|
+
providerRegistry;
|
|
2604
3083
|
/**
|
|
2605
3084
|
* REQUIRED: Which authentication providers to show in this dialog
|
|
2606
3085
|
* @example ['google', 'linkedin', 'emailPassword']
|
|
@@ -2612,8 +3091,9 @@ class LoginDialogComponent {
|
|
|
2612
3091
|
loading = false;
|
|
2613
3092
|
showPassword = false;
|
|
2614
3093
|
oauthProviders = [];
|
|
2615
|
-
constructor(auth) {
|
|
3094
|
+
constructor(auth, providerRegistry) {
|
|
2616
3095
|
this.auth = auth;
|
|
3096
|
+
this.providerRegistry = providerRegistry;
|
|
2617
3097
|
}
|
|
2618
3098
|
ngOnInit() {
|
|
2619
3099
|
if (!this.providers || this.providers.length === 0) {
|
|
@@ -2628,20 +3108,16 @@ class LoginDialogComponent {
|
|
|
2628
3108
|
return this.providers.includes(provider);
|
|
2629
3109
|
}
|
|
2630
3110
|
getProviderLabel(provider) {
|
|
2631
|
-
|
|
2632
|
-
google: 'Sign in with Google',
|
|
2633
|
-
linkedin: 'Sign in with LinkedIn',
|
|
2634
|
-
apple: 'Sign in with Apple',
|
|
2635
|
-
microsoft: 'Sign in with Microsoft',
|
|
2636
|
-
github: 'Sign in with GitHub',
|
|
2637
|
-
zoho: 'Sign in with Zoho',
|
|
2638
|
-
emailPassword: 'Sign in with Email'
|
|
2639
|
-
};
|
|
2640
|
-
return labels[provider];
|
|
3111
|
+
return this.providerRegistry.getLabel(provider);
|
|
2641
3112
|
}
|
|
2642
3113
|
getProviderIcon(provider) {
|
|
2643
|
-
|
|
2644
|
-
|
|
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);
|
|
2645
3121
|
}
|
|
2646
3122
|
async onEmailLogin() {
|
|
2647
3123
|
if (!this.email || !this.password) {
|
|
@@ -2687,7 +3163,7 @@ class LoginDialogComponent {
|
|
|
2687
3163
|
// For now, just emit a console message
|
|
2688
3164
|
console.log('Register clicked - platform should handle navigation');
|
|
2689
3165
|
}
|
|
2690
|
-
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 });
|
|
2691
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: `
|
|
2692
3168
|
<div class="login-dialog">
|
|
2693
3169
|
<h2 class="login-title">Sign In</h2>
|
|
@@ -2743,7 +3219,8 @@ class LoginDialogComponent {
|
|
|
2743
3219
|
<button
|
|
2744
3220
|
(click)="onOAuthLogin(provider)"
|
|
2745
3221
|
[disabled]="loading"
|
|
2746
|
-
class="btn btn-oauth
|
|
3222
|
+
[class]="'btn btn-oauth ' + getProviderCssClass(provider)"
|
|
3223
|
+
[ngStyle]="getProviderButtonStyle(provider)">
|
|
2747
3224
|
@if (getProviderIcon(provider)) {
|
|
2748
3225
|
<span class="oauth-icon">
|
|
2749
3226
|
{{ getProviderIcon(provider) }}
|
|
@@ -2775,7 +3252,7 @@ class LoginDialogComponent {
|
|
|
2775
3252
|
<a href="#" (click)="onRegisterClick($event)">Sign up</a>
|
|
2776
3253
|
</div>
|
|
2777
3254
|
</div>
|
|
2778
|
-
`, 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:
|
|
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"] }] });
|
|
2779
3256
|
}
|
|
2780
3257
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LoginDialogComponent, decorators: [{
|
|
2781
3258
|
type: Component,
|
|
@@ -2834,7 +3311,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
2834
3311
|
<button
|
|
2835
3312
|
(click)="onOAuthLogin(provider)"
|
|
2836
3313
|
[disabled]="loading"
|
|
2837
|
-
class="btn btn-oauth
|
|
3314
|
+
[class]="'btn btn-oauth ' + getProviderCssClass(provider)"
|
|
3315
|
+
[ngStyle]="getProviderButtonStyle(provider)">
|
|
2838
3316
|
@if (getProviderIcon(provider)) {
|
|
2839
3317
|
<span class="oauth-icon">
|
|
2840
3318
|
{{ getProviderIcon(provider) }}
|
|
@@ -2867,12 +3345,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
2867
3345
|
</div>
|
|
2868
3346
|
</div>
|
|
2869
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"] }]
|
|
2870
|
-
}], ctorParameters: () => [{ type: AuthService }], propDecorators: { providers: [{
|
|
3348
|
+
}], ctorParameters: () => [{ type: AuthService }, { type: ProviderRegistryService }], propDecorators: { providers: [{
|
|
2871
3349
|
type: Input
|
|
2872
3350
|
}] } });
|
|
2873
3351
|
|
|
2874
3352
|
class TenantRegisterComponent {
|
|
2875
3353
|
auth;
|
|
3354
|
+
providerRegistry;
|
|
2876
3355
|
// Component Configuration
|
|
2877
3356
|
title = 'Create New Organization';
|
|
2878
3357
|
providers = ['google'];
|
|
@@ -2917,8 +3396,9 @@ class TenantRegisterComponent {
|
|
|
2917
3396
|
oauthProviders = [];
|
|
2918
3397
|
showPassword = false;
|
|
2919
3398
|
showConfirmPassword = false;
|
|
2920
|
-
constructor(auth) {
|
|
3399
|
+
constructor(auth, providerRegistry) {
|
|
2921
3400
|
this.auth = auth;
|
|
3401
|
+
this.providerRegistry = providerRegistry;
|
|
2922
3402
|
}
|
|
2923
3403
|
ngOnInit() {
|
|
2924
3404
|
if (!this.providers || this.providers.length === 0) {
|
|
@@ -2935,19 +3415,16 @@ class TenantRegisterComponent {
|
|
|
2935
3415
|
return this.providers.includes(provider);
|
|
2936
3416
|
}
|
|
2937
3417
|
getProviderLabel(provider) {
|
|
2938
|
-
|
|
2939
|
-
google: 'Sign up with Google',
|
|
2940
|
-
linkedin: 'Sign up with LinkedIn',
|
|
2941
|
-
apple: 'Sign up with Apple',
|
|
2942
|
-
microsoft: 'Sign up with Microsoft',
|
|
2943
|
-
github: 'Sign up with GitHub',
|
|
2944
|
-
zoho: 'Sign up with Zoho',
|
|
2945
|
-
emailPassword: 'Sign up with Email'
|
|
2946
|
-
};
|
|
2947
|
-
return labels[provider];
|
|
3418
|
+
return this.providerRegistry.getSignupLabel(provider);
|
|
2948
3419
|
}
|
|
2949
3420
|
getProviderIcon(provider) {
|
|
2950
|
-
return
|
|
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);
|
|
2951
3428
|
}
|
|
2952
3429
|
onTenantNameChange() {
|
|
2953
3430
|
// Auto-generate slug from tenant name
|
|
@@ -3093,7 +3570,7 @@ class TenantRegisterComponent {
|
|
|
3093
3570
|
event.preventDefault();
|
|
3094
3571
|
this.navigateToLogin.emit();
|
|
3095
3572
|
}
|
|
3096
|
-
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 });
|
|
3097
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: `
|
|
3098
3575
|
<div class="tenant-register-dialog">
|
|
3099
3576
|
<h2 class="register-title">{{ title }}</h2>
|
|
@@ -3167,7 +3644,8 @@ class TenantRegisterComponent {
|
|
|
3167
3644
|
type="button"
|
|
3168
3645
|
(click)="onOAuthRegister(provider)"
|
|
3169
3646
|
[disabled]="loading || !isFormValid()"
|
|
3170
|
-
class="btn btn-oauth
|
|
3647
|
+
[class]="'btn btn-oauth ' + getProviderCssClass(provider)"
|
|
3648
|
+
[ngStyle]="getProviderButtonStyle(provider)">
|
|
3171
3649
|
@if (getProviderIcon(provider)) {
|
|
3172
3650
|
<span class="oauth-icon">
|
|
3173
3651
|
{{ getProviderIcon(provider) }}
|
|
@@ -3301,7 +3779,7 @@ class TenantRegisterComponent {
|
|
|
3301
3779
|
<a href="#" (click)="onLoginClick($event)">{{ loginLinkAction }}</a>
|
|
3302
3780
|
</div>
|
|
3303
3781
|
</div>
|
|
3304
|
-
`, 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:
|
|
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"] }] });
|
|
3305
3783
|
}
|
|
3306
3784
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TenantRegisterComponent, decorators: [{
|
|
3307
3785
|
type: Component,
|
|
@@ -3378,7 +3856,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
3378
3856
|
type="button"
|
|
3379
3857
|
(click)="onOAuthRegister(provider)"
|
|
3380
3858
|
[disabled]="loading || !isFormValid()"
|
|
3381
|
-
class="btn btn-oauth
|
|
3859
|
+
[class]="'btn btn-oauth ' + getProviderCssClass(provider)"
|
|
3860
|
+
[ngStyle]="getProviderButtonStyle(provider)">
|
|
3382
3861
|
@if (getProviderIcon(provider)) {
|
|
3383
3862
|
<span class="oauth-icon">
|
|
3384
3863
|
{{ getProviderIcon(provider) }}
|
|
@@ -3513,7 +3992,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
3513
3992
|
</div>
|
|
3514
3993
|
</div>
|
|
3515
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"] }]
|
|
3516
|
-
}], ctorParameters: () => [{ type: AuthService }], propDecorators: { title: [{
|
|
3995
|
+
}], ctorParameters: () => [{ type: AuthService }, { type: ProviderRegistryService }], propDecorators: { title: [{
|
|
3517
3996
|
type: Input
|
|
3518
3997
|
}], providers: [{
|
|
3519
3998
|
type: Input
|
|
@@ -3779,5 +4258,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
3779
4258
|
* Generated bundle index. Do not edit.
|
|
3780
4259
|
*/
|
|
3781
4260
|
|
|
3782
|
-
export { ApiConnectionService, ApiResponse, AuthPageComponent, AuthService, CsrfService, DbService, 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 };
|
|
3783
4262
|
//# sourceMappingURL=progalaxyelabs-ngx-stonescriptphp-client.mjs.map
|