Skip to content

Commit 66f6313

Browse files
authored
Merge branch 'main' into aromanovv/PD-4790-improve-auth-challenge
2 parents 1b17bb8 + c85bfe4 commit 66f6313

10 files changed

Lines changed: 96 additions & 17 deletions

File tree

AGENTS.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,17 @@ If a library component (e.g. in orcid-registry-ui) looks correct in the docs app
2222
## Dialogs and async data
2323

2424
When a dialog or view needs data from an HTTP (or other async) call, opening the dialog only after the request completes can feel slow. Consider opening the dialog **immediately** with skeleton or static data (e.g. labels, empty lists, or a `loading: true` flag), then assigning the full payload to the dialog’s component instance when the observable emits. The dialog can show placeholders or a loading state until then.
25+
26+
## RUM / Observability
27+
28+
The app sends events to **New Relic** (and optionally logs them in the console) for analytics and debugging. The central service is **`RumJourneyEventService`** (`src/app/rum/service/customEvent.service.ts`).
29+
30+
### Two ways to record events
31+
32+
1. **`recordSimpleEvent(eventName, attrs?)`** — Standalone one-off events. No journey lifecycle. Use for: record page actions (expand featured work, summary toggle), errors (`http_error`, `client_error`), or any action that isn't part of a multi-step journey.
33+
2. **`recordEvent(journeyType, eventName, eventAttrs?)`** — Events inside a **journey**. Requires `startJourney(journeyType, context)` first; events are tied to that journey (elapsed time, journey id). Use for: registration flow, OAuth, notifications. If no journey is active, the event is ignored (and logged in console when debug is on).
34+
35+
### Event names: use the enum
36+
37+
- **All event name strings** live in **`AppEventName`** in `src/app/register/app-event-names.ts`. Do not hardcode event names elsewhere.
38+
- For **dynamic** names (e.g. `step-${step}-loaded`), add a small helper in the same file (e.g. `stepLoadedEvent(step)`) and use it when calling `recordEvent`.

projects/orcid-registry-ui/src/lib/components/import-works-dialog/import-works-dialog.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@
9898
</ng-container>
9999
</div>
100100
<div class="import-works-dialog__card-content">
101-
<p class="import-works-dialog__card-title">{{ link.name }}</p>
101+
<h3 class="import-works-dialog__card-title">{{ link.name }}</h3>
102102
<p class="import-works-dialog__card-description">
103103
{{ link.description }}
104104
</p>
@@ -247,7 +247,7 @@
247247
>
248248
</div>
249249
<div class="import-works-dialog__card-content">
250-
<p class="import-works-dialog__card-title">{{ link.name }}</p>
250+
<h3 class="import-works-dialog__card-title import-works-dialog__card-title--more">{{ link.name }}</h3>
251251
<p class="import-works-dialog__card-description">
252252
{{ link.description }}
253253
</p>

projects/orcid-registry-ui/src/lib/components/import-works-dialog/import-works-dialog.component.scss

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,18 @@
143143
font-weight: var(--orcid-font-weight-bold, 700);
144144
line-height: 27px;
145145
color: var(--orcid-color-text-dark-high, #000000);
146+
147+
// Certified: 4px vertical space between CSP title and description (PD-5141)
148+
.import-works-dialog__card--certified & {
149+
margin-bottom: 4px;
150+
}
151+
}
152+
153+
// More Services: item titles 16px/24px; 2px spacing maintained (PD-5141)
154+
.import-works-dialog__card-title--more {
155+
margin: 0 0 2px;
156+
font-size: 16px;
157+
line-height: 24px;
146158
}
147159

148160
.import-works-dialog__card-description {

src/app/cdk/panel/panel/panel.component.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ import { WINDOW } from 'src/app/cdk/window'
3333
import { TogglzService } from '../../../core/togglz/togglz.service'
3434
import { getAriaLabel } from '../../../constants'
3535
import { RecordBiographyService } from 'src/app/core/record-biography/record-biography.service'
36+
import { RumJourneyEventService } from 'src/app/rum/service/customEvent.service'
37+
import { AppEventName } from 'src/app/register/app-event-names'
3638
import { ExpandedWorkFeaturedModalComponent } from 'src/app/record/components/work-featured/modals/expanded-work-featured-modal/expanded-work-featured-modal.component'
3739

3840
@Component({
@@ -153,6 +155,7 @@ export class PanelComponent implements OnInit {
153155
private _worksService: RecordWorksService,
154156
private _verificationEmailModalService: VerificationEmailModalService,
155157
private _recordBiographyService: RecordBiographyService,
158+
private _rumEvents: RumJourneyEventService,
156159
private _togglzService: TogglzService
157160
) {}
158161

@@ -264,6 +267,7 @@ export class PanelComponent implements OnInit {
264267
}
265268

266269
expandItem() {
270+
this._rumEvents.recordSimpleEvent(AppEventName.RecordExpandFeaturedWorkClicked)
267271
const modalComponent = this._dialog.open(
268272
ExpandedWorkFeaturedModalComponent,
269273
{

src/app/core/error-handler/error-handler.service.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { ErrorReport } from 'src/app/types'
88
import { ERROR_REPORT } from 'src/app/errors'
99
import { CookieService } from 'ngx-cookie-service'
1010
import { RumJourneyEventService } from 'src/app/rum/service/customEvent.service'
11+
import { AppEventName } from 'src/app/register/app-event-names'
1112

1213
@Injectable({
1314
providedIn: 'root',
@@ -52,7 +53,7 @@ export class ErrorHandlerService {
5253
// Server error
5354
if (processedError instanceof HttpErrorResponse) {
5455
try {
55-
this._observability.recordSimpleEvent('http_error', {
56+
this._observability.recordSimpleEvent(AppEventName.HttpError, {
5657
status: processedError.status,
5758
statusText: processedError.statusText,
5859
url: processedError.url,
@@ -73,7 +74,7 @@ ok: "${processedError.ok}"
7374
} else {
7475
// Client side error
7576
try {
76-
this._observability.recordSimpleEvent('client_error', {
77+
this._observability.recordSimpleEvent(AppEventName.ClientError, {
7778
name: processedError.name,
7879
message: processedError.message,
7980
browserSupport: this.browserSupport,

src/app/core/lang-preload/firefox-xsrf-preload.interceptor.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { TestBed, fakeAsync, flushMicrotasks } from '@angular/core/testing'
77
import { CookieService } from 'ngx-cookie-service'
88
import { FirefoxXsrfPreloadInterceptor } from './firefox-xsrf-preload.interceptor'
99
import { RumJourneyEventService } from 'src/app/rum/service/customEvent.service'
10+
import { AppEventName } from 'src/app/register/app-event-names'
1011
import { WINDOW } from 'src/app/cdk/window'
1112
import { Platform } from '@angular/cdk/platform'
1213

@@ -110,7 +111,7 @@ describe('FirefoxXsrfPreloadInterceptor', () => {
110111

111112
expect(fetchSpy).toHaveBeenCalled()
112113
expect(recordSimpleEventSpy).toHaveBeenCalledWith(
113-
'xsrf_missing_after_preload',
114+
AppEventName.XsrfMissingAfterPreload,
114115
jasmine.objectContaining({
115116
error: 'xsrf_missing',
116117
})

src/app/core/lang-preload/firefox-xsrf-preload.interceptor.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Observable, Subject } from 'rxjs'
99
import { switchMap, take } from 'rxjs/operators'
1010
import { CookieService } from 'ngx-cookie-service'
1111
import { RumJourneyEventService } from 'src/app/rum/service/customEvent.service'
12+
import { AppEventName } from 'src/app/register/app-event-names'
1213
import { WINDOW } from 'src/app/cdk/window'
1314
import { Platform } from '@angular/cdk/platform'
1415

@@ -83,7 +84,7 @@ export class FirefoxXsrfPreloadInterceptor implements HttpInterceptor {
8384
if (!this.hasXsrfCookie()) {
8485
try {
8586
this._observability.recordSimpleEvent(
86-
'xsrf_missing_after_preload',
87+
AppEventName.XsrfMissingAfterPreload,
8788
{
8889
error: 'xsrf_missing',
8990
errorDescription:

src/app/record/components/record-header/record-header.component.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import {
3232
AffiliationUIGroup,
3333
Affiliation,
3434
} from 'src/app/types/record-affiliation.endpoint'
35+
import { RumJourneyEventService } from 'src/app/rum/service/customEvent.service'
36+
import { AppEventName } from 'src/app/register/app-event-names'
3537

3638
@Component({
3739
selector: 'app-record-header',
@@ -151,7 +153,8 @@ export class RecordHeaderComponent implements OnInit {
151153
private _compact: HeaderCompactService,
152154
private _togglz: TogglzService,
153155
private _router: Router,
154-
private _affiliations: RecordAffiliationService
156+
private _affiliations: RecordAffiliationService,
157+
private _rumEvents: RumJourneyEventService
155158
) {}
156159

157160
ngOnInit(): void {
@@ -251,6 +254,9 @@ export class RecordHeaderComponent implements OnInit {
251254
}
252255
recordSummaryLinkClick() {
253256
const next = !this.recordSummaryOpen
257+
this._rumEvents.recordSimpleEvent(AppEventName.RecordSummaryToggleClicked, {
258+
expanded: next,
259+
})
254260
this._state.setRecordSummaryOpen(next)
255261
this.recordSummaryOpenChange.emit(next)
256262
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* Central enum for all app observability event names (RUM / New Relic).
3+
* Use with RumJourneyEventService.recordSimpleEvent() or .recordEvent().
4+
*/
5+
export enum AppEventName {
6+
// ─── Simple events (recordSimpleEvent) ─────────────────────────────────────
7+
RecordExpandFeaturedWorkClicked = 'record_expand_featured_work_clicked',
8+
RecordSummaryToggleClicked = 'record_summary_toggle_clicked',
9+
HttpError = 'http_error',
10+
ClientError = 'client_error',
11+
XsrfMissingAfterPreload = 'xsrf_missing_after_preload',
12+
13+
// ─── Orcid registration journey events ─────────────────────────────────────
14+
StepASignInButtonClicked = 'step-a-sign-in-button-clicked',
15+
StepANextButtonClicked = 'step-a-next-button-clicked',
16+
StepBNextButtonClicked = 'step-b-next-button-clicked',
17+
StepC2NextButtonClicked = 'step-c2-next-button-clicked',
18+
StepC2SkipButtonClicked = 'step-c2-skip-button-clicked',
19+
StepCNextButtonClicked = 'step-c-next-button-clicked',
20+
StepDNextButtonClicked = 'step-d-next-button-clicked',
21+
JourneyComplete = 'journey-complete',
22+
}
23+
24+
/** Event name for step back button: `step-${step}-back-button-clicked` */
25+
export function stepBackButtonClickedEvent(step: string): string {
26+
return `step-${step}-back-button-clicked`
27+
}
28+
29+
/** Event name for step loaded: `step-${step}-loaded` */
30+
export function stepLoadedEvent(step: 'a' | 'b' | 'c2' | 'c' | 'd'): string {
31+
return `step-${step}-loaded`
32+
}

src/app/register/register-observability.service.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ import { RumJourneyEventService } from '../rum/service/customEvent.service'
77
import { JourneyType } from '../rum/journeys/types'
88
import { UntypedFormArray, UntypedFormGroup } from '@angular/forms'
99
import { RegisterStateService } from './register-state.service'
10+
import {
11+
AppEventName,
12+
stepBackButtonClickedEvent,
13+
stepLoadedEvent,
14+
} from './app-event-names'
1015
import init from 'helphero'
1116
@Injectable({
1217
providedIn: 'root',
@@ -24,22 +29,22 @@ export class RegisterObservabilityService {
2429
this._registrationStateService.getBackButtonClick().subscribe((step) => {
2530
this._observability.recordEvent(
2631
'orcid_registration',
27-
`step-${step.step}-back-button-clicked`
32+
stepBackButtonClickedEvent(step.step)
2833
)
2934
})
3035
}
3136

3237
signInButtonClicked() {
3338
this._observability.recordEvent(
3439
'orcid_registration',
35-
'step-a-sign-in-button-clicked'
40+
AppEventName.StepASignInButtonClicked
3641
)
3742
}
3843

3944
stepANextButtonClicked(form: UntypedFormGroup) {
4045
this._observability.recordEvent(
4146
'orcid_registration',
42-
'step-a-next-button-clicked',
47+
AppEventName.StepANextButtonClicked,
4348
{
4449
// List all form controls errors
4550
formValues: form.value,
@@ -61,7 +66,7 @@ export class RegisterObservabilityService {
6166
stepBNextButtonClicked(form: UntypedFormGroup) {
6267
this._observability.recordEvent(
6368
'orcid_registration',
64-
'step-b-next-button-clicked',
69+
AppEventName.StepBNextButtonClicked,
6570
{
6671
// List all form controls errors
6772
formValues: form.value,
@@ -77,7 +82,7 @@ export class RegisterObservabilityService {
7782
stepC2NextButtonClicked(form: UntypedFormGroup) {
7883
this._observability.recordEvent(
7984
'orcid_registration',
80-
'step-c2-next-button-clicked',
85+
AppEventName.StepC2NextButtonClicked,
8186
{
8287
// List all form controls errors
8388
formValues: form.value,
@@ -95,7 +100,7 @@ export class RegisterObservabilityService {
95100
stepC2SkipButtonClicked(form: UntypedFormGroup) {
96101
this._observability.recordEvent(
97102
'orcid_registration',
98-
'step-c2-skip-button-clicked',
103+
AppEventName.StepC2SkipButtonClicked,
99104
{
100105
// List all form controls errors
101106
formValues: form.value,
@@ -113,7 +118,7 @@ export class RegisterObservabilityService {
113118
stepCNextButtonClicked(form: UntypedFormGroup) {
114119
this._observability.recordEvent(
115120
'orcid_registration',
116-
'step-c-next-button-clicked',
121+
AppEventName.StepCNextButtonClicked,
117122
{
118123
// List all form controls errors
119124
formValues: form.value,
@@ -128,7 +133,7 @@ export class RegisterObservabilityService {
128133
stepDNextButtonClicked(form: UntypedFormGroup) {
129134
this._observability.recordEvent(
130135
'orcid_registration',
131-
'step-d-next-button-clicked',
136+
AppEventName.StepDNextButtonClicked,
132137
{
133138
// List all form controls errors
134139
formValues: form.value,
@@ -143,7 +148,10 @@ export class RegisterObservabilityService {
143148
}
144149

145150
stepLoaded(step: 'a' | 'b' | 'c2' | 'c' | 'd') {
146-
this._observability.recordEvent('orcid_registration', `step-${step}-loaded`)
151+
this._observability.recordEvent(
152+
'orcid_registration',
153+
stepLoadedEvent(step)
154+
)
147155
}
148156

149157
initializeJourney(reactivation) {
@@ -158,7 +166,7 @@ export class RegisterObservabilityService {
158166
completeJourney(attributes: any = {}) {
159167
this._observability.recordEvent(
160168
'orcid_registration',
161-
'journey-complete',
169+
AppEventName.JourneyComplete,
162170
attributes
163171
)
164172
this._observability.finishJourney('orcid_registration')

0 commit comments

Comments
 (0)