@idp.global/catalog 4.0.0 → 5.1.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/readme.md CHANGED
@@ -78,6 +78,38 @@ 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
+ The current union includes global-administrator grant and removal lifecycle
86
+ events emitted by the platform administration API.
87
+
88
+ ```ts
89
+ import {
90
+ formatAdminActivityDetail,
91
+ formatAdminActivityTitle,
92
+ getAdminActivityTone,
93
+ type TIdpAdminActivity,
94
+ } from '@idp.global/catalog';
95
+
96
+ const activity: TIdpAdminActivity = {
97
+ action: 'passkey_login',
98
+ timestamp: Date.now(),
99
+ outcome: 'succeeded',
100
+ targetType: 'passkey',
101
+ facts: null,
102
+ };
103
+
104
+ const title = formatAdminActivityTitle(activity);
105
+ const detail = formatAdminActivityDetail(activity);
106
+ const statusColor = getAdminActivityTone(activity);
107
+ ```
108
+
109
+ The npm package publishes ESM component modules, declarations, and assets. The
110
+ local showcase HTML and its browser bundle are development artifacts and are
111
+ not part of the npm tarball.
112
+
81
113
  ## Development
82
114
 
83
115
  ```bash
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@idp.global/catalog',
6
- version: '4.0.0',
6
+ version: '5.1.0',
7
7
  description: 'Web component catalog for idp.global, based on the v5 Apple-system design language.'
8
8
  }
@@ -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 { idpAdminSharedStyles, renderAdminKpi, renderAdminStateCard, formatAdminTimeAgo, type TIdpAdminKpi } from './idp-admin-shared.js';
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
- { id: 'a1', action: 'login_success', description: 'Signed in with passkey', timestamp: now - 300000 },
42
- { id: 'a2', action: 'app_authorized', description: 'Granted access to foss.global', timestamp: now - 7200000 },
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: IIdpAdminActivity[] = [];
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:var(--idp-info)"></span><div class="feed-text"><strong>${activityArg.action.replace(/_/g, ' ')}</strong> - ${activityArg.description}</div><span class="feed-meta">${formatAdminTimeAgo(activityArg.timestamp)}</span></div>
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 { idpAdminSharedStyles, renderAdminKpi, renderAdminStateCard, formatAdminTimeAgo, type TIdpAdminKpi } from './idp-admin-shared.js';
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';
@@ -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: IIdpAdminActivity[] = [];
1011
+ public accessor activities: TIdpAdminActivity[] = [];
1011
1012
 
1012
1013
  @property({ type: Array })
1013
1014
  public accessor orgMembers: IIdpAdminMember[] = [];
@@ -2422,14 +2423,10 @@ export class IdpAdminShell extends DeesElement {
2422
2423
  const org = this.currentOrg;
2423
2424
  const connectedAppCount = this.orgApps.filter((appArg) => appArg.isConnected).length;
2424
2425
  const orgActivities = this.activities
2425
- .filter((activityArg) => {
2426
- const searchableText = `${activityArg.targetType || ''} ${activityArg.description || ''}`.toLowerCase();
2427
- return Boolean(org.slug && searchableText.includes(org.slug.toLowerCase()))
2428
- || Boolean(org.name && searchableText.includes(org.name.toLowerCase()))
2429
- || searchableText.includes('org')
2430
- || searchableText.includes('organization')
2431
- || searchableText.includes('organisation');
2432
- })
2426
+ .filter((activityArg) =>
2427
+ activityArg.targetType === 'organization'
2428
+ || activityArg.targetType.startsWith('organization_')
2429
+ )
2433
2430
  .slice(0, 5);
2434
2431
  const kpis: TIdpAdminKpi[] = [
2435
2432
  {
@@ -2496,10 +2493,10 @@ export class IdpAdminShell extends DeesElement {
2496
2493
  </div>
2497
2494
  </div>
2498
2495
  <div class="card">
2499
- <div class="card-head"><span class="card-title">Recent organisation activity</span><idp-badge>${orgActivities.length} events</idp-badge></div>
2496
+ <div class="card-head"><span class="card-title">Organisation-classified account activity</span><idp-badge>${orgActivities.length} events</idp-badge></div>
2500
2497
  ${orgActivities.length ? orgActivities.map((activityArg) => html`
2501
- <div class="feed-item"><span class="feed-dot" style="--dot-color:var(--idp-info)"></span><div class="feed-text"><strong>${activityArg.action.replace(/_/g, ' ')}</strong> - ${activityArg.description}</div><span class="feed-meta">${this.formatTimeAgo(activityArg.timestamp)}</span></div>
2502
- `) : this.renderStateCard('No org activity yet', 'Organisation-related activity will appear here when the backend reports matching activity events.', 'activity')}
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')}
2503
2500
  </div>
2504
2501
  </div>
2505
2502
  </div>