@rolatech/angular-platform 20.3.0-beta.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.
- package/README.md +3 -0
- package/fesm2022/rolatech-angular-platform-application-create-DqPMquaO.mjs +129 -0
- package/fesm2022/rolatech-angular-platform-application-create-DqPMquaO.mjs.map +1 -0
- package/fesm2022/rolatech-angular-platform-application-detail-BoITs_yE.mjs +54 -0
- package/fesm2022/rolatech-angular-platform-application-detail-BoITs_yE.mjs.map +1 -0
- package/fesm2022/rolatech-angular-platform-application-index-BlCbJVDc.mjs +82 -0
- package/fesm2022/rolatech-angular-platform-application-index-BlCbJVDc.mjs.map +1 -0
- package/fesm2022/rolatech-angular-platform-application-organization-index-D7TJ8CID.mjs +54 -0
- package/fesm2022/rolatech-angular-platform-application-organization-index-D7TJ8CID.mjs.map +1 -0
- package/fesm2022/rolatech-angular-platform-application-role-index-9_XVRBgD.mjs +66 -0
- package/fesm2022/rolatech-angular-platform-application-role-index-9_XVRBgD.mjs.map +1 -0
- package/fesm2022/rolatech-angular-platform-application-routes-DE5CJMgn.mjs +38 -0
- package/fesm2022/rolatech-angular-platform-application-routes-DE5CJMgn.mjs.map +1 -0
- package/fesm2022/rolatech-angular-platform-platform-auth-client-detail-BMvM7PvP.mjs +137 -0
- package/fesm2022/rolatech-angular-platform-platform-auth-client-detail-BMvM7PvP.mjs.map +1 -0
- package/fesm2022/rolatech-angular-platform-platform-auth-client-editor-75GPRUsF.mjs +281 -0
- package/fesm2022/rolatech-angular-platform-platform-auth-client-editor-75GPRUsF.mjs.map +1 -0
- package/fesm2022/rolatech-angular-platform-platform-auth-client-index-DilT3UzO.mjs +95 -0
- package/fesm2022/rolatech-angular-platform-platform-auth-client-index-DilT3UzO.mjs.map +1 -0
- package/fesm2022/rolatech-angular-platform-platform-auth-client.routes-D_VFpAgu.mjs +21 -0
- package/fesm2022/rolatech-angular-platform-platform-auth-client.routes-D_VFpAgu.mjs.map +1 -0
- package/fesm2022/rolatech-angular-platform-platform-endpoint-detail-CEaO9MFQ.mjs +57 -0
- package/fesm2022/rolatech-angular-platform-platform-endpoint-detail-CEaO9MFQ.mjs.map +1 -0
- package/fesm2022/rolatech-angular-platform-platform-endpoint-index-69W62bRP.mjs +92 -0
- package/fesm2022/rolatech-angular-platform-platform-endpoint-index-69W62bRP.mjs.map +1 -0
- package/fesm2022/rolatech-angular-platform-platform-endpoint.routes-Cj66S7N-.mjs +13 -0
- package/fesm2022/rolatech-angular-platform-platform-endpoint.routes-Cj66S7N-.mjs.map +1 -0
- package/fesm2022/rolatech-angular-platform-platform-role-index-8kRxDJiW.mjs +35 -0
- package/fesm2022/rolatech-angular-platform-platform-role-index-8kRxDJiW.mjs.map +1 -0
- package/fesm2022/rolatech-angular-platform-platform-service-registry-detail-CQ4Wk1R8.mjs +89 -0
- package/fesm2022/rolatech-angular-platform-platform-service-registry-detail-CQ4Wk1R8.mjs.map +1 -0
- package/fesm2022/rolatech-angular-platform-platform-service-registry-editor-Bd3nIfxd.mjs +246 -0
- package/fesm2022/rolatech-angular-platform-platform-service-registry-editor-Bd3nIfxd.mjs.map +1 -0
- package/fesm2022/rolatech-angular-platform-platform-service-registry-index-D4470pau.mjs +95 -0
- package/fesm2022/rolatech-angular-platform-platform-service-registry-index-D4470pau.mjs.map +1 -0
- package/fesm2022/rolatech-angular-platform-platform-service-registry.routes-DlWUwwws.mjs +21 -0
- package/fesm2022/rolatech-angular-platform-platform-service-registry.routes-DlWUwwws.mjs.map +1 -0
- package/fesm2022/rolatech-angular-platform-platform-user-detail-fzo89PHV.mjs +81 -0
- package/fesm2022/rolatech-angular-platform-platform-user-detail-fzo89PHV.mjs.map +1 -0
- package/fesm2022/rolatech-angular-platform-platform-user-index-DBT4N0zi.mjs +89 -0
- package/fesm2022/rolatech-angular-platform-platform-user-index-DBT4N0zi.mjs.map +1 -0
- package/fesm2022/rolatech-angular-platform-platform-user.routes-C6OoAsWU.mjs +13 -0
- package/fesm2022/rolatech-angular-platform-platform-user.routes-C6OoAsWU.mjs.map +1 -0
- package/fesm2022/rolatech-angular-platform-role-permission-page-DslhArZQ.mjs +527 -0
- package/fesm2022/rolatech-angular-platform-role-permission-page-DslhArZQ.mjs.map +1 -0
- package/fesm2022/rolatech-angular-platform-role-permission-page.routes-IiX17wDW.mjs +11 -0
- package/fesm2022/rolatech-angular-platform-role-permission-page.routes-IiX17wDW.mjs.map +1 -0
- package/fesm2022/rolatech-angular-platform.mjs +293 -0
- package/fesm2022/rolatech-angular-platform.mjs.map +1 -0
- package/package.json +33 -0
- package/types/rolatech-angular-platform.d.ts +70 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rolatech-angular-platform-application-role-index-9_XVRBgD.mjs","sources":["../../../../packages/angular-platform/src/lib/store/application-role-index.facade.ts","../../../../packages/angular-platform/src/lib/pages/application/application-role-index/application-role-index.ts","../../../../packages/angular-platform/src/lib/pages/application/application-role-index/application-role-index.html"],"sourcesContent":["import { Injectable, inject, signal } from '@angular/core';\nimport { firstValueFrom } from 'rxjs';\nimport { RoleResponse, RoleService } from '@rolatech/angular-services';\n\nexport interface ApplicationRoleItem {\n id: string;\n name: string;\n code: string;\n scope: 'APP' | 'ORG';\n permissionCount: number;\n memberCount: number;\n status: 'ACTIVE' | 'INACTIVE' | 'DRAFT';\n}\n\n@Injectable()\nexport class ApplicationRoleIndexFacade {\n private readonly roleService = inject(RoleService);\n\n readonly loading = signal(false);\n readonly items = signal<ApplicationRoleItem[]>([]);\n\n async load(applicationId: string): Promise<void> {\n if (!applicationId) {\n this.items.set([]);\n return;\n }\n\n this.loading.set(true);\n try {\n const roles = await firstValueFrom(this.roleService.findApplicationRoles(applicationId));\n this.items.set((roles ?? []).map((role) => this.toRoleItem(role)));\n } finally {\n this.loading.set(false);\n }\n }\n\n private toRoleItem(role: RoleResponse): ApplicationRoleItem {\n return {\n id: role.id,\n name: role.name,\n code: role.code,\n scope: role.scope === 'ORG' ? 'ORG' : 'APP',\n permissionCount: role.permissionIds.length,\n memberCount: 0,\n status: role.enabled ? 'ACTIVE' : 'INACTIVE',\n };\n }\n}\n","import { ChangeDetectionStrategy, Component, OnInit, inject } from '@angular/core';\nimport { ActivatedRoute, Router } from '@angular/router';\nimport { PlatformPageHeader, PlatformDataTable, PlatformScopeBadge, PlatformStatusBadge } from '../../../shared';\nimport { ApplicationRoleIndexFacade } from '../../../store/application-role-index.facade';\n\n@Component({\n selector: 'rolatech-application-role-index',\n standalone: true,\n imports: [PlatformPageHeader, PlatformDataTable, PlatformScopeBadge, PlatformStatusBadge],\n templateUrl: './application-role-index.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [ApplicationRoleIndexFacade],\n})\nexport class ApplicationRoleIndex implements OnInit {\n readonly facade = inject(ApplicationRoleIndexFacade);\n private readonly route = inject(ActivatedRoute);\n private readonly router = inject(Router);\n\n readonly applicationId = this.route.snapshot.paramMap.get('appId') ?? this.route.snapshot.paramMap.get('id') ?? '';\n\n ngOnInit(): void {\n if (this.applicationId) {\n void this.facade.load(this.applicationId);\n }\n }\n\n openRole(id: string): void {\n void this.router.navigate([id, 'permissions'], { relativeTo: this.route });\n }\n}\n","<section class=\"rt-platform-theme flex flex-col gap-6 bg-(--rt-base-background) p-6 text-(--rt-text-primary)\">\n @if (facade.loading()) {\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-card p-10 text-center text-muted-foreground shadow-sm\">Loading roles...</section>\n } @else {\n <rolatech-platform-page-header>\n <div header-left class=\"space-y-1\">\n <h1 class=\"text-2xl font-semibold tracking-tight\">Application Roles</h1>\n <p class=\"text-sm text-muted-foreground\">Roles defined within this application.</p>\n </div>\n </rolatech-platform-page-header>\n\n <rolatech-platform-data-table>\n <div table-toolbar class=\"flex items-center justify-between gap-3\">\n <h2 class=\"text-base font-semibold\">Roles</h2>\n <div class=\"text-sm text-muted-foreground\">{{ facade.items().length }} items</div>\n </div>\n\n <thead class=\"bg-muted/40\">\n <tr class=\"text-left\">\n <th class=\"px-4 py-3 font-medium\">Name</th>\n <th class=\"px-4 py-3 font-medium\">Code</th>\n <th class=\"px-4 py-3 font-medium\">Scope</th>\n <th class=\"px-4 py-3 font-medium\">Permissions</th>\n <th class=\"px-4 py-3 font-medium\">Members</th>\n <th class=\"px-4 py-3 font-medium\">Status</th>\n <th class=\"px-4 py-3 font-medium\">Actions</th>\n </tr>\n </thead>\n\n <tbody>\n @if (facade.items().length === 0) {\n <tr>\n <td colspan=\"7\" class=\"px-4 py-10 text-center text-muted-foreground\">No roles found.</td>\n </tr>\n } @else { @for (item of facade.items(); track item.id) {\n <tr class=\"border-t border-(--rt-border-color)\">\n <td class=\"px-4 py-3 font-medium\">{{ item.name }}</td>\n <td class=\"px-4 py-3 text-muted-foreground\">{{ item.code }}</td>\n <td class=\"px-4 py-3\">\n <rolatech-platform-scope-badge [scope]=\"item.scope\" />\n </td>\n <td class=\"px-4 py-3\">{{ item.permissionCount }}</td>\n <td class=\"px-4 py-3\">{{ item.memberCount }}</td>\n <td class=\"px-4 py-3\">\n <rolatech-platform-status-badge [status]=\"item.status\" />\n </td>\n <td class=\"px-4 py-3\">\n <button\n type=\"button\"\n class=\"inline-flex items-center rounded-xl border border-(--rt-border-color) px-3 py-2 text-sm font-medium\"\n (click)=\"openRole(item.id)\"\n >\n View Role\n </button>\n </td>\n </tr>\n } }\n </tbody>\n </rolatech-platform-data-table>\n }\n</section>\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const APPLICATION_ROUTES = [
|
|
2
|
+
{
|
|
3
|
+
path: '',
|
|
4
|
+
loadComponent: () => import('./rolatech-angular-platform-application-index-BlCbJVDc.mjs').then((m) => m.ApplicationIndex),
|
|
5
|
+
},
|
|
6
|
+
{
|
|
7
|
+
path: 'new',
|
|
8
|
+
loadComponent: () => import('./rolatech-angular-platform-application-create-DqPMquaO.mjs').then((m) => m.ApplicationCreate),
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
path: ':appId',
|
|
12
|
+
loadComponent: () => import('./rolatech-angular-platform-application-detail-BoITs_yE.mjs').then((m) => m.ApplicationDetail),
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
path: ':appId/edit',
|
|
16
|
+
loadComponent: () => import('./rolatech-angular-platform-application-create-DqPMquaO.mjs').then((m) => m.ApplicationCreate),
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
path: ':appId/organizations',
|
|
20
|
+
loadComponent: () => import('./rolatech-angular-platform-application-organization-index-D7TJ8CID.mjs').then((m) => m.ApplicationOrganizationIndex),
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
path: ':appId/settings',
|
|
24
|
+
loadComponent: () => import('./rolatech-angular-platform-application-detail-BoITs_yE.mjs').then((m) => m.ApplicationDetail),
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
path: ':appId/roles',
|
|
28
|
+
loadComponent: () => import('./rolatech-angular-platform-application-role-index-9_XVRBgD.mjs').then((m) => m.ApplicationRoleIndex),
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
path: ':appId/roles/:id/permissions',
|
|
32
|
+
data: { scope: 'application' },
|
|
33
|
+
loadComponent: () => import('./rolatech-angular-platform-role-permission-page-DslhArZQ.mjs').then((m) => m.RolePermissionPage),
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
export { APPLICATION_ROUTES };
|
|
38
|
+
//# sourceMappingURL=rolatech-angular-platform-application-routes-DE5CJMgn.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rolatech-angular-platform-application-routes-DE5CJMgn.mjs","sources":["../../../../packages/angular-platform/src/lib/pages/application/application-routes.ts"],"sourcesContent":["import { Routes } from '@angular/router';\n\nexport const APPLICATION_ROUTES: Routes = [\n {\n path: '',\n loadComponent: () => import('./application-index/application-index').then((m) => m.ApplicationIndex),\n },\n {\n path: 'new',\n loadComponent: () => import('./application-create/application-create').then((m) => m.ApplicationCreate),\n },\n {\n path: ':appId',\n loadComponent: () => import('./application-detail/application-detail').then((m) => m.ApplicationDetail),\n },\n {\n path: ':appId/edit',\n loadComponent: () => import('./application-create/application-create').then((m) => m.ApplicationCreate),\n },\n {\n path: ':appId/organizations',\n loadComponent: () =>\n import('./application-organization-index/application-organization-index').then((m) => m.ApplicationOrganizationIndex),\n },\n\n {\n path: ':appId/settings',\n loadComponent: () => import('./application-detail/application-detail').then((m) => m.ApplicationDetail),\n },\n {\n path: ':appId/roles',\n loadComponent: () => import('./application-role-index/application-role-index').then((m) => m.ApplicationRoleIndex),\n },\n {\n path: ':appId/roles/:id/permissions',\n data: { scope: 'application' },\n loadComponent: () => import('../role/role-permission-page/role-permission-page').then((m) => m.RolePermissionPage),\n },\n];\n"],"names":[],"mappings":"AAEO,MAAM,kBAAkB,GAAW;AACxC,IAAA;AACE,QAAA,IAAI,EAAE,EAAE;AACR,QAAA,aAAa,EAAE,MAAM,OAAO,4DAAuC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,gBAAgB,CAAC;AACrG,KAAA;AACD,IAAA;AACE,QAAA,IAAI,EAAE,KAAK;AACX,QAAA,aAAa,EAAE,MAAM,OAAO,6DAAyC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,iBAAiB,CAAC;AACxG,KAAA;AACD,IAAA;AACE,QAAA,IAAI,EAAE,QAAQ;AACd,QAAA,aAAa,EAAE,MAAM,OAAO,6DAAyC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,iBAAiB,CAAC;AACxG,KAAA;AACD,IAAA;AACE,QAAA,IAAI,EAAE,aAAa;AACnB,QAAA,aAAa,EAAE,MAAM,OAAO,6DAAyC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,iBAAiB,CAAC;AACxG,KAAA;AACD,IAAA;AACE,QAAA,IAAI,EAAE,sBAAsB;AAC5B,QAAA,aAAa,EAAE,MACb,OAAO,yEAAiE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,4BAA4B,CAAC;AACxH,KAAA;AAED,IAAA;AACE,QAAA,IAAI,EAAE,iBAAiB;AACvB,QAAA,aAAa,EAAE,MAAM,OAAO,6DAAyC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,iBAAiB,CAAC;AACxG,KAAA;AACD,IAAA;AACE,QAAA,IAAI,EAAE,cAAc;AACpB,QAAA,aAAa,EAAE,MAAM,OAAO,iEAAiD,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,oBAAoB,CAAC;AACnH,KAAA;AACD,IAAA;AACE,QAAA,IAAI,EAAE,8BAA8B;AACpC,QAAA,IAAI,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE;AAC9B,QAAA,aAAa,EAAE,MAAM,OAAO,+DAAmD,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,kBAAkB,CAAC;AACnH,KAAA;;;;;"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { KeyValuePipe, JsonPipe } from '@angular/common';
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { inject, signal, Injectable, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
4
|
+
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
|
5
|
+
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
|
|
6
|
+
import { PlatformPageHeader, PlatformStatCard, PlatformDetailPanel, PlatformStatusBadge } from './rolatech-angular-platform.mjs';
|
|
7
|
+
import { firstValueFrom } from 'rxjs';
|
|
8
|
+
import { PlatformAuthClientService } from '@rolatech/angular-services';
|
|
9
|
+
|
|
10
|
+
class PlatformAuthClientDetailFacade {
|
|
11
|
+
authClientService = inject(PlatformAuthClientService);
|
|
12
|
+
loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
|
|
13
|
+
rotating = signal(false, ...(ngDevMode ? [{ debugName: "rotating" }] : []));
|
|
14
|
+
deleting = signal(false, ...(ngDevMode ? [{ debugName: "deleting" }] : []));
|
|
15
|
+
error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
|
|
16
|
+
item = signal(null, ...(ngDevMode ? [{ debugName: "item" }] : []));
|
|
17
|
+
latestSecret = signal(null, ...(ngDevMode ? [{ debugName: "latestSecret" }] : []));
|
|
18
|
+
seedLatestSecret(secret) {
|
|
19
|
+
this.latestSecret.set({ clientSecret: secret });
|
|
20
|
+
}
|
|
21
|
+
async load(clientId) {
|
|
22
|
+
this.loading.set(true);
|
|
23
|
+
this.error.set(null);
|
|
24
|
+
try {
|
|
25
|
+
this.item.set(await firstValueFrom(this.authClientService.findClientById(clientId)));
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
console.error(error);
|
|
29
|
+
this.item.set(null);
|
|
30
|
+
this.error.set('Unable to load auth client details.');
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
this.loading.set(false);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async rotateSecret(clientId) {
|
|
37
|
+
this.rotating.set(true);
|
|
38
|
+
this.error.set(null);
|
|
39
|
+
try {
|
|
40
|
+
const result = await firstValueFrom(this.authClientService.resetClientSecret(clientId));
|
|
41
|
+
this.latestSecret.set(result);
|
|
42
|
+
await this.load(clientId);
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.error(error);
|
|
47
|
+
this.error.set('Unable to rotate the auth client secret.');
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
this.rotating.set(false);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async delete(clientId) {
|
|
55
|
+
this.deleting.set(true);
|
|
56
|
+
this.error.set(null);
|
|
57
|
+
try {
|
|
58
|
+
await firstValueFrom(this.authClientService.deleteClient(clientId));
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.error(error);
|
|
63
|
+
this.error.set('Unable to delete the auth client.');
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
this.deleting.set(false);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PlatformAuthClientDetailFacade, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
71
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PlatformAuthClientDetailFacade });
|
|
72
|
+
}
|
|
73
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PlatformAuthClientDetailFacade, decorators: [{
|
|
74
|
+
type: Injectable
|
|
75
|
+
}] });
|
|
76
|
+
|
|
77
|
+
class PlatformAuthClientDetailPage {
|
|
78
|
+
facade = inject(PlatformAuthClientDetailFacade);
|
|
79
|
+
route = inject(ActivatedRoute);
|
|
80
|
+
router = inject(Router);
|
|
81
|
+
snackBar = inject(MatSnackBar);
|
|
82
|
+
clientId = this.route.snapshot.paramMap.get('clientId') ?? '';
|
|
83
|
+
ngOnInit() {
|
|
84
|
+
const createdSecret = globalThis.history?.state?.['createdSecret'];
|
|
85
|
+
if (typeof createdSecret === 'string' && createdSecret.length > 0) {
|
|
86
|
+
this.facade.seedLatestSecret(createdSecret);
|
|
87
|
+
}
|
|
88
|
+
if (this.clientId) {
|
|
89
|
+
void this.facade.load(this.clientId);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async rotateSecret() {
|
|
93
|
+
const result = await this.facade.rotateSecret(this.clientId);
|
|
94
|
+
if (!result?.clientSecret) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
await this.copySecret(result.clientSecret);
|
|
98
|
+
this.snackBar.open('Client secret rotated and copied.', 'Dismiss', { duration: 3000 });
|
|
99
|
+
}
|
|
100
|
+
async copyLatestSecret(secret) {
|
|
101
|
+
if (!secret) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
await this.copySecret(secret);
|
|
105
|
+
this.snackBar.open('Client secret copied.', 'Dismiss', { duration: 3000 });
|
|
106
|
+
}
|
|
107
|
+
async deleteClient() {
|
|
108
|
+
const item = this.facade.item();
|
|
109
|
+
if (!item) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (globalThis.confirm && !globalThis.confirm(`Delete auth client "${item.name}"? This cannot be undone.`)) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const deleted = await this.facade.delete(this.clientId);
|
|
116
|
+
if (!deleted) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
this.snackBar.open('Auth client deleted.', 'Dismiss', { duration: 3000 });
|
|
120
|
+
await this.router.navigate(['/platform/clients']);
|
|
121
|
+
}
|
|
122
|
+
async copySecret(secret) {
|
|
123
|
+
if (!globalThis.navigator?.clipboard) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
await globalThis.navigator.clipboard.writeText(secret);
|
|
127
|
+
}
|
|
128
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PlatformAuthClientDetailPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
129
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PlatformAuthClientDetailPage, isStandalone: true, selector: "rolatech-platform-auth-client-detail-page", providers: [PlatformAuthClientDetailFacade], ngImport: i0, template: "<section class=\"rt-platform-theme flex flex-col gap-6 bg-(--rt-base-background) p-6 text-(--rt-text-primary)\">\n @if (facade.loading()) {\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-card p-10 text-center text-muted-foreground shadow-sm\">Loading auth client...</section>\n } @else if (facade.item(); as item) {\n <rolatech-platform-page-header>\n <div header-left class=\"space-y-2\">\n <div class=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <a routerLink=\"../\" class=\"hover:underline cursor-pointer\">Auth Clients</a>\n <span>/</span>\n <span>{{ item.name }}</span>\n </div>\n\n <div>\n <h1 class=\"text-2xl font-semibold tracking-tight\">{{ item.name }}</h1>\n <div class=\"mt-1 text-sm text-muted-foreground break-all\">{{ item.clientId }}</div>\n </div>\n\n @if (item.description) {\n <p class=\"max-w-3xl text-sm text-muted-foreground\">{{ item.description }}</p>\n }\n\n <div class=\"flex flex-wrap gap-2\">\n <rolatech-platform-status-badge [status]=\"item.status || (item.enabled ? 'ENABLED' : 'DISABLED')\" />\n <span class=\"rt-platform-chip\">{{ item.confidential ? 'Confidential' : 'Public client' }}</span>\n </div>\n </div>\n\n <div header-actions class=\"flex flex-wrap gap-3\">\n <button\n type=\"button\"\n class=\"inline-flex items-center rounded-xl border border-(--rt-border-color) px-4 py-2 text-sm font-medium\"\n [routerLink]=\"['/platform/clients', item.id, 'edit']\"\n >\n Edit\n </button>\n <button\n type=\"button\"\n class=\"inline-flex items-center rounded-xl border border-rose-300/40 px-4 py-2 text-sm font-medium text-rose-200\"\n [disabled]=\"facade.deleting()\"\n (click)=\"deleteClient()\"\n >\n {{ facade.deleting() ? 'Deleting\u2026' : 'Delete' }}\n </button>\n <button\n type=\"button\"\n class=\"inline-flex items-center rounded-xl border border-(--rt-border-color) px-4 py-2 text-sm font-medium\"\n [disabled]=\"facade.rotating()\"\n (click)=\"rotateSecret()\"\n >\n {{ facade.rotating() ? 'Rotating\u2026' : 'Rotate Secret' }}\n </button>\n </div>\n </rolatech-platform-page-header>\n\n @if (facade.error()) {\n <section class=\"rounded-2xl border border-rose-300/40 bg-rose-500/10 px-4 py-3 text-sm text-(--rt-text-primary)\">\n {{ facade.error() }}\n </section>\n }\n\n <section class=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n <rolatech-platform-stat-card>\n <div stat-label>Grant Types</div>\n <div stat-value>{{ item.grantTypes.length }}</div>\n </rolatech-platform-stat-card>\n\n <rolatech-platform-stat-card>\n <div stat-label>Redirect URIs</div>\n <div stat-value>{{ item.redirectUris.length }}</div>\n </rolatech-platform-stat-card>\n\n <rolatech-platform-stat-card>\n <div stat-label>Scopes</div>\n <div stat-value>{{ item.scopes.length }}</div>\n </rolatech-platform-stat-card>\n\n <rolatech-platform-stat-card>\n <div stat-label>Permissions</div>\n <div stat-value>{{ item.permissions.length }}</div>\n </rolatech-platform-stat-card>\n </section>\n\n <section class=\"grid gap-6 xl:grid-cols-2\">\n <rolatech-platform-detail-panel>\n <div panel-title>Client Details</div>\n <div panel-description>Primary client configuration returned by the platform API.</div>\n\n <div class=\"grid gap-4 md:grid-cols-2\">\n <div>\n <div class=\"text-xs text-muted-foreground\">Client ID</div>\n <div class=\"font-medium break-all\">{{ item.clientId }}</div>\n </div>\n <div>\n <div class=\"text-xs text-muted-foreground\">App ID</div>\n <div class=\"font-medium break-all\">{{ item.appId || '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-muted-foreground\">Created At</div>\n <div class=\"font-medium\">{{ item.createdAt || '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-muted-foreground\">Updated At</div>\n <div class=\"font-medium\">{{ item.updatedAt || '\u2014' }}</div>\n </div>\n </div>\n </rolatech-platform-detail-panel>\n\n <rolatech-platform-detail-panel>\n <div panel-title>Secrets</div>\n <div panel-description>Current secret visibility depends on backend response. Rotating returns a new secret once.</div>\n\n <div class=\"space-y-4\">\n <div>\n <div class=\"text-xs text-muted-foreground\">Latest Secret</div>\n <div class=\"mt-2 break-all rounded-2xl border border-(--rt-border-color) bg-(--rt-raised-background) px-4 py-3 text-sm font-medium\">\n {{ facade.latestSecret()?.clientSecret || item.clientSecret || 'Not exposed by backend' }}\n </div>\n </div>\n\n @if (facade.latestSecret()?.clientSecret || item.clientSecret) {\n <button\n type=\"button\"\n class=\"inline-flex items-center rounded-xl border border-(--rt-border-color) px-4 py-2 text-sm font-medium\"\n (click)=\"copyLatestSecret(facade.latestSecret()?.clientSecret || item.clientSecret)\"\n >\n Copy Secret\n </button>\n }\n </div>\n </rolatech-platform-detail-panel>\n </section>\n\n <section class=\"grid gap-6 xl:grid-cols-2\">\n <rolatech-platform-detail-panel>\n <div panel-title>Grant Types</div>\n <div panel-description>OAuth grant flows allowed for this client.</div>\n\n <div class=\"flex flex-wrap gap-2\">\n @for (grantType of item.grantTypes; track grantType) {\n <span class=\"rt-platform-chip\">{{ grantType }}</span>\n } @if (item.grantTypes.length === 0) {\n <span class=\"text-sm text-muted-foreground\">No grant types returned.</span>\n }\n </div>\n </rolatech-platform-detail-panel>\n\n <rolatech-platform-detail-panel>\n <div panel-title>Redirect URIs</div>\n <div panel-description>Configured redirect targets.</div>\n\n <div class=\"space-y-3\">\n @for (uri of item.redirectUris; track uri) {\n <div class=\"rounded-2xl border border-(--rt-border-color) p-4 text-sm font-medium break-all\">{{ uri }}</div>\n } @if (item.redirectUris.length === 0) {\n <div class=\"rounded-2xl border border-(--rt-border-color) border-dashed p-4 text-sm text-muted-foreground\">No redirect URIs returned.</div>\n }\n </div>\n </rolatech-platform-detail-panel>\n </section>\n\n <section class=\"grid gap-6 xl:grid-cols-2\">\n <rolatech-platform-detail-panel>\n <div panel-title>Scopes</div>\n <div panel-description>Scopes exposed to this client.</div>\n\n <div class=\"flex flex-wrap gap-2\">\n @for (scope of item.scopes; track scope) {\n <span class=\"rt-platform-chip\">{{ scope }}</span>\n } @if (item.scopes.length === 0) {\n <span class=\"text-sm text-muted-foreground\">No scopes returned.</span>\n }\n </div>\n </rolatech-platform-detail-panel>\n\n <rolatech-platform-detail-panel>\n <div panel-title>Permissions</div>\n <div panel-description>Permission bindings surfaced by the backend.</div>\n\n <div class=\"flex flex-wrap gap-2\">\n @for (permission of item.permissions; track permission) {\n <span class=\"rt-platform-chip\">{{ permission }}</span>\n } @if (item.permissions.length === 0) {\n <span class=\"text-sm text-muted-foreground\">No explicit permissions returned.</span>\n }\n </div>\n </rolatech-platform-detail-panel>\n </section>\n\n <rolatech-platform-detail-panel>\n <div panel-title>Additional Attributes</div>\n <div panel-description>Unmapped backend fields are surfaced here so the platform console remains forward-compatible.</div>\n\n @if (item.attributes && (item.attributes | keyvalue).length > 0) {\n <div class=\"grid gap-4 md:grid-cols-2\">\n @for (entry of item.attributes | keyvalue; track entry.key) {\n <div class=\"rounded-2xl border border-(--rt-border-color) p-4\">\n <div class=\"text-xs text-muted-foreground\">{{ entry.key }}</div>\n <div class=\"mt-2 break-all text-sm font-medium\">{{ entry.value | json }}</div>\n </div>\n }\n </div>\n } @else {\n <div class=\"rounded-2xl border border-(--rt-border-color) border-dashed p-4 text-sm text-muted-foreground\">No additional client metadata was returned.</div>\n }\n </rolatech-platform-detail-panel>\n } @else {\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-card p-10 text-center text-muted-foreground shadow-sm\">\n {{ facade.error() || 'Auth client not found.' }}\n </section>\n }\n</section>\n", styles: [":host{display:block}.rt-platform-chip{display:inline-flex;align-items:center;border:1px solid var(--rt-outline);border-radius:9999px;padding:.25rem .75rem;font-size:.75rem;font-weight:600;background-color:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-text-primary)}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "component", type: PlatformPageHeader, selector: "rolatech-platform-page-header" }, { kind: "component", type: PlatformStatCard, selector: "rolatech-platform-stat-card" }, { kind: "component", type: PlatformDetailPanel, selector: "rolatech-platform-detail-panel" }, { kind: "component", type: PlatformStatusBadge, selector: "rolatech-platform-status-badge", inputs: ["status"] }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }, { kind: "pipe", type: JsonPipe, name: "json" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
130
|
+
}
|
|
131
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PlatformAuthClientDetailPage, decorators: [{
|
|
132
|
+
type: Component,
|
|
133
|
+
args: [{ selector: 'rolatech-platform-auth-client-detail-page', standalone: true, imports: [RouterLink, KeyValuePipe, JsonPipe, MatSnackBarModule, PlatformPageHeader, PlatformStatCard, PlatformDetailPanel, PlatformStatusBadge], changeDetection: ChangeDetectionStrategy.OnPush, providers: [PlatformAuthClientDetailFacade], template: "<section class=\"rt-platform-theme flex flex-col gap-6 bg-(--rt-base-background) p-6 text-(--rt-text-primary)\">\n @if (facade.loading()) {\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-card p-10 text-center text-muted-foreground shadow-sm\">Loading auth client...</section>\n } @else if (facade.item(); as item) {\n <rolatech-platform-page-header>\n <div header-left class=\"space-y-2\">\n <div class=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <a routerLink=\"../\" class=\"hover:underline cursor-pointer\">Auth Clients</a>\n <span>/</span>\n <span>{{ item.name }}</span>\n </div>\n\n <div>\n <h1 class=\"text-2xl font-semibold tracking-tight\">{{ item.name }}</h1>\n <div class=\"mt-1 text-sm text-muted-foreground break-all\">{{ item.clientId }}</div>\n </div>\n\n @if (item.description) {\n <p class=\"max-w-3xl text-sm text-muted-foreground\">{{ item.description }}</p>\n }\n\n <div class=\"flex flex-wrap gap-2\">\n <rolatech-platform-status-badge [status]=\"item.status || (item.enabled ? 'ENABLED' : 'DISABLED')\" />\n <span class=\"rt-platform-chip\">{{ item.confidential ? 'Confidential' : 'Public client' }}</span>\n </div>\n </div>\n\n <div header-actions class=\"flex flex-wrap gap-3\">\n <button\n type=\"button\"\n class=\"inline-flex items-center rounded-xl border border-(--rt-border-color) px-4 py-2 text-sm font-medium\"\n [routerLink]=\"['/platform/clients', item.id, 'edit']\"\n >\n Edit\n </button>\n <button\n type=\"button\"\n class=\"inline-flex items-center rounded-xl border border-rose-300/40 px-4 py-2 text-sm font-medium text-rose-200\"\n [disabled]=\"facade.deleting()\"\n (click)=\"deleteClient()\"\n >\n {{ facade.deleting() ? 'Deleting\u2026' : 'Delete' }}\n </button>\n <button\n type=\"button\"\n class=\"inline-flex items-center rounded-xl border border-(--rt-border-color) px-4 py-2 text-sm font-medium\"\n [disabled]=\"facade.rotating()\"\n (click)=\"rotateSecret()\"\n >\n {{ facade.rotating() ? 'Rotating\u2026' : 'Rotate Secret' }}\n </button>\n </div>\n </rolatech-platform-page-header>\n\n @if (facade.error()) {\n <section class=\"rounded-2xl border border-rose-300/40 bg-rose-500/10 px-4 py-3 text-sm text-(--rt-text-primary)\">\n {{ facade.error() }}\n </section>\n }\n\n <section class=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n <rolatech-platform-stat-card>\n <div stat-label>Grant Types</div>\n <div stat-value>{{ item.grantTypes.length }}</div>\n </rolatech-platform-stat-card>\n\n <rolatech-platform-stat-card>\n <div stat-label>Redirect URIs</div>\n <div stat-value>{{ item.redirectUris.length }}</div>\n </rolatech-platform-stat-card>\n\n <rolatech-platform-stat-card>\n <div stat-label>Scopes</div>\n <div stat-value>{{ item.scopes.length }}</div>\n </rolatech-platform-stat-card>\n\n <rolatech-platform-stat-card>\n <div stat-label>Permissions</div>\n <div stat-value>{{ item.permissions.length }}</div>\n </rolatech-platform-stat-card>\n </section>\n\n <section class=\"grid gap-6 xl:grid-cols-2\">\n <rolatech-platform-detail-panel>\n <div panel-title>Client Details</div>\n <div panel-description>Primary client configuration returned by the platform API.</div>\n\n <div class=\"grid gap-4 md:grid-cols-2\">\n <div>\n <div class=\"text-xs text-muted-foreground\">Client ID</div>\n <div class=\"font-medium break-all\">{{ item.clientId }}</div>\n </div>\n <div>\n <div class=\"text-xs text-muted-foreground\">App ID</div>\n <div class=\"font-medium break-all\">{{ item.appId || '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-muted-foreground\">Created At</div>\n <div class=\"font-medium\">{{ item.createdAt || '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-muted-foreground\">Updated At</div>\n <div class=\"font-medium\">{{ item.updatedAt || '\u2014' }}</div>\n </div>\n </div>\n </rolatech-platform-detail-panel>\n\n <rolatech-platform-detail-panel>\n <div panel-title>Secrets</div>\n <div panel-description>Current secret visibility depends on backend response. Rotating returns a new secret once.</div>\n\n <div class=\"space-y-4\">\n <div>\n <div class=\"text-xs text-muted-foreground\">Latest Secret</div>\n <div class=\"mt-2 break-all rounded-2xl border border-(--rt-border-color) bg-(--rt-raised-background) px-4 py-3 text-sm font-medium\">\n {{ facade.latestSecret()?.clientSecret || item.clientSecret || 'Not exposed by backend' }}\n </div>\n </div>\n\n @if (facade.latestSecret()?.clientSecret || item.clientSecret) {\n <button\n type=\"button\"\n class=\"inline-flex items-center rounded-xl border border-(--rt-border-color) px-4 py-2 text-sm font-medium\"\n (click)=\"copyLatestSecret(facade.latestSecret()?.clientSecret || item.clientSecret)\"\n >\n Copy Secret\n </button>\n }\n </div>\n </rolatech-platform-detail-panel>\n </section>\n\n <section class=\"grid gap-6 xl:grid-cols-2\">\n <rolatech-platform-detail-panel>\n <div panel-title>Grant Types</div>\n <div panel-description>OAuth grant flows allowed for this client.</div>\n\n <div class=\"flex flex-wrap gap-2\">\n @for (grantType of item.grantTypes; track grantType) {\n <span class=\"rt-platform-chip\">{{ grantType }}</span>\n } @if (item.grantTypes.length === 0) {\n <span class=\"text-sm text-muted-foreground\">No grant types returned.</span>\n }\n </div>\n </rolatech-platform-detail-panel>\n\n <rolatech-platform-detail-panel>\n <div panel-title>Redirect URIs</div>\n <div panel-description>Configured redirect targets.</div>\n\n <div class=\"space-y-3\">\n @for (uri of item.redirectUris; track uri) {\n <div class=\"rounded-2xl border border-(--rt-border-color) p-4 text-sm font-medium break-all\">{{ uri }}</div>\n } @if (item.redirectUris.length === 0) {\n <div class=\"rounded-2xl border border-(--rt-border-color) border-dashed p-4 text-sm text-muted-foreground\">No redirect URIs returned.</div>\n }\n </div>\n </rolatech-platform-detail-panel>\n </section>\n\n <section class=\"grid gap-6 xl:grid-cols-2\">\n <rolatech-platform-detail-panel>\n <div panel-title>Scopes</div>\n <div panel-description>Scopes exposed to this client.</div>\n\n <div class=\"flex flex-wrap gap-2\">\n @for (scope of item.scopes; track scope) {\n <span class=\"rt-platform-chip\">{{ scope }}</span>\n } @if (item.scopes.length === 0) {\n <span class=\"text-sm text-muted-foreground\">No scopes returned.</span>\n }\n </div>\n </rolatech-platform-detail-panel>\n\n <rolatech-platform-detail-panel>\n <div panel-title>Permissions</div>\n <div panel-description>Permission bindings surfaced by the backend.</div>\n\n <div class=\"flex flex-wrap gap-2\">\n @for (permission of item.permissions; track permission) {\n <span class=\"rt-platform-chip\">{{ permission }}</span>\n } @if (item.permissions.length === 0) {\n <span class=\"text-sm text-muted-foreground\">No explicit permissions returned.</span>\n }\n </div>\n </rolatech-platform-detail-panel>\n </section>\n\n <rolatech-platform-detail-panel>\n <div panel-title>Additional Attributes</div>\n <div panel-description>Unmapped backend fields are surfaced here so the platform console remains forward-compatible.</div>\n\n @if (item.attributes && (item.attributes | keyvalue).length > 0) {\n <div class=\"grid gap-4 md:grid-cols-2\">\n @for (entry of item.attributes | keyvalue; track entry.key) {\n <div class=\"rounded-2xl border border-(--rt-border-color) p-4\">\n <div class=\"text-xs text-muted-foreground\">{{ entry.key }}</div>\n <div class=\"mt-2 break-all text-sm font-medium\">{{ entry.value | json }}</div>\n </div>\n }\n </div>\n } @else {\n <div class=\"rounded-2xl border border-(--rt-border-color) border-dashed p-4 text-sm text-muted-foreground\">No additional client metadata was returned.</div>\n }\n </rolatech-platform-detail-panel>\n } @else {\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-card p-10 text-center text-muted-foreground shadow-sm\">\n {{ facade.error() || 'Auth client not found.' }}\n </section>\n }\n</section>\n", styles: [":host{display:block}.rt-platform-chip{display:inline-flex;align-items:center;border:1px solid var(--rt-outline);border-radius:9999px;padding:.25rem .75rem;font-size:.75rem;font-weight:600;background-color:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-text-primary)}\n"] }]
|
|
134
|
+
}] });
|
|
135
|
+
|
|
136
|
+
export { PlatformAuthClientDetailPage };
|
|
137
|
+
//# sourceMappingURL=rolatech-angular-platform-platform-auth-client-detail-BMvM7PvP.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rolatech-angular-platform-platform-auth-client-detail-BMvM7PvP.mjs","sources":["../../../../packages/angular-platform/src/lib/store/platform-auth-client-detail.facade.ts","../../../../packages/angular-platform/src/lib/pages/client/platform-auth-client-detail/platform-auth-client-detail.ts","../../../../packages/angular-platform/src/lib/pages/client/platform-auth-client-detail/platform-auth-client-detail.html"],"sourcesContent":["import { Injectable, inject, signal } from '@angular/core';\nimport { firstValueFrom } from 'rxjs';\nimport {\n PlatformAuthClientDetailResponse,\n PlatformAuthClientSecretResetResponse,\n PlatformAuthClientService,\n} from '@rolatech/angular-services';\n\n@Injectable()\nexport class PlatformAuthClientDetailFacade {\n private readonly authClientService = inject(PlatformAuthClientService);\n\n readonly loading = signal(false);\n readonly rotating = signal(false);\n readonly deleting = signal(false);\n readonly error = signal<string | null>(null);\n readonly item = signal<PlatformAuthClientDetailResponse | null>(null);\n readonly latestSecret = signal<PlatformAuthClientSecretResetResponse | null>(null);\n\n seedLatestSecret(secret: string): void {\n this.latestSecret.set({ clientSecret: secret });\n }\n\n async load(clientId: string): Promise<void> {\n this.loading.set(true);\n this.error.set(null);\n\n try {\n this.item.set(await firstValueFrom(this.authClientService.findClientById(clientId)));\n } catch (error) {\n console.error(error);\n this.item.set(null);\n this.error.set('Unable to load auth client details.');\n } finally {\n this.loading.set(false);\n }\n }\n\n async rotateSecret(clientId: string): Promise<PlatformAuthClientSecretResetResponse | null> {\n this.rotating.set(true);\n this.error.set(null);\n\n try {\n const result = await firstValueFrom(this.authClientService.resetClientSecret(clientId));\n this.latestSecret.set(result);\n await this.load(clientId);\n return result;\n } catch (error) {\n console.error(error);\n this.error.set('Unable to rotate the auth client secret.');\n return null;\n } finally {\n this.rotating.set(false);\n }\n }\n\n async delete(clientId: string): Promise<boolean> {\n this.deleting.set(true);\n this.error.set(null);\n\n try {\n await firstValueFrom(this.authClientService.deleteClient(clientId));\n return true;\n } catch (error) {\n console.error(error);\n this.error.set('Unable to delete the auth client.');\n return false;\n } finally {\n this.deleting.set(false);\n }\n }\n}\n","import { JsonPipe, KeyValuePipe } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, OnInit, inject } from '@angular/core';\nimport { ActivatedRoute, Router, RouterLink } from '@angular/router';\nimport { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';\nimport { PlatformDetailPanel, PlatformPageHeader, PlatformStatCard, PlatformStatusBadge } from '../../../shared';\nimport { PlatformAuthClientDetailFacade } from '../../../store/platform-auth-client-detail.facade';\n\n@Component({\n selector: 'rolatech-platform-auth-client-detail-page',\n standalone: true,\n imports: [RouterLink, KeyValuePipe, JsonPipe, MatSnackBarModule, PlatformPageHeader, PlatformStatCard, PlatformDetailPanel, PlatformStatusBadge],\n templateUrl: './platform-auth-client-detail.html',\n styleUrl: './platform-auth-client-detail.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [PlatformAuthClientDetailFacade],\n})\nexport class PlatformAuthClientDetailPage implements OnInit {\n readonly facade = inject(PlatformAuthClientDetailFacade);\n private readonly route = inject(ActivatedRoute);\n private readonly router = inject(Router);\n private readonly snackBar = inject(MatSnackBar);\n\n readonly clientId = this.route.snapshot.paramMap.get('clientId') ?? '';\n\n ngOnInit(): void {\n const createdSecret = globalThis.history?.state?.['createdSecret'];\n if (typeof createdSecret === 'string' && createdSecret.length > 0) {\n this.facade.seedLatestSecret(createdSecret);\n }\n\n if (this.clientId) {\n void this.facade.load(this.clientId);\n }\n }\n\n async rotateSecret(): Promise<void> {\n const result = await this.facade.rotateSecret(this.clientId);\n if (!result?.clientSecret) {\n return;\n }\n\n await this.copySecret(result.clientSecret);\n this.snackBar.open('Client secret rotated and copied.', 'Dismiss', { duration: 3000 });\n }\n\n async copyLatestSecret(secret: string | null | undefined): Promise<void> {\n if (!secret) {\n return;\n }\n\n await this.copySecret(secret);\n this.snackBar.open('Client secret copied.', 'Dismiss', { duration: 3000 });\n }\n\n async deleteClient(): Promise<void> {\n const item = this.facade.item();\n if (!item) {\n return;\n }\n\n if (globalThis.confirm && !globalThis.confirm(`Delete auth client \"${item.name}\"? This cannot be undone.`)) {\n return;\n }\n\n const deleted = await this.facade.delete(this.clientId);\n if (!deleted) {\n return;\n }\n\n this.snackBar.open('Auth client deleted.', 'Dismiss', { duration: 3000 });\n await this.router.navigate(['/platform/clients']);\n }\n\n private async copySecret(secret: string): Promise<void> {\n if (!globalThis.navigator?.clipboard) {\n return;\n }\n\n await globalThis.navigator.clipboard.writeText(secret);\n }\n}\n","<section class=\"rt-platform-theme flex flex-col gap-6 bg-(--rt-base-background) p-6 text-(--rt-text-primary)\">\n @if (facade.loading()) {\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-card p-10 text-center text-muted-foreground shadow-sm\">Loading auth client...</section>\n } @else if (facade.item(); as item) {\n <rolatech-platform-page-header>\n <div header-left class=\"space-y-2\">\n <div class=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <a routerLink=\"../\" class=\"hover:underline cursor-pointer\">Auth Clients</a>\n <span>/</span>\n <span>{{ item.name }}</span>\n </div>\n\n <div>\n <h1 class=\"text-2xl font-semibold tracking-tight\">{{ item.name }}</h1>\n <div class=\"mt-1 text-sm text-muted-foreground break-all\">{{ item.clientId }}</div>\n </div>\n\n @if (item.description) {\n <p class=\"max-w-3xl text-sm text-muted-foreground\">{{ item.description }}</p>\n }\n\n <div class=\"flex flex-wrap gap-2\">\n <rolatech-platform-status-badge [status]=\"item.status || (item.enabled ? 'ENABLED' : 'DISABLED')\" />\n <span class=\"rt-platform-chip\">{{ item.confidential ? 'Confidential' : 'Public client' }}</span>\n </div>\n </div>\n\n <div header-actions class=\"flex flex-wrap gap-3\">\n <button\n type=\"button\"\n class=\"inline-flex items-center rounded-xl border border-(--rt-border-color) px-4 py-2 text-sm font-medium\"\n [routerLink]=\"['/platform/clients', item.id, 'edit']\"\n >\n Edit\n </button>\n <button\n type=\"button\"\n class=\"inline-flex items-center rounded-xl border border-rose-300/40 px-4 py-2 text-sm font-medium text-rose-200\"\n [disabled]=\"facade.deleting()\"\n (click)=\"deleteClient()\"\n >\n {{ facade.deleting() ? 'Deleting…' : 'Delete' }}\n </button>\n <button\n type=\"button\"\n class=\"inline-flex items-center rounded-xl border border-(--rt-border-color) px-4 py-2 text-sm font-medium\"\n [disabled]=\"facade.rotating()\"\n (click)=\"rotateSecret()\"\n >\n {{ facade.rotating() ? 'Rotating…' : 'Rotate Secret' }}\n </button>\n </div>\n </rolatech-platform-page-header>\n\n @if (facade.error()) {\n <section class=\"rounded-2xl border border-rose-300/40 bg-rose-500/10 px-4 py-3 text-sm text-(--rt-text-primary)\">\n {{ facade.error() }}\n </section>\n }\n\n <section class=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n <rolatech-platform-stat-card>\n <div stat-label>Grant Types</div>\n <div stat-value>{{ item.grantTypes.length }}</div>\n </rolatech-platform-stat-card>\n\n <rolatech-platform-stat-card>\n <div stat-label>Redirect URIs</div>\n <div stat-value>{{ item.redirectUris.length }}</div>\n </rolatech-platform-stat-card>\n\n <rolatech-platform-stat-card>\n <div stat-label>Scopes</div>\n <div stat-value>{{ item.scopes.length }}</div>\n </rolatech-platform-stat-card>\n\n <rolatech-platform-stat-card>\n <div stat-label>Permissions</div>\n <div stat-value>{{ item.permissions.length }}</div>\n </rolatech-platform-stat-card>\n </section>\n\n <section class=\"grid gap-6 xl:grid-cols-2\">\n <rolatech-platform-detail-panel>\n <div panel-title>Client Details</div>\n <div panel-description>Primary client configuration returned by the platform API.</div>\n\n <div class=\"grid gap-4 md:grid-cols-2\">\n <div>\n <div class=\"text-xs text-muted-foreground\">Client ID</div>\n <div class=\"font-medium break-all\">{{ item.clientId }}</div>\n </div>\n <div>\n <div class=\"text-xs text-muted-foreground\">App ID</div>\n <div class=\"font-medium break-all\">{{ item.appId || '—' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-muted-foreground\">Created At</div>\n <div class=\"font-medium\">{{ item.createdAt || '—' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-muted-foreground\">Updated At</div>\n <div class=\"font-medium\">{{ item.updatedAt || '—' }}</div>\n </div>\n </div>\n </rolatech-platform-detail-panel>\n\n <rolatech-platform-detail-panel>\n <div panel-title>Secrets</div>\n <div panel-description>Current secret visibility depends on backend response. Rotating returns a new secret once.</div>\n\n <div class=\"space-y-4\">\n <div>\n <div class=\"text-xs text-muted-foreground\">Latest Secret</div>\n <div class=\"mt-2 break-all rounded-2xl border border-(--rt-border-color) bg-(--rt-raised-background) px-4 py-3 text-sm font-medium\">\n {{ facade.latestSecret()?.clientSecret || item.clientSecret || 'Not exposed by backend' }}\n </div>\n </div>\n\n @if (facade.latestSecret()?.clientSecret || item.clientSecret) {\n <button\n type=\"button\"\n class=\"inline-flex items-center rounded-xl border border-(--rt-border-color) px-4 py-2 text-sm font-medium\"\n (click)=\"copyLatestSecret(facade.latestSecret()?.clientSecret || item.clientSecret)\"\n >\n Copy Secret\n </button>\n }\n </div>\n </rolatech-platform-detail-panel>\n </section>\n\n <section class=\"grid gap-6 xl:grid-cols-2\">\n <rolatech-platform-detail-panel>\n <div panel-title>Grant Types</div>\n <div panel-description>OAuth grant flows allowed for this client.</div>\n\n <div class=\"flex flex-wrap gap-2\">\n @for (grantType of item.grantTypes; track grantType) {\n <span class=\"rt-platform-chip\">{{ grantType }}</span>\n } @if (item.grantTypes.length === 0) {\n <span class=\"text-sm text-muted-foreground\">No grant types returned.</span>\n }\n </div>\n </rolatech-platform-detail-panel>\n\n <rolatech-platform-detail-panel>\n <div panel-title>Redirect URIs</div>\n <div panel-description>Configured redirect targets.</div>\n\n <div class=\"space-y-3\">\n @for (uri of item.redirectUris; track uri) {\n <div class=\"rounded-2xl border border-(--rt-border-color) p-4 text-sm font-medium break-all\">{{ uri }}</div>\n } @if (item.redirectUris.length === 0) {\n <div class=\"rounded-2xl border border-(--rt-border-color) border-dashed p-4 text-sm text-muted-foreground\">No redirect URIs returned.</div>\n }\n </div>\n </rolatech-platform-detail-panel>\n </section>\n\n <section class=\"grid gap-6 xl:grid-cols-2\">\n <rolatech-platform-detail-panel>\n <div panel-title>Scopes</div>\n <div panel-description>Scopes exposed to this client.</div>\n\n <div class=\"flex flex-wrap gap-2\">\n @for (scope of item.scopes; track scope) {\n <span class=\"rt-platform-chip\">{{ scope }}</span>\n } @if (item.scopes.length === 0) {\n <span class=\"text-sm text-muted-foreground\">No scopes returned.</span>\n }\n </div>\n </rolatech-platform-detail-panel>\n\n <rolatech-platform-detail-panel>\n <div panel-title>Permissions</div>\n <div panel-description>Permission bindings surfaced by the backend.</div>\n\n <div class=\"flex flex-wrap gap-2\">\n @for (permission of item.permissions; track permission) {\n <span class=\"rt-platform-chip\">{{ permission }}</span>\n } @if (item.permissions.length === 0) {\n <span class=\"text-sm text-muted-foreground\">No explicit permissions returned.</span>\n }\n </div>\n </rolatech-platform-detail-panel>\n </section>\n\n <rolatech-platform-detail-panel>\n <div panel-title>Additional Attributes</div>\n <div panel-description>Unmapped backend fields are surfaced here so the platform console remains forward-compatible.</div>\n\n @if (item.attributes && (item.attributes | keyvalue).length > 0) {\n <div class=\"grid gap-4 md:grid-cols-2\">\n @for (entry of item.attributes | keyvalue; track entry.key) {\n <div class=\"rounded-2xl border border-(--rt-border-color) p-4\">\n <div class=\"text-xs text-muted-foreground\">{{ entry.key }}</div>\n <div class=\"mt-2 break-all text-sm font-medium\">{{ entry.value | json }}</div>\n </div>\n }\n </div>\n } @else {\n <div class=\"rounded-2xl border border-(--rt-border-color) border-dashed p-4 text-sm text-muted-foreground\">No additional client metadata was returned.</div>\n }\n </rolatech-platform-detail-panel>\n } @else {\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-card p-10 text-center text-muted-foreground shadow-sm\">\n {{ facade.error() || 'Auth client not found.' }}\n </section>\n }\n</section>\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, signal, Injectable, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
import { ActivatedRoute, Router } from '@angular/router';
|
|
4
|
+
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
|
|
5
|
+
import { PlatformPageHeader, PlatformDetailPanel, PlatformStickyActionBar } from './rolatech-angular-platform.mjs';
|
|
6
|
+
import { PlatformAuthClientService } from '@rolatech/angular-services';
|
|
7
|
+
import { firstValueFrom } from 'rxjs';
|
|
8
|
+
|
|
9
|
+
class PlatformAuthClientEditorFacade {
|
|
10
|
+
authClientService = inject(PlatformAuthClientService);
|
|
11
|
+
loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
|
|
12
|
+
saving = signal(false, ...(ngDevMode ? [{ debugName: "saving" }] : []));
|
|
13
|
+
deleting = signal(false, ...(ngDevMode ? [{ debugName: "deleting" }] : []));
|
|
14
|
+
error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
|
|
15
|
+
id = signal(null, ...(ngDevMode ? [{ debugName: "id" }] : []));
|
|
16
|
+
clientId = signal('', ...(ngDevMode ? [{ debugName: "clientId" }] : []));
|
|
17
|
+
name = signal('', ...(ngDevMode ? [{ debugName: "name" }] : []));
|
|
18
|
+
description = signal('', ...(ngDevMode ? [{ debugName: "description" }] : []));
|
|
19
|
+
enabled = signal(true, ...(ngDevMode ? [{ debugName: "enabled" }] : []));
|
|
20
|
+
confidential = signal(true, ...(ngDevMode ? [{ debugName: "confidential" }] : []));
|
|
21
|
+
grantTypesText = signal('', ...(ngDevMode ? [{ debugName: "grantTypesText" }] : []));
|
|
22
|
+
redirectUrisText = signal('', ...(ngDevMode ? [{ debugName: "redirectUrisText" }] : []));
|
|
23
|
+
scopesText = signal('', ...(ngDevMode ? [{ debugName: "scopesText" }] : []));
|
|
24
|
+
permissionsText = signal('', ...(ngDevMode ? [{ debugName: "permissionsText" }] : []));
|
|
25
|
+
appId = signal('', ...(ngDevMode ? [{ debugName: "appId" }] : []));
|
|
26
|
+
appSecret = signal('', ...(ngDevMode ? [{ debugName: "appSecret" }] : []));
|
|
27
|
+
attributesText = signal('', ...(ngDevMode ? [{ debugName: "attributesText" }] : []));
|
|
28
|
+
async load(id) {
|
|
29
|
+
this.loading.set(true);
|
|
30
|
+
this.error.set(null);
|
|
31
|
+
try {
|
|
32
|
+
const item = await firstValueFrom(this.authClientService.findClientById(id));
|
|
33
|
+
this.patchForm(item);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
console.error(error);
|
|
37
|
+
this.error.set('Unable to load the auth client.');
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
this.loading.set(false);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async save() {
|
|
44
|
+
const name = this.name().trim();
|
|
45
|
+
if (!name) {
|
|
46
|
+
this.error.set('Name is required.');
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const attributes = this.parseAttributes(this.attributesText());
|
|
50
|
+
if (attributes === undefined) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
this.saving.set(true);
|
|
54
|
+
this.error.set(null);
|
|
55
|
+
try {
|
|
56
|
+
const payload = {
|
|
57
|
+
clientId: this.clientId().trim() || null,
|
|
58
|
+
name,
|
|
59
|
+
description: this.description().trim() || null,
|
|
60
|
+
enabled: this.enabled(),
|
|
61
|
+
confidential: this.confidential(),
|
|
62
|
+
grantTypes: this.parseList(this.grantTypesText()),
|
|
63
|
+
redirectUris: this.parseList(this.redirectUrisText()),
|
|
64
|
+
scopes: this.parseList(this.scopesText()),
|
|
65
|
+
permissions: this.parseList(this.permissionsText()),
|
|
66
|
+
appId: this.appId().trim() || null,
|
|
67
|
+
appSecret: this.appSecret().trim() || null,
|
|
68
|
+
attributes,
|
|
69
|
+
};
|
|
70
|
+
const result = this.id()
|
|
71
|
+
? await firstValueFrom(this.authClientService.updateClient(this.id(), payload))
|
|
72
|
+
: await firstValueFrom(this.authClientService.createClient(payload));
|
|
73
|
+
this.patchForm(result);
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
console.error(error);
|
|
78
|
+
this.error.set('Unable to save the auth client.');
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
this.saving.set(false);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async delete(id = this.id()) {
|
|
86
|
+
if (!id) {
|
|
87
|
+
this.error.set('Auth client ID is missing.');
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
this.deleting.set(true);
|
|
91
|
+
this.error.set(null);
|
|
92
|
+
try {
|
|
93
|
+
await firstValueFrom(this.authClientService.deleteClient(id));
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
console.error(error);
|
|
98
|
+
this.error.set('Unable to delete the auth client.');
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
this.deleting.set(false);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
setClientId(value) {
|
|
106
|
+
this.clientId.set(value);
|
|
107
|
+
}
|
|
108
|
+
setName(value) {
|
|
109
|
+
this.name.set(value);
|
|
110
|
+
}
|
|
111
|
+
setDescription(value) {
|
|
112
|
+
this.description.set(value);
|
|
113
|
+
}
|
|
114
|
+
setEnabled(value) {
|
|
115
|
+
this.enabled.set(value);
|
|
116
|
+
}
|
|
117
|
+
setConfidential(value) {
|
|
118
|
+
this.confidential.set(value);
|
|
119
|
+
}
|
|
120
|
+
setGrantTypesText(value) {
|
|
121
|
+
this.grantTypesText.set(value);
|
|
122
|
+
}
|
|
123
|
+
setRedirectUrisText(value) {
|
|
124
|
+
this.redirectUrisText.set(value);
|
|
125
|
+
}
|
|
126
|
+
setScopesText(value) {
|
|
127
|
+
this.scopesText.set(value);
|
|
128
|
+
}
|
|
129
|
+
setPermissionsText(value) {
|
|
130
|
+
this.permissionsText.set(value);
|
|
131
|
+
}
|
|
132
|
+
setAppId(value) {
|
|
133
|
+
this.appId.set(value);
|
|
134
|
+
}
|
|
135
|
+
setAppSecret(value) {
|
|
136
|
+
this.appSecret.set(value);
|
|
137
|
+
}
|
|
138
|
+
setAttributesText(value) {
|
|
139
|
+
this.attributesText.set(value);
|
|
140
|
+
}
|
|
141
|
+
patchForm(item) {
|
|
142
|
+
this.id.set(item.id);
|
|
143
|
+
this.clientId.set(item.clientId ?? '');
|
|
144
|
+
this.name.set(item.name ?? '');
|
|
145
|
+
this.description.set(item.description ?? '');
|
|
146
|
+
this.enabled.set(item.enabled);
|
|
147
|
+
this.confidential.set(item.confidential);
|
|
148
|
+
this.grantTypesText.set(item.grantTypes.join('\n'));
|
|
149
|
+
this.redirectUrisText.set(item.redirectUris.join('\n'));
|
|
150
|
+
this.scopesText.set(item.scopes.join('\n'));
|
|
151
|
+
this.permissionsText.set(item.permissions.join('\n'));
|
|
152
|
+
this.appId.set(item.appId ?? '');
|
|
153
|
+
this.appSecret.set(item.appSecret ?? '');
|
|
154
|
+
this.attributesText.set(item.attributes ? JSON.stringify(item.attributes, null, 2) : '');
|
|
155
|
+
}
|
|
156
|
+
parseList(value) {
|
|
157
|
+
return Array.from(new Set(value
|
|
158
|
+
.split(/\r?\n|,/)
|
|
159
|
+
.map((item) => item.trim())
|
|
160
|
+
.filter(Boolean)));
|
|
161
|
+
}
|
|
162
|
+
parseAttributes(value) {
|
|
163
|
+
const trimmed = value.trim();
|
|
164
|
+
if (!trimmed) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
const parsed = JSON.parse(trimmed);
|
|
169
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
170
|
+
this.error.set('Additional attributes must be a JSON object.');
|
|
171
|
+
return undefined;
|
|
172
|
+
}
|
|
173
|
+
return parsed;
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
console.error(error);
|
|
177
|
+
this.error.set('Additional attributes must be valid JSON.');
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PlatformAuthClientEditorFacade, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
182
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PlatformAuthClientEditorFacade });
|
|
183
|
+
}
|
|
184
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PlatformAuthClientEditorFacade, decorators: [{
|
|
185
|
+
type: Injectable
|
|
186
|
+
}] });
|
|
187
|
+
|
|
188
|
+
class PlatformAuthClientEditorPage {
|
|
189
|
+
facade = inject(PlatformAuthClientEditorFacade);
|
|
190
|
+
route = inject(ActivatedRoute);
|
|
191
|
+
router = inject(Router);
|
|
192
|
+
snackBar = inject(MatSnackBar);
|
|
193
|
+
clientId = this.route.snapshot.paramMap.get('clientId');
|
|
194
|
+
get isEdit() {
|
|
195
|
+
return !!this.clientId;
|
|
196
|
+
}
|
|
197
|
+
ngOnInit() {
|
|
198
|
+
if (this.clientId) {
|
|
199
|
+
void this.facade.load(this.clientId);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
onClientIdInput(event) {
|
|
203
|
+
this.facade.setClientId(event.target.value);
|
|
204
|
+
}
|
|
205
|
+
onNameInput(event) {
|
|
206
|
+
this.facade.setName(event.target.value);
|
|
207
|
+
}
|
|
208
|
+
onDescriptionInput(event) {
|
|
209
|
+
this.facade.setDescription(event.target.value);
|
|
210
|
+
}
|
|
211
|
+
onEnabledChange(event) {
|
|
212
|
+
this.facade.setEnabled(event.target.checked);
|
|
213
|
+
}
|
|
214
|
+
onConfidentialChange(event) {
|
|
215
|
+
this.facade.setConfidential(event.target.checked);
|
|
216
|
+
}
|
|
217
|
+
onGrantTypesInput(event) {
|
|
218
|
+
this.facade.setGrantTypesText(event.target.value);
|
|
219
|
+
}
|
|
220
|
+
onRedirectUrisInput(event) {
|
|
221
|
+
this.facade.setRedirectUrisText(event.target.value);
|
|
222
|
+
}
|
|
223
|
+
onScopesInput(event) {
|
|
224
|
+
this.facade.setScopesText(event.target.value);
|
|
225
|
+
}
|
|
226
|
+
onPermissionsInput(event) {
|
|
227
|
+
this.facade.setPermissionsText(event.target.value);
|
|
228
|
+
}
|
|
229
|
+
onAppIdInput(event) {
|
|
230
|
+
this.facade.setAppId(event.target.value);
|
|
231
|
+
}
|
|
232
|
+
onAppSecretInput(event) {
|
|
233
|
+
this.facade.setAppSecret(event.target.value);
|
|
234
|
+
}
|
|
235
|
+
onAttributesInput(event) {
|
|
236
|
+
this.facade.setAttributesText(event.target.value);
|
|
237
|
+
}
|
|
238
|
+
async save() {
|
|
239
|
+
const result = await this.facade.save();
|
|
240
|
+
if (!result) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
let message = this.isEdit ? 'Auth client updated.' : 'Auth client created.';
|
|
244
|
+
if (!this.isEdit && result.clientSecret) {
|
|
245
|
+
try {
|
|
246
|
+
await this.copySecret(result.clientSecret);
|
|
247
|
+
message = 'Auth client created. Client secret copied.';
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
console.error(error);
|
|
251
|
+
message = 'Auth client created. Client secret is available on the detail page.';
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
this.snackBar.open(message, 'Dismiss', { duration: 3000 });
|
|
255
|
+
await this.router.navigate(['/platform/clients', result.id], {
|
|
256
|
+
state: result.clientSecret ? { createdSecret: result.clientSecret } : undefined,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
async cancel() {
|
|
260
|
+
if (this.isEdit && this.clientId) {
|
|
261
|
+
await this.router.navigate(['/platform/clients', this.clientId]);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
await this.router.navigate(['/platform/clients']);
|
|
265
|
+
}
|
|
266
|
+
async copySecret(secret) {
|
|
267
|
+
if (!globalThis.navigator?.clipboard) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
await globalThis.navigator.clipboard.writeText(secret);
|
|
271
|
+
}
|
|
272
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PlatformAuthClientEditorPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
273
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PlatformAuthClientEditorPage, isStandalone: true, selector: "rolatech-platform-auth-client-editor-page", providers: [PlatformAuthClientEditorFacade], ngImport: i0, template: "<section class=\"rt-platform-theme flex flex-col gap-6 bg-(--rt-base-background) p-6 text-(--rt-text-primary)\">\n <rolatech-platform-page-header>\n <div header-left class=\"space-y-1\">\n <h1 class=\"text-2xl font-semibold tracking-tight\">@if (isEdit) { Edit Auth Client } @else { Create Auth Client }</h1>\n <p class=\"text-sm text-muted-foreground\">\n @if (isEdit) { Update auth client configuration, redirect coverage, and linked app settings. } @else { Create a new auth client for platform-managed integrations. }\n </p>\n </div>\n </rolatech-platform-page-header>\n\n @if (facade.loading()) {\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-card p-10 text-center text-muted-foreground shadow-sm\">Loading auth client...</section>\n } @else {\n @if (facade.error()) {\n <section class=\"rounded-2xl border border-rose-300/40 bg-rose-500/10 px-4 py-3 text-sm text-(--rt-text-primary)\">\n {{ facade.error() }}\n </section>\n }\n\n <rolatech-platform-detail-panel>\n <div panel-title>Basic Information</div>\n <div panel-description>Core client identity and activation state.</div>\n\n <div class=\"grid gap-4 md:grid-cols-2\">\n <label class=\"flex flex-col gap-2\">\n <span class=\"text-sm font-medium\">Name</span>\n <input\n type=\"text\"\n [value]=\"facade.name()\"\n (input)=\"onNameInput($event)\"\n placeholder=\"Store Console\"\n class=\"h-10 rounded-xl border border-(--rt-border-color) px-3 text-sm outline-none\"\n />\n </label>\n\n <label class=\"flex flex-col gap-2\">\n <span class=\"text-sm font-medium\">Client ID</span>\n <input\n type=\"text\"\n [value]=\"facade.clientId()\"\n (input)=\"onClientIdInput($event)\"\n [readOnly]=\"isEdit\"\n placeholder=\"Optional if generated by backend\"\n class=\"h-10 rounded-xl border border-(--rt-border-color) px-3 text-sm outline-none\"\n />\n </label>\n\n <label class=\"md:col-span-2 flex flex-col gap-2\">\n <span class=\"text-sm font-medium\">Description</span>\n <textarea\n rows=\"4\"\n [value]=\"facade.description()\"\n (input)=\"onDescriptionInput($event)\"\n placeholder=\"Describe where this client is used.\"\n class=\"rounded-xl border border-(--rt-border-color) px-3 py-2 text-sm outline-none\"\n ></textarea>\n </label>\n\n <label class=\"flex items-center gap-2 pt-8\">\n <input type=\"checkbox\" [checked]=\"facade.enabled()\" (change)=\"onEnabledChange($event)\" />\n <span class=\"text-sm font-medium\">Enabled</span>\n </label>\n\n <label class=\"flex items-center gap-2 pt-8\">\n <input type=\"checkbox\" [checked]=\"facade.confidential()\" (change)=\"onConfidentialChange($event)\" />\n <span class=\"text-sm font-medium\">Confidential client</span>\n </label>\n </div>\n </rolatech-platform-detail-panel>\n\n <section class=\"grid gap-6 xl:grid-cols-2\">\n <rolatech-platform-detail-panel>\n <div panel-title>OAuth Configuration</div>\n <div panel-description>One value per line or comma separated.</div>\n\n <div class=\"space-y-4\">\n <label class=\"flex flex-col gap-2\">\n <span class=\"text-sm font-medium\">Grant Types</span>\n <textarea\n rows=\"5\"\n [value]=\"facade.grantTypesText()\"\n (input)=\"onGrantTypesInput($event)\"\n placeholder=\"authorization_code refresh_token\"\n class=\"rounded-xl border border-(--rt-border-color) px-3 py-2 text-sm outline-none\"\n ></textarea>\n </label>\n\n <label class=\"flex flex-col gap-2\">\n <span class=\"text-sm font-medium\">Scopes</span>\n <textarea\n rows=\"5\"\n [value]=\"facade.scopesText()\"\n (input)=\"onScopesInput($event)\"\n placeholder=\"openid profile\"\n class=\"rounded-xl border border-(--rt-border-color) px-3 py-2 text-sm outline-none\"\n ></textarea>\n </label>\n\n <label class=\"flex flex-col gap-2\">\n <span class=\"text-sm font-medium\">Permissions</span>\n <textarea\n rows=\"5\"\n [value]=\"facade.permissionsText()\"\n (input)=\"onPermissionsInput($event)\"\n placeholder=\"platform.clients.read platform.clients.write\"\n class=\"rounded-xl border border-(--rt-border-color) px-3 py-2 text-sm outline-none\"\n ></textarea>\n </label>\n </div>\n </rolatech-platform-detail-panel>\n\n <rolatech-platform-detail-panel>\n <div panel-title>Endpoints And Attributes</div>\n <div panel-description>Redirect URIs and optional app-linked credentials.</div>\n\n <div class=\"space-y-4\">\n <label class=\"flex flex-col gap-2\">\n <span class=\"text-sm font-medium\">Redirect URIs</span>\n <textarea\n rows=\"6\"\n [value]=\"facade.redirectUrisText()\"\n (input)=\"onRedirectUrisInput($event)\"\n placeholder=\"https://console.rolatech.com/callback\"\n class=\"rounded-xl border border-(--rt-border-color) px-3 py-2 text-sm outline-none\"\n ></textarea>\n </label>\n\n <div class=\"grid gap-4 md:grid-cols-2\">\n <label class=\"flex flex-col gap-2\">\n <span class=\"text-sm font-medium\">App ID</span>\n <input\n type=\"text\"\n [value]=\"facade.appId()\"\n (input)=\"onAppIdInput($event)\"\n placeholder=\"Optional linked app id\"\n class=\"h-10 rounded-xl border border-(--rt-border-color) px-3 text-sm outline-none\"\n />\n </label>\n\n <label class=\"flex flex-col gap-2\">\n <span class=\"text-sm font-medium\">App Secret</span>\n <input\n type=\"text\"\n [value]=\"facade.appSecret()\"\n (input)=\"onAppSecretInput($event)\"\n placeholder=\"Optional linked app secret\"\n class=\"h-10 rounded-xl border border-(--rt-border-color) px-3 text-sm outline-none\"\n />\n </label>\n </div>\n\n <label class=\"flex flex-col gap-2\">\n <span class=\"text-sm font-medium\">Additional Attributes (JSON object)</span>\n <textarea\n rows=\"8\"\n [value]=\"facade.attributesText()\"\n (input)=\"onAttributesInput($event)\"\n placeholder='{\"owner\":\"platform\"}'\n class=\"rounded-xl border border-(--rt-border-color) px-3 py-2 font-mono text-sm outline-none\"\n ></textarea>\n </label>\n </div>\n </rolatech-platform-detail-panel>\n </section>\n\n <rolatech-platform-sticky-action-bar>\n <div action-message>@if (isEdit) { Save changes to this auth client. } @else { Create the auth client and keep the generated secret if one is returned. }</div>\n\n <div action-buttons class=\"flex flex-wrap gap-3\">\n <button\n type=\"button\"\n class=\"inline-flex items-center rounded-xl border border-(--rt-border-color) px-4 py-2 text-sm font-medium\"\n [disabled]=\"facade.saving()\"\n (click)=\"cancel()\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"inline-flex items-center rounded-xl bg-(--rt-accent) px-4 py-2 text-sm font-medium text-(--rt-accent-foreground)\"\n [disabled]=\"facade.saving()\"\n (click)=\"save()\"\n >\n @if (facade.saving()) { Saving... } @else if (isEdit) { Save Changes } @else { Create Client }\n </button>\n </div>\n </rolatech-platform-sticky-action-bar>\n }\n</section>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatSnackBarModule }, { kind: "component", type: PlatformPageHeader, selector: "rolatech-platform-page-header" }, { kind: "component", type: PlatformDetailPanel, selector: "rolatech-platform-detail-panel" }, { kind: "component", type: PlatformStickyActionBar, selector: "rolatech-platform-sticky-action-bar" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
274
|
+
}
|
|
275
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PlatformAuthClientEditorPage, decorators: [{
|
|
276
|
+
type: Component,
|
|
277
|
+
args: [{ selector: 'rolatech-platform-auth-client-editor-page', standalone: true, imports: [MatSnackBarModule, PlatformPageHeader, PlatformDetailPanel, PlatformStickyActionBar], changeDetection: ChangeDetectionStrategy.OnPush, providers: [PlatformAuthClientEditorFacade], template: "<section class=\"rt-platform-theme flex flex-col gap-6 bg-(--rt-base-background) p-6 text-(--rt-text-primary)\">\n <rolatech-platform-page-header>\n <div header-left class=\"space-y-1\">\n <h1 class=\"text-2xl font-semibold tracking-tight\">@if (isEdit) { Edit Auth Client } @else { Create Auth Client }</h1>\n <p class=\"text-sm text-muted-foreground\">\n @if (isEdit) { Update auth client configuration, redirect coverage, and linked app settings. } @else { Create a new auth client for platform-managed integrations. }\n </p>\n </div>\n </rolatech-platform-page-header>\n\n @if (facade.loading()) {\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-card p-10 text-center text-muted-foreground shadow-sm\">Loading auth client...</section>\n } @else {\n @if (facade.error()) {\n <section class=\"rounded-2xl border border-rose-300/40 bg-rose-500/10 px-4 py-3 text-sm text-(--rt-text-primary)\">\n {{ facade.error() }}\n </section>\n }\n\n <rolatech-platform-detail-panel>\n <div panel-title>Basic Information</div>\n <div panel-description>Core client identity and activation state.</div>\n\n <div class=\"grid gap-4 md:grid-cols-2\">\n <label class=\"flex flex-col gap-2\">\n <span class=\"text-sm font-medium\">Name</span>\n <input\n type=\"text\"\n [value]=\"facade.name()\"\n (input)=\"onNameInput($event)\"\n placeholder=\"Store Console\"\n class=\"h-10 rounded-xl border border-(--rt-border-color) px-3 text-sm outline-none\"\n />\n </label>\n\n <label class=\"flex flex-col gap-2\">\n <span class=\"text-sm font-medium\">Client ID</span>\n <input\n type=\"text\"\n [value]=\"facade.clientId()\"\n (input)=\"onClientIdInput($event)\"\n [readOnly]=\"isEdit\"\n placeholder=\"Optional if generated by backend\"\n class=\"h-10 rounded-xl border border-(--rt-border-color) px-3 text-sm outline-none\"\n />\n </label>\n\n <label class=\"md:col-span-2 flex flex-col gap-2\">\n <span class=\"text-sm font-medium\">Description</span>\n <textarea\n rows=\"4\"\n [value]=\"facade.description()\"\n (input)=\"onDescriptionInput($event)\"\n placeholder=\"Describe where this client is used.\"\n class=\"rounded-xl border border-(--rt-border-color) px-3 py-2 text-sm outline-none\"\n ></textarea>\n </label>\n\n <label class=\"flex items-center gap-2 pt-8\">\n <input type=\"checkbox\" [checked]=\"facade.enabled()\" (change)=\"onEnabledChange($event)\" />\n <span class=\"text-sm font-medium\">Enabled</span>\n </label>\n\n <label class=\"flex items-center gap-2 pt-8\">\n <input type=\"checkbox\" [checked]=\"facade.confidential()\" (change)=\"onConfidentialChange($event)\" />\n <span class=\"text-sm font-medium\">Confidential client</span>\n </label>\n </div>\n </rolatech-platform-detail-panel>\n\n <section class=\"grid gap-6 xl:grid-cols-2\">\n <rolatech-platform-detail-panel>\n <div panel-title>OAuth Configuration</div>\n <div panel-description>One value per line or comma separated.</div>\n\n <div class=\"space-y-4\">\n <label class=\"flex flex-col gap-2\">\n <span class=\"text-sm font-medium\">Grant Types</span>\n <textarea\n rows=\"5\"\n [value]=\"facade.grantTypesText()\"\n (input)=\"onGrantTypesInput($event)\"\n placeholder=\"authorization_code refresh_token\"\n class=\"rounded-xl border border-(--rt-border-color) px-3 py-2 text-sm outline-none\"\n ></textarea>\n </label>\n\n <label class=\"flex flex-col gap-2\">\n <span class=\"text-sm font-medium\">Scopes</span>\n <textarea\n rows=\"5\"\n [value]=\"facade.scopesText()\"\n (input)=\"onScopesInput($event)\"\n placeholder=\"openid profile\"\n class=\"rounded-xl border border-(--rt-border-color) px-3 py-2 text-sm outline-none\"\n ></textarea>\n </label>\n\n <label class=\"flex flex-col gap-2\">\n <span class=\"text-sm font-medium\">Permissions</span>\n <textarea\n rows=\"5\"\n [value]=\"facade.permissionsText()\"\n (input)=\"onPermissionsInput($event)\"\n placeholder=\"platform.clients.read platform.clients.write\"\n class=\"rounded-xl border border-(--rt-border-color) px-3 py-2 text-sm outline-none\"\n ></textarea>\n </label>\n </div>\n </rolatech-platform-detail-panel>\n\n <rolatech-platform-detail-panel>\n <div panel-title>Endpoints And Attributes</div>\n <div panel-description>Redirect URIs and optional app-linked credentials.</div>\n\n <div class=\"space-y-4\">\n <label class=\"flex flex-col gap-2\">\n <span class=\"text-sm font-medium\">Redirect URIs</span>\n <textarea\n rows=\"6\"\n [value]=\"facade.redirectUrisText()\"\n (input)=\"onRedirectUrisInput($event)\"\n placeholder=\"https://console.rolatech.com/callback\"\n class=\"rounded-xl border border-(--rt-border-color) px-3 py-2 text-sm outline-none\"\n ></textarea>\n </label>\n\n <div class=\"grid gap-4 md:grid-cols-2\">\n <label class=\"flex flex-col gap-2\">\n <span class=\"text-sm font-medium\">App ID</span>\n <input\n type=\"text\"\n [value]=\"facade.appId()\"\n (input)=\"onAppIdInput($event)\"\n placeholder=\"Optional linked app id\"\n class=\"h-10 rounded-xl border border-(--rt-border-color) px-3 text-sm outline-none\"\n />\n </label>\n\n <label class=\"flex flex-col gap-2\">\n <span class=\"text-sm font-medium\">App Secret</span>\n <input\n type=\"text\"\n [value]=\"facade.appSecret()\"\n (input)=\"onAppSecretInput($event)\"\n placeholder=\"Optional linked app secret\"\n class=\"h-10 rounded-xl border border-(--rt-border-color) px-3 text-sm outline-none\"\n />\n </label>\n </div>\n\n <label class=\"flex flex-col gap-2\">\n <span class=\"text-sm font-medium\">Additional Attributes (JSON object)</span>\n <textarea\n rows=\"8\"\n [value]=\"facade.attributesText()\"\n (input)=\"onAttributesInput($event)\"\n placeholder='{\"owner\":\"platform\"}'\n class=\"rounded-xl border border-(--rt-border-color) px-3 py-2 font-mono text-sm outline-none\"\n ></textarea>\n </label>\n </div>\n </rolatech-platform-detail-panel>\n </section>\n\n <rolatech-platform-sticky-action-bar>\n <div action-message>@if (isEdit) { Save changes to this auth client. } @else { Create the auth client and keep the generated secret if one is returned. }</div>\n\n <div action-buttons class=\"flex flex-wrap gap-3\">\n <button\n type=\"button\"\n class=\"inline-flex items-center rounded-xl border border-(--rt-border-color) px-4 py-2 text-sm font-medium\"\n [disabled]=\"facade.saving()\"\n (click)=\"cancel()\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"inline-flex items-center rounded-xl bg-(--rt-accent) px-4 py-2 text-sm font-medium text-(--rt-accent-foreground)\"\n [disabled]=\"facade.saving()\"\n (click)=\"save()\"\n >\n @if (facade.saving()) { Saving... } @else if (isEdit) { Save Changes } @else { Create Client }\n </button>\n </div>\n </rolatech-platform-sticky-action-bar>\n }\n</section>\n" }]
|
|
278
|
+
}] });
|
|
279
|
+
|
|
280
|
+
export { PlatformAuthClientEditorPage };
|
|
281
|
+
//# sourceMappingURL=rolatech-angular-platform-platform-auth-client-editor-75GPRUsF.mjs.map
|