@idp.global/catalog 3.6.0 → 5.0.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/changelog.md +19 -0
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/elements/idp-admin-overview.d.ts +3 -2
- package/dist_ts_web/elements/idp-admin-overview.js +5 -5
- package/dist_ts_web/elements/idp-admin-shared.d.ts +5 -0
- package/dist_ts_web/elements/idp-admin-shared.js +25 -1
- package/dist_ts_web/elements/idp-admin-shell.d.ts +3 -10
- package/dist_ts_web/elements/idp-admin-shell.js +11 -21
- package/package.json +8 -9
- package/readme.md +30 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements/idp-admin-overview.ts +15 -6
- package/ts_web/elements/idp-admin-shared.ts +31 -0
- package/ts_web/elements/idp-admin-shell.ts +25 -32
- package/dist_bundle/bundle.js +0 -10389
- package/html/index.html +0 -29
- package/html/index.ts +0 -39
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idp.global/catalog",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Web component catalog for idp.global, based on the v5 Apple-system design language.",
|
|
6
6
|
"exports": {
|
|
@@ -12,25 +12,24 @@
|
|
|
12
12
|
"author": "Task Venture Capital GmbH",
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@design.estate/dees-catalog": "^3.
|
|
15
|
+
"@design.estate/dees-catalog": "^3.96.3",
|
|
16
16
|
"@design.estate/dees-domtools": "^2.5.6",
|
|
17
|
-
"@design.estate/dees-element": "^2.
|
|
18
|
-
"@design.estate/dees-wcctools": "^3.9.
|
|
19
|
-
"
|
|
17
|
+
"@design.estate/dees-element": "^2.4.0",
|
|
18
|
+
"@design.estate/dees-wcctools": "^3.9.2",
|
|
19
|
+
"@idp.global/interfaces": "^13.0.0",
|
|
20
|
+
"lucide": "^1.24.0"
|
|
20
21
|
},
|
|
21
22
|
"devDependencies": {
|
|
22
23
|
"@git.zone/tsbuild": "^4.4.2",
|
|
23
|
-
"@git.zone/tsbundle": "^2.11.
|
|
24
|
+
"@git.zone/tsbundle": "^2.11.2",
|
|
24
25
|
"@git.zone/tswatch": "^3.3.5",
|
|
25
26
|
"@push.rocks/projectinfo": "^5.1.0",
|
|
26
|
-
"@types/node": "^
|
|
27
|
+
"@types/node": "^26.1.1"
|
|
27
28
|
},
|
|
28
29
|
"files": [
|
|
29
30
|
"ts_web/**/*",
|
|
30
31
|
"dist_ts_web/**/*",
|
|
31
|
-
"dist_bundle/**/*",
|
|
32
32
|
"assets/**/*",
|
|
33
|
-
"html/**/*",
|
|
34
33
|
"license",
|
|
35
34
|
"readme.md",
|
|
36
35
|
"changelog.md"
|
package/readme.md
CHANGED
|
@@ -78,6 +78,36 @@ The catalog uses shared tokens from `ts_web/elements/tokens.ts` for color, type,
|
|
|
78
78
|
|
|
79
79
|
This package is component-only. It does not perform authentication, store sessions, call the idp.global backend, or implement business logic. Use it together with `@idp.global/sdk/browser` and `@idp.global/interfaces` when you need live identity behavior.
|
|
80
80
|
|
|
81
|
+
Admin activity surfaces accept `TIdpAdminActivity`, an alias of the structured
|
|
82
|
+
`@idp.global/interfaces` activity DTO. Activity rendering uses finite actions,
|
|
83
|
+
outcomes, target types, and reason codes; it does not accept record IDs,
|
|
84
|
+
free-text descriptions, IP addresses, user agents, or generic metadata.
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import {
|
|
88
|
+
formatAdminActivityDetail,
|
|
89
|
+
formatAdminActivityTitle,
|
|
90
|
+
getAdminActivityTone,
|
|
91
|
+
type TIdpAdminActivity,
|
|
92
|
+
} from '@idp.global/catalog';
|
|
93
|
+
|
|
94
|
+
const activity: TIdpAdminActivity = {
|
|
95
|
+
action: 'passkey_login',
|
|
96
|
+
timestamp: Date.now(),
|
|
97
|
+
outcome: 'succeeded',
|
|
98
|
+
targetType: 'passkey',
|
|
99
|
+
facts: null,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const title = formatAdminActivityTitle(activity);
|
|
103
|
+
const detail = formatAdminActivityDetail(activity);
|
|
104
|
+
const statusColor = getAdminActivityTone(activity);
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The npm package publishes ESM component modules, declarations, and assets. The
|
|
108
|
+
local showcase HTML and its browser bundle are development artifacts and are
|
|
109
|
+
not part of the npm tarball.
|
|
110
|
+
|
|
81
111
|
## Development
|
|
82
112
|
|
|
83
113
|
```bash
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { DeesElement, html, property, customElement, css, type TemplateResult } from '@design.estate/dees-element';
|
|
2
2
|
import { idpElementStyles } from './tokens.js';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
idpAdminSharedStyles,
|
|
5
|
+
renderAdminKpi,
|
|
6
|
+
renderAdminStateCard,
|
|
7
|
+
formatAdminTimeAgo,
|
|
8
|
+
formatAdminActivityTitle,
|
|
9
|
+
formatAdminActivityDetail,
|
|
10
|
+
getAdminActivityTone,
|
|
11
|
+
type TIdpAdminActivity,
|
|
12
|
+
type TIdpAdminKpi,
|
|
13
|
+
} from './idp-admin-shared.js';
|
|
4
14
|
import './idp-icon.js';
|
|
5
15
|
import './idp-badge.js';
|
|
6
16
|
import '@design.estate/dees-catalog/dist_ts_web/elements/00group-chart/dees-chart-area/index.js';
|
|
@@ -9,7 +19,6 @@ import type {
|
|
|
9
19
|
IIdpAdminUser,
|
|
10
20
|
IIdpAdminOrg,
|
|
11
21
|
IIdpAdminSession,
|
|
12
|
-
IIdpAdminActivity,
|
|
13
22
|
IIdpAdminPassportDevice,
|
|
14
23
|
IIdpAdminOverviewData,
|
|
15
24
|
IIdpAdminNavigateEventDetail,
|
|
@@ -38,8 +47,8 @@ export class IdpAdminOverview extends DeesElement {
|
|
|
38
47
|
{ id: 's2', deviceName: 'iPhone 15 Pro', browser: 'Safari', os: 'iOS 18', ip: '84.132.10.4', lastActive: now - 3600000, createdAt: now - 86400000 * 10, isCurrent: false },
|
|
39
48
|
]}
|
|
40
49
|
.activities=${[
|
|
41
|
-
{
|
|
42
|
-
{
|
|
50
|
+
{ action: 'passkey_login', timestamp: now - 300000, outcome: 'succeeded', targetType: 'passkey', facts: null },
|
|
51
|
+
{ action: 'oidc_authorization_granted', timestamp: now - 7200000, outcome: 'succeeded', targetType: 'oidc_authorization', facts: { grantType: 'authorization_code' } },
|
|
43
52
|
]}
|
|
44
53
|
.overviewData=${{
|
|
45
54
|
chartLabel: 'Approval activity',
|
|
@@ -190,7 +199,7 @@ export class IdpAdminOverview extends DeesElement {
|
|
|
190
199
|
public accessor sessions: IIdpAdminSession[] = [];
|
|
191
200
|
|
|
192
201
|
@property({ type: Array })
|
|
193
|
-
public accessor activities:
|
|
202
|
+
public accessor activities: TIdpAdminActivity[] = [];
|
|
194
203
|
|
|
195
204
|
@property({ type: Array })
|
|
196
205
|
public accessor passportDevices: IIdpAdminPassportDevice[] = [];
|
|
@@ -371,7 +380,7 @@ export class IdpAdminOverview extends DeesElement {
|
|
|
371
380
|
<div class="card">
|
|
372
381
|
<div class="card-head"><span class="card-title">Recent activity</span><idp-badge>${this.activities.length} events</idp-badge></div>
|
|
373
382
|
${this.activities.length ? this.activities.slice(0, 8).map((activityArg) => html`
|
|
374
|
-
<div class="feed-item"><span class="feed-dot" style="--dot-color
|
|
383
|
+
<div class="feed-item"><span class="feed-dot" style="--dot-color:${getAdminActivityTone(activityArg)}"></span><div class="feed-text"><strong>${formatAdminActivityTitle(activityArg)}</strong> - ${formatAdminActivityDetail(activityArg)}</div><span class="feed-meta">${formatAdminTimeAgo(activityArg.timestamp)}</span></div>
|
|
375
384
|
`) : renderAdminStateCard('No activity yet', 'Activity events will appear here after logins, app changes, and organisation updates.', 'activity')}
|
|
376
385
|
</div>
|
|
377
386
|
<div class="card">
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { css, html, type TemplateResult } from '@design.estate/dees-element';
|
|
2
|
+
import type { dto } from '@idp.global/interfaces';
|
|
2
3
|
import './idp-icon.js';
|
|
3
4
|
|
|
5
|
+
export type TIdpAdminActivity = dto.TActivityEventDto;
|
|
6
|
+
|
|
4
7
|
export type TIdpAdminKpi = {
|
|
5
8
|
label: string;
|
|
6
9
|
value: string;
|
|
@@ -417,3 +420,31 @@ export const formatAdminTimeAgo = (timestampArg?: number): string => {
|
|
|
417
420
|
if (days < 7) return `${days}d ago`;
|
|
418
421
|
return new Date(timestampArg).toLocaleDateString();
|
|
419
422
|
};
|
|
423
|
+
|
|
424
|
+
export const formatAdminActivityTitle = (activityArg: TIdpAdminActivity): string =>
|
|
425
|
+
activityArg.action
|
|
426
|
+
.split('_')
|
|
427
|
+
.map((segmentArg) => `${segmentArg.slice(0, 1).toUpperCase()}${segmentArg.slice(1)}`)
|
|
428
|
+
.join(' ');
|
|
429
|
+
|
|
430
|
+
export const formatAdminActivityDetail = (activityArg: TIdpAdminActivity): string => {
|
|
431
|
+
const classifications = [
|
|
432
|
+
activityArg.outcome,
|
|
433
|
+
activityArg.targetType.replace(/_/g, ' '),
|
|
434
|
+
activityArg.reasonCode?.replace(/_/g, ' '),
|
|
435
|
+
];
|
|
436
|
+
return classifications.filter(Boolean).join(' · ');
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
export const getAdminActivityTone = (activityArg: TIdpAdminActivity): string => {
|
|
440
|
+
switch (activityArg.outcome) {
|
|
441
|
+
case 'succeeded':
|
|
442
|
+
return 'var(--idp-ok)';
|
|
443
|
+
case 'denied':
|
|
444
|
+
return 'var(--idp-warn)';
|
|
445
|
+
case 'failed':
|
|
446
|
+
return 'var(--idp-error)';
|
|
447
|
+
case 'attempted':
|
|
448
|
+
return 'var(--idp-info)';
|
|
449
|
+
}
|
|
450
|
+
};
|
|
@@ -10,7 +10,17 @@ import './idp-admin-sidebar.js';
|
|
|
10
10
|
import './idp-admin-overview.js';
|
|
11
11
|
import './idp-sso-connection-form.js';
|
|
12
12
|
import './idp-scim-token-panel.js';
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
idpAdminSharedStyles,
|
|
15
|
+
renderAdminKpi,
|
|
16
|
+
renderAdminStateCard,
|
|
17
|
+
formatAdminTimeAgo,
|
|
18
|
+
formatAdminActivityTitle,
|
|
19
|
+
formatAdminActivityDetail,
|
|
20
|
+
getAdminActivityTone,
|
|
21
|
+
type TIdpAdminActivity,
|
|
22
|
+
type TIdpAdminKpi,
|
|
23
|
+
} from './idp-admin-shared.js';
|
|
14
24
|
import type { IIdpDelegation, IIdpDelegationApp } from './idp-delegation-section.js';
|
|
15
25
|
import type { IIdpSsoConnectionItem, IIdpSsoSpDetails } from './idp-sso-connection-form.js';
|
|
16
26
|
import type { IIdpScimTokenItem } from './idp-scim-token-panel.js';
|
|
@@ -130,7 +140,7 @@ export interface IIdpAdminGaAppConfigureEventDetail {
|
|
|
130
140
|
redirectUris: string[];
|
|
131
141
|
postLogoutRedirectUris: string[];
|
|
132
142
|
allowedScopes: string[];
|
|
133
|
-
grantTypes: Array<'authorization_code' | '
|
|
143
|
+
grantTypes: Array<'authorization_code' | 'refresh_token'>;
|
|
134
144
|
oidcSettings: {
|
|
135
145
|
accessTokenLifetime: number;
|
|
136
146
|
idTokenLifetime: number;
|
|
@@ -199,15 +209,6 @@ export interface IIdpAdminSession {
|
|
|
199
209
|
isCurrent: boolean;
|
|
200
210
|
}
|
|
201
211
|
|
|
202
|
-
export interface IIdpAdminActivity {
|
|
203
|
-
id: string;
|
|
204
|
-
action: string;
|
|
205
|
-
description: string;
|
|
206
|
-
timestamp: number;
|
|
207
|
-
ip?: string;
|
|
208
|
-
targetType?: string;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
212
|
export interface IIdpAdminApp {
|
|
212
213
|
id: string;
|
|
213
214
|
name: string;
|
|
@@ -1007,7 +1008,7 @@ export class IdpAdminShell extends DeesElement {
|
|
|
1007
1008
|
public accessor sessions: IIdpAdminSession[] = [];
|
|
1008
1009
|
|
|
1009
1010
|
@property({ type: Array })
|
|
1010
|
-
public accessor activities:
|
|
1011
|
+
public accessor activities: TIdpAdminActivity[] = [];
|
|
1011
1012
|
|
|
1012
1013
|
@property({ type: Array })
|
|
1013
1014
|
public accessor orgMembers: IIdpAdminMember[] = [];
|
|
@@ -1734,10 +1735,10 @@ export class IdpAdminShell extends DeesElement {
|
|
|
1734
1735
|
const postLogoutRedirectUris = this.parseLines(this.dialogPostLogoutRedirectUris);
|
|
1735
1736
|
const allowedScopes = this.parseCsv(this.dialogAllowedScopes);
|
|
1736
1737
|
const configuredGrantTypes = this.parseCsv(this.dialogGrantTypes);
|
|
1737
|
-
const supportedGrantTypes = ['authorization_code', '
|
|
1738
|
+
const supportedGrantTypes = ['authorization_code', 'refresh_token'];
|
|
1738
1739
|
const invalidGrantTypes = configuredGrantTypes.filter((grantArg) => !supportedGrantTypes.includes(grantArg));
|
|
1739
1740
|
const grantTypes = configuredGrantTypes
|
|
1740
|
-
.filter((grantArg): grantArg is 'authorization_code' | '
|
|
1741
|
+
.filter((grantArg): grantArg is 'authorization_code' | 'refresh_token' => supportedGrantTypes.includes(grantArg));
|
|
1741
1742
|
const invalidUris = [...redirectUris, ...postLogoutRedirectUris].filter((uriArg) => !this.isValidExactUri(uriArg));
|
|
1742
1743
|
const lifetimesAreValid = [this.dialogAccessTokenLifetime, this.dialogIdTokenLifetime]
|
|
1743
1744
|
.every((lifetimeArg) => Number.isInteger(lifetimeArg) && lifetimeArg >= 300 && lifetimeArg <= 86400)
|
|
@@ -1764,10 +1765,6 @@ export class IdpAdminShell extends DeesElement {
|
|
|
1764
1765
|
this.dialogError = 'The refresh_token grant requires the authorization_code grant.';
|
|
1765
1766
|
return;
|
|
1766
1767
|
}
|
|
1767
|
-
if (this.dialogClientType === 'public' && grantTypes.includes('client_credentials')) {
|
|
1768
|
-
this.dialogError = 'Public clients cannot use the client_credentials grant.';
|
|
1769
|
-
return;
|
|
1770
|
-
}
|
|
1771
1768
|
if (!lifetimesAreValid) {
|
|
1772
1769
|
this.dialogError = 'Access and ID token lifetimes must be 300 to 86400 seconds; refresh family lifetime must be 3600 to 7776000 seconds.';
|
|
1773
1770
|
return;
|
|
@@ -2034,7 +2031,7 @@ export class IdpAdminShell extends DeesElement {
|
|
|
2034
2031
|
${this.renderFormRow('Callback URIs', 'One exact absolute URI per line; required for authorization_code.', html`<textarea class="textarea" .value=${this.dialogRedirectUris} @input=${(eventArg: Event) => { this.dialogRedirectUris = (eventArg.target as HTMLTextAreaElement).value; }}></textarea>`)}
|
|
2035
2032
|
${this.renderFormRow('Post-logout URIs', 'One exact RP-Initiated Logout destination per line.', html`<textarea class="textarea" .value=${this.dialogPostLogoutRedirectUris} @input=${(eventArg: Event) => { this.dialogPostLogoutRedirectUris = (eventArg.target as HTMLTextAreaElement).value; }}></textarea>`)}
|
|
2036
2033
|
${this.renderFormRow('Allowed scopes', 'Comma-separated scopes; authorization_code requires openid.', html`<input class="input" .value=${this.dialogAllowedScopes} @input=${(eventArg: Event) => { this.dialogAllowedScopes = (eventArg.target as HTMLInputElement).value; }} />`, true)}
|
|
2037
|
-
${this.renderFormRow('Grant types', 'authorization_code
|
|
2034
|
+
${this.renderFormRow('Grant types', 'authorization_code and refresh_token are supported.', html`<input class="input" .value=${this.dialogGrantTypes} @input=${(eventArg: Event) => { this.dialogGrantTypes = (eventArg.target as HTMLInputElement).value; }} />`, true)}
|
|
2038
2035
|
<div class="mapping-row">
|
|
2039
2036
|
<div>${this.renderFormRow('Access token seconds', '300 to 86400.', html`<input class="input" type="number" .value=${String(this.dialogAccessTokenLifetime)} @input=${(eventArg: Event) => this.setDialogNumber('access', eventArg)} />`, true)}</div>
|
|
2040
2037
|
<div>${this.renderFormRow('ID token seconds', '300 to 86400.', html`<input class="input" type="number" .value=${String(this.dialogIdTokenLifetime)} @input=${(eventArg: Event) => this.setDialogNumber('id', eventArg)} />`, true)}</div>
|
|
@@ -2250,10 +2247,10 @@ export class IdpAdminShell extends DeesElement {
|
|
|
2250
2247
|
${this.deleteAccountOpen
|
|
2251
2248
|
? html`
|
|
2252
2249
|
${this.renderFormRow('Confirm your email', 'Type your account email to confirm', html`<input class="input" .value=${this.deleteAccountConfirmEmail} ?disabled=${this.accountDeleting} @input=${(eventArg: Event) => { this.deleteAccountConfirmEmail = (eventArg.target as HTMLInputElement).value; }} />`, true)}
|
|
2253
|
-
${this.renderFormRow('Password', 'Required
|
|
2250
|
+
${this.renderFormRow('Password', 'Required. Passwordless account deletion needs a future fresh-auth step-up.', html`<input class="input" type="password" autocomplete="current-password" .value=${this.deleteAccountPassword} ?disabled=${this.accountDeleting} @input=${(eventArg: Event) => { this.deleteAccountPassword = (eventArg.target as HTMLInputElement).value; }} />`)}
|
|
2254
2251
|
<div class="split-row" style="justify-content:flex-end;gap:10px;margin-top:14px">
|
|
2255
2252
|
<idp-button variant="outline" shape="rect" ?disabled=${this.accountDeleting} @click=${() => { this.deleteAccountOpen = false; this.deleteAccountPassword = ''; this.deleteAccountConfirmEmail = ''; }}>Cancel</idp-button>
|
|
2256
|
-
<idp-button variant="accent" shape="rect" ?loading=${this.accountDeleting} ?disabled=${this.deleteAccountConfirmEmail.trim().toLowerCase() !== (this.user?.email || '').trim().toLowerCase()} @click=${() => this.dispatchShellEvent<IIdpAdminAccountDeleteEventDetail>('idp-admin-account-delete', { password: this.deleteAccountPassword, confirmEmail: this.deleteAccountConfirmEmail.trim() })}>Delete my account</idp-button>
|
|
2253
|
+
<idp-button variant="accent" shape="rect" ?loading=${this.accountDeleting} ?disabled=${!this.deleteAccountPassword || this.deleteAccountConfirmEmail.trim().toLowerCase() !== (this.user?.email || '').trim().toLowerCase()} @click=${() => this.dispatchShellEvent<IIdpAdminAccountDeleteEventDetail>('idp-admin-account-delete', { password: this.deleteAccountPassword, confirmEmail: this.deleteAccountConfirmEmail.trim() })}>Delete my account</idp-button>
|
|
2257
2254
|
</div>
|
|
2258
2255
|
`
|
|
2259
2256
|
: html`<div class="split-row"><div><div class="section-title">Delete this account</div><div class="muted">Organisations you own must be transferred or deleted first.</div></div><idp-button variant="outline" shape="rect" @click=${() => { this.deleteAccountOpen = true; }}>Delete account…</idp-button></div>`}
|
|
@@ -2426,14 +2423,10 @@ export class IdpAdminShell extends DeesElement {
|
|
|
2426
2423
|
const org = this.currentOrg;
|
|
2427
2424
|
const connectedAppCount = this.orgApps.filter((appArg) => appArg.isConnected).length;
|
|
2428
2425
|
const orgActivities = this.activities
|
|
2429
|
-
.filter((activityArg) =>
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|| searchableText.includes('org')
|
|
2434
|
-
|| searchableText.includes('organization')
|
|
2435
|
-
|| searchableText.includes('organisation');
|
|
2436
|
-
})
|
|
2426
|
+
.filter((activityArg) =>
|
|
2427
|
+
activityArg.targetType === 'organization'
|
|
2428
|
+
|| activityArg.targetType.startsWith('organization_')
|
|
2429
|
+
)
|
|
2437
2430
|
.slice(0, 5);
|
|
2438
2431
|
const kpis: TIdpAdminKpi[] = [
|
|
2439
2432
|
{
|
|
@@ -2500,10 +2493,10 @@ export class IdpAdminShell extends DeesElement {
|
|
|
2500
2493
|
</div>
|
|
2501
2494
|
</div>
|
|
2502
2495
|
<div class="card">
|
|
2503
|
-
<div class="card-head"><span class="card-title">
|
|
2496
|
+
<div class="card-head"><span class="card-title">Organisation-classified account activity</span><idp-badge>${orgActivities.length} events</idp-badge></div>
|
|
2504
2497
|
${orgActivities.length ? orgActivities.map((activityArg) => html`
|
|
2505
|
-
<div class="feed-item"><span class="feed-dot" style="--dot-color
|
|
2506
|
-
`) : this.renderStateCard('No
|
|
2498
|
+
<div class="feed-item"><span class="feed-dot" style="--dot-color:${getAdminActivityTone(activityArg)}"></span><div class="feed-text"><strong>${formatAdminActivityTitle(activityArg)}</strong> - ${formatAdminActivityDetail(activityArg)}</div><span class="feed-meta">${this.formatTimeAgo(activityArg.timestamp)}</span></div>
|
|
2499
|
+
`) : this.renderStateCard('No organisation activity yet', 'Account events with an organisation target classification will appear here without exposing target identities.', 'activity')}
|
|
2507
2500
|
</div>
|
|
2508
2501
|
</div>
|
|
2509
2502
|
</div>
|