Skip to content

Commit fe21046

Browse files
committed
Merge remote-tracking branch 'origin/golive/2.4.y.z' into 2.4.y.z/CORE-6673/presistent-unlock-indicator-prerelease
2 parents 45c47c9 + b72c005 commit fe21046

File tree

11 files changed

+229
-39
lines changed

11 files changed

+229
-39
lines changed

.github/workflows/p2-aus-appv3.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ jobs:
177177
export CUSTOM_BADGE_PROJECT_URL=https://badge.aus.practera.com
178178
export CUSTOM_UPLOAD_TUS_ENDPOINT=https://tusd.practera.com/uploads/
179179
export CUSTOM_UPLOAD_MAX_FILE_SIZE=2147483648
180-
export CUSTOM_HELPLINE=help@practera.com
180+
export CUSTOM_HELPLINE=programs@practera.com
181181
export CUSTOM_STACK_NAME=${{ env.STACK_NAME }}
182182
183183
printf "Angular environment variable creation complete\n\n"

.github/workflows/p2-euk-appv3.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ jobs:
177177
export CUSTOM_BADGE_PROJECT_URL=https://badge.euk.practera.com
178178
export CUSTOM_UPLOAD_TUS_ENDPOINT=https://tusd.practera.com/uploads/
179179
export CUSTOM_UPLOAD_MAX_FILE_SIZE=2147483648
180-
export CUSTOM_HELPLINE=help@practera.com
180+
export CUSTOM_HELPLINE=programs@practera.com
181181
export CUSTOM_STACK_NAME=${{ env.STACK_NAME }}
182182
183183
printf "Angular environment variable creation complete\n\n"

.github/workflows/p2-prerelease-appv3.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ jobs:
180180
export CUSTOM_BADGE_PROJECT_URL=https://badge.p2-prerelease.practera.com
181181
export CUSTOM_UPLOAD_TUS_ENDPOINT=https://tusd.practera.com/uploads/
182182
export CUSTOM_UPLOAD_MAX_FILE_SIZE=2147483648
183-
export CUSTOM_HELPLINE=help@practera.com
183+
export CUSTOM_HELPLINE=programs@practera.com
184184
export CUSTOM_STACK_NAME=${{ env.STACK_NAME }}
185185
186186
printf "Angular environment variable creation complete\n\n"

.github/workflows/p2-stage-appv3.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ jobs:
180180
export CUSTOM_INTERCOM=$(aws secretsmanager get-secret-value --secret-id $STACK_NAME-IntercomSecret-$ENV| jq --raw-output '.SecretString' | jq -r .app_id)
181181
export CUSTOM_BADGE_PROJECT_URL=https://badge.p2-stage.practera.com
182182
export CUSTOM_UPLOAD_TUS_ENDPOINT=https://tusd.practera.com/uploads/
183-
export CUSTOM_HELPLINE=help@practera.com
183+
export CUSTOM_HELPLINE=programs@practera.com
184184
export CUSTOM_STACK_NAME=${{ env.STACK_NAME }}
185185
186186
printf "Angular environment variable creation complete\n\n"

.github/workflows/p2-usa-appv3.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ jobs:
177177
export CUSTOM_BADGE_PROJECT_URL=https://badge.usa.practera.com
178178
export CUSTOM_UPLOAD_TUS_ENDPOINT=https://tusd.practera.com/uploads/
179179
export CUSTOM_UPLOAD_MAX_FILE_SIZE=2147483648
180-
export CUSTOM_HELPLINE=help@practera.com
180+
export CUSTOM_HELPLINE=programs@practera.com
181181
export CUSTOM_STACK_NAME=${{ env.STACK_NAME }}
182182
183183
printf "Angular environment variable creation complete\n\n"

docs/contact support UI flow.md

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
### Document: Contact Support Modal or Fallback to Email Client Logic Flow
2+
3+
---
4+
5+
#### **Overview**
6+
This document outlines the logic flow for determining whether the Practera app displays a **HubSpot support form modal** or falls back to the **email client** for user support. The decision is based on the email address configured in the admin settings.
7+
8+
---
9+
10+
### **Logic Flow**
11+
12+
#### **1. Input: Support Email Address**
13+
The support email address is retrieved from the current experience's configuration in the app's storage.
14+
15+
#### **2. Conditions**
16+
The app evaluates the email address to determine the appropriate support path:
17+
18+
1. **Practera Support Email**:
19+
- If the email address contains `@practera.com`, the app:
20+
- Activates the **HubSpot support form modal**.
21+
- Users can submit their queries directly within the app.
22+
- Example: `support@practera.com`.
23+
24+
2. **Custom Support Email**:
25+
- If the email address does **not** contain `@practera.com`, the app:
26+
- Falls back to the **email client**.
27+
- Opens the user's default email client with a pre-filled subject line containing the current program name.
28+
- Example: `support@customdomain.com`.
29+
30+
3. **Default Fallback**:
31+
- If no email address is configured, the app uses the default Practera helpline email (`programs@practera.com`) and opens the email client.
32+
33+
---
34+
35+
### **Logic Implementation**
36+
37+
#### **Key Functions**
38+
1. **`checkIsPracteraSupportEmail()`**:
39+
- Checks if the configured email address contains `@practera.com`.
40+
- Broadcasts an event (`support-email-checked`) with `true` or `false` to notify other parts of the app.
41+
42+
```typescript
43+
checkIsPracteraSupportEmail() {
44+
const currentExperience = this.storageService.get('experience');
45+
if (currentExperience && currentExperience.supportEmail) {
46+
const supportEmail = currentExperience.supportEmail;
47+
if (supportEmail.includes("@practera.com")) {
48+
this.broadcastEvent('support-email-checked', true);
49+
return true;
50+
}
51+
this.broadcastEvent('support-email-checked', false);
52+
return false;
53+
}
54+
this.broadcastEvent('support-email-checked', false);
55+
return false;
56+
}
57+
```
58+
59+
2. **`openSupportPopup(event)`**:
60+
- Determines whether to show the HubSpot modal or fallback to the email client based on the `hubspotActivated` flag.
61+
62+
```typescript
63+
async openSupportPopup(event): Promise<void> {
64+
if (event instanceof KeyboardEvent && event.key !== 'Enter' && event.key !== ' ') {
65+
return;
66+
}
67+
if (this.hubspotActivated === true) {
68+
const componentProps = {
69+
mode: 'modal',
70+
isShowFormOnly: true,
71+
};
72+
73+
const modal = await this.modalController.create({
74+
componentProps,
75+
component: SupportPopupComponent,
76+
cssClass: 'support-popup',
77+
backdropDismiss: false,
78+
});
79+
80+
return modal.present();
81+
}
82+
83+
return this.mailTo(event);
84+
}
85+
```
86+
87+
3. **`mailTo(event)`**:
88+
- Opens the email client with a pre-filled subject line and recipient email address.
89+
90+
```typescript
91+
mailTo(event) {
92+
if (event instanceof KeyboardEvent && event.key !== 'Enter' && event.key !== ' ') {
93+
return;
94+
}
95+
96+
let mailto = `mailto:${this.helpline}?subject=${this.currentProgramName}`;
97+
const supportEmail = this.utils.getSupportEmail();
98+
99+
if (!this.utils.checkIsPracteraSupportEmail() && !this.utils.isEmpty(supportEmail)) {
100+
mailto = `mailto:${supportEmail}?subject=${this.currentProgramName}`;
101+
}
102+
window.open(mailto, '_self');
103+
}
104+
```
105+
106+
---
107+
108+
### **Logic Flow Diagram**
109+
110+
```plaintext
111+
Start
112+
|
113+
v
114+
Retrieve Support Email Address (after experience selection / login)
115+
|
116+
v
117+
Is Email Address Practera Support Email? (contains "@practera.com")
118+
| Yes | No
119+
v v
120+
Show HubSpot Modal Open Email Client
121+
| |
122+
v v
123+
User Submits Query Pre-fill Email with:
124+
- Recipient: Support Email
125+
- Subject: Current Program Name
126+
|
127+
v
128+
End
129+
```
130+
131+
---
132+
133+
### **Default Email Address**
134+
If no email address is configured in the admin settings, the app defaults to using `programs@practera.com` as the recipient email.
135+
136+
---
137+
138+
### **How to Configure the Support Email**
139+
1. Go to the **Admin Settings** in the Practera platform.
140+
2. Locate the **Support Email** field under the current experience or program settings.
141+
3. Enter a valid email address:
142+
- Use a `@practera.com` email for HubSpot integration.
143+
- Use a custom email for fallback to the email client.
144+
4. Save the changes.
145+
146+
---
147+
148+
### **Supported and Unsupported Configurations**
149+
150+
#### **Supported Configurations**
151+
- **Practera Support Email**: Enables HubSpot modal.
152+
- **Custom Support Email**: Falls back to the email client.
153+
154+
#### **Unsupported Configurations**
155+
- **Empty or Missing Email Address**: Defaults to `programs@practera.com`.
156+
157+
---
158+
159+
### **Testing**
160+
- Test with both Practera and custom email addresses.
161+
- Verify the HubSpot modal opens for Practera emails.
162+
- Verify the email client opens with the correct pre-filled details for custom emails.
163+
- Test fallback to the default email address when no email is configured.
164+
165+
---
166+
167+
This document ensures clarity on the logic flow and configuration for the support functionality in the Practera app.

projects/v3/src/app/components/topic/topic.component.ts

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Topic, TopicService } from '@v3/services/topic.service';
2-
import { Component, Input, Output, EventEmitter, Inject, OnChanges, SimpleChanges, OnDestroy } from '@angular/core';
2+
import { Component, Input, Output, EventEmitter, Inject, OnChanges, SimpleChanges, OnDestroy, OnInit } from '@angular/core';
33
import { DOCUMENT } from '@angular/common';
44
import { UtilsService } from '@v3/services/utils.service';
55
import { SharedService } from '@v3/services/shared.service';
@@ -8,7 +8,7 @@ import { EmbedVideoService } from '@v3/services/ngx-embed-video.service';
88
import { SafeHtml, DomSanitizer } from '@angular/platform-browser';
99
import { FilestackService } from '@v3/app/services/filestack.service';
1010
import { NotificationsService } from '@v3/app/services/notifications.service';
11-
import { BehaviorSubject, Subscription } from 'rxjs';
11+
import { BehaviorSubject, exhaustMap, filter, finalize, Subject, Subscription } from 'rxjs';
1212
import { Activity, Task } from '@v3/app/services/activity.service';
1313
import { ComponentCleanupService } from '@v3/app/services/component-cleanup.service';
1414

@@ -17,7 +17,7 @@ import { ComponentCleanupService } from '@v3/app/services/component-cleanup.serv
1717
templateUrl: './topic.component.html',
1818
styleUrls: ['./topic.component.scss']
1919
})
20-
export class TopicComponent implements OnChanges, OnDestroy {
20+
export class TopicComponent implements OnInit, OnChanges, OnDestroy {
2121
@Input() topic: Topic;
2222
@Input() task: Task;
2323
continuing: boolean;
@@ -32,6 +32,7 @@ export class TopicComponent implements OnChanges, OnDestroy {
3232
iframeHtml: SafeHtml;
3333
sanitizedTitle: SafeHtml;
3434

35+
private continueAction$ = new Subject<Topic>();
3536
private cleanupSub: Subscription;
3637

3738
constructor(
@@ -51,6 +52,25 @@ export class TopicComponent implements OnChanges, OnDestroy {
5152
});
5253
}
5354

55+
ngOnInit() {
56+
this.continueAction$.pipe(
57+
filter(() => !this.continuing),
58+
exhaustMap((topic) => {
59+
this.continuing = true;
60+
this.buttonDisabled$.next(true);
61+
62+
this.continue.emit(topic);
63+
64+
// 1sec cooldown to prevent multiple clicks
65+
return new Promise(resolve => setTimeout(resolve, 1000));
66+
}),
67+
finalize(() => {
68+
this.continuing = false;
69+
this.buttonDisabled$.next(false);
70+
})
71+
).subscribe();
72+
}
73+
5474
ngOnChanges(changes: SimpleChanges): void {
5575
this.continuing = false;
5676
if (this.topic) {
@@ -70,6 +90,7 @@ export class TopicComponent implements OnChanges, OnDestroy {
7090
this.topicService.clearTopic();
7191
this.cleanupMedia();
7292
this.cleanupSub.unsubscribe();
93+
this.continueAction$.complete();
7394
}
7495

7596
ionViewWillLeave() {
@@ -108,8 +129,8 @@ export class TopicComponent implements OnChanges, OnDestroy {
108129

109130
private _setVideoUrlElelemts() {
110131
if (this.topic.videolink.includes('vimeo') ||
111-
this.topic.videolink.includes('youtube') ||
112-
this.topic.videolink.includes('youtu.be')) {
132+
this.topic.videolink.includes('youtube') ||
133+
this.topic.videolink.includes('youtu.be')) {
113134
this.iframeHtml =
114135
this.embedService.embed(this.topic.videolink, {
115136
attr: {
@@ -188,9 +209,7 @@ export class TopicComponent implements OnChanges, OnDestroy {
188209
}
189210

190211
async actionBarContinue(topic): Promise<void> {
191-
this.continuing = true;
192-
this.continue.emit(topic);
193-
return;
212+
this.continueAction$.next(topic);
194213
}
195214

196215
handleVideoError(videoError) {

projects/v3/src/app/pages/topic-mobile/topic-mobile.page.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core';
22
import { ActivatedRoute, Router } from '@angular/router';
33
import { ActivityService, Task } from '@v3/app/services/activity.service';
44
import { TopicService, Topic } from '@v3/app/services/topic.service';
5-
import { BehaviorSubject } from 'rxjs';
5+
import { BehaviorSubject, firstValueFrom } from 'rxjs';
66

77
@Component({
88
selector: 'app-topic-mobile',
@@ -52,7 +52,7 @@ export class TopicMobilePage implements OnInit {
5252
}
5353

5454
// mark the topic as completer
55-
await this.topicService.updateTopicProgress(this.topic.id, 'completed').toPromise();
55+
await firstValueFrom(this.topicService.updateTopicProgress(this.topic.id, 'completed'));
5656
// get the latest activity tasks and navigate to the next task
5757
return this.activityService.getActivity(this.activityId, true, this.currentTask, () => {
5858
this.btnDisabled$.next(false);

projects/v3/src/app/services/fast-feedback.service.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { NotificationsService } from './notifications.service';
33
import { BrowserStorageService } from '@v3/services/storage.service';
44
import { UtilsService } from '@v3/services/utils.service';
55
import { of, from, Observable } from 'rxjs';
6-
import { switchMap, delay, take, retryWhen, finalize } from 'rxjs/operators';
6+
import { switchMap, retry, finalize } from 'rxjs/operators';
77
import { environment } from '@v3/environments/environment';
88
import { DemoService } from './demo.service';
99
import { ApolloService } from './apollo.service';
@@ -19,7 +19,7 @@ export class FastFeedbackService {
1919
private utils: UtilsService,
2020
private demo: DemoService,
2121
private apolloService: ApolloService,
22-
) {}
22+
) { }
2323

2424
private _getFastFeedback(skipChecking = false, type?: string): Observable<ApiResponse<{
2525
pulseCheck: {
@@ -88,11 +88,12 @@ export class FastFeedbackService {
8888
closable?: boolean; // allow skipping modal popup (with a close button)
8989
type?: string; // some pulsecheck require type: 'skills'
9090
} = {
91-
modalOnly: false,
92-
skipChecking: false,
93-
closable: false,
94-
}): Observable<any> {
95-
return this._getFastFeedback(options.skipChecking, options.type).pipe(
91+
modalOnly: false,
92+
skipChecking: false,
93+
closable: false
94+
}
95+
): Observable<any> {
96+
return this._getFastFeedback(options.skipChecking).pipe(
9697
switchMap((res) => {
9798
try {
9899
// don't open it again if there's one opening
@@ -152,9 +153,9 @@ export class FastFeedbackService {
152153
});
153154
}
154155
}),
155-
retryWhen((errors) => {
156-
// retry for 3 times if API go wrong
157-
return errors.pipe(delay(1000), take(3));
156+
retry({
157+
count: 3,
158+
delay: 1000
158159
})
159160
);
160161
}

projects/v3/src/app/services/shared.service.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,14 +164,14 @@ export class SharedService {
164164
*/
165165
markTopicStopOnNavigating() {
166166
if (this.storage.get('startReadTopic')) {
167-
this.topicService.updateTopicProgress(this.storage.get('startReadTopic'), 'stopped').subscribe(
168-
_response => {
167+
this.topicService.updateTopicProgress(this.storage.get('startReadTopic'), 'stopped').subscribe({
168+
next: _response => {
169169
this.storage.remove('startReadTopic');
170170
},
171-
err => {
171+
error: err => {
172172
console.error('error in mark Topic Stop On Navigating - ', err);
173173
}
174-
);
174+
});
175175
}
176176
}
177177

0 commit comments

Comments
 (0)