@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.
@@ -1,41 +1,41 @@
1
- import { useState as A, useRef as j, useCallback as N, useEffect as _ } from "react";
2
- const R = () => window.__notificationSDK?.client, I = ({
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: o,
4
+ onPreferencesChange: f,
5
5
  onError: e
6
6
  }) => {
7
- const n = R(), [c, i] = A(!1), [f, l] = A(null), u = j(), d = j({}), D = N(
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 r = { ...t }, s = g.split(".");
12
- let p = r;
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(r), d.current[g] = v, u.current && clearTimeout(u.current), i(!0), u.current = setTimeout(async () => {
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: E, environmentId: y } = a;
23
- if (!h || !E || !y)
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 m = {
26
- emailEnabled: r.channels.email,
27
- pushEnabled: r.channels.push,
28
- inAppEnabled: r.channels.inApp,
29
- smsEnabled: r.channels.sms
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
- r.subscriptions.length > 0 && (m.categories = {}, r.subscriptions.forEach((b) => {
32
- m.categories[b.workflowId] = {
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(E, h, m), d.current = {}, l(null);
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 (r) {
47
- const s = r instanceof Error ? r : new Error("Failed to update preference");
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, o, n, e]
51
+ [t, f, n, e]
52
52
  );
53
- return _(() => () => {
53
+ return N(() => () => {
54
54
  u.current && clearTimeout(u.current);
55
55
  }, []), {
56
- updatePreference: D,
56
+ updatePreference: y,
57
57
  isSaving: c,
58
- error: f
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), F = (t, o, e) => ({
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
- }), K = () => {
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
- }, o = (e, n, c) => {
81
+ }, f = (e, n, c) => {
82
82
  if (!e) return [];
83
83
  const i = [];
84
- return Array.isArray(e) ? (e.forEach((f) => {
85
- i.push(F(f, n || "", c));
86
- }), i) : (e.primary && i.push({
87
- type: "custom",
88
- label: e.primary.label || "Primary Action",
89
- icon: void 0,
90
- handler: async () => {
91
- }
92
- }), e.secondary && i.push({
93
- type: "custom",
94
- label: e.secondary.label || "Secondary Action",
95
- icon: void 0,
96
- handler: async () => {
97
- }
98
- }), i);
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: o(e.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: f, body: l } = t(i, e?.channel);
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: f || "Notification",
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: o(e.actions, e.notificationId),
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
- K as c,
157
- I as u
173
+ I as c,
174
+ T as u
158
175
  };
159
- //# sourceMappingURL=hooks-Cv5k48VE.js.map
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":";AAGA,MAAMA,IAAyB,MACrB,OAAe,mBAAmB,QAe/BC,IAAqB,CAAC;AAAA,EACjC,aAAAC;AAAA,EACA,qBAAAC;AAAA,EACA,SAAAC;AACF,MAAyD;AACvD,QAAMC,IAAqBL,EAAA,GACrB,CAACM,GAAUC,CAAW,IAAIC,EAAS,EAAK,GACxC,CAACC,GAAOC,CAAQ,IAAIF,EAAuB,IAAI,GAC/CG,IAAiBC,EAAA,GACjBC,IAAoBD,EAA+B,EAAE,GAErDE,IAAmBC;AAAA,IACvB,CAACC,GAAcC,MAAe;AAC5B,UAAI;AACF,QAAAP,EAAS,IAAI;AAEb,cAAMQ,IAAqB,EAAE,GAAGhB,EAAA,GAC1BiB,IAAYH,EAAK,MAAM,GAAG;AAEhC,YAAII,IAAeF;AACnB,iBAASG,IAAI,GAAGA,IAAIF,EAAU,SAAS,GAAGE;AACxC,UAAID,EAAQD,EAAUE,CAAC,CAAC,MAAM,WAC5BD,EAAQD,EAAUE,CAAC,CAAC,IAAI,CAAA,IAE1BD,IAAUA,EAAQD,EAAUE,CAAC,CAAC;AAEhC,QAAAD,EAAQD,EAAUA,EAAU,SAAS,CAAC,CAAC,IAAIF,GAE3Cd,EAAoBe,CAAkB,GAEtCL,EAAkB,QAAQG,CAAI,IAAIC,GAE9BN,EAAe,WACjB,aAAaA,EAAe,OAAO,GAGrCJ,EAAY,EAAI,GAEhBI,EAAe,UAAU,WAAW,YAAY;AAC9C,cAAI;AACF,gBAAI,CAACN;AACH,oBAAM,IAAI,MAAM,mCAAmC;AAGrD,kBAAMiB,IAAa,OAAe,mBAAmB;AAErD,gBAAI,CAACA;AACH,oBAAM,IAAI,MAAM,iCAAiC;AAGnD,kBAAM,EAAE,cAAAC,GAAc,UAAAC,GAAU,eAAAC,EAAA,IAAkBH;AAElD,gBAAI,CAACC,KAAgB,CAACC,KAAY,CAACC;AACjC,oBAAM,IAAI,MAAM,6EAA6E;AAG/F,kBAAMC,IAAmB;AAAA,cACvB,cAAcR,EAAmB,SAAS;AAAA,cAC1C,aAAaA,EAAmB,SAAS;AAAA,cACzC,cAAcA,EAAmB,SAAS;AAAA,cAC1C,YAAYA,EAAmB,SAAS;AAAA,YAAA;AAG1C,YAAIA,EAAmB,cAAc,SAAS,MAC5CQ,EAAY,aAAa,CAAA,GACzBR,EAAmB,cAAc,QAAQ,CAAAS,MAAO;AAC9C,cAAAD,EAAY,WAAWC,EAAI,UAAU,IAAI;AAAA,gBACvC,cAAcA,EAAI,SAAS;AAAA,gBAC3B,aAAaA,EAAI,SAAS;AAAA,gBAC1B,cAAcA,EAAI,SAAS;AAAA,gBAC3B,YAAYA,EAAI,SAAS;AAAA,cAAA;AAAA,YAE7B,CAAC,IAMH,MAAMtB,EAAmB,YAAY,OAAOmB,GAAUD,GAAcG,CAAW,GAE/Eb,EAAkB,UAAU,CAAA,GAC5BH,EAAS,IAAI;AAAA,UACf,SAASkB,GAAU;AACjB,kBAAMnB,IAAQmB,aAAe,QAAQA,IAAM,IAAI,MAAM,4BAA4B;AAEjF,YAAI,QAAQ,IAAI,UAIhBlB,EAASD,CAAK,GACdL,IAAUK,CAAK;AAAA,UAEjB,UAAA;AACE,YAAAF,EAAY,EAAK;AAAA,UACnB;AAAA,QACF,GAAG,GAAG;AAAA,MACR,SAASqB,GAAU;AACjB,cAAMnB,IAAQmB,aAAe,QAAQA,IAAM,IAAI,MAAM,6BAA6B;AAClF,QAAAlB,EAASD,CAAK,GACdL,IAAUK,CAAK,GACfF,EAAY,EAAK;AAAA,MACnB;AAAA,IACF;AAAA,IACA,CAACL,GAAaC,GAAqBE,GAAoBD,CAAO;AAAA,EAAA;AAGhE,SAAAyB,EAAU,MACD,MAAM;AACX,IAAIlB,EAAe,WACjB,aAAaA,EAAe,OAAO;AAAA,EAEvC,GACC,CAAA,CAAE,GAEE;AAAA,IACL,kBAAAG;AAAA,IACA,UAAAR;AAAA,IACA,OAAAG;AAAA,EAAA;AAEJ,GC/HMqB,IAA2B,CAACC,MAC5B,GAACA,KAAW,OAAOA,KAAY,YAKZ,CAAC,kBAAkB,WAAW,mBAAmB,QAAQ,UAAU,EACrD,OAAO,CAAAC,MAAS,EAAEA,KAASD,EAAQ,EAEtD,SAAS,KAKvB,OAAOA,EAAQ,WAAY,YAK3B,OAAOA,EAAQ,QAAS,aAAa,OAAOA,EAAQ,YAAa,aAKjE,OAAOA,EAAQ,mBAAoB,YAAYA,EAAQ,oBAAoB,OAS3EE,IAA6B,CACjCC,GACAC,GACAC,OAEO;AAAA,EACL,MAAM;AAAA,EACN,OAAOF,EAAU;AAAA,EACjB,MAAM;AAAA,EACN,SAAS,YAAY;AACnB,QAAI;AAKF,UAAIA,EAAU,KAAK;AACjB,cAAMG,IAASH,EAAU,UAAU;AACnC,eAAO,KAAKA,EAAU,KAAKG,CAAM;AAAA,MACnC;AAEA,MAAIH,EAAU;AAAA,IAGhB,SAASzB,GAAO;AAEd,YAAMA;AAAA,IACR;AAAA,EACF;AAAA,IAIS6B,IAA2B,MAA0B;AAChE,QAAMC,IAAwB,CAC5BC,GACAC,MACsC;AACtC,QAAI,CAACD,KAAmB,OAAOA,KAAoB;AACjD,aAAO,EAAE,SAAS,IAAI,MAAM,GAAA;AAG9B,QAAIE,IAAU,IACVC,IAAO;AAEX,WAAIH,EAAgB,QAClBE,IAAUF,EAAgB,KAAK,SAAS,IACxCG,IAAOH,EAAgB,KAAK,QAAQ,MAC3BA,EAAgB,SACzBE,IAAUF,EAAgB,MAAM,WAAW,IAC3CG,IAAOH,EAAgB,MAAM,QAAQA,EAAgB,MAAM,QAAQ,MAC1DA,EAAgB,UACzBE,IAAUF,EAAgB,OAAO,SAAS,IAC1CG,IAAOH,EAAgB,OAAO,WAAW,MAChCA,EAAgB,OACzBE,IAAU,OACVC,IAAOH,EAAgB,IAAI,WAAW,MAC7BC,MAAY,YACrBC,IAAUF,EAAgB,SAAS,IACnCG,IAAOH,EAAgB,WAAW,OAElCE,IAAUF,EAAgB,SAASA,EAAgB,WAAW,IAC9DG,IAAOH,EAAgB,WAAWA,EAAgB,QAAQA,EAAgB,QAAQ,KAG7E,EAAE,SAAAE,GAAS,MAAAC,EAAA;AAAA,EACpB,GAEMC,IAAkC,CACtCC,GACAV,GACAC,MACyB;AACzB,QAAI,CAACS,EAAS,QAAO,CAAA;AAErB,UAAMC,IAA+B,CAAA;AAErC,QAAI,MAAM,QAAQD,CAAO;AACvB,aAAAA,EAAQ,QAAQ,CAACX,MAAc;AAC7B,QAAAY,EAAO,KAAKb,EAA2BC,GAAWC,KAAkB,IAAIC,CAAe,CAAC;AAAA,MAC1F,CAAC,GACMU;AAGT,QAAID,EAAQ,SAAS;AACnB,YAAME,IAAgBF,EAAQ;AAC9B,MAAAC,EAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,OAAOC,EAAc,SAAS;AAAA,QAC9B,MAAM;AAAA,QACN,SAAS,OAAOZ,MAA2B;AACzC,UAAIY,EAAc,OAChB,OAAO,KAAKA,EAAc,KAAK,OAAO;AAAA,QAK1C;AAAA,MAAA,CACD;AAAA,IACH;AAEA,QAAIF,EAAQ,WAAW;AACrB,YAAMG,IAAkBH,EAAQ;AAChC,MAAAC,EAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,OAAOE,EAAgB,SAAS;AAAA,QAChC,MAAM;AAAA,QACN,SAAS,OAAOb,MAA2B;AACzC,UAAIa,EAAgB,OAClB,OAAO,KAAKA,EAAgB,KAAK,OAAO;AAAA,QAK5C;AAAA,MAAA,CACD;AAAA,IACH;AAEA,WAAOF;AAAA,EACT;AAEA,SAAO;AAAA,IACL,qBAAqBG,GAAsD;AACzE,YAAM,EAAE,SAAAP,GAAS,MAAAC,MAASJ,EAAsBU,EAAa,iBAAiBA,EAAa,OAAO;AAElG,aAAO;AAAA,QACL,IAAIA,EAAa;AAAA,QACjB,SAASP,KAAW;AAAA,QACpB,MAAMC,KAAQ;AAAA,QACd,QAAQM,EAAa;AAAA,QACrB,YAAYA,EAAa;AAAA,QACzB,WAAWA,EAAa,YAAY,IAAI,KAAKA,EAAa,SAAS,IAAI,oBAAI,KAAA;AAAA,QAC3E,MAAM,CAAA;AAAA,QACN,SAASL;AAAA,UACPK,EAAa,iBAAiB,WAAWA,EAAa;AAAA,QAAA;AAAA,QAExD,UAAU;AAAA,UACR,SAASA,EAAa;AAAA,UACtB,iBAAiBA,EAAa;AAAA,QAAA;AAAA,MAChC;AAAA,IAEJ;AAAA,IAEA,kCAAkCC,GAA8B;AAC9D,UAAI,CAACpB,EAAyBoB,CAAS,GAAG;AAExC,cAAMC,IACJD,GAAW,mBAAmB,OAAOA,EAAU,mBAAoB,WAC/DA,EAAU,kBACV,CAAA,GACA,EAAE,SAAAR,GAAS,MAAAC,MAASJ,EAAsBY,GAAyBD,GAAW,OAAO;AAC3F,eAAO;AAAA,UACL,IAAIA,GAAW,kBAAkB,gBAAgB,KAAK,KAAK;AAAA,UAC3D,SAASR,KAAW;AAAA,UACpB,MAAMC,KAAQ;AAAA,UACd,QAAQO,GAAW,QAAQ;AAAA,UAC3B,YAAYA,GAAW,YAAY;AAAA,UACnC,WAAWA,GAAW,YAAY,IAAI,KAAKA,EAAU,SAAS,IAAI,oBAAI,KAAA;AAAA,UACtE,MAAM,CAAA;AAAA,UACN,SAAS,CAAA;AAAA,UACT,UAAU;AAAA,YACR,SAASA,GAAW,WAAW;AAAA,YAC/B,iBAAiBC;AAAA,UAAA;AAAA,QACnB;AAAA,MAEJ;AAEA,YAAM,EAAE,SAAAT,GAAS,MAAAC,MAASJ,EAAsBW,EAAU,iBAAiBA,EAAU,OAAO;AAE5F,aAAO;AAAA,QACL,IAAIA,EAAU;AAAA,QACd,SAASR,KAAW;AAAA,QACpB,MAAMC,KAAQ;AAAA,QACd,QAAQO,EAAU,QAAQ;AAAA,QAC1B,YAAYA,EAAU,YAAY;AAAA,QAClC,WAAWA,EAAU,YAAY,IAAI,KAAKA,EAAU,SAAS,IAAI,oBAAI,KAAA;AAAA,QACrE,MAAM,CAAA;AAAA,QACN,SAASN;AAAA,UACPM,EAAU,iBAAiB,WAAWA,EAAU;AAAA,UAChDA,EAAU;AAAA,QAAA;AAAA,QAEZ,UAAU;AAAA,UACR,SAASA,EAAU;AAAA,UACnB,iBAAiBA,EAAU;AAAA,QAAA;AAAA,MAC7B;AAAA,IAEJ;AAAA,IAEA,0BAAApB;AAAA,EAAA;AAEJ;"}
@@ -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-CggRWw6Q.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&&`
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-Cv5k48VE.js";
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.31",
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",
@@ -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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"}