@burgan-tech/morph-touch-runtime 0.0.2

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.
Files changed (244) hide show
  1. package/LICENSE +21 -0
  2. package/burgan-tech-morph-touch-runtime-0.0.2.tgz +0 -0
  3. package/package.json +21 -0
  4. package/touch/Extensions/.gitkeep +0 -0
  5. package/touch/Functions/.gitkeep +0 -0
  6. package/touch/Functions/check-livekit-room-access.1.0.0.json +28 -0
  7. package/touch/Functions/check-livekit-room-access.http +44 -0
  8. package/touch/Functions/get-absence-entry.1.0.0.json +28 -0
  9. package/touch/Functions/get-advisor-stats.1.0.0.json +27 -0
  10. package/touch/Functions/get-available-slots.1.0.0.json +30 -0
  11. package/touch/Functions/get-available-slots.http +29 -0
  12. package/touch/Functions/get-chat-rooms.1.0.0.json +28 -0
  13. package/touch/Functions/get-chat-rooms.http +62 -0
  14. package/touch/Functions/get-customer-info.1.0.0.json +27 -0
  15. package/touch/Functions/get-matrix-sync.1.0.0.json +28 -0
  16. package/touch/Functions/get-rezervations.1.0.0.json +26 -0
  17. package/touch/Functions/get-rezervations.http +59 -0
  18. package/touch/Functions/get-room-messages.1.0.0.json +29 -0
  19. package/touch/Functions/get-room-messages.http +45 -0
  20. package/touch/Functions/rezervation-transfer.http +118 -0
  21. package/touch/Functions/send-room-message.1.0.0.json +29 -0
  22. package/touch/Functions/src/CheckLivekitRoomAccessMapping.csx +238 -0
  23. package/touch/Functions/src/GetAbsenceEntryMapping.csx +225 -0
  24. package/touch/Functions/src/GetAdvisorStatsMapping.csx +190 -0
  25. package/touch/Functions/src/GetAvailableSlotsMapping.csx +468 -0
  26. package/touch/Functions/src/GetChatRoomsMapping.csx +202 -0
  27. package/touch/Functions/src/GetCustomerInfoMapping.csx +140 -0
  28. package/touch/Functions/src/GetMatrixSyncMapping.csx +197 -0
  29. package/touch/Functions/src/GetRezervationsMapping.csx +162 -0
  30. package/touch/Functions/src/GetRoomMessagesMapping.csx +165 -0
  31. package/touch/Functions/src/SendCancelNotificationMapping.csx +63 -0
  32. package/touch/Functions/src/SendRoomMessageMapping.csx +131 -0
  33. package/touch/Schemas/.gitkeep +0 -0
  34. package/touch/Schemas/absence-entry.1.0.0.json +232 -0
  35. package/touch/Schemas/advisor-chat-rooms.1.0.0.json +62 -0
  36. package/touch/Schemas/chat-room.1.0.0.json +110 -0
  37. package/touch/Schemas/notification-sender.1.0.0.json +159 -0
  38. package/touch/Schemas/portfolio-manager.1.0.0.json +63 -0
  39. package/touch/Schemas/rezervation.1.0.0.json +102 -0
  40. package/touch/Tasks/.gitkeep +0 -0
  41. package/touch/Tasks/cancel-absence-entry-for-rezervation.1.0.0.json +21 -0
  42. package/touch/Tasks/check-already-completed-chat-room.1.0.0.json +24 -0
  43. package/touch/Tasks/check-duplicate-portfolio-manager.1.0.0.json +23 -0
  44. package/touch/Tasks/check-duplicate-rezervation.1.0.0.json +23 -0
  45. package/touch/Tasks/check-existing-permanent-chat-room.1.0.0.json +21 -0
  46. package/touch/Tasks/check-randevu-time.1.0.0.json +17 -0
  47. package/touch/Tasks/create-permanent-chat-room.1.0.0.json +28 -0
  48. package/touch/Tasks/determine-transfer-type.1.0.0.json +16 -0
  49. package/touch/Tasks/enrich-rezervations-for-transfer.1.0.0.json +23 -0
  50. package/touch/Tasks/execute-permanent-chatroom-update.1.0.0.json +21 -0
  51. package/touch/Tasks/execute-rez-chatroom-transfer.1.0.0.json +21 -0
  52. package/touch/Tasks/execute-rezervation-transfer.1.0.0.json +19 -0
  53. package/touch/Tasks/fetch-absence-entries-for-transfer.1.0.0.json +23 -0
  54. package/touch/Tasks/fetch-advisors-for-transfer.1.0.0.json +23 -0
  55. package/touch/Tasks/fetch-permanent-chatrooms-for-transfer.1.0.0.json +23 -0
  56. package/touch/Tasks/fetch-rez-chatrooms-for-transfer.1.0.0.json +23 -0
  57. package/touch/Tasks/fetch-rezervations-for-transfer.1.0.0.json +22 -0
  58. package/touch/Tasks/get-absence-entry-task.1.0.0.json +23 -0
  59. package/touch/Tasks/get-available-slots-http.1.0.0.json +27 -0
  60. package/touch/Tasks/get-available-slots.1.0.0.json +24 -0
  61. package/touch/Tasks/get-chat-room-data-for-rezervation.1.0.0.json +23 -0
  62. package/touch/Tasks/get-chat-token.1.0.0.json +27 -0
  63. package/touch/Tasks/get-matrix-room-messages.1.0.0.json +25 -0
  64. package/touch/Tasks/get-matrix-sync.1.0.0.json +25 -0
  65. package/touch/Tasks/get-rezervation-data-task.1.0.0.json +22 -0
  66. package/touch/Tasks/get-rezervation-state-task.1.0.0.json +22 -0
  67. package/touch/Tasks/get-rezervations-task.1.0.0.json +21 -0
  68. package/touch/Tasks/get-user-info-for-rezervation.1.0.0.json +24 -0
  69. package/touch/Tasks/get-video-call-url.1.0.0.json +17 -0
  70. package/touch/Tasks/invite-user-to-rezervation-room.1.0.0.json +27 -0
  71. package/touch/Tasks/join-chat-room-for-rezervation.1.0.0.json +28 -0
  72. package/touch/Tasks/join-matrix-room.1.0.0.json +27 -0
  73. package/touch/Tasks/join-user-to-room.1.0.0.json +27 -0
  74. package/touch/Tasks/merge-invited-user.1.0.0.json +17 -0
  75. package/touch/Tasks/merge-rezervation-update.1.0.0.json +16 -0
  76. package/touch/Tasks/merge-target-advisor-for-transfer.1.0.0.json +17 -0
  77. package/touch/Tasks/merge-update-body-for-randevu-update.1.0.0.json +16 -0
  78. package/touch/Tasks/merge-video-call-urls.1.0.0.json +17 -0
  79. package/touch/Tasks/query-chat-room-instances.1.0.0.json +23 -0
  80. package/touch/Tasks/remove-member-from-room.1.0.0.json +28 -0
  81. package/touch/Tasks/send-matrix-room-message.1.0.0.json +26 -0
  82. package/touch/Tasks/send-push-notification.1.0.0.json +26 -0
  83. package/touch/Tasks/send-sms-notification.1.0.0.json +26 -0
  84. package/touch/Tasks/set-chat-room-status-active.1.0.0.json +16 -0
  85. package/touch/Tasks/set-chat-room-status-deactive.1.0.0.json +16 -0
  86. package/touch/Tasks/set-previous-advisor.1.0.0.json +16 -0
  87. package/touch/Tasks/set-user-from-headers-for-rezervation.1.0.0.json +17 -0
  88. package/touch/Tasks/start-absence-entry-for-rezervation.1.0.0.json +21 -0
  89. package/touch/Tasks/start-chat-room-for-rezervation.1.0.0.json +20 -0
  90. package/touch/Tasks/start-notification-sender.1.0.0.json +20 -0
  91. package/touch/Tasks/start-permanent-chat-room.1.0.0.json +21 -0
  92. package/touch/Tasks/start-transfer-from-absence-entry.1.0.0.json +19 -0
  93. package/touch/Tasks/start-video-call.1.0.0.json +22 -0
  94. package/touch/Tasks/trigger-chat-room-deactivate.1.0.0.json +21 -0
  95. package/touch/Tasks/trigger-chat-room-transfer.1.0.0.json +21 -0
  96. package/touch/Tasks/trigger-chat-room-update.1.0.0.json +20 -0
  97. package/touch/Tasks/trigger-rezervation-add-invited-participant.1.0.0.json +20 -0
  98. package/touch/Tasks/trigger-rezervation-apply-update.1.0.0.json +20 -0
  99. package/touch/Tasks/trigger-rezervation-to-in-meet.1.0.0.json +20 -0
  100. package/touch/Tasks/trigger-rezervation-video-call-update.1.0.0.json +21 -0
  101. package/touch/Tasks/validate-date-for-rezervation.1.0.0.json +18 -0
  102. package/touch/Tasks/validate-slot-for-rezervation.1.0.0.json +24 -0
  103. package/touch/Tasks/validate-transfer-availability.1.0.0.json +23 -0
  104. package/touch/Views/.gitkeep +0 -0
  105. package/touch/Workflows/.gitkeep +0 -0
  106. package/touch/Workflows/absence-entry.http +458 -0
  107. package/touch/Workflows/absence-entry.json +179 -0
  108. package/touch/Workflows/add-participant-to-rezervation.http +47 -0
  109. package/touch/Workflows/add-participant-to-rezervation.json +340 -0
  110. package/touch/Workflows/chat-room.http +207 -0
  111. package/touch/Workflows/chat-room.json +452 -0
  112. package/touch/Workflows/investment-advisor.json +367 -0
  113. package/touch/Workflows/notification-sender.http +299 -0
  114. package/touch/Workflows/notification-sender.json +262 -0
  115. package/touch/Workflows/portfolio-manager.json +367 -0
  116. package/touch/Workflows/randevu-update.http +87 -0
  117. package/touch/Workflows/rezervation-start.json +467 -0
  118. package/touch/Workflows/rezervation-transfer.json +512 -0
  119. package/touch/Workflows/rezervation-update.http +87 -0
  120. package/touch/Workflows/rezervation-update.json +485 -0
  121. package/touch/Workflows/rezervation.http +214 -0
  122. package/touch/Workflows/rezervation.json +726 -0
  123. package/touch/Workflows/src/AllChatRoomsSuccessRule.csx +65 -0
  124. package/touch/Workflows/src/AlwaysTrueRule.csx +13 -0
  125. package/touch/Workflows/src/AnyChatRoomFailedRule.csx +64 -0
  126. package/touch/Workflows/src/AutoProcessRule.csx +38 -0
  127. package/touch/Workflows/src/BuildRezChatRoomPlanMapping.csx +160 -0
  128. package/touch/Workflows/src/CanStartRezervationTimerMapping.csx +48 -0
  129. package/touch/Workflows/src/CanStartTransferMapping.csx +14 -0
  130. package/touch/Workflows/src/CancelAbsenceEntryForRezervationMapping.csx +48 -0
  131. package/touch/Workflows/src/CancelAbsenceForRandevuUpdateMapping.csx +62 -0
  132. package/touch/Workflows/src/ChatRoomCreatedFailedRule.csx +54 -0
  133. package/touch/Workflows/src/ChatRoomCreatedSuccessRule.csx +54 -0
  134. package/touch/Workflows/src/ChatRoomRemoveMapping.csx +104 -0
  135. package/touch/Workflows/src/ChatRoomTransferInviteMapping.csx +109 -0
  136. package/touch/Workflows/src/ChatRoomTransferRemoveMapping.csx +71 -0
  137. package/touch/Workflows/src/ChatRoomUpdateMapping.csx +107 -0
  138. package/touch/Workflows/src/CheckAlreadyCompletedChatRoomMapping.csx +100 -0
  139. package/touch/Workflows/src/CheckDuplicatePortfolioManagerMapping.csx +119 -0
  140. package/touch/Workflows/src/CheckDuplicateRezervationMapping.csx +150 -0
  141. package/touch/Workflows/src/CheckExistingPermanentChatRoomMapping.csx +169 -0
  142. package/touch/Workflows/src/CheckRandevuTimeMapping.csx +67 -0
  143. package/touch/Workflows/src/CheckRezervationStateActiveOrInMeetRule.csx +14 -0
  144. package/touch/Workflows/src/CreateAbsenceEntryForRezervationMapping.csx +111 -0
  145. package/touch/Workflows/src/CreateAbsenceForRandevuUpdateMapping.csx +108 -0
  146. package/touch/Workflows/src/CreateChatRoomForRezervationMapping.csx +122 -0
  147. package/touch/Workflows/src/CreatePermanentChatRoomMapping.csx +140 -0
  148. package/touch/Workflows/src/CustomerControlToEndRule.csx +14 -0
  149. package/touch/Workflows/src/DetermineTransferTypeMapping.csx +92 -0
  150. package/touch/Workflows/src/EnrichRezervationsForTransferMapping.csx +495 -0
  151. package/touch/Workflows/src/ExecutePermanentChatRoomUpdateMapping.csx +124 -0
  152. package/touch/Workflows/src/ExecuteRezChatRoomTransferMapping.csx +95 -0
  153. package/touch/Workflows/src/ExecuteTransferMapping.csx +152 -0
  154. package/touch/Workflows/src/FetchAbsenceEntriesForTransferMapping.csx +116 -0
  155. package/touch/Workflows/src/FetchAdvisorsForTransferMapping.csx +145 -0
  156. package/touch/Workflows/src/FetchPermanentChatRoomsMapping.csx +128 -0
  157. package/touch/Workflows/src/FetchRezervationsForTransferMapping.csx +124 -0
  158. package/touch/Workflows/src/GetChatRoomDataForRezervationMapping.csx +100 -0
  159. package/touch/Workflows/src/GetChatTokenMapping.csx +85 -0
  160. package/touch/Workflows/src/GetRezervationDataForAddParticipantMapping.csx +105 -0
  161. package/touch/Workflows/src/GetRezervationDataForRandevuUpdateMapping.csx +110 -0
  162. package/touch/Workflows/src/GetRezervationDataMapping.csx +111 -0
  163. package/touch/Workflows/src/GetRezervationStateForRandevuUpdateMapping.csx +80 -0
  164. package/touch/Workflows/src/GetRezervationStateMapping.csx +92 -0
  165. package/touch/Workflows/src/GetUserInfoForRezervationMapping.csx +63 -0
  166. package/touch/Workflows/src/GetVideoCallUrlMapping.csx +62 -0
  167. package/touch/Workflows/src/HasChangedAdvisorRule.csx +28 -0
  168. package/touch/Workflows/src/HasChatIntegrationRule.csx +41 -0
  169. package/touch/Workflows/src/HasExistingPermanentRoomRule.csx +28 -0
  170. package/touch/Workflows/src/InviteAdvisorForRandevuUpdateMapping.csx +86 -0
  171. package/touch/Workflows/src/InviteNewAdvisorToRezervationRoomMapping.csx +78 -0
  172. package/touch/Workflows/src/InviteNewParticipantToRandevuRoomMapping.csx +88 -0
  173. package/touch/Workflows/src/InvitedUserMergeMapping.csx +77 -0
  174. package/touch/Workflows/src/InvitedUserNotAllowedRule.csx +71 -0
  175. package/touch/Workflows/src/InvitedUserNotInMeetRule.csx +19 -0
  176. package/touch/Workflows/src/IsNotfSentRule.csx +36 -0
  177. package/touch/Workflows/src/JoinChatRoomForAddParticipantMapping.csx +118 -0
  178. package/touch/Workflows/src/JoinChatRoomForRandevuStartMapping.csx +119 -0
  179. package/touch/Workflows/src/JoinChatRoomForRezervationMapping.csx +118 -0
  180. package/touch/Workflows/src/JoinMatrixRoomMapping.csx +100 -0
  181. package/touch/Workflows/src/JoinUserToRoomMapping.csx +118 -0
  182. package/touch/Workflows/src/LoginForAddParticipantChatMapping.csx +102 -0
  183. package/touch/Workflows/src/LoginForRandevuStartChatMapping.csx +96 -0
  184. package/touch/Workflows/src/MergeRezervationUpdateMapping.csx +82 -0
  185. package/touch/Workflows/src/MergeTargetAdvisorForTransferMapping.csx +79 -0
  186. package/touch/Workflows/src/MergeUpdateBodyForRandevuUpdateMapping.csx +91 -0
  187. package/touch/Workflows/src/NoExistingPermanentRoomRule.csx +23 -0
  188. package/touch/Workflows/src/NotificationCompleteAlwaysRule.csx +14 -0
  189. package/touch/Workflows/src/NotificationCompleteFromPendingRule.csx +41 -0
  190. package/touch/Workflows/src/NotificationCompleteFromSmsRule.csx +36 -0
  191. package/touch/Workflows/src/NotificationPushFromPendingRule.csx +42 -0
  192. package/touch/Workflows/src/NotificationPushFromSmsRule.csx +36 -0
  193. package/touch/Workflows/src/NotificationSmsPendingRule.csx +32 -0
  194. package/touch/Workflows/src/PermanentDoneRule.csx +44 -0
  195. package/touch/Workflows/src/PermanentProcessNextRule.csx +44 -0
  196. package/touch/Workflows/src/RezChatRoomProcessNextRule.csx +44 -0
  197. package/touch/Workflows/src/RezChatRoomsDoneRule.csx +44 -0
  198. package/touch/Workflows/src/SendPushNotificationMapping.csx +146 -0
  199. package/touch/Workflows/src/SendSmsNotificationMapping.csx +138 -0
  200. package/touch/Workflows/src/SetChatRoomStatusActiveMapping.csx +22 -0
  201. package/touch/Workflows/src/SetChatRoomStatusDeactiveMapping.csx +22 -0
  202. package/touch/Workflows/src/SetPreviousAdvisorMapping.csx +28 -0
  203. package/touch/Workflows/src/SetUserFromHeadersForRezervationMapping.csx +24 -0
  204. package/touch/Workflows/src/SlotAvailableRule.csx +33 -0
  205. package/touch/Workflows/src/SlotUnavailableRule.csx +33 -0
  206. package/touch/Workflows/src/StartChatRoomForRezervationMapping.csx +100 -0
  207. package/touch/Workflows/src/StartGetChatTokenForRezervationMapping.csx +71 -0
  208. package/touch/Workflows/src/StartNotificationForAdvisorMapping.csx +117 -0
  209. package/touch/Workflows/src/StartNotificationForRezervationFromUserInfoMapping.csx +128 -0
  210. package/touch/Workflows/src/StartNotificationForUserMapping.csx +117 -0
  211. package/touch/Workflows/src/StartPermanentChatRoomMapping.csx +92 -0
  212. package/touch/Workflows/src/StartTransferFromAbsenceEntryMapping.csx +94 -0
  213. package/touch/Workflows/src/StartVideoCallCustomerSubFlowMapping.csx +37 -0
  214. package/touch/Workflows/src/StartVideoCallForInvitedParticipantMapping.csx +54 -0
  215. package/touch/Workflows/src/StartVideoCallForRandevuStartMapping.csx +70 -0
  216. package/touch/Workflows/src/StartVideoCallStaffSubFlowMapping.csx +37 -0
  217. package/touch/Workflows/src/StateActiveForUpdateRule.csx +14 -0
  218. package/touch/Workflows/src/StateActiveRule.csx +14 -0
  219. package/touch/Workflows/src/StateInMeetRule.csx +14 -0
  220. package/touch/Workflows/src/StateNotActiveForUpdateRule.csx +14 -0
  221. package/touch/Workflows/src/StateNotActiveOrInMeetRule.csx +14 -0
  222. package/touch/Workflows/src/StatusCustomerEnterRule.csx +25 -0
  223. package/touch/Workflows/src/StatusStaffEnterRule.csx +25 -0
  224. package/touch/Workflows/src/SyncWorkingHoursRule.csx +46 -0
  225. package/touch/Workflows/src/TokenSuccessRule.csx +24 -0
  226. package/touch/Workflows/src/TransferAllDoneRule.csx +50 -0
  227. package/touch/Workflows/src/TransferHasInvalidRule.csx +34 -0
  228. package/touch/Workflows/src/TransferProcessNextRule.csx +50 -0
  229. package/touch/Workflows/src/TriggerChatRoomDeactivateMapping.csx +77 -0
  230. package/touch/Workflows/src/TriggerChatRoomTransferMapping.csx +93 -0
  231. package/touch/Workflows/src/TriggerChatRoomUpdateMapping.csx +99 -0
  232. package/touch/Workflows/src/TriggerRezervationAddInvitedParticipantMapping.csx +77 -0
  233. package/touch/Workflows/src/TriggerRezervationApplyUpdateMapping.csx +66 -0
  234. package/touch/Workflows/src/TriggerRezervationToInMeetMapping.csx +69 -0
  235. package/touch/Workflows/src/TriggerRezervationVideoCallUpdateMapping.csx +67 -0
  236. package/touch/Workflows/src/ValidateDateForRezervationMapping.csx +60 -0
  237. package/touch/Workflows/src/ValidateSlotForRandevuUpdateMapping.csx +211 -0
  238. package/touch/Workflows/src/ValidateSlotForRezervationMapping.csx +246 -0
  239. package/touch/Workflows/src/ValidateTransferMapping.csx +243 -0
  240. package/touch/Workflows/src/VideoCallUrlUpdateMergeMapping.csx +141 -0
  241. package/touch/Workflows/start-chat.json +217 -0
  242. package/touch/Workflows/start-video-call.http +93 -0
  243. package/touch/Workflows/start-video-call.json +206 -0
  244. package/vnext.config.json +58 -0
@@ -0,0 +1,468 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using System.Linq;
4
+ using System.Threading.Tasks;
5
+ using BBT.Workflow.Scripting;
6
+ using BBT.Workflow.Definitions;
7
+ /// <summary>
8
+ /// Mapping for GetAvailableSlotsTask (DaprServiceTask - Type 3)
9
+ /// Queries absence-entry workflow (state: complete) via Dapr service invocation to get
10
+ /// public holidays, working hours changes, and personal leaves.
11
+ /// Then calculates available appointment slots for the given advisor and date.
12
+ ///
13
+ /// Working hours priority: advisor-specific > company-wide (advisor=null) > no hours
14
+ /// Half-day public holidays only block the holiday hours, not the entire day.
15
+ /// </summary>
16
+ public class GetAvailableSlotsMapping : ScriptBase, IMapping
17
+ {
18
+ public Task<ScriptResponse> InputHandler(WorkflowTask task, ScriptContext context)
19
+ {
20
+ var serviceTask = task as DaprServiceTask;
21
+ if (serviceTask == null)
22
+ throw new InvalidOperationException("Task must be a DaprServiceTask");
23
+
24
+ var appId = GetConfigValue("DAPR_APP_ID");
25
+ if (!string.IsNullOrEmpty(appId))
26
+ serviceTask.SetAppId(appId);
27
+
28
+ var advisorId = context.QueryParameters?["advisorId"]?.ToString();
29
+ var date = context.QueryParameters?["date"]?.ToString();
30
+
31
+ if (string.IsNullOrEmpty(advisorId) || string.IsNullOrEmpty(date))
32
+ {
33
+ return Task.FromResult(new ScriptResponse
34
+ {
35
+ Key = "validation-error",
36
+ Data = new
37
+ {
38
+ error = "advisorId and date are required",
39
+ errorCode = "VALIDATION_ERROR"
40
+ }
41
+ });
42
+ }
43
+
44
+ var requestedDate = DateTime.Parse(date);
45
+ if (requestedDate.Date < DateTime.UtcNow.Date)
46
+ throw new ArgumentException("Date cannot be in the past.", "date");
47
+
48
+ var filterJson =
49
+ "{\"and\":[" +
50
+ "{\"or\":[{\"currentState\":{\"eq\":\"complete\"}},{\"currentState\":{\"eq\":\"complete-with-transfer\"}}]}," +
51
+ "{\"or\":[" +
52
+ "{\"attributes\":{\"advisor\":{\"eq\":\"" + advisorId + "\"}}}," +
53
+ "{\"attributes\":{\"absenceType\":{\"eq\":\"public-holiday\"}}}," +
54
+ "{\"attributes\":{\"absenceType\":{\"eq\":\"working-hours-change\"}}}" +
55
+ "]}" +
56
+ "]}";
57
+
58
+ var queryParams = new List<string>
59
+ {
60
+ "pageSize=100",
61
+ $"filter={filterJson}"
62
+ };
63
+
64
+ serviceTask.SetQueryString(string.Join("&", queryParams));
65
+
66
+ return Task.FromResult(new ScriptResponse());
67
+ }
68
+
69
+ public Task<ScriptResponse> OutputHandler(ScriptContext context)
70
+ {
71
+ try
72
+ {
73
+ var advisorId = context.QueryParameters?["advisorId"]?.ToString();
74
+ var dateStr = context.QueryParameters?["date"]?.ToString();
75
+ var duration = GetAppointmentDuration(context);
76
+
77
+ if (string.IsNullOrEmpty(advisorId) || string.IsNullOrEmpty(dateStr))
78
+ {
79
+ return Task.FromResult(new ScriptResponse
80
+ {
81
+ Key = "validation-error",
82
+ Data = new
83
+ {
84
+ error = "advisorId and date are required",
85
+ errorCode = "VALIDATION_ERROR",
86
+ advisorId,
87
+ date = dateStr
88
+ }
89
+ });
90
+ }
91
+
92
+ var requestedDate = DateTime.Parse(dateStr);
93
+ var dayOfWeek = requestedDate.DayOfWeek.ToString().ToLower();
94
+
95
+ var response = context.Body;
96
+
97
+ if (response?.isSuccess == false)
98
+ {
99
+ var errorType = ClassifyServiceError(response?.errorMessage?.ToString() ?? "");
100
+ return Task.FromResult(new ScriptResponse
101
+ {
102
+ Key = "service-error",
103
+ Data = new
104
+ {
105
+ error = "Failed to fetch absence entries",
106
+ errorCode = "SERVICE_ERROR",
107
+ errorType = errorType,
108
+ errorMessage = response?.errorMessage?.ToString(),
109
+ serviceInfo = new
110
+ {
111
+ appId = response?.metadata?.appId?.ToString(),
112
+ methodName = response?.metadata?.methodName?.ToString(),
113
+ httpVerb = response?.metadata?.httpVerb?.ToString()
114
+ }
115
+ },
116
+ Tags = new[] { "appointments", "error" }
117
+ });
118
+ }
119
+
120
+ var responseData = response?.data;
121
+ var items = responseData?.items;
122
+
123
+ var instances = new List<dynamic>();
124
+ if (items != null)
125
+ {
126
+ foreach (var item in items)
127
+ {
128
+ instances.Add(item);
129
+ }
130
+ }
131
+
132
+ var publicHolidays = new List<dynamic>();
133
+ var advisorWorkingHours = new List<dynamic>();
134
+ var companyWorkingHours = new List<dynamic>();
135
+ var personalLeaves = new List<dynamic>();
136
+
137
+ foreach (var instance in instances)
138
+ {
139
+ var attributes = instance?.attributes;
140
+ if (attributes == null) continue;
141
+
142
+ var absenceType = attributes?.absenceType?.ToString();
143
+ var advisor = HasProperty(attributes, "advisor") ? attributes?.advisor?.ToString() : null;
144
+
145
+ switch (absenceType)
146
+ {
147
+ case "public-holiday":
148
+ publicHolidays.Add(instance);
149
+ break;
150
+ case "working-hours-change":
151
+ if (!string.IsNullOrEmpty(advisor) && advisor == advisorId)
152
+ advisorWorkingHours.Add(instance);
153
+ else if (string.IsNullOrEmpty(advisor))
154
+ companyWorkingHours.Add(instance);
155
+ break;
156
+ case "personal-leave" when advisor == advisorId:
157
+ personalLeaves.Add(instance);
158
+ break;
159
+ case "rezervation" when advisor == advisorId:
160
+ personalLeaves.Add(instance);
161
+ break;
162
+ }
163
+ }
164
+
165
+ var unavailableRanges = GetUnavailableRanges(personalLeaves, requestedDate);
166
+
167
+ // Half-day / full-day public holiday handling
168
+ var holidayRanges = GetHolidayRanges(publicHolidays, requestedDate);
169
+ if (holidayRanges != null)
170
+ {
171
+ if (holidayRanges.Count == 1
172
+ && holidayRanges[0].Item1 == TimeSpan.Zero
173
+ && holidayRanges[0].Item2 >= new TimeSpan(23, 59, 0))
174
+ {
175
+ return Task.FromResult(new ScriptResponse
176
+ {
177
+ Key = "public-holiday",
178
+ Data = new { availableSlots = new List<string>() },
179
+ Tags = new[] { "appointments", "holiday" }
180
+ });
181
+ }
182
+
183
+ unavailableRanges.AddRange(holidayRanges);
184
+ }
185
+
186
+ // Working hours priority: advisor-specific > company-wide
187
+ var workingHours = GetWorkingHoursForDay(advisorWorkingHours, dayOfWeek);
188
+ if (workingHours == null || workingHours.Count == 0)
189
+ workingHours = GetWorkingHoursForDay(companyWorkingHours, dayOfWeek);
190
+
191
+ if (workingHours == null || workingHours.Count == 0)
192
+ {
193
+ return Task.FromResult(new ScriptResponse
194
+ {
195
+ Key = "no-working-hours",
196
+ Data = new { availableSlots = new List<string>() },
197
+ Tags = new[] { "appointments", "closed" }
198
+ });
199
+ }
200
+
201
+ var availableSlots = CalculateAvailableSlots(workingHours, unavailableRanges, duration);
202
+
203
+ return Task.FromResult(new ScriptResponse
204
+ {
205
+ Key = "available-slots-success",
206
+ Data = new { availableSlots },
207
+ Tags = new[] { "appointments", "availability", "success" }
208
+ });
209
+ }
210
+ catch (Exception ex)
211
+ {
212
+ return Task.FromResult(new ScriptResponse
213
+ {
214
+ Key = "output-error",
215
+ Data = new
216
+ {
217
+ error = "Internal processing error",
218
+ errorCode = "PROCESSING_ERROR",
219
+ errorDescription = ex.Message
220
+ },
221
+ Tags = new[] { "appointments", "error" }
222
+ });
223
+ }
224
+ }
225
+
226
+ #region Helper Methods
227
+
228
+ private string ClassifyServiceError(string errorMessage)
229
+ {
230
+ if (errorMessage.Contains("timeout", StringComparison.OrdinalIgnoreCase))
231
+ return "timeout";
232
+ if (errorMessage.Contains("unauthorized", StringComparison.OrdinalIgnoreCase))
233
+ return "authentication";
234
+ if (errorMessage.Contains("forbidden", StringComparison.OrdinalIgnoreCase))
235
+ return "authorization";
236
+ if (errorMessage.Contains("not found", StringComparison.OrdinalIgnoreCase))
237
+ return "not-found";
238
+ if (errorMessage.Contains("service unavailable", StringComparison.OrdinalIgnoreCase))
239
+ return "service-unavailable";
240
+ if (errorMessage.Contains("connection", StringComparison.OrdinalIgnoreCase))
241
+ return "connection-error";
242
+
243
+ return "general-error";
244
+ }
245
+
246
+ private int GetAppointmentDuration(ScriptContext context)
247
+ {
248
+ const int defaultDuration = 30;
249
+
250
+ if (context.QueryParameters != null && HasProperty(context.QueryParameters, "duration"))
251
+ {
252
+ var durationParam = context.QueryParameters["duration"];
253
+ int queryDuration = 0;
254
+ if (durationParam != null && int.TryParse(durationParam.ToString(), out queryDuration))
255
+ {
256
+ return queryDuration;
257
+ }
258
+ }
259
+
260
+ var configDuration = GetConfigValue("Appointments:DefaultDuration");
261
+ int configDurationValue = 0;
262
+ if (!string.IsNullOrEmpty(configDuration) && int.TryParse(configDuration, out configDurationValue))
263
+ {
264
+ return configDurationValue;
265
+ }
266
+
267
+ return defaultDuration;
268
+ }
269
+
270
+ #endregion
271
+
272
+ #region Business Logic Methods
273
+
274
+ /// <summary>
275
+ /// Returns unavailable time ranges for public holidays on the requested date.
276
+ /// null = no holiday on this date.
277
+ /// Full-day holiday: single range 00:00-23:59.
278
+ /// Half-day holiday: partial range (e.g., 00:00-13:00).
279
+ /// </summary>
280
+ private List<Tuple<TimeSpan, TimeSpan>> GetHolidayRanges(List<dynamic> holidays, DateTime date)
281
+ {
282
+ var ranges = new List<Tuple<TimeSpan, TimeSpan>>();
283
+ bool hasHoliday = false;
284
+
285
+ foreach (var holiday in holidays)
286
+ {
287
+ var holidayData = holiday?.attributes;
288
+ if (holidayData == null) continue;
289
+
290
+ string startDateTimeStr = null;
291
+ string endDateTimeStr = null;
292
+
293
+ if (HasProperty(holidayData, "startDateTime") && holidayData.startDateTime != null)
294
+ startDateTimeStr = holidayData.startDateTime.ToString();
295
+
296
+ if (HasProperty(holidayData, "endDateTime") && holidayData.endDateTime != null)
297
+ endDateTimeStr = holidayData.endDateTime.ToString();
298
+
299
+ if (string.IsNullOrEmpty(startDateTimeStr) || string.IsNullOrEmpty(endDateTimeStr))
300
+ continue;
301
+
302
+ var startDateTime = DateTime.Parse(startDateTimeStr);
303
+ var endDateTime = DateTime.Parse(endDateTimeStr);
304
+
305
+ if (date.Date < startDateTime.Date || date.Date > endDateTime.Date)
306
+ continue;
307
+
308
+ hasHoliday = true;
309
+
310
+ if (startDateTime.Date != endDateTime.Date)
311
+ {
312
+ if (date.Date == startDateTime.Date)
313
+ ranges.Add(Tuple.Create(startDateTime.TimeOfDay, new TimeSpan(23, 59, 59)));
314
+ else if (date.Date == endDateTime.Date)
315
+ ranges.Add(Tuple.Create(TimeSpan.Zero, endDateTime.TimeOfDay));
316
+ else
317
+ ranges.Add(Tuple.Create(TimeSpan.Zero, new TimeSpan(23, 59, 59)));
318
+ }
319
+ else
320
+ {
321
+ ranges.Add(Tuple.Create(startDateTime.TimeOfDay, endDateTime.TimeOfDay));
322
+ }
323
+ }
324
+
325
+ return hasHoliday ? ranges : null;
326
+ }
327
+
328
+ private List<object> GetWorkingHoursForDay(List<dynamic> workingHoursChanges, string dayOfWeek)
329
+ {
330
+ foreach (var change in workingHoursChanges)
331
+ {
332
+ var changeData = change?.attributes;
333
+ if (changeData == null) continue;
334
+
335
+ var customWH = changeData?.customWorkingHours;
336
+ if (customWH == null) continue;
337
+
338
+ var dayHours = GetDayHoursFromCustom(customWH, dayOfWeek);
339
+ if (dayHours != null) return dayHours;
340
+ }
341
+
342
+ return null;
343
+ }
344
+
345
+ private List<object> GetDayHoursFromCustom(dynamic customWH, string dayOfWeek)
346
+ {
347
+ dynamic dayHours = null;
348
+ switch (dayOfWeek)
349
+ {
350
+ case "monday": dayHours = customWH?.monday; break;
351
+ case "tuesday": dayHours = customWH?.tuesday; break;
352
+ case "wednesday": dayHours = customWH?.wednesday; break;
353
+ case "thursday": dayHours = customWH?.thursday; break;
354
+ case "friday": dayHours = customWH?.friday; break;
355
+ case "saturday": dayHours = customWH?.saturday; break;
356
+ case "sunday": dayHours = customWH?.sunday; break;
357
+ }
358
+
359
+ if (dayHours == null) return null;
360
+
361
+ var result = new List<object>();
362
+ foreach (var range in dayHours)
363
+ {
364
+ var start = range?.start?.ToString();
365
+ var end = range?.end?.ToString();
366
+ if (!string.IsNullOrEmpty(start) && !string.IsNullOrEmpty(end))
367
+ {
368
+ result.Add(new { start, end });
369
+ }
370
+ }
371
+ return result.Count > 0 ? result : null;
372
+ }
373
+
374
+ private List<Tuple<TimeSpan, TimeSpan>> GetUnavailableRanges(List<dynamic> personalLeaves, DateTime date)
375
+ {
376
+ var ranges = new List<Tuple<TimeSpan, TimeSpan>>();
377
+
378
+ foreach (var leave in personalLeaves)
379
+ {
380
+ var leaveData = leave?.attributes;
381
+ if (leaveData == null) continue;
382
+
383
+ string startDateTimeStr = null;
384
+ string endDateTimeStr = null;
385
+
386
+ if (HasProperty(leaveData, "startDateTime") && leaveData.startDateTime != null)
387
+ startDateTimeStr = leaveData.startDateTime.ToString();
388
+
389
+ if (HasProperty(leaveData, "endDateTime") && leaveData.endDateTime != null)
390
+ endDateTimeStr = leaveData.endDateTime.ToString();
391
+
392
+ if (string.IsNullOrEmpty(startDateTimeStr) || string.IsNullOrEmpty(endDateTimeStr))
393
+ continue;
394
+
395
+ var startDateTime = DateTime.Parse(startDateTimeStr);
396
+ var endDateTime = DateTime.Parse(endDateTimeStr);
397
+
398
+ if (date.Date < startDateTime.Date || date.Date > endDateTime.Date)
399
+ continue;
400
+
401
+ if (startDateTime.Date != endDateTime.Date)
402
+ {
403
+ if (date.Date == startDateTime.Date)
404
+ ranges.Add(Tuple.Create(startDateTime.TimeOfDay, new TimeSpan(23, 59, 59)));
405
+ else if (date.Date == endDateTime.Date)
406
+ ranges.Add(Tuple.Create(TimeSpan.Zero, endDateTime.TimeOfDay));
407
+ else
408
+ ranges.Add(Tuple.Create(TimeSpan.Zero, new TimeSpan(23, 59, 59)));
409
+ }
410
+ else
411
+ {
412
+ ranges.Add(Tuple.Create(startDateTime.TimeOfDay, endDateTime.TimeOfDay));
413
+ }
414
+ }
415
+
416
+ return ranges;
417
+ }
418
+
419
+ private List<string> CalculateAvailableSlots(List<object> workingHours, List<Tuple<TimeSpan, TimeSpan>> unavailableRanges, int durationMinutes)
420
+ {
421
+ var slots = new List<string>();
422
+ var duration = TimeSpan.FromMinutes(durationMinutes);
423
+
424
+ foreach (var wh in workingHours)
425
+ {
426
+ dynamic whDyn = wh;
427
+ var startStr = whDyn?.start?.ToString();
428
+ var endStr = whDyn?.end?.ToString();
429
+
430
+ if (string.IsNullOrEmpty(startStr) || string.IsNullOrEmpty(endStr))
431
+ continue;
432
+
433
+ var whStart = TimeSpan.Parse(startStr);
434
+ var whEnd = TimeSpan.Parse(endStr);
435
+
436
+ if (whStart == whEnd)
437
+ continue;
438
+
439
+ var current = whStart;
440
+ while (current + duration <= whEnd)
441
+ {
442
+ var slotStart = current;
443
+ var slotEnd = current + duration;
444
+
445
+ bool isAvailable = true;
446
+ foreach (var ur in unavailableRanges)
447
+ {
448
+ if (slotStart < ur.Item2 && slotEnd > ur.Item1)
449
+ {
450
+ isAvailable = false;
451
+ break;
452
+ }
453
+ }
454
+
455
+ if (isAvailable)
456
+ {
457
+ slots.Add($"{slotStart:hh\\:mm}-{slotEnd:hh\\:mm}");
458
+ }
459
+
460
+ current = current + duration;
461
+ }
462
+ }
463
+
464
+ return slots;
465
+ }
466
+
467
+ #endregion
468
+ }
@@ -0,0 +1,202 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using System.Threading.Tasks;
4
+ using BBT.Workflow.Scripting;
5
+ using BBT.Workflow.Definitions;
6
+
7
+ /// <summary>
8
+ /// Mapping for query-chat-room-instances DaprServiceTask (Type 3).
9
+ /// Flow-level function (scope: F) -- no instance context.
10
+ /// Reads "user" OR "advisor" from query parameters (mutually exclusive).
11
+ /// For advisor: server filter advisorId=X OR members contains { role: "member", memberId: X }.
12
+ /// For user: returns rooms where user=X (server-side filter).
13
+ /// </summary>
14
+ public class GetChatRoomsMapping : ScriptBase, IMapping
15
+ {
16
+ public Task<ScriptResponse> InputHandler(WorkflowTask task, ScriptContext context)
17
+ {
18
+ var serviceTask = task as DaprServiceTask;
19
+ if (serviceTask == null)
20
+ throw new InvalidOperationException("Task must be a DaprServiceTask");
21
+
22
+ var appId = GetConfigValue("DAPR_APP_ID");
23
+ if (!string.IsNullOrEmpty(appId))
24
+ serviceTask.SetAppId(appId);
25
+
26
+ var user = ReadParam(context, "user");
27
+ var advisor = ReadParam(context, "advisor");
28
+
29
+ bool hasUser = !string.IsNullOrWhiteSpace(user);
30
+ bool hasAdvisor = !string.IsNullOrWhiteSpace(advisor);
31
+
32
+ if (hasUser && hasAdvisor)
33
+ throw new InvalidOperationException("VALIDATION_ERROR: user and advisor are mutually exclusive, provide only one");
34
+
35
+ if (!hasUser && !hasAdvisor)
36
+ throw new InvalidOperationException("VALIDATION_ERROR: either user or advisor query parameter is required");
37
+
38
+ string attributeFilter;
39
+ if (hasUser)
40
+ {
41
+ var escaped = user.Replace("\\", "\\\\").Replace("\"", "\\\"");
42
+ attributeFilter = "{\"attributes\":{\"user\":{\"eq\":\"" + escaped + "\"}}}";
43
+ }
44
+ else
45
+ {
46
+ var escaped = advisor.Replace("\\", "\\\\").Replace("\"", "\\\"");
47
+ attributeFilter = "{\"attributes\":{\"advisorId\":{\"eq\":\"" + escaped + "\"}}}";
48
+
49
+ // attributeFilter = "{\"or\":[" +
50
+ // "{\"attributes\":{\"advisorId\":{\"eq\":\"" + escaped + "\"}}}," +
51
+ // "{\"attributes\":{\"members\":{\"contains\":{\"role\":\"member\",\"memberId\":\"" + escaped + "\"}}}" +
52
+ // "]}";
53
+ }
54
+
55
+ var roomType = ReadParam(context, "roomType");
56
+ if (!string.IsNullOrWhiteSpace(roomType))
57
+ {
58
+ var escapedType = roomType.Replace("\\", "\\\\").Replace("\"", "\\\"");
59
+ attributeFilter = "{\"and\":[" + attributeFilter + ",{\"attributes\":{\"roomType\":{\"eq\":\"" + escapedType + "\"}}}]}";
60
+ }
61
+
62
+ var state = ReadParam(context, "state");
63
+ string stateFilter;
64
+ if (string.IsNullOrWhiteSpace(state))
65
+ {
66
+ stateFilter = "{\"or\":[{\"currentState\":{\"eq\":\"activated\"}},{\"currentState\":{\"eq\":\"deactivated\"}}]}";
67
+ }
68
+ else
69
+ {
70
+ var escapedState = state.Replace("\\", "\\\\").Replace("\"", "\\\"");
71
+ stateFilter = "{\"currentState\":{\"eq\":\"" + escapedState + "\"}}";
72
+ }
73
+
74
+ var filterBody = "{\"and\":[" + attributeFilter + "," + stateFilter + "]}";
75
+ var queryParams = new List<string>
76
+ {
77
+ "pageSize=100",
78
+ "filter=" + filterBody
79
+ };
80
+
81
+ serviceTask.SetQueryString(string.Join("&", queryParams));
82
+
83
+ return Task.FromResult(new ScriptResponse());
84
+ }
85
+
86
+ public Task<ScriptResponse> OutputHandler(ScriptContext context)
87
+ {
88
+ try
89
+ {
90
+ var response = context.Body;
91
+
92
+ if (response?.isSuccess == false)
93
+ {
94
+ var errorMessage = response?.errorMessage?.ToString() ?? "Service call failed";
95
+ return Task.FromResult(new ScriptResponse
96
+ {
97
+ Key = "get-chat-rooms-failure",
98
+ Data = new
99
+ {
100
+ error = errorMessage,
101
+ errorCode = "SERVICE_ERROR",
102
+ rooms = new List<object>()
103
+ },
104
+ Tags = new[] { "chat", "rooms", "error" }
105
+ });
106
+ }
107
+
108
+ var responseData = response?.data;
109
+ var items = responseData?.items;
110
+ var rooms = new List<object>();
111
+
112
+ if (items != null)
113
+ {
114
+ foreach (var item in items)
115
+ {
116
+ // Runtime may expose instance payload as "attributes" or "data"
117
+ var attributes = item?.attributes ?? item?.data;
118
+ if (attributes == null) continue;
119
+
120
+ var metadata = item?.metadata;
121
+ string currentState = metadata != null ? GetString(metadata, "currentState") : null;
122
+
123
+ string roomId = null;
124
+ if (HasProperty(attributes, "chatIntegration") && attributes.chatIntegration != null)
125
+ roomId = GetString(attributes.chatIntegration, "roomId");
126
+
127
+ var members = new List<object>();
128
+ if (HasProperty(attributes, "members") && attributes.members != null)
129
+ {
130
+ foreach (var m in attributes.members)
131
+ {
132
+ var mid = GetString(m, "memberId") ?? GetString(m, "advisorId");
133
+ members.Add(new { memberId = mid, role = GetString(m, "role") });
134
+ }
135
+ }
136
+
137
+ var instanceKey = GetString(item, "key");
138
+ rooms.Add(new
139
+ {
140
+ id = GetString(item, "id") ?? instanceKey,
141
+ instanceKey = instanceKey,
142
+ user = GetString(attributes, "user"),
143
+ advisorType = GetString(attributes, "advisorType"),
144
+ advisorId = GetString(attributes, "advisorId"),
145
+ roomId = roomId,
146
+ status = GetString(attributes, "status"),
147
+ roomType = GetString(attributes, "roomType"),
148
+ startDateTime = GetString(attributes, "startDateTime"),
149
+ endDateTime = GetString(attributes, "endDateTime"),
150
+ members = members,
151
+ metadata = currentState != null ? new { currentState } : null
152
+ });
153
+ }
154
+ }
155
+
156
+ return Task.FromResult(new ScriptResponse
157
+ {
158
+ Key = "get-chat-rooms-success",
159
+ Data = new { rooms = rooms },
160
+ Tags = new[] { "chat", "rooms", "success" }
161
+ });
162
+ }
163
+ catch (Exception ex)
164
+ {
165
+ return Task.FromResult(new ScriptResponse
166
+ {
167
+ Key = "get-chat-rooms-error",
168
+ Data = new
169
+ {
170
+ error = "Internal processing error",
171
+ errorCode = "PROCESSING_ERROR",
172
+ errorDescription = ex.Message,
173
+ rooms = new List<object>()
174
+ },
175
+ Tags = new[] { "chat", "rooms", "error" }
176
+ });
177
+ }
178
+ }
179
+
180
+ private string ReadParam(ScriptContext context, string name)
181
+ {
182
+ if (context.QueryParameters == null || !HasProperty(context.QueryParameters, name))
183
+ return null;
184
+ var val = context.QueryParameters[name];
185
+ return val != null ? val.ToString() : null;
186
+ }
187
+
188
+ private string GetString(dynamic obj, string name)
189
+ {
190
+ if (obj == null) return null;
191
+ try
192
+ {
193
+ if (HasProperty(obj, name))
194
+ {
195
+ var v = GetPropertyValue(obj, name);
196
+ return v?.ToString();
197
+ }
198
+ }
199
+ catch { }
200
+ return null;
201
+ }
202
+ }