@tribe-nest/forge 3.59.0 → 3.61.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 (266) hide show
  1. package/package.json +2 -1
  2. package/src/_tests/testCommunity.tsx +28 -0
  3. package/src/contexts/CartContext.tsx +34 -1
  4. package/src/contexts/CommunityContext.tsx +192 -0
  5. package/src/contexts/PublicAuthContext.tsx +15 -3
  6. package/src/data/queries/_tests/metaSignals.spec.tsx +130 -0
  7. package/src/data/queries/useAccount.ts +33 -2
  8. package/src/data/queries/useAnalytics.ts +6 -6
  9. package/src/data/queries/useCallChat.ts +2 -2
  10. package/src/data/queries/useChat.ts +52 -40
  11. package/src/data/queries/useCheckouts.ts +2 -1
  12. package/src/data/queries/useCoachingAvailability.ts +10 -1
  13. package/src/data/queries/useCommunities.ts +345 -0
  14. package/src/data/queries/useCommunity.ts +450 -102
  15. package/src/data/queries/useCommunityAgent.ts +247 -0
  16. package/src/data/queries/useCommunityCalendar.ts +150 -0
  17. package/src/data/queries/useCommunityClassroom.ts +75 -0
  18. package/src/data/queries/useCommunityMembers.ts +108 -0
  19. package/src/data/queries/useCommunityModeration.ts +327 -0
  20. package/src/data/queries/useCommunityWiki.ts +137 -0
  21. package/src/data/queries/useCourses.ts +2 -0
  22. package/src/data/queries/useEmailSignIn.ts +79 -0
  23. package/src/data/queries/useEvents.ts +6 -1
  24. package/src/data/queries/useForms.ts +5 -1
  25. package/src/data/queries/useLeadMagnet.ts +6 -2
  26. package/src/data/queries/useMembership.ts +32 -7
  27. package/src/data/queries/useMembershipCalls.ts +116 -7
  28. package/src/data/queries/useMyBookings.ts +24 -1
  29. package/src/data/queries/useMyTickets.ts +24 -1
  30. package/src/data/queries/useNotifications.ts +6 -2
  31. package/src/data/queries/useOrders.ts +2 -1
  32. package/src/data/queries/usePWA.ts +111 -35
  33. package/src/data/queries/usePostCollections.ts +28 -16
  34. package/src/data/queries/usePosts.ts +16 -14
  35. package/src/data/queries/useSiteApp.ts +130 -0
  36. package/src/data/queries/useSubscriptions.ts +28 -9
  37. package/src/i18n/de.json +891 -2
  38. package/src/i18n/en.json +891 -2
  39. package/src/index.ts +36 -7
  40. package/src/runtime/pages.spec.ts +36 -0
  41. package/src/runtime/pages.ts +52 -27
  42. package/src/server/_tests/buildWebManifest.spec.ts +1 -1
  43. package/src/server/_tests/forgeServiceWorkerScript.spec.ts +4 -4
  44. package/src/types/models.ts +11 -0
  45. package/src/ui/analytics/ForgeAnalytics.tsx +64 -34
  46. package/src/ui/analytics/PageMetaPixel.tsx +23 -8
  47. package/src/ui/analytics/_tests/ForgeAnalytics.meta.spec.tsx +129 -0
  48. package/src/ui/analytics/_tests/metaCommerceEvents.spec.tsx +159 -0
  49. package/src/ui/analytics/useMetaEvents.ts +78 -0
  50. package/src/ui/format/_tests/membershipGate.spec.ts +6 -6
  51. package/src/ui/format/membershipGate.ts +19 -15
  52. package/src/ui/headless/_tests/communityAgent.spec.tsx +519 -0
  53. package/src/ui/headless/_tests/communityCalendar.spec.tsx +195 -0
  54. package/src/ui/headless/_tests/communityClassroom.spec.tsx +120 -0
  55. package/src/ui/headless/_tests/communityGate.spec.tsx +80 -0
  56. package/src/ui/headless/_tests/communityHarness.tsx +212 -0
  57. package/src/ui/headless/_tests/communityMembers.spec.tsx +316 -0
  58. package/src/ui/headless/_tests/communityPoll.spec.tsx +389 -0
  59. package/src/ui/headless/_tests/communityProvider.spec.tsx +151 -0
  60. package/src/ui/headless/_tests/memberHomeBar.spec.ts +84 -0
  61. package/src/ui/headless/_tests/siteAppBar.spec.ts +83 -0
  62. package/src/ui/headless/_tests/siteAppHarness.ts +19 -0
  63. package/src/ui/headless/_tests/siteAppHome.spec.ts +226 -0
  64. package/src/ui/headless/_tests/siteAppPrompts.spec.ts +185 -0
  65. package/src/ui/headless/_tests/useCommunityInvite.spec.tsx +104 -0
  66. package/src/ui/headless/_tests/useCommunityJoin.spec.tsx +596 -0
  67. package/src/ui/headless/_tests/useCommunityMembership.spec.tsx +179 -0
  68. package/src/ui/headless/_tests/useCommunityModeration.spec.tsx +420 -0
  69. package/src/ui/headless/_tests/useCommunityReads.spec.tsx +295 -0
  70. package/src/ui/headless/_tests/useCommunityUpgrade.spec.tsx +157 -0
  71. package/src/ui/headless/_tests/useCommunityWiki.spec.tsx +224 -0
  72. package/src/ui/headless/_tests/useSiteApp.spec.tsx +52 -0
  73. package/src/ui/headless/_tests/wikiMediaImage.spec.tsx +132 -0
  74. package/src/ui/headless/auth/useEmailCodeSignIn.ts +186 -0
  75. package/src/ui/headless/auth/useSignupForm.ts +30 -3
  76. package/src/ui/headless/chat/useChatAttachmentUpload.ts +9 -6
  77. package/src/ui/headless/chat/useChatRoom.ts +35 -1
  78. package/src/ui/headless/communities/CommunityGate.tsx +67 -0
  79. package/src/ui/headless/communities/index.ts +96 -0
  80. package/src/ui/headless/communities/paths.ts +36 -0
  81. package/src/ui/headless/communities/useCommunity.ts +140 -0
  82. package/src/ui/headless/communities/useCommunityAbout.ts +71 -0
  83. package/src/ui/headless/communities/useCommunityCalendar.ts +156 -0
  84. package/src/ui/headless/communities/useCommunityClassroom.ts +84 -0
  85. package/src/ui/headless/communities/useCommunityHome.ts +91 -0
  86. package/src/ui/headless/communities/useCommunityInvite.ts +116 -0
  87. package/src/ui/headless/communities/useCommunityJoin.ts +494 -0
  88. package/src/ui/headless/communities/useCommunityMembership.ts +182 -0
  89. package/src/ui/headless/communities/useCommunityUpgrade.ts +213 -0
  90. package/src/ui/headless/communities/useCommunityWiki.ts +266 -0
  91. package/src/ui/headless/communities/useMyMemberships.ts +59 -0
  92. package/src/ui/headless/community/communityPoll.ts +297 -0
  93. package/src/ui/headless/community/countries.ts +123 -0
  94. package/src/ui/headless/community/index.ts +81 -1
  95. package/src/ui/headless/community/linkPreview.ts +71 -0
  96. package/src/ui/headless/community/richLexical.ts +27 -2
  97. package/src/ui/headless/community/useCommunityAgent.ts +285 -0
  98. package/src/ui/headless/community/useCommunityAttachmentUpload.ts +7 -3
  99. package/src/ui/headless/community/useCommunityMembers.ts +175 -0
  100. package/src/ui/headless/community/useCommunityModeration.ts +300 -0
  101. package/src/ui/headless/community/useCommunitySpaceFeed.ts +22 -4
  102. package/src/ui/headless/community/useCommunityThread.ts +16 -3
  103. package/src/ui/headless/community/useMentionComposer.ts +17 -9
  104. package/src/ui/headless/community/useRichMentionComposer.ts +21 -5
  105. package/src/ui/headless/downloads/index.ts +8 -0
  106. package/src/ui/headless/downloads/useDownloadPage.ts +134 -0
  107. package/src/ui/headless/index.ts +21 -4
  108. package/src/ui/headless/memberHome/index.ts +11 -1
  109. package/src/ui/headless/memberHome/memberHomeBar.ts +91 -0
  110. package/src/ui/headless/memberHome/useMemberHomeNav.ts +84 -44
  111. package/src/ui/headless/membership/_tests/membershipCheckoutRefetch.spec.tsx +8 -0
  112. package/src/ui/headless/membership/_tests/useMembershipCheckoutApplication.spec.tsx +5 -4
  113. package/src/ui/headless/membership/_tests/useMembershipCheckoutQuestionnaire.spec.tsx +1 -1
  114. package/src/ui/headless/membership/_tests/useMembershipCheckoutTrial.spec.tsx +1 -1
  115. package/src/ui/headless/membership/useMembershipCheckout.ts +89 -22
  116. package/src/ui/headless/membership/useMembershipGateNotice.ts +25 -7
  117. package/src/ui/headless/portal/index.ts +57 -0
  118. package/src/ui/headless/portal/usePortal.ts +274 -0
  119. package/src/ui/headless/portal/usePortalFiles.ts +68 -0
  120. package/src/ui/headless/portal/usePortalMessages.ts +189 -0
  121. package/src/ui/headless/portal/usePortalPreview.ts +142 -0
  122. package/src/ui/headless/posts/upsell.ts +54 -8
  123. package/src/ui/headless/posts/useMemberPostsFeed.ts +6 -5
  124. package/src/ui/headless/posts/usePostCollectionDetail.ts +7 -4
  125. package/src/ui/headless/posts/usePostCollectionsList.ts +6 -5
  126. package/src/ui/headless/posts/usePostItem.ts +6 -5
  127. package/src/ui/headless/siteApp/appBar.ts +102 -0
  128. package/src/ui/headless/siteApp/appHome.ts +344 -0
  129. package/src/ui/headless/siteApp/appPrompts.ts +229 -0
  130. package/src/ui/headless/siteApp/index.ts +58 -0
  131. package/src/ui/headless/siteApp/useAppHome.ts +210 -0
  132. package/src/ui/headless/siteApp/useAppInstallRequest.ts +16 -0
  133. package/src/ui/headless/work/index.ts +2 -0
  134. package/src/ui/headless/work/useWorkPortal.ts +37 -26
  135. package/src/ui/index.ts +28 -12
  136. package/src/ui/styled/AccountDashboard.tsx +754 -602
  137. package/src/ui/styled/AiAgentWidget.tsx +13 -1
  138. package/src/ui/styled/ChatRoom.tsx +126 -16
  139. package/src/ui/styled/Checkout.tsx +8 -0
  140. package/src/ui/styled/CheckoutConfirmation.tsx +8 -0
  141. package/src/ui/styled/CoachingConfirmation.tsx +8 -0
  142. package/src/ui/styled/CoachingDetail.tsx +2 -0
  143. package/src/ui/styled/CourseConfirmation.tsx +5 -0
  144. package/src/ui/styled/CourseDetail.tsx +2 -0
  145. package/src/ui/styled/DownloadPage.tsx +263 -0
  146. package/src/ui/styled/EventConfirmation.tsx +10 -0
  147. package/src/ui/styled/EventDetail.tsx +10 -0
  148. package/src/ui/styled/FilmLibrary.tsx +22 -9
  149. package/src/ui/styled/MembershipCheckout.tsx +37 -17
  150. package/src/ui/styled/MembershipTierCallout.tsx +20 -1
  151. package/src/ui/styled/MembershipTiers.tsx +49 -11
  152. package/src/ui/styled/MusicLinkPage.tsx +5 -5
  153. package/src/ui/styled/Paywall.tsx +1 -1
  154. package/src/ui/styled/ProductDetail.tsx +12 -0
  155. package/src/ui/styled/PwaRegistration.tsx +5 -1
  156. package/src/ui/styled/SignupForm.tsx +18 -3
  157. package/src/ui/styled/_tests/AccountDashboardBookingCall.spec.tsx +6 -6
  158. package/src/ui/styled/_tests/AccountDashboardMemberships.spec.tsx +151 -0
  159. package/src/ui/styled/_tests/AccountDashboardRentals.spec.tsx +1 -1
  160. package/src/ui/styled/_tests/AccountNotifications.spec.tsx +135 -0
  161. package/src/ui/styled/_tests/CourseCallsSection.spec.tsx +7 -1
  162. package/src/ui/styled/_tests/DownloadPage.spec.tsx +68 -0
  163. package/src/ui/styled/_tests/MembershipCheckout.spec.tsx +54 -11
  164. package/src/ui/styled/_tests/membershipTierCallout.spec.tsx +1 -1
  165. package/src/ui/styled/_tests/membershipTiersCuratedAccess.spec.tsx +1 -1
  166. package/src/ui/styled/app/AppBar.tsx +298 -0
  167. package/src/ui/styled/app/AppHome.tsx +1139 -0
  168. package/src/ui/styled/app/AppHomeMark.tsx +66 -0
  169. package/src/ui/styled/app/AppPrompts.tsx +464 -0
  170. package/src/ui/styled/app/_tests/AppBar.spec.tsx +96 -0
  171. package/src/ui/styled/app/_tests/AppHome.spec.tsx +209 -0
  172. package/src/ui/styled/app/_tests/AppPrompts.spec.tsx +232 -0
  173. package/src/ui/styled/app/index.ts +7 -0
  174. package/src/ui/styled/chat/ChatThreadPanel.tsx +29 -4
  175. package/src/ui/styled/chat/MessageBubble.tsx +43 -6
  176. package/src/ui/styled/communities/CommunitiesList.tsx +88 -0
  177. package/src/ui/styled/communities/CommunityAbout.tsx +145 -0
  178. package/src/ui/styled/communities/CommunityCalendar.tsx +484 -0
  179. package/src/ui/styled/communities/CommunityClassroom.tsx +302 -0
  180. package/src/ui/styled/communities/CommunityHome.tsx +298 -0
  181. package/src/ui/styled/communities/CommunityJoinSheet.tsx +544 -0
  182. package/src/ui/styled/communities/CommunityMembershipMenu.tsx +249 -0
  183. package/src/ui/styled/communities/CommunityPending.tsx +49 -0
  184. package/src/ui/styled/communities/CommunityPublicWiki.tsx +75 -0
  185. package/src/ui/styled/communities/CommunityUpgradeSheet.tsx +137 -0
  186. package/src/ui/styled/communities/CommunityWiki.tsx +289 -0
  187. package/src/ui/styled/communities/MyMembershipsList.tsx +115 -0
  188. package/src/ui/styled/communities/_tests/CommunityAppHome.spec.tsx +92 -0
  189. package/src/ui/styled/communities/_tests/CommunityClassroomCalendar.spec.tsx +453 -0
  190. package/src/ui/styled/communities/_tests/CommunityMobile.spec.tsx +399 -0
  191. package/src/ui/styled/communities/_tests/CommunityPages.spec.tsx +102 -0
  192. package/src/ui/styled/communities/_tests/CommunityWiki.spec.tsx +223 -0
  193. package/src/ui/styled/communities/index.ts +19 -0
  194. package/src/ui/styled/communities/shared.tsx +177 -0
  195. package/src/ui/styled/community/CommunityAgent.tsx +286 -0
  196. package/src/ui/styled/community/CommunityComposer.tsx +266 -15
  197. package/src/ui/styled/community/CommunityFeed.tsx +50 -1
  198. package/src/ui/styled/community/CommunityLinkPreview.tsx +159 -0
  199. package/src/ui/styled/community/CommunityMemberGlobe.tsx +171 -0
  200. package/src/ui/styled/community/CommunityMemberProfile.tsx +137 -4
  201. package/src/ui/styled/community/CommunityMembers.tsx +387 -0
  202. package/src/ui/styled/community/CommunityModeration.tsx +587 -0
  203. package/src/ui/styled/community/CommunityModerationState.tsx +127 -0
  204. package/src/ui/styled/community/CommunityModeratorMenu.tsx +317 -0
  205. package/src/ui/styled/community/CommunityNotifications.tsx +52 -6
  206. package/src/ui/styled/community/CommunityPollCard.tsx +250 -0
  207. package/src/ui/styled/community/CommunityPostCard.tsx +110 -34
  208. package/src/ui/styled/community/CommunityPostDetail.tsx +157 -71
  209. package/src/ui/styled/community/CommunityRichText.tsx +177 -32
  210. package/src/ui/styled/community/CommunitySpaces.tsx +49 -14
  211. package/src/ui/styled/community/_tests/CommunityAgent.spec.tsx +571 -0
  212. package/src/ui/styled/community/_tests/CommunityComposer.spec.tsx +4 -1
  213. package/src/ui/styled/community/_tests/CommunityLinkPreview.spec.tsx +167 -0
  214. package/src/ui/styled/community/_tests/CommunityMembers.spec.tsx +137 -0
  215. package/src/ui/styled/community/_tests/CommunityModeration.spec.tsx +291 -0
  216. package/src/ui/styled/community/_tests/CommunityPoll.spec.tsx +386 -0
  217. package/src/ui/styled/community/_tests/CommunityPostDetail.spec.tsx +4 -1
  218. package/src/ui/styled/community/index.ts +37 -2
  219. package/src/ui/styled/forge-utilities.css +253 -0
  220. package/src/ui/styled/members/CallRecordingActions.tsx +182 -0
  221. package/src/ui/styled/members/CallTranscriptViewer.tsx +424 -0
  222. package/src/ui/styled/members/MemberCallsCalendar.tsx +41 -54
  223. package/src/ui/styled/members/MemberHomeLayout.tsx +327 -29
  224. package/src/ui/styled/members/MemberPostCard.tsx +9 -5
  225. package/src/ui/styled/members/MemberPostsFeed.tsx +3 -2
  226. package/src/ui/styled/members/PostCollectionDetail.tsx +18 -10
  227. package/src/ui/styled/members/PostCollectionsGrid.tsx +24 -10
  228. package/src/ui/styled/members/_tests/MemberCallsCalendar.spec.tsx +167 -8
  229. package/src/ui/styled/members/callTranscriptText.ts +67 -0
  230. package/src/ui/styled/members/index.ts +8 -0
  231. package/src/ui/styled/portal/PortalAccountSection.tsx +61 -0
  232. package/src/ui/styled/portal/PortalActivity.tsx +167 -0
  233. package/src/ui/styled/portal/PortalBilling.tsx +197 -0
  234. package/src/ui/styled/portal/PortalFiles.tsx +279 -0
  235. package/src/ui/styled/portal/PortalHome.tsx +401 -0
  236. package/src/ui/styled/portal/PortalMessages.tsx +300 -0
  237. package/src/ui/styled/portal/PortalPreviewBanner.tsx +75 -0
  238. package/src/ui/styled/portal/PortalProjects.tsx +407 -0
  239. package/src/ui/styled/portal/PortalShell.tsx +397 -0
  240. package/src/ui/styled/portal/_tests/PortalAppHome.spec.tsx +90 -0
  241. package/src/ui/styled/portal/_tests/PortalFiles.spec.tsx +159 -0
  242. package/src/ui/styled/portal/_tests/PortalHome.spec.tsx +148 -0
  243. package/src/ui/styled/portal/_tests/PortalMessages.spec.tsx +168 -0
  244. package/src/ui/styled/portal/_tests/PortalPreviewBanner.spec.tsx +132 -0
  245. package/src/ui/styled/portal/_tests/portalNav.spec.ts +123 -0
  246. package/src/ui/styled/portal/index.ts +13 -0
  247. package/src/ui/styled/portal/portalNav.ts +260 -0
  248. package/src/ui/styled/portal/shared.tsx +206 -0
  249. package/src/ui/styled/work/WorkInviteAccept.tsx +228 -168
  250. package/src/ui/styled/work/WorkPortalProjects.tsx +68 -120
  251. package/src/ui/styled/work/WorkProjectDocuments.tsx +99 -128
  252. package/src/ui/styled/work/WorkProjectInvoices.tsx +91 -86
  253. package/src/ui/styled/work/WorkProjectReport.tsx +11 -17
  254. package/src/ui/styled/work/WorkReportBody.tsx +223 -248
  255. package/src/ui/styled/work/WorkTaskAttachments.tsx +23 -29
  256. package/src/ui/styled/work/WorkTaskComments.tsx +75 -102
  257. package/src/ui/styled/work/WorkTaskDetail.tsx +48 -59
  258. package/src/ui/styled/work/WorkTokenReport.tsx +8 -16
  259. package/src/ui/styled/work/index.ts +1 -1
  260. package/src/utils/_tests/metaEvents.spec.ts +264 -0
  261. package/src/utils/_tests/safeRedirect.spec.ts +2 -2
  262. package/src/utils/landing.ts +19 -6
  263. package/src/utils/metaEvents.ts +302 -0
  264. package/src/ui/headless/membership/MembershipGate.tsx +0 -44
  265. package/src/ui/styled/_tests/AccountDashboardCommunity.spec.tsx +0 -149
  266. package/src/ui/styled/_tests/AccountDashboardTrial.spec.tsx +0 -105
@@ -50,6 +50,17 @@
50
50
  .fg\:pointer-events-none {
51
51
  pointer-events: none;
52
52
  }
53
+ .fg\:sr-only {
54
+ position: absolute;
55
+ width: 1px;
56
+ height: 1px;
57
+ padding: 0;
58
+ margin: -1px;
59
+ overflow: hidden;
60
+ clip: rect(0, 0, 0, 0);
61
+ white-space: nowrap;
62
+ border-width: 0;
63
+ }
53
64
  .fg\:absolute {
54
65
  position: absolute;
55
66
  }
@@ -65,6 +76,9 @@
65
76
  .fg\:inset-0 {
66
77
  inset: calc(var(--fg-spacing) * 0);
67
78
  }
79
+ .fg\:inset-x-0 {
80
+ inset-inline: calc(var(--fg-spacing) * 0);
81
+ }
68
82
  .fg\:top-2 {
69
83
  top: calc(var(--fg-spacing) * 2);
70
84
  }
@@ -134,6 +148,12 @@
134
148
  .fg\:mt-8 {
135
149
  margin-top: calc(var(--fg-spacing) * 8);
136
150
  }
151
+ .fg\:mt-10 {
152
+ margin-top: calc(var(--fg-spacing) * 10);
153
+ }
154
+ .fg\:-mr-4 {
155
+ margin-right: calc(var(--fg-spacing) * -4);
156
+ }
137
157
  .fg\:mb-1 {
138
158
  margin-bottom: calc(var(--fg-spacing) * 1);
139
159
  }
@@ -161,6 +181,15 @@
161
181
  .fg\:ml-2 {
162
182
  margin-left: calc(var(--fg-spacing) * 2);
163
183
  }
184
+ .fg\:ml-auto {
185
+ margin-left: auto;
186
+ }
187
+ .fg\:line-clamp-3 {
188
+ overflow: hidden;
189
+ display: -webkit-box;
190
+ -webkit-box-orient: vertical;
191
+ -webkit-line-clamp: 3;
192
+ }
164
193
  .fg\:block {
165
194
  display: block;
166
195
  }
@@ -179,6 +208,9 @@
179
208
  .fg\:inline-flex {
180
209
  display: inline-flex;
181
210
  }
211
+ .fg\:aspect-square {
212
+ aspect-ratio: 1 / 1;
213
+ }
182
214
  .fg\:h-0\.5 {
183
215
  height: calc(var(--fg-spacing) * 0.5);
184
216
  }
@@ -197,6 +229,9 @@
197
229
  .fg\:h-12 {
198
230
  height: calc(var(--fg-spacing) * 12);
199
231
  }
232
+ .fg\:h-16 {
233
+ height: calc(var(--fg-spacing) * 16);
234
+ }
200
235
  .fg\:h-\[70vh\] {
201
236
  height: 70vh;
202
237
  }
@@ -206,6 +241,9 @@
206
241
  .fg\:h-full {
207
242
  height: 100%;
208
243
  }
244
+ .fg\:max-h-\[90dvh\] {
245
+ max-height: 90dvh;
246
+ }
209
247
  .fg\:min-h-\[320px\] {
210
248
  min-height: 320px;
211
249
  }
@@ -224,6 +262,12 @@
224
262
  .fg\:w-10 {
225
263
  width: calc(var(--fg-spacing) * 10);
226
264
  }
265
+ .fg\:w-11 {
266
+ width: calc(var(--fg-spacing) * 11);
267
+ }
268
+ .fg\:w-16 {
269
+ width: calc(var(--fg-spacing) * 16);
270
+ }
227
271
  .fg\:w-52 {
228
272
  width: calc(var(--fg-spacing) * 52);
229
273
  }
@@ -242,6 +286,21 @@
242
286
  .fg\:max-w-6xl {
243
287
  max-width: var(--fg-container-6xl);
244
288
  }
289
+ .fg\:max-w-\[85\%\] {
290
+ max-width: 85%;
291
+ }
292
+ .fg\:max-w-\[220px\] {
293
+ max-width: 220px;
294
+ }
295
+ .fg\:max-w-\[420px\] {
296
+ max-width: 420px;
297
+ }
298
+ .fg\:max-w-\[720px\] {
299
+ max-width: 720px;
300
+ }
301
+ .fg\:max-w-full {
302
+ max-width: 100%;
303
+ }
245
304
  .fg\:max-w-md {
246
305
  max-width: var(--fg-container-md);
247
306
  }
@@ -251,12 +310,18 @@
251
310
  .fg\:min-w-0 {
252
311
  min-width: calc(var(--fg-spacing) * 0);
253
312
  }
313
+ .fg\:min-w-1 {
314
+ min-width: calc(var(--fg-spacing) * 1);
315
+ }
254
316
  .fg\:flex-1 {
255
317
  flex: 1;
256
318
  }
257
319
  .fg\:shrink-0 {
258
320
  flex-shrink: 0;
259
321
  }
322
+ .fg\:cursor-grab {
323
+ cursor: grab;
324
+ }
260
325
  .fg\:cursor-pointer {
261
326
  cursor: pointer;
262
327
  }
@@ -275,6 +340,9 @@
275
340
  .fg\:grid-cols-2 {
276
341
  grid-template-columns: repeat(2, minmax(0, 1fr));
277
342
  }
343
+ .fg\:grid-cols-4 {
344
+ grid-template-columns: repeat(4, minmax(0, 1fr));
345
+ }
278
346
  .fg\:flex-col {
279
347
  flex-direction: column;
280
348
  }
@@ -284,6 +352,9 @@
284
352
  .fg\:flex-wrap {
285
353
  flex-wrap: wrap;
286
354
  }
355
+ .fg\:items-baseline {
356
+ align-items: baseline;
357
+ }
287
358
  .fg\:items-center {
288
359
  align-items: center;
289
360
  }
@@ -329,9 +400,15 @@
329
400
  .fg\:gap-6 {
330
401
  gap: calc(var(--fg-spacing) * 6);
331
402
  }
403
+ .fg\:gap-7 {
404
+ gap: calc(var(--fg-spacing) * 7);
405
+ }
332
406
  .fg\:gap-8 {
333
407
  gap: calc(var(--fg-spacing) * 8);
334
408
  }
409
+ .fg\:gap-10 {
410
+ gap: calc(var(--fg-spacing) * 10);
411
+ }
335
412
  .fg\:space-y-2 {
336
413
  :where(& > :not(:last-child)) {
337
414
  --tw-space-y-reverse: 0;
@@ -353,9 +430,15 @@
353
430
  margin-block-end: calc(calc(var(--fg-spacing) * 6) * calc(1 - var(--tw-space-y-reverse)));
354
431
  }
355
432
  }
433
+ .fg\:gap-x-3 {
434
+ column-gap: calc(var(--fg-spacing) * 3);
435
+ }
356
436
  .fg\:gap-x-4 {
357
437
  column-gap: calc(var(--fg-spacing) * 4);
358
438
  }
439
+ .fg\:gap-x-5 {
440
+ column-gap: calc(var(--fg-spacing) * 5);
441
+ }
359
442
  .fg\:space-x-4 {
360
443
  :where(& > :not(:last-child)) {
361
444
  --tw-space-x-reverse: 0;
@@ -366,12 +449,20 @@
366
449
  .fg\:gap-y-2 {
367
450
  row-gap: calc(var(--fg-spacing) * 2);
368
451
  }
452
+ .fg\:truncate {
453
+ overflow: hidden;
454
+ text-overflow: ellipsis;
455
+ white-space: nowrap;
456
+ }
369
457
  .fg\:overflow-hidden {
370
458
  overflow: hidden;
371
459
  }
372
460
  .fg\:overflow-x-auto {
373
461
  overflow-x: auto;
374
462
  }
463
+ .fg\:overflow-y-auto {
464
+ overflow-y: auto;
465
+ }
375
466
  .fg\:rounded {
376
467
  border-radius: 0.25rem;
377
468
  }
@@ -387,6 +478,10 @@
387
478
  .fg\:rounded-xl {
388
479
  border-radius: var(--fg-radius-xl);
389
480
  }
481
+ .fg\:rounded-t-\[var\(--cs-radius\)\] {
482
+ border-top-left-radius: var(--cs-radius);
483
+ border-top-right-radius: var(--cs-radius);
484
+ }
390
485
  .fg\:border {
391
486
  border-style: var(--tw-border-style);
392
487
  border-width: 1px;
@@ -463,6 +558,9 @@
463
558
  .fg\:p-8 {
464
559
  padding: calc(var(--fg-spacing) * 8);
465
560
  }
561
+ .fg\:px-0 {
562
+ padding-inline: calc(var(--fg-spacing) * 0);
563
+ }
466
564
  .fg\:px-2 {
467
565
  padding-inline: calc(var(--fg-spacing) * 2);
468
566
  }
@@ -472,6 +570,9 @@
472
570
  .fg\:px-4 {
473
571
  padding-inline: calc(var(--fg-spacing) * 4);
474
572
  }
573
+ .fg\:px-5 {
574
+ padding-inline: calc(var(--fg-spacing) * 5);
575
+ }
475
576
  .fg\:py-0\.5 {
476
577
  padding-block: calc(var(--fg-spacing) * 0.5);
477
578
  }
@@ -487,6 +588,9 @@
487
588
  .fg\:py-3 {
488
589
  padding-block: calc(var(--fg-spacing) * 3);
489
590
  }
591
+ .fg\:py-4 {
592
+ padding-block: calc(var(--fg-spacing) * 4);
593
+ }
490
594
  .fg\:py-5 {
491
595
  padding-block: calc(var(--fg-spacing) * 5);
492
596
  }
@@ -496,6 +600,9 @@
496
600
  .fg\:py-8 {
497
601
  padding-block: calc(var(--fg-spacing) * 8);
498
602
  }
603
+ .fg\:py-10 {
604
+ padding-block: calc(var(--fg-spacing) * 10);
605
+ }
499
606
  .fg\:py-12 {
500
607
  padding-block: calc(var(--fg-spacing) * 12);
501
608
  }
@@ -505,24 +612,39 @@
505
612
  .fg\:pt-2 {
506
613
  padding-top: calc(var(--fg-spacing) * 2);
507
614
  }
615
+ .fg\:pt-3 {
616
+ padding-top: calc(var(--fg-spacing) * 3);
617
+ }
508
618
  .fg\:pt-4 {
509
619
  padding-top: calc(var(--fg-spacing) * 4);
510
620
  }
511
621
  .fg\:pt-6 {
512
622
  padding-top: calc(var(--fg-spacing) * 6);
513
623
  }
624
+ .fg\:pr-4 {
625
+ padding-right: calc(var(--fg-spacing) * 4);
626
+ }
514
627
  .fg\:pb-4 {
515
628
  padding-bottom: calc(var(--fg-spacing) * 4);
516
629
  }
517
630
  .fg\:pb-6 {
518
631
  padding-bottom: calc(var(--fg-spacing) * 6);
519
632
  }
633
+ .fg\:pb-28 {
634
+ padding-bottom: calc(var(--fg-spacing) * 28);
635
+ }
520
636
  .fg\:pb-32 {
521
637
  padding-bottom: calc(var(--fg-spacing) * 32);
522
638
  }
639
+ .fg\:pb-\[calc\(72px\+env\(safe-area-inset-bottom\)\)\] {
640
+ padding-bottom: calc(72px + env(safe-area-inset-bottom));
641
+ }
523
642
  .fg\:text-center {
524
643
  text-align: center;
525
644
  }
645
+ .fg\:text-left {
646
+ text-align: left;
647
+ }
526
648
  .fg\:text-right {
527
649
  text-align: right;
528
650
  }
@@ -586,9 +708,15 @@
586
708
  --tw-tracking: var(--fg-tracking-wide);
587
709
  letter-spacing: var(--fg-tracking-wide);
588
710
  }
711
+ .fg\:break-words {
712
+ overflow-wrap: break-word;
713
+ }
589
714
  .fg\:whitespace-pre-line {
590
715
  white-space: pre-line;
591
716
  }
717
+ .fg\:whitespace-pre-wrap {
718
+ white-space: pre-wrap;
719
+ }
592
720
  .fg\:text-muted {
593
721
  color: var(--fg-color-muted);
594
722
  }
@@ -645,6 +773,11 @@
645
773
  -webkit-user-select: none;
646
774
  user-select: none;
647
775
  }
776
+ .fg\:first\:mt-0 {
777
+ &:first-child {
778
+ margin-top: calc(var(--fg-spacing) * 0);
779
+ }
780
+ }
648
781
  .fg\:focus\:border-primary {
649
782
  &:focus {
650
783
  border-color: var(--fg-color-primary);
@@ -680,6 +813,11 @@
680
813
  flex-direction: row;
681
814
  }
682
815
  }
816
+ .fg\:sm\:flex-wrap {
817
+ @media (width >= 40rem) {
818
+ flex-wrap: wrap;
819
+ }
820
+ }
683
821
  .fg\:sm\:items-center {
684
822
  @media (width >= 40rem) {
685
823
  align-items: center;
@@ -750,6 +888,11 @@
750
888
  width: calc(var(--fg-spacing) * 64);
751
889
  }
752
890
  }
891
+ .fg\:md\:w-\[240px\] {
892
+ @media (width >= 48rem) {
893
+ width: 240px;
894
+ }
895
+ }
753
896
  .fg\:md\:w-\[260px\] {
754
897
  @media (width >= 48rem) {
755
898
  width: 260px;
@@ -760,11 +903,21 @@
760
903
  width: 300px;
761
904
  }
762
905
  }
906
+ .fg\:md\:w-\[320px\] {
907
+ @media (width >= 48rem) {
908
+ width: 320px;
909
+ }
910
+ }
763
911
  .fg\:md\:w-\[360px\] {
764
912
  @media (width >= 48rem) {
765
913
  width: 360px;
766
914
  }
767
915
  }
916
+ .fg\:md\:max-w-\[70\%\] {
917
+ @media (width >= 48rem) {
918
+ max-width: 70%;
919
+ }
920
+ }
768
921
  .fg\:md\:shrink-0 {
769
922
  @media (width >= 48rem) {
770
923
  flex-shrink: 0;
@@ -790,6 +943,41 @@
790
943
  align-items: flex-start;
791
944
  }
792
945
  }
946
+ .fg\:md\:py-16 {
947
+ @media (width >= 48rem) {
948
+ padding-block: calc(var(--fg-spacing) * 16);
949
+ }
950
+ }
951
+ .fg\:lg\:sticky {
952
+ @media (width >= 64rem) {
953
+ position: sticky;
954
+ }
955
+ }
956
+ .fg\:lg\:inset-x-auto {
957
+ @media (width >= 64rem) {
958
+ inset-inline: auto;
959
+ }
960
+ }
961
+ .fg\:lg\:top-1\/2 {
962
+ @media (width >= 64rem) {
963
+ top: calc(1/2 * 100%);
964
+ }
965
+ }
966
+ .fg\:lg\:top-6 {
967
+ @media (width >= 64rem) {
968
+ top: calc(var(--fg-spacing) * 6);
969
+ }
970
+ }
971
+ .fg\:lg\:bottom-auto {
972
+ @media (width >= 64rem) {
973
+ bottom: auto;
974
+ }
975
+ }
976
+ .fg\:lg\:left-1\/2 {
977
+ @media (width >= 64rem) {
978
+ left: calc(1/2 * 100%);
979
+ }
980
+ }
793
981
  .fg\:lg\:col-span-1 {
794
982
  @media (width >= 64rem) {
795
983
  grid-column: span 1 / span 1;
@@ -800,6 +988,11 @@
800
988
  grid-column: span 2 / span 2;
801
989
  }
802
990
  }
991
+ .fg\:lg\:block {
992
+ @media (width >= 64rem) {
993
+ display: block;
994
+ }
995
+ }
803
996
  .fg\:lg\:flex {
804
997
  @media (width >= 64rem) {
805
998
  display: flex;
@@ -810,6 +1003,28 @@
810
1003
  display: none;
811
1004
  }
812
1005
  }
1006
+ .fg\:lg\:max-h-\[88vh\] {
1007
+ @media (width >= 64rem) {
1008
+ max-height: 88vh;
1009
+ }
1010
+ }
1011
+ .fg\:lg\:w-\[min\(560px\,calc\(100vw-32px\)\)\] {
1012
+ @media (width >= 64rem) {
1013
+ width: min(560px, calc(100vw - 32px));
1014
+ }
1015
+ }
1016
+ .fg\:lg\:-translate-x-1\/2 {
1017
+ @media (width >= 64rem) {
1018
+ --tw-translate-x: calc(calc(1/2 * 100%) * -1);
1019
+ translate: var(--tw-translate-x) var(--tw-translate-y);
1020
+ }
1021
+ }
1022
+ .fg\:lg\:-translate-y-1\/2 {
1023
+ @media (width >= 64rem) {
1024
+ --tw-translate-y: calc(calc(1/2 * 100%) * -1);
1025
+ translate: var(--tw-translate-x) var(--tw-translate-y);
1026
+ }
1027
+ }
813
1028
  .fg\:lg\:grid-cols-3 {
814
1029
  @media (width >= 64rem) {
815
1030
  grid-template-columns: repeat(3, minmax(0, 1fr));
@@ -830,6 +1045,21 @@
830
1045
  gap: calc(var(--fg-spacing) * 8);
831
1046
  }
832
1047
  }
1048
+ .fg\:lg\:gap-12 {
1049
+ @media (width >= 64rem) {
1050
+ gap: calc(var(--fg-spacing) * 12);
1051
+ }
1052
+ }
1053
+ .fg\:lg\:rounded-\[var\(--cs-radius-lg\)\] {
1054
+ @media (width >= 64rem) {
1055
+ border-radius: var(--cs-radius-lg);
1056
+ }
1057
+ }
1058
+ .fg\:lg\:p-6 {
1059
+ @media (width >= 64rem) {
1060
+ padding: calc(var(--fg-spacing) * 6);
1061
+ }
1062
+ }
833
1063
  .fg\:lg\:p-8 {
834
1064
  @media (width >= 64rem) {
835
1065
  padding: calc(var(--fg-spacing) * 8);
@@ -840,6 +1070,11 @@
840
1070
  padding-block: calc(var(--fg-spacing) * 12);
841
1071
  }
842
1072
  }
1073
+ .fg\:lg\:pb-0 {
1074
+ @media (width >= 64rem) {
1075
+ padding-bottom: calc(var(--fg-spacing) * 0);
1076
+ }
1077
+ }
843
1078
  .fg\:lg\:text-3xl {
844
1079
  @media (width >= 64rem) {
845
1080
  font-size: var(--fg-text-3xl);
@@ -939,6 +1174,21 @@
939
1174
  inherits: false;
940
1175
  initial-value: 0 0 #0000;
941
1176
  }
1177
+ @property --tw-translate-x {
1178
+ syntax: "*";
1179
+ inherits: false;
1180
+ initial-value: 0;
1181
+ }
1182
+ @property --tw-translate-y {
1183
+ syntax: "*";
1184
+ inherits: false;
1185
+ initial-value: 0;
1186
+ }
1187
+ @property --tw-translate-z {
1188
+ syntax: "*";
1189
+ inherits: false;
1190
+ initial-value: 0;
1191
+ }
942
1192
  @layer properties {
943
1193
  @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
944
1194
  *, ::before, ::after, ::backdrop {
@@ -962,6 +1212,9 @@
962
1212
  --tw-ring-offset-width: 0px;
963
1213
  --tw-ring-offset-color: #fff;
964
1214
  --tw-ring-offset-shadow: 0 0 #0000;
1215
+ --tw-translate-x: 0;
1216
+ --tw-translate-y: 0;
1217
+ --tw-translate-z: 0;
965
1218
  }
966
1219
  }
967
1220
  }
@@ -0,0 +1,182 @@
1
+ import { useState, type CSSProperties } from "react";
2
+ import { Download, FileText, Loader2, PlayCircle } from "lucide-react";
3
+ import { useQueryClient } from "@tanstack/react-query";
4
+ import { useForgeT } from "../../../i18n";
5
+ import { useThemeTokens } from "../../theme/ForgeThemeProvider";
6
+ import { useForge } from "../../../provider/ForgeProvider";
7
+ import {
8
+ fetchMemberCallTranscript,
9
+ memberCallTranscriptKey,
10
+ type MemberCallTranscript,
11
+ type MyMembershipCall,
12
+ } from "../../../data/queries/useMembershipCalls";
13
+ import { downloadTranscript } from "./callTranscriptText";
14
+
15
+ /**
16
+ * The rewatch pieces of a past call, shared by every surface that lists calls
17
+ * a member attended: the members calendar's details modal and the community
18
+ * calendar's agenda. One set of pieces, so a recording reads the same wherever
19
+ * it is found.
20
+ */
21
+
22
+ /** A call's recording as the attendee reads return it. */
23
+ export type CallRecording = NonNullable<MyMembershipCall["recording"]>;
24
+
25
+ /** Whether a recording can be watched now. */
26
+ export function callRecordingWatchable(recording: CallRecording | null | undefined): boolean {
27
+ return recording?.status === "ready" && !!recording.watchUrl;
28
+ }
29
+
30
+ export interface CallRecordingWatchLinkProps {
31
+ recording: CallRecording;
32
+ /** Drawn as a secondary action, when a stronger one (Join) sits beside it. */
33
+ secondary?: boolean;
34
+ }
35
+
36
+ /** "Watch recording", opening the signed video address in a new tab. Renders nothing until it is ready. */
37
+ export function CallRecordingWatchLink({ recording, secondary }: CallRecordingWatchLinkProps) {
38
+ const t = useThemeTokens();
39
+ const tr = useForgeT();
40
+ if (!callRecordingWatchable(recording)) return null;
41
+ const minutes = recording.durationSeconds ? Math.round(recording.durationSeconds / 60) : null;
42
+ return (
43
+ <a
44
+ data-testid="call-recording-watch"
45
+ href={recording.watchUrl!}
46
+ target="_blank"
47
+ rel="noreferrer"
48
+ style={{
49
+ display: "flex",
50
+ alignItems: "center",
51
+ gap: 6,
52
+ padding: "10px 20px",
53
+ borderRadius: t.cornerRadius,
54
+ background: secondary ? "transparent" : t.primary,
55
+ border: secondary ? `1px solid ${t.border}` : "none",
56
+ color: secondary ? t.text : t.textPrimary,
57
+ fontSize: 14,
58
+ fontWeight: 700,
59
+ textDecoration: "none",
60
+ }}
61
+ >
62
+ <PlayCircle size={16} />
63
+ {minutes ? tr("forge.call_recording_actions.watch_minutes", { minutes }) : tr("forge.call_recording_actions.watch")}
64
+ </a>
65
+ );
66
+ }
67
+
68
+ /** The recording is still being put together: come back shortly, not an error. */
69
+ export function CallRecordingProcessing() {
70
+ const t = useThemeTokens();
71
+ const tr = useForgeT();
72
+ return (
73
+ <span
74
+ data-testid="call-recording-processing"
75
+ style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 14, color: t.muted, fontWeight: 600 }}
76
+ >
77
+ <Loader2 size={15} />
78
+ {tr("forge.call_recording_actions.processing")}
79
+ </span>
80
+ );
81
+ }
82
+
83
+ /**
84
+ * The call's summary, and the way into its transcript.
85
+ *
86
+ * The lines are NOT here: a two-hour meeting is thousands of them, which is
87
+ * unusable in a modal or a list. "Read transcript" opens them beside the video
88
+ * (`onRead`), and Download reads them once without opening anything. A member
89
+ * sees the finished thing or that it is being prepared, never a failure: why
90
+ * one could not be made is the business's problem, not its audience's.
91
+ */
92
+ export function CallTranscriptPanel({
93
+ recordingId,
94
+ transcript,
95
+ onRead,
96
+ }: {
97
+ recordingId: string;
98
+ transcript: MemberCallTranscript;
99
+ onRead: () => void;
100
+ }) {
101
+ const t = useThemeTokens();
102
+ const tr = useForgeT();
103
+ const { client, profileId } = useForge();
104
+ const queryClient = useQueryClient();
105
+ const [downloading, setDownloading] = useState(false);
106
+ const heading: CSSProperties = { display: "flex", alignItems: "center", gap: 8, fontSize: 14, fontWeight: 700 };
107
+ const button: CSSProperties = {
108
+ display: "inline-flex",
109
+ alignItems: "center",
110
+ gap: 6,
111
+ padding: "8px 14px",
112
+ borderRadius: t.cornerRadius,
113
+ border: `1px solid ${t.border}`,
114
+ background: "transparent",
115
+ color: t.text,
116
+ fontFamily: t.fontFamily,
117
+ fontSize: 14,
118
+ fontWeight: 600,
119
+ cursor: "pointer",
120
+ };
121
+
122
+ if (transcript.status === "processing") {
123
+ return (
124
+ <div style={{ display: "flex", flexDirection: "column", gap: 6 }} data-testid="member-call-transcript">
125
+ <span style={heading}>
126
+ <FileText size={16} />
127
+ {tr("forge.call_recording_actions.transcript")}
128
+ </span>
129
+ <span style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 14, color: t.muted }} role="status">
130
+ <Loader2 size={15} />
131
+ {tr("forge.call_recording_actions.transcript_processing")}
132
+ </span>
133
+ </div>
134
+ );
135
+ }
136
+
137
+ const download = async () => {
138
+ setDownloading(true);
139
+ try {
140
+ const detail = await queryClient.fetchQuery({
141
+ queryKey: memberCallTranscriptKey(profileId, recordingId),
142
+ queryFn: () => fetchMemberCallTranscript(client, profileId, recordingId),
143
+ });
144
+ downloadTranscript(detail);
145
+ } finally {
146
+ setDownloading(false);
147
+ }
148
+ };
149
+
150
+ return (
151
+ <div style={{ display: "flex", flexDirection: "column", gap: 10 }} data-testid="member-call-transcript">
152
+ <span style={heading}>
153
+ <FileText size={16} />
154
+ {tr("forge.call_recording_actions.summary")}
155
+ </span>
156
+ <p
157
+ style={{
158
+ margin: 0,
159
+ fontSize: 14,
160
+ lineHeight: 1.5,
161
+ display: "-webkit-box",
162
+ WebkitLineClamp: 4,
163
+ WebkitBoxOrient: "vertical",
164
+ overflow: "hidden",
165
+ color: transcript.summary ? t.text : t.muted,
166
+ }}
167
+ >
168
+ {transcript.summary ? transcript.summary.summary : tr("forge.call_recording_actions.transcript_ready")}
169
+ </p>
170
+ <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
171
+ <button type="button" onClick={onRead} style={button}>
172
+ <FileText size={15} />
173
+ {tr("forge.call_recording_actions.read_transcript")}
174
+ </button>
175
+ <button type="button" onClick={() => void download()} disabled={downloading} style={button}>
176
+ {downloading ? <Loader2 size={15} /> : <Download size={15} />}
177
+ {tr("forge.call_recording_actions.download")}
178
+ </button>
179
+ </div>
180
+ </div>
181
+ );
182
+ }