@repzo/widget-react-native 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (448) hide show
  1. package/README.md +482 -0
  2. package/lib/commonjs/agent-ui-blocks.js +159 -0
  3. package/lib/commonjs/agent-ui-blocks.js.map +1 -0
  4. package/lib/commonjs/api/client.js +267 -0
  5. package/lib/commonjs/api/client.js.map +1 -0
  6. package/lib/commonjs/api/realtime.js +193 -0
  7. package/lib/commonjs/api/realtime.js.map +1 -0
  8. package/lib/commonjs/api/sse-parser.js +58 -0
  9. package/lib/commonjs/api/sse-parser.js.map +1 -0
  10. package/lib/commonjs/controller.js +23 -0
  11. package/lib/commonjs/controller.js.map +1 -0
  12. package/lib/commonjs/device-id.js +75 -0
  13. package/lib/commonjs/device-id.js.map +1 -0
  14. package/lib/commonjs/events.js +49 -0
  15. package/lib/commonjs/events.js.map +1 -0
  16. package/lib/commonjs/i18n.js +106 -0
  17. package/lib/commonjs/i18n.js.map +1 -0
  18. package/lib/commonjs/index.js +98 -0
  19. package/lib/commonjs/index.js.map +1 -0
  20. package/lib/commonjs/locales.js +85 -0
  21. package/lib/commonjs/locales.js.map +1 -0
  22. package/lib/commonjs/package.json +1 -0
  23. package/lib/commonjs/push/push-notifications.js +208 -0
  24. package/lib/commonjs/push/push-notifications.js.map +1 -0
  25. package/lib/commonjs/state/use-widget-state.js +23 -0
  26. package/lib/commonjs/state/use-widget-state.js.map +1 -0
  27. package/lib/commonjs/state/widget-store.js +1682 -0
  28. package/lib/commonjs/state/widget-store.js.map +1 -0
  29. package/lib/commonjs/storage.js +147 -0
  30. package/lib/commonjs/storage.js.map +1 -0
  31. package/lib/commonjs/types.js +44 -0
  32. package/lib/commonjs/types.js.map +1 -0
  33. package/lib/commonjs/ui/WidgetProvider.js +404 -0
  34. package/lib/commonjs/ui/WidgetProvider.js.map +1 -0
  35. package/lib/commonjs/ui/components/AgentUiBlocks.js +864 -0
  36. package/lib/commonjs/ui/components/AgentUiBlocks.js.map +1 -0
  37. package/lib/commonjs/ui/components/Avatar.js +76 -0
  38. package/lib/commonjs/ui/components/Avatar.js.map +1 -0
  39. package/lib/commonjs/ui/components/ConversationRow.js +155 -0
  40. package/lib/commonjs/ui/components/ConversationRow.js.map +1 -0
  41. package/lib/commonjs/ui/components/ErrorBoundary.js +44 -0
  42. package/lib/commonjs/ui/components/ErrorBoundary.js.map +1 -0
  43. package/lib/commonjs/ui/components/Gradient.js +168 -0
  44. package/lib/commonjs/ui/components/Gradient.js.map +1 -0
  45. package/lib/commonjs/ui/components/Launcher.js +125 -0
  46. package/lib/commonjs/ui/components/Launcher.js.map +1 -0
  47. package/lib/commonjs/ui/components/Markdown.js +257 -0
  48. package/lib/commonjs/ui/components/Markdown.js.map +1 -0
  49. package/lib/commonjs/ui/components/OutboundMessageCard.js +157 -0
  50. package/lib/commonjs/ui/components/OutboundMessageCard.js.map +1 -0
  51. package/lib/commonjs/ui/components/WidgetPanel.js +131 -0
  52. package/lib/commonjs/ui/components/WidgetPanel.js.map +1 -0
  53. package/lib/commonjs/ui/components/icons.js +383 -0
  54. package/lib/commonjs/ui/components/icons.js.map +1 -0
  55. package/lib/commonjs/ui/hooks/use-keyboard-overlap.js +113 -0
  56. package/lib/commonjs/ui/hooks/use-keyboard-overlap.js.map +1 -0
  57. package/lib/commonjs/ui/hooks/use-reduce-motion.js +38 -0
  58. package/lib/commonjs/ui/hooks/use-reduce-motion.js.map +1 -0
  59. package/lib/commonjs/ui/hooks/use-widget-insets.js +41 -0
  60. package/lib/commonjs/ui/hooks/use-widget-insets.js.map +1 -0
  61. package/lib/commonjs/ui/screens/chat/ChatView.js +590 -0
  62. package/lib/commonjs/ui/screens/chat/ChatView.js.map +1 -0
  63. package/lib/commonjs/ui/screens/chat/ChatView.styles.js +362 -0
  64. package/lib/commonjs/ui/screens/chat/ChatView.styles.js.map +1 -0
  65. package/lib/commonjs/ui/screens/chat/ChatView.types.js +6 -0
  66. package/lib/commonjs/ui/screens/chat/ChatView.types.js.map +1 -0
  67. package/lib/commonjs/ui/screens/conversations/ConversationsList.js +150 -0
  68. package/lib/commonjs/ui/screens/conversations/ConversationsList.js.map +1 -0
  69. package/lib/commonjs/ui/screens/conversations/ConversationsList.styles.js +118 -0
  70. package/lib/commonjs/ui/screens/conversations/ConversationsList.styles.js.map +1 -0
  71. package/lib/commonjs/ui/screens/conversations/ConversationsList.types.js +6 -0
  72. package/lib/commonjs/ui/screens/conversations/ConversationsList.types.js.map +1 -0
  73. package/lib/commonjs/ui/screens/home/HomeView.js +336 -0
  74. package/lib/commonjs/ui/screens/home/HomeView.js.map +1 -0
  75. package/lib/commonjs/ui/screens/home/HomeView.styles.js +245 -0
  76. package/lib/commonjs/ui/screens/home/HomeView.styles.js.map +1 -0
  77. package/lib/commonjs/ui/screens/home/HomeView.types.js +6 -0
  78. package/lib/commonjs/ui/screens/home/HomeView.types.js.map +1 -0
  79. package/lib/commonjs/ui/screens/home/greeting.js +49 -0
  80. package/lib/commonjs/ui/screens/home/greeting.js.map +1 -0
  81. package/lib/commonjs/ui/screens/pre-chat/PreChatForm.js +229 -0
  82. package/lib/commonjs/ui/screens/pre-chat/PreChatForm.js.map +1 -0
  83. package/lib/commonjs/ui/screens/pre-chat/PreChatForm.styles.js +125 -0
  84. package/lib/commonjs/ui/screens/pre-chat/PreChatForm.styles.js.map +1 -0
  85. package/lib/commonjs/ui/screens/pre-chat/PreChatForm.types.js +6 -0
  86. package/lib/commonjs/ui/screens/pre-chat/PreChatForm.types.js.map +1 -0
  87. package/lib/commonjs/ui/styles/global.js +55 -0
  88. package/lib/commonjs/ui/styles/global.js.map +1 -0
  89. package/lib/commonjs/ui/styles/theme.js +148 -0
  90. package/lib/commonjs/ui/styles/theme.js.map +1 -0
  91. package/lib/commonjs/ui/utils/conversations.js +125 -0
  92. package/lib/commonjs/ui/utils/conversations.js.map +1 -0
  93. package/lib/commonjs/ui/utils/html.js +44 -0
  94. package/lib/commonjs/ui/utils/html.js.map +1 -0
  95. package/lib/commonjs/ui/utils/native-gradient.js +65 -0
  96. package/lib/commonjs/ui/utils/native-gradient.js.map +1 -0
  97. package/lib/commonjs/ui/utils/rtl.js +56 -0
  98. package/lib/commonjs/ui/utils/rtl.js.map +1 -0
  99. package/lib/commonjs/ui/utils/safe-url.js +67 -0
  100. package/lib/commonjs/ui/utils/safe-url.js.map +1 -0
  101. package/lib/module/agent-ui-blocks.js +152 -0
  102. package/lib/module/agent-ui-blocks.js.map +1 -0
  103. package/lib/module/api/client.js +261 -0
  104. package/lib/module/api/client.js.map +1 -0
  105. package/lib/module/api/realtime.js +189 -0
  106. package/lib/module/api/realtime.js.map +1 -0
  107. package/lib/module/api/sse-parser.js +53 -0
  108. package/lib/module/api/sse-parser.js.map +1 -0
  109. package/lib/module/controller.js +19 -0
  110. package/lib/module/controller.js.map +1 -0
  111. package/lib/module/device-id.js +69 -0
  112. package/lib/module/device-id.js.map +1 -0
  113. package/lib/module/events.js +44 -0
  114. package/lib/module/events.js.map +1 -0
  115. package/lib/module/i18n.js +99 -0
  116. package/lib/module/i18n.js.map +1 -0
  117. package/lib/module/index.js +55 -0
  118. package/lib/module/index.js.map +1 -0
  119. package/lib/module/locales.js +80 -0
  120. package/lib/module/locales.js.map +1 -0
  121. package/lib/module/package.json +1 -0
  122. package/lib/module/push/push-notifications.js +204 -0
  123. package/lib/module/push/push-notifications.js.map +1 -0
  124. package/lib/module/state/use-widget-state.js +18 -0
  125. package/lib/module/state/use-widget-state.js.map +1 -0
  126. package/lib/module/state/widget-store.js +1677 -0
  127. package/lib/module/state/widget-store.js.map +1 -0
  128. package/lib/module/storage.js +141 -0
  129. package/lib/module/storage.js.map +1 -0
  130. package/lib/module/types.js +40 -0
  131. package/lib/module/types.js.map +1 -0
  132. package/lib/module/ui/WidgetProvider.js +400 -0
  133. package/lib/module/ui/WidgetProvider.js.map +1 -0
  134. package/lib/module/ui/components/AgentUiBlocks.js +857 -0
  135. package/lib/module/ui/components/AgentUiBlocks.js.map +1 -0
  136. package/lib/module/ui/components/Avatar.js +71 -0
  137. package/lib/module/ui/components/Avatar.js.map +1 -0
  138. package/lib/module/ui/components/ConversationRow.js +150 -0
  139. package/lib/module/ui/components/ConversationRow.js.map +1 -0
  140. package/lib/module/ui/components/ErrorBoundary.js +39 -0
  141. package/lib/module/ui/components/ErrorBoundary.js.map +1 -0
  142. package/lib/module/ui/components/Gradient.js +165 -0
  143. package/lib/module/ui/components/Gradient.js.map +1 -0
  144. package/lib/module/ui/components/Launcher.js +121 -0
  145. package/lib/module/ui/components/Launcher.js.map +1 -0
  146. package/lib/module/ui/components/Markdown.js +253 -0
  147. package/lib/module/ui/components/Markdown.js.map +1 -0
  148. package/lib/module/ui/components/OutboundMessageCard.js +153 -0
  149. package/lib/module/ui/components/OutboundMessageCard.js.map +1 -0
  150. package/lib/module/ui/components/WidgetPanel.js +127 -0
  151. package/lib/module/ui/components/WidgetPanel.js.map +1 -0
  152. package/lib/module/ui/components/icons.js +370 -0
  153. package/lib/module/ui/components/icons.js.map +1 -0
  154. package/lib/module/ui/hooks/use-keyboard-overlap.js +109 -0
  155. package/lib/module/ui/hooks/use-keyboard-overlap.js.map +1 -0
  156. package/lib/module/ui/hooks/use-reduce-motion.js +34 -0
  157. package/lib/module/ui/hooks/use-reduce-motion.js.map +1 -0
  158. package/lib/module/ui/hooks/use-widget-insets.js +37 -0
  159. package/lib/module/ui/hooks/use-widget-insets.js.map +1 -0
  160. package/lib/module/ui/screens/chat/ChatView.js +586 -0
  161. package/lib/module/ui/screens/chat/ChatView.js.map +1 -0
  162. package/lib/module/ui/screens/chat/ChatView.styles.js +358 -0
  163. package/lib/module/ui/screens/chat/ChatView.styles.js.map +1 -0
  164. package/lib/module/ui/screens/chat/ChatView.types.js +4 -0
  165. package/lib/module/ui/screens/chat/ChatView.types.js.map +1 -0
  166. package/lib/module/ui/screens/conversations/ConversationsList.js +146 -0
  167. package/lib/module/ui/screens/conversations/ConversationsList.js.map +1 -0
  168. package/lib/module/ui/screens/conversations/ConversationsList.styles.js +114 -0
  169. package/lib/module/ui/screens/conversations/ConversationsList.styles.js.map +1 -0
  170. package/lib/module/ui/screens/conversations/ConversationsList.types.js +4 -0
  171. package/lib/module/ui/screens/conversations/ConversationsList.types.js.map +1 -0
  172. package/lib/module/ui/screens/home/HomeView.js +332 -0
  173. package/lib/module/ui/screens/home/HomeView.js.map +1 -0
  174. package/lib/module/ui/screens/home/HomeView.styles.js +242 -0
  175. package/lib/module/ui/screens/home/HomeView.styles.js.map +1 -0
  176. package/lib/module/ui/screens/home/HomeView.types.js +4 -0
  177. package/lib/module/ui/screens/home/HomeView.types.js.map +1 -0
  178. package/lib/module/ui/screens/home/greeting.js +44 -0
  179. package/lib/module/ui/screens/home/greeting.js.map +1 -0
  180. package/lib/module/ui/screens/pre-chat/PreChatForm.js +225 -0
  181. package/lib/module/ui/screens/pre-chat/PreChatForm.js.map +1 -0
  182. package/lib/module/ui/screens/pre-chat/PreChatForm.styles.js +121 -0
  183. package/lib/module/ui/screens/pre-chat/PreChatForm.styles.js.map +1 -0
  184. package/lib/module/ui/screens/pre-chat/PreChatForm.types.js +4 -0
  185. package/lib/module/ui/screens/pre-chat/PreChatForm.types.js.map +1 -0
  186. package/lib/module/ui/styles/global.js +51 -0
  187. package/lib/module/ui/styles/global.js.map +1 -0
  188. package/lib/module/ui/styles/theme.js +137 -0
  189. package/lib/module/ui/styles/theme.js.map +1 -0
  190. package/lib/module/ui/utils/conversations.js +115 -0
  191. package/lib/module/ui/utils/conversations.js.map +1 -0
  192. package/lib/module/ui/utils/html.js +39 -0
  193. package/lib/module/ui/utils/html.js.map +1 -0
  194. package/lib/module/ui/utils/native-gradient.js +60 -0
  195. package/lib/module/ui/utils/native-gradient.js.map +1 -0
  196. package/lib/module/ui/utils/rtl.js +49 -0
  197. package/lib/module/ui/utils/rtl.js.map +1 -0
  198. package/lib/module/ui/utils/safe-url.js +59 -0
  199. package/lib/module/ui/utils/safe-url.js.map +1 -0
  200. package/lib/typescript/commonjs/agent-ui-blocks.d.ts +20 -0
  201. package/lib/typescript/commonjs/agent-ui-blocks.d.ts.map +1 -0
  202. package/lib/typescript/commonjs/api/client.d.ts +115 -0
  203. package/lib/typescript/commonjs/api/client.d.ts.map +1 -0
  204. package/lib/typescript/commonjs/api/realtime.d.ts +39 -0
  205. package/lib/typescript/commonjs/api/realtime.d.ts.map +1 -0
  206. package/lib/typescript/commonjs/api/sse-parser.d.ts +13 -0
  207. package/lib/typescript/commonjs/api/sse-parser.d.ts.map +1 -0
  208. package/lib/typescript/commonjs/controller.d.ts +15 -0
  209. package/lib/typescript/commonjs/controller.d.ts.map +1 -0
  210. package/lib/typescript/commonjs/device-id.d.ts +15 -0
  211. package/lib/typescript/commonjs/device-id.d.ts.map +1 -0
  212. package/lib/typescript/commonjs/events.d.ts +41 -0
  213. package/lib/typescript/commonjs/events.d.ts.map +1 -0
  214. package/lib/typescript/commonjs/i18n.d.ts +78 -0
  215. package/lib/typescript/commonjs/i18n.d.ts.map +1 -0
  216. package/lib/typescript/commonjs/index.d.ts +103 -0
  217. package/lib/typescript/commonjs/index.d.ts.map +1 -0
  218. package/lib/typescript/commonjs/locales.d.ts +18 -0
  219. package/lib/typescript/commonjs/locales.d.ts.map +1 -0
  220. package/lib/typescript/commonjs/package.json +1 -0
  221. package/lib/typescript/commonjs/push/push-notifications.d.ts +71 -0
  222. package/lib/typescript/commonjs/push/push-notifications.d.ts.map +1 -0
  223. package/lib/typescript/commonjs/state/use-widget-state.d.ts +10 -0
  224. package/lib/typescript/commonjs/state/use-widget-state.d.ts.map +1 -0
  225. package/lib/typescript/commonjs/state/widget-store.d.ts +227 -0
  226. package/lib/typescript/commonjs/state/widget-store.d.ts.map +1 -0
  227. package/lib/typescript/commonjs/storage.d.ts +42 -0
  228. package/lib/typescript/commonjs/storage.d.ts.map +1 -0
  229. package/lib/typescript/commonjs/types.d.ts +391 -0
  230. package/lib/typescript/commonjs/types.d.ts.map +1 -0
  231. package/lib/typescript/commonjs/ui/WidgetProvider.d.ts +15 -0
  232. package/lib/typescript/commonjs/ui/WidgetProvider.d.ts.map +1 -0
  233. package/lib/typescript/commonjs/ui/components/AgentUiBlocks.d.ts +27 -0
  234. package/lib/typescript/commonjs/ui/components/AgentUiBlocks.d.ts.map +1 -0
  235. package/lib/typescript/commonjs/ui/components/Avatar.d.ts +19 -0
  236. package/lib/typescript/commonjs/ui/components/Avatar.d.ts.map +1 -0
  237. package/lib/typescript/commonjs/ui/components/ConversationRow.d.ts +26 -0
  238. package/lib/typescript/commonjs/ui/components/ConversationRow.d.ts.map +1 -0
  239. package/lib/typescript/commonjs/ui/components/ErrorBoundary.d.ts +27 -0
  240. package/lib/typescript/commonjs/ui/components/ErrorBoundary.d.ts.map +1 -0
  241. package/lib/typescript/commonjs/ui/components/Gradient.d.ts +33 -0
  242. package/lib/typescript/commonjs/ui/components/Gradient.d.ts.map +1 -0
  243. package/lib/typescript/commonjs/ui/components/Launcher.d.ts +18 -0
  244. package/lib/typescript/commonjs/ui/components/Launcher.d.ts.map +1 -0
  245. package/lib/typescript/commonjs/ui/components/Markdown.d.ts +19 -0
  246. package/lib/typescript/commonjs/ui/components/Markdown.d.ts.map +1 -0
  247. package/lib/typescript/commonjs/ui/components/OutboundMessageCard.d.ts +20 -0
  248. package/lib/typescript/commonjs/ui/components/OutboundMessageCard.d.ts.map +1 -0
  249. package/lib/typescript/commonjs/ui/components/WidgetPanel.d.ts +26 -0
  250. package/lib/typescript/commonjs/ui/components/WidgetPanel.d.ts.map +1 -0
  251. package/lib/typescript/commonjs/ui/components/icons.d.ts +38 -0
  252. package/lib/typescript/commonjs/ui/components/icons.d.ts.map +1 -0
  253. package/lib/typescript/commonjs/ui/hooks/use-keyboard-overlap.d.ts +33 -0
  254. package/lib/typescript/commonjs/ui/hooks/use-keyboard-overlap.d.ts.map +1 -0
  255. package/lib/typescript/commonjs/ui/hooks/use-reduce-motion.d.ts +11 -0
  256. package/lib/typescript/commonjs/ui/hooks/use-reduce-motion.d.ts.map +1 -0
  257. package/lib/typescript/commonjs/ui/hooks/use-widget-insets.d.ts +16 -0
  258. package/lib/typescript/commonjs/ui/hooks/use-widget-insets.d.ts.map +1 -0
  259. package/lib/typescript/commonjs/ui/screens/chat/ChatView.d.ts +11 -0
  260. package/lib/typescript/commonjs/ui/screens/chat/ChatView.d.ts.map +1 -0
  261. package/lib/typescript/commonjs/ui/screens/chat/ChatView.styles.d.ts +349 -0
  262. package/lib/typescript/commonjs/ui/screens/chat/ChatView.styles.d.ts.map +1 -0
  263. package/lib/typescript/commonjs/ui/screens/chat/ChatView.types.d.ts +48 -0
  264. package/lib/typescript/commonjs/ui/screens/chat/ChatView.types.d.ts.map +1 -0
  265. package/lib/typescript/commonjs/ui/screens/conversations/ConversationsList.d.ts +10 -0
  266. package/lib/typescript/commonjs/ui/screens/conversations/ConversationsList.d.ts.map +1 -0
  267. package/lib/typescript/commonjs/ui/screens/conversations/ConversationsList.styles.d.ts +113 -0
  268. package/lib/typescript/commonjs/ui/screens/conversations/ConversationsList.styles.d.ts.map +1 -0
  269. package/lib/typescript/commonjs/ui/screens/conversations/ConversationsList.types.d.ts +31 -0
  270. package/lib/typescript/commonjs/ui/screens/conversations/ConversationsList.types.d.ts.map +1 -0
  271. package/lib/typescript/commonjs/ui/screens/home/HomeView.d.ts +17 -0
  272. package/lib/typescript/commonjs/ui/screens/home/HomeView.d.ts.map +1 -0
  273. package/lib/typescript/commonjs/ui/screens/home/HomeView.styles.d.ts +227 -0
  274. package/lib/typescript/commonjs/ui/screens/home/HomeView.styles.d.ts.map +1 -0
  275. package/lib/typescript/commonjs/ui/screens/home/HomeView.types.d.ts +40 -0
  276. package/lib/typescript/commonjs/ui/screens/home/HomeView.types.d.ts.map +1 -0
  277. package/lib/typescript/commonjs/ui/screens/home/greeting.d.ts +19 -0
  278. package/lib/typescript/commonjs/ui/screens/home/greeting.d.ts.map +1 -0
  279. package/lib/typescript/commonjs/ui/screens/pre-chat/PreChatForm.d.ts +7 -0
  280. package/lib/typescript/commonjs/ui/screens/pre-chat/PreChatForm.d.ts.map +1 -0
  281. package/lib/typescript/commonjs/ui/screens/pre-chat/PreChatForm.styles.d.ts +115 -0
  282. package/lib/typescript/commonjs/ui/screens/pre-chat/PreChatForm.styles.d.ts.map +1 -0
  283. package/lib/typescript/commonjs/ui/screens/pre-chat/PreChatForm.types.d.ts +20 -0
  284. package/lib/typescript/commonjs/ui/screens/pre-chat/PreChatForm.types.d.ts.map +1 -0
  285. package/lib/typescript/commonjs/ui/styles/global.d.ts +46 -0
  286. package/lib/typescript/commonjs/ui/styles/global.d.ts.map +1 -0
  287. package/lib/typescript/commonjs/ui/styles/theme.d.ts +103 -0
  288. package/lib/typescript/commonjs/ui/styles/theme.d.ts.map +1 -0
  289. package/lib/typescript/commonjs/ui/utils/conversations.d.ts +41 -0
  290. package/lib/typescript/commonjs/ui/utils/conversations.d.ts.map +1 -0
  291. package/lib/typescript/commonjs/ui/utils/html.d.ts +10 -0
  292. package/lib/typescript/commonjs/ui/utils/html.d.ts.map +1 -0
  293. package/lib/typescript/commonjs/ui/utils/native-gradient.d.ts +61 -0
  294. package/lib/typescript/commonjs/ui/utils/native-gradient.d.ts.map +1 -0
  295. package/lib/typescript/commonjs/ui/utils/rtl.d.ts +33 -0
  296. package/lib/typescript/commonjs/ui/utils/rtl.d.ts.map +1 -0
  297. package/lib/typescript/commonjs/ui/utils/safe-url.d.ts +21 -0
  298. package/lib/typescript/commonjs/ui/utils/safe-url.d.ts.map +1 -0
  299. package/lib/typescript/module/agent-ui-blocks.d.ts +20 -0
  300. package/lib/typescript/module/agent-ui-blocks.d.ts.map +1 -0
  301. package/lib/typescript/module/api/client.d.ts +115 -0
  302. package/lib/typescript/module/api/client.d.ts.map +1 -0
  303. package/lib/typescript/module/api/realtime.d.ts +39 -0
  304. package/lib/typescript/module/api/realtime.d.ts.map +1 -0
  305. package/lib/typescript/module/api/sse-parser.d.ts +13 -0
  306. package/lib/typescript/module/api/sse-parser.d.ts.map +1 -0
  307. package/lib/typescript/module/controller.d.ts +15 -0
  308. package/lib/typescript/module/controller.d.ts.map +1 -0
  309. package/lib/typescript/module/device-id.d.ts +15 -0
  310. package/lib/typescript/module/device-id.d.ts.map +1 -0
  311. package/lib/typescript/module/events.d.ts +41 -0
  312. package/lib/typescript/module/events.d.ts.map +1 -0
  313. package/lib/typescript/module/i18n.d.ts +78 -0
  314. package/lib/typescript/module/i18n.d.ts.map +1 -0
  315. package/lib/typescript/module/index.d.ts +103 -0
  316. package/lib/typescript/module/index.d.ts.map +1 -0
  317. package/lib/typescript/module/locales.d.ts +18 -0
  318. package/lib/typescript/module/locales.d.ts.map +1 -0
  319. package/lib/typescript/module/package.json +1 -0
  320. package/lib/typescript/module/push/push-notifications.d.ts +71 -0
  321. package/lib/typescript/module/push/push-notifications.d.ts.map +1 -0
  322. package/lib/typescript/module/state/use-widget-state.d.ts +10 -0
  323. package/lib/typescript/module/state/use-widget-state.d.ts.map +1 -0
  324. package/lib/typescript/module/state/widget-store.d.ts +227 -0
  325. package/lib/typescript/module/state/widget-store.d.ts.map +1 -0
  326. package/lib/typescript/module/storage.d.ts +42 -0
  327. package/lib/typescript/module/storage.d.ts.map +1 -0
  328. package/lib/typescript/module/types.d.ts +391 -0
  329. package/lib/typescript/module/types.d.ts.map +1 -0
  330. package/lib/typescript/module/ui/WidgetProvider.d.ts +15 -0
  331. package/lib/typescript/module/ui/WidgetProvider.d.ts.map +1 -0
  332. package/lib/typescript/module/ui/components/AgentUiBlocks.d.ts +27 -0
  333. package/lib/typescript/module/ui/components/AgentUiBlocks.d.ts.map +1 -0
  334. package/lib/typescript/module/ui/components/Avatar.d.ts +19 -0
  335. package/lib/typescript/module/ui/components/Avatar.d.ts.map +1 -0
  336. package/lib/typescript/module/ui/components/ConversationRow.d.ts +26 -0
  337. package/lib/typescript/module/ui/components/ConversationRow.d.ts.map +1 -0
  338. package/lib/typescript/module/ui/components/ErrorBoundary.d.ts +27 -0
  339. package/lib/typescript/module/ui/components/ErrorBoundary.d.ts.map +1 -0
  340. package/lib/typescript/module/ui/components/Gradient.d.ts +33 -0
  341. package/lib/typescript/module/ui/components/Gradient.d.ts.map +1 -0
  342. package/lib/typescript/module/ui/components/Launcher.d.ts +18 -0
  343. package/lib/typescript/module/ui/components/Launcher.d.ts.map +1 -0
  344. package/lib/typescript/module/ui/components/Markdown.d.ts +19 -0
  345. package/lib/typescript/module/ui/components/Markdown.d.ts.map +1 -0
  346. package/lib/typescript/module/ui/components/OutboundMessageCard.d.ts +20 -0
  347. package/lib/typescript/module/ui/components/OutboundMessageCard.d.ts.map +1 -0
  348. package/lib/typescript/module/ui/components/WidgetPanel.d.ts +26 -0
  349. package/lib/typescript/module/ui/components/WidgetPanel.d.ts.map +1 -0
  350. package/lib/typescript/module/ui/components/icons.d.ts +38 -0
  351. package/lib/typescript/module/ui/components/icons.d.ts.map +1 -0
  352. package/lib/typescript/module/ui/hooks/use-keyboard-overlap.d.ts +33 -0
  353. package/lib/typescript/module/ui/hooks/use-keyboard-overlap.d.ts.map +1 -0
  354. package/lib/typescript/module/ui/hooks/use-reduce-motion.d.ts +11 -0
  355. package/lib/typescript/module/ui/hooks/use-reduce-motion.d.ts.map +1 -0
  356. package/lib/typescript/module/ui/hooks/use-widget-insets.d.ts +16 -0
  357. package/lib/typescript/module/ui/hooks/use-widget-insets.d.ts.map +1 -0
  358. package/lib/typescript/module/ui/screens/chat/ChatView.d.ts +11 -0
  359. package/lib/typescript/module/ui/screens/chat/ChatView.d.ts.map +1 -0
  360. package/lib/typescript/module/ui/screens/chat/ChatView.styles.d.ts +349 -0
  361. package/lib/typescript/module/ui/screens/chat/ChatView.styles.d.ts.map +1 -0
  362. package/lib/typescript/module/ui/screens/chat/ChatView.types.d.ts +48 -0
  363. package/lib/typescript/module/ui/screens/chat/ChatView.types.d.ts.map +1 -0
  364. package/lib/typescript/module/ui/screens/conversations/ConversationsList.d.ts +10 -0
  365. package/lib/typescript/module/ui/screens/conversations/ConversationsList.d.ts.map +1 -0
  366. package/lib/typescript/module/ui/screens/conversations/ConversationsList.styles.d.ts +113 -0
  367. package/lib/typescript/module/ui/screens/conversations/ConversationsList.styles.d.ts.map +1 -0
  368. package/lib/typescript/module/ui/screens/conversations/ConversationsList.types.d.ts +31 -0
  369. package/lib/typescript/module/ui/screens/conversations/ConversationsList.types.d.ts.map +1 -0
  370. package/lib/typescript/module/ui/screens/home/HomeView.d.ts +17 -0
  371. package/lib/typescript/module/ui/screens/home/HomeView.d.ts.map +1 -0
  372. package/lib/typescript/module/ui/screens/home/HomeView.styles.d.ts +227 -0
  373. package/lib/typescript/module/ui/screens/home/HomeView.styles.d.ts.map +1 -0
  374. package/lib/typescript/module/ui/screens/home/HomeView.types.d.ts +40 -0
  375. package/lib/typescript/module/ui/screens/home/HomeView.types.d.ts.map +1 -0
  376. package/lib/typescript/module/ui/screens/home/greeting.d.ts +19 -0
  377. package/lib/typescript/module/ui/screens/home/greeting.d.ts.map +1 -0
  378. package/lib/typescript/module/ui/screens/pre-chat/PreChatForm.d.ts +7 -0
  379. package/lib/typescript/module/ui/screens/pre-chat/PreChatForm.d.ts.map +1 -0
  380. package/lib/typescript/module/ui/screens/pre-chat/PreChatForm.styles.d.ts +115 -0
  381. package/lib/typescript/module/ui/screens/pre-chat/PreChatForm.styles.d.ts.map +1 -0
  382. package/lib/typescript/module/ui/screens/pre-chat/PreChatForm.types.d.ts +20 -0
  383. package/lib/typescript/module/ui/screens/pre-chat/PreChatForm.types.d.ts.map +1 -0
  384. package/lib/typescript/module/ui/styles/global.d.ts +46 -0
  385. package/lib/typescript/module/ui/styles/global.d.ts.map +1 -0
  386. package/lib/typescript/module/ui/styles/theme.d.ts +103 -0
  387. package/lib/typescript/module/ui/styles/theme.d.ts.map +1 -0
  388. package/lib/typescript/module/ui/utils/conversations.d.ts +41 -0
  389. package/lib/typescript/module/ui/utils/conversations.d.ts.map +1 -0
  390. package/lib/typescript/module/ui/utils/html.d.ts +10 -0
  391. package/lib/typescript/module/ui/utils/html.d.ts.map +1 -0
  392. package/lib/typescript/module/ui/utils/native-gradient.d.ts +61 -0
  393. package/lib/typescript/module/ui/utils/native-gradient.d.ts.map +1 -0
  394. package/lib/typescript/module/ui/utils/rtl.d.ts +33 -0
  395. package/lib/typescript/module/ui/utils/rtl.d.ts.map +1 -0
  396. package/lib/typescript/module/ui/utils/safe-url.d.ts +21 -0
  397. package/lib/typescript/module/ui/utils/safe-url.d.ts.map +1 -0
  398. package/metro.js +40 -0
  399. package/package.json +116 -0
  400. package/src/agent-ui-blocks.ts +302 -0
  401. package/src/api/client.ts +332 -0
  402. package/src/api/realtime.ts +225 -0
  403. package/src/api/sse-parser.ts +51 -0
  404. package/src/controller.ts +20 -0
  405. package/src/device-id.ts +71 -0
  406. package/src/events.ts +74 -0
  407. package/src/i18n.ts +114 -0
  408. package/src/index.tsx +199 -0
  409. package/src/locales.ts +80 -0
  410. package/src/push/push-notifications.ts +254 -0
  411. package/src/state/use-widget-state.ts +20 -0
  412. package/src/state/widget-store.ts +1815 -0
  413. package/src/storage.ts +153 -0
  414. package/src/types.ts +445 -0
  415. package/src/ui/WidgetProvider.tsx +434 -0
  416. package/src/ui/components/AgentUiBlocks.tsx +912 -0
  417. package/src/ui/components/Avatar.tsx +86 -0
  418. package/src/ui/components/ConversationRow.tsx +163 -0
  419. package/src/ui/components/ErrorBoundary.tsx +49 -0
  420. package/src/ui/components/Gradient.tsx +165 -0
  421. package/src/ui/components/Launcher.tsx +128 -0
  422. package/src/ui/components/Markdown.tsx +291 -0
  423. package/src/ui/components/OutboundMessageCard.tsx +154 -0
  424. package/src/ui/components/WidgetPanel.tsx +133 -0
  425. package/src/ui/components/icons.tsx +315 -0
  426. package/src/ui/hooks/use-keyboard-overlap.ts +134 -0
  427. package/src/ui/hooks/use-reduce-motion.ts +39 -0
  428. package/src/ui/hooks/use-widget-insets.ts +44 -0
  429. package/src/ui/screens/chat/ChatView.styles.ts +357 -0
  430. package/src/ui/screens/chat/ChatView.tsx +690 -0
  431. package/src/ui/screens/chat/ChatView.types.ts +46 -0
  432. package/src/ui/screens/conversations/ConversationsList.styles.ts +111 -0
  433. package/src/ui/screens/conversations/ConversationsList.tsx +144 -0
  434. package/src/ui/screens/conversations/ConversationsList.types.ts +33 -0
  435. package/src/ui/screens/home/HomeView.styles.ts +246 -0
  436. package/src/ui/screens/home/HomeView.tsx +336 -0
  437. package/src/ui/screens/home/HomeView.types.ts +42 -0
  438. package/src/ui/screens/home/greeting.ts +50 -0
  439. package/src/ui/screens/pre-chat/PreChatForm.styles.ts +120 -0
  440. package/src/ui/screens/pre-chat/PreChatForm.tsx +221 -0
  441. package/src/ui/screens/pre-chat/PreChatForm.types.ts +22 -0
  442. package/src/ui/styles/global.ts +50 -0
  443. package/src/ui/styles/theme.ts +155 -0
  444. package/src/ui/utils/conversations.ts +122 -0
  445. package/src/ui/utils/html.ts +58 -0
  446. package/src/ui/utils/native-gradient.ts +60 -0
  447. package/src/ui/utils/rtl.ts +43 -0
  448. package/src/ui/utils/safe-url.ts +63 -0
@@ -0,0 +1,912 @@
1
+ /**
2
+ * Renders agent-authored rich blocks (product cards, slot pickers, lead forms,
3
+ * quotes, receipts) and posts the visitor's choice back through
4
+ * `POST /api/widget/messages` as an `agentUiAction`.
5
+ * Port of `client/src/widget/components/AgentUiBlocks.tsx`.
6
+ */
7
+
8
+ import { createContext, useContext, useState } from 'react';
9
+ import { Image, Pressable, StyleSheet, Text, TextInput, View, type ViewStyle } from 'react-native';
10
+
11
+ import type { AgentUiAction, AgentUiBlock } from '../../types';
12
+ import { fontSize, palette, radius, shadows, spacing, withAlpha } from '../styles/theme';
13
+
14
+ export interface AgentUiBlockLabels {
15
+ service: string;
16
+ product: string;
17
+ chooseTime: string;
18
+ professional: string;
19
+ priceOffer: string;
20
+ working: string;
21
+ }
22
+
23
+ const DEFAULT_BLOCK_LABELS: AgentUiBlockLabels = {
24
+ service: 'Service',
25
+ product: 'Product',
26
+ chooseTime: 'Choose a time',
27
+ professional: 'Professional',
28
+ priceOffer: 'Price offer',
29
+ working: 'Working...',
30
+ };
31
+
32
+ const BlockLabelContext = createContext<AgentUiBlockLabels>(DEFAULT_BLOCK_LABELS);
33
+
34
+ interface AgentUiBlocksProps {
35
+ blocks: AgentUiBlock[];
36
+ color: string;
37
+ labels?: AgentUiBlockLabels;
38
+ onAction: (action: AgentUiAction) => void | Promise<void>;
39
+ }
40
+
41
+ export function AgentUiBlocks({
42
+ blocks,
43
+ color,
44
+ labels = DEFAULT_BLOCK_LABELS,
45
+ onAction,
46
+ }: AgentUiBlocksProps) {
47
+ const [selectedVariants, setSelectedVariants] = useState<Record<string, string>>({});
48
+ const [consents, setConsents] = useState<Record<string, boolean>>({});
49
+ const [values, setValues] = useState<Record<string, string>>({});
50
+ const [selectedSlots, setSelectedSlots] = useState<Record<string, string>>({});
51
+ const [pending, setPending] = useState<string | null>(null);
52
+
53
+ const requiredConsent = blocks.filter((block) => block.type === 'consent' && block.required);
54
+ const consentGranted = requiredConsent.every((block) => consents[block.id]);
55
+
56
+ const act = async (action: AgentUiAction) => {
57
+ if (pending) return;
58
+ const productId =
59
+ typeof action.payload.productId === 'string' ? action.payload.productId : undefined;
60
+ const nextAction =
61
+ productId && selectedVariants[productId]
62
+ ? { ...action, payload: { ...action.payload, variantId: selectedVariants[productId] } }
63
+ : action;
64
+ setPending(action.id);
65
+ try {
66
+ await onAction(nextAction);
67
+ } finally {
68
+ setPending(null);
69
+ }
70
+ };
71
+
72
+ return (
73
+ <BlockLabelContext.Provider value={labels}>
74
+ <View style={styles.stack}>
75
+ {blocks.map((block) => {
76
+ if (block.type === 'product_service_card') {
77
+ return (
78
+ <View key={block.id} style={styles.card}>
79
+ {block.product.imageUrl ? (
80
+ <Image
81
+ source={{ uri: block.product.imageUrl }}
82
+ style={styles.cardImage}
83
+ resizeMode="cover"
84
+ accessibilityIgnoresInvertColors
85
+ />
86
+ ) : null}
87
+ <View style={styles.cardBody}>
88
+ <View style={styles.rowStart}>
89
+ <View style={styles.flexMin}>
90
+ <Text style={styles.title}>{block.product.name}</Text>
91
+ <Text style={styles.description}>
92
+ {block.product.shortDescription || block.product.description || ''}
93
+ </Text>
94
+ </View>
95
+ <Badge
96
+ label={block.product.type === 'service' ? labels.service : labels.product}
97
+ />
98
+ </View>
99
+ <View style={styles.row}>
100
+ <Text style={styles.muted}>
101
+ {block.product.durationMinutes
102
+ ? `${block.product.durationMinutes} min`
103
+ : block.product.unit || ''}
104
+ </Text>
105
+ <Text style={styles.price}>
106
+ {formatMoney(block.product.price, block.product.currency)}
107
+ </Text>
108
+ </View>
109
+ </View>
110
+ </View>
111
+ );
112
+ }
113
+
114
+ if (block.type === 'product_choices') {
115
+ const selected =
116
+ selectedVariants[block.productId] ||
117
+ block.variants.find((variant) => variant.available)?.id ||
118
+ '';
119
+ return (
120
+ <View key={block.id} style={[styles.card, styles.cardPadded]}>
121
+ <Text style={styles.title}>{block.title}</Text>
122
+ <View style={styles.chipRow}>
123
+ {block.variants.map((variant) => (
124
+ <Chip
125
+ key={variant.id}
126
+ selected={selected === variant.id}
127
+ disabled={!variant.available}
128
+ color={color}
129
+ onPress={() =>
130
+ setSelectedVariants((current) => ({
131
+ ...current,
132
+ [block.productId]: variant.id,
133
+ }))
134
+ }
135
+ label={variant.title}
136
+ sublabel={formatMoney(variant.price, variant.currency)}
137
+ />
138
+ ))}
139
+ </View>
140
+ </View>
141
+ );
142
+ }
143
+
144
+ if (block.type === 'commerce_actions') {
145
+ return (
146
+ <View key={block.id} style={styles.actionRow}>
147
+ {block.actions.map((action, index) => (
148
+ <View key={action.id} style={styles.actionCell}>
149
+ <ActionButton
150
+ color={color}
151
+ secondary={index === 0 && block.actions.length > 1}
152
+ pending={pending === action.id}
153
+ onPress={() => act(action)}>
154
+ {action.label}
155
+ </ActionButton>
156
+ </View>
157
+ ))}
158
+ </View>
159
+ );
160
+ }
161
+
162
+ if (block.type === 'cart_order_summary') {
163
+ return (
164
+ <View key={block.id} style={[styles.card, styles.cardPadded]}>
165
+ <View style={styles.rowStart}>
166
+ <Text style={styles.title}>{block.title}</Text>
167
+ {block.status ? <Badge label={block.status} /> : null}
168
+ </View>
169
+ <View style={styles.itemList}>
170
+ {block.items.map((item, index) => (
171
+ <View key={`${item.name}:${index}`} style={styles.row}>
172
+ <View style={styles.flexMin}>
173
+ <Text style={styles.itemName}>{item.name}</Text>
174
+ <Text style={styles.muted}>
175
+ {item.quantity} × {formatMoney(item.unitPrice, block.currency)}
176
+ </Text>
177
+ </View>
178
+ <Text style={styles.itemTotal}>
179
+ {formatMoney(item.lineTotal, block.currency)}
180
+ </Text>
181
+ </View>
182
+ ))}
183
+ </View>
184
+ <View style={styles.totals}>
185
+ <MoneyRow label="Subtotal" value={formatMoney(block.subtotal, block.currency)} />
186
+ {block.discount ? (
187
+ <MoneyRow
188
+ label="Discount"
189
+ value={`−${formatMoney(block.discount, block.currency)}`}
190
+ />
191
+ ) : null}
192
+ {block.tax ? (
193
+ <MoneyRow label="Tax" value={formatMoney(block.tax, block.currency)} />
194
+ ) : null}
195
+ {block.shipping ? (
196
+ <MoneyRow
197
+ label="Shipping"
198
+ value={formatMoney(block.shipping, block.currency)}
199
+ />
200
+ ) : null}
201
+ <MoneyRow label="Total" value={formatMoney(block.total, block.currency)} strong />
202
+ </View>
203
+ {block.action ? (
204
+ <View style={styles.blockAction}>
205
+ <ActionButton
206
+ color={color}
207
+ pending={pending === block.action.id}
208
+ onPress={() => block.action && act(block.action)}>
209
+ {block.action.label}
210
+ </ActionButton>
211
+ </View>
212
+ ) : null}
213
+ </View>
214
+ );
215
+ }
216
+
217
+ if (block.type === 'contact_lead_form') {
218
+ const complete = block.fields.every(
219
+ (field) =>
220
+ !field.required || (values[`${block.id}:${field.name}`] ?? field.value ?? '').trim()
221
+ );
222
+ return (
223
+ <View key={block.id} style={[styles.card, styles.cardPadded]}>
224
+ <Text style={styles.title}>{block.title}</Text>
225
+ {block.description ? (
226
+ <Text style={styles.description}>{block.description}</Text>
227
+ ) : null}
228
+ <View style={styles.fieldStack}>
229
+ {block.fields.map((field) => {
230
+ const id = `${block.id}:${field.name}`;
231
+ return (
232
+ <TextInput
233
+ key={id}
234
+ value={values[id] ?? field.value ?? ''}
235
+ onChangeText={(text) =>
236
+ setValues((current) => ({ ...current, [id]: text }))
237
+ }
238
+ placeholder={field.placeholder || field.label}
239
+ placeholderTextColor={palette.textSubtle}
240
+ accessibilityLabel={field.label}
241
+ keyboardType={keyboardTypeFor(field.type)}
242
+ autoCapitalize={field.type === 'email' ? 'none' : 'sentences'}
243
+ multiline={field.type === 'textarea'}
244
+ style={[styles.input, field.type === 'textarea' && styles.textarea]}
245
+ />
246
+ );
247
+ })}
248
+ </View>
249
+ <View style={styles.blockAction}>
250
+ <ActionButton
251
+ color={color}
252
+ disabled={!complete || !consentGranted}
253
+ pending={pending === `submit:${block.id}`}
254
+ onPress={() =>
255
+ act({
256
+ id: `submit:${block.id}`,
257
+ type: 'submit_contact',
258
+ label: block.submitLabel,
259
+ module: block.module,
260
+ payload: Object.fromEntries(
261
+ block.fields.map((field) => [
262
+ field.name,
263
+ values[`${block.id}:${field.name}`] || field.value || '',
264
+ ])
265
+ ),
266
+ })
267
+ }>
268
+ {block.submitLabel}
269
+ </ActionButton>
270
+ </View>
271
+ </View>
272
+ );
273
+ }
274
+
275
+ if (block.type === 'consent') {
276
+ const checked = consents[block.id] || false;
277
+ return (
278
+ <View key={block.id} style={[styles.card, styles.cardPadded]}>
279
+ <Pressable
280
+ onPress={() => setConsents((current) => ({ ...current, [block.id]: !checked }))}
281
+ accessibilityRole="checkbox"
282
+ accessibilityState={{ checked }}
283
+ style={styles.consentRow}>
284
+ <View
285
+ style={[
286
+ styles.checkbox,
287
+ checked && { backgroundColor: color, borderColor: color },
288
+ ]}>
289
+ {checked ? <Text style={styles.checkmark}>✓</Text> : null}
290
+ </View>
291
+ <Text style={styles.consentText}>{block.statement}</Text>
292
+ </Pressable>
293
+ <View style={[styles.row, styles.blockAction]}>
294
+ <View style={styles.chipRow}>
295
+ {block.channels.map((channel) => (
296
+ <Badge key={channel} label={channel} />
297
+ ))}
298
+ </View>
299
+ <Pressable
300
+ disabled={!checked || pending !== null}
301
+ onPress={() =>
302
+ act({
303
+ id: `consent:${block.id}`,
304
+ type: 'record_consent',
305
+ label: 'Save consent',
306
+ module: 'contacts',
307
+ payload: {
308
+ channels: block.channels,
309
+ consent: true,
310
+ statement: block.statement,
311
+ },
312
+ })
313
+ }
314
+ hitSlop={8}>
315
+ <Text
316
+ style={[styles.linkButton, { color: checked ? color : palette.textSubtle }]}>
317
+ Save
318
+ </Text>
319
+ </Pressable>
320
+ </View>
321
+ </View>
322
+ );
323
+ }
324
+
325
+ if (block.type === 'appointment_slot_picker') {
326
+ const selected = selectedSlots[block.id] || '';
327
+ const selectedSlot = block.days
328
+ .flatMap((day) => day.slots.map((slot) => ({ ...slot, date: day.date })))
329
+ .find((slot) => `${slot.date}:${slot.startTime}` === selected);
330
+ const providerById = new Map(
331
+ block.providers.map((provider) => [provider.id, provider])
332
+ );
333
+ const selectedProvider = selectedSlot?.staffUserId
334
+ ? providerById.get(selectedSlot.staffUserId)
335
+ : undefined;
336
+ const activeProviderId = block.selectedProviderId || 'any';
337
+
338
+ return (
339
+ <View key={block.id} style={[styles.card, styles.cardPadded]}>
340
+ <Text style={styles.title}>{labels.chooseTime}</Text>
341
+ <Text style={styles.description}>
342
+ {block.serviceName} · {block.durationMinutes} min
343
+ {block.timezone ? ` · ${block.timezone}` : ''}
344
+ </Text>
345
+
346
+ {block.providers.length > 0 ? (
347
+ <View style={styles.section}>
348
+ <Text style={styles.sectionLabel}>{labels.professional}</Text>
349
+ <View style={styles.chipRow}>
350
+ <Chip
351
+ selected={activeProviderId === 'any'}
352
+ color={color}
353
+ disabled={pending !== null}
354
+ label="Any available"
355
+ onPress={() =>
356
+ activeProviderId !== 'any' &&
357
+ act({
358
+ id: `provider:${block.serviceId}:any`,
359
+ type: 'select_appointment_provider',
360
+ label: 'Choose any professional',
361
+ module: 'appointments',
362
+ payload: {
363
+ serviceId: block.serviceId,
364
+ serviceName: block.serviceName,
365
+ durationMinutes: block.durationMinutes,
366
+ locationId: block.locationId,
367
+ providerName: 'any available professional',
368
+ },
369
+ })
370
+ }
371
+ />
372
+ {block.providers.map((provider) => (
373
+ <Chip
374
+ key={provider.id}
375
+ selected={activeProviderId === provider.id}
376
+ color={color}
377
+ disabled={pending !== null}
378
+ label={provider.name}
379
+ onPress={() =>
380
+ activeProviderId !== provider.id &&
381
+ act({
382
+ id: `provider:${block.serviceId}:${provider.id}`,
383
+ type: 'select_appointment_provider',
384
+ label: `Choose ${provider.name}`,
385
+ module: 'appointments',
386
+ payload: {
387
+ serviceId: block.serviceId,
388
+ serviceName: block.serviceName,
389
+ durationMinutes: block.durationMinutes,
390
+ locationId: block.locationId,
391
+ providerId: provider.id,
392
+ providerName: provider.name,
393
+ },
394
+ })
395
+ }
396
+ />
397
+ ))}
398
+ </View>
399
+ </View>
400
+ ) : null}
401
+
402
+ <View style={styles.section}>
403
+ {block.days.map((day) => (
404
+ <View key={day.date} style={styles.day}>
405
+ <Text style={styles.sectionLabel}>{formatDate(day.date)}</Text>
406
+ <View style={styles.chipRow}>
407
+ {day.slots.map((slot) => {
408
+ const value = `${day.date}:${slot.startTime}`;
409
+ return (
410
+ <Chip
411
+ key={value}
412
+ selected={selected === value}
413
+ color={color}
414
+ label={formatTime(slot.startTime)}
415
+ onPress={() =>
416
+ setSelectedSlots((current) => ({ ...current, [block.id]: value }))
417
+ }
418
+ />
419
+ );
420
+ })}
421
+ </View>
422
+ </View>
423
+ ))}
424
+ </View>
425
+
426
+ {selectedProvider ? (
427
+ <Text style={[styles.muted, styles.blockAction]}>
428
+ With {selectedProvider.name}
429
+ </Text>
430
+ ) : null}
431
+ <View style={styles.blockAction}>
432
+ <ActionButton
433
+ color={color}
434
+ disabled={!selectedSlot}
435
+ pending={pending?.startsWith(`book:${block.serviceId}`) || false}
436
+ onPress={() =>
437
+ selectedSlot &&
438
+ act({
439
+ id: `book:${block.serviceId}:${selected}`,
440
+ type: 'select_appointment_slot',
441
+ label: 'Book this time',
442
+ module: 'appointments',
443
+ payload: {
444
+ serviceId: block.serviceId,
445
+ serviceName: block.serviceName,
446
+ durationMinutes: block.durationMinutes,
447
+ date: selectedSlot.date,
448
+ startTime: selectedSlot.startTime,
449
+ endTime: selectedSlot.endTime,
450
+ userId: selectedSlot.staffUserId,
451
+ slotToken: selectedSlot.slotToken,
452
+ providerName: selectedProvider?.name,
453
+ },
454
+ })
455
+ }>
456
+ Book this time
457
+ </ActionButton>
458
+ </View>
459
+ </View>
460
+ );
461
+ }
462
+
463
+ if (block.type === 'quote_preview') {
464
+ const publicActions = block.actions.filter((action) => action.type !== 'open_record');
465
+ return (
466
+ <View key={block.id} style={[styles.card, styles.cardPadded]}>
467
+ <View style={styles.rowStart}>
468
+ <View style={styles.flexMin}>
469
+ <Text style={styles.title}>{block.name}</Text>
470
+ <Text style={styles.description}>
471
+ {block.serialNumber || labels.priceOffer}
472
+ {block.contactName ? ` · ${block.contactName}` : ''}
473
+ </Text>
474
+ </View>
475
+ <Badge label={block.status} />
476
+ </View>
477
+ <Text style={styles.amount}>{formatMoney(block.amount, block.currency)}</Text>
478
+ {block.validUntil ? (
479
+ <Text style={styles.muted}>
480
+ Valid until {formatDate(block.validUntil.slice(0, 10))}
481
+ </Text>
482
+ ) : null}
483
+ {publicActions.map((action) => (
484
+ <View key={action.id} style={styles.blockAction}>
485
+ <ActionButton
486
+ color={color}
487
+ pending={pending === action.id}
488
+ onPress={() => act(action)}>
489
+ {action.label}
490
+ </ActionButton>
491
+ </View>
492
+ ))}
493
+ </View>
494
+ );
495
+ }
496
+
497
+ // success_receipt
498
+ return (
499
+ <View key={block.id} style={[styles.card, styles.cardPadded]}>
500
+ <View style={styles.rowStart}>
501
+ <Text style={[styles.checkGlyph, { color }]}>✓</Text>
502
+ <View style={styles.flexMin}>
503
+ <Text style={styles.title}>{block.title}</Text>
504
+ {block.description ? (
505
+ <Text style={styles.description}>{block.description}</Text>
506
+ ) : null}
507
+ </View>
508
+ </View>
509
+ <View style={[styles.row, styles.blockAction]}>
510
+ {block.reference ? <Badge label={block.reference} /> : <View />}
511
+ {block.amount != null && block.currency ? (
512
+ <Text style={styles.itemTotal}>{formatMoney(block.amount, block.currency)}</Text>
513
+ ) : null}
514
+ </View>
515
+ {block.nextActions
516
+ .filter((action) => action.type !== 'open_record')
517
+ .map((action) => (
518
+ <View key={action.id} style={styles.blockAction}>
519
+ <ActionButton
520
+ color={color}
521
+ pending={pending === action.id}
522
+ onPress={() => act(action)}>
523
+ {action.label}
524
+ </ActionButton>
525
+ </View>
526
+ ))}
527
+ </View>
528
+ );
529
+ })}
530
+ </View>
531
+ </BlockLabelContext.Provider>
532
+ );
533
+ }
534
+
535
+ function ActionButton({
536
+ children,
537
+ color,
538
+ secondary = false,
539
+ pending = false,
540
+ disabled = false,
541
+ onPress,
542
+ }: {
543
+ children: string;
544
+ color: string;
545
+ secondary?: boolean;
546
+ pending?: boolean;
547
+ disabled?: boolean;
548
+ onPress?: () => void;
549
+ }) {
550
+ const labels = useContext(BlockLabelContext);
551
+ const inactive = disabled || pending;
552
+ return (
553
+ <Pressable
554
+ disabled={inactive}
555
+ onPress={onPress}
556
+ accessibilityRole="button"
557
+ style={({ pressed }) => [
558
+ styles.actionButton,
559
+ secondary
560
+ ? { borderColor: color, backgroundColor: palette.white }
561
+ : { backgroundColor: color },
562
+ inactive && styles.actionButtonDisabled,
563
+ pressed && styles.pressed,
564
+ ]}>
565
+ <Text
566
+ style={[
567
+ styles.actionButtonLabel,
568
+ { color: inactive ? palette.white : secondary ? color : palette.white },
569
+ ]}>
570
+ {pending ? labels.working : children}
571
+ </Text>
572
+ </Pressable>
573
+ );
574
+ }
575
+
576
+ function Chip({
577
+ label,
578
+ sublabel,
579
+ selected,
580
+ disabled = false,
581
+ color,
582
+ onPress,
583
+ }: {
584
+ label: string;
585
+ sublabel?: string;
586
+ selected: boolean;
587
+ disabled?: boolean;
588
+ color: string;
589
+ onPress: () => void;
590
+ }) {
591
+ return (
592
+ <Pressable
593
+ disabled={disabled}
594
+ onPress={onPress}
595
+ accessibilityRole="button"
596
+ accessibilityState={{ selected, disabled }}
597
+ style={[
598
+ styles.chip,
599
+ {
600
+ borderColor: selected ? color : palette.disabled,
601
+ backgroundColor: selected ? withAlpha(color, 0.07) : palette.white,
602
+ },
603
+ disabled && styles.chipDisabled,
604
+ ]}>
605
+ <Text style={[styles.chipLabel, { color: selected ? color : '#374151' }]}>{label}</Text>
606
+ {sublabel ? <Text style={styles.chipSublabel}>{sublabel}</Text> : null}
607
+ </Pressable>
608
+ );
609
+ }
610
+
611
+ function Badge({ label }: { label: string }) {
612
+ return (
613
+ <View style={styles.badge}>
614
+ <Text style={styles.badgeLabel} numberOfLines={1}>
615
+ {label}
616
+ </Text>
617
+ </View>
618
+ );
619
+ }
620
+
621
+ function MoneyRow({
622
+ label,
623
+ value,
624
+ strong = false,
625
+ }: {
626
+ label: string;
627
+ value: string;
628
+ strong?: boolean;
629
+ }) {
630
+ return (
631
+ <View style={styles.row}>
632
+ <Text style={[styles.moneyLabel, strong && styles.moneyStrong]}>{label}</Text>
633
+ <Text style={[styles.moneyLabel, strong && styles.moneyStrong]}>{value}</Text>
634
+ </View>
635
+ );
636
+ }
637
+
638
+ function keyboardTypeFor(type: string) {
639
+ if (type === 'email') return 'email-address' as const;
640
+ if (type === 'phone') return 'phone-pad' as const;
641
+ return 'default' as const;
642
+ }
643
+
644
+ export function formatMoney(amount: number, currency: string) {
645
+ // Blocks are validated upstream; this is the last line against a bad value.
646
+ const value = typeof amount === 'number' && Number.isFinite(amount) ? amount : 0;
647
+ try {
648
+ return new Intl.NumberFormat(undefined, { style: 'currency', currency }).format(value);
649
+ } catch {
650
+ return `${value.toFixed(2)} ${currency ?? ''}`.trim();
651
+ }
652
+ }
653
+
654
+ export function formatDate(date: string) {
655
+ const parsed = new Date(`${date}T12:00:00`);
656
+ if (Number.isNaN(parsed.getTime())) return date;
657
+ try {
658
+ return new Intl.DateTimeFormat(undefined, {
659
+ weekday: 'short',
660
+ month: 'short',
661
+ day: 'numeric',
662
+ }).format(parsed);
663
+ } catch {
664
+ return date;
665
+ }
666
+ }
667
+
668
+ export function formatTime(time: string) {
669
+ const [hour, minute] = time.split(':').map(Number);
670
+ try {
671
+ return new Intl.DateTimeFormat(undefined, { hour: 'numeric', minute: '2-digit' }).format(
672
+ new Date(2000, 0, 1, hour ?? 0, minute ?? 0)
673
+ );
674
+ } catch {
675
+ return time;
676
+ }
677
+ }
678
+
679
+ const cardStyle: ViewStyle = {
680
+ overflow: 'hidden',
681
+ borderWidth: StyleSheet.hairlineWidth,
682
+ borderColor: palette.border,
683
+ borderRadius: radius.xl,
684
+ backgroundColor: palette.white,
685
+ ...shadows.card,
686
+ };
687
+
688
+ const styles = StyleSheet.create({
689
+ stack: {
690
+ width: '100%',
691
+ maxWidth: 350,
692
+ gap: spacing.sm,
693
+ marginTop: spacing.sm,
694
+ },
695
+ card: cardStyle,
696
+ cardPadded: {
697
+ padding: spacing.md,
698
+ },
699
+ cardBody: {
700
+ gap: spacing.sm + 2,
701
+ padding: spacing.md,
702
+ },
703
+ cardImage: {
704
+ width: '100%',
705
+ aspectRatio: 16 / 7,
706
+ },
707
+ row: {
708
+ flexDirection: 'row',
709
+ alignItems: 'center',
710
+ justifyContent: 'space-between',
711
+ gap: spacing.sm + 2,
712
+ },
713
+ rowStart: {
714
+ flexDirection: 'row',
715
+ alignItems: 'flex-start',
716
+ justifyContent: 'space-between',
717
+ gap: spacing.sm + 2,
718
+ },
719
+ flexMin: {
720
+ flex: 1,
721
+ minWidth: 0,
722
+ },
723
+ title: {
724
+ fontSize: fontSize.body,
725
+ lineHeight: fontSize.body * 1.3,
726
+ fontWeight: '600',
727
+ color: palette.text,
728
+ },
729
+ description: {
730
+ fontSize: fontSize.meta,
731
+ lineHeight: fontSize.meta * 1.45,
732
+ color: palette.textMuted,
733
+ marginTop: 3,
734
+ },
735
+ muted: {
736
+ fontSize: fontSize.caption,
737
+ color: palette.textMuted,
738
+ },
739
+ price: {
740
+ fontSize: fontSize.title,
741
+ fontWeight: '600',
742
+ color: palette.text,
743
+ },
744
+ amount: {
745
+ fontSize: fontSize.heading,
746
+ fontWeight: '700',
747
+ color: palette.text,
748
+ marginTop: spacing.sm + 2,
749
+ },
750
+ badge: {
751
+ borderWidth: StyleSheet.hairlineWidth,
752
+ borderColor: palette.border,
753
+ borderRadius: radius.pill,
754
+ paddingHorizontal: 7,
755
+ paddingVertical: 3,
756
+ backgroundColor: palette.surface,
757
+ },
758
+ badgeLabel: {
759
+ fontSize: fontSize.caption - 1,
760
+ color: '#4b5563',
761
+ },
762
+ chipRow: {
763
+ flexDirection: 'row',
764
+ flexWrap: 'wrap',
765
+ gap: spacing.xs + 2,
766
+ marginTop: spacing.sm,
767
+ },
768
+ chip: {
769
+ borderWidth: 1,
770
+ borderRadius: radius.md,
771
+ paddingHorizontal: spacing.md,
772
+ paddingVertical: spacing.sm,
773
+ minHeight: 40,
774
+ justifyContent: 'center',
775
+ },
776
+ chipDisabled: {
777
+ opacity: 0.45,
778
+ },
779
+ chipLabel: {
780
+ fontSize: fontSize.meta,
781
+ fontWeight: '600',
782
+ },
783
+ chipSublabel: {
784
+ fontSize: fontSize.caption - 1,
785
+ opacity: 0.75,
786
+ marginTop: 2,
787
+ },
788
+ actionRow: {
789
+ flexDirection: 'row',
790
+ gap: spacing.sm,
791
+ },
792
+ actionCell: {
793
+ flex: 1,
794
+ },
795
+ actionButton: {
796
+ width: '100%',
797
+ borderWidth: 1,
798
+ borderColor: 'transparent',
799
+ borderRadius: radius.md,
800
+ paddingVertical: spacing.md,
801
+ paddingHorizontal: spacing.md,
802
+ alignItems: 'center',
803
+ justifyContent: 'center',
804
+ minHeight: 44,
805
+ },
806
+ actionButtonDisabled: {
807
+ backgroundColor: palette.disabled,
808
+ borderColor: palette.disabled,
809
+ },
810
+ actionButtonLabel: {
811
+ fontSize: fontSize.meta,
812
+ fontWeight: '600',
813
+ },
814
+ pressed: {
815
+ opacity: 0.85,
816
+ },
817
+ blockAction: {
818
+ marginTop: spacing.sm + 2,
819
+ },
820
+ itemList: {
821
+ gap: spacing.sm,
822
+ marginTop: spacing.sm + 2,
823
+ },
824
+ itemName: {
825
+ fontSize: fontSize.meta,
826
+ fontWeight: '600',
827
+ color: palette.text,
828
+ },
829
+ itemTotal: {
830
+ fontSize: fontSize.meta,
831
+ fontWeight: '600',
832
+ color: palette.text,
833
+ },
834
+ totals: {
835
+ borderTopWidth: StyleSheet.hairlineWidth,
836
+ borderTopColor: palette.border,
837
+ marginTop: spacing.sm + 2,
838
+ paddingTop: spacing.sm + 2,
839
+ gap: 5,
840
+ },
841
+ moneyLabel: {
842
+ fontSize: fontSize.meta,
843
+ color: palette.text,
844
+ },
845
+ moneyStrong: {
846
+ fontWeight: '700',
847
+ },
848
+ fieldStack: {
849
+ gap: spacing.sm,
850
+ marginTop: spacing.sm + 2,
851
+ },
852
+ input: {
853
+ borderWidth: StyleSheet.hairlineWidth,
854
+ borderColor: palette.disabled,
855
+ borderRadius: radius.md,
856
+ paddingHorizontal: spacing.md,
857
+ paddingVertical: spacing.sm + 2,
858
+ fontSize: fontSize.input,
859
+ color: palette.text,
860
+ backgroundColor: palette.white,
861
+ minHeight: 44,
862
+ },
863
+ textarea: {
864
+ minHeight: 84,
865
+ textAlignVertical: 'top',
866
+ },
867
+ consentRow: {
868
+ flexDirection: 'row',
869
+ alignItems: 'flex-start',
870
+ gap: spacing.sm,
871
+ },
872
+ checkbox: {
873
+ width: 22,
874
+ height: 22,
875
+ borderRadius: 6,
876
+ borderWidth: 1.5,
877
+ borderColor: palette.disabled,
878
+ alignItems: 'center',
879
+ justifyContent: 'center',
880
+ },
881
+ checkmark: {
882
+ color: palette.white,
883
+ fontSize: fontSize.meta,
884
+ fontWeight: '700',
885
+ },
886
+ consentText: {
887
+ flex: 1,
888
+ fontSize: fontSize.meta,
889
+ lineHeight: fontSize.meta * 1.45,
890
+ color: palette.text,
891
+ },
892
+ linkButton: {
893
+ fontSize: fontSize.meta,
894
+ fontWeight: '600',
895
+ padding: spacing.xs,
896
+ },
897
+ section: {
898
+ marginTop: spacing.sm + 2,
899
+ },
900
+ sectionLabel: {
901
+ fontSize: fontSize.caption,
902
+ fontWeight: '600',
903
+ color: palette.text,
904
+ },
905
+ day: {
906
+ marginTop: spacing.sm + 2,
907
+ },
908
+ checkGlyph: {
909
+ fontSize: fontSize.title,
910
+ fontWeight: '700',
911
+ },
912
+ });