app-tutor-ai-consumer 1.20.0 → 1.21.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 +6 -0
- package/package.json +2 -1
- package/src/config/datahub/actions.ts +10 -0
- package/src/config/datahub/constants.ts +43 -0
- package/src/config/datahub/entities.ts +4 -0
- package/src/config/datahub/index.ts +5 -0
- package/src/config/datahub/schemas/base-schema.ts +72 -0
- package/src/config/datahub/schemas/constants.ts +10 -0
- package/src/config/datahub/schemas/index.ts +2 -0
- package/src/config/datahub/schemas/tutor/__tests__/click-hotmart-tutor.spec.ts +81 -0
- package/src/config/datahub/schemas/tutor/click-hotmart-tutor.ts +61 -0
- package/src/config/datahub/schemas/tutor/constants.ts +4 -0
- package/src/config/datahub/schemas/tutor/index.ts +3 -0
- package/src/config/datahub/schemas/tutor/types.ts +3 -0
- package/src/config/datahub/schemas/types.ts +19 -0
- package/src/config/datahub/service.ts +32 -0
- package/src/config/datahub/store.ts +87 -0
- package/src/config/datahub/types.ts +35 -0
- package/src/development-bootstrap.tsx +1 -0
- package/src/modules/messages/components/message-actions/message-actions.spec.tsx +53 -0
- package/src/modules/messages/components/message-actions/message-actions.tsx +15 -3
- package/src/modules/widget/hooks/use-init-widget/use-init-widget.tsx +8 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
# [1.21.0](https://github.com/Hotmart-Org/app-tutor-ai-consumer/compare/v1.20.0...v1.21.0) (2025-07-29)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
- add DataHub Config ([745264a](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/745264a166e868884958abb19d0d720e06294ffe))
|
|
6
|
+
|
|
1
7
|
# [1.20.0](https://github.com/Hotmart-Org/app-tutor-ai-consumer/compare/v1.19.0...v1.20.0) (2025-07-29)
|
|
2
8
|
|
|
3
9
|
### Features
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "app-tutor-ai-consumer",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.21.0",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "rspack serve --env=development --config config/rspack/rspack.config.js",
|
|
@@ -104,6 +104,7 @@
|
|
|
104
104
|
},
|
|
105
105
|
"dependencies": {
|
|
106
106
|
"@hot-observability-js/react": "~1.1.0",
|
|
107
|
+
"@hotmart/event-agent-js": "~1.1.2",
|
|
107
108
|
"@hotmart/sparkie": "~5.1.0",
|
|
108
109
|
"@optimizely/react-sdk": "~3.2.4",
|
|
109
110
|
"@tanstack/query-sync-storage-persister": "~5.80.7",
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export const DATA_VERSION = '1.0'
|
|
2
|
+
|
|
3
|
+
export const EVENT_VERSION = '1.1'
|
|
4
|
+
|
|
5
|
+
export const System = {
|
|
6
|
+
HOTMART_CLUB: 'hotmart_club'
|
|
7
|
+
} as const
|
|
8
|
+
|
|
9
|
+
export const ActionNames = {
|
|
10
|
+
CLICK_HOTMART_TUTOR: 'click_hotmart_tutor'
|
|
11
|
+
} as const
|
|
12
|
+
|
|
13
|
+
export const ScreenNames = {
|
|
14
|
+
NOT_APP_EVENT: 'NOT_APP_EVENT',
|
|
15
|
+
HOME_CONSUMER: 'HOME_CONSUMER'
|
|
16
|
+
} as const
|
|
17
|
+
|
|
18
|
+
export const Platform = {
|
|
19
|
+
WEB: 'WEB'
|
|
20
|
+
} as const
|
|
21
|
+
|
|
22
|
+
export const ResultType = {
|
|
23
|
+
SUCCESS: 'SUCCESSFUL',
|
|
24
|
+
FAILURE: 'FAILURE',
|
|
25
|
+
FAILURE_DESCRIPTION: 'NOT_FAILURE_RESULT_EVENT'
|
|
26
|
+
} as const
|
|
27
|
+
|
|
28
|
+
export const ComponentNames = {
|
|
29
|
+
BUTTON_LIKE_ANSWER: 'BUTTON_LIKE_ANSWER',
|
|
30
|
+
BUTTON_DISLIKE_ANSWER: 'BUTTON_DISLIKE_ANSWER'
|
|
31
|
+
} as const
|
|
32
|
+
|
|
33
|
+
export const ComponentSource = {
|
|
34
|
+
ACTION_BAR_STATUS: 'ACTION_BAR_STATUS'
|
|
35
|
+
} as const
|
|
36
|
+
|
|
37
|
+
export const ArrivedFrom = {
|
|
38
|
+
HOME: 'HOME'
|
|
39
|
+
} as const
|
|
40
|
+
|
|
41
|
+
export const PushKind = {
|
|
42
|
+
NOT_PUSH_EVENT: 'NOT_PUSH_EVENT'
|
|
43
|
+
} as const
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { APP_VERSION } from '@/src/lib/utils'
|
|
2
|
+
import { DataHubStore } from '..'
|
|
3
|
+
import { Platform } from '../constants'
|
|
4
|
+
import type {
|
|
5
|
+
ActionNamesType,
|
|
6
|
+
DataHubEntityTypes,
|
|
7
|
+
InitDataParams,
|
|
8
|
+
PlatformType,
|
|
9
|
+
SchemaType
|
|
10
|
+
} from '../types'
|
|
11
|
+
|
|
12
|
+
import { ClubVersion, UserRole } from './constants'
|
|
13
|
+
import type { ClubVersionType, UserRoleType } from './types'
|
|
14
|
+
|
|
15
|
+
abstract class BaseSchema implements SchemaType, InitDataParams {
|
|
16
|
+
appVersion: string
|
|
17
|
+
osVersion: string
|
|
18
|
+
platform: PlatformType
|
|
19
|
+
platformDetail: string
|
|
20
|
+
|
|
21
|
+
membershipId: string
|
|
22
|
+
membershipSlug: string
|
|
23
|
+
sessionId: string
|
|
24
|
+
ucode: string
|
|
25
|
+
userId: number
|
|
26
|
+
|
|
27
|
+
clubVersion: ClubVersionType
|
|
28
|
+
userRole: UserRoleType
|
|
29
|
+
url: string
|
|
30
|
+
|
|
31
|
+
abstract action: ActionNamesType
|
|
32
|
+
abstract entity: DataHubEntityTypes
|
|
33
|
+
abstract isLogged: boolean
|
|
34
|
+
abstract getDataHubEventData(): Record<string, unknown>
|
|
35
|
+
|
|
36
|
+
constructor() {
|
|
37
|
+
this.appVersion = APP_VERSION
|
|
38
|
+
this.clubVersion = ClubVersion.MEMBERSHIP
|
|
39
|
+
this.membershipId = DataHubStore.membershipId
|
|
40
|
+
this.membershipSlug = DataHubStore.membershipSlug
|
|
41
|
+
this.osVersion = DataHubStore.browserInfo.version ?? ''
|
|
42
|
+
this.platform = Platform.WEB
|
|
43
|
+
this.platformDetail = DataHubStore.browserInfo.name ?? ''
|
|
44
|
+
this.sessionId = DataHubStore.sessionId
|
|
45
|
+
this.ucode = DataHubStore.ucode
|
|
46
|
+
this.url = window.location.href
|
|
47
|
+
this.userId = DataHubStore.userId
|
|
48
|
+
this.userRole = UserRole.CONSUMER
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
prepare(): Record<string, unknown> {
|
|
52
|
+
return {
|
|
53
|
+
action: this.action,
|
|
54
|
+
entity: this.entity,
|
|
55
|
+
isLogged: this.isLogged,
|
|
56
|
+
appVersion: this.appVersion,
|
|
57
|
+
osVersion: this.osVersion,
|
|
58
|
+
platform: this.platform,
|
|
59
|
+
platformDetail: this.platformDetail,
|
|
60
|
+
membershipId: this.membershipId,
|
|
61
|
+
membershipSlug: this.membershipSlug,
|
|
62
|
+
sessionId: this.sessionId,
|
|
63
|
+
ucode: this.ucode,
|
|
64
|
+
userId: this.userId,
|
|
65
|
+
clubVersion: this.clubVersion,
|
|
66
|
+
userRole: this.userRole,
|
|
67
|
+
url: this.url
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export default BaseSchema
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { chance } from '@/src/config/tests'
|
|
2
|
+
import { DataHubStore } from '../../..'
|
|
3
|
+
import { ActionNames, ComponentNames, ScreenNames } from '../../../constants'
|
|
4
|
+
import { DataHubEntities } from '../../../entities'
|
|
5
|
+
import { UserRole } from '../../constants'
|
|
6
|
+
import ClickHotmartTutor from '../click-hotmart-tutor'
|
|
7
|
+
import { ButtonReactions } from '../constants'
|
|
8
|
+
|
|
9
|
+
describe('ClickHotmartTutor', () => {
|
|
10
|
+
it('should send the event with the correct structure', () => {
|
|
11
|
+
const event = {
|
|
12
|
+
reactionType: ButtonReactions.DISLIKE,
|
|
13
|
+
messageId: chance.guid(),
|
|
14
|
+
hasAccess: true,
|
|
15
|
+
isLogged: true
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
expect(new ClickHotmartTutor(event).getDataHubEventData()).toMatchObject({
|
|
19
|
+
action: ActionNames.CLICK_HOTMART_TUTOR,
|
|
20
|
+
appVersion: '',
|
|
21
|
+
clubVersion: 'MEMBERSHIP',
|
|
22
|
+
componentName: ComponentNames.BUTTON_DISLIKE_ANSWER,
|
|
23
|
+
entity: DataHubEntities.HOME,
|
|
24
|
+
hasAccess: event.hasAccess,
|
|
25
|
+
isLogged: event.isLogged,
|
|
26
|
+
membershipId: 'UNDEFINED',
|
|
27
|
+
membershipSlug: 'UNDEFINED',
|
|
28
|
+
messageId: event.messageId,
|
|
29
|
+
osVersion: '537.36',
|
|
30
|
+
platform: 'WEB',
|
|
31
|
+
platformDetail: 'WebKit',
|
|
32
|
+
screenName: ScreenNames.HOME_CONSUMER,
|
|
33
|
+
sessionId: 'UNDEFINED',
|
|
34
|
+
ucode: 'UNDEFINED',
|
|
35
|
+
url: 'http://localhost:3000/',
|
|
36
|
+
userId: 0,
|
|
37
|
+
userRole: UserRole.CONSUMER
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('should send reflect DataHubStore current state when sending the event', () => {
|
|
42
|
+
const event = {
|
|
43
|
+
reactionType: ButtonReactions.LIKE,
|
|
44
|
+
messageId: chance.guid(),
|
|
45
|
+
hasAccess: true,
|
|
46
|
+
isLogged: true
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const store = {
|
|
50
|
+
ucode: chance.guid(),
|
|
51
|
+
membershipId: chance.guid(),
|
|
52
|
+
membershipSlug: chance.animal(),
|
|
53
|
+
sessionId: chance.guid(),
|
|
54
|
+
userId: chance.integer()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
DataHubStore.initData(store)
|
|
58
|
+
|
|
59
|
+
expect(new ClickHotmartTutor(event).getDataHubEventData()).toMatchObject({
|
|
60
|
+
action: ActionNames.CLICK_HOTMART_TUTOR,
|
|
61
|
+
appVersion: '',
|
|
62
|
+
clubVersion: 'MEMBERSHIP',
|
|
63
|
+
componentName: ComponentNames.BUTTON_LIKE_ANSWER,
|
|
64
|
+
entity: DataHubEntities.HOME,
|
|
65
|
+
hasAccess: event.hasAccess,
|
|
66
|
+
isLogged: event.isLogged,
|
|
67
|
+
membershipId: store.membershipId,
|
|
68
|
+
membershipSlug: store.membershipSlug,
|
|
69
|
+
messageId: event.messageId,
|
|
70
|
+
osVersion: '537.36',
|
|
71
|
+
platform: 'WEB',
|
|
72
|
+
platformDetail: 'WebKit',
|
|
73
|
+
screenName: ScreenNames.HOME_CONSUMER,
|
|
74
|
+
sessionId: store.sessionId,
|
|
75
|
+
ucode: store.ucode,
|
|
76
|
+
url: 'http://localhost:3000/',
|
|
77
|
+
userId: store.userId,
|
|
78
|
+
userRole: UserRole.CONSUMER
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
})
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { ActionNames, ComponentNames, ScreenNames } from '../../constants'
|
|
2
|
+
import { DataHubEntities } from '../../entities'
|
|
3
|
+
import type {
|
|
4
|
+
ActionNamesType,
|
|
5
|
+
ComponentNamesType,
|
|
6
|
+
DataHubEntityTypes,
|
|
7
|
+
ScreenNamesType
|
|
8
|
+
} from '../../types'
|
|
9
|
+
import BaseSchema from '../base-schema'
|
|
10
|
+
|
|
11
|
+
import { ButtonReactions } from './constants'
|
|
12
|
+
import type { ButtonReactionsType } from './types'
|
|
13
|
+
|
|
14
|
+
class ClickHotmartTutor extends BaseSchema {
|
|
15
|
+
private componentName: ComponentNamesType
|
|
16
|
+
private screenName: ScreenNamesType
|
|
17
|
+
private messageId: string
|
|
18
|
+
private hasAccess: boolean
|
|
19
|
+
|
|
20
|
+
action: ActionNamesType
|
|
21
|
+
entity: DataHubEntityTypes
|
|
22
|
+
isLogged: boolean
|
|
23
|
+
|
|
24
|
+
constructor({
|
|
25
|
+
reactionType,
|
|
26
|
+
messageId,
|
|
27
|
+
hasAccess = true,
|
|
28
|
+
isLogged = true
|
|
29
|
+
}: {
|
|
30
|
+
reactionType: ButtonReactionsType
|
|
31
|
+
messageId: string
|
|
32
|
+
hasAccess?: boolean
|
|
33
|
+
isLogged?: boolean
|
|
34
|
+
}) {
|
|
35
|
+
super()
|
|
36
|
+
|
|
37
|
+
this.action = ActionNames.CLICK_HOTMART_TUTOR
|
|
38
|
+
this.entity = DataHubEntities.HOME
|
|
39
|
+
|
|
40
|
+
this.componentName =
|
|
41
|
+
reactionType === ButtonReactions.LIKE
|
|
42
|
+
? ComponentNames.BUTTON_LIKE_ANSWER
|
|
43
|
+
: ComponentNames.BUTTON_DISLIKE_ANSWER
|
|
44
|
+
this.screenName = ScreenNames.HOME_CONSUMER
|
|
45
|
+
this.messageId = messageId
|
|
46
|
+
this.hasAccess = hasAccess
|
|
47
|
+
this.isLogged = isLogged
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
getDataHubEventData(): Record<string, unknown> {
|
|
51
|
+
return {
|
|
52
|
+
...super.prepare(),
|
|
53
|
+
componentName: this.componentName,
|
|
54
|
+
screenName: this.screenName,
|
|
55
|
+
messageId: this.messageId,
|
|
56
|
+
hasAccess: this.hasAccess
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export default ClickHotmartTutor
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ClubVersion, UserRole } from './constants'
|
|
2
|
+
|
|
3
|
+
export type UserRoleType = (typeof UserRole)[keyof typeof UserRole]
|
|
4
|
+
|
|
5
|
+
export type ClubVersionType = (typeof ClubVersion)[keyof typeof ClubVersion]
|
|
6
|
+
|
|
7
|
+
export type SchemaBasedProps = {
|
|
8
|
+
membershipSlug?: string
|
|
9
|
+
membershipId?: string
|
|
10
|
+
ucode?: string
|
|
11
|
+
userId?: number
|
|
12
|
+
hasAccess?: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type BaseSchemaProps = {
|
|
16
|
+
membershipId: string
|
|
17
|
+
membershipSlug: string
|
|
18
|
+
userId: string
|
|
19
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import EventAgent from '@hotmart/event-agent-js'
|
|
2
|
+
|
|
3
|
+
import { productionMode } from '@/src/lib/utils'
|
|
4
|
+
|
|
5
|
+
import { DATA_VERSION, EVENT_VERSION, System } from './constants'
|
|
6
|
+
import type { SendEventParams } from './types'
|
|
7
|
+
|
|
8
|
+
EventAgent.mode = productionMode ? 'production' : 'staging'
|
|
9
|
+
|
|
10
|
+
class DataHubService {
|
|
11
|
+
sendEvent({
|
|
12
|
+
schema,
|
|
13
|
+
dataVersion = DATA_VERSION,
|
|
14
|
+
eventVersion = EVENT_VERSION,
|
|
15
|
+
system = System.HOTMART_CLUB
|
|
16
|
+
}: SendEventParams): void {
|
|
17
|
+
const dataHubEvent = {
|
|
18
|
+
system,
|
|
19
|
+
action: schema.action,
|
|
20
|
+
entity: schema.entity,
|
|
21
|
+
data_version: dataVersion,
|
|
22
|
+
event_version: eventVersion,
|
|
23
|
+
event: schema.getDataHubEventData()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!productionMode) console.warn('DataHub event dispatched: ', dataHubEvent)
|
|
27
|
+
|
|
28
|
+
void EventAgent.send(dataHubEvent)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default new DataHubService()
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { UAParser } from 'ua-parser-js'
|
|
2
|
+
|
|
3
|
+
import type { InitDataParams } from './types'
|
|
4
|
+
|
|
5
|
+
class DataHubStore {
|
|
6
|
+
private instanceObj?: {
|
|
7
|
+
userAgentParser: UAParser.IResult
|
|
8
|
+
browserInfo: UAParser.IBrowser
|
|
9
|
+
deviceType: string
|
|
10
|
+
deviceVendor: UAParser.IDevice['vendor']
|
|
11
|
+
deviceModel: UAParser.IDevice['model']
|
|
12
|
+
ucode: string
|
|
13
|
+
sessionId: string
|
|
14
|
+
userId: number
|
|
15
|
+
membershipSlug: string
|
|
16
|
+
membershipId: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
private get instance() {
|
|
20
|
+
return this.instanceObj ?? this.resetData()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
initData({ ucode, membershipId, membershipSlug, sessionId, userId }: InitDataParams) {
|
|
24
|
+
this.instance.userAgentParser = UAParser(navigator.userAgent)
|
|
25
|
+
this.instance.browserInfo = this.instance.userAgentParser.browser
|
|
26
|
+
this.instance.deviceType =
|
|
27
|
+
this.instance.userAgentParser.device?.type?.toUpperCase() || 'DESKTOP'
|
|
28
|
+
this.instance.deviceVendor = this.instance.userAgentParser.device?.vendor?.toUpperCase() ?? ''
|
|
29
|
+
this.instance.deviceModel = this.instance.userAgentParser.device?.model?.toUpperCase() ?? ''
|
|
30
|
+
this.instance.ucode = ucode
|
|
31
|
+
this.instance.sessionId = sessionId
|
|
32
|
+
this.instance.userId = userId || 0
|
|
33
|
+
this.instance.membershipSlug = membershipSlug
|
|
34
|
+
this.instance.membershipId = membershipId
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
resetData() {
|
|
38
|
+
const userAgent = UAParser(navigator.userAgent)
|
|
39
|
+
|
|
40
|
+
const clearData = {
|
|
41
|
+
userAgentParser: userAgent,
|
|
42
|
+
browserInfo: userAgent.browser,
|
|
43
|
+
deviceType: userAgent.device.type?.toUpperCase() || 'DESKTOP',
|
|
44
|
+
deviceVendor: userAgent.device.vendor,
|
|
45
|
+
deviceModel: userAgent.device.model,
|
|
46
|
+
ucode: 'UNDEFINED',
|
|
47
|
+
sessionId: 'UNDEFINED',
|
|
48
|
+
userId: 0,
|
|
49
|
+
membershipSlug: 'UNDEFINED',
|
|
50
|
+
membershipId: 'UNDEFINED'
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
this.instanceObj = clearData
|
|
54
|
+
|
|
55
|
+
return clearData
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get userAgentParser() {
|
|
59
|
+
return this.instance.userAgentParser
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
get browserInfo() {
|
|
63
|
+
return this.instance.browserInfo
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get ucode() {
|
|
67
|
+
return this.instance.ucode
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
get sessionId() {
|
|
71
|
+
return this.instance.sessionId
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
get userId() {
|
|
75
|
+
return this.instance.userId
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
get membershipSlug() {
|
|
79
|
+
return this.instance.membershipSlug
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
get membershipId() {
|
|
83
|
+
return this.instance.membershipId
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export default new DataHubStore()
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ActionNames, ComponentNames, Platform, ScreenNames, System } from './constants'
|
|
2
|
+
import type { DataHubEntities } from './entities'
|
|
3
|
+
|
|
4
|
+
export type ActionNamesType = (typeof ActionNames)[keyof typeof ActionNames]
|
|
5
|
+
export type DataHubEntityTypes = (typeof DataHubEntities)[keyof typeof DataHubEntities]
|
|
6
|
+
export type PlatformType = (typeof Platform)[keyof typeof Platform]
|
|
7
|
+
export type SystemType = (typeof System)[keyof typeof System]
|
|
8
|
+
export type ComponentNamesType = (typeof ComponentNames)[keyof typeof ComponentNames]
|
|
9
|
+
export type ScreenNamesType = (typeof ScreenNames)[keyof typeof ScreenNames]
|
|
10
|
+
|
|
11
|
+
export type SchemaType = {
|
|
12
|
+
action: ActionNamesType
|
|
13
|
+
entity: DataHubEntityTypes
|
|
14
|
+
osVersion: string
|
|
15
|
+
appVersion: string
|
|
16
|
+
platformDetail: string
|
|
17
|
+
platform: PlatformType
|
|
18
|
+
prepare: () => Record<string, unknown>
|
|
19
|
+
getDataHubEventData: () => Record<string, unknown>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type SendEventParams = {
|
|
23
|
+
schema: SchemaType
|
|
24
|
+
system?: SystemType
|
|
25
|
+
dataVersion?: string
|
|
26
|
+
eventVersion?: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type InitDataParams = {
|
|
30
|
+
ucode: string
|
|
31
|
+
sessionId: string
|
|
32
|
+
userId: number
|
|
33
|
+
membershipSlug: string
|
|
34
|
+
membershipId: string
|
|
35
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { DataHubService } from '@/src/config/datahub'
|
|
2
|
+
import { ButtonReactions, ClickHotmartTutor } from '@/src/config/datahub/schemas/tutor'
|
|
3
|
+
import { render, screen } from '@/src/config/tests'
|
|
4
|
+
import ParsedMessageBuilder from '../../__tests__/parsed-message.builder'
|
|
5
|
+
|
|
6
|
+
import MessageActions from './message-actions'
|
|
7
|
+
|
|
8
|
+
describe('<MessageActions/>', () => {
|
|
9
|
+
const defaultProps = {
|
|
10
|
+
message: new ParsedMessageBuilder()
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const renderComponent = (props = defaultProps) => render(<MessageActions {...props} />)
|
|
14
|
+
|
|
15
|
+
it('should render without errors', () => {
|
|
16
|
+
renderComponent()
|
|
17
|
+
|
|
18
|
+
expect(screen.getByRole('button', { name: /general.buttons.like/i })).toBeInTheDocument()
|
|
19
|
+
expect(screen.getByRole('button', { name: /general.buttons.dislike/i })).toBeInTheDocument()
|
|
20
|
+
expect(screen.getByRole('button', { name: /general.buttons.copy/i })).toBeInTheDocument()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('should copy the current message text when clicking the copy button', async () => {
|
|
24
|
+
vi.spyOn(navigator.clipboard, 'writeText').mockImplementation(() => Promise.resolve(undefined))
|
|
25
|
+
|
|
26
|
+
const { user } = renderComponent()
|
|
27
|
+
|
|
28
|
+
const copyBtn = screen.getByRole('button', { name: /general.buttons.copy/i })
|
|
29
|
+
|
|
30
|
+
await user.click(copyBtn)
|
|
31
|
+
|
|
32
|
+
expect(navigator.clipboard.writeText).toBeCalledTimes(1)
|
|
33
|
+
expect(navigator.clipboard.writeText).toHaveBeenNthCalledWith(1, defaultProps.message.text)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('should send like event when clicking the like button', async () => {
|
|
37
|
+
vi.spyOn(DataHubService, 'sendEvent').mockImplementation(() => undefined)
|
|
38
|
+
|
|
39
|
+
const { user } = renderComponent()
|
|
40
|
+
|
|
41
|
+
const likeBtn = screen.getByRole('button', { name: /general.buttons.like/i })
|
|
42
|
+
|
|
43
|
+
await user.click(likeBtn)
|
|
44
|
+
|
|
45
|
+
expect(DataHubService.sendEvent).toBeCalledTimes(1)
|
|
46
|
+
expect(DataHubService.sendEvent).toHaveBeenNthCalledWith(1, {
|
|
47
|
+
schema: new ClickHotmartTutor({
|
|
48
|
+
messageId: defaultProps.message.id,
|
|
49
|
+
reactionType: ButtonReactions.LIKE
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
})
|
|
@@ -3,6 +3,9 @@ import dayjs from 'dayjs'
|
|
|
3
3
|
import type { CSSProperties } from 'react'
|
|
4
4
|
import { useTranslation } from 'react-i18next'
|
|
5
5
|
|
|
6
|
+
import { DataHubService } from '@/src/config/datahub'
|
|
7
|
+
import type { ButtonReactionsType } from '@/src/config/datahub/schemas/tutor'
|
|
8
|
+
import { ButtonReactions, ClickHotmartTutor } from '@/src/config/datahub/schemas/tutor'
|
|
6
9
|
import { Button, Icon } from '@/src/lib/components'
|
|
7
10
|
import type { ParsedMessage } from '../../types'
|
|
8
11
|
|
|
@@ -14,7 +17,8 @@ export type MessageActionsProps = {
|
|
|
14
17
|
|
|
15
18
|
function MessageActions({ message, className, showActions = false }: MessageActionsProps) {
|
|
16
19
|
const { t } = useTranslation()
|
|
17
|
-
|
|
20
|
+
|
|
21
|
+
const copyToClipboard = (): void => {
|
|
18
22
|
if (!message.text) return
|
|
19
23
|
|
|
20
24
|
navigator.clipboard.writeText(message.text).catch((err) => {
|
|
@@ -22,6 +26,11 @@ function MessageActions({ message, className, showActions = false }: MessageActi
|
|
|
22
26
|
})
|
|
23
27
|
}
|
|
24
28
|
|
|
29
|
+
const handleReaction = (reactionType: ButtonReactionsType = ButtonReactions.LIKE): void => {
|
|
30
|
+
const schema = new ClickHotmartTutor({ messageId: message.id, reactionType })
|
|
31
|
+
return DataHubService.sendEvent({ schema })
|
|
32
|
+
}
|
|
33
|
+
|
|
25
34
|
return (
|
|
26
35
|
<div className={className}>
|
|
27
36
|
<span className='text-xs tracking-wide'>
|
|
@@ -32,10 +41,13 @@ function MessageActions({ message, className, showActions = false }: MessageActi
|
|
|
32
41
|
className={clsx('flex flex-nowrap gap-2 text-neutral-600', {
|
|
33
42
|
hidden: !showActions
|
|
34
43
|
})}>
|
|
35
|
-
<Button aria-label={t('general.buttons.like')}>
|
|
44
|
+
<Button onClick={() => handleReaction()} aria-label={t('general.buttons.like')}>
|
|
36
45
|
<Icon name='like' className='h-3 w-3.5' />
|
|
37
46
|
</Button>
|
|
38
|
-
<Button
|
|
47
|
+
<Button
|
|
48
|
+
className='rotate-180 scale-x-[-1]'
|
|
49
|
+
onClick={() => handleReaction(ButtonReactions.DISLIKE)}
|
|
50
|
+
aria-label={t('general.buttons.dislike')}>
|
|
39
51
|
<Icon name='like' className='h-3 w-3.5' />
|
|
40
52
|
</Button>
|
|
41
53
|
<Button onClick={copyToClipboard} aria-label={t('general.buttons.copy')}>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react'
|
|
2
2
|
|
|
3
|
+
import { DataHubStore } from '@/src/config/datahub'
|
|
3
4
|
import { initDayjs } from '@/src/config/dayjs'
|
|
4
5
|
import { initAxios } from '@/src/config/request/api'
|
|
5
6
|
import { SparkieService } from '@/src/modules/sparkie'
|
|
@@ -10,6 +11,13 @@ const init = async (settings: WidgetSettingProps) => {
|
|
|
10
11
|
try {
|
|
11
12
|
initAxios(settings.hotmartToken)
|
|
12
13
|
await initDayjs(settings.locale)
|
|
14
|
+
DataHubStore.initData({
|
|
15
|
+
ucode: settings.user?.ucode ?? '',
|
|
16
|
+
membershipId: settings.membershipId ?? '',
|
|
17
|
+
membershipSlug: settings.membershipSlug ?? '',
|
|
18
|
+
sessionId: settings.sessionId,
|
|
19
|
+
userId: Number(settings.userId)
|
|
20
|
+
})
|
|
13
21
|
await SparkieService.initSparkie({
|
|
14
22
|
token: settings?.hotmartToken,
|
|
15
23
|
skipPresenceSetup: true,
|