@edusight/notification-widget 1.0.31 → 1.0.32
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/dist/{hooks-Cv5k48VE.js → hooks-BTG0z6yd.js} +65 -48
- package/dist/hooks-BTG0z6yd.js.map +1 -0
- package/dist/hooks-kLhwdW29.cjs +2 -0
- package/dist/hooks-kLhwdW29.cjs.map +1 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.esm.js +1 -1
- package/package.json +1 -1
- package/dist/hooks-CggRWw6Q.cjs +0 -2
- package/dist/hooks-CggRWw6Q.cjs.map +0 -1
- package/dist/hooks-Cv5k48VE.js.map +0 -1
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
import { useState as
|
|
2
|
-
const R = () => window.__notificationSDK?.client,
|
|
1
|
+
import { useState as E, useRef as j, useCallback as _, useEffect as N } from "react";
|
|
2
|
+
const R = () => window.__notificationSDK?.client, T = ({
|
|
3
3
|
preferences: t,
|
|
4
|
-
onPreferencesChange:
|
|
4
|
+
onPreferencesChange: f,
|
|
5
5
|
onError: e
|
|
6
6
|
}) => {
|
|
7
|
-
const n = R(), [c, i] =
|
|
7
|
+
const n = R(), [c, i] = E(!1), [r, l] = E(null), u = j(), m = j({}), y = _(
|
|
8
8
|
(g, v) => {
|
|
9
9
|
try {
|
|
10
10
|
l(null);
|
|
11
|
-
const
|
|
12
|
-
let p =
|
|
11
|
+
const o = { ...t }, s = g.split(".");
|
|
12
|
+
let p = o;
|
|
13
13
|
for (let a = 0; a < s.length - 1; a++)
|
|
14
14
|
p[s[a]] === void 0 && (p[s[a]] = {}), p = p[s[a]];
|
|
15
|
-
p[s[s.length - 1]] = v, o
|
|
15
|
+
p[s[s.length - 1]] = v, f(o), m.current[g] = v, u.current && clearTimeout(u.current), i(!0), u.current = setTimeout(async () => {
|
|
16
16
|
try {
|
|
17
17
|
if (!n)
|
|
18
18
|
throw new Error("Notification client not available");
|
|
19
19
|
const a = window.__notificationSDK?.config;
|
|
20
20
|
if (!a)
|
|
21
21
|
throw new Error("SDK configuration not available");
|
|
22
|
-
const { subscriberId: h, tenantId:
|
|
23
|
-
if (!h || !
|
|
22
|
+
const { subscriberId: h, tenantId: A, environmentId: D } = a;
|
|
23
|
+
if (!h || !A || !D)
|
|
24
24
|
throw new Error("SubscriberId, TenantId or EnvironmentId not available in SDK configuration.");
|
|
25
|
-
const
|
|
26
|
-
emailEnabled:
|
|
27
|
-
pushEnabled:
|
|
28
|
-
inAppEnabled:
|
|
29
|
-
smsEnabled:
|
|
25
|
+
const d = {
|
|
26
|
+
emailEnabled: o.channels.email,
|
|
27
|
+
pushEnabled: o.channels.push,
|
|
28
|
+
inAppEnabled: o.channels.inApp,
|
|
29
|
+
smsEnabled: o.channels.sms
|
|
30
30
|
};
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
o.subscriptions.length > 0 && (d.categories = {}, o.subscriptions.forEach((b) => {
|
|
32
|
+
d.categories[b.workflowId] = {
|
|
33
33
|
emailEnabled: b.channels.email,
|
|
34
34
|
pushEnabled: b.channels.push,
|
|
35
35
|
inAppEnabled: b.channels.inApp,
|
|
36
36
|
smsEnabled: b.channels.sms
|
|
37
37
|
};
|
|
38
|
-
})), await n.preferences.update(
|
|
38
|
+
})), await n.preferences.update(A, h, d), m.current = {}, l(null);
|
|
39
39
|
} catch (a) {
|
|
40
40
|
const h = a instanceof Error ? a : new Error("Failed to save preferences");
|
|
41
41
|
process.env.NODE_ENV, l(h), e?.(h);
|
|
@@ -43,21 +43,21 @@ const R = () => window.__notificationSDK?.client, I = ({
|
|
|
43
43
|
i(!1);
|
|
44
44
|
}
|
|
45
45
|
}, 500);
|
|
46
|
-
} catch (
|
|
47
|
-
const s =
|
|
46
|
+
} catch (o) {
|
|
47
|
+
const s = o instanceof Error ? o : new Error("Failed to update preference");
|
|
48
48
|
l(s), e?.(s), i(!1);
|
|
49
49
|
}
|
|
50
50
|
},
|
|
51
|
-
[t,
|
|
51
|
+
[t, f, n, e]
|
|
52
52
|
);
|
|
53
|
-
return
|
|
53
|
+
return N(() => () => {
|
|
54
54
|
u.current && clearTimeout(u.current);
|
|
55
55
|
}, []), {
|
|
56
|
-
updatePreference:
|
|
56
|
+
updatePreference: y,
|
|
57
57
|
isSaving: c,
|
|
58
|
-
error:
|
|
58
|
+
error: r
|
|
59
59
|
};
|
|
60
|
-
}, S = (t) => !(!t || typeof t != "object" || ["notificationId", "channel", "renderedContent", "read", "archived"].filter((n) => !(n in t)).length > 0 || typeof t.channel != "string" || typeof t.read != "boolean" || typeof t.archived != "boolean" || typeof t.renderedContent != "object" || t.renderedContent === null),
|
|
60
|
+
}, S = (t) => !(!t || typeof t != "object" || ["notificationId", "channel", "renderedContent", "read", "archived"].filter((n) => !(n in t)).length > 0 || typeof t.channel != "string" || typeof t.read != "boolean" || typeof t.archived != "boolean" || typeof t.renderedContent != "object" || t.renderedContent === null), w = (t, f, e) => ({
|
|
61
61
|
type: "custom",
|
|
62
62
|
label: t.label,
|
|
63
63
|
icon: void 0,
|
|
@@ -72,30 +72,42 @@ const R = () => window.__notificationSDK?.client, I = ({
|
|
|
72
72
|
throw n;
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
|
-
}),
|
|
75
|
+
}), I = () => {
|
|
76
76
|
const t = (e, n) => {
|
|
77
77
|
if (!e || typeof e != "object")
|
|
78
78
|
return { subject: "", body: "" };
|
|
79
79
|
let c = "", i = "";
|
|
80
80
|
return e.push ? (c = e.push.title || "", i = e.push.body || "") : e.email ? (c = e.email.subject || "", i = e.email.html || e.email.text || "") : e.in_app ? (c = e.in_app.title || "", i = e.in_app.message || "") : e.sms ? (c = "SMS", i = e.sms.message || "") : n === "in_app" ? (c = e.title || "", i = e.message || "") : (c = e.title || e.subject || "", i = e.message || e.body || e.text || ""), { subject: c, body: i };
|
|
81
|
-
},
|
|
81
|
+
}, f = (e, n, c) => {
|
|
82
82
|
if (!e) return [];
|
|
83
83
|
const i = [];
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
}
|
|
84
|
+
if (Array.isArray(e))
|
|
85
|
+
return e.forEach((r) => {
|
|
86
|
+
i.push(w(r, n || "", c));
|
|
87
|
+
}), i;
|
|
88
|
+
if (e.primary) {
|
|
89
|
+
const r = e.primary;
|
|
90
|
+
i.push({
|
|
91
|
+
type: "custom",
|
|
92
|
+
label: r.label || "Primary Action",
|
|
93
|
+
icon: void 0,
|
|
94
|
+
handler: async (l) => {
|
|
95
|
+
r.url && window.open(r.url, "_self");
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
if (e.secondary) {
|
|
100
|
+
const r = e.secondary;
|
|
101
|
+
i.push({
|
|
102
|
+
type: "custom",
|
|
103
|
+
label: r.label || "Secondary Action",
|
|
104
|
+
icon: void 0,
|
|
105
|
+
handler: async (l) => {
|
|
106
|
+
r.url && window.open(r.url, "_self");
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return i;
|
|
99
111
|
};
|
|
100
112
|
return {
|
|
101
113
|
toWidgetNotification(e) {
|
|
@@ -108,7 +120,9 @@ const R = () => window.__notificationSDK?.client, I = ({
|
|
|
108
120
|
isArchived: e.archived,
|
|
109
121
|
timestamp: e.createdAt ? new Date(e.createdAt) : /* @__PURE__ */ new Date(),
|
|
110
122
|
tags: [],
|
|
111
|
-
actions:
|
|
123
|
+
actions: f(
|
|
124
|
+
e.renderedContent?.actions || e.actions
|
|
125
|
+
),
|
|
112
126
|
metadata: {
|
|
113
127
|
channel: e.channel,
|
|
114
128
|
renderedContent: e.renderedContent
|
|
@@ -117,10 +131,10 @@ const R = () => window.__notificationSDK?.client, I = ({
|
|
|
117
131
|
},
|
|
118
132
|
toWidgetNotificationFromWebSocket(e) {
|
|
119
133
|
if (!S(e)) {
|
|
120
|
-
const i = e?.renderedContent && typeof e.renderedContent == "object" ? e.renderedContent : {}, { subject:
|
|
134
|
+
const i = e?.renderedContent && typeof e.renderedContent == "object" ? e.renderedContent : {}, { subject: r, body: l } = t(i, e?.channel);
|
|
121
135
|
return {
|
|
122
136
|
id: e?.notificationId || `notification-${Date.now()}`,
|
|
123
|
-
subject:
|
|
137
|
+
subject: r || "Notification",
|
|
124
138
|
body: l || "",
|
|
125
139
|
isRead: e?.read || !1,
|
|
126
140
|
isArchived: e?.archived || !1,
|
|
@@ -142,7 +156,10 @@ const R = () => window.__notificationSDK?.client, I = ({
|
|
|
142
156
|
isArchived: e.archived || !1,
|
|
143
157
|
timestamp: e.createdAt ? new Date(e.createdAt) : /* @__PURE__ */ new Date(),
|
|
144
158
|
tags: [],
|
|
145
|
-
actions:
|
|
159
|
+
actions: f(
|
|
160
|
+
e.renderedContent?.actions || e.actions,
|
|
161
|
+
e.notificationId
|
|
162
|
+
),
|
|
146
163
|
metadata: {
|
|
147
164
|
channel: e.channel,
|
|
148
165
|
renderedContent: e.renderedContent
|
|
@@ -153,7 +170,7 @@ const R = () => window.__notificationSDK?.client, I = ({
|
|
|
153
170
|
};
|
|
154
171
|
};
|
|
155
172
|
export {
|
|
156
|
-
|
|
157
|
-
|
|
173
|
+
I as c,
|
|
174
|
+
T as u
|
|
158
175
|
};
|
|
159
|
-
//# sourceMappingURL=hooks-
|
|
176
|
+
//# sourceMappingURL=hooks-BTG0z6yd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks-BTG0z6yd.js","sources":["../src/hooks/useLivePreferences.ts","../src/utils/notification-mapper.ts"],"sourcesContent":["import { useCallback, useRef, useState, useEffect } from 'react';\r\nimport { NotificationPreferences } from '../types/core';\r\n\r\nconst useNotificationsClient = () => {\r\n return (window as any).__notificationSDK?.client;\r\n};\r\n\r\nexport interface UseLivePreferencesProps {\r\n preferences: NotificationPreferences;\r\n onPreferencesChange: (preferences: NotificationPreferences) => void;\r\n onError?: (error: Error) => void;\r\n}\r\n\r\nexport interface UseLivePreferencesResult {\r\n updatePreference: (path: string, value: any) => void;\r\n isSaving: boolean;\r\n error: Error | null;\r\n}\r\n\r\nexport const useLivePreferences = ({\r\n preferences,\r\n onPreferencesChange,\r\n onError,\r\n}: UseLivePreferencesProps): UseLivePreferencesResult => {\r\n const notificationClient = useNotificationsClient();\r\n const [isSaving, setIsSaving] = useState(false);\r\n const [error, setError] = useState<Error | null>(null);\r\n const saveTimeoutRef = useRef<NodeJS.Timeout>();\r\n const pendingUpdatesRef = useRef<{ [key: string]: any }>({});\r\n\r\n const updatePreference = useCallback(\r\n (path: string, value: any) => {\r\n try {\r\n setError(null);\r\n\r\n const updatedPreferences = { ...preferences };\r\n const pathParts = path.split('.');\r\n\r\n let current: any = updatedPreferences;\r\n for (let i = 0; i < pathParts.length - 1; i++) {\r\n if (current[pathParts[i]] === undefined) {\r\n current[pathParts[i]] = {};\r\n }\r\n current = current[pathParts[i]];\r\n }\r\n current[pathParts[pathParts.length - 1]] = value;\r\n\r\n onPreferencesChange(updatedPreferences);\r\n\r\n pendingUpdatesRef.current[path] = value;\r\n\r\n if (saveTimeoutRef.current) {\r\n clearTimeout(saveTimeoutRef.current);\r\n }\r\n\r\n setIsSaving(true);\r\n\r\n saveTimeoutRef.current = setTimeout(async () => {\r\n try {\r\n if (!notificationClient) {\r\n throw new Error('Notification client not available');\r\n }\r\n\r\n const sdkConfig = (window as any).__notificationSDK?.config;\r\n\r\n if (!sdkConfig) {\r\n throw new Error('SDK configuration not available');\r\n }\r\n\r\n const { subscriberId, tenantId, environmentId } = sdkConfig;\r\n\r\n if (!subscriberId || !tenantId || !environmentId) {\r\n throw new Error('SubscriberId, TenantId or EnvironmentId not available in SDK configuration.');\r\n }\r\n\r\n const savePayload: any = {\r\n emailEnabled: updatedPreferences.channels.email,\r\n pushEnabled: updatedPreferences.channels.push,\r\n inAppEnabled: updatedPreferences.channels.inApp,\r\n smsEnabled: updatedPreferences.channels.sms,\r\n };\r\n\r\n if (updatedPreferences.subscriptions.length > 0) {\r\n savePayload.categories = {};\r\n updatedPreferences.subscriptions.forEach(sub => {\r\n savePayload.categories[sub.workflowId] = {\r\n emailEnabled: sub.channels.email,\r\n pushEnabled: sub.channels.push,\r\n inAppEnabled: sub.channels.inApp,\r\n smsEnabled: sub.channels.sms,\r\n };\r\n });\r\n }\r\n\r\n // NOTE: deliverySchedule removed as backend doesn't accept it (returns 400)\r\n // Quiet hours and weekdays management will be added when backend supports it\r\n\r\n await notificationClient.preferences.update(tenantId, subscriberId, savePayload);\r\n\r\n pendingUpdatesRef.current = {};\r\n setError(null);\r\n } catch (err: any) {\r\n const error = err instanceof Error ? err : new Error('Failed to save preferences');\r\n\r\n if (process.env.NODE_ENV === 'development') {\r\n console.error('Failed to save preferences:', error);\r\n }\r\n\r\n setError(error);\r\n onError?.(error);\r\n\r\n } finally {\r\n setIsSaving(false);\r\n }\r\n }, 500);\r\n } catch (err: any) {\r\n const error = err instanceof Error ? err : new Error('Failed to update preference');\r\n setError(error);\r\n onError?.(error);\r\n setIsSaving(false);\r\n }\r\n },\r\n [preferences, onPreferencesChange, notificationClient, onError]\r\n );\r\n\r\n useEffect(() => {\r\n return () => {\r\n if (saveTimeoutRef.current) {\r\n clearTimeout(saveTimeoutRef.current);\r\n }\r\n };\r\n }, []);\r\n\r\n return {\r\n updatePreference,\r\n isSaving,\r\n error,\r\n };\r\n};","import type { RenderedNotificationItem, RenderedNotificationActionMap, NotificationAction as SDKNotificationAction } from '@edusight/notification-sdk';\nimport type { Notification, NotificationAction } from '../types/core';\n\nexport interface WebSocketNotificationReceivedPayload extends RenderedNotificationItem {}\n\nexport interface NotificationMapper {\n toWidgetNotification(renderedItem: RenderedNotificationItem): Notification;\n toWidgetNotificationFromWebSocket(wsPayload: any): Notification;\n validateWebSocketPayload(payload: any): payload is WebSocketNotificationReceivedPayload;\n}\n\nconst validateWebSocketPayload = (payload: any): payload is WebSocketNotificationReceivedPayload => {\n if (!payload || typeof payload !== 'object') {\n console.warn('Invalid WebSocket payload: not an object', payload);\n return false;\n }\n\n const requiredFields = ['notificationId', 'channel', 'renderedContent', 'read', 'archived'];\n const missingFields = requiredFields.filter(field => !(field in payload));\n\n if (missingFields.length > 0) {\n console.warn('WebSocket payload missing required fields:', missingFields, payload);\n return false;\n }\n\n if (typeof payload.channel !== 'string') {\n console.warn('WebSocket payload channel is invalid', payload);\n return false;\n }\n\n if (typeof payload.read !== 'boolean' || typeof payload.archived !== 'boolean') {\n console.warn('WebSocket payload read/archived flags are invalid', payload);\n return false;\n }\n\n if (typeof payload.renderedContent !== 'object' || payload.renderedContent === null) {\n console.warn('WebSocket payload renderedContent is not an object', payload);\n return false;\n }\n\n return true;\n};\n\n\nconst mapSDKActionToWidgetAction = (\n sdkAction: SDKNotificationAction,\n notificationId: string,\n onActionExecute?: (actionId: string, notificationId: string) => Promise<void>\n): NotificationAction => {\n return {\n type: 'custom',\n label: sdkAction.label,\n icon: undefined,\n handler: async () => {\n try {\n if (onActionExecute) {\n await onActionExecute(sdkAction.id, notificationId);\n }\n\n if (sdkAction.url) {\n const target = sdkAction.target || '_self';\n window.open(sdkAction.url, target);\n }\n\n if (sdkAction.markAsReadOnClick) {\n // This will be handled by the widget's action handler\n }\n } catch (error) {\n console.error('Error executing action:', error);\n throw error;\n }\n },\n };\n};\n\nexport const createNotificationMapper = (): NotificationMapper => {\n const extractSubjectAndBody = (\n renderedContent: Record<string, any>,\n channel?: string\n ): { subject: string; body: string } => {\n if (!renderedContent || typeof renderedContent !== 'object') {\n return { subject: '', body: '' };\n }\n\n let subject = '';\n let body = '';\n\n if (renderedContent.push) {\n subject = renderedContent.push.title || '';\n body = renderedContent.push.body || '';\n } else if (renderedContent.email) {\n subject = renderedContent.email.subject || '';\n body = renderedContent.email.html || renderedContent.email.text || '';\n } else if (renderedContent.in_app) {\n subject = renderedContent.in_app.title || '';\n body = renderedContent.in_app.message || '';\n } else if (renderedContent.sms) {\n subject = 'SMS';\n body = renderedContent.sms.message || '';\n } else if (channel === 'in_app') {\n subject = renderedContent.title || '';\n body = renderedContent.message || '';\n } else {\n subject = renderedContent.title || renderedContent.subject || '';\n body = renderedContent.message || renderedContent.body || renderedContent.text || '';\n }\n\n return { subject, body };\n };\n\n const mapActionsToNotificationActions = (\n actions?: RenderedNotificationActionMap | SDKNotificationAction[],\n notificationId?: string,\n onActionExecute?: (actionId: string, notificationId: string) => Promise<void>\n ): NotificationAction[] => {\n if (!actions) return [];\n\n const result: NotificationAction[] = [];\n\n if (Array.isArray(actions)) {\n actions.forEach((sdkAction) => {\n result.push(mapSDKActionToWidgetAction(sdkAction, notificationId || '', onActionExecute));\n });\n return result;\n }\n\n if (actions.primary) {\n const primaryAction = actions.primary;\n result.push({\n type: 'custom',\n label: primaryAction.label || 'Primary Action',\n icon: undefined,\n handler: async (notificationId: string) => {\n if (primaryAction.url) {\n window.open(primaryAction.url, '_self');\n }\n if (onActionExecute) {\n await onActionExecute('primary', notificationId);\n }\n },\n });\n }\n\n if (actions.secondary) {\n const secondaryAction = actions.secondary;\n result.push({\n type: 'custom',\n label: secondaryAction.label || 'Secondary Action',\n icon: undefined,\n handler: async (notificationId: string) => {\n if (secondaryAction.url) {\n window.open(secondaryAction.url, '_self');\n }\n if (onActionExecute) {\n await onActionExecute('secondary', notificationId);\n }\n },\n });\n }\n\n return result;\n };\n\n return {\n toWidgetNotification(renderedItem: RenderedNotificationItem): Notification {\n const { subject, body } = extractSubjectAndBody(renderedItem.renderedContent, renderedItem.channel);\n\n return {\n id: renderedItem.notificationId,\n subject: subject || 'Notification',\n body: body || '',\n isRead: renderedItem.read,\n isArchived: renderedItem.archived,\n timestamp: renderedItem.createdAt ? new Date(renderedItem.createdAt) : new Date(),\n tags: [],\n actions: mapActionsToNotificationActions(\n renderedItem.renderedContent?.actions || renderedItem.actions\n ),\n metadata: {\n channel: renderedItem.channel,\n renderedContent: renderedItem.renderedContent,\n },\n };\n },\n\n toWidgetNotificationFromWebSocket(wsPayload: any): Notification {\n if (!validateWebSocketPayload(wsPayload)) {\n console.error('Invalid WebSocket payload structure, using fallback', wsPayload);\n const fallbackRenderedContent =\n wsPayload?.renderedContent && typeof wsPayload.renderedContent === 'object'\n ? wsPayload.renderedContent\n : {};\n const { subject, body } = extractSubjectAndBody(fallbackRenderedContent, wsPayload?.channel);\n return {\n id: wsPayload?.notificationId || `notification-${Date.now()}`,\n subject: subject || 'Notification',\n body: body || '',\n isRead: wsPayload?.read || false,\n isArchived: wsPayload?.archived || false,\n timestamp: wsPayload?.createdAt ? new Date(wsPayload.createdAt) : new Date(),\n tags: [],\n actions: [],\n metadata: {\n channel: wsPayload?.channel || 'in_app',\n renderedContent: fallbackRenderedContent,\n },\n };\n }\n\n const { subject, body } = extractSubjectAndBody(wsPayload.renderedContent, wsPayload.channel);\n\n return {\n id: wsPayload.notificationId,\n subject: subject || 'Notification',\n body: body || '',\n isRead: wsPayload.read || false,\n isArchived: wsPayload.archived || false,\n timestamp: wsPayload.createdAt ? new Date(wsPayload.createdAt) : new Date(),\n tags: [],\n actions: mapActionsToNotificationActions(\n wsPayload.renderedContent?.actions || wsPayload.actions, \n wsPayload.notificationId\n ),\n metadata: {\n channel: wsPayload.channel,\n renderedContent: wsPayload.renderedContent,\n },\n };\n },\n\n validateWebSocketPayload,\n };\n};\n"],"names":["useNotificationsClient","useLivePreferences","preferences","onPreferencesChange","onError","notificationClient","isSaving","setIsSaving","useState","error","setError","saveTimeoutRef","useRef","pendingUpdatesRef","updatePreference","useCallback","path","value","updatedPreferences","pathParts","current","i","sdkConfig","subscriberId","tenantId","environmentId","savePayload","sub","err","useEffect","validateWebSocketPayload","payload","field","mapSDKActionToWidgetAction","sdkAction","notificationId","onActionExecute","target","createNotificationMapper","extractSubjectAndBody","renderedContent","channel","subject","body","mapActionsToNotificationActions","actions","result","primaryAction","secondaryAction","renderedItem","wsPayload","fallbackRenderedContent"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";const u=require("react"),D=()=>window.__notificationSDK?.client,_=({preferences:t,onPreferencesChange:f,onError:e})=>{const n=D(),[c,i]=u.useState(!1),[r,l]=u.useState(null),p=u.useRef(),g=u.useRef({}),S=u.useCallback((v,A)=>{try{l(null);const a={...t},o=v.split(".");let h=a;for(let s=0;s<o.length-1;s++)h[o[s]]===void 0&&(h[o[s]]={}),h=h[o[s]];h[o[o.length-1]]=A,f(a),g.current[v]=A,p.current&&clearTimeout(p.current),i(!0),p.current=setTimeout(async()=>{try{if(!n)throw new Error("Notification client not available");const s=window.__notificationSDK?.config;if(!s)throw new Error("SDK configuration not available");const{subscriberId:b,tenantId:E,environmentId:y}=s;if(!b||!E||!y)throw new Error("SubscriberId, TenantId or EnvironmentId not available in SDK configuration.");const m={emailEnabled:a.channels.email,pushEnabled:a.channels.push,inAppEnabled:a.channels.inApp,smsEnabled:a.channels.sms};a.subscriptions.length>0&&(m.categories={},a.subscriptions.forEach(d=>{m.categories[d.workflowId]={emailEnabled:d.channels.email,pushEnabled:d.channels.push,inAppEnabled:d.channels.inApp,smsEnabled:d.channels.sms}})),await n.preferences.update(E,b,m),g.current={},l(null)}catch(s){const b=s instanceof Error?s:new Error("Failed to save preferences");process.env.NODE_ENV,l(b),e?.(b)}finally{i(!1)}},500)}catch(a){const o=a instanceof Error?a:new Error("Failed to update preference");l(o),e?.(o),i(!1)}},[t,f,n,e]);return u.useEffect(()=>()=>{p.current&&clearTimeout(p.current)},[]),{updatePreference:S,isSaving:c,error:r}},j=t=>!(!t||typeof t!="object"||["notificationId","channel","renderedContent","read","archived"].filter(n=>!(n in t)).length>0||typeof t.channel!="string"||typeof t.read!="boolean"||typeof t.archived!="boolean"||typeof t.renderedContent!="object"||t.renderedContent===null),N=(t,f,e)=>({type:"custom",label:t.label,icon:void 0,handler:async()=>{try{if(t.url){const n=t.target||"_self";window.open(t.url,n)}t.markAsReadOnClick}catch(n){throw n}}}),R=()=>{const t=(e,n)=>{if(!e||typeof e!="object")return{subject:"",body:""};let c="",i="";return e.push?(c=e.push.title||"",i=e.push.body||""):e.email?(c=e.email.subject||"",i=e.email.html||e.email.text||""):e.in_app?(c=e.in_app.title||"",i=e.in_app.message||""):e.sms?(c="SMS",i=e.sms.message||""):n==="in_app"?(c=e.title||"",i=e.message||""):(c=e.title||e.subject||"",i=e.message||e.body||e.text||""),{subject:c,body:i}},f=(e,n,c)=>{if(!e)return[];const i=[];if(Array.isArray(e))return e.forEach(r=>{i.push(N(r,n||"",c))}),i;if(e.primary){const r=e.primary;i.push({type:"custom",label:r.label||"Primary Action",icon:void 0,handler:async l=>{r.url&&window.open(r.url,"_self")}})}if(e.secondary){const r=e.secondary;i.push({type:"custom",label:r.label||"Secondary Action",icon:void 0,handler:async l=>{r.url&&window.open(r.url,"_self")}})}return i};return{toWidgetNotification(e){const{subject:n,body:c}=t(e.renderedContent,e.channel);return{id:e.notificationId,subject:n||"Notification",body:c||"",isRead:e.read,isArchived:e.archived,timestamp:e.createdAt?new Date(e.createdAt):new Date,tags:[],actions:f(e.renderedContent?.actions||e.actions),metadata:{channel:e.channel,renderedContent:e.renderedContent}}},toWidgetNotificationFromWebSocket(e){if(!j(e)){const i=e?.renderedContent&&typeof e.renderedContent=="object"?e.renderedContent:{},{subject:r,body:l}=t(i,e?.channel);return{id:e?.notificationId||`notification-${Date.now()}`,subject:r||"Notification",body:l||"",isRead:e?.read||!1,isArchived:e?.archived||!1,timestamp:e?.createdAt?new Date(e.createdAt):new Date,tags:[],actions:[],metadata:{channel:e?.channel||"in_app",renderedContent:i}}}const{subject:n,body:c}=t(e.renderedContent,e.channel);return{id:e.notificationId,subject:n||"Notification",body:c||"",isRead:e.read||!1,isArchived:e.archived||!1,timestamp:e.createdAt?new Date(e.createdAt):new Date,tags:[],actions:f(e.renderedContent?.actions||e.actions,e.notificationId),metadata:{channel:e.channel,renderedContent:e.renderedContent}}},validateWebSocketPayload:j}};exports.createNotificationMapper=R;exports.useLivePreferences=_;
|
|
2
|
+
//# sourceMappingURL=hooks-kLhwdW29.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks-kLhwdW29.cjs","sources":["../src/hooks/useLivePreferences.ts","../src/utils/notification-mapper.ts"],"sourcesContent":["import { useCallback, useRef, useState, useEffect } from 'react';\r\nimport { NotificationPreferences } from '../types/core';\r\n\r\nconst useNotificationsClient = () => {\r\n return (window as any).__notificationSDK?.client;\r\n};\r\n\r\nexport interface UseLivePreferencesProps {\r\n preferences: NotificationPreferences;\r\n onPreferencesChange: (preferences: NotificationPreferences) => void;\r\n onError?: (error: Error) => void;\r\n}\r\n\r\nexport interface UseLivePreferencesResult {\r\n updatePreference: (path: string, value: any) => void;\r\n isSaving: boolean;\r\n error: Error | null;\r\n}\r\n\r\nexport const useLivePreferences = ({\r\n preferences,\r\n onPreferencesChange,\r\n onError,\r\n}: UseLivePreferencesProps): UseLivePreferencesResult => {\r\n const notificationClient = useNotificationsClient();\r\n const [isSaving, setIsSaving] = useState(false);\r\n const [error, setError] = useState<Error | null>(null);\r\n const saveTimeoutRef = useRef<NodeJS.Timeout>();\r\n const pendingUpdatesRef = useRef<{ [key: string]: any }>({});\r\n\r\n const updatePreference = useCallback(\r\n (path: string, value: any) => {\r\n try {\r\n setError(null);\r\n\r\n const updatedPreferences = { ...preferences };\r\n const pathParts = path.split('.');\r\n\r\n let current: any = updatedPreferences;\r\n for (let i = 0; i < pathParts.length - 1; i++) {\r\n if (current[pathParts[i]] === undefined) {\r\n current[pathParts[i]] = {};\r\n }\r\n current = current[pathParts[i]];\r\n }\r\n current[pathParts[pathParts.length - 1]] = value;\r\n\r\n onPreferencesChange(updatedPreferences);\r\n\r\n pendingUpdatesRef.current[path] = value;\r\n\r\n if (saveTimeoutRef.current) {\r\n clearTimeout(saveTimeoutRef.current);\r\n }\r\n\r\n setIsSaving(true);\r\n\r\n saveTimeoutRef.current = setTimeout(async () => {\r\n try {\r\n if (!notificationClient) {\r\n throw new Error('Notification client not available');\r\n }\r\n\r\n const sdkConfig = (window as any).__notificationSDK?.config;\r\n\r\n if (!sdkConfig) {\r\n throw new Error('SDK configuration not available');\r\n }\r\n\r\n const { subscriberId, tenantId, environmentId } = sdkConfig;\r\n\r\n if (!subscriberId || !tenantId || !environmentId) {\r\n throw new Error('SubscriberId, TenantId or EnvironmentId not available in SDK configuration.');\r\n }\r\n\r\n const savePayload: any = {\r\n emailEnabled: updatedPreferences.channels.email,\r\n pushEnabled: updatedPreferences.channels.push,\r\n inAppEnabled: updatedPreferences.channels.inApp,\r\n smsEnabled: updatedPreferences.channels.sms,\r\n };\r\n\r\n if (updatedPreferences.subscriptions.length > 0) {\r\n savePayload.categories = {};\r\n updatedPreferences.subscriptions.forEach(sub => {\r\n savePayload.categories[sub.workflowId] = {\r\n emailEnabled: sub.channels.email,\r\n pushEnabled: sub.channels.push,\r\n inAppEnabled: sub.channels.inApp,\r\n smsEnabled: sub.channels.sms,\r\n };\r\n });\r\n }\r\n\r\n // NOTE: deliverySchedule removed as backend doesn't accept it (returns 400)\r\n // Quiet hours and weekdays management will be added when backend supports it\r\n\r\n await notificationClient.preferences.update(tenantId, subscriberId, savePayload);\r\n\r\n pendingUpdatesRef.current = {};\r\n setError(null);\r\n } catch (err: any) {\r\n const error = err instanceof Error ? err : new Error('Failed to save preferences');\r\n\r\n if (process.env.NODE_ENV === 'development') {\r\n console.error('Failed to save preferences:', error);\r\n }\r\n\r\n setError(error);\r\n onError?.(error);\r\n\r\n } finally {\r\n setIsSaving(false);\r\n }\r\n }, 500);\r\n } catch (err: any) {\r\n const error = err instanceof Error ? err : new Error('Failed to update preference');\r\n setError(error);\r\n onError?.(error);\r\n setIsSaving(false);\r\n }\r\n },\r\n [preferences, onPreferencesChange, notificationClient, onError]\r\n );\r\n\r\n useEffect(() => {\r\n return () => {\r\n if (saveTimeoutRef.current) {\r\n clearTimeout(saveTimeoutRef.current);\r\n }\r\n };\r\n }, []);\r\n\r\n return {\r\n updatePreference,\r\n isSaving,\r\n error,\r\n };\r\n};","import type { RenderedNotificationItem, RenderedNotificationActionMap, NotificationAction as SDKNotificationAction } from '@edusight/notification-sdk';\nimport type { Notification, NotificationAction } from '../types/core';\n\nexport interface WebSocketNotificationReceivedPayload extends RenderedNotificationItem {}\n\nexport interface NotificationMapper {\n toWidgetNotification(renderedItem: RenderedNotificationItem): Notification;\n toWidgetNotificationFromWebSocket(wsPayload: any): Notification;\n validateWebSocketPayload(payload: any): payload is WebSocketNotificationReceivedPayload;\n}\n\nconst validateWebSocketPayload = (payload: any): payload is WebSocketNotificationReceivedPayload => {\n if (!payload || typeof payload !== 'object') {\n console.warn('Invalid WebSocket payload: not an object', payload);\n return false;\n }\n\n const requiredFields = ['notificationId', 'channel', 'renderedContent', 'read', 'archived'];\n const missingFields = requiredFields.filter(field => !(field in payload));\n\n if (missingFields.length > 0) {\n console.warn('WebSocket payload missing required fields:', missingFields, payload);\n return false;\n }\n\n if (typeof payload.channel !== 'string') {\n console.warn('WebSocket payload channel is invalid', payload);\n return false;\n }\n\n if (typeof payload.read !== 'boolean' || typeof payload.archived !== 'boolean') {\n console.warn('WebSocket payload read/archived flags are invalid', payload);\n return false;\n }\n\n if (typeof payload.renderedContent !== 'object' || payload.renderedContent === null) {\n console.warn('WebSocket payload renderedContent is not an object', payload);\n return false;\n }\n\n return true;\n};\n\n\nconst mapSDKActionToWidgetAction = (\n sdkAction: SDKNotificationAction,\n notificationId: string,\n onActionExecute?: (actionId: string, notificationId: string) => Promise<void>\n): NotificationAction => {\n return {\n type: 'custom',\n label: sdkAction.label,\n icon: undefined,\n handler: async () => {\n try {\n if (onActionExecute) {\n await onActionExecute(sdkAction.id, notificationId);\n }\n\n if (sdkAction.url) {\n const target = sdkAction.target || '_self';\n window.open(sdkAction.url, target);\n }\n\n if (sdkAction.markAsReadOnClick) {\n // This will be handled by the widget's action handler\n }\n } catch (error) {\n console.error('Error executing action:', error);\n throw error;\n }\n },\n };\n};\n\nexport const createNotificationMapper = (): NotificationMapper => {\n const extractSubjectAndBody = (\n renderedContent: Record<string, any>,\n channel?: string\n ): { subject: string; body: string } => {\n if (!renderedContent || typeof renderedContent !== 'object') {\n return { subject: '', body: '' };\n }\n\n let subject = '';\n let body = '';\n\n if (renderedContent.push) {\n subject = renderedContent.push.title || '';\n body = renderedContent.push.body || '';\n } else if (renderedContent.email) {\n subject = renderedContent.email.subject || '';\n body = renderedContent.email.html || renderedContent.email.text || '';\n } else if (renderedContent.in_app) {\n subject = renderedContent.in_app.title || '';\n body = renderedContent.in_app.message || '';\n } else if (renderedContent.sms) {\n subject = 'SMS';\n body = renderedContent.sms.message || '';\n } else if (channel === 'in_app') {\n subject = renderedContent.title || '';\n body = renderedContent.message || '';\n } else {\n subject = renderedContent.title || renderedContent.subject || '';\n body = renderedContent.message || renderedContent.body || renderedContent.text || '';\n }\n\n return { subject, body };\n };\n\n const mapActionsToNotificationActions = (\n actions?: RenderedNotificationActionMap | SDKNotificationAction[],\n notificationId?: string,\n onActionExecute?: (actionId: string, notificationId: string) => Promise<void>\n ): NotificationAction[] => {\n if (!actions) return [];\n\n const result: NotificationAction[] = [];\n\n if (Array.isArray(actions)) {\n actions.forEach((sdkAction) => {\n result.push(mapSDKActionToWidgetAction(sdkAction, notificationId || '', onActionExecute));\n });\n return result;\n }\n\n if (actions.primary) {\n const primaryAction = actions.primary;\n result.push({\n type: 'custom',\n label: primaryAction.label || 'Primary Action',\n icon: undefined,\n handler: async (notificationId: string) => {\n if (primaryAction.url) {\n window.open(primaryAction.url, '_self');\n }\n if (onActionExecute) {\n await onActionExecute('primary', notificationId);\n }\n },\n });\n }\n\n if (actions.secondary) {\n const secondaryAction = actions.secondary;\n result.push({\n type: 'custom',\n label: secondaryAction.label || 'Secondary Action',\n icon: undefined,\n handler: async (notificationId: string) => {\n if (secondaryAction.url) {\n window.open(secondaryAction.url, '_self');\n }\n if (onActionExecute) {\n await onActionExecute('secondary', notificationId);\n }\n },\n });\n }\n\n return result;\n };\n\n return {\n toWidgetNotification(renderedItem: RenderedNotificationItem): Notification {\n const { subject, body } = extractSubjectAndBody(renderedItem.renderedContent, renderedItem.channel);\n\n return {\n id: renderedItem.notificationId,\n subject: subject || 'Notification',\n body: body || '',\n isRead: renderedItem.read,\n isArchived: renderedItem.archived,\n timestamp: renderedItem.createdAt ? new Date(renderedItem.createdAt) : new Date(),\n tags: [],\n actions: mapActionsToNotificationActions(\n renderedItem.renderedContent?.actions || renderedItem.actions\n ),\n metadata: {\n channel: renderedItem.channel,\n renderedContent: renderedItem.renderedContent,\n },\n };\n },\n\n toWidgetNotificationFromWebSocket(wsPayload: any): Notification {\n if (!validateWebSocketPayload(wsPayload)) {\n console.error('Invalid WebSocket payload structure, using fallback', wsPayload);\n const fallbackRenderedContent =\n wsPayload?.renderedContent && typeof wsPayload.renderedContent === 'object'\n ? wsPayload.renderedContent\n : {};\n const { subject, body } = extractSubjectAndBody(fallbackRenderedContent, wsPayload?.channel);\n return {\n id: wsPayload?.notificationId || `notification-${Date.now()}`,\n subject: subject || 'Notification',\n body: body || '',\n isRead: wsPayload?.read || false,\n isArchived: wsPayload?.archived || false,\n timestamp: wsPayload?.createdAt ? new Date(wsPayload.createdAt) : new Date(),\n tags: [],\n actions: [],\n metadata: {\n channel: wsPayload?.channel || 'in_app',\n renderedContent: fallbackRenderedContent,\n },\n };\n }\n\n const { subject, body } = extractSubjectAndBody(wsPayload.renderedContent, wsPayload.channel);\n\n return {\n id: wsPayload.notificationId,\n subject: subject || 'Notification',\n body: body || '',\n isRead: wsPayload.read || false,\n isArchived: wsPayload.archived || false,\n timestamp: wsPayload.createdAt ? new Date(wsPayload.createdAt) : new Date(),\n tags: [],\n actions: mapActionsToNotificationActions(\n wsPayload.renderedContent?.actions || wsPayload.actions, \n wsPayload.notificationId\n ),\n metadata: {\n channel: wsPayload.channel,\n renderedContent: wsPayload.renderedContent,\n },\n };\n },\n\n validateWebSocketPayload,\n };\n};\n"],"names":["useNotificationsClient","useLivePreferences","preferences","onPreferencesChange","onError","notificationClient","isSaving","setIsSaving","useState","error","setError","saveTimeoutRef","useRef","pendingUpdatesRef","updatePreference","useCallback","path","value","updatedPreferences","pathParts","current","i","sdkConfig","subscriberId","tenantId","environmentId","savePayload","sub","err","useEffect","validateWebSocketPayload","payload","field","mapSDKActionToWidgetAction","sdkAction","notificationId","onActionExecute","target","createNotificationMapper","extractSubjectAndBody","renderedContent","channel","subject","body","mapActionsToNotificationActions","actions","result","primaryAction","secondaryAction","renderedItem","wsPayload","fallbackRenderedContent"],"mappings":"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"}
|
package/dist/index.cjs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("react/jsx-runtime"),i=require("react"),F=require("socket.io-client"),U=require("@edusight/notification-sdk"),E=require("./components-KafszaqK.cjs"),v=require("./hooks-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("react/jsx-runtime"),i=require("react"),F=require("socket.io-client"),U=require("@edusight/notification-sdk"),E=require("./components-KafszaqK.cjs"),v=require("./hooks-kLhwdW29.cjs"),B=({error:t,onRetry:r})=>s.jsxs("div",{className:"p-4 bg-[var(--widget-error)]/10 border border-[var(--widget-error)]/20 rounded-lg",role:"alert","data-testid":"error-boundary-fallback",children:[s.jsxs("div",{className:"flex items-center mb-2",children:[s.jsx(E.MdError,{className:"w-6 h-6 text-[var(--widget-error)] mr-2","aria-hidden":"true"}),s.jsx("h3",{className:"text-sm font-medium text-[var(--widget-error)]",children:"Something went wrong"})]}),s.jsx("p",{className:"text-sm mb-3 text-[var(--widget-error)]/80",children:"The notification widget encountered an error and couldn't load properly."}),process.env.NODE_ENV==="development"&&t&&s.jsxs("details",{className:"mb-3",children:[s.jsx("summary",{className:"text-xs cursor-pointer text-[var(--widget-error)] hover:text-[var(--widget-error)]/80 transition-colors",children:"Error details (development only)"}),s.jsxs("pre",{className:"mt-2 text-xs bg-[var(--widget-error)]/5 p-2 rounded overflow-auto max-h-32 text-[var(--widget-error)]",children:[t.message,t.stack&&`
|
|
2
2
|
|
|
3
3
|
${t.stack}`]})]}),s.jsxs("button",{type:"button",className:"inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-[var(--widget-error)] bg-[var(--widget-error)]/10 hover:bg-[var(--widget-error)]/20 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[var(--widget-error)] transition-colors",onClick:r,"data-testid":"error-retry-button",children:[s.jsx(E.MdRefresh,{className:"mr-1 w-4 h-4","aria-hidden":"true"}),"Try again"]})]});class A extends i.Component{constructor(r){super(r),this.handleRetry=()=>{this.setState({hasError:!1,error:null,errorInfo:null})},this.state={hasError:!1,error:null,errorInfo:null}}static getDerivedStateFromError(r){return{hasError:!0,error:r}}componentDidCatch(r,u){this.setState({errorInfo:u}),this.props.onError&&this.props.onError(r,u)}render(){return this.state.hasError?s.jsx(B,{error:this.state.error,onRetry:this.handleRetry}):this.props.children}}const L=()=>window.__notificationSDK?.client,M=({onPreferencesLoaded:t,onError:r})=>{const u=L(),[w,c]=i.useState(!0),[o,n]=i.useState(null),[l,f]=i.useState(null),m=i.useCallback(async()=>{try{if(c(!0),n(null),!u)throw new Error("Notification client not available");const p=window.__notificationSDK?.config;if(!p)throw new Error("SDK configuration not available");const{subscriberId:S,tenantId:T,environmentId:b}=p;if(!S||!T||!b)throw new Error("SubscriberId, TenantId or EnvironmentId not available in config");const y=await u.preferences.get(T,S,b),k={channels:{email:y.emailEnabled??!0,push:y.pushEnabled??!0,sms:y.smsEnabled??!1,inApp:y.inAppEnabled??!0},subscriptions:[],deliverySchedule:{timezone:"UTC",quietHours:{start:y.quietHoursStart??"22:00",end:y.quietHoursEnd??"08:00"},weekdays:[!0,!0,!0,!0,!0,!1,!1]}};y.categories&&Object.entries(y.categories).forEach(([N,C])=>{k.subscriptions.push({workflowId:N,name:N,enabled:!0,channels:{email:C.emailEnabled??!0,push:C.pushEnabled??!0,sms:C.smsEnabled??!1,inApp:C.inAppEnabled??!0}})}),f(k),t(k)}catch(p){const S=p instanceof Error?p:new Error("Failed to load preferences");p?.response?.status===404||p?.status===404||(n(S),r?.(S));const b={channels:{email:!0,push:!0,sms:!1,inApp:!0},subscriptions:[],deliverySchedule:{timezone:"UTC",quietHours:{start:"22:00",end:"08:00"},weekdays:[!0,!0,!0,!0,!0,!1,!1]}};f(b),t(b)}finally{c(!1)}},[u,t,r]);return i.useEffect(()=>{u&&m()},[u,m]),{isLoading:w,error:o,preferences:l}},q=()=>window.__notificationSDK?.client,V=()=>{const t=q(),[r,u]=i.useState([]),[w,c]=i.useState(!0),[o,n]=i.useState(null),l=i.useCallback(async()=>{try{if(c(!0),n(null),!t)throw new Error("Notification client not available");const f=window.__notificationSDK?.config;if(!f)throw new Error("SDK configuration not available");const{tenantId:m,environmentId:p}=f;if(!m||!p)throw new Error("TenantId or EnvironmentId not available in config");const T=((await t.workflows.list({status:"active"},m,p))?.workflows||[]).map(b=>({workflowId:b.workflowId,name:b.name||b.workflowId,description:b.description}));u(T)}catch(f){const m=f instanceof Error?f:new Error("Failed to load workflows");n(m),u([])}finally{c(!1)}},[t]);return i.useEffect(()=>{t&&l()},[t,l]),{workflows:r,isLoading:w,error:o,refetch:l}},O=i.createContext(null),z=()=>{const t=i.useContext(O);if(!t)throw new Error("useSDK must be used within a NotificationWidget");return t},$={notifications:[],unreadCount:0,preferences:{channels:{email:!0,push:!0,sms:!1,inApp:!0},subscriptions:[],deliverySchedule:{timezone:"UTC",quietHours:{start:"22:00",end:"08:00"},weekdays:[!0,!0,!0,!0,!0,!1,!1]}},ui:{isOpen:!1,currentView:"notifications",selectedNotifications:[],isLoading:!1,error:null},websocket:{connected:!1,reconnecting:!1}},H=({config:t,children:r})=>{const[u,w]=i.useState({client:null,isInitialized:!1,error:null});return i.useEffect(()=>{let c=!0;return(async()=>{try{const n=window.__notificationSDK;if(n?.client&&JSON.stringify(n.config)===JSON.stringify(t)){c&&w({client:n.client,isInitialized:!0,error:null});return}const l=new U.NotificationClient({apiUrl:t.baseUrl,apiKey:t.apiKey,tenantId:t.tenantId,environmentId:t.environmentId});window.__notificationSDK={client:l,config:t},c&&w({client:l,isInitialized:!0,error:null})}catch(n){c&&w({client:null,isInitialized:!1,error:n})}})(),()=>{c=!1}},[t]),s.jsx(O.Provider,{value:u,children:r})},J=(t,r)=>{switch(r.type){case"SET_NOTIFICATIONS":return{...t,notifications:r.payload,unreadCount:r.payload.filter(o=>!o.isRead).length};case"ADD_NOTIFICATION":const u=[r.payload,...t.notifications];return{...t,notifications:u,unreadCount:u.filter(o=>!o.isRead).length};case"UPDATE_NOTIFICATION":const w=t.notifications.map(o=>o.id===r.payload.id?{...o,...r.payload.updates}:o);return{...t,notifications:w,unreadCount:w.filter(o=>!o.isRead).length};case"DELETE_NOTIFICATION":const c=t.notifications.filter(o=>o.id!==r.payload);return{...t,notifications:c,unreadCount:c.filter(o=>!o.isRead).length};case"SET_PREFERENCES":return{...t,preferences:r.payload};case"SET_UI_STATE":return{...t,ui:{...t.ui,...r.payload}};case"SET_WEBSOCKET_STATE":return{...t,websocket:{...t.websocket,...r.payload}};default:return t}},G=({position:t="right",size:r="medium",theme:u="light",className:w="",onError:c})=>{const[o,n]=i.useReducer(J,$),{client:l,isInitialized:f,error:m}=z(),p=i.useRef(null),S=i.useCallback(e=>{n({type:"SET_PREFERENCES",payload:e})},[]),T=i.useCallback(e=>{c&&c(e)},[c]),{error:b}=M({onPreferencesLoaded:S,onError:T}),{workflows:y,isLoading:k}=V(),{updatePreference:N,isSaving:C,error:x}=v.useLivePreferences({preferences:o.preferences,onPreferencesChange:S,onError:T});i.useEffect(()=>{if(!k&&y.length>0){const e=y.map(a=>o.preferences.subscriptions.find(g=>g.workflowId===a.workflowId)||{workflowId:a.workflowId,name:a.name,enabled:!0,channels:{email:!0,push:!0,sms:!1,inApp:!0}});JSON.stringify(e)!==JSON.stringify(o.preferences.subscriptions)&&n({type:"SET_PREFERENCES",payload:{...o.preferences,subscriptions:e}})}},[y,k,o.preferences]);const _=i.useCallback(e=>{try{const a=v.createNotificationMapper();switch(e.type){case"notification_received":{const d=e.data;if(!a.validateWebSocketPayload(d))break;const g=a.toWidgetNotificationFromWebSocket(d);n({type:"ADD_NOTIFICATION",payload:g});break}case"notification_updated":n({type:"UPDATE_NOTIFICATION",payload:{id:e.data.id||e.data.notificationId,updates:e.data}});break;case"notification_deleted":n({type:"DELETE_NOTIFICATION",payload:e.data.id||e.data.notificationId});break;case"preferences_updated":n({type:"SET_PREFERENCES",payload:e.data});break;default:}}catch{}},[]),D=i.useCallback(async()=>{if(!(!l||!f||p.current))try{n({type:"SET_WEBSOCKET_STATE",payload:{reconnecting:!0}});const e=window.__notificationSDK?.config;if(!e)throw new Error("SDK configuration not available for WebSocket connection");const g=`${l.getApiHost()}/v1/notifications`,h=F.io(g,{query:{tenantId:e.tenantId,subscriberId:e.subscriberId,environmentId:e.environmentId},transports:["websocket"],autoConnect:!1,reconnection:!0,reconnectionAttempts:10,reconnectionDelay:1e3,reconnectionDelayMax:5e3});p.current=h,h.on("notification",I=>{_({type:"notification_received",data:I})}),h.on("connect",()=>{n({type:"SET_WEBSOCKET_STATE",payload:{connected:!0,reconnecting:!1}})}),h.on("disconnect",I=>{n({type:"SET_WEBSOCKET_STATE",payload:{connected:!1,reconnecting:!1}})}),h.on("reconnect_attempt",I=>{n({type:"SET_WEBSOCKET_STATE",payload:{connected:!1,reconnecting:!0}})}),h.on("connect_error",I=>{n({type:"SET_WEBSOCKET_STATE",payload:{connected:!1,reconnecting:!1}}),c&&c(I)}),h.connect()}catch(e){n({type:"SET_WEBSOCKET_STATE",payload:{connected:!1,reconnecting:!1}}),c&&c(e)}},[l,f,_,c]),K=i.useCallback(()=>{if(p.current)try{p.current.disconnect&&p.current.disconnect(),p.current=null,n({type:"SET_WEBSOCKET_STATE",payload:{connected:!1,reconnecting:!1}})}catch{}},[]),j=i.useCallback(()=>{n({type:"SET_UI_STATE",payload:{isOpen:!o.ui.isOpen}})},[o.ui.isOpen]),W=i.useCallback(()=>{n({type:"SET_UI_STATE",payload:{isOpen:!1}})},[]),R=i.useCallback(e=>{n({type:"SET_UI_STATE",payload:{currentView:e}})},[]),P=i.useCallback(async(e,a)=>{if(!l||!f)return;const d=window.__notificationSDK?.config;if(d){if(!e)throw new Error("notificationId is required");try{switch(a.type){case"mark_read":await l.inbox.markAsRead(e,d.tenantId,d.environmentId,d.subscriberId);break;case"mark_unread":await l.inbox.markAsUnread(e,d.tenantId,d.environmentId,d.subscriberId);break;case"archive":await l.inbox.archive(e,d.tenantId,d.environmentId,d.subscriberId);break;case"delete":await l.inbox.delete(e,d.tenantId,d.environmentId,d.subscriberId);break;default:a.handler&&await a.handler(e);break}switch(a.type){case"mark_read":n({type:"UPDATE_NOTIFICATION",payload:{id:e,updates:{isRead:!0}}});break;case"mark_unread":n({type:"UPDATE_NOTIFICATION",payload:{id:e,updates:{isRead:!1}}});break;case"archive":n({type:"UPDATE_NOTIFICATION",payload:{id:e,updates:{isArchived:!0}}});break;case"delete":n({type:"DELETE_NOTIFICATION",payload:e});break}}catch(g){c&&c(g)}}},[l]);return i.useEffect(()=>{if(!l||!f)return;(async()=>{try{n({type:"SET_UI_STATE",payload:{isLoading:!0}});const a=window.__notificationSDK?.config;if(!a)throw new Error("SDK configuration not available");const d=v.createNotificationMapper(),h=((await l.inbox.getRenderedNotifications({channel:"in_app",limit:50,offset:0},a.tenantId,a.environmentId,a.subscriberId))?.items||[]).map(I=>d.toWidgetNotification(I));n({type:"SET_NOTIFICATIONS",payload:h}),n({type:"SET_UI_STATE",payload:{isLoading:!1,error:null}})}catch(a){n({type:"SET_UI_STATE",payload:{isLoading:!1,error:a}}),c&&c(a)}})()},[l,f,c]),i.useEffect(()=>(l&&f&&D(),()=>{K()}),[l]),i.useEffect(()=>{if(!l||!f||o.websocket.connected)return;const e=setInterval(async()=>{try{const a=window.__notificationSDK?.config;if(!a)return;const d=v.createNotificationMapper(),h=((await l.inbox.getRenderedNotifications({channel:"in_app",limit:50,offset:0},a.tenantId,a.environmentId,a.subscriberId))?.items||[]).map(I=>d.toWidgetNotification(I));n({type:"SET_NOTIFICATIONS",payload:h})}catch{}},3e4);return()=>clearInterval(e)},[l,o.websocket.connected]),i.useEffect(()=>{const e=a=>{if(a.key==="notification_widget_sync"&&a.newValue)try{const d=JSON.parse(a.newValue);_(d)}catch{}};return window.addEventListener("storage",e),()=>window.removeEventListener("storage",e)},[_]),i.useEffect(()=>{n({type:"SET_UI_STATE",payload:{isLoading:C}})},[C]),i.useEffect(()=>{const e=m||x||b;e&&n({type:"SET_UI_STATE",payload:{error:e}})},[m,x,b]),!f&&!m?s.jsx("div",{className:`relative inline-block ${w}`,"data-widget-size":r||"small","data-theme":u,"data-testid":"notification-widget",children:s.jsx(E.ComponentErrorBoundary,{componentName:"BellComponent",children:s.jsx(E.BellComponent,{unreadCount:0,onClick:()=>{},size:r,disabled:!0})})}):m?s.jsx("div",{className:`relative inline-block ${w}`,"data-widget-size":r||"small","data-theme":u,"data-testid":"notification-widget",children:s.jsx(E.ComponentErrorBoundary,{componentName:"BellComponent",fallback:s.jsx(E.SDKConnectionFallback,{error:m.message,onRetry:()=>window.location.reload()}),children:s.jsx(E.BellComponent,{unreadCount:0,onClick:()=>{},size:r,disabled:!0})})}):s.jsxs("div",{className:`relative inline-block ${w}`,"data-widget-size":r||"small","data-theme":u,"data-testid":"notification-widget",children:[s.jsx(E.ComponentErrorBoundary,{componentName:"BellComponent",children:s.jsx(E.BellComponent,{unreadCount:o.unreadCount,onClick:j,size:r,disabled:o.ui.isLoading})}),s.jsx(E.ComponentErrorBoundary,{componentName:"InboxPopover",fallback:s.jsx(E.LoadingFallback,{message:"Unable to load notifications"}),children:s.jsx(E.InboxPopover,{isOpen:o.ui.isOpen,onClose:W,position:t,currentView:o.ui.currentView,onViewChange:R,notifications:o.notifications,onNotificationAction:P,preferences:o.preferences,onPreferenceChange:N,isPreferencesLoading:C})})]})},Q=({sdkConfig:t,...r})=>s.jsx(A,{onError:r.onError,children:s.jsx(H,{config:t,children:s.jsx(G,{...r})})}),X="3.0.0",Y="@edusight/notification-widget";exports.BellComponent=E.BellComponent;exports.InboxPopover=E.InboxPopover;exports.NotificationItem=E.NotificationItem;exports.PreferencesView=E.PreferencesView;exports.NotificationWidget=Q;exports.NotificationWidgetErrorBoundary=A;exports.VERSION=X;exports.WIDGET_NAME=Y;
|
|
4
4
|
//# sourceMappingURL=index.cjs.js.map
|
package/dist/index.esm.js
CHANGED
|
@@ -4,7 +4,7 @@ import { io as J } from "socket.io-client";
|
|
|
4
4
|
import { NotificationClient as j } from "@edusight/notification-sdk";
|
|
5
5
|
import { M as G, a as Q, C as O, B as D, S as X, I as Y, L as Z } from "./components-D8XxiFEB.js";
|
|
6
6
|
import { N as Te, P as _e } from "./components-D8XxiFEB.js";
|
|
7
|
-
import { u as ee, c as x } from "./hooks-
|
|
7
|
+
import { u as ee, c as x } from "./hooks-BTG0z6yd.js";
|
|
8
8
|
const te = ({ error: t, onRetry: r }) => /* @__PURE__ */ k(
|
|
9
9
|
"div",
|
|
10
10
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edusight/notification-widget",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.32",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "React notification center widget for EduSight Notification Service, aligned with Novu's React UI and functionalities",
|
|
6
6
|
"main": "./dist/index.cjs.js",
|
package/dist/hooks-CggRWw6Q.cjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";const f=require("react"),y=()=>window.__notificationSDK?.client,N=({preferences:t,onPreferencesChange:o,onError:e})=>{const n=y(),[c,i]=f.useState(!1),[u,l]=f.useState(null),p=f.useRef(),g=f.useRef({}),S=f.useCallback((v,E)=>{try{l(null);const r={...t},s=v.split(".");let h=r;for(let a=0;a<s.length-1;a++)h[s[a]]===void 0&&(h[s[a]]={}),h=h[s[a]];h[s[s.length-1]]=E,o(r),g.current[v]=E,p.current&&clearTimeout(p.current),i(!0),p.current=setTimeout(async()=>{try{if(!n)throw new Error("Notification client not available");const a=window.__notificationSDK?.config;if(!a)throw new Error("SDK configuration not available");const{subscriberId:b,tenantId:A,environmentId:D}=a;if(!b||!A||!D)throw new Error("SubscriberId, TenantId or EnvironmentId not available in SDK configuration.");const d={emailEnabled:r.channels.email,pushEnabled:r.channels.push,inAppEnabled:r.channels.inApp,smsEnabled:r.channels.sms};r.subscriptions.length>0&&(d.categories={},r.subscriptions.forEach(m=>{d.categories[m.workflowId]={emailEnabled:m.channels.email,pushEnabled:m.channels.push,inAppEnabled:m.channels.inApp,smsEnabled:m.channels.sms}})),await n.preferences.update(A,b,d),g.current={},l(null)}catch(a){const b=a instanceof Error?a:new Error("Failed to save preferences");process.env.NODE_ENV,l(b),e?.(b)}finally{i(!1)}},500)}catch(r){const s=r instanceof Error?r:new Error("Failed to update preference");l(s),e?.(s),i(!1)}},[t,o,n,e]);return f.useEffect(()=>()=>{p.current&&clearTimeout(p.current)},[]),{updatePreference:S,isSaving:c,error:u}},j=t=>!(!t||typeof t!="object"||["notificationId","channel","renderedContent","read","archived"].filter(n=>!(n in t)).length>0||typeof t.channel!="string"||typeof t.read!="boolean"||typeof t.archived!="boolean"||typeof t.renderedContent!="object"||t.renderedContent===null),_=(t,o,e)=>({type:"custom",label:t.label,icon:void 0,handler:async()=>{try{if(t.url){const n=t.target||"_self";window.open(t.url,n)}t.markAsReadOnClick}catch(n){throw n}}}),R=()=>{const t=(e,n)=>{if(!e||typeof e!="object")return{subject:"",body:""};let c="",i="";return e.push?(c=e.push.title||"",i=e.push.body||""):e.email?(c=e.email.subject||"",i=e.email.html||e.email.text||""):e.in_app?(c=e.in_app.title||"",i=e.in_app.message||""):e.sms?(c="SMS",i=e.sms.message||""):n==="in_app"?(c=e.title||"",i=e.message||""):(c=e.title||e.subject||"",i=e.message||e.body||e.text||""),{subject:c,body:i}},o=(e,n,c)=>{if(!e)return[];const i=[];return Array.isArray(e)?(e.forEach(u=>{i.push(_(u,n||"",c))}),i):(e.primary&&i.push({type:"custom",label:e.primary.label||"Primary Action",icon:void 0,handler:async()=>{}}),e.secondary&&i.push({type:"custom",label:e.secondary.label||"Secondary Action",icon:void 0,handler:async()=>{}}),i)};return{toWidgetNotification(e){const{subject:n,body:c}=t(e.renderedContent,e.channel);return{id:e.notificationId,subject:n||"Notification",body:c||"",isRead:e.read,isArchived:e.archived,timestamp:e.createdAt?new Date(e.createdAt):new Date,tags:[],actions:o(e.actions),metadata:{channel:e.channel,renderedContent:e.renderedContent}}},toWidgetNotificationFromWebSocket(e){if(!j(e)){const i=e?.renderedContent&&typeof e.renderedContent=="object"?e.renderedContent:{},{subject:u,body:l}=t(i,e?.channel);return{id:e?.notificationId||`notification-${Date.now()}`,subject:u||"Notification",body:l||"",isRead:e?.read||!1,isArchived:e?.archived||!1,timestamp:e?.createdAt?new Date(e.createdAt):new Date,tags:[],actions:[],metadata:{channel:e?.channel||"in_app",renderedContent:i}}}const{subject:n,body:c}=t(e.renderedContent,e.channel);return{id:e.notificationId,subject:n||"Notification",body:c||"",isRead:e.read||!1,isArchived:e.archived||!1,timestamp:e.createdAt?new Date(e.createdAt):new Date,tags:[],actions:o(e.actions,e.notificationId),metadata:{channel:e.channel,renderedContent:e.renderedContent}}},validateWebSocketPayload:j}};exports.createNotificationMapper=R;exports.useLivePreferences=N;
|
|
2
|
-
//# sourceMappingURL=hooks-CggRWw6Q.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hooks-CggRWw6Q.cjs","sources":["../src/hooks/useLivePreferences.ts","../src/utils/notification-mapper.ts"],"sourcesContent":["import { useCallback, useRef, useState, useEffect } from 'react';\r\nimport { NotificationPreferences } from '../types/core';\r\n\r\nconst useNotificationsClient = () => {\r\n return (window as any).__notificationSDK?.client;\r\n};\r\n\r\nexport interface UseLivePreferencesProps {\r\n preferences: NotificationPreferences;\r\n onPreferencesChange: (preferences: NotificationPreferences) => void;\r\n onError?: (error: Error) => void;\r\n}\r\n\r\nexport interface UseLivePreferencesResult {\r\n updatePreference: (path: string, value: any) => void;\r\n isSaving: boolean;\r\n error: Error | null;\r\n}\r\n\r\nexport const useLivePreferences = ({\r\n preferences,\r\n onPreferencesChange,\r\n onError,\r\n}: UseLivePreferencesProps): UseLivePreferencesResult => {\r\n const notificationClient = useNotificationsClient();\r\n const [isSaving, setIsSaving] = useState(false);\r\n const [error, setError] = useState<Error | null>(null);\r\n const saveTimeoutRef = useRef<NodeJS.Timeout>();\r\n const pendingUpdatesRef = useRef<{ [key: string]: any }>({});\r\n\r\n const updatePreference = useCallback(\r\n (path: string, value: any) => {\r\n try {\r\n setError(null);\r\n\r\n const updatedPreferences = { ...preferences };\r\n const pathParts = path.split('.');\r\n\r\n let current: any = updatedPreferences;\r\n for (let i = 0; i < pathParts.length - 1; i++) {\r\n if (current[pathParts[i]] === undefined) {\r\n current[pathParts[i]] = {};\r\n }\r\n current = current[pathParts[i]];\r\n }\r\n current[pathParts[pathParts.length - 1]] = value;\r\n\r\n onPreferencesChange(updatedPreferences);\r\n\r\n pendingUpdatesRef.current[path] = value;\r\n\r\n if (saveTimeoutRef.current) {\r\n clearTimeout(saveTimeoutRef.current);\r\n }\r\n\r\n setIsSaving(true);\r\n\r\n saveTimeoutRef.current = setTimeout(async () => {\r\n try {\r\n if (!notificationClient) {\r\n throw new Error('Notification client not available');\r\n }\r\n\r\n const sdkConfig = (window as any).__notificationSDK?.config;\r\n\r\n if (!sdkConfig) {\r\n throw new Error('SDK configuration not available');\r\n }\r\n\r\n const { subscriberId, tenantId, environmentId } = sdkConfig;\r\n\r\n if (!subscriberId || !tenantId || !environmentId) {\r\n throw new Error('SubscriberId, TenantId or EnvironmentId not available in SDK configuration.');\r\n }\r\n\r\n const savePayload: any = {\r\n emailEnabled: updatedPreferences.channels.email,\r\n pushEnabled: updatedPreferences.channels.push,\r\n inAppEnabled: updatedPreferences.channels.inApp,\r\n smsEnabled: updatedPreferences.channels.sms,\r\n };\r\n\r\n if (updatedPreferences.subscriptions.length > 0) {\r\n savePayload.categories = {};\r\n updatedPreferences.subscriptions.forEach(sub => {\r\n savePayload.categories[sub.workflowId] = {\r\n emailEnabled: sub.channels.email,\r\n pushEnabled: sub.channels.push,\r\n inAppEnabled: sub.channels.inApp,\r\n smsEnabled: sub.channels.sms,\r\n };\r\n });\r\n }\r\n\r\n // NOTE: deliverySchedule removed as backend doesn't accept it (returns 400)\r\n // Quiet hours and weekdays management will be added when backend supports it\r\n\r\n await notificationClient.preferences.update(tenantId, subscriberId, savePayload);\r\n\r\n pendingUpdatesRef.current = {};\r\n setError(null);\r\n } catch (err: any) {\r\n const error = err instanceof Error ? err : new Error('Failed to save preferences');\r\n\r\n if (process.env.NODE_ENV === 'development') {\r\n console.error('Failed to save preferences:', error);\r\n }\r\n\r\n setError(error);\r\n onError?.(error);\r\n\r\n } finally {\r\n setIsSaving(false);\r\n }\r\n }, 500);\r\n } catch (err: any) {\r\n const error = err instanceof Error ? err : new Error('Failed to update preference');\r\n setError(error);\r\n onError?.(error);\r\n setIsSaving(false);\r\n }\r\n },\r\n [preferences, onPreferencesChange, notificationClient, onError]\r\n );\r\n\r\n useEffect(() => {\r\n return () => {\r\n if (saveTimeoutRef.current) {\r\n clearTimeout(saveTimeoutRef.current);\r\n }\r\n };\r\n }, []);\r\n\r\n return {\r\n updatePreference,\r\n isSaving,\r\n error,\r\n };\r\n};","import type { RenderedNotificationItem, RenderedNotificationActionMap, NotificationAction as SDKNotificationAction } from '@edusight/notification-sdk';\r\nimport type { Notification, NotificationAction } from '../types/core';\r\n\r\nexport interface WebSocketNotificationReceivedPayload extends RenderedNotificationItem {}\r\n\r\nexport interface NotificationMapper {\r\n toWidgetNotification(renderedItem: RenderedNotificationItem): Notification;\r\n toWidgetNotificationFromWebSocket(wsPayload: any): Notification;\r\n validateWebSocketPayload(payload: any): payload is WebSocketNotificationReceivedPayload;\r\n}\r\n\r\nconst validateWebSocketPayload = (payload: any): payload is WebSocketNotificationReceivedPayload => {\r\n if (!payload || typeof payload !== 'object') {\r\n console.warn('Invalid WebSocket payload: not an object', payload);\r\n return false;\r\n }\r\n\r\n const requiredFields = ['notificationId', 'channel', 'renderedContent', 'read', 'archived'];\r\n const missingFields = requiredFields.filter(field => !(field in payload));\r\n\r\n if (missingFields.length > 0) {\r\n console.warn('WebSocket payload missing required fields:', missingFields, payload);\r\n return false;\r\n }\r\n\r\n if (typeof payload.channel !== 'string') {\r\n console.warn('WebSocket payload channel is invalid', payload);\r\n return false;\r\n }\r\n\r\n if (typeof payload.read !== 'boolean' || typeof payload.archived !== 'boolean') {\r\n console.warn('WebSocket payload read/archived flags are invalid', payload);\r\n return false;\r\n }\r\n\r\n if (typeof payload.renderedContent !== 'object' || payload.renderedContent === null) {\r\n console.warn('WebSocket payload renderedContent is not an object', payload);\r\n return false;\r\n }\r\n\r\n return true;\r\n};\r\n\r\n\r\nconst mapSDKActionToWidgetAction = (\r\n sdkAction: SDKNotificationAction,\r\n notificationId: string,\r\n onActionExecute?: (actionId: string, notificationId: string) => Promise<void>\r\n): NotificationAction => {\r\n return {\r\n type: 'custom',\r\n label: sdkAction.label,\r\n icon: undefined,\r\n handler: async () => {\r\n try {\r\n if (onActionExecute) {\r\n await onActionExecute(sdkAction.id, notificationId);\r\n }\r\n\r\n if (sdkAction.url) {\r\n const target = sdkAction.target || '_self';\r\n window.open(sdkAction.url, target);\r\n }\r\n\r\n if (sdkAction.markAsReadOnClick) {\r\n // This will be handled by the widget's action handler\r\n }\r\n } catch (error) {\r\n console.error('Error executing action:', error);\r\n throw error;\r\n }\r\n },\r\n };\r\n};\r\n\r\nexport const createNotificationMapper = (): NotificationMapper => {\r\n const extractSubjectAndBody = (\r\n renderedContent: Record<string, any>,\r\n channel?: string\r\n ): { subject: string; body: string } => {\r\n if (!renderedContent || typeof renderedContent !== 'object') {\r\n return { subject: '', body: '' };\r\n }\r\n\r\n let subject = '';\r\n let body = '';\r\n\r\n if (renderedContent.push) {\r\n subject = renderedContent.push.title || '';\r\n body = renderedContent.push.body || '';\r\n } else if (renderedContent.email) {\r\n subject = renderedContent.email.subject || '';\r\n body = renderedContent.email.html || renderedContent.email.text || '';\r\n } else if (renderedContent.in_app) {\r\n subject = renderedContent.in_app.title || '';\r\n body = renderedContent.in_app.message || '';\r\n } else if (renderedContent.sms) {\r\n subject = 'SMS';\r\n body = renderedContent.sms.message || '';\r\n } else if (channel === 'in_app') {\r\n subject = renderedContent.title || '';\r\n body = renderedContent.message || '';\r\n } else {\r\n subject = renderedContent.title || renderedContent.subject || '';\r\n body = renderedContent.message || renderedContent.body || renderedContent.text || '';\r\n }\r\n\r\n return { subject, body };\r\n };\r\n\r\n const mapActionsToNotificationActions = (\r\n actions?: RenderedNotificationActionMap | SDKNotificationAction[],\r\n notificationId?: string,\r\n onActionExecute?: (actionId: string, notificationId: string) => Promise<void>\r\n ): NotificationAction[] => {\r\n if (!actions) return [];\r\n\r\n const result: NotificationAction[] = [];\r\n\r\n if (Array.isArray(actions)) {\r\n actions.forEach((sdkAction) => {\r\n result.push(mapSDKActionToWidgetAction(sdkAction, notificationId || '', onActionExecute));\r\n });\r\n return result;\r\n }\r\n\r\n if (actions.primary) {\r\n result.push({\r\n type: 'custom',\r\n label: actions.primary.label || 'Primary Action',\r\n icon: undefined,\r\n handler: async () => {\r\n },\r\n });\r\n }\r\n\r\n if (actions.secondary) {\r\n result.push({\r\n type: 'custom',\r\n label: actions.secondary.label || 'Secondary Action',\r\n icon: undefined,\r\n handler: async () => {\r\n },\r\n });\r\n }\r\n\r\n return result;\r\n };\r\n\r\n return {\r\n toWidgetNotification(renderedItem: RenderedNotificationItem): Notification {\r\n const { subject, body } = extractSubjectAndBody(renderedItem.renderedContent, renderedItem.channel);\r\n\r\n return {\r\n id: renderedItem.notificationId,\r\n subject: subject || 'Notification',\r\n body: body || '',\r\n isRead: renderedItem.read,\r\n isArchived: renderedItem.archived,\r\n timestamp: renderedItem.createdAt ? new Date(renderedItem.createdAt) : new Date(),\r\n tags: [],\r\n actions: mapActionsToNotificationActions(renderedItem.actions),\r\n metadata: {\r\n channel: renderedItem.channel,\r\n renderedContent: renderedItem.renderedContent,\r\n },\r\n };\r\n },\r\n\r\n toWidgetNotificationFromWebSocket(wsPayload: any): Notification {\r\n if (!validateWebSocketPayload(wsPayload)) {\r\n console.error('Invalid WebSocket payload structure, using fallback', wsPayload);\r\n const fallbackRenderedContent =\r\n wsPayload?.renderedContent && typeof wsPayload.renderedContent === 'object'\r\n ? wsPayload.renderedContent\r\n : {};\r\n const { subject, body } = extractSubjectAndBody(fallbackRenderedContent, wsPayload?.channel);\r\n return {\r\n id: wsPayload?.notificationId || `notification-${Date.now()}`,\r\n subject: subject || 'Notification',\r\n body: body || '',\r\n isRead: wsPayload?.read || false,\r\n isArchived: wsPayload?.archived || false,\r\n timestamp: wsPayload?.createdAt ? new Date(wsPayload.createdAt) : new Date(),\r\n tags: [],\r\n actions: [],\r\n metadata: {\r\n channel: wsPayload?.channel || 'in_app',\r\n renderedContent: fallbackRenderedContent,\r\n },\r\n };\r\n }\r\n\r\n const { subject, body } = extractSubjectAndBody(wsPayload.renderedContent, wsPayload.channel);\r\n\r\n return {\r\n id: wsPayload.notificationId,\r\n subject: subject || 'Notification',\r\n body: body || '',\r\n isRead: wsPayload.read || false,\r\n isArchived: wsPayload.archived || false,\r\n timestamp: wsPayload.createdAt ? new Date(wsPayload.createdAt) : new Date(),\r\n tags: [],\r\n actions: mapActionsToNotificationActions(wsPayload.actions, wsPayload.notificationId),\r\n metadata: {\r\n channel: wsPayload.channel,\r\n renderedContent: wsPayload.renderedContent,\r\n },\r\n };\r\n },\r\n\r\n validateWebSocketPayload,\r\n };\r\n};\r\n"],"names":["useNotificationsClient","useLivePreferences","preferences","onPreferencesChange","onError","notificationClient","isSaving","setIsSaving","useState","error","setError","saveTimeoutRef","useRef","pendingUpdatesRef","updatePreference","useCallback","path","value","updatedPreferences","pathParts","current","i","sdkConfig","subscriberId","tenantId","environmentId","savePayload","sub","err","useEffect","validateWebSocketPayload","payload","field","mapSDKActionToWidgetAction","sdkAction","notificationId","onActionExecute","target","createNotificationMapper","extractSubjectAndBody","renderedContent","channel","subject","body","mapActionsToNotificationActions","actions","result","renderedItem","wsPayload","fallbackRenderedContent"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hooks-Cv5k48VE.js","sources":["../src/hooks/useLivePreferences.ts","../src/utils/notification-mapper.ts"],"sourcesContent":["import { useCallback, useRef, useState, useEffect } from 'react';\r\nimport { NotificationPreferences } from '../types/core';\r\n\r\nconst useNotificationsClient = () => {\r\n return (window as any).__notificationSDK?.client;\r\n};\r\n\r\nexport interface UseLivePreferencesProps {\r\n preferences: NotificationPreferences;\r\n onPreferencesChange: (preferences: NotificationPreferences) => void;\r\n onError?: (error: Error) => void;\r\n}\r\n\r\nexport interface UseLivePreferencesResult {\r\n updatePreference: (path: string, value: any) => void;\r\n isSaving: boolean;\r\n error: Error | null;\r\n}\r\n\r\nexport const useLivePreferences = ({\r\n preferences,\r\n onPreferencesChange,\r\n onError,\r\n}: UseLivePreferencesProps): UseLivePreferencesResult => {\r\n const notificationClient = useNotificationsClient();\r\n const [isSaving, setIsSaving] = useState(false);\r\n const [error, setError] = useState<Error | null>(null);\r\n const saveTimeoutRef = useRef<NodeJS.Timeout>();\r\n const pendingUpdatesRef = useRef<{ [key: string]: any }>({});\r\n\r\n const updatePreference = useCallback(\r\n (path: string, value: any) => {\r\n try {\r\n setError(null);\r\n\r\n const updatedPreferences = { ...preferences };\r\n const pathParts = path.split('.');\r\n\r\n let current: any = updatedPreferences;\r\n for (let i = 0; i < pathParts.length - 1; i++) {\r\n if (current[pathParts[i]] === undefined) {\r\n current[pathParts[i]] = {};\r\n }\r\n current = current[pathParts[i]];\r\n }\r\n current[pathParts[pathParts.length - 1]] = value;\r\n\r\n onPreferencesChange(updatedPreferences);\r\n\r\n pendingUpdatesRef.current[path] = value;\r\n\r\n if (saveTimeoutRef.current) {\r\n clearTimeout(saveTimeoutRef.current);\r\n }\r\n\r\n setIsSaving(true);\r\n\r\n saveTimeoutRef.current = setTimeout(async () => {\r\n try {\r\n if (!notificationClient) {\r\n throw new Error('Notification client not available');\r\n }\r\n\r\n const sdkConfig = (window as any).__notificationSDK?.config;\r\n\r\n if (!sdkConfig) {\r\n throw new Error('SDK configuration not available');\r\n }\r\n\r\n const { subscriberId, tenantId, environmentId } = sdkConfig;\r\n\r\n if (!subscriberId || !tenantId || !environmentId) {\r\n throw new Error('SubscriberId, TenantId or EnvironmentId not available in SDK configuration.');\r\n }\r\n\r\n const savePayload: any = {\r\n emailEnabled: updatedPreferences.channels.email,\r\n pushEnabled: updatedPreferences.channels.push,\r\n inAppEnabled: updatedPreferences.channels.inApp,\r\n smsEnabled: updatedPreferences.channels.sms,\r\n };\r\n\r\n if (updatedPreferences.subscriptions.length > 0) {\r\n savePayload.categories = {};\r\n updatedPreferences.subscriptions.forEach(sub => {\r\n savePayload.categories[sub.workflowId] = {\r\n emailEnabled: sub.channels.email,\r\n pushEnabled: sub.channels.push,\r\n inAppEnabled: sub.channels.inApp,\r\n smsEnabled: sub.channels.sms,\r\n };\r\n });\r\n }\r\n\r\n // NOTE: deliverySchedule removed as backend doesn't accept it (returns 400)\r\n // Quiet hours and weekdays management will be added when backend supports it\r\n\r\n await notificationClient.preferences.update(tenantId, subscriberId, savePayload);\r\n\r\n pendingUpdatesRef.current = {};\r\n setError(null);\r\n } catch (err: any) {\r\n const error = err instanceof Error ? err : new Error('Failed to save preferences');\r\n\r\n if (process.env.NODE_ENV === 'development') {\r\n console.error('Failed to save preferences:', error);\r\n }\r\n\r\n setError(error);\r\n onError?.(error);\r\n\r\n } finally {\r\n setIsSaving(false);\r\n }\r\n }, 500);\r\n } catch (err: any) {\r\n const error = err instanceof Error ? err : new Error('Failed to update preference');\r\n setError(error);\r\n onError?.(error);\r\n setIsSaving(false);\r\n }\r\n },\r\n [preferences, onPreferencesChange, notificationClient, onError]\r\n );\r\n\r\n useEffect(() => {\r\n return () => {\r\n if (saveTimeoutRef.current) {\r\n clearTimeout(saveTimeoutRef.current);\r\n }\r\n };\r\n }, []);\r\n\r\n return {\r\n updatePreference,\r\n isSaving,\r\n error,\r\n };\r\n};","import type { RenderedNotificationItem, RenderedNotificationActionMap, NotificationAction as SDKNotificationAction } from '@edusight/notification-sdk';\r\nimport type { Notification, NotificationAction } from '../types/core';\r\n\r\nexport interface WebSocketNotificationReceivedPayload extends RenderedNotificationItem {}\r\n\r\nexport interface NotificationMapper {\r\n toWidgetNotification(renderedItem: RenderedNotificationItem): Notification;\r\n toWidgetNotificationFromWebSocket(wsPayload: any): Notification;\r\n validateWebSocketPayload(payload: any): payload is WebSocketNotificationReceivedPayload;\r\n}\r\n\r\nconst validateWebSocketPayload = (payload: any): payload is WebSocketNotificationReceivedPayload => {\r\n if (!payload || typeof payload !== 'object') {\r\n console.warn('Invalid WebSocket payload: not an object', payload);\r\n return false;\r\n }\r\n\r\n const requiredFields = ['notificationId', 'channel', 'renderedContent', 'read', 'archived'];\r\n const missingFields = requiredFields.filter(field => !(field in payload));\r\n\r\n if (missingFields.length > 0) {\r\n console.warn('WebSocket payload missing required fields:', missingFields, payload);\r\n return false;\r\n }\r\n\r\n if (typeof payload.channel !== 'string') {\r\n console.warn('WebSocket payload channel is invalid', payload);\r\n return false;\r\n }\r\n\r\n if (typeof payload.read !== 'boolean' || typeof payload.archived !== 'boolean') {\r\n console.warn('WebSocket payload read/archived flags are invalid', payload);\r\n return false;\r\n }\r\n\r\n if (typeof payload.renderedContent !== 'object' || payload.renderedContent === null) {\r\n console.warn('WebSocket payload renderedContent is not an object', payload);\r\n return false;\r\n }\r\n\r\n return true;\r\n};\r\n\r\n\r\nconst mapSDKActionToWidgetAction = (\r\n sdkAction: SDKNotificationAction,\r\n notificationId: string,\r\n onActionExecute?: (actionId: string, notificationId: string) => Promise<void>\r\n): NotificationAction => {\r\n return {\r\n type: 'custom',\r\n label: sdkAction.label,\r\n icon: undefined,\r\n handler: async () => {\r\n try {\r\n if (onActionExecute) {\r\n await onActionExecute(sdkAction.id, notificationId);\r\n }\r\n\r\n if (sdkAction.url) {\r\n const target = sdkAction.target || '_self';\r\n window.open(sdkAction.url, target);\r\n }\r\n\r\n if (sdkAction.markAsReadOnClick) {\r\n // This will be handled by the widget's action handler\r\n }\r\n } catch (error) {\r\n console.error('Error executing action:', error);\r\n throw error;\r\n }\r\n },\r\n };\r\n};\r\n\r\nexport const createNotificationMapper = (): NotificationMapper => {\r\n const extractSubjectAndBody = (\r\n renderedContent: Record<string, any>,\r\n channel?: string\r\n ): { subject: string; body: string } => {\r\n if (!renderedContent || typeof renderedContent !== 'object') {\r\n return { subject: '', body: '' };\r\n }\r\n\r\n let subject = '';\r\n let body = '';\r\n\r\n if (renderedContent.push) {\r\n subject = renderedContent.push.title || '';\r\n body = renderedContent.push.body || '';\r\n } else if (renderedContent.email) {\r\n subject = renderedContent.email.subject || '';\r\n body = renderedContent.email.html || renderedContent.email.text || '';\r\n } else if (renderedContent.in_app) {\r\n subject = renderedContent.in_app.title || '';\r\n body = renderedContent.in_app.message || '';\r\n } else if (renderedContent.sms) {\r\n subject = 'SMS';\r\n body = renderedContent.sms.message || '';\r\n } else if (channel === 'in_app') {\r\n subject = renderedContent.title || '';\r\n body = renderedContent.message || '';\r\n } else {\r\n subject = renderedContent.title || renderedContent.subject || '';\r\n body = renderedContent.message || renderedContent.body || renderedContent.text || '';\r\n }\r\n\r\n return { subject, body };\r\n };\r\n\r\n const mapActionsToNotificationActions = (\r\n actions?: RenderedNotificationActionMap | SDKNotificationAction[],\r\n notificationId?: string,\r\n onActionExecute?: (actionId: string, notificationId: string) => Promise<void>\r\n ): NotificationAction[] => {\r\n if (!actions) return [];\r\n\r\n const result: NotificationAction[] = [];\r\n\r\n if (Array.isArray(actions)) {\r\n actions.forEach((sdkAction) => {\r\n result.push(mapSDKActionToWidgetAction(sdkAction, notificationId || '', onActionExecute));\r\n });\r\n return result;\r\n }\r\n\r\n if (actions.primary) {\r\n result.push({\r\n type: 'custom',\r\n label: actions.primary.label || 'Primary Action',\r\n icon: undefined,\r\n handler: async () => {\r\n },\r\n });\r\n }\r\n\r\n if (actions.secondary) {\r\n result.push({\r\n type: 'custom',\r\n label: actions.secondary.label || 'Secondary Action',\r\n icon: undefined,\r\n handler: async () => {\r\n },\r\n });\r\n }\r\n\r\n return result;\r\n };\r\n\r\n return {\r\n toWidgetNotification(renderedItem: RenderedNotificationItem): Notification {\r\n const { subject, body } = extractSubjectAndBody(renderedItem.renderedContent, renderedItem.channel);\r\n\r\n return {\r\n id: renderedItem.notificationId,\r\n subject: subject || 'Notification',\r\n body: body || '',\r\n isRead: renderedItem.read,\r\n isArchived: renderedItem.archived,\r\n timestamp: renderedItem.createdAt ? new Date(renderedItem.createdAt) : new Date(),\r\n tags: [],\r\n actions: mapActionsToNotificationActions(renderedItem.actions),\r\n metadata: {\r\n channel: renderedItem.channel,\r\n renderedContent: renderedItem.renderedContent,\r\n },\r\n };\r\n },\r\n\r\n toWidgetNotificationFromWebSocket(wsPayload: any): Notification {\r\n if (!validateWebSocketPayload(wsPayload)) {\r\n console.error('Invalid WebSocket payload structure, using fallback', wsPayload);\r\n const fallbackRenderedContent =\r\n wsPayload?.renderedContent && typeof wsPayload.renderedContent === 'object'\r\n ? wsPayload.renderedContent\r\n : {};\r\n const { subject, body } = extractSubjectAndBody(fallbackRenderedContent, wsPayload?.channel);\r\n return {\r\n id: wsPayload?.notificationId || `notification-${Date.now()}`,\r\n subject: subject || 'Notification',\r\n body: body || '',\r\n isRead: wsPayload?.read || false,\r\n isArchived: wsPayload?.archived || false,\r\n timestamp: wsPayload?.createdAt ? new Date(wsPayload.createdAt) : new Date(),\r\n tags: [],\r\n actions: [],\r\n metadata: {\r\n channel: wsPayload?.channel || 'in_app',\r\n renderedContent: fallbackRenderedContent,\r\n },\r\n };\r\n }\r\n\r\n const { subject, body } = extractSubjectAndBody(wsPayload.renderedContent, wsPayload.channel);\r\n\r\n return {\r\n id: wsPayload.notificationId,\r\n subject: subject || 'Notification',\r\n body: body || '',\r\n isRead: wsPayload.read || false,\r\n isArchived: wsPayload.archived || false,\r\n timestamp: wsPayload.createdAt ? new Date(wsPayload.createdAt) : new Date(),\r\n tags: [],\r\n actions: mapActionsToNotificationActions(wsPayload.actions, wsPayload.notificationId),\r\n metadata: {\r\n channel: wsPayload.channel,\r\n renderedContent: wsPayload.renderedContent,\r\n },\r\n };\r\n },\r\n\r\n validateWebSocketPayload,\r\n };\r\n};\r\n"],"names":["useNotificationsClient","useLivePreferences","preferences","onPreferencesChange","onError","notificationClient","isSaving","setIsSaving","useState","error","setError","saveTimeoutRef","useRef","pendingUpdatesRef","updatePreference","useCallback","path","value","updatedPreferences","pathParts","current","i","sdkConfig","subscriberId","tenantId","environmentId","savePayload","sub","err","useEffect","validateWebSocketPayload","payload","field","mapSDKActionToWidgetAction","sdkAction","notificationId","onActionExecute","target","createNotificationMapper","extractSubjectAndBody","renderedContent","channel","subject","body","mapActionsToNotificationActions","actions","result","renderedItem","wsPayload","fallbackRenderedContent"],"mappings":"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"}
|