@memberjunction/ng-dashboards 5.29.0 → 5.30.0
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/dist/AI/components/agents/agent-configuration.component.d.ts.map +1 -1
- package/dist/AI/components/agents/agent-configuration.component.js +11 -9
- package/dist/AI/components/agents/agent-configuration.component.js.map +1 -1
- package/dist/AI/components/agents/agent-editor.component.d.ts.map +1 -1
- package/dist/AI/components/agents/agent-editor.component.js +21 -19
- package/dist/AI/components/agents/agent-editor.component.js.map +1 -1
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js +154 -154
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js.map +1 -1
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.d.ts.map +1 -1
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.js +8 -0
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.js.map +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.d.ts +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.d.ts.map +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.js +2 -2
- package/dist/AI/components/vectors/vector-management-resource.component.js.map +1 -1
- package/dist/APIKeys/api-applications-panel.component.d.ts +1 -1
- package/dist/APIKeys/api-applications-panel.component.d.ts.map +1 -1
- package/dist/APIKeys/api-applications-panel.component.js +15 -2
- package/dist/APIKeys/api-applications-panel.component.js.map +1 -1
- package/dist/APIKeys/api-key-edit-panel.component.d.ts.map +1 -1
- package/dist/APIKeys/api-key-edit-panel.component.js +17 -5
- package/dist/APIKeys/api-key-edit-panel.component.js.map +1 -1
- package/dist/Credentials/components/credentials-list-resource.component.d.ts.map +1 -1
- package/dist/Credentials/components/credentials-list-resource.component.js +56 -71
- package/dist/Credentials/components/credentials-list-resource.component.js.map +1 -1
- package/dist/DashboardBrowser/dashboard-browser-resource.component.d.ts +0 -8
- package/dist/DashboardBrowser/dashboard-browser-resource.component.d.ts.map +1 -1
- package/dist/DashboardBrowser/dashboard-browser-resource.component.js +73 -74
- package/dist/DashboardBrowser/dashboard-browser-resource.component.js.map +1 -1
- package/dist/DashboardBrowser/dashboard-share-adapter.d.ts +25 -0
- package/dist/DashboardBrowser/dashboard-share-adapter.d.ts.map +1 -0
- package/dist/DashboardBrowser/dashboard-share-adapter.js +99 -0
- package/dist/DashboardBrowser/dashboard-share-adapter.js.map +1 -0
- package/dist/DashboardBrowser/dashboard-share-dialog.component.d.ts +21 -104
- package/dist/DashboardBrowser/dashboard-share-dialog.component.d.ts.map +1 -1
- package/dist/DashboardBrowser/dashboard-share-dialog.component.js +48 -530
- package/dist/DashboardBrowser/dashboard-share-dialog.component.js.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts +5 -0
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.js +17 -0
- package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
- package/dist/Integration/components/connections/connections.component.d.ts +18 -1
- package/dist/Integration/components/connections/connections.component.d.ts.map +1 -1
- package/dist/Integration/components/connections/connections.component.js +251 -199
- package/dist/Integration/components/connections/connections.component.js.map +1 -1
- package/dist/Integration/services/integration-data.service.d.ts +7 -2
- package/dist/Integration/services/integration-data.service.d.ts.map +1 -1
- package/dist/Integration/services/integration-data.service.js +10 -2
- package/dist/Integration/services/integration-data.service.js.map +1 -1
- package/dist/KnowledgeHub/components/analytics/analytics-resource.component.js +144 -144
- package/dist/KnowledgeHub/components/analytics/analytics-resource.component.js.map +1 -1
- package/dist/Lists/components/lists-operations-resource.component.d.ts.map +1 -1
- package/dist/Lists/components/lists-operations-resource.component.js +12 -14
- package/dist/Lists/components/lists-operations-resource.component.js.map +1 -1
- package/dist/MCP/mcp-dashboard.component.d.ts.map +1 -1
- package/dist/MCP/mcp-dashboard.component.js +28 -57
- package/dist/MCP/mcp-dashboard.component.js.map +1 -1
- package/dist/Permissions/audit-log-resource.component.d.ts +38 -0
- package/dist/Permissions/audit-log-resource.component.d.ts.map +1 -0
- package/dist/Permissions/audit-log-resource.component.js +380 -0
- package/dist/Permissions/audit-log-resource.component.js.map +1 -0
- package/dist/Permissions/permissions-shared.d.ts +51 -0
- package/dist/Permissions/permissions-shared.d.ts.map +1 -0
- package/dist/Permissions/permissions-shared.js +91 -0
- package/dist/Permissions/permissions-shared.js.map +1 -0
- package/dist/Permissions/resource-access-resource.component.d.ts +45 -0
- package/dist/Permissions/resource-access-resource.component.d.ts.map +1 -0
- package/dist/Permissions/resource-access-resource.component.js +342 -0
- package/dist/Permissions/resource-access-resource.component.js.map +1 -0
- package/dist/Permissions/user-access-resource.component.d.ts +39 -0
- package/dist/Permissions/user-access-resource.component.d.ts.map +1 -0
- package/dist/Permissions/user-access-resource.component.js +346 -0
- package/dist/Permissions/user-access-resource.component.js.map +1 -0
- package/dist/component-studio-dashboards.module.d.ts +1 -0
- package/dist/component-studio-dashboards.module.d.ts.map +1 -1
- package/dist/component-studio-dashboards.module.js +7 -0
- package/dist/component-studio-dashboards.module.js.map +1 -1
- package/dist/core-dashboards.module.d.ts +24 -20
- package/dist/core-dashboards.module.d.ts.map +1 -1
- package/dist/core-dashboards.module.js +26 -1
- package/dist/core-dashboards.module.js.map +1 -1
- package/dist/public-api.d.ts +4 -1
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +4 -0
- package/dist/public-api.js.map +1 -1
- package/dist/shared/pipes/highlight-search.pipe.d.ts +1 -1
- package/dist/shared/pipes/highlight-search.pipe.d.ts.map +1 -1
- package/dist/shared/pipes/highlight-search.pipe.js +14 -4
- package/dist/shared/pipes/highlight-search.pipe.js.map +1 -1
- package/package.json +51 -50
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Metadata, RunView, UserInfo, UserRoleInfo } from '@memberjunction/core';
|
|
2
|
+
import { PERMISSION_DOMAIN_ICONS, PermissionEngine } from '@memberjunction/core-entities';
|
|
3
|
+
import { UUIDsEqual } from '@memberjunction/global';
|
|
4
|
+
/**
|
|
5
|
+
* Domain-to-icon lookup used by the User Access Report and the Audit Log tabs.
|
|
6
|
+
* Re-exports the shared constant from `@memberjunction/core-entities` so the admin
|
|
7
|
+
* dashboard and the end-user Sharing Center always render the same glyph. Add new
|
|
8
|
+
* domains in `core-entities/src/engines/PermissionEngine.ts`, not here.
|
|
9
|
+
*/
|
|
10
|
+
export const PERMISSIONS_DOMAIN_ICONS = PERMISSION_DOMAIN_ICONS;
|
|
11
|
+
/**
|
|
12
|
+
* Load every user from `MJ: Users` with just the columns the Permissions admin
|
|
13
|
+
* dropdowns need. Sorted by Name.
|
|
14
|
+
*/
|
|
15
|
+
export async function loadPermissionsUsers() {
|
|
16
|
+
const rv = new RunView();
|
|
17
|
+
const result = await rv.RunView({
|
|
18
|
+
EntityName: 'MJ: Users',
|
|
19
|
+
Fields: ['ID', 'Name', 'Email'],
|
|
20
|
+
OrderBy: 'Name',
|
|
21
|
+
ResultType: 'simple',
|
|
22
|
+
});
|
|
23
|
+
if (!result.Success) {
|
|
24
|
+
throw new Error(result.ErrorMessage ?? 'Failed to load users');
|
|
25
|
+
}
|
|
26
|
+
return result.Results ?? [];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Hydrate a full `UserInfo` for the user we want to report against. When the target
|
|
30
|
+
* is the current user we reuse `Metadata.CurrentUser` directly (it already has
|
|
31
|
+
* roles loaded). Otherwise we hit `MJ: User Roles` to assemble a UserInfo-shaped
|
|
32
|
+
* object with the target user's roles attached, because every provider needs roles
|
|
33
|
+
* to evaluate access correctly.
|
|
34
|
+
*/
|
|
35
|
+
export async function resolvePermissionsUser(userId, userDropdown) {
|
|
36
|
+
const md = new Metadata();
|
|
37
|
+
if (md.CurrentUser && UUIDsEqual(md.CurrentUser.ID, userId)) {
|
|
38
|
+
return md.CurrentUser;
|
|
39
|
+
}
|
|
40
|
+
const rv = new RunView();
|
|
41
|
+
const rolesResult = await rv.RunView({
|
|
42
|
+
EntityName: 'MJ: User Roles',
|
|
43
|
+
ExtraFilter: `UserID='${userId}'`,
|
|
44
|
+
Fields: ['ID', 'UserID', 'RoleID', 'Role'],
|
|
45
|
+
ResultType: 'simple',
|
|
46
|
+
});
|
|
47
|
+
const hit = userDropdown.find((u) => UUIDsEqual(u.ID, userId));
|
|
48
|
+
if (!hit)
|
|
49
|
+
return null;
|
|
50
|
+
const userRoles = (rolesResult.Results ?? []).map((r) => new UserRoleInfo({
|
|
51
|
+
ID: r.ID,
|
|
52
|
+
UserID: r.UserID,
|
|
53
|
+
RoleID: r.RoleID,
|
|
54
|
+
Role: r.Role,
|
|
55
|
+
}));
|
|
56
|
+
return new UserInfo(undefined, {
|
|
57
|
+
ID: hit.ID,
|
|
58
|
+
Name: hit.Name,
|
|
59
|
+
Email: hit.Email,
|
|
60
|
+
UserRoles: userRoles,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Bucket a flat list of {@link NormalizedPermission} into {@link PermissionsDomainGroup}s,
|
|
65
|
+
* sorted by each domain's catalog DisplayOrder (falling back to alpha when the
|
|
66
|
+
* engine's domain catalog doesn't know the domain).
|
|
67
|
+
*
|
|
68
|
+
* @param domainOrderMap map of domain name → DisplayOrder, typically built from
|
|
69
|
+
* `PermissionEngine.Instance.Domains`.
|
|
70
|
+
*/
|
|
71
|
+
export function groupPermissionsByDomain(rows, domainOrderMap) {
|
|
72
|
+
const bucket = new Map();
|
|
73
|
+
for (const row of rows) {
|
|
74
|
+
const list = bucket.get(row.DomainName) ?? [];
|
|
75
|
+
list.push(row);
|
|
76
|
+
bucket.set(row.DomainName, list);
|
|
77
|
+
}
|
|
78
|
+
const groups = [];
|
|
79
|
+
for (const [domainName, list] of bucket) {
|
|
80
|
+
groups.push({
|
|
81
|
+
DomainName: domainName,
|
|
82
|
+
Icon: PermissionEngine.DomainIconFor(domainName),
|
|
83
|
+
Count: list.length,
|
|
84
|
+
Expanded: list.length <= 10,
|
|
85
|
+
Rows: list.sort((a, b) => (a.ResourceName ?? '').localeCompare(b.ResourceName ?? '')),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
groups.sort((a, b) => (domainOrderMap.get(a.DomainName) ?? 999) - (domainOrderMap.get(b.DomainName) ?? 999));
|
|
89
|
+
return groups;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=permissions-shared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions-shared.js","sourceRoot":"","sources":["../../src/Permissions/permissions-shared.ts"],"names":[],"mappings":"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","sourcesContent":["import { Metadata, NormalizedPermission, RunView, UserInfo, UserRoleInfo } from '@memberjunction/core';\nimport { PERMISSION_DOMAIN_ICONS, PermissionEngine } from '@memberjunction/core-entities';\nimport { UUIDsEqual } from '@memberjunction/global';\n\n/**\n * Narrow shape the Permissions admin resource components need when rendering a user picker.\n * We only pull the handful of columns used in dropdown labels and UserInfo hydration.\n */\nexport interface PermissionsUserOption {\n ID: string;\n Name: string;\n Email: string;\n}\n\n/**\n * Domain-to-icon lookup used by the User Access Report and the Audit Log tabs.\n * Re-exports the shared constant from `@memberjunction/core-entities` so the admin\n * dashboard and the end-user Sharing Center always render the same glyph. Add new\n * domains in `core-entities/src/engines/PermissionEngine.ts`, not here.\n */\nexport const PERMISSIONS_DOMAIN_ICONS = PERMISSION_DOMAIN_ICONS;\n\n/**\n * A single domain section in the User Access Report — used by the UI to render\n * collapsible groups of {@link NormalizedPermission} rows.\n */\nexport interface PermissionsDomainGroup {\n DomainName: string;\n Icon: string;\n Count: number;\n Expanded: boolean;\n Rows: NormalizedPermission[];\n}\n\n/**\n * Load every user from `MJ: Users` with just the columns the Permissions admin\n * dropdowns need. Sorted by Name.\n */\nexport async function loadPermissionsUsers(): Promise<PermissionsUserOption[]> {\n const rv = new RunView();\n const result = await rv.RunView<PermissionsUserOption>({\n EntityName: 'MJ: Users',\n Fields: ['ID', 'Name', 'Email'],\n OrderBy: 'Name',\n ResultType: 'simple',\n });\n if (!result.Success) {\n throw new Error(result.ErrorMessage ?? 'Failed to load users');\n }\n return result.Results ?? [];\n}\n\n/**\n * Hydrate a full `UserInfo` for the user we want to report against. When the target\n * is the current user we reuse `Metadata.CurrentUser` directly (it already has\n * roles loaded). Otherwise we hit `MJ: User Roles` to assemble a UserInfo-shaped\n * object with the target user's roles attached, because every provider needs roles\n * to evaluate access correctly.\n */\nexport async function resolvePermissionsUser(\n userId: string,\n userDropdown: PermissionsUserOption[]\n): Promise<UserInfo | null> {\n const md = new Metadata();\n if (md.CurrentUser && UUIDsEqual(md.CurrentUser.ID, userId)) {\n return md.CurrentUser;\n }\n\n const rv = new RunView();\n const rolesResult = await rv.RunView<{\n ID: string;\n UserID: string;\n RoleID: string;\n Role: string;\n }>({\n EntityName: 'MJ: User Roles',\n ExtraFilter: `UserID='${userId}'`,\n Fields: ['ID', 'UserID', 'RoleID', 'Role'],\n ResultType: 'simple',\n });\n\n const hit = userDropdown.find((u) => UUIDsEqual(u.ID, userId));\n if (!hit) return null;\n\n const userRoles: UserRoleInfo[] = (rolesResult.Results ?? []).map(\n (r) =>\n new UserRoleInfo({\n ID: r.ID,\n UserID: r.UserID,\n RoleID: r.RoleID,\n Role: r.Role,\n })\n );\n\n return new UserInfo(undefined, {\n ID: hit.ID,\n Name: hit.Name,\n Email: hit.Email,\n UserRoles: userRoles,\n });\n}\n\n/**\n * Bucket a flat list of {@link NormalizedPermission} into {@link PermissionsDomainGroup}s,\n * sorted by each domain's catalog DisplayOrder (falling back to alpha when the\n * engine's domain catalog doesn't know the domain).\n *\n * @param domainOrderMap map of domain name → DisplayOrder, typically built from\n * `PermissionEngine.Instance.Domains`.\n */\nexport function groupPermissionsByDomain(\n rows: NormalizedPermission[],\n domainOrderMap: Map<string, number>\n): PermissionsDomainGroup[] {\n const bucket = new Map<string, NormalizedPermission[]>();\n for (const row of rows) {\n const list = bucket.get(row.DomainName) ?? [];\n list.push(row);\n bucket.set(row.DomainName, list);\n }\n\n const groups: PermissionsDomainGroup[] = [];\n for (const [domainName, list] of bucket) {\n groups.push({\n DomainName: domainName,\n Icon: PermissionEngine.DomainIconFor(domainName),\n Count: list.length,\n Expanded: list.length <= 10,\n Rows: list.sort((a, b) => (a.ResourceName ?? '').localeCompare(b.ResourceName ?? '')),\n });\n }\n\n groups.sort((a, b) => (domainOrderMap.get(a.DomainName) ?? 999) - (domainOrderMap.get(b.DomainName) ?? 999));\n return groups;\n}\n"]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ChangeDetectorRef, OnDestroy, OnInit } from '@angular/core';
|
|
2
|
+
import { NormalizedPermission } from '@memberjunction/core';
|
|
3
|
+
import { MJPermissionDomainEntity, ResourceData } from '@memberjunction/core-entities';
|
|
4
|
+
import { BaseResourceComponent } from '@memberjunction/ng-shared';
|
|
5
|
+
import * as i0 from "@angular/core";
|
|
6
|
+
/**
|
|
7
|
+
* Resource Access Report resource — one of three tabs in the Permissions admin application.
|
|
8
|
+
* Given a (domain, resource type, resource ID) triple, lists every grantee on that resource
|
|
9
|
+
* with their effective actions, effect (Allow/Deny), and optional expiration.
|
|
10
|
+
* Powered by `PermissionEngine.GetResourcePermissions`.
|
|
11
|
+
*/
|
|
12
|
+
export declare class PermissionsResourceAccessResourceComponent extends BaseResourceComponent implements OnInit, OnDestroy {
|
|
13
|
+
private cdr;
|
|
14
|
+
Domains: MJPermissionDomainEntity[];
|
|
15
|
+
SelectedDomainName: string | null;
|
|
16
|
+
/** Auto-populated whenever the selected domain changes. Empty = adapter doesn't enumerate its types. */
|
|
17
|
+
ResourceTypes: string[];
|
|
18
|
+
ResourceTypeInput: string;
|
|
19
|
+
ResourceIdInput: string;
|
|
20
|
+
ResourceAccessRows: NormalizedPermission[];
|
|
21
|
+
IsLoading: boolean;
|
|
22
|
+
LastQueryLabel: string | null;
|
|
23
|
+
ErrorMessage: string | null;
|
|
24
|
+
constructor(cdr: ChangeDetectorRef);
|
|
25
|
+
GetResourceDisplayName(_data: ResourceData): Promise<string>;
|
|
26
|
+
GetResourceIconClass(_data: ResourceData): Promise<string>;
|
|
27
|
+
ngOnInit(): Promise<void>;
|
|
28
|
+
ngOnDestroy(): void;
|
|
29
|
+
OnLookupResource(): Promise<void>;
|
|
30
|
+
OnDomainChanged(domainName: string): void;
|
|
31
|
+
/**
|
|
32
|
+
* Populate {@link ResourceTypes} from the provider behind `domainName`. If the
|
|
33
|
+
* current `ResourceTypeInput` isn't a member of the new list, clear it — the
|
|
34
|
+
* previous choice doesn't make sense against a different domain. When a
|
|
35
|
+
* domain has exactly one supported type we auto-select it so the user
|
|
36
|
+
* doesn't have to open the picker for a trivial choice.
|
|
37
|
+
*/
|
|
38
|
+
private loadResourceTypesForDomain;
|
|
39
|
+
TrackByResourceRow(_index: number, row: NormalizedPermission): string;
|
|
40
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<PermissionsResourceAccessResourceComponent, never>;
|
|
41
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<PermissionsResourceAccessResourceComponent, "mj-permissions-resource-access-resource", never, {}, {}, never, never, false, never>;
|
|
42
|
+
}
|
|
43
|
+
/** Tree-shaking prevention — referenced from `public-api.ts`. */
|
|
44
|
+
export declare function LoadPermissionsResourceAccessResource(): void;
|
|
45
|
+
//# sourceMappingURL=resource-access-resource.component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-access-resource.component.d.ts","sourceRoot":"","sources":["../../src/Permissions/resource-access-resource.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAa,SAAS,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,wBAAwB,EAAoB,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAEzG,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;;AAElE;;;;;GAKG;AACH,qBAOa,0CAA2C,SAAQ,qBAAsB,YAAW,MAAM,EAAE,SAAS;IAYlG,OAAO,CAAC,GAAG;IAXvB,OAAO,EAAE,wBAAwB,EAAE,CAAM;IACzC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAQ;IACzC,wGAAwG;IACxG,aAAa,EAAE,MAAM,EAAE,CAAM;IAC7B,iBAAiB,SAAM;IACvB,eAAe,SAAM;IACrB,kBAAkB,EAAE,oBAAoB,EAAE,CAAM;IAChD,SAAS,UAAS;IAClB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAQ;IACrC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;gBAEf,GAAG,EAAE,iBAAiB;IAI3B,sBAAsB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAI5D,oBAAoB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAI1D,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAU/B,WAAW,IAAI,IAAI;IAItB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAkCvC,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAQzC;;;;;;OAMG;IACH,OAAO,CAAC,0BAA0B;IAclC,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,oBAAoB,GAAG,MAAM;yCArG5D,0CAA0C;2CAA1C,0CAA0C;CAwGtD;AAED,iEAAiE;AACjE,wBAAgB,qCAAqC,IAAI,IAAI,CAE5D"}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { Component } from '@angular/core';
|
|
8
|
+
import { PermissionEngine } from '@memberjunction/core-entities';
|
|
9
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
10
|
+
import { BaseResourceComponent } from '@memberjunction/ng-shared';
|
|
11
|
+
import * as i0 from "@angular/core";
|
|
12
|
+
import * as i1 from "@angular/forms";
|
|
13
|
+
import * as i2 from "@memberjunction/ng-ui-components";
|
|
14
|
+
import * as i3 from "@memberjunction/ng-shared-generic";
|
|
15
|
+
import * as i4 from "@angular/common";
|
|
16
|
+
const _forTrack0 = ($index, $item) => $item.ID;
|
|
17
|
+
function PermissionsResourceAccessResourceComponent_Conditional_8_Template(rf, ctx) { if (rf & 1) {
|
|
18
|
+
i0.ɵɵelementStart(0, "div", 6);
|
|
19
|
+
i0.ɵɵelement(1, "i", 18);
|
|
20
|
+
i0.ɵɵelementStart(2, "span");
|
|
21
|
+
i0.ɵɵtext(3);
|
|
22
|
+
i0.ɵɵelementEnd()();
|
|
23
|
+
} if (rf & 2) {
|
|
24
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
25
|
+
i0.ɵɵadvance(3);
|
|
26
|
+
i0.ɵɵtextInterpolate(ctx_r0.ErrorMessage);
|
|
27
|
+
} }
|
|
28
|
+
function PermissionsResourceAccessResourceComponent_For_14_Template(rf, ctx) { if (rf & 1) {
|
|
29
|
+
i0.ɵɵelementStart(0, "option", 10);
|
|
30
|
+
i0.ɵɵtext(1);
|
|
31
|
+
i0.ɵɵelementEnd();
|
|
32
|
+
} if (rf & 2) {
|
|
33
|
+
const d_r2 = ctx.$implicit;
|
|
34
|
+
i0.ɵɵproperty("ngValue", d_r2.Name);
|
|
35
|
+
i0.ɵɵadvance();
|
|
36
|
+
i0.ɵɵtextInterpolate(d_r2.Name);
|
|
37
|
+
} }
|
|
38
|
+
function PermissionsResourceAccessResourceComponent_Conditional_22_Template(rf, ctx) { if (rf & 1) {
|
|
39
|
+
i0.ɵɵelement(0, "i", 19);
|
|
40
|
+
i0.ɵɵtext(1, " Looking up... ");
|
|
41
|
+
} }
|
|
42
|
+
function PermissionsResourceAccessResourceComponent_Conditional_23_Template(rf, ctx) { if (rf & 1) {
|
|
43
|
+
i0.ɵɵelement(0, "i", 20);
|
|
44
|
+
i0.ɵɵtext(1, " Lookup ");
|
|
45
|
+
} }
|
|
46
|
+
function PermissionsResourceAccessResourceComponent_Conditional_24_Template(rf, ctx) { if (rf & 1) {
|
|
47
|
+
i0.ɵɵelement(0, "mj-loading", 16);
|
|
48
|
+
} }
|
|
49
|
+
function PermissionsResourceAccessResourceComponent_Conditional_25_Template(rf, ctx) { if (rf & 1) {
|
|
50
|
+
i0.ɵɵelementStart(0, "div", 17);
|
|
51
|
+
i0.ɵɵelement(1, "i", 21);
|
|
52
|
+
i0.ɵɵelementStart(2, "p");
|
|
53
|
+
i0.ɵɵtext(3, "Pick a domain and enter a resource type + ID, then press Lookup to see who has access.");
|
|
54
|
+
i0.ɵɵelementEnd()();
|
|
55
|
+
} }
|
|
56
|
+
function PermissionsResourceAccessResourceComponent_Conditional_26_Template(rf, ctx) { if (rf & 1) {
|
|
57
|
+
i0.ɵɵelementStart(0, "div", 17);
|
|
58
|
+
i0.ɵɵelement(1, "i", 22);
|
|
59
|
+
i0.ɵɵelementStart(2, "p");
|
|
60
|
+
i0.ɵɵtext(3, "No grantees found for ");
|
|
61
|
+
i0.ɵɵelementStart(4, "strong");
|
|
62
|
+
i0.ɵɵtext(5);
|
|
63
|
+
i0.ɵɵelementEnd();
|
|
64
|
+
i0.ɵɵtext(6, ".");
|
|
65
|
+
i0.ɵɵelementEnd()();
|
|
66
|
+
} if (rf & 2) {
|
|
67
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
68
|
+
i0.ɵɵadvance(5);
|
|
69
|
+
i0.ɵɵtextInterpolate(ctx_r0.LastQueryLabel);
|
|
70
|
+
} }
|
|
71
|
+
function PermissionsResourceAccessResourceComponent_Conditional_27_For_22_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
72
|
+
i0.ɵɵelementStart(0, "span", 28);
|
|
73
|
+
i0.ɵɵtext(1);
|
|
74
|
+
i0.ɵɵelementEnd();
|
|
75
|
+
} if (rf & 2) {
|
|
76
|
+
const row_r3 = i0.ɵɵnextContext().$implicit;
|
|
77
|
+
i0.ɵɵadvance();
|
|
78
|
+
i0.ɵɵtextInterpolate(row_r3.GranteeID);
|
|
79
|
+
} }
|
|
80
|
+
function PermissionsResourceAccessResourceComponent_Conditional_27_For_22_For_10_Template(rf, ctx) { if (rf & 1) {
|
|
81
|
+
i0.ɵɵelementStart(0, "span", 30);
|
|
82
|
+
i0.ɵɵtext(1);
|
|
83
|
+
i0.ɵɵelementEnd();
|
|
84
|
+
} if (rf & 2) {
|
|
85
|
+
const action_r4 = ctx.$implicit;
|
|
86
|
+
i0.ɵɵadvance();
|
|
87
|
+
i0.ɵɵtextInterpolate(action_r4);
|
|
88
|
+
} }
|
|
89
|
+
function PermissionsResourceAccessResourceComponent_Conditional_27_For_22_Conditional_15_Template(rf, ctx) { if (rf & 1) {
|
|
90
|
+
i0.ɵɵelementStart(0, "span");
|
|
91
|
+
i0.ɵɵtext(1);
|
|
92
|
+
i0.ɵɵpipe(2, "date");
|
|
93
|
+
i0.ɵɵelementEnd();
|
|
94
|
+
} if (rf & 2) {
|
|
95
|
+
const row_r3 = i0.ɵɵnextContext().$implicit;
|
|
96
|
+
i0.ɵɵadvance();
|
|
97
|
+
i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(2, 1, row_r3.ExpiresAt, "short"));
|
|
98
|
+
} }
|
|
99
|
+
function PermissionsResourceAccessResourceComponent_Conditional_27_For_22_Conditional_16_Template(rf, ctx) { if (rf & 1) {
|
|
100
|
+
i0.ɵɵelementStart(0, "span", 32);
|
|
101
|
+
i0.ɵɵtext(1, "\u2014");
|
|
102
|
+
i0.ɵɵelementEnd();
|
|
103
|
+
} }
|
|
104
|
+
function PermissionsResourceAccessResourceComponent_Conditional_27_For_22_Template(rf, ctx) { if (rf & 1) {
|
|
105
|
+
i0.ɵɵelementStart(0, "tr")(1, "td", 26)(2, "span", 27);
|
|
106
|
+
i0.ɵɵtext(3);
|
|
107
|
+
i0.ɵɵelementEnd();
|
|
108
|
+
i0.ɵɵconditionalCreate(4, PermissionsResourceAccessResourceComponent_Conditional_27_For_22_Conditional_4_Template, 2, 1, "span", 28);
|
|
109
|
+
i0.ɵɵelementEnd();
|
|
110
|
+
i0.ɵɵelementStart(5, "td");
|
|
111
|
+
i0.ɵɵtext(6);
|
|
112
|
+
i0.ɵɵelementEnd();
|
|
113
|
+
i0.ɵɵelementStart(7, "td")(8, "div", 29);
|
|
114
|
+
i0.ɵɵrepeaterCreate(9, PermissionsResourceAccessResourceComponent_Conditional_27_For_22_For_10_Template, 2, 1, "span", 30, i0.ɵɵrepeaterTrackByIdentity);
|
|
115
|
+
i0.ɵɵelementEnd()();
|
|
116
|
+
i0.ɵɵelementStart(11, "td")(12, "span", 31);
|
|
117
|
+
i0.ɵɵtext(13);
|
|
118
|
+
i0.ɵɵelementEnd()();
|
|
119
|
+
i0.ɵɵelementStart(14, "td");
|
|
120
|
+
i0.ɵɵconditionalCreate(15, PermissionsResourceAccessResourceComponent_Conditional_27_For_22_Conditional_15_Template, 3, 4, "span")(16, PermissionsResourceAccessResourceComponent_Conditional_27_For_22_Conditional_16_Template, 2, 0, "span", 32);
|
|
121
|
+
i0.ɵɵelementEnd()();
|
|
122
|
+
} if (rf & 2) {
|
|
123
|
+
const row_r3 = ctx.$implicit;
|
|
124
|
+
i0.ɵɵadvance(3);
|
|
125
|
+
i0.ɵɵtextInterpolate(row_r3.GranteeName || row_r3.GranteeID || row_r3.GranteeType);
|
|
126
|
+
i0.ɵɵadvance();
|
|
127
|
+
i0.ɵɵconditional(row_r3.GranteeID && row_r3.GranteeName && row_r3.GranteeName !== row_r3.GranteeID ? 4 : -1);
|
|
128
|
+
i0.ɵɵadvance(2);
|
|
129
|
+
i0.ɵɵtextInterpolate(row_r3.GranteeType);
|
|
130
|
+
i0.ɵɵadvance(3);
|
|
131
|
+
i0.ɵɵrepeater(row_r3.Actions);
|
|
132
|
+
i0.ɵɵadvance(3);
|
|
133
|
+
i0.ɵɵclassProp("effect-allow", row_r3.Effect === "Allow")("effect-deny", row_r3.Effect === "Deny");
|
|
134
|
+
i0.ɵɵadvance();
|
|
135
|
+
i0.ɵɵtextInterpolate1(" ", row_r3.Effect, " ");
|
|
136
|
+
i0.ɵɵadvance(2);
|
|
137
|
+
i0.ɵɵconditional(row_r3.ExpiresAt ? 15 : 16);
|
|
138
|
+
} }
|
|
139
|
+
function PermissionsResourceAccessResourceComponent_Conditional_27_Template(rf, ctx) { if (rf & 1) {
|
|
140
|
+
i0.ɵɵelementStart(0, "div", 23)(1, "strong");
|
|
141
|
+
i0.ɵɵtext(2);
|
|
142
|
+
i0.ɵɵelementEnd();
|
|
143
|
+
i0.ɵɵtext(3);
|
|
144
|
+
i0.ɵɵelementStart(4, "strong");
|
|
145
|
+
i0.ɵɵtext(5);
|
|
146
|
+
i0.ɵɵelementEnd()();
|
|
147
|
+
i0.ɵɵelementStart(6, "div", 24)(7, "table", 25)(8, "thead")(9, "tr")(10, "th");
|
|
148
|
+
i0.ɵɵtext(11, "Grantee");
|
|
149
|
+
i0.ɵɵelementEnd();
|
|
150
|
+
i0.ɵɵelementStart(12, "th");
|
|
151
|
+
i0.ɵɵtext(13, "Type");
|
|
152
|
+
i0.ɵɵelementEnd();
|
|
153
|
+
i0.ɵɵelementStart(14, "th");
|
|
154
|
+
i0.ɵɵtext(15, "Actions");
|
|
155
|
+
i0.ɵɵelementEnd();
|
|
156
|
+
i0.ɵɵelementStart(16, "th");
|
|
157
|
+
i0.ɵɵtext(17, "Effect");
|
|
158
|
+
i0.ɵɵelementEnd();
|
|
159
|
+
i0.ɵɵelementStart(18, "th");
|
|
160
|
+
i0.ɵɵtext(19, "Expires");
|
|
161
|
+
i0.ɵɵelementEnd()()();
|
|
162
|
+
i0.ɵɵelementStart(20, "tbody");
|
|
163
|
+
i0.ɵɵrepeaterCreate(21, PermissionsResourceAccessResourceComponent_Conditional_27_For_22_Template, 17, 9, "tr", null, i0.ɵɵcomponentInstance().TrackByResourceRow, true);
|
|
164
|
+
i0.ɵɵelementEnd()()();
|
|
165
|
+
} if (rf & 2) {
|
|
166
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
167
|
+
i0.ɵɵadvance(2);
|
|
168
|
+
i0.ɵɵtextInterpolate(ctx_r0.ResourceAccessRows.length);
|
|
169
|
+
i0.ɵɵadvance();
|
|
170
|
+
i0.ɵɵtextInterpolate1(" grantee", ctx_r0.ResourceAccessRows.length === 1 ? "" : "s", " on ");
|
|
171
|
+
i0.ɵɵadvance(2);
|
|
172
|
+
i0.ɵɵtextInterpolate(ctx_r0.LastQueryLabel);
|
|
173
|
+
i0.ɵɵadvance(16);
|
|
174
|
+
i0.ɵɵrepeater(ctx_r0.ResourceAccessRows);
|
|
175
|
+
} }
|
|
176
|
+
/**
|
|
177
|
+
* Resource Access Report resource — one of three tabs in the Permissions admin application.
|
|
178
|
+
* Given a (domain, resource type, resource ID) triple, lists every grantee on that resource
|
|
179
|
+
* with their effective actions, effect (Allow/Deny), and optional expiration.
|
|
180
|
+
* Powered by `PermissionEngine.GetResourcePermissions`.
|
|
181
|
+
*/
|
|
182
|
+
let PermissionsResourceAccessResourceComponent = class PermissionsResourceAccessResourceComponent extends BaseResourceComponent {
|
|
183
|
+
cdr;
|
|
184
|
+
Domains = [];
|
|
185
|
+
SelectedDomainName = null;
|
|
186
|
+
/** Auto-populated whenever the selected domain changes. Empty = adapter doesn't enumerate its types. */
|
|
187
|
+
ResourceTypes = [];
|
|
188
|
+
ResourceTypeInput = '';
|
|
189
|
+
ResourceIdInput = '';
|
|
190
|
+
ResourceAccessRows = [];
|
|
191
|
+
IsLoading = false;
|
|
192
|
+
LastQueryLabel = null;
|
|
193
|
+
ErrorMessage = null;
|
|
194
|
+
constructor(cdr) {
|
|
195
|
+
super();
|
|
196
|
+
this.cdr = cdr;
|
|
197
|
+
}
|
|
198
|
+
async GetResourceDisplayName(_data) {
|
|
199
|
+
return 'Resource Access Report';
|
|
200
|
+
}
|
|
201
|
+
async GetResourceIconClass(_data) {
|
|
202
|
+
return 'fa-solid fa-cube';
|
|
203
|
+
}
|
|
204
|
+
async ngOnInit() {
|
|
205
|
+
super.ngOnInit();
|
|
206
|
+
this.Domains = PermissionEngine.Instance.Domains;
|
|
207
|
+
if (this.Domains.length > 0) {
|
|
208
|
+
this.SelectedDomainName = this.Domains[0].Name;
|
|
209
|
+
this.loadResourceTypesForDomain(this.SelectedDomainName);
|
|
210
|
+
}
|
|
211
|
+
this.NotifyLoadComplete();
|
|
212
|
+
}
|
|
213
|
+
ngOnDestroy() {
|
|
214
|
+
super.ngOnDestroy();
|
|
215
|
+
}
|
|
216
|
+
async OnLookupResource() {
|
|
217
|
+
if (!this.SelectedDomainName) {
|
|
218
|
+
this.ErrorMessage = 'Pick a permission domain first.';
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (!this.ResourceTypeInput.trim() || !this.ResourceIdInput.trim()) {
|
|
222
|
+
this.ErrorMessage = 'Resource type and resource ID are both required.';
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
this.ErrorMessage = null;
|
|
226
|
+
this.IsLoading = true;
|
|
227
|
+
this.ResourceAccessRows = [];
|
|
228
|
+
this.LastQueryLabel = null;
|
|
229
|
+
this.cdr.detectChanges();
|
|
230
|
+
try {
|
|
231
|
+
const rows = await PermissionEngine.Instance.GetResourcePermissions(this.SelectedDomainName, this.ResourceTypeInput.trim(), this.ResourceIdInput.trim());
|
|
232
|
+
this.ResourceAccessRows = rows.sort((a, b) => (a.GranteeName ?? '').localeCompare(b.GranteeName ?? ''));
|
|
233
|
+
this.LastQueryLabel = `${this.SelectedDomainName} / ${this.ResourceTypeInput.trim()} / ${this.ResourceIdInput.trim()}`;
|
|
234
|
+
}
|
|
235
|
+
catch (e) {
|
|
236
|
+
this.ErrorMessage = `Error looking up resource: ${e instanceof Error ? e.message : String(e)}`;
|
|
237
|
+
}
|
|
238
|
+
this.IsLoading = false;
|
|
239
|
+
this.cdr.detectChanges();
|
|
240
|
+
}
|
|
241
|
+
OnDomainChanged(domainName) {
|
|
242
|
+
this.SelectedDomainName = domainName;
|
|
243
|
+
this.loadResourceTypesForDomain(domainName);
|
|
244
|
+
this.ResourceAccessRows = [];
|
|
245
|
+
this.LastQueryLabel = null;
|
|
246
|
+
this.cdr.detectChanges();
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Populate {@link ResourceTypes} from the provider behind `domainName`. If the
|
|
250
|
+
* current `ResourceTypeInput` isn't a member of the new list, clear it — the
|
|
251
|
+
* previous choice doesn't make sense against a different domain. When a
|
|
252
|
+
* domain has exactly one supported type we auto-select it so the user
|
|
253
|
+
* doesn't have to open the picker for a trivial choice.
|
|
254
|
+
*/
|
|
255
|
+
loadResourceTypesForDomain(domainName) {
|
|
256
|
+
if (!domainName) {
|
|
257
|
+
this.ResourceTypes = [];
|
|
258
|
+
this.ResourceTypeInput = '';
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
this.ResourceTypes = PermissionEngine.Instance.GetResourceTypes(domainName);
|
|
262
|
+
if (this.ResourceTypes.length === 1) {
|
|
263
|
+
this.ResourceTypeInput = this.ResourceTypes[0];
|
|
264
|
+
}
|
|
265
|
+
else if (!this.ResourceTypes.includes(this.ResourceTypeInput)) {
|
|
266
|
+
this.ResourceTypeInput = '';
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
TrackByResourceRow(_index, row) {
|
|
270
|
+
return row.SourceRecordID ?? `${row.GranteeType}|${row.GranteeID ?? ''}`;
|
|
271
|
+
}
|
|
272
|
+
static ɵfac = function PermissionsResourceAccessResourceComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PermissionsResourceAccessResourceComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
|
|
273
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PermissionsResourceAccessResourceComponent, selectors: [["mj-permissions-resource-access-resource"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 28, vars: 14, consts: [[1, "permissions-container"], [1, "header"], [1, "header-left"], [1, "fa-solid", "fa-cube", "header-icon"], [1, "header-title"], [1, "description"], [1, "message", "error-message"], [1, "selector-row", "wrap"], ["for", "domain-select", 1, "selector-label"], ["id", "domain-select", 1, "mj-input", 3, "ngModelChange", "ngModel", "disabled"], [3, "ngValue"], ["for", "rtype-input", 1, "selector-label"], ["id", "rtype-input", "Placeholder", "Select or type a resource type", 3, "ngModelChange", "Data", "ValuePrimitive", "Filterable", "AllowCustom", "Disabled", "ngModel"], ["for", "rid-input", 1, "selector-label"], ["id", "rid-input", "placeholder", "UUID or record ID", 1, "mj-input", "mono", 3, "ngModelChange", "ngModel", "disabled"], ["mjButton", "", "variant", "primary", 3, "click", "disabled"], ["text", "Looking up grantees...", "size", "medium"], [1, "empty-state"], [1, "fa-solid", "fa-circle-exclamation"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-magnifying-glass"], [1, "fa-solid", "fa-cube"], [1, "fa-solid", "fa-user-slash"], [1, "total-summary"], [1, "domain-body"], [1, "perms-table"], [1, "resource-cell"], [1, "resource-name"], [1, "resource-id"], [1, "action-chips"], [1, "action-chip"], [1, "effect-badge"], [1, "muted"]], template: function PermissionsResourceAccessResourceComponent_Template(rf, ctx) { if (rf & 1) {
|
|
274
|
+
i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div", 2);
|
|
275
|
+
i0.ɵɵelement(3, "i", 3);
|
|
276
|
+
i0.ɵɵelementStart(4, "h2", 4);
|
|
277
|
+
i0.ɵɵtext(5, "Resource Access Report");
|
|
278
|
+
i0.ɵɵelementEnd()()();
|
|
279
|
+
i0.ɵɵelementStart(6, "p", 5);
|
|
280
|
+
i0.ɵɵtext(7, " Look up every grantee for a specific resource. Pick a permission domain, supply the resource type and the resource's ID, then press Lookup. ");
|
|
281
|
+
i0.ɵɵelementEnd();
|
|
282
|
+
i0.ɵɵconditionalCreate(8, PermissionsResourceAccessResourceComponent_Conditional_8_Template, 4, 1, "div", 6);
|
|
283
|
+
i0.ɵɵelementStart(9, "div", 7)(10, "label", 8);
|
|
284
|
+
i0.ɵɵtext(11, "Domain:");
|
|
285
|
+
i0.ɵɵelementEnd();
|
|
286
|
+
i0.ɵɵelementStart(12, "select", 9);
|
|
287
|
+
i0.ɵɵlistener("ngModelChange", function PermissionsResourceAccessResourceComponent_Template_select_ngModelChange_12_listener($event) { return ctx.OnDomainChanged($event); });
|
|
288
|
+
i0.ɵɵrepeaterCreate(13, PermissionsResourceAccessResourceComponent_For_14_Template, 2, 2, "option", 10, _forTrack0);
|
|
289
|
+
i0.ɵɵelementEnd();
|
|
290
|
+
i0.ɵɵelementStart(15, "label", 11);
|
|
291
|
+
i0.ɵɵtext(16, "Resource Type:");
|
|
292
|
+
i0.ɵɵelementEnd();
|
|
293
|
+
i0.ɵɵelementStart(17, "mj-combobox", 12);
|
|
294
|
+
i0.ɵɵtwoWayListener("ngModelChange", function PermissionsResourceAccessResourceComponent_Template_mj_combobox_ngModelChange_17_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.ResourceTypeInput, $event) || (ctx.ResourceTypeInput = $event); return $event; });
|
|
295
|
+
i0.ɵɵelementEnd();
|
|
296
|
+
i0.ɵɵelementStart(18, "label", 13);
|
|
297
|
+
i0.ɵɵtext(19, "Resource ID:");
|
|
298
|
+
i0.ɵɵelementEnd();
|
|
299
|
+
i0.ɵɵelementStart(20, "input", 14);
|
|
300
|
+
i0.ɵɵtwoWayListener("ngModelChange", function PermissionsResourceAccessResourceComponent_Template_input_ngModelChange_20_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.ResourceIdInput, $event) || (ctx.ResourceIdInput = $event); return $event; });
|
|
301
|
+
i0.ɵɵelementEnd();
|
|
302
|
+
i0.ɵɵelementStart(21, "button", 15);
|
|
303
|
+
i0.ɵɵlistener("click", function PermissionsResourceAccessResourceComponent_Template_button_click_21_listener() { return ctx.OnLookupResource(); });
|
|
304
|
+
i0.ɵɵconditionalCreate(22, PermissionsResourceAccessResourceComponent_Conditional_22_Template, 2, 0)(23, PermissionsResourceAccessResourceComponent_Conditional_23_Template, 2, 0);
|
|
305
|
+
i0.ɵɵelementEnd()();
|
|
306
|
+
i0.ɵɵconditionalCreate(24, PermissionsResourceAccessResourceComponent_Conditional_24_Template, 1, 0, "mj-loading", 16)(25, PermissionsResourceAccessResourceComponent_Conditional_25_Template, 4, 0, "div", 17)(26, PermissionsResourceAccessResourceComponent_Conditional_26_Template, 7, 1, "div", 17)(27, PermissionsResourceAccessResourceComponent_Conditional_27_Template, 23, 3);
|
|
307
|
+
i0.ɵɵelementEnd();
|
|
308
|
+
} if (rf & 2) {
|
|
309
|
+
i0.ɵɵadvance(8);
|
|
310
|
+
i0.ɵɵconditional(ctx.ErrorMessage ? 8 : -1);
|
|
311
|
+
i0.ɵɵadvance(4);
|
|
312
|
+
i0.ɵɵproperty("ngModel", ctx.SelectedDomainName)("disabled", ctx.IsLoading);
|
|
313
|
+
i0.ɵɵadvance();
|
|
314
|
+
i0.ɵɵrepeater(ctx.Domains);
|
|
315
|
+
i0.ɵɵadvance(4);
|
|
316
|
+
i0.ɵɵproperty("Data", ctx.ResourceTypes)("ValuePrimitive", true)("Filterable", true)("AllowCustom", true)("Disabled", ctx.IsLoading || !ctx.SelectedDomainName);
|
|
317
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx.ResourceTypeInput);
|
|
318
|
+
i0.ɵɵadvance(3);
|
|
319
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx.ResourceIdInput);
|
|
320
|
+
i0.ɵɵproperty("disabled", ctx.IsLoading);
|
|
321
|
+
i0.ɵɵadvance();
|
|
322
|
+
i0.ɵɵproperty("disabled", ctx.IsLoading);
|
|
323
|
+
i0.ɵɵadvance();
|
|
324
|
+
i0.ɵɵconditional(ctx.IsLoading ? 22 : 23);
|
|
325
|
+
i0.ɵɵadvance(2);
|
|
326
|
+
i0.ɵɵconditional(ctx.IsLoading ? 24 : !ctx.LastQueryLabel ? 25 : ctx.ResourceAccessRows.length === 0 ? 26 : 27);
|
|
327
|
+
} }, dependencies: [i1.NgSelectOption, i1.ɵNgSelectMultipleOption, i1.DefaultValueAccessor, i1.SelectControlValueAccessor, i1.NgControlStatus, i1.NgModel, i2.MJButtonDirective, i2.MJComboboxComponent, i3.LoadingComponent, i4.DatePipe], styles: [".permissions-container[_ngcontent-%COMP%] {\n padding: 24px;\n max-width: 1080px;\n margin: 0 auto;\n height: 100%;\n overflow-y: auto;\n box-sizing: border-box;\n}\n\n\n\n.header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 12px;\n}\n\n.header-left[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.header-icon[_ngcontent-%COMP%] {\n font-size: 24px;\n color: var(--mj-brand-primary);\n}\n\n.header-title[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 22px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.header-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n align-items: center;\n}\n\n.description[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n font-size: 14px;\n margin-bottom: 20px;\n line-height: 1.5;\n}\n\n\n\n.tab-bar[_ngcontent-%COMP%] {\n display: flex;\n gap: 4px;\n border-bottom: 1px solid var(--mj-border-default);\n margin-bottom: 16px;\n}\n\n.tab[_ngcontent-%COMP%] {\n background: transparent;\n border: none;\n padding: 10px 16px;\n cursor: pointer;\n color: var(--mj-text-secondary);\n font: inherit;\n font-weight: 500;\n border-bottom: 2px solid transparent;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n\n.tab[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-hover);\n}\n\n.tab-active[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n border-bottom-color: var(--mj-brand-primary);\n}\n\n.selector-row.wrap[_ngcontent-%COMP%] {\n flex-wrap: wrap;\n}\n\n.mj-input.mono[_ngcontent-%COMP%] {\n font-family: var(--mj-font-mono, monospace);\n min-width: 300px;\n}\n\n.muted[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n}\n\n\n\n.message[_ngcontent-%COMP%] {\n padding: 10px 14px;\n border-radius: 6px;\n margin-bottom: 16px;\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n}\n\n.error-message[_ngcontent-%COMP%] {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n border: 1px solid var(--mj-status-error-border);\n}\n\n\n\n.user-selector-row[_ngcontent-%COMP%], \n.selector-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 20px;\n flex-wrap: wrap;\n}\n\n.user-selector-label[_ngcontent-%COMP%], \n.selector-label[_ngcontent-%COMP%] {\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n#user-select[_ngcontent-%COMP%], \n#domain-select[_ngcontent-%COMP%] {\n min-width: 320px;\n flex: 0 1 auto;\n}\n\n.roles-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border-radius: 12px;\n font-size: 12px;\n font-weight: 500;\n}\n\n\n\n.empty-state[_ngcontent-%COMP%] {\n text-align: center;\n padding: 48px 16px;\n color: var(--mj-text-muted);\n border: 1px dashed var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface-card);\n}\n\n.empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 32px;\n margin-bottom: 12px;\n display: block;\n}\n\n.empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n}\n\n.total-summary[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n font-size: 13px;\n margin-bottom: 12px;\n}\n\n.total-summary[_ngcontent-%COMP%] strong[_ngcontent-%COMP%] {\n color: var(--mj-text-primary);\n}\n\n\n\n.domain-groups[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.domain-group[_ngcontent-%COMP%] {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n background: var(--mj-bg-surface);\n}\n\n.domain-header[_ngcontent-%COMP%] {\n width: 100%;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n background: var(--mj-bg-surface-card);\n border: none;\n cursor: pointer;\n text-align: left;\n color: var(--mj-text-primary);\n font: inherit;\n}\n\n.domain-header[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.domain-header-left[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.domain-icon[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-size: 16px;\n}\n\n.domain-name[_ngcontent-%COMP%] {\n font-weight: 600;\n font-size: 15px;\n}\n\n.domain-count[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n height: 20px;\n padding: 0 8px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-radius: 10px;\n font-size: 12px;\n font-weight: 600;\n}\n\n.toggle-icon[_ngcontent-%COMP%] {\n transition: transform 0.15s ease;\n color: var(--mj-text-muted);\n}\n\n.toggle-icon.rotated[_ngcontent-%COMP%] {\n transform: rotate(180deg);\n}\n\n.domain-body[_ngcontent-%COMP%] {\n padding: 0;\n background: var(--mj-bg-surface);\n overflow-x: auto;\n}\n\n\n\n.perms-table[_ngcontent-%COMP%] {\n width: 100%;\n border-collapse: collapse;\n font-size: 13px;\n}\n\n.perms-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%], \n.perms-table[_ngcontent-%COMP%] td[_ngcontent-%COMP%] {\n padding: 8px 16px;\n text-align: left;\n border-bottom: 1px solid var(--mj-border-subtle);\n vertical-align: top;\n}\n\n.perms-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n font-weight: 600;\n text-transform: uppercase;\n font-size: 11px;\n letter-spacing: 0.5px;\n}\n\n.perms-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:last-child td[_ngcontent-%COMP%] {\n border-bottom: none;\n}\n\n.perms-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.resource-cell[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.resource-name[_ngcontent-%COMP%] {\n color: var(--mj-text-primary);\n font-weight: 500;\n}\n\n.resource-id[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 11px;\n font-family: var(--mj-font-mono, monospace);\n}\n\n\n\n.action-chips[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n}\n\n.action-chip[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n}\n\n\n\n.effect-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n.effect-allow[_ngcontent-%COMP%] {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n}\n\n.effect-deny[_ngcontent-%COMP%] {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n}"] });
|
|
328
|
+
};
|
|
329
|
+
PermissionsResourceAccessResourceComponent = __decorate([
|
|
330
|
+
RegisterClass(BaseResourceComponent, 'PermissionsResourceAccessResource')
|
|
331
|
+
], PermissionsResourceAccessResourceComponent);
|
|
332
|
+
export { PermissionsResourceAccessResourceComponent };
|
|
333
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PermissionsResourceAccessResourceComponent, [{
|
|
334
|
+
type: Component,
|
|
335
|
+
args: [{ standalone: false, selector: 'mj-permissions-resource-access-resource', template: "<div class=\"permissions-container\">\n <div class=\"header\">\n <div class=\"header-left\">\n <i class=\"fa-solid fa-cube header-icon\"></i>\n <h2 class=\"header-title\">Resource Access Report</h2>\n </div>\n </div>\n\n <p class=\"description\">\n Look up every grantee for a specific resource. Pick a permission domain, supply the resource\n type and the resource's ID, then press Lookup.\n </p>\n\n @if (ErrorMessage) {\n <div class=\"message error-message\">\n <i class=\"fa-solid fa-circle-exclamation\"></i>\n <span>{{ ErrorMessage }}</span>\n </div>\n }\n\n <div class=\"selector-row wrap\">\n <label class=\"selector-label\" for=\"domain-select\">Domain:</label>\n <select\n id=\"domain-select\"\n class=\"mj-input\"\n [ngModel]=\"SelectedDomainName\"\n (ngModelChange)=\"OnDomainChanged($event)\"\n [disabled]=\"IsLoading\"\n >\n @for (d of Domains; track d.ID) {\n <option [ngValue]=\"d.Name\">{{ d.Name }}</option>\n }\n </select>\n\n <label class=\"selector-label\" for=\"rtype-input\">Resource Type:</label>\n <mj-combobox\n id=\"rtype-input\"\n [Data]=\"ResourceTypes\"\n [ValuePrimitive]=\"true\"\n [Filterable]=\"true\"\n [AllowCustom]=\"true\"\n Placeholder=\"Select or type a resource type\"\n [Disabled]=\"IsLoading || !SelectedDomainName\"\n [(ngModel)]=\"ResourceTypeInput\">\n </mj-combobox>\n\n <label class=\"selector-label\" for=\"rid-input\">Resource ID:</label>\n <input\n id=\"rid-input\"\n class=\"mj-input mono\"\n placeholder=\"UUID or record ID\"\n [(ngModel)]=\"ResourceIdInput\"\n [disabled]=\"IsLoading\"\n />\n\n <button\n mjButton\n variant=\"primary\"\n (click)=\"OnLookupResource()\"\n [disabled]=\"IsLoading\"\n >\n @if (IsLoading) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Looking up...\n } @else {\n <i class=\"fa-solid fa-magnifying-glass\"></i> Lookup\n }\n </button>\n </div>\n\n @if (IsLoading) {\n <mj-loading text=\"Looking up grantees...\" size=\"medium\"></mj-loading>\n } @else if (!LastQueryLabel) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-cube\"></i>\n <p>Pick a domain and enter a resource type + ID, then press Lookup to see who has access.</p>\n </div>\n } @else if (ResourceAccessRows.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-user-slash\"></i>\n <p>No grantees found for <strong>{{ LastQueryLabel }}</strong>.</p>\n </div>\n } @else {\n <div class=\"total-summary\">\n <strong>{{ ResourceAccessRows.length }}</strong> grantee{{ ResourceAccessRows.length === 1 ? '' : 's' }}\n on <strong>{{ LastQueryLabel }}</strong>\n </div>\n\n <div class=\"domain-body\">\n <table class=\"perms-table\">\n <thead>\n <tr>\n <th>Grantee</th>\n <th>Type</th>\n <th>Actions</th>\n <th>Effect</th>\n <th>Expires</th>\n </tr>\n </thead>\n <tbody>\n @for (row of ResourceAccessRows; track TrackByResourceRow($index, row)) {\n <tr>\n <td class=\"resource-cell\">\n <span class=\"resource-name\">{{ row.GranteeName || row.GranteeID || row.GranteeType }}</span>\n @if (row.GranteeID && row.GranteeName && row.GranteeName !== row.GranteeID) {\n <span class=\"resource-id\">{{ row.GranteeID }}</span>\n }\n </td>\n <td>{{ row.GranteeType }}</td>\n <td>\n <div class=\"action-chips\">\n @for (action of row.Actions; track action) {\n <span class=\"action-chip\">{{ action }}</span>\n }\n </div>\n </td>\n <td>\n <span class=\"effect-badge\" [class.effect-allow]=\"row.Effect === 'Allow'\" [class.effect-deny]=\"row.Effect === 'Deny'\">\n {{ row.Effect }}\n </span>\n </td>\n <td>\n @if (row.ExpiresAt) {\n <span>{{ row.ExpiresAt | date:'short' }}</span>\n } @else {\n <span class=\"muted\">\u2014</span>\n }\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n</div>\n", styles: [".permissions-container {\n padding: 24px;\n max-width: 1080px;\n margin: 0 auto;\n height: 100%;\n overflow-y: auto;\n box-sizing: border-box;\n}\n\n/* Header */\n.header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 12px;\n}\n\n.header-left {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.header-icon {\n font-size: 24px;\n color: var(--mj-brand-primary);\n}\n\n.header-title {\n margin: 0;\n font-size: 22px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.header-actions {\n display: flex;\n gap: 8px;\n align-items: center;\n}\n\n.description {\n color: var(--mj-text-secondary);\n font-size: 14px;\n margin-bottom: 20px;\n line-height: 1.5;\n}\n\n/* Tab bar */\n.tab-bar {\n display: flex;\n gap: 4px;\n border-bottom: 1px solid var(--mj-border-default);\n margin-bottom: 16px;\n}\n\n.tab {\n background: transparent;\n border: none;\n padding: 10px 16px;\n cursor: pointer;\n color: var(--mj-text-secondary);\n font: inherit;\n font-weight: 500;\n border-bottom: 2px solid transparent;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n\n.tab:hover {\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-hover);\n}\n\n.tab-active {\n color: var(--mj-brand-primary);\n border-bottom-color: var(--mj-brand-primary);\n}\n\n.selector-row.wrap {\n flex-wrap: wrap;\n}\n\n.mj-input.mono {\n font-family: var(--mj-font-mono, monospace);\n min-width: 300px;\n}\n\n.muted {\n color: var(--mj-text-muted);\n}\n\n/* Messages */\n.message {\n padding: 10px 14px;\n border-radius: 6px;\n margin-bottom: 16px;\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n}\n\n.error-message {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n border: 1px solid var(--mj-status-error-border);\n}\n\n/* User selector (legacy name kept for User Access tab, plus generic selector-row for Resource tab) */\n.user-selector-row,\n.selector-row {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 20px;\n flex-wrap: wrap;\n}\n\n.user-selector-label,\n.selector-label {\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n#user-select,\n#domain-select {\n min-width: 320px;\n flex: 0 1 auto;\n}\n\n.roles-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border-radius: 12px;\n font-size: 12px;\n font-weight: 500;\n}\n\n/* Empty state */\n.empty-state {\n text-align: center;\n padding: 48px 16px;\n color: var(--mj-text-muted);\n border: 1px dashed var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface-card);\n}\n\n.empty-state i {\n font-size: 32px;\n margin-bottom: 12px;\n display: block;\n}\n\n.empty-state p {\n margin: 0;\n font-size: 14px;\n}\n\n.total-summary {\n color: var(--mj-text-secondary);\n font-size: 13px;\n margin-bottom: 12px;\n}\n\n.total-summary strong {\n color: var(--mj-text-primary);\n}\n\n/* Domain groups */\n.domain-groups {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.domain-group {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n background: var(--mj-bg-surface);\n}\n\n.domain-header {\n width: 100%;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n background: var(--mj-bg-surface-card);\n border: none;\n cursor: pointer;\n text-align: left;\n color: var(--mj-text-primary);\n font: inherit;\n}\n\n.domain-header:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.domain-header-left {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.domain-icon {\n color: var(--mj-brand-primary);\n font-size: 16px;\n}\n\n.domain-name {\n font-weight: 600;\n font-size: 15px;\n}\n\n.domain-count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n height: 20px;\n padding: 0 8px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-radius: 10px;\n font-size: 12px;\n font-weight: 600;\n}\n\n.toggle-icon {\n transition: transform 0.15s ease;\n color: var(--mj-text-muted);\n}\n\n.toggle-icon.rotated {\n transform: rotate(180deg);\n}\n\n.domain-body {\n padding: 0;\n background: var(--mj-bg-surface);\n overflow-x: auto;\n}\n\n/* Permissions table */\n.perms-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 13px;\n}\n\n.perms-table th,\n.perms-table td {\n padding: 8px 16px;\n text-align: left;\n border-bottom: 1px solid var(--mj-border-subtle);\n vertical-align: top;\n}\n\n.perms-table th {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n font-weight: 600;\n text-transform: uppercase;\n font-size: 11px;\n letter-spacing: 0.5px;\n}\n\n.perms-table tbody tr:last-child td {\n border-bottom: none;\n}\n\n.perms-table tbody tr:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.resource-cell {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.resource-name {\n color: var(--mj-text-primary);\n font-weight: 500;\n}\n\n.resource-id {\n color: var(--mj-text-muted);\n font-size: 11px;\n font-family: var(--mj-font-mono, monospace);\n}\n\n/* Action chips */\n.action-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n}\n\n.action-chip {\n display: inline-flex;\n align-items: center;\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n}\n\n/* Effect badge */\n.effect-badge {\n display: inline-flex;\n align-items: center;\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n.effect-allow {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n}\n\n.effect-deny {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n}\n"] }]
|
|
336
|
+
}], () => [{ type: i0.ChangeDetectorRef }], null); })();
|
|
337
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PermissionsResourceAccessResourceComponent, { className: "PermissionsResourceAccessResourceComponent", filePath: "src/Permissions/resource-access-resource.component.ts", lineNumber: 20 }); })();
|
|
338
|
+
/** Tree-shaking prevention — referenced from `public-api.ts`. */
|
|
339
|
+
export function LoadPermissionsResourceAccessResource() {
|
|
340
|
+
// intentionally empty
|
|
341
|
+
}
|
|
342
|
+
//# sourceMappingURL=resource-access-resource.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-access-resource.component.js","sourceRoot":"","sources":["../../src/Permissions/resource-access-resource.component.ts","../../src/Permissions/resource-access-resource.component.html"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAqB,SAAS,EAAqB,MAAM,eAAe,CAAC;AAEhF,OAAO,EAA4B,gBAAgB,EAAgB,MAAM,+BAA+B,CAAC;AACzG,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;;;;;;;;ICU9D,8BAAmC;IACjC,wBAA8C;IAC9C,4BAAM;IAAA,YAAkB;IAC1B,AAD0B,iBAAO,EAC3B;;;IADE,eAAkB;IAAlB,yCAAkB;;;IActB,kCAA2B;IAAA,YAAY;IAAA,iBAAS;;;IAAxC,mCAAkB;IAAC,cAAY;IAAZ,+BAAY;;;IAgCvC,wBAA2C;IAAC,+BAC9C;;;IACE,wBAA4C;IAAC,wBAC/C;;;IAKF,iCAAqE;;;IAErE,+BAAyB;IACvB,wBAAgC;IAChC,yBAAG;IAAA,sGAAsF;IAC3F,AAD2F,iBAAI,EACzF;;;IAEN,+BAAyB;IACvB,wBAAsC;IACtC,yBAAG;IAAA,sCAAsB;IAAA,8BAAQ;IAAA,YAAoB;IAAA,iBAAS;IAAA,iBAAC;IACjE,AADiE,iBAAI,EAC/D;;;IAD6B,eAAoB;IAApB,2CAAoB;;;IAyBzC,gCAA0B;IAAA,YAAmB;IAAA,iBAAO;;;IAA1B,cAAmB;IAAnB,sCAAmB;;;IAO3C,gCAA0B;IAAA,YAAY;IAAA,iBAAO;;;IAAnB,cAAY;IAAZ,+BAAY;;;IAWxC,4BAAM;IAAA,YAAkC;;IAAA,iBAAO;;;IAAzC,cAAkC;IAAlC,qEAAkC;;;IAExC,gCAAoB;IAAA,sBAAC;IAAA,iBAAO;;;IAtB9B,AADF,AADF,0BAAI,aACwB,eACI;IAAA,YAAyD;IAAA,iBAAO;IAC5F,oIAA6E;IAG/E,iBAAK;IACL,0BAAI;IAAA,YAAqB;IAAA,iBAAK;IAE5B,AADF,0BAAI,cACwB;IACxB,wJAEC;IAEL,AADE,iBAAM,EACH;IAEH,AADF,2BAAI,gBACmH;IACnH,aACF;IACF,AADE,iBAAO,EACJ;IACL,2BAAI;IAGA,AAFF,kIAAqB,gHAEZ;IAIb,AADE,iBAAK,EACF;;;IAzB2B,eAAyD;IAAzD,kFAAyD;IACrF,cAEC;IAFD,4GAEC;IAEC,eAAqB;IAArB,wCAAqB;IAGrB,eAEC;IAFD,6BAEC;IAIwB,eAA6C;IAAC,AAA9C,yDAA6C,yCAA4C;IAClH,cACF;IADE,8CACF;IAGA,eAIC;IAJD,4CAIC;;;IA1CX,AADF,+BAA2B,aACjB;IAAA,YAA+B;IAAA,iBAAS;IAAC,YAC9C;IAAA,8BAAQ;IAAA,YAAoB;IACjC,AADiC,iBAAS,EACpC;IAME,AADF,AADF,AADF,AADF,+BAAyB,gBACI,YAClB,SACD,UACE;IAAA,wBAAO;IAAA,iBAAK;IAChB,2BAAI;IAAA,qBAAI;IAAA,iBAAK;IACb,2BAAI;IAAA,wBAAO;IAAA,iBAAK;IAChB,2BAAI;IAAA,uBAAM;IAAA,iBAAK;IACf,2BAAI;IAAA,wBAAO;IAEf,AADE,AADa,iBAAK,EACb,EACC;IACR,8BAAO;IACL,wKA6BC;IAGP,AADE,AADE,iBAAQ,EACF,EACJ;;;IAhDI,eAA+B;IAA/B,sDAA+B;IAAU,cAC9C;IAD8C,4FAC9C;IAAQ,eAAoB;IAApB,2CAAoB;IAe3B,gBA6BC;IA7BD,wCA6BC;;AD1HX;;;;;GAKG;AAQI,IAAM,0CAA0C,GAAhD,MAAM,0CAA2C,SAAQ,qBAAqB;IAY7D;IAXpB,OAAO,GAA+B,EAAE,CAAC;IACzC,kBAAkB,GAAkB,IAAI,CAAC;IACzC,wGAAwG;IACxG,aAAa,GAAa,EAAE,CAAC;IAC7B,iBAAiB,GAAG,EAAE,CAAC;IACvB,eAAe,GAAG,EAAE,CAAC;IACrB,kBAAkB,GAA2B,EAAE,CAAC;IAChD,SAAS,GAAG,KAAK,CAAC;IAClB,cAAc,GAAkB,IAAI,CAAC;IACrC,YAAY,GAAkB,IAAI,CAAC;IAEnC,YAAoB,GAAsB;QACtC,KAAK,EAAE,CAAC;QADQ,QAAG,GAAH,GAAG,CAAmB;IAE1C,CAAC;IAEQ,KAAK,CAAC,sBAAsB,CAAC,KAAmB;QACrD,OAAO,wBAAwB,CAAC;IACpC,CAAC;IAEQ,KAAK,CAAC,oBAAoB,CAAC,KAAmB;QACnD,OAAO,kBAAkB,CAAC;IAC9B,CAAC;IAEQ,KAAK,CAAC,QAAQ;QACnB,KAAK,CAAC,QAAQ,EAAE,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,gBAAgB,CAAC,QAAQ,CAAC,OAAO,CAAC;QACjD,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAC/C,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QAC7D,CAAC;QACD,IAAI,CAAC,kBAAkB,EAAE,CAAC;IAC9B,CAAC;IAEQ,WAAW;QAChB,KAAK,CAAC,WAAW,EAAE,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,gBAAgB;QAClB,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC3B,IAAI,CAAC,YAAY,GAAG,iCAAiC,CAAC;YACtD,OAAO;QACX,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,EAAE,CAAC;YACjE,IAAI,CAAC,YAAY,GAAG,kDAAkD,CAAC;YACvE,OAAO;QACX,CAAC;QAED,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,kBAAkB,GAAG,EAAE,CAAC;QAC7B,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC3B,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QAEzB,IAAI,CAAC;YACD,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC,sBAAsB,CAC/D,IAAI,CAAC,kBAAkB,EACvB,IAAI,CAAC,iBAAiB,CAAC,IAAI,EAAE,EAC7B,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,CAC9B,CAAC;YACF,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACzC,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,CAC3D,CAAC;YACF,IAAI,CAAC,cAAc,GAAG,GAAG,IAAI,CAAC,kBAAkB,MAAM,IAAI,CAAC,iBAAiB,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,EAAE,CAAC;QAC3H,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,IAAI,CAAC,YAAY,GAAG,8BAA8B,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACnG,CAAC;QAED,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;QACvB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;IAC7B,CAAC;IAED,eAAe,CAAC,UAAkB;QAC9B,IAAI,CAAC,kBAAkB,GAAG,UAAU,CAAC;QACrC,IAAI,CAAC,0BAA0B,CAAC,UAAU,CAAC,CAAC;QAC5C,IAAI,CAAC,kBAAkB,GAAG,EAAE,CAAC;QAC7B,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC3B,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;IAC7B,CAAC;IAED;;;;;;OAMG;IACK,0BAA0B,CAAC,UAAyB;QACxD,IAAI,CAAC,UAAU,EAAE,CAAC;YACd,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;YACxB,IAAI,CAAC,iBAAiB,GAAG,EAAE,CAAC;YAC5B,OAAO;QACX,CAAC;QACD,IAAI,CAAC,aAAa,GAAG,gBAAgB,CAAC,QAAQ,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;QAC5E,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAClC,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;QACnD,CAAC;aAAM,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC;YAC9D,IAAI,CAAC,iBAAiB,GAAG,EAAE,CAAC;QAChC,CAAC;IACL,CAAC;IAED,kBAAkB,CAAC,MAAc,EAAE,GAAyB;QACxD,OAAO,GAAG,CAAC,cAAc,IAAI,GAAG,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC;IAC7E,CAAC;oIAvGQ,0CAA0C;6DAA1C,0CAA0C;YCjBnD,AADF,AADF,8BAAmC,aACb,aACO;YACvB,uBAA4C;YAC5C,6BAAyB;YAAA,sCAAsB;YAEnD,AADE,AADiD,iBAAK,EAChD,EACF;YAEN,4BAAuB;YACrB,6JAEF;YAAA,iBAAI;YAEJ,4GAAoB;YAQlB,AADF,8BAA+B,gBACqB;YAAA,wBAAO;YAAA,iBAAQ;YACjE,kCAMC;YAFC,8IAAiB,2BAAuB,IAAC;YAGzC,mHAEC;YACH,iBAAS;YAET,kCAAgD;YAAA,+BAAc;YAAA,iBAAQ;YACtE,wCAQkC;YAAhC,6PAA+B;YACjC,iBAAc;YAEd,kCAA8C;YAAA,6BAAY;YAAA,iBAAQ;YAClE,kCAME;YAFA,mPAA6B;YAJ/B,iBAME;YAEF,mCAKC;YAFC,wHAAS,sBAAkB,IAAC;YAK1B,AAFF,oGAAiB,8EAER;YAIb,AADE,iBAAS,EACL;YAcJ,AALA,AALA,AAFF,sHAAiB,yFAEa,yFAKgB,+EAKrC;YAoDX,iBAAM;;YAxHJ,eAKC;YALD,2CAKC;YAOG,eAA8B;YAE9B,AAFA,gDAA8B,2BAER;YAEtB,cAEC;YAFD,0BAEC;YAMD,eAAsB;YAKtB,AAFA,AADA,AADA,AADA,wCAAsB,wBACC,oBACJ,qBACC,sDAEyB;YAC7C,qDAA+B;YAQ/B,eAA6B;YAA7B,mDAA6B;YAC7B,wCAAsB;YAOtB,cAAsB;YAAtB,wCAAsB;YAEtB,cAIC;YAJD,yCAIC;YAIL,eA+DC;YA/DD,+GA+DC;;;ADjHU,0CAA0C;IAPtD,aAAa,CAAC,qBAAqB,EAAE,mCAAmC,CAAC;GAO7D,0CAA0C,CAwGtD;;iFAxGY,0CAA0C;cANtD,SAAS;6BACM,KAAK,YACP,yCAAyC;;kFAI1C,0CAA0C;AA0GvD,iEAAiE;AACjE,MAAM,UAAU,qCAAqC;IACjD,sBAAsB;AAC1B,CAAC","sourcesContent":["import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';\nimport { NormalizedPermission } from '@memberjunction/core';\nimport { MJPermissionDomainEntity, PermissionEngine, ResourceData } from '@memberjunction/core-entities';\nimport { RegisterClass } from '@memberjunction/global';\nimport { BaseResourceComponent } from '@memberjunction/ng-shared';\n\n/**\n * Resource Access Report resource — one of three tabs in the Permissions admin application.\n * Given a (domain, resource type, resource ID) triple, lists every grantee on that resource\n * with their effective actions, effect (Allow/Deny), and optional expiration.\n * Powered by `PermissionEngine.GetResourcePermissions`.\n */\n@RegisterClass(BaseResourceComponent, 'PermissionsResourceAccessResource')\n@Component({\n standalone: false,\n selector: 'mj-permissions-resource-access-resource',\n templateUrl: './resource-access-resource.component.html',\n styleUrls: ['./permissions-resource.component.css'],\n})\nexport class PermissionsResourceAccessResourceComponent extends BaseResourceComponent implements OnInit, OnDestroy {\n Domains: MJPermissionDomainEntity[] = [];\n SelectedDomainName: string | null = null;\n /** Auto-populated whenever the selected domain changes. Empty = adapter doesn't enumerate its types. */\n ResourceTypes: string[] = [];\n ResourceTypeInput = '';\n ResourceIdInput = '';\n ResourceAccessRows: NormalizedPermission[] = [];\n IsLoading = false;\n LastQueryLabel: string | null = null;\n ErrorMessage: string | null = null;\n\n constructor(private cdr: ChangeDetectorRef) {\n super();\n }\n\n override async GetResourceDisplayName(_data: ResourceData): Promise<string> {\n return 'Resource Access Report';\n }\n\n override async GetResourceIconClass(_data: ResourceData): Promise<string> {\n return 'fa-solid fa-cube';\n }\n\n override async ngOnInit(): Promise<void> {\n super.ngOnInit();\n this.Domains = PermissionEngine.Instance.Domains;\n if (this.Domains.length > 0) {\n this.SelectedDomainName = this.Domains[0].Name;\n this.loadResourceTypesForDomain(this.SelectedDomainName);\n }\n this.NotifyLoadComplete();\n }\n\n override ngOnDestroy(): void {\n super.ngOnDestroy();\n }\n\n async OnLookupResource(): Promise<void> {\n if (!this.SelectedDomainName) {\n this.ErrorMessage = 'Pick a permission domain first.';\n return;\n }\n if (!this.ResourceTypeInput.trim() || !this.ResourceIdInput.trim()) {\n this.ErrorMessage = 'Resource type and resource ID are both required.';\n return;\n }\n\n this.ErrorMessage = null;\n this.IsLoading = true;\n this.ResourceAccessRows = [];\n this.LastQueryLabel = null;\n this.cdr.detectChanges();\n\n try {\n const rows = await PermissionEngine.Instance.GetResourcePermissions(\n this.SelectedDomainName,\n this.ResourceTypeInput.trim(),\n this.ResourceIdInput.trim()\n );\n this.ResourceAccessRows = rows.sort((a, b) =>\n (a.GranteeName ?? '').localeCompare(b.GranteeName ?? '')\n );\n this.LastQueryLabel = `${this.SelectedDomainName} / ${this.ResourceTypeInput.trim()} / ${this.ResourceIdInput.trim()}`;\n } catch (e) {\n this.ErrorMessage = `Error looking up resource: ${e instanceof Error ? e.message : String(e)}`;\n }\n\n this.IsLoading = false;\n this.cdr.detectChanges();\n }\n\n OnDomainChanged(domainName: string): void {\n this.SelectedDomainName = domainName;\n this.loadResourceTypesForDomain(domainName);\n this.ResourceAccessRows = [];\n this.LastQueryLabel = null;\n this.cdr.detectChanges();\n }\n\n /**\n * Populate {@link ResourceTypes} from the provider behind `domainName`. If the\n * current `ResourceTypeInput` isn't a member of the new list, clear it — the\n * previous choice doesn't make sense against a different domain. When a\n * domain has exactly one supported type we auto-select it so the user\n * doesn't have to open the picker for a trivial choice.\n */\n private loadResourceTypesForDomain(domainName: string | null): void {\n if (!domainName) {\n this.ResourceTypes = [];\n this.ResourceTypeInput = '';\n return;\n }\n this.ResourceTypes = PermissionEngine.Instance.GetResourceTypes(domainName);\n if (this.ResourceTypes.length === 1) {\n this.ResourceTypeInput = this.ResourceTypes[0];\n } else if (!this.ResourceTypes.includes(this.ResourceTypeInput)) {\n this.ResourceTypeInput = '';\n }\n }\n\n TrackByResourceRow(_index: number, row: NormalizedPermission): string {\n return row.SourceRecordID ?? `${row.GranteeType}|${row.GranteeID ?? ''}`;\n }\n}\n\n/** Tree-shaking prevention — referenced from `public-api.ts`. */\nexport function LoadPermissionsResourceAccessResource(): void {\n // intentionally empty\n}\n","<div class=\"permissions-container\">\n <div class=\"header\">\n <div class=\"header-left\">\n <i class=\"fa-solid fa-cube header-icon\"></i>\n <h2 class=\"header-title\">Resource Access Report</h2>\n </div>\n </div>\n\n <p class=\"description\">\n Look up every grantee for a specific resource. Pick a permission domain, supply the resource\n type and the resource's ID, then press Lookup.\n </p>\n\n @if (ErrorMessage) {\n <div class=\"message error-message\">\n <i class=\"fa-solid fa-circle-exclamation\"></i>\n <span>{{ ErrorMessage }}</span>\n </div>\n }\n\n <div class=\"selector-row wrap\">\n <label class=\"selector-label\" for=\"domain-select\">Domain:</label>\n <select\n id=\"domain-select\"\n class=\"mj-input\"\n [ngModel]=\"SelectedDomainName\"\n (ngModelChange)=\"OnDomainChanged($event)\"\n [disabled]=\"IsLoading\"\n >\n @for (d of Domains; track d.ID) {\n <option [ngValue]=\"d.Name\">{{ d.Name }}</option>\n }\n </select>\n\n <label class=\"selector-label\" for=\"rtype-input\">Resource Type:</label>\n <mj-combobox\n id=\"rtype-input\"\n [Data]=\"ResourceTypes\"\n [ValuePrimitive]=\"true\"\n [Filterable]=\"true\"\n [AllowCustom]=\"true\"\n Placeholder=\"Select or type a resource type\"\n [Disabled]=\"IsLoading || !SelectedDomainName\"\n [(ngModel)]=\"ResourceTypeInput\">\n </mj-combobox>\n\n <label class=\"selector-label\" for=\"rid-input\">Resource ID:</label>\n <input\n id=\"rid-input\"\n class=\"mj-input mono\"\n placeholder=\"UUID or record ID\"\n [(ngModel)]=\"ResourceIdInput\"\n [disabled]=\"IsLoading\"\n />\n\n <button\n mjButton\n variant=\"primary\"\n (click)=\"OnLookupResource()\"\n [disabled]=\"IsLoading\"\n >\n @if (IsLoading) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Looking up...\n } @else {\n <i class=\"fa-solid fa-magnifying-glass\"></i> Lookup\n }\n </button>\n </div>\n\n @if (IsLoading) {\n <mj-loading text=\"Looking up grantees...\" size=\"medium\"></mj-loading>\n } @else if (!LastQueryLabel) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-cube\"></i>\n <p>Pick a domain and enter a resource type + ID, then press Lookup to see who has access.</p>\n </div>\n } @else if (ResourceAccessRows.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-user-slash\"></i>\n <p>No grantees found for <strong>{{ LastQueryLabel }}</strong>.</p>\n </div>\n } @else {\n <div class=\"total-summary\">\n <strong>{{ ResourceAccessRows.length }}</strong> grantee{{ ResourceAccessRows.length === 1 ? '' : 's' }}\n on <strong>{{ LastQueryLabel }}</strong>\n </div>\n\n <div class=\"domain-body\">\n <table class=\"perms-table\">\n <thead>\n <tr>\n <th>Grantee</th>\n <th>Type</th>\n <th>Actions</th>\n <th>Effect</th>\n <th>Expires</th>\n </tr>\n </thead>\n <tbody>\n @for (row of ResourceAccessRows; track TrackByResourceRow($index, row)) {\n <tr>\n <td class=\"resource-cell\">\n <span class=\"resource-name\">{{ row.GranteeName || row.GranteeID || row.GranteeType }}</span>\n @if (row.GranteeID && row.GranteeName && row.GranteeName !== row.GranteeID) {\n <span class=\"resource-id\">{{ row.GranteeID }}</span>\n }\n </td>\n <td>{{ row.GranteeType }}</td>\n <td>\n <div class=\"action-chips\">\n @for (action of row.Actions; track action) {\n <span class=\"action-chip\">{{ action }}</span>\n }\n </div>\n </td>\n <td>\n <span class=\"effect-badge\" [class.effect-allow]=\"row.Effect === 'Allow'\" [class.effect-deny]=\"row.Effect === 'Deny'\">\n {{ row.Effect }}\n </span>\n </td>\n <td>\n @if (row.ExpiresAt) {\n <span>{{ row.ExpiresAt | date:'short' }}</span>\n } @else {\n <span class=\"muted\">—</span>\n }\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n</div>\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ChangeDetectorRef, OnDestroy, OnInit } from '@angular/core';
|
|
2
|
+
import { NormalizedPermission, PermissionAction } from '@memberjunction/core';
|
|
3
|
+
import { ResourceData } from '@memberjunction/core-entities';
|
|
4
|
+
import { BaseResourceComponent } from '@memberjunction/ng-shared';
|
|
5
|
+
import { PermissionsDomainGroup, PermissionsUserOption } from './permissions-shared';
|
|
6
|
+
import * as i0 from "@angular/core";
|
|
7
|
+
/**
|
|
8
|
+
* User Access Report resource — one of three tabs in the Permissions admin application.
|
|
9
|
+
* Lets an admin pick any user and see every resource that user has access to across
|
|
10
|
+
* every registered permission domain. Powered by `PermissionEngine.GetAllUserPermissions`.
|
|
11
|
+
*/
|
|
12
|
+
export declare class PermissionsUserAccessResourceComponent extends BaseResourceComponent implements OnInit, OnDestroy {
|
|
13
|
+
private cdr;
|
|
14
|
+
Users: PermissionsUserOption[];
|
|
15
|
+
SelectedUserId: string | null;
|
|
16
|
+
SelectedUserRoles: string[];
|
|
17
|
+
DomainGroups: PermissionsDomainGroup[];
|
|
18
|
+
IsLoadingUsers: boolean;
|
|
19
|
+
IsLoadingPermissions: boolean;
|
|
20
|
+
ErrorMessage: string | null;
|
|
21
|
+
constructor(cdr: ChangeDetectorRef);
|
|
22
|
+
GetResourceDisplayName(_data: ResourceData): Promise<string>;
|
|
23
|
+
GetResourceIconClass(_data: ResourceData): Promise<string>;
|
|
24
|
+
ngOnInit(): Promise<void>;
|
|
25
|
+
ngOnDestroy(): void;
|
|
26
|
+
loadUsers(): Promise<void>;
|
|
27
|
+
OnUserChanged(userId: string): Promise<void>;
|
|
28
|
+
private loadPermissionsForSelectedUser;
|
|
29
|
+
ToggleGroup(group: PermissionsDomainGroup): void;
|
|
30
|
+
TrackByDomain(_index: number, group: PermissionsDomainGroup): string;
|
|
31
|
+
TrackByPermission(_index: number, row: NormalizedPermission): string;
|
|
32
|
+
ActionsLabel(actions: PermissionAction[]): string;
|
|
33
|
+
get TotalCount(): number;
|
|
34
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<PermissionsUserAccessResourceComponent, never>;
|
|
35
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<PermissionsUserAccessResourceComponent, "mj-permissions-user-access-resource", never, {}, {}, never, never, false, never>;
|
|
36
|
+
}
|
|
37
|
+
/** Tree-shaking prevention — referenced from `public-api.ts`. */
|
|
38
|
+
export declare function LoadPermissionsUserAccessResource(): void;
|
|
39
|
+
//# sourceMappingURL=user-access-resource.component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-access-resource.component.d.ts","sourceRoot":"","sources":["../../src/Permissions/user-access-resource.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAa,SAAS,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAY,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxF,OAAO,EAAoB,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE/E,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAElE,OAAO,EACH,sBAAsB,EACtB,qBAAqB,EAIxB,MAAM,sBAAsB,CAAC;;AAE9B;;;;GAIG;AACH,qBAOa,sCAAuC,SAAQ,qBAAsB,YAAW,MAAM,EAAE,SAAS;IAS9F,OAAO,CAAC,GAAG;IARvB,KAAK,EAAE,qBAAqB,EAAE,CAAM;IACpC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAQ;IACrC,iBAAiB,EAAE,MAAM,EAAE,CAAM;IACjC,YAAY,EAAE,sBAAsB,EAAE,CAAM;IAC5C,cAAc,UAAQ;IACtB,oBAAoB,UAAS;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;gBAEf,GAAG,EAAE,iBAAiB;IAI3B,sBAAsB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAI5D,oBAAoB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAI1D,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAM/B,WAAW,IAAI,IAAI;IAItB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAiB1B,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAKpC,8BAA8B;IA+B5C,WAAW,CAAC,KAAK,EAAE,sBAAsB,GAAG,IAAI;IAKhD,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,sBAAsB,GAAG,MAAM;IAIpE,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,oBAAoB,GAAG,MAAM;IAIpE,YAAY,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,MAAM;IAIjD,IAAI,UAAU,IAAI,MAAM,CAEvB;yCAvGQ,sCAAsC;2CAAtC,sCAAsC;CAwGlD;AAED,iEAAiE;AACjE,wBAAgB,iCAAiC,IAAI,IAAI,CAExD"}
|