@fluid-app/portal-sdk 0.1.350 → 0.1.351

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 (276) hide show
  1. package/dist/{AddressAutocompleteInput-JBXGT1iL.cjs → AddressAutocompleteInput-C2Wx9yrV.cjs} +9 -9
  2. package/dist/AddressAutocompleteInput-C2Wx9yrV.cjs.map +1 -0
  3. package/dist/{AddressAutocompleteInput-BANMgwzR.mjs → AddressAutocompleteInput-CgOFJKwF.mjs} +9 -9
  4. package/dist/AddressAutocompleteInput-CgOFJKwF.mjs.map +1 -0
  5. package/dist/{AlertWidget-BmOGoxTA.mjs → AlertWidget-DMwOt9u8.mjs} +3 -3
  6. package/dist/{AlertWidget-BmOGoxTA.mjs.map → AlertWidget-DMwOt9u8.mjs.map} +1 -1
  7. package/dist/{AppDownloadScreen-C78ggXk3.mjs → AppDownloadScreen-CBg2shvo.mjs} +5 -5
  8. package/dist/{AppDownloadScreen-C78ggXk3.mjs.map → AppDownloadScreen-CBg2shvo.mjs.map} +1 -1
  9. package/dist/{AppDownloadScreen-DIcwvAZ4.cjs → AppDownloadScreen-CMR1y4kP.cjs} +4 -4
  10. package/dist/{AppDownloadScreen-DIcwvAZ4.cjs.map → AppDownloadScreen-CMR1y4kP.cjs.map} +1 -1
  11. package/dist/{AppNavigationContext-BCj6iFxr.mjs → AppNavigationContext-B-wToUBG.mjs} +3 -3
  12. package/dist/AppNavigationContext-B-wToUBG.mjs.map +1 -0
  13. package/dist/{AppNavigationContext-CoNtdUrr.cjs → AppNavigationContext-CLOwdlpx.cjs} +2 -2
  14. package/dist/AppNavigationContext-CLOwdlpx.cjs.map +1 -0
  15. package/dist/{BulletListWidget-AIIf29RP.mjs → BulletListWidget-D--Mmasw.mjs} +2 -2
  16. package/dist/{BulletListWidget-AIIf29RP.mjs.map → BulletListWidget-D--Mmasw.mjs.map} +1 -1
  17. package/dist/{CalendarWidget-DW5wz5ke.cjs → CalendarWidget-Aa7H9M7f.cjs} +4 -4
  18. package/dist/{CalendarWidget-DW5wz5ke.cjs.map → CalendarWidget-Aa7H9M7f.cjs.map} +1 -1
  19. package/dist/{CalendarWidget-Dn55musX.mjs → CalendarWidget-Cx6KvkaG.mjs} +5 -5
  20. package/dist/{CalendarWidget-Dn55musX.mjs.map → CalendarWidget-Cx6KvkaG.mjs.map} +1 -1
  21. package/dist/{CardWidget-DsfLoyup.cjs → CardWidget-Caytu8qs.cjs} +3 -3
  22. package/dist/{CardWidget--S5FBquC.cjs → CardWidget-CqewqU5a.cjs} +2 -2
  23. package/dist/{CardWidget--S5FBquC.cjs.map → CardWidget-CqewqU5a.cjs.map} +1 -1
  24. package/dist/{CardWidget-ChfurL_z.mjs → CardWidget-DARqLSwy.mjs} +3 -3
  25. package/dist/{CardWidget-ChfurL_z.mjs.map → CardWidget-DARqLSwy.mjs.map} +1 -1
  26. package/dist/{CarouselWidget-SrCl4cSn.mjs → CarouselWidget-Bnl_iggA.mjs} +2 -2
  27. package/dist/{CarouselWidget-SrCl4cSn.mjs.map → CarouselWidget-Bnl_iggA.mjs.map} +1 -1
  28. package/dist/{CatchUpWidget-B_in7Bjq.mjs → CatchUpWidget-Bplozt8D.mjs} +5 -5
  29. package/dist/{CatchUpWidget-B_in7Bjq.mjs.map → CatchUpWidget-Bplozt8D.mjs.map} +1 -1
  30. package/dist/{CatchUpWidget-_ErrC-e8.cjs → CatchUpWidget-DEyXTEFj.cjs} +4 -4
  31. package/dist/{CatchUpWidget-_ErrC-e8.cjs.map → CatchUpWidget-DEyXTEFj.cjs.map} +1 -1
  32. package/dist/{ChartWidget-CIr3v8x6.mjs → ChartWidget-BPOZNKI_.mjs} +2 -2
  33. package/dist/{ChartWidget-CIr3v8x6.mjs.map → ChartWidget-BPOZNKI_.mjs.map} +1 -1
  34. package/dist/{ContactsScreen-f3dXV198.cjs → ContactsScreen-BFbWTMu9.cjs} +4 -4
  35. package/dist/{ContactsScreen-f3dXV198.cjs.map → ContactsScreen-BFbWTMu9.cjs.map} +1 -1
  36. package/dist/{ContactsScreen-DJ8O35Pl.mjs → ContactsScreen-CnLSICv5.mjs} +5 -5
  37. package/dist/{ContactsScreen-DJ8O35Pl.mjs.map → ContactsScreen-CnLSICv5.mjs.map} +1 -1
  38. package/dist/{ContactsScreen-CVmYvRZi.cjs → ContactsScreen-DQjdfcAV.cjs} +5 -5
  39. package/dist/{ContainerWidget-BHWPtBmF.mjs → ContainerWidget-Bv0f8-TC.mjs} +3 -3
  40. package/dist/{ContainerWidget-BHWPtBmF.mjs.map → ContainerWidget-Bv0f8-TC.mjs.map} +1 -1
  41. package/dist/{ContainerWidget-Cf_D4TAi.cjs → ContainerWidget-CF6VDxd8.cjs} +2 -2
  42. package/dist/{ContainerWidget-Cf_D4TAi.cjs.map → ContainerWidget-CF6VDxd8.cjs.map} +1 -1
  43. package/dist/ContainerWidget-D3JoA0IP.cjs +8 -0
  44. package/dist/{CustomersScreen-DGBwaZrt.mjs → CustomersScreen-BHqlHdHZ.mjs} +3 -3
  45. package/dist/{CustomersScreen-DGBwaZrt.mjs.map → CustomersScreen-BHqlHdHZ.mjs.map} +1 -1
  46. package/dist/{CustomersScreen-BXvWK7Y1.cjs → CustomersScreen-C8b3P79M.cjs} +2 -2
  47. package/dist/{CustomersScreen-BXvWK7Y1.cjs.map → CustomersScreen-C8b3P79M.cjs.map} +1 -1
  48. package/dist/{EmbedWidget-D5kFw9HS.mjs → EmbedWidget-DRfjHvRs.mjs} +2 -2
  49. package/dist/{EmbedWidget-D5kFw9HS.mjs.map → EmbedWidget-DRfjHvRs.mjs.map} +1 -1
  50. package/dist/{FluidProvider-CYBXFElI.mjs → FluidProvider-B4Za_Bnw.mjs} +69 -69
  51. package/dist/FluidProvider-B4Za_Bnw.mjs.map +1 -0
  52. package/dist/{FluidProvider-BVJmIM13.cjs → FluidProvider-DvHJZkGi.cjs} +36 -36
  53. package/dist/FluidProvider-DvHJZkGi.cjs.map +1 -0
  54. package/dist/{ImageWidget-B3ZTuy4v.mjs → ImageWidget-C7nfRJ6p.mjs} +2 -2
  55. package/dist/{ImageWidget-B3ZTuy4v.mjs.map → ImageWidget-C7nfRJ6p.mjs.map} +1 -1
  56. package/dist/{LayoutWidget-3G-w-YLz.cjs → LayoutWidget-6_Huueb4.cjs} +3 -3
  57. package/dist/{LayoutWidget-CrZG6Ipw.mjs → LayoutWidget-CSFWeXYY.mjs} +3 -3
  58. package/dist/{LayoutWidget-CrZG6Ipw.mjs.map → LayoutWidget-CSFWeXYY.mjs.map} +1 -1
  59. package/dist/{LayoutWidget-CG-dWz_c.cjs → LayoutWidget-DD1ZqWXg.cjs} +2 -2
  60. package/dist/{LayoutWidget-CG-dWz_c.cjs.map → LayoutWidget-DD1ZqWXg.cjs.map} +1 -1
  61. package/dist/{LinkWidget-fPowKN73.mjs → LinkWidget-DI2Uod5O.mjs} +2 -2
  62. package/dist/{LinkWidget-fPowKN73.mjs.map → LinkWidget-DI2Uod5O.mjs.map} +1 -1
  63. package/dist/{ListWidget-CQuNRg_g.mjs → ListWidget-BdUhs2fo.mjs} +2 -2
  64. package/dist/{ListWidget-CQuNRg_g.mjs.map → ListWidget-BdUhs2fo.mjs.map} +1 -1
  65. package/dist/MessagingScreen-B3LmRgr4.mjs +50 -0
  66. package/dist/{MessagingScreen-Db6wQydU.cjs → MessagingScreen-B6kMcx8v.cjs} +25 -25
  67. package/dist/{MessagingScreen-3o_Qb6u_.mjs → MessagingScreen-CCVF9paI.mjs} +10 -10
  68. package/dist/MessagingScreen-CCVF9paI.mjs.map +1 -0
  69. package/dist/{MessagingScreen-C4SUSPy3.cjs → MessagingScreen-DQYJZhz6.cjs} +8 -8
  70. package/dist/MessagingScreen-DQYJZhz6.cjs.map +1 -0
  71. package/dist/{MySiteScreen-D3rkI2UJ.cjs → MySiteScreen-BZnLxHAq.cjs} +6 -6
  72. package/dist/{MySiteScreen-D3rkI2UJ.cjs.map → MySiteScreen-BZnLxHAq.cjs.map} +1 -1
  73. package/dist/MySiteScreen-CYGVxzCE.cjs +11 -0
  74. package/dist/{MySiteScreen-BC_K8gIq.mjs → MySiteScreen-tKgO4G0B.mjs} +7 -7
  75. package/dist/{MySiteScreen-BC_K8gIq.mjs.map → MySiteScreen-tKgO4G0B.mjs.map} +1 -1
  76. package/dist/{MySiteWidget-D6QyWuzN.cjs → MySiteWidget-CabBtq5C.cjs} +4 -4
  77. package/dist/{MySiteWidget-D6QyWuzN.cjs.map → MySiteWidget-CabBtq5C.cjs.map} +1 -1
  78. package/dist/{MySiteWidget-BLALS8Ve.mjs → MySiteWidget-ivytLi6H.mjs} +5 -5
  79. package/dist/{MySiteWidget-BLALS8Ve.mjs.map → MySiteWidget-ivytLi6H.mjs.map} +1 -1
  80. package/dist/{NestedWidget-DuNuNaeT.mjs → NestedWidget-gqdWZS9Q.mjs} +2 -2
  81. package/dist/{NestedWidget-DuNuNaeT.mjs.map → NestedWidget-gqdWZS9Q.mjs.map} +1 -1
  82. package/dist/{OrdersScreen-CJzegrYb.cjs → OrdersScreen-BY3EQX7P.cjs} +11 -11
  83. package/dist/OrdersScreen-BY3EQX7P.cjs.map +1 -0
  84. package/dist/{OrdersScreen-FaoTq71a.mjs → OrdersScreen-D9ztzvIZ.mjs} +12 -12
  85. package/dist/OrdersScreen-D9ztzvIZ.mjs.map +1 -0
  86. package/dist/OrdersScreen-DCbX1m9M.cjs +50 -0
  87. package/dist/OrdersScreen-ze31P1y2.mjs +48 -0
  88. package/dist/{PointsWidget-BvGANrJq.mjs → PointsWidget-BJlOIK6o.mjs} +5 -5
  89. package/dist/{PointsWidget-BvGANrJq.mjs.map → PointsWidget-BJlOIK6o.mjs.map} +1 -1
  90. package/dist/{PointsWidget-Byd2-eEi.cjs → PointsWidget-Him7U2k2.cjs} +4 -4
  91. package/dist/{PointsWidget-Byd2-eEi.cjs.map → PointsWidget-Him7U2k2.cjs.map} +1 -1
  92. package/dist/{PortalTenantClientProvider-Bpm-CZq1.cjs → PortalTenantClientProvider-C0eJp8MN.cjs} +3 -3
  93. package/dist/PortalTenantClientProvider-C0eJp8MN.cjs.map +1 -0
  94. package/dist/{PortalTenantClientProvider-BmRtQAbi.mjs → PortalTenantClientProvider-DVClpfbi.mjs} +4 -4
  95. package/dist/PortalTenantClientProvider-DVClpfbi.mjs.map +1 -0
  96. package/dist/ProfileScreen-BDVGVcuz.cjs +53 -0
  97. package/dist/{ProfileScreen-CU4Y3sBq.mjs → ProfileScreen-BKAjNpTa.mjs} +8 -8
  98. package/dist/{ProfileScreen-CU4Y3sBq.mjs.map → ProfileScreen-BKAjNpTa.mjs.map} +1 -1
  99. package/dist/{ProfileScreen-DxjLSL0A.cjs → ProfileScreen-C8oqPt_i.cjs} +8 -8
  100. package/dist/{ProfileScreen-DxjLSL0A.cjs.map → ProfileScreen-C8oqPt_i.cjs.map} +1 -1
  101. package/dist/ProfileScreen-DYQklavp.mjs +51 -0
  102. package/dist/{QuickLinksWidget-D8LqZkUS.mjs → QuickLinksWidget-DJUI7r5_.mjs} +2 -2
  103. package/dist/{QuickLinksWidget-D8LqZkUS.mjs.map → QuickLinksWidget-DJUI7r5_.mjs.map} +1 -1
  104. package/dist/{QuickShareWidget-BJpqzZp0.mjs → QuickShareWidget-B7DhQOyt.mjs} +2 -2
  105. package/dist/{QuickShareWidget-BJpqzZp0.mjs.map → QuickShareWidget-B7DhQOyt.mjs.map} +1 -1
  106. package/dist/{RecentActivityWidget-ClgOlTXl.mjs → RecentActivityWidget-BMGeLSUa.mjs} +5 -5
  107. package/dist/{RecentActivityWidget-ClgOlTXl.mjs.map → RecentActivityWidget-BMGeLSUa.mjs.map} +1 -1
  108. package/dist/{RecentActivityWidget-BYzAzBI-.cjs → RecentActivityWidget-DvrmBGsD.cjs} +4 -4
  109. package/dist/{RecentActivityWidget-BYzAzBI-.cjs.map → RecentActivityWidget-DvrmBGsD.cjs.map} +1 -1
  110. package/dist/{ScreenHeaderContext-BiGgRqjY.cjs → ScreenHeaderContext-VOp8pVHr.cjs} +4 -4
  111. package/dist/ScreenHeaderContext-VOp8pVHr.cjs.map +1 -0
  112. package/dist/{ScreenHeaderContext-BjpQOCck.mjs → ScreenHeaderContext-cuVMk00X.mjs} +5 -5
  113. package/dist/ScreenHeaderContext-cuVMk00X.mjs.map +1 -0
  114. package/dist/{ScreenRenderer-TobkTBMC.mjs → ScreenRenderer-D3zNFSr3.mjs} +4 -4
  115. package/dist/ScreenRenderer-D3zNFSr3.mjs.map +1 -0
  116. package/dist/{ScreenRenderer-CLDJUinO.cjs → ScreenRenderer-IMkMMnUJ.cjs} +3 -3
  117. package/dist/ScreenRenderer-IMkMMnUJ.cjs.map +1 -0
  118. package/dist/{SearchSort-B5hq2j-l.cjs → SearchSort-BjjBpuEi.cjs} +2 -2
  119. package/dist/SearchSort-BjjBpuEi.cjs.map +1 -0
  120. package/dist/{SearchSort-CMUL0qt3.mjs → SearchSort-C6RV6d9g.mjs} +3 -3
  121. package/dist/SearchSort-C6RV6d9g.mjs.map +1 -0
  122. package/dist/{SeparatorWidget-CDvDjL-k.mjs → SeparatorWidget-CGFzy35l.mjs} +2 -2
  123. package/dist/{SeparatorWidget-CDvDjL-k.mjs.map → SeparatorWidget-CGFzy35l.mjs.map} +1 -1
  124. package/dist/{ShareablesScreen-COg_WpdP.cjs → ShareablesScreen-B0vxU7i6.cjs} +20 -20
  125. package/dist/ShareablesScreen-B0vxU7i6.cjs.map +1 -0
  126. package/dist/{ShareablesScreen-C9EmRZJW.mjs → ShareablesScreen-CefIC5H8.mjs} +22 -22
  127. package/dist/ShareablesScreen-CefIC5H8.mjs.map +1 -0
  128. package/dist/ShareablesScreen-Cs8iC1qk.mjs +15 -0
  129. package/dist/ShareablesScreen-igzq70Xz.cjs +17 -0
  130. package/dist/{ShopScreen-Bgx6548O.cjs → ShopScreen-BO1eOqyu.cjs} +10 -10
  131. package/dist/ShopScreen-BO1eOqyu.cjs.map +1 -0
  132. package/dist/ShopScreen-DY10zu6I.mjs +48 -0
  133. package/dist/{ShopScreen-DTL5xiPY.mjs → ShopScreen-IJalLKdV.mjs} +10 -10
  134. package/dist/ShopScreen-IJalLKdV.mjs.map +1 -0
  135. package/dist/ShopScreen-PMwvVuff.cjs +50 -0
  136. package/dist/{ShopWidget-Cmx4Pb6Q.cjs → ShopWidget-BWURiWyx.cjs} +4 -4
  137. package/dist/{ShopWidget-Cmx4Pb6Q.cjs.map → ShopWidget-BWURiWyx.cjs.map} +1 -1
  138. package/dist/{ShopWidget-C76SSZwW.cjs → ShopWidget-C4jeRRut.cjs} +4 -4
  139. package/dist/{ShopWidget-BzAuvfyC.mjs → ShopWidget-Cqnwq0AG.mjs} +5 -5
  140. package/dist/{ShopWidget-BzAuvfyC.mjs.map → ShopWidget-Cqnwq0AG.mjs.map} +1 -1
  141. package/dist/{SpacerWidget-B0l19UqF.mjs → SpacerWidget-BhxIaqYF.mjs} +2 -2
  142. package/dist/{SpacerWidget-B0l19UqF.mjs.map → SpacerWidget-BhxIaqYF.mjs.map} +1 -1
  143. package/dist/SubscriptionsScreen-BbUhL-zf.mjs +50 -0
  144. package/dist/{SubscriptionsScreen-D8bjSX3s.cjs → SubscriptionsScreen-CihWk-dF.cjs} +26 -26
  145. package/dist/SubscriptionsScreen-CihWk-dF.cjs.map +1 -0
  146. package/dist/{SubscriptionsScreen-DGE3efWd.cjs → SubscriptionsScreen-DBbYVWWt.cjs} +26 -26
  147. package/dist/{SubscriptionsScreen-qKJoibtI.mjs → SubscriptionsScreen-d527tZfc.mjs} +27 -27
  148. package/dist/SubscriptionsScreen-d527tZfc.mjs.map +1 -0
  149. package/dist/{TableWidget-CpHI9CGQ.mjs → TableWidget-Dwud4Fif.mjs} +2 -2
  150. package/dist/{TableWidget-CpHI9CGQ.mjs.map → TableWidget-Dwud4Fif.mjs.map} +1 -1
  151. package/dist/{TextWidget-Jy-Ktqje.mjs → TextWidget-B5ZtQMX-.mjs} +2 -2
  152. package/dist/{TextWidget-Jy-Ktqje.mjs.map → TextWidget-B5ZtQMX-.mjs.map} +1 -1
  153. package/dist/ToDoWidget-BJ2Ip97m.cjs +11 -0
  154. package/dist/{ToDoWidget-BaWksZpJ.mjs → ToDoWidget-Bnml3xLS.mjs} +6 -6
  155. package/dist/{ToDoWidget-BaWksZpJ.mjs.map → ToDoWidget-Bnml3xLS.mjs.map} +1 -1
  156. package/dist/{ToDoWidget-CZh5a5-z.cjs → ToDoWidget-C3Ob2TP2.cjs} +5 -5
  157. package/dist/{ToDoWidget-CZh5a5-z.cjs.map → ToDoWidget-C3Ob2TP2.cjs.map} +1 -1
  158. package/dist/{UpgradeScreen-VpfRqKdO.cjs → UpgradeScreen-B40dCeub.cjs} +3 -3
  159. package/dist/{UpgradeScreen-VpfRqKdO.cjs.map → UpgradeScreen-B40dCeub.cjs.map} +1 -1
  160. package/dist/{UpgradeScreen-C55rEuCN.cjs → UpgradeScreen-CbdTRnIj.cjs} +3 -3
  161. package/dist/{UpgradeScreen-x9TdC5aR.mjs → UpgradeScreen-GzkZOOR4.mjs} +4 -4
  162. package/dist/{UpgradeScreen-x9TdC5aR.mjs.map → UpgradeScreen-GzkZOOR4.mjs.map} +1 -1
  163. package/dist/{VideoWidget-DP-VHt2s.mjs → VideoWidget-Cm3UQ3-Z.mjs} +2 -2
  164. package/dist/{VideoWidget-DP-VHt2s.mjs.map → VideoWidget-Cm3UQ3-Z.mjs.map} +1 -1
  165. package/dist/{countries-api-context-G-NW4BoH.cjs → countries-api-context-16PZpF7O.cjs} +2 -2
  166. package/dist/countries-api-context-16PZpF7O.cjs.map +1 -0
  167. package/dist/{countries-api-context-DScC_39w.mjs → countries-api-context-Donus2X5.mjs} +3 -3
  168. package/dist/countries-api-context-Donus2X5.mjs.map +1 -0
  169. package/dist/{error-state-DvzIn9Tz.mjs → error-state-BUe589mD.mjs} +3 -3
  170. package/dist/error-state-BUe589mD.mjs.map +1 -0
  171. package/dist/{error-state-DJq7C-23.cjs → error-state-CU87JUpz.cjs} +2 -2
  172. package/dist/error-state-CU87JUpz.cjs.map +1 -0
  173. package/dist/{es-jA6aVeLD.mjs → es-C19weaa-.mjs} +2 -2
  174. package/dist/{es-jA6aVeLD.mjs.map → es-C19weaa-.mjs.map} +1 -1
  175. package/dist/index.cjs +65 -65
  176. package/dist/index.cjs.map +1 -1
  177. package/dist/index.mjs +83 -83
  178. package/dist/index.mjs.map +1 -1
  179. package/dist/{mysite-api-context-kUTM3GNG.mjs → mysite-api-context-CoLr9vIf.mjs} +3 -3
  180. package/dist/mysite-api-context-CoLr9vIf.mjs.map +1 -0
  181. package/dist/{mysite-api-context-CilZcDS4.cjs → mysite-api-context-DtEXblIV.cjs} +2 -2
  182. package/dist/mysite-api-context-DtEXblIV.cjs.map +1 -0
  183. package/dist/{preview-context-D9ZzEfWh.mjs → preview-context-BXEGWx9T.mjs} +3 -3
  184. package/dist/preview-context-BXEGWx9T.mjs.map +1 -0
  185. package/dist/{preview-context-BWCl-xyj.cjs → preview-context-DrXkIImI.cjs} +2 -2
  186. package/dist/preview-context-DrXkIImI.cjs.map +1 -0
  187. package/dist/{registry-context-BKvTiuXB.mjs → registry-context-CcoVxCii.mjs} +3 -3
  188. package/dist/{registry-context-BKvTiuXB.mjs.map → registry-context-CcoVxCii.mjs.map} +1 -1
  189. package/dist/{registry-context-DJ5xiVnt.cjs → registry-context-Q_1Iq2Ea.cjs} +2 -2
  190. package/dist/{registry-context-DJ5xiVnt.cjs.map → registry-context-Q_1Iq2Ea.cjs.map} +1 -1
  191. package/dist/{static-dict-adapter-JAau5LHb.cjs → static-dict-adapter-BLq4QzCI.cjs} +2 -2
  192. package/dist/static-dict-adapter-BLq4QzCI.cjs.map +1 -0
  193. package/dist/{static-dict-adapter-DjCpubZc.mjs → static-dict-adapter-DRBq3ndO.mjs} +3 -3
  194. package/dist/static-dict-adapter-DRBq3ndO.mjs.map +1 -0
  195. package/dist/{store-api-context-DViwxyG4.mjs → store-api-context-B_vtKkXO.mjs} +3 -3
  196. package/dist/store-api-context-B_vtKkXO.mjs.map +1 -0
  197. package/dist/{store-api-context-D1gZn22Z.cjs → store-api-context-CGH3YsZB.cjs} +2 -2
  198. package/dist/store-api-context-CGH3YsZB.cjs.map +1 -0
  199. package/dist/{task-composer-form-D_Pbl6qk.mjs → task-composer-form-CZgRqbIc.mjs} +4 -4
  200. package/dist/task-composer-form-CZgRqbIc.mjs.map +1 -0
  201. package/dist/{task-composer-form-BEZGTBBZ.cjs → task-composer-form-Cz8hpMTT.cjs} +3 -3
  202. package/dist/task-composer-form-Cz8hpMTT.cjs.map +1 -0
  203. package/dist/{translation-api-context-factory-BSRK6Z50.cjs → translation-api-context-factory-CjRDqIhF.cjs} +2 -2
  204. package/dist/translation-api-context-factory-CjRDqIhF.cjs.map +1 -0
  205. package/dist/{translation-api-context-factory-CJrVq_EB.mjs → translation-api-context-factory-DFr9yJ6Q.mjs} +3 -3
  206. package/dist/translation-api-context-factory-DFr9yJ6Q.mjs.map +1 -0
  207. package/dist/{use-account-CQ-mhA3W.cjs → use-account-DltRHEwC.cjs} +2 -2
  208. package/dist/{use-account-CQ-mhA3W.cjs.map → use-account-DltRHEwC.cjs.map} +1 -1
  209. package/dist/{use-account-CwGoFpwg.mjs → use-account-Dm6Svko1.mjs} +2 -2
  210. package/dist/{use-account-CwGoFpwg.mjs.map → use-account-Dm6Svko1.mjs.map} +1 -1
  211. package/dist/{use-mysite-portal-ChDJ4z34.mjs → use-mysite-portal-3Tn3bFoE.mjs} +2 -2
  212. package/dist/{use-mysite-portal-ChDJ4z34.mjs.map → use-mysite-portal-3Tn3bFoE.mjs.map} +1 -1
  213. package/dist/{use-mysite-portal-CH9ZQROw.cjs → use-mysite-portal-D3X51_Ax.cjs} +2 -2
  214. package/dist/{use-mysite-portal-CH9ZQROw.cjs.map → use-mysite-portal-D3X51_Ax.cjs.map} +1 -1
  215. package/dist/{use-navigation-parent-DvHbbMB0.mjs → use-navigation-parent-GjQMvU4i.mjs} +2 -2
  216. package/dist/{use-navigation-parent-DvHbbMB0.mjs.map → use-navigation-parent-GjQMvU4i.mjs.map} +1 -1
  217. package/dist/{use-navigation-parent-DQ8CiN9L.cjs → use-navigation-parent-q-la1wD2.cjs} +2 -2
  218. package/dist/{use-navigation-parent-DQ8CiN9L.cjs.map → use-navigation-parent-q-la1wD2.cjs.map} +1 -1
  219. package/dist/{use-store-cwcCLxl_.mjs → use-store-C6KYHvRw.mjs} +2 -2
  220. package/dist/{use-store-cwcCLxl_.mjs.map → use-store-C6KYHvRw.mjs.map} +1 -1
  221. package/dist/{use-store-lOOUcpRT.cjs → use-store-j_6I4DU4.cjs} +2 -2
  222. package/dist/{use-store-lOOUcpRT.cjs.map → use-store-j_6I4DU4.cjs.map} +1 -1
  223. package/package.json +18 -18
  224. package/dist/AddressAutocompleteInput-BANMgwzR.mjs.map +0 -1
  225. package/dist/AddressAutocompleteInput-JBXGT1iL.cjs.map +0 -1
  226. package/dist/AppNavigationContext-BCj6iFxr.mjs.map +0 -1
  227. package/dist/AppNavigationContext-CoNtdUrr.cjs.map +0 -1
  228. package/dist/ContainerWidget-CyDVJu83.cjs +0 -8
  229. package/dist/FluidProvider-BVJmIM13.cjs.map +0 -1
  230. package/dist/FluidProvider-CYBXFElI.mjs.map +0 -1
  231. package/dist/MessagingScreen-3o_Qb6u_.mjs.map +0 -1
  232. package/dist/MessagingScreen-C4SUSPy3.cjs.map +0 -1
  233. package/dist/MessagingScreen-D-wunIkf.mjs +0 -50
  234. package/dist/MySiteScreen-DiCU6P9d.cjs +0 -11
  235. package/dist/OrdersScreen-BL5Ta3Tt.cjs +0 -50
  236. package/dist/OrdersScreen-B_JxQTW8.mjs +0 -48
  237. package/dist/OrdersScreen-CJzegrYb.cjs.map +0 -1
  238. package/dist/OrdersScreen-FaoTq71a.mjs.map +0 -1
  239. package/dist/PortalTenantClientProvider-BmRtQAbi.mjs.map +0 -1
  240. package/dist/PortalTenantClientProvider-Bpm-CZq1.cjs.map +0 -1
  241. package/dist/ProfileScreen-DMDKlk20.cjs +0 -53
  242. package/dist/ProfileScreen-Dcjp4wNO.mjs +0 -51
  243. package/dist/ScreenHeaderContext-BiGgRqjY.cjs.map +0 -1
  244. package/dist/ScreenHeaderContext-BjpQOCck.mjs.map +0 -1
  245. package/dist/ScreenRenderer-CLDJUinO.cjs.map +0 -1
  246. package/dist/ScreenRenderer-TobkTBMC.mjs.map +0 -1
  247. package/dist/SearchSort-B5hq2j-l.cjs.map +0 -1
  248. package/dist/SearchSort-CMUL0qt3.mjs.map +0 -1
  249. package/dist/ShareablesScreen-BQd1-Og8.mjs +0 -15
  250. package/dist/ShareablesScreen-C9EmRZJW.mjs.map +0 -1
  251. package/dist/ShareablesScreen-COg_WpdP.cjs.map +0 -1
  252. package/dist/ShareablesScreen-CoFM63kh.cjs +0 -17
  253. package/dist/ShopScreen-Bgx6548O.cjs.map +0 -1
  254. package/dist/ShopScreen-Bm2RlZas.cjs +0 -50
  255. package/dist/ShopScreen-Cz7aMt4z.mjs +0 -48
  256. package/dist/ShopScreen-DTL5xiPY.mjs.map +0 -1
  257. package/dist/SubscriptionsScreen-CF6AuW3I.mjs +0 -50
  258. package/dist/SubscriptionsScreen-D8bjSX3s.cjs.map +0 -1
  259. package/dist/SubscriptionsScreen-qKJoibtI.mjs.map +0 -1
  260. package/dist/ToDoWidget-1wI1ntdD.cjs +0 -11
  261. package/dist/countries-api-context-DScC_39w.mjs.map +0 -1
  262. package/dist/countries-api-context-G-NW4BoH.cjs.map +0 -1
  263. package/dist/error-state-DJq7C-23.cjs.map +0 -1
  264. package/dist/error-state-DvzIn9Tz.mjs.map +0 -1
  265. package/dist/mysite-api-context-CilZcDS4.cjs.map +0 -1
  266. package/dist/mysite-api-context-kUTM3GNG.mjs.map +0 -1
  267. package/dist/preview-context-BWCl-xyj.cjs.map +0 -1
  268. package/dist/preview-context-D9ZzEfWh.mjs.map +0 -1
  269. package/dist/static-dict-adapter-DjCpubZc.mjs.map +0 -1
  270. package/dist/static-dict-adapter-JAau5LHb.cjs.map +0 -1
  271. package/dist/store-api-context-D1gZn22Z.cjs.map +0 -1
  272. package/dist/store-api-context-DViwxyG4.mjs.map +0 -1
  273. package/dist/task-composer-form-BEZGTBBZ.cjs.map +0 -1
  274. package/dist/task-composer-form-D_Pbl6qk.mjs.map +0 -1
  275. package/dist/translation-api-context-factory-BSRK6Z50.cjs.map +0 -1
  276. package/dist/translation-api-context-factory-CJrVq_EB.mjs.map +0 -1
@@ -1,6 +1,6 @@
1
- import { t as createTranslationContext } from "./translation-api-context-factory-CJrVq_EB.mjs";
1
+ import { t as createTranslationContext } from "./translation-api-context-factory-DFr9yJ6Q.mjs";
2
2
  import { f as fluidToast, kn as cn } from "./src-CJw6JbdS.mjs";
3
- import { createContext, useContext, useEffect, useMemo, useRef, useState } from "react";
3
+ import { createContext, use, useEffect, useMemo, useRef, useState } from "react";
4
4
  import { useInfiniteQuery, useMutation, useQueryClient } from "@tanstack/react-query";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
  import { X } from "lucide-react";
@@ -93,7 +93,7 @@ function parseApiErrors(error, fallback) {
93
93
  const ContactsApiContext = createContext(null);
94
94
  const ContactsApiProvider = ContactsApiContext.Provider;
95
95
  function useContactsDomainApi() {
96
- const api = useContext(ContactsApiContext);
96
+ const api = use(ContactsApiContext);
97
97
  if (!api) throw new Error("useContactsDomainApi must be used within a ContactsApiProvider");
98
98
  return api;
99
99
  }
@@ -357,4 +357,4 @@ function TaskComposerForm({ contactId, onDone, onClose, autoFocus = true }) {
357
357
  //#endregion
358
358
  export { contactsKeys as a, useContactsTranslation as c, useGroupsApi as d, useNotesApi as f, CONTACTS_QUERY_KEYS as i, ContactsApiProvider as l, parseApiErrors as m, startOfLocalDay as n, parseTaskBody as o, useTasksApi as p, useInfiniteContacts as r, ContactsTranslationProvider as s, TaskComposerForm as t, useContactsCrud as u };
359
359
 
360
- //# sourceMappingURL=task-composer-form-D_Pbl6qk.mjs.map
360
+ //# sourceMappingURL=task-composer-form-CZgRqbIc.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-composer-form-CZgRqbIc.mjs","names":[],"sources":["../../../platform/api-client-core/src/parse-api-errors.ts","../../../contacts/core/src/contacts-api-context.ts","../../../contacts/core/src/translation-api-context.ts","../../../contacts/core/src/parse-task-body.ts","../../../contacts/core/src/query-keys.ts","../../../contacts/core/src/hooks/use-infinite-contacts.ts","../../../contacts/core/src/iso-date.ts","../../../contacts/ui/src/portal/hooks/contacts/use-create-contact-task.ts","../../../contacts/ui/src/portal/components/tasks/task-composer-form.tsx"],"sourcesContent":["/**\n * Framework-agnostic API error parsing utilities.\n *\n * Extracts structured field-level errors from API responses and formats\n * them into human-readable messages. Works with ApiError from this package\n * as well as any error object that has `status`, `data`, and optional `message`.\n */\n\n/**\n * Converts snake_case or camelCase field names to Title Case\n */\nexport function formatFieldName(field: string): string {\n return field\n .replace(/_/g, \" \")\n .replace(/([A-Z])/g, \" $1\")\n .split(\" \")\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(\" \")\n .trim();\n}\n\nexport interface ParsedFieldError {\n field: string;\n messages: string[];\n}\n\n/**\n * Type guard to check if an error looks like an API error\n */\nexport function isApiLikeError(\n error: unknown,\n): error is { message: string | undefined; status: number; data: unknown } {\n if (!error || typeof error !== \"object\") {\n return false;\n }\n\n const err = error as Record<string, unknown>;\n return (\n typeof err.status === \"number\" &&\n \"data\" in err &&\n (typeof err.message === \"string\" || err.message === undefined)\n );\n}\n\n/**\n * Extracts field-level errors from API error data\n */\nexport function extractFieldErrors(data: unknown): ParsedFieldError[] {\n const errors: ParsedFieldError[] = [];\n\n if (!data || typeof data !== \"object\") {\n return errors;\n }\n\n const errorObj = data as Record<string, unknown>;\n\n for (const [key, value] of Object.entries(errorObj)) {\n if (Array.isArray(value) && value.length > 0) {\n errors.push({\n field: formatFieldName(key),\n messages: value.map((item) =>\n typeof item === \"string\" ? item : JSON.stringify(item),\n ),\n });\n } else if (typeof value === \"string\" && value.length > 0) {\n errors.push({\n field: formatFieldName(key),\n messages: [value],\n });\n } else if (value && typeof value === \"object\" && !Array.isArray(value)) {\n const nestedErrors = extractFieldErrors(value);\n nestedErrors.forEach((nestedError) => {\n errors.push({\n field: `${formatFieldName(key)} → ${nestedError.field}`,\n messages: nestedError.messages,\n });\n });\n }\n }\n\n return errors;\n}\n\n/**\n * Formats field errors into a readable description string\n */\nexport function formatErrorDescription(errors: ParsedFieldError[]): string {\n if (errors.length === 0) {\n return \"\";\n }\n\n if (errors.length === 1) {\n const err = errors[0];\n if (!err) return \"\";\n const message = err.messages[0] || \"is invalid\";\n return `${err.field} ${message}`;\n }\n\n if (errors.length <= 3) {\n return errors\n .map((e) => `${e.field} ${e.messages[0] || \"is invalid\"}`)\n .join(\"\\n\");\n }\n\n const shown = errors\n .slice(0, 3)\n .map((e) => `${e.field} ${e.messages[0] || \"is invalid\"}`)\n .join(\"\\n\");\n const remaining = errors.length - 3;\n return `${shown}\\n...and ${remaining} more ${remaining === 1 ? \"error\" : \"errors\"}`;\n}\n\n/**\n * Parses an error and returns a human-readable description string.\n *\n * Handles:\n * - API-like errors with structured field-level data\n * - API-like errors with a top-level message\n * - Standard Error instances\n * - Falls back to the provided fallback string\n *\n * @param error - The error to parse (ApiError, Error, or unknown)\n * @param fallback - Optional fallback description if error cannot be parsed\n * @returns A human-readable error description, or undefined if nothing could be extracted\n */\nexport function parseApiErrors(\n error: unknown,\n fallback?: string,\n): string | undefined {\n if (isApiLikeError(error)) {\n if (error.data) {\n const fieldErrors = extractFieldErrors(error.data);\n if (fieldErrors.length > 0) {\n return formatErrorDescription(fieldErrors);\n }\n }\n\n if (error.message) {\n return error.message;\n }\n } else if (error instanceof Error) {\n return error.message;\n }\n\n return fallback;\n}\n","import { createContext, use } from \"react\";\nimport type { ContactsApi } from \"./contacts-api\";\nimport type { NotesApi } from \"./notes-api\";\nimport type { TasksApi } from \"./tasks-api\";\nimport type { GroupsApi } from \"./groups-api\";\n\nexport interface ContactsDomainApi {\n contacts: ContactsApi;\n notes: NotesApi;\n tasks: TasksApi;\n groups?: GroupsApi;\n}\n\nconst ContactsApiContext = createContext<ContactsDomainApi | null>(null);\n\nexport const ContactsApiProvider = ContactsApiContext.Provider;\n\nexport function useContactsDomainApi(): ContactsDomainApi {\n const api = use(ContactsApiContext);\n if (!api) {\n throw new Error(\n \"useContactsDomainApi must be used within a ContactsApiProvider\",\n );\n }\n return api;\n}\n\nexport function useContactsCrud(): ContactsApi {\n return useContactsDomainApi().contacts;\n}\n\nexport function useNotesApi(): NotesApi {\n return useContactsDomainApi().notes;\n}\n\nexport function useTasksApi(): TasksApi {\n return useContactsDomainApi().tasks;\n}\n\n/** Returns GroupsApi if the provider supplies one, otherwise null. */\nexport function useGroupsApi(): GroupsApi | null {\n return useContactsDomainApi().groups ?? null;\n}\n","import type { Provider } from \"react\";\nimport { createTranslationContext } from \"@fluid-app/i18n/translation-api-context-factory\";\nimport type { TranslationApi } from \"@fluid-app/i18n/translation-api\";\nimport type { ContactsDict } from \"./translation-dictionary\";\n\nconst { Provider: ContactsProvider, useTranslation } =\n createTranslationContext<ContactsDict>(\"Contacts\");\n\nexport const ContactsTranslationProvider: Provider<TranslationApi<ContactsDict> | null> =\n ContactsProvider;\nexport const useContactsTranslation = useTranslation;\n","/**\n * Tasks store both a title (first line) and an optional body separated by a\n * blank line. This is the canonical convention used by the task editor and\n * by every consumer that displays tasks (contacts UI list, portal todo\n * widget). Drift across consumers would mean some surfaces show \"Title\\n\\n\n * body details\" verbatim while others split correctly — keep the delimiter\n * convention in one place.\n */\nexport function parseTaskBody(raw: string): { title: string; body: string } {\n const split = raw.indexOf(\"\\n\\n\");\n if (split >= 0) {\n return {\n title: raw.slice(0, split),\n body: raw.slice(split + 2),\n };\n }\n return { title: raw, body: \"\" };\n}\n","export const CONTACTS_QUERY_KEYS = {\n all: (prefix: string) => [prefix] as const,\n list: (prefix: string) =>\n [...CONTACTS_QUERY_KEYS.all(prefix), \"list\"] as const,\n detail: (prefix: string, id: string) =>\n [...CONTACTS_QUERY_KEYS.all(prefix), \"detail\", id] as const,\n} as const;\n\nexport const contactsKeys = {\n activities: (contactId: string) =>\n [\"portal-contacts\", \"activities\", contactId] as const,\n tasks: (contactId: string) =>\n [\"portal-contacts\", \"tasks\", contactId] as const,\n notes: (contactId: string) =>\n [\"portal-contacts\", \"notes\", contactId] as const,\n orders: (contactId: string) => [\"rep-contacts\", \"orders\", contactId] as const,\n subscriptionOrders: (contactId: string) =>\n [\"rep-contacts\", \"subscription-orders\", contactId] as const,\n groups: () => [\"portal-contacts\", \"groups\"] as const,\n} as const;\n","import { useInfiniteQuery } from \"@tanstack/react-query\";\nimport { useContactsCrud } from \"../contacts-api-context\";\nimport { CONTACTS_QUERY_KEYS } from \"../query-keys\";\n\nexport interface UseInfiniteContactsParams {\n search_query?: string;\n status?: string;\n sort_by?: string;\n sort_direction?: string;\n per_page?: number;\n tags?: string[];\n}\n\nexport function useInfiniteContacts(params: UseInfiniteContactsParams) {\n const api = useContactsCrud();\n return useInfiniteQuery({\n queryKey: [...CONTACTS_QUERY_KEYS.list(\"contacts\"), params],\n queryFn: ({ pageParam }) =>\n api.listContacts({\n ...params,\n page: pageParam,\n }),\n getNextPageParam: (lastPage) => {\n const currentPage = lastPage.meta.current_page;\n // Contacts API is page-number based; next_cursor and total_pages\n // are both used as \"has-next-page\" signals.\n if (currentPage == null) return undefined;\n if (lastPage.meta.next_cursor) return currentPage + 1;\n if (\n lastPage.meta.total_pages != null &&\n currentPage < lastPage.meta.total_pages\n ) {\n return currentPage + 1;\n }\n return undefined;\n },\n initialPageParam: 1,\n });\n}\n","/**\n * Format a date as YYYY-MM-DD in the renderer's local timezone, optionally\n * shifted by `offsetDays`. Use this for due-date inputs where \"today\" must\n * resolve to the user's local calendar date — never `toISOString().slice(0, 10)`,\n * which returns the UTC date and silently rolls over a day for users east/west\n * of UTC at the wrong hours.\n */\nexport function isoDate(offsetDays: number, now: Date = new Date()): string {\n const d = new Date(now);\n d.setDate(d.getDate() + offsetDays);\n const yyyy = d.getFullYear();\n const mm = String(d.getMonth() + 1).padStart(2, \"0\");\n const dd = String(d.getDate()).padStart(2, \"0\");\n return `${yyyy}-${mm}-${dd}`;\n}\n\n/**\n * Return a Date pinned to local midnight of the calendar day represented by\n * `input`. Use this whenever you need to compare two due dates by calendar day\n * (overdue / today / tomorrow / future).\n *\n * Why not just `new Date(input)`? `new Date(\"YYYY-MM-DD\")` parses the string\n * as UTC midnight, which lands on the *previous* calendar day in any UTC−\n * timezone — a task due \"May 1\" then reads as April 30 for a user in the\n * Americas, classifying it as \"Overdue\" all day. Parse the date components\n * directly so the calendar-day intent of the string is preserved.\n */\nexport function startOfLocalDay(input: Date | string): Date {\n if (typeof input === \"string\") {\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})/.exec(input);\n if (match?.[1] && match[2] && match[3]) {\n return new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]));\n }\n }\n const d = typeof input === \"string\" ? new Date(input) : input;\n return new Date(d.getFullYear(), d.getMonth(), d.getDate());\n}\n","\"use client\";\n\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { fluidToast } from \"@fluid-app/ui-primitives\";\nimport { parseApiErrors } from \"@fluid-app/api-client-core\";\nimport { useTasksApi } from \"@fluid-app/contacts-core/contacts-api-context\";\nimport { contactsKeys } from \"@fluid-app/contacts-core/query-keys\";\nimport type { CreateTaskInput } from \"@fluid-app/contacts-core/types\";\n\nexport type { CreateTaskInput };\n\nexport function useCreateContactTask(\n contactId: string,\n options?: { onSuccess?: () => void },\n) {\n const queryClient = useQueryClient();\n const api = useTasksApi();\n\n return useMutation({\n mutationFn: (input: CreateTaskInput) => api.createTask(contactId, input),\n onSuccess: () => {\n fluidToast({ title: \"Task created\", type: \"success\" });\n queryClient.invalidateQueries({\n queryKey: contactsKeys.tasks(contactId),\n });\n options?.onSuccess?.();\n },\n onError: (error) => {\n const description = parseApiErrors(error);\n fluidToast({\n title: \"Failed to create task\",\n type: \"error\",\n ...(description ? { description } : {}),\n });\n },\n });\n}\n","\"use client\";\n\nimport React, { useEffect, useMemo, useRef, useState } from \"react\";\nimport { X } from \"lucide-react\";\nimport { cn } from \"@fluid-app/ui-primitives\";\nimport { useContactsTranslation } from \"@fluid-app/contacts-core/translation-api-context\";\nimport { isoDate } from \"@fluid-app/contacts-core/iso-date\";\nimport { useCreateContactTask } from \"../../hooks/contacts/use-create-contact-task\";\n\nconst QUICK_DATES = [\n { key: \"today\", offsetDays: 0 },\n { key: \"tomorrow\", offsetDays: 1 },\n { key: \"next_week\", offsetDays: 7 },\n] as const;\n\nexport interface TaskComposerFormProps {\n contactId: string;\n /** Called after a successful create. */\n onDone?: () => void;\n /** When provided, an X button is rendered and Escape closes the form. */\n onClose?: () => void;\n /** Focus the body input on mount. Default true. */\n autoFocus?: boolean;\n}\n\nexport function TaskComposerForm({\n contactId,\n onDone,\n onClose,\n autoFocus = true,\n}: TaskComposerFormProps): React.JSX.Element {\n const { t } = useContactsTranslation();\n const [body, setBody] = useState(\"\");\n const [dueDate, setDueDate] = useState<string | null>(null);\n const inputRef = useRef<HTMLInputElement | null>(null);\n\n const createTask = useCreateContactTask(contactId, {\n onSuccess: () => {\n setBody(\"\");\n setDueDate(null);\n onDone?.();\n },\n });\n\n useEffect(() => {\n if (autoFocus) inputRef.current?.focus();\n }, [autoFocus]);\n\n // Compute the date strings once per mount. Re-running on every render would\n // re-evaluate `new Date()` and could flip \"today\" if the form happens to be\n // open across midnight — acceptable but unnecessary churn.\n const quickDates = useMemo(\n () =>\n QUICK_DATES.map((q) => ({\n key: q.key,\n iso: isoDate(q.offsetDays),\n })),\n [],\n );\n\n const quickLabels: Record<(typeof QUICK_DATES)[number][\"key\"], string> = {\n today: t(\"quick_today\"),\n tomorrow: t(\"quick_tomorrow\"),\n next_week: t(\"quick_next_week\"),\n };\n\n const submit = () => {\n const trimmed = body.trim();\n if (!trimmed || createTask.isPending) return;\n createTask.mutate({ body: trimmed, due_at: dueDate });\n };\n\n const canSubmit = body.trim().length > 0 && !createTask.isPending;\n\n return (\n <div className=\"border-border/50 focus-within:border-foreground/30 rounded-xl border p-3 transition-colors\">\n <div className=\"flex items-start gap-3\">\n <div\n className=\"border-muted-foreground/50 mt-1.5 size-5 shrink-0 rounded-full border-2\"\n aria-hidden=\"true\"\n />\n <input\n ref={inputRef}\n value={body}\n onChange={(e) => setBody(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n submit();\n } else if (e.key === \"Escape\" && onClose) {\n e.preventDefault();\n onClose();\n }\n }}\n placeholder={t(\"task_describe_placeholder\")}\n aria-label={t(\"task_description_aria\")}\n className=\"placeholder:text-muted-foreground/80 text-foreground flex-1 border-0 bg-transparent text-sm font-medium outline-none pointer-coarse:text-base\"\n />\n {onClose && (\n <button\n type=\"button\"\n onClick={onClose}\n aria-label={t(\"task_discard_aria\")}\n className=\"text-muted-foreground hover:bg-muted hover:text-foreground -mt-0.5 -mr-0.5 flex size-7 shrink-0 items-center justify-center rounded-md transition-colors\"\n >\n <X className=\"size-4\" />\n </button>\n )}\n </div>\n\n {/* --row-indent aligns the chip row with the input text above (size-5\n circle + gap-3 = 2rem). The mobile Add button cancels it via\n -ml-(--row-indent) + w-[calc(100%+var(--row-indent))] so the two\n values stay locked to a single source. */}\n <div className=\"mt-3 flex flex-wrap items-center gap-1.5 pl-(--row-indent) [--row-indent:2rem]\">\n {quickDates.map((q) => {\n const isActive = dueDate === q.iso;\n return (\n <button\n key={q.key}\n type=\"button\"\n onClick={() => setDueDate(isActive ? null : q.iso)}\n aria-pressed={isActive}\n className={cn(\n \"shrink-0 rounded-full px-3 py-1 text-xs font-medium transition-colors\",\n isActive\n ? \"bg-primary text-primary-foreground\"\n : \"bg-muted text-muted-foreground hover:bg-muted/70\",\n )}\n >\n {quickLabels[q.key]}\n </button>\n );\n })}\n <button\n type=\"button\"\n onClick={submit}\n disabled={!canSubmit}\n className=\"bg-primary text-primary-foreground hover:bg-primary/90 disabled:bg-muted disabled:text-muted-foreground mt-1 -ml-(--row-indent) inline-flex h-10 w-[calc(100%+var(--row-indent))] items-center justify-center rounded-lg text-sm font-semibold transition-colors disabled:cursor-not-allowed sm:mt-0 sm:ml-auto sm:h-auto sm:w-auto sm:shrink-0 sm:gap-1.5 sm:rounded-full sm:px-4 sm:py-1.5 sm:text-xs\"\n >\n {createTask.isPending ? t(\"adding\") : t(\"add_task\")}\n </button>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-9hOWP6kD.cjs");
2
- const require_translation_api_context_factory = require("./translation-api-context-factory-BSRK6Z50.cjs");
2
+ const require_translation_api_context_factory = require("./translation-api-context-factory-CjRDqIhF.cjs");
3
3
  const require_src = require("./src-Cx7UyT_c.cjs");
4
4
  let react = require("react");
5
5
  react = require_chunk.__toESM(react);
@@ -95,7 +95,7 @@ function parseApiErrors(error, fallback) {
95
95
  const ContactsApiContext = (0, react.createContext)(null);
96
96
  const ContactsApiProvider = ContactsApiContext.Provider;
97
97
  function useContactsDomainApi() {
98
- const api = (0, react.useContext)(ContactsApiContext);
98
+ const api = (0, react.use)(ContactsApiContext);
99
99
  if (!api) throw new Error("useContactsDomainApi must be used within a ContactsApiProvider");
100
100
  return api;
101
101
  }
@@ -442,4 +442,4 @@ Object.defineProperty(exports, "useTasksApi", {
442
442
  }
443
443
  });
444
444
 
445
- //# sourceMappingURL=task-composer-form-BEZGTBBZ.cjs.map
445
+ //# sourceMappingURL=task-composer-form-Cz8hpMTT.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-composer-form-Cz8hpMTT.cjs","names":["createTranslationContext","X","cn"],"sources":["../../../platform/api-client-core/src/parse-api-errors.ts","../../../contacts/core/src/contacts-api-context.ts","../../../contacts/core/src/translation-api-context.ts","../../../contacts/core/src/parse-task-body.ts","../../../contacts/core/src/query-keys.ts","../../../contacts/core/src/hooks/use-infinite-contacts.ts","../../../contacts/core/src/iso-date.ts","../../../contacts/ui/src/portal/hooks/contacts/use-create-contact-task.ts","../../../contacts/ui/src/portal/components/tasks/task-composer-form.tsx"],"sourcesContent":["/**\n * Framework-agnostic API error parsing utilities.\n *\n * Extracts structured field-level errors from API responses and formats\n * them into human-readable messages. Works with ApiError from this package\n * as well as any error object that has `status`, `data`, and optional `message`.\n */\n\n/**\n * Converts snake_case or camelCase field names to Title Case\n */\nexport function formatFieldName(field: string): string {\n return field\n .replace(/_/g, \" \")\n .replace(/([A-Z])/g, \" $1\")\n .split(\" \")\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(\" \")\n .trim();\n}\n\nexport interface ParsedFieldError {\n field: string;\n messages: string[];\n}\n\n/**\n * Type guard to check if an error looks like an API error\n */\nexport function isApiLikeError(\n error: unknown,\n): error is { message: string | undefined; status: number; data: unknown } {\n if (!error || typeof error !== \"object\") {\n return false;\n }\n\n const err = error as Record<string, unknown>;\n return (\n typeof err.status === \"number\" &&\n \"data\" in err &&\n (typeof err.message === \"string\" || err.message === undefined)\n );\n}\n\n/**\n * Extracts field-level errors from API error data\n */\nexport function extractFieldErrors(data: unknown): ParsedFieldError[] {\n const errors: ParsedFieldError[] = [];\n\n if (!data || typeof data !== \"object\") {\n return errors;\n }\n\n const errorObj = data as Record<string, unknown>;\n\n for (const [key, value] of Object.entries(errorObj)) {\n if (Array.isArray(value) && value.length > 0) {\n errors.push({\n field: formatFieldName(key),\n messages: value.map((item) =>\n typeof item === \"string\" ? item : JSON.stringify(item),\n ),\n });\n } else if (typeof value === \"string\" && value.length > 0) {\n errors.push({\n field: formatFieldName(key),\n messages: [value],\n });\n } else if (value && typeof value === \"object\" && !Array.isArray(value)) {\n const nestedErrors = extractFieldErrors(value);\n nestedErrors.forEach((nestedError) => {\n errors.push({\n field: `${formatFieldName(key)} → ${nestedError.field}`,\n messages: nestedError.messages,\n });\n });\n }\n }\n\n return errors;\n}\n\n/**\n * Formats field errors into a readable description string\n */\nexport function formatErrorDescription(errors: ParsedFieldError[]): string {\n if (errors.length === 0) {\n return \"\";\n }\n\n if (errors.length === 1) {\n const err = errors[0];\n if (!err) return \"\";\n const message = err.messages[0] || \"is invalid\";\n return `${err.field} ${message}`;\n }\n\n if (errors.length <= 3) {\n return errors\n .map((e) => `${e.field} ${e.messages[0] || \"is invalid\"}`)\n .join(\"\\n\");\n }\n\n const shown = errors\n .slice(0, 3)\n .map((e) => `${e.field} ${e.messages[0] || \"is invalid\"}`)\n .join(\"\\n\");\n const remaining = errors.length - 3;\n return `${shown}\\n...and ${remaining} more ${remaining === 1 ? \"error\" : \"errors\"}`;\n}\n\n/**\n * Parses an error and returns a human-readable description string.\n *\n * Handles:\n * - API-like errors with structured field-level data\n * - API-like errors with a top-level message\n * - Standard Error instances\n * - Falls back to the provided fallback string\n *\n * @param error - The error to parse (ApiError, Error, or unknown)\n * @param fallback - Optional fallback description if error cannot be parsed\n * @returns A human-readable error description, or undefined if nothing could be extracted\n */\nexport function parseApiErrors(\n error: unknown,\n fallback?: string,\n): string | undefined {\n if (isApiLikeError(error)) {\n if (error.data) {\n const fieldErrors = extractFieldErrors(error.data);\n if (fieldErrors.length > 0) {\n return formatErrorDescription(fieldErrors);\n }\n }\n\n if (error.message) {\n return error.message;\n }\n } else if (error instanceof Error) {\n return error.message;\n }\n\n return fallback;\n}\n","import { createContext, use } from \"react\";\nimport type { ContactsApi } from \"./contacts-api\";\nimport type { NotesApi } from \"./notes-api\";\nimport type { TasksApi } from \"./tasks-api\";\nimport type { GroupsApi } from \"./groups-api\";\n\nexport interface ContactsDomainApi {\n contacts: ContactsApi;\n notes: NotesApi;\n tasks: TasksApi;\n groups?: GroupsApi;\n}\n\nconst ContactsApiContext = createContext<ContactsDomainApi | null>(null);\n\nexport const ContactsApiProvider = ContactsApiContext.Provider;\n\nexport function useContactsDomainApi(): ContactsDomainApi {\n const api = use(ContactsApiContext);\n if (!api) {\n throw new Error(\n \"useContactsDomainApi must be used within a ContactsApiProvider\",\n );\n }\n return api;\n}\n\nexport function useContactsCrud(): ContactsApi {\n return useContactsDomainApi().contacts;\n}\n\nexport function useNotesApi(): NotesApi {\n return useContactsDomainApi().notes;\n}\n\nexport function useTasksApi(): TasksApi {\n return useContactsDomainApi().tasks;\n}\n\n/** Returns GroupsApi if the provider supplies one, otherwise null. */\nexport function useGroupsApi(): GroupsApi | null {\n return useContactsDomainApi().groups ?? null;\n}\n","import type { Provider } from \"react\";\nimport { createTranslationContext } from \"@fluid-app/i18n/translation-api-context-factory\";\nimport type { TranslationApi } from \"@fluid-app/i18n/translation-api\";\nimport type { ContactsDict } from \"./translation-dictionary\";\n\nconst { Provider: ContactsProvider, useTranslation } =\n createTranslationContext<ContactsDict>(\"Contacts\");\n\nexport const ContactsTranslationProvider: Provider<TranslationApi<ContactsDict> | null> =\n ContactsProvider;\nexport const useContactsTranslation = useTranslation;\n","/**\n * Tasks store both a title (first line) and an optional body separated by a\n * blank line. This is the canonical convention used by the task editor and\n * by every consumer that displays tasks (contacts UI list, portal todo\n * widget). Drift across consumers would mean some surfaces show \"Title\\n\\n\n * body details\" verbatim while others split correctly — keep the delimiter\n * convention in one place.\n */\nexport function parseTaskBody(raw: string): { title: string; body: string } {\n const split = raw.indexOf(\"\\n\\n\");\n if (split >= 0) {\n return {\n title: raw.slice(0, split),\n body: raw.slice(split + 2),\n };\n }\n return { title: raw, body: \"\" };\n}\n","export const CONTACTS_QUERY_KEYS = {\n all: (prefix: string) => [prefix] as const,\n list: (prefix: string) =>\n [...CONTACTS_QUERY_KEYS.all(prefix), \"list\"] as const,\n detail: (prefix: string, id: string) =>\n [...CONTACTS_QUERY_KEYS.all(prefix), \"detail\", id] as const,\n} as const;\n\nexport const contactsKeys = {\n activities: (contactId: string) =>\n [\"portal-contacts\", \"activities\", contactId] as const,\n tasks: (contactId: string) =>\n [\"portal-contacts\", \"tasks\", contactId] as const,\n notes: (contactId: string) =>\n [\"portal-contacts\", \"notes\", contactId] as const,\n orders: (contactId: string) => [\"rep-contacts\", \"orders\", contactId] as const,\n subscriptionOrders: (contactId: string) =>\n [\"rep-contacts\", \"subscription-orders\", contactId] as const,\n groups: () => [\"portal-contacts\", \"groups\"] as const,\n} as const;\n","import { useInfiniteQuery } from \"@tanstack/react-query\";\nimport { useContactsCrud } from \"../contacts-api-context\";\nimport { CONTACTS_QUERY_KEYS } from \"../query-keys\";\n\nexport interface UseInfiniteContactsParams {\n search_query?: string;\n status?: string;\n sort_by?: string;\n sort_direction?: string;\n per_page?: number;\n tags?: string[];\n}\n\nexport function useInfiniteContacts(params: UseInfiniteContactsParams) {\n const api = useContactsCrud();\n return useInfiniteQuery({\n queryKey: [...CONTACTS_QUERY_KEYS.list(\"contacts\"), params],\n queryFn: ({ pageParam }) =>\n api.listContacts({\n ...params,\n page: pageParam,\n }),\n getNextPageParam: (lastPage) => {\n const currentPage = lastPage.meta.current_page;\n // Contacts API is page-number based; next_cursor and total_pages\n // are both used as \"has-next-page\" signals.\n if (currentPage == null) return undefined;\n if (lastPage.meta.next_cursor) return currentPage + 1;\n if (\n lastPage.meta.total_pages != null &&\n currentPage < lastPage.meta.total_pages\n ) {\n return currentPage + 1;\n }\n return undefined;\n },\n initialPageParam: 1,\n });\n}\n","/**\n * Format a date as YYYY-MM-DD in the renderer's local timezone, optionally\n * shifted by `offsetDays`. Use this for due-date inputs where \"today\" must\n * resolve to the user's local calendar date — never `toISOString().slice(0, 10)`,\n * which returns the UTC date and silently rolls over a day for users east/west\n * of UTC at the wrong hours.\n */\nexport function isoDate(offsetDays: number, now: Date = new Date()): string {\n const d = new Date(now);\n d.setDate(d.getDate() + offsetDays);\n const yyyy = d.getFullYear();\n const mm = String(d.getMonth() + 1).padStart(2, \"0\");\n const dd = String(d.getDate()).padStart(2, \"0\");\n return `${yyyy}-${mm}-${dd}`;\n}\n\n/**\n * Return a Date pinned to local midnight of the calendar day represented by\n * `input`. Use this whenever you need to compare two due dates by calendar day\n * (overdue / today / tomorrow / future).\n *\n * Why not just `new Date(input)`? `new Date(\"YYYY-MM-DD\")` parses the string\n * as UTC midnight, which lands on the *previous* calendar day in any UTC−\n * timezone — a task due \"May 1\" then reads as April 30 for a user in the\n * Americas, classifying it as \"Overdue\" all day. Parse the date components\n * directly so the calendar-day intent of the string is preserved.\n */\nexport function startOfLocalDay(input: Date | string): Date {\n if (typeof input === \"string\") {\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})/.exec(input);\n if (match?.[1] && match[2] && match[3]) {\n return new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]));\n }\n }\n const d = typeof input === \"string\" ? new Date(input) : input;\n return new Date(d.getFullYear(), d.getMonth(), d.getDate());\n}\n","\"use client\";\n\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { fluidToast } from \"@fluid-app/ui-primitives\";\nimport { parseApiErrors } from \"@fluid-app/api-client-core\";\nimport { useTasksApi } from \"@fluid-app/contacts-core/contacts-api-context\";\nimport { contactsKeys } from \"@fluid-app/contacts-core/query-keys\";\nimport type { CreateTaskInput } from \"@fluid-app/contacts-core/types\";\n\nexport type { CreateTaskInput };\n\nexport function useCreateContactTask(\n contactId: string,\n options?: { onSuccess?: () => void },\n) {\n const queryClient = useQueryClient();\n const api = useTasksApi();\n\n return useMutation({\n mutationFn: (input: CreateTaskInput) => api.createTask(contactId, input),\n onSuccess: () => {\n fluidToast({ title: \"Task created\", type: \"success\" });\n queryClient.invalidateQueries({\n queryKey: contactsKeys.tasks(contactId),\n });\n options?.onSuccess?.();\n },\n onError: (error) => {\n const description = parseApiErrors(error);\n fluidToast({\n title: \"Failed to create task\",\n type: \"error\",\n ...(description ? { description } : {}),\n });\n },\n });\n}\n","\"use client\";\n\nimport React, { useEffect, useMemo, useRef, useState } from \"react\";\nimport { X } from \"lucide-react\";\nimport { cn } from \"@fluid-app/ui-primitives\";\nimport { useContactsTranslation } from \"@fluid-app/contacts-core/translation-api-context\";\nimport { isoDate } from \"@fluid-app/contacts-core/iso-date\";\nimport { useCreateContactTask } from \"../../hooks/contacts/use-create-contact-task\";\n\nconst QUICK_DATES = [\n { key: \"today\", offsetDays: 0 },\n { key: \"tomorrow\", offsetDays: 1 },\n { key: \"next_week\", offsetDays: 7 },\n] as const;\n\nexport interface TaskComposerFormProps {\n contactId: string;\n /** Called after a successful create. */\n onDone?: () => void;\n /** When provided, an X button is rendered and Escape closes the form. */\n onClose?: () => void;\n /** Focus the body input on mount. Default true. */\n autoFocus?: boolean;\n}\n\nexport function TaskComposerForm({\n contactId,\n onDone,\n onClose,\n autoFocus = true,\n}: TaskComposerFormProps): React.JSX.Element {\n const { t } = useContactsTranslation();\n const [body, setBody] = useState(\"\");\n const [dueDate, setDueDate] = useState<string | null>(null);\n const inputRef = useRef<HTMLInputElement | null>(null);\n\n const createTask = useCreateContactTask(contactId, {\n onSuccess: () => {\n setBody(\"\");\n setDueDate(null);\n onDone?.();\n },\n });\n\n useEffect(() => {\n if (autoFocus) inputRef.current?.focus();\n }, [autoFocus]);\n\n // Compute the date strings once per mount. Re-running on every render would\n // re-evaluate `new Date()` and could flip \"today\" if the form happens to be\n // open across midnight — acceptable but unnecessary churn.\n const quickDates = useMemo(\n () =>\n QUICK_DATES.map((q) => ({\n key: q.key,\n iso: isoDate(q.offsetDays),\n })),\n [],\n );\n\n const quickLabels: Record<(typeof QUICK_DATES)[number][\"key\"], string> = {\n today: t(\"quick_today\"),\n tomorrow: t(\"quick_tomorrow\"),\n next_week: t(\"quick_next_week\"),\n };\n\n const submit = () => {\n const trimmed = body.trim();\n if (!trimmed || createTask.isPending) return;\n createTask.mutate({ body: trimmed, due_at: dueDate });\n };\n\n const canSubmit = body.trim().length > 0 && !createTask.isPending;\n\n return (\n <div className=\"border-border/50 focus-within:border-foreground/30 rounded-xl border p-3 transition-colors\">\n <div className=\"flex items-start gap-3\">\n <div\n className=\"border-muted-foreground/50 mt-1.5 size-5 shrink-0 rounded-full border-2\"\n aria-hidden=\"true\"\n />\n <input\n ref={inputRef}\n value={body}\n onChange={(e) => setBody(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n submit();\n } else if (e.key === \"Escape\" && onClose) {\n e.preventDefault();\n onClose();\n }\n }}\n placeholder={t(\"task_describe_placeholder\")}\n aria-label={t(\"task_description_aria\")}\n className=\"placeholder:text-muted-foreground/80 text-foreground flex-1 border-0 bg-transparent text-sm font-medium outline-none pointer-coarse:text-base\"\n />\n {onClose && (\n <button\n type=\"button\"\n onClick={onClose}\n aria-label={t(\"task_discard_aria\")}\n className=\"text-muted-foreground hover:bg-muted hover:text-foreground -mt-0.5 -mr-0.5 flex size-7 shrink-0 items-center justify-center rounded-md transition-colors\"\n >\n <X className=\"size-4\" />\n </button>\n )}\n </div>\n\n {/* --row-indent aligns the chip row with the input text above (size-5\n circle + gap-3 = 2rem). The mobile Add button cancels it via\n -ml-(--row-indent) + w-[calc(100%+var(--row-indent))] so the two\n values stay locked to a single source. */}\n <div className=\"mt-3 flex flex-wrap items-center gap-1.5 pl-(--row-indent) [--row-indent:2rem]\">\n {quickDates.map((q) => {\n const isActive = dueDate === q.iso;\n return (\n <button\n key={q.key}\n type=\"button\"\n onClick={() => setDueDate(isActive ? null : q.iso)}\n aria-pressed={isActive}\n className={cn(\n \"shrink-0 rounded-full px-3 py-1 text-xs font-medium transition-colors\",\n isActive\n ? \"bg-primary text-primary-foreground\"\n : \"bg-muted text-muted-foreground hover:bg-muted/70\",\n )}\n >\n {quickLabels[q.key]}\n </button>\n );\n })}\n <button\n type=\"button\"\n onClick={submit}\n disabled={!canSubmit}\n className=\"bg-primary text-primary-foreground hover:bg-primary/90 disabled:bg-muted disabled:text-muted-foreground mt-1 -ml-(--row-indent) inline-flex h-10 w-[calc(100%+var(--row-indent))] items-center justify-center rounded-lg text-sm font-semibold transition-colors disabled:cursor-not-allowed sm:mt-0 sm:ml-auto sm:h-auto sm:w-auto sm:shrink-0 sm:gap-1.5 sm:rounded-full sm:px-4 sm:py-1.5 sm:text-xs\"\n >\n {createTask.isPending ? t(\"adding\") : t(\"add_task\")}\n </button>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -4,7 +4,7 @@ let react = require("react");
4
4
  function createTranslationContext(domainName) {
5
5
  const Ctx = (0, react.createContext)(null);
6
6
  function useTranslation() {
7
- const api = (0, react.useContext)(Ctx);
7
+ const api = (0, react.use)(Ctx);
8
8
  if (!api) throw new Error(`use${domainName}Translation must be used within a ${domainName}TranslationProvider`);
9
9
  return api;
10
10
  }
@@ -22,4 +22,4 @@ Object.defineProperty(exports, "createTranslationContext", {
22
22
  }
23
23
  });
24
24
 
25
- //# sourceMappingURL=translation-api-context-factory-BSRK6Z50.cjs.map
25
+ //# sourceMappingURL=translation-api-context-factory-CjRDqIhF.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translation-api-context-factory-CjRDqIhF.cjs","names":[],"sources":["../../../platform/i18n/src/translation-api-context-factory.ts"],"sourcesContent":["import { createContext, type Context, type Provider, use } from \"react\";\nimport type { TranslationApi } from \"./translation-api\";\n\nexport function createTranslationContext<D extends Record<string, string>>(\n domainName: string,\n): {\n Context: Context<TranslationApi<D> | null>;\n Provider: Provider<TranslationApi<D> | null>;\n useTranslation: () => TranslationApi<D>;\n} {\n const Ctx = createContext<TranslationApi<D> | null>(null);\n\n function useTranslation(): TranslationApi<D> {\n const api = use(Ctx);\n if (!api) {\n throw new Error(\n `use${domainName}Translation must be used within a ${domainName}TranslationProvider`,\n );\n }\n return api;\n }\n\n return { Context: Ctx, Provider: Ctx.Provider, useTranslation };\n}\n"],"mappings":";;;AAGA,SAAgB,yBACd,YAKA;CACA,MAAM,OAAA,GAAA,MAAA,eAA8C,KAAK;CAEzD,SAAS,iBAAoC;EAC3C,MAAM,OAAA,GAAA,MAAA,KAAU,IAAI;AACpB,MAAI,CAAC,IACH,OAAM,IAAI,MACR,MAAM,WAAW,oCAAoC,WAAW,qBACjE;AAEH,SAAO;;AAGT,QAAO;EAAE,SAAS;EAAK,UAAU,IAAI;EAAU;EAAgB"}
@@ -1,9 +1,9 @@
1
- import { createContext, useContext } from "react";
1
+ import { createContext, use } from "react";
2
2
  //#region ../../platform/i18n/src/translation-api-context-factory.ts
3
3
  function createTranslationContext(domainName) {
4
4
  const Ctx = createContext(null);
5
5
  function useTranslation() {
6
- const api = useContext(Ctx);
6
+ const api = use(Ctx);
7
7
  if (!api) throw new Error(`use${domainName}Translation must be used within a ${domainName}TranslationProvider`);
8
8
  return api;
9
9
  }
@@ -16,4 +16,4 @@ function createTranslationContext(domainName) {
16
16
  //#endregion
17
17
  export { createTranslationContext as t };
18
18
 
19
- //# sourceMappingURL=translation-api-context-factory-CJrVq_EB.mjs.map
19
+ //# sourceMappingURL=translation-api-context-factory-DFr9yJ6Q.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translation-api-context-factory-DFr9yJ6Q.mjs","names":[],"sources":["../../../platform/i18n/src/translation-api-context-factory.ts"],"sourcesContent":["import { createContext, type Context, type Provider, use } from \"react\";\nimport type { TranslationApi } from \"./translation-api\";\n\nexport function createTranslationContext<D extends Record<string, string>>(\n domainName: string,\n): {\n Context: Context<TranslationApi<D> | null>;\n Provider: Provider<TranslationApi<D> | null>;\n useTranslation: () => TranslationApi<D>;\n} {\n const Ctx = createContext<TranslationApi<D> | null>(null);\n\n function useTranslation(): TranslationApi<D> {\n const api = use(Ctx);\n if (!api) {\n throw new Error(\n `use${domainName}Translation must be used within a ${domainName}TranslationProvider`,\n );\n }\n return api;\n }\n\n return { Context: Ctx, Provider: Ctx.Provider, useTranslation };\n}\n"],"mappings":";;AAGA,SAAgB,yBACd,YAKA;CACA,MAAM,MAAM,cAAwC,KAAK;CAEzD,SAAS,iBAAoC;EAC3C,MAAM,MAAM,IAAI,IAAI;AACpB,MAAI,CAAC,IACH,OAAM,IAAI,MACR,MAAM,WAAW,oCAAoC,WAAW,qBACjE;AAEH,SAAO;;AAGT,QAAO;EAAE,SAAS;EAAK,UAAU,IAAI;EAAU;EAAgB"}
@@ -1,5 +1,5 @@
1
1
  require("./chunk-9hOWP6kD.cjs");
2
- const require_PortalTenantClientProvider = require("./PortalTenantClientProvider-Bpm-CZq1.cjs");
2
+ const require_PortalTenantClientProvider = require("./PortalTenantClientProvider-C0eJp8MN.cjs");
3
3
  const require_query_keys = require("./query-keys-elu0svUd.cjs");
4
4
  let _tanstack_react_query = require("@tanstack/react-query");
5
5
  //#region src/hooks/use-account.ts
@@ -25,4 +25,4 @@ Object.defineProperty(exports, "useAccount", {
25
25
  }
26
26
  });
27
27
 
28
- //# sourceMappingURL=use-account-CQ-mhA3W.cjs.map
28
+ //# sourceMappingURL=use-account-DltRHEwC.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-account-CQ-mhA3W.cjs","names":["useAccountApi","accountKeys"],"sources":["../src/hooks/use-account.ts"],"sourcesContent":["import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useAccountApi } from \"@fluid-app/portal-core/account-api-context\";\nimport type { AccountRep } from \"@fluid-app/portal-core/account-types\";\nimport { accountKeys } from \"../account/query-keys\";\n\n/**\n * Fetches the current user's account from GET /api/account.\n * Replaces the old useCurrentUser (which called non-existent /api/me).\n */\nexport function useAccount(): UseQueryResult<AccountRep> {\n const api = useAccountApi();\n\n return useQuery({\n queryKey: accountKeys.detail(),\n queryFn: async () => {\n const response = await api.fetchAccount();\n return response.account;\n },\n staleTime: 5 * 60 * 1000,\n });\n}\n"],"mappings":";;;;;;;;;AASA,SAAgB,aAAyC;CACvD,MAAM,MAAMA,mCAAAA,eAAe;AAE3B,SAAA,GAAA,sBAAA,UAAgB;EACd,UAAUC,mBAAAA,YAAY,QAAQ;EAC9B,SAAS,YAAY;AAEnB,WADiB,MAAM,IAAI,cAAc,EACzB;;EAElB,WAAW,MAAS;EACrB,CAAC"}
1
+ {"version":3,"file":"use-account-DltRHEwC.cjs","names":["useAccountApi","accountKeys"],"sources":["../src/hooks/use-account.ts"],"sourcesContent":["import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useAccountApi } from \"@fluid-app/portal-core/account-api-context\";\nimport type { AccountRep } from \"@fluid-app/portal-core/account-types\";\nimport { accountKeys } from \"../account/query-keys\";\n\n/**\n * Fetches the current user's account from GET /api/account.\n * Replaces the old useCurrentUser (which called non-existent /api/me).\n */\nexport function useAccount(): UseQueryResult<AccountRep> {\n const api = useAccountApi();\n\n return useQuery({\n queryKey: accountKeys.detail(),\n queryFn: async () => {\n const response = await api.fetchAccount();\n return response.account;\n },\n staleTime: 5 * 60 * 1000,\n });\n}\n"],"mappings":";;;;;;;;;AASA,SAAgB,aAAyC;CACvD,MAAM,MAAMA,mCAAAA,eAAe;AAE3B,SAAA,GAAA,sBAAA,UAAgB;EACd,UAAUC,mBAAAA,YAAY,QAAQ;EAC9B,SAAS,YAAY;AAEnB,WADiB,MAAM,IAAI,cAAc,EACzB;;EAElB,WAAW,MAAS;EACrB,CAAC"}
@@ -1,4 +1,4 @@
1
- import { gt as useAccountApi } from "./PortalTenantClientProvider-BmRtQAbi.mjs";
1
+ import { gt as useAccountApi } from "./PortalTenantClientProvider-DVClpfbi.mjs";
2
2
  import { t as accountKeys } from "./query-keys-xJy_fapN.mjs";
3
3
  import { useQuery } from "@tanstack/react-query";
4
4
  //#region src/hooks/use-account.ts
@@ -19,4 +19,4 @@ function useAccount() {
19
19
  //#endregion
20
20
  export { useAccount as t };
21
21
 
22
- //# sourceMappingURL=use-account-CwGoFpwg.mjs.map
22
+ //# sourceMappingURL=use-account-Dm6Svko1.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-account-CwGoFpwg.mjs","names":[],"sources":["../src/hooks/use-account.ts"],"sourcesContent":["import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useAccountApi } from \"@fluid-app/portal-core/account-api-context\";\nimport type { AccountRep } from \"@fluid-app/portal-core/account-types\";\nimport { accountKeys } from \"../account/query-keys\";\n\n/**\n * Fetches the current user's account from GET /api/account.\n * Replaces the old useCurrentUser (which called non-existent /api/me).\n */\nexport function useAccount(): UseQueryResult<AccountRep> {\n const api = useAccountApi();\n\n return useQuery({\n queryKey: accountKeys.detail(),\n queryFn: async () => {\n const response = await api.fetchAccount();\n return response.account;\n },\n staleTime: 5 * 60 * 1000,\n });\n}\n"],"mappings":";;;;;;;;AASA,SAAgB,aAAyC;CACvD,MAAM,MAAM,eAAe;AAE3B,QAAO,SAAS;EACd,UAAU,YAAY,QAAQ;EAC9B,SAAS,YAAY;AAEnB,WADiB,MAAM,IAAI,cAAc,EACzB;;EAElB,WAAW,MAAS;EACrB,CAAC"}
1
+ {"version":3,"file":"use-account-Dm6Svko1.mjs","names":[],"sources":["../src/hooks/use-account.ts"],"sourcesContent":["import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useAccountApi } from \"@fluid-app/portal-core/account-api-context\";\nimport type { AccountRep } from \"@fluid-app/portal-core/account-types\";\nimport { accountKeys } from \"../account/query-keys\";\n\n/**\n * Fetches the current user's account from GET /api/account.\n * Replaces the old useCurrentUser (which called non-existent /api/me).\n */\nexport function useAccount(): UseQueryResult<AccountRep> {\n const api = useAccountApi();\n\n return useQuery({\n queryKey: accountKeys.detail(),\n queryFn: async () => {\n const response = await api.fetchAccount();\n return response.account;\n },\n staleTime: 5 * 60 * 1000,\n });\n}\n"],"mappings":";;;;;;;;AASA,SAAgB,aAAyC;CACvD,MAAM,MAAM,eAAe;AAE3B,QAAO,SAAS;EACd,UAAU,YAAY,QAAQ;EAC9B,SAAS,YAAY;AAEnB,WADiB,MAAM,IAAI,cAAc,EACzB;;EAElB,WAAW,MAAS;EACrB,CAAC"}
@@ -1,4 +1,4 @@
1
- import { n as useMySiteApi } from "./mysite-api-context-kUTM3GNG.mjs";
1
+ import { n as useMySiteApi } from "./mysite-api-context-CoLr9vIf.mjs";
2
2
  import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
3
3
  //#region ../../mysite/ui/src/portal/hooks/use-mysite-portal.ts
4
4
  const PORTAL_MYSITE_KEYS = {
@@ -127,4 +127,4 @@ function usePortalMySiteThemes() {
127
127
  //#endregion
128
128
  export { usePortalFavorites as a, usePortalMySiteThemes as c, usePortalUpdateLink as d, usePortalUpdateSettings as f, usePortalDeleteLink as i, usePortalReorderFavorites as l, usePortalCreateLink as n, usePortalLinks as o, usePortalDeleteFavorite as r, usePortalMySiteProfile as s, PORTAL_MYSITE_KEYS as t, usePortalReorderLinks as u };
129
129
 
130
- //# sourceMappingURL=use-mysite-portal-ChDJ4z34.mjs.map
130
+ //# sourceMappingURL=use-mysite-portal-3Tn3bFoE.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-mysite-portal-ChDJ4z34.mjs","names":[],"sources":["../../../mysite/ui/src/portal/hooks/use-mysite-portal.ts"],"sourcesContent":["\"use client\";\n\nimport { useQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { useMySiteApi } from \"@fluid-app/mysite-core/mysite-api-context\";\nimport type {\n MySiteProfile,\n MySiteLink,\n MySiteFavorite,\n MySiteTheme,\n CreateLinkBody,\n UpdateLinkBody,\n UpdateProfileBody,\n UpdateSettingsBody,\n AddFavoriteBody,\n} from \"@fluid-app/mysite-core/mysite-api-types\";\n\n// ---------------------------------------------------------------------------\n// Query keys — session-scoped, no userId/affiliateId needed\n// ---------------------------------------------------------------------------\n\nexport const PORTAL_MYSITE_KEYS = {\n profile: () => [\"portal-mysite\", \"profile\"] as const,\n links: () => [\"portal-mysite\", \"links\"] as const,\n favorites: () => [\"portal-mysite\", \"favorites\"] as const,\n themes: () => [\"portal-mysite\", \"themes\"] as const,\n};\n\n// ---------------------------------------------------------------------------\n// Profile hooks\n// ---------------------------------------------------------------------------\n\nexport function usePortalMySiteProfile() {\n const api = useMySiteApi();\n return useQuery<MySiteProfile>({\n queryKey: PORTAL_MYSITE_KEYS.profile(),\n queryFn: () => api.fetchProfile(),\n });\n}\n\nexport function usePortalUpdateProfile(options?: { onSuccess?: () => void }) {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (body: UpdateProfileBody) => api.updateProfile(body),\n onSuccess: (data) => {\n queryClient.setQueryData(PORTAL_MYSITE_KEYS.profile(), data);\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.profile(),\n });\n options?.onSuccess?.();\n },\n });\n}\n\nexport function usePortalUpdateSettings(options?: { onSuccess?: () => void }) {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (body: UpdateSettingsBody) => api.updateSettings(body),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.profile(),\n });\n options?.onSuccess?.();\n },\n });\n}\n\n// ---------------------------------------------------------------------------\n// Links hooks\n// ---------------------------------------------------------------------------\n\nexport function usePortalLinks() {\n const api = useMySiteApi();\n return useQuery<MySiteLink[]>({\n queryKey: PORTAL_MYSITE_KEYS.links(),\n queryFn: () => api.listLinks(),\n });\n}\n\nexport function usePortalCreateLink() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (body: CreateLinkBody) => api.createLink(body),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.links(),\n });\n },\n });\n}\n\nexport function usePortalUpdateLink() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: ({ linkId, body }: { linkId: number; body: UpdateLinkBody }) =>\n api.updateLink(linkId, body),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.links(),\n });\n },\n });\n}\n\nexport function usePortalDeleteLink() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (linkId: number) => api.deleteLink(linkId),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.links(),\n });\n },\n });\n}\n\nexport function usePortalReorderLinks() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: ({\n orderedIds,\n }: {\n orderedIds: number[];\n optimisticItems: MySiteLink[];\n }) => api.reorderLinks(orderedIds),\n onMutate: async ({ optimisticItems }) => {\n await queryClient.cancelQueries({\n queryKey: PORTAL_MYSITE_KEYS.links(),\n });\n const previousData = queryClient.getQueryData<MySiteLink[]>(\n PORTAL_MYSITE_KEYS.links(),\n );\n queryClient.setQueryData(PORTAL_MYSITE_KEYS.links(), optimisticItems);\n return { previousData };\n },\n onError: (_err, _vars, context) => {\n if (context?.previousData) {\n queryClient.setQueryData(\n PORTAL_MYSITE_KEYS.links(),\n context.previousData,\n );\n }\n },\n onSettled: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.links(),\n });\n },\n });\n}\n\n// ---------------------------------------------------------------------------\n// Favorites hooks\n// ---------------------------------------------------------------------------\n\nexport function usePortalFavorites() {\n const api = useMySiteApi();\n return useQuery<MySiteFavorite[]>({\n queryKey: PORTAL_MYSITE_KEYS.favorites(),\n queryFn: () => api.listFavorites(),\n });\n}\n\nexport function usePortalAddFavorite() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (body: AddFavoriteBody) => api.addFavorite(body),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.favorites(),\n });\n },\n });\n}\n\nexport function usePortalDeleteFavorite() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (favoriteId: number) => api.deleteFavorite(favoriteId),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.favorites(),\n });\n },\n });\n}\n\nexport function usePortalReorderFavorites() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: ({\n orderedIds,\n }: {\n orderedIds: number[];\n optimisticItems: MySiteFavorite[];\n }) => api.reorderFavorites(orderedIds),\n onMutate: async ({ optimisticItems }) => {\n await queryClient.cancelQueries({\n queryKey: PORTAL_MYSITE_KEYS.favorites(),\n });\n const previousData = queryClient.getQueryData<MySiteFavorite[]>(\n PORTAL_MYSITE_KEYS.favorites(),\n );\n queryClient.setQueryData(PORTAL_MYSITE_KEYS.favorites(), optimisticItems);\n return { previousData };\n },\n onError: (_err, _vars, context) => {\n if (context?.previousData) {\n queryClient.setQueryData(\n PORTAL_MYSITE_KEYS.favorites(),\n context.previousData,\n );\n }\n },\n onSettled: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.favorites(),\n });\n },\n });\n}\n\n// ---------------------------------------------------------------------------\n// Themes hook\n// ---------------------------------------------------------------------------\n\nexport function usePortalMySiteThemes() {\n const api = useMySiteApi();\n return useQuery<MySiteTheme[]>({\n queryKey: PORTAL_MYSITE_KEYS.themes(),\n queryFn: () => api.listThemes(),\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-mysite-portal-3Tn3bFoE.mjs","names":[],"sources":["../../../mysite/ui/src/portal/hooks/use-mysite-portal.ts"],"sourcesContent":["\"use client\";\n\nimport { useQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { useMySiteApi } from \"@fluid-app/mysite-core/mysite-api-context\";\nimport type {\n MySiteProfile,\n MySiteLink,\n MySiteFavorite,\n MySiteTheme,\n CreateLinkBody,\n UpdateLinkBody,\n UpdateProfileBody,\n UpdateSettingsBody,\n AddFavoriteBody,\n} from \"@fluid-app/mysite-core/mysite-api-types\";\n\n// ---------------------------------------------------------------------------\n// Query keys — session-scoped, no userId/affiliateId needed\n// ---------------------------------------------------------------------------\n\nexport const PORTAL_MYSITE_KEYS = {\n profile: () => [\"portal-mysite\", \"profile\"] as const,\n links: () => [\"portal-mysite\", \"links\"] as const,\n favorites: () => [\"portal-mysite\", \"favorites\"] as const,\n themes: () => [\"portal-mysite\", \"themes\"] as const,\n};\n\n// ---------------------------------------------------------------------------\n// Profile hooks\n// ---------------------------------------------------------------------------\n\nexport function usePortalMySiteProfile() {\n const api = useMySiteApi();\n return useQuery<MySiteProfile>({\n queryKey: PORTAL_MYSITE_KEYS.profile(),\n queryFn: () => api.fetchProfile(),\n });\n}\n\nexport function usePortalUpdateProfile(options?: { onSuccess?: () => void }) {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (body: UpdateProfileBody) => api.updateProfile(body),\n onSuccess: (data) => {\n queryClient.setQueryData(PORTAL_MYSITE_KEYS.profile(), data);\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.profile(),\n });\n options?.onSuccess?.();\n },\n });\n}\n\nexport function usePortalUpdateSettings(options?: { onSuccess?: () => void }) {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (body: UpdateSettingsBody) => api.updateSettings(body),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.profile(),\n });\n options?.onSuccess?.();\n },\n });\n}\n\n// ---------------------------------------------------------------------------\n// Links hooks\n// ---------------------------------------------------------------------------\n\nexport function usePortalLinks() {\n const api = useMySiteApi();\n return useQuery<MySiteLink[]>({\n queryKey: PORTAL_MYSITE_KEYS.links(),\n queryFn: () => api.listLinks(),\n });\n}\n\nexport function usePortalCreateLink() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (body: CreateLinkBody) => api.createLink(body),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.links(),\n });\n },\n });\n}\n\nexport function usePortalUpdateLink() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: ({ linkId, body }: { linkId: number; body: UpdateLinkBody }) =>\n api.updateLink(linkId, body),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.links(),\n });\n },\n });\n}\n\nexport function usePortalDeleteLink() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (linkId: number) => api.deleteLink(linkId),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.links(),\n });\n },\n });\n}\n\nexport function usePortalReorderLinks() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: ({\n orderedIds,\n }: {\n orderedIds: number[];\n optimisticItems: MySiteLink[];\n }) => api.reorderLinks(orderedIds),\n onMutate: async ({ optimisticItems }) => {\n await queryClient.cancelQueries({\n queryKey: PORTAL_MYSITE_KEYS.links(),\n });\n const previousData = queryClient.getQueryData<MySiteLink[]>(\n PORTAL_MYSITE_KEYS.links(),\n );\n queryClient.setQueryData(PORTAL_MYSITE_KEYS.links(), optimisticItems);\n return { previousData };\n },\n onError: (_err, _vars, context) => {\n if (context?.previousData) {\n queryClient.setQueryData(\n PORTAL_MYSITE_KEYS.links(),\n context.previousData,\n );\n }\n },\n onSettled: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.links(),\n });\n },\n });\n}\n\n// ---------------------------------------------------------------------------\n// Favorites hooks\n// ---------------------------------------------------------------------------\n\nexport function usePortalFavorites() {\n const api = useMySiteApi();\n return useQuery<MySiteFavorite[]>({\n queryKey: PORTAL_MYSITE_KEYS.favorites(),\n queryFn: () => api.listFavorites(),\n });\n}\n\nexport function usePortalAddFavorite() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (body: AddFavoriteBody) => api.addFavorite(body),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.favorites(),\n });\n },\n });\n}\n\nexport function usePortalDeleteFavorite() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (favoriteId: number) => api.deleteFavorite(favoriteId),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.favorites(),\n });\n },\n });\n}\n\nexport function usePortalReorderFavorites() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: ({\n orderedIds,\n }: {\n orderedIds: number[];\n optimisticItems: MySiteFavorite[];\n }) => api.reorderFavorites(orderedIds),\n onMutate: async ({ optimisticItems }) => {\n await queryClient.cancelQueries({\n queryKey: PORTAL_MYSITE_KEYS.favorites(),\n });\n const previousData = queryClient.getQueryData<MySiteFavorite[]>(\n PORTAL_MYSITE_KEYS.favorites(),\n );\n queryClient.setQueryData(PORTAL_MYSITE_KEYS.favorites(), optimisticItems);\n return { previousData };\n },\n onError: (_err, _vars, context) => {\n if (context?.previousData) {\n queryClient.setQueryData(\n PORTAL_MYSITE_KEYS.favorites(),\n context.previousData,\n );\n }\n },\n onSettled: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.favorites(),\n });\n },\n });\n}\n\n// ---------------------------------------------------------------------------\n// Themes hook\n// ---------------------------------------------------------------------------\n\nexport function usePortalMySiteThemes() {\n const api = useMySiteApi();\n return useQuery<MySiteTheme[]>({\n queryKey: PORTAL_MYSITE_KEYS.themes(),\n queryFn: () => api.listThemes(),\n });\n}\n"],"mappings":";;;AAoBA,MAAa,qBAAqB;CAChC,eAAe,CAAC,iBAAiB,UAAU;CAC3C,aAAa,CAAC,iBAAiB,QAAQ;CACvC,iBAAiB,CAAC,iBAAiB,YAAY;CAC/C,cAAc,CAAC,iBAAiB,SAAS;CAC1C;AAMD,SAAgB,yBAAyB;CACvC,MAAM,MAAM,cAAc;AAC1B,QAAO,SAAwB;EAC7B,UAAU,mBAAmB,SAAS;EACtC,eAAe,IAAI,cAAc;EAClC,CAAC;;AAmBJ,SAAgB,wBAAwB,SAAsC;CAC5E,MAAM,MAAM,cAAc;CAC1B,MAAM,cAAc,gBAAgB;AAEpC,QAAO,YAAY;EACjB,aAAa,SAA6B,IAAI,eAAe,KAAK;EAClE,iBAAiB;AACf,eAAY,kBAAkB,EAC5B,UAAU,mBAAmB,SAAS,EACvC,CAAC;AACF,YAAS,aAAa;;EAEzB,CAAC;;AAOJ,SAAgB,iBAAiB;CAC/B,MAAM,MAAM,cAAc;AAC1B,QAAO,SAAuB;EAC5B,UAAU,mBAAmB,OAAO;EACpC,eAAe,IAAI,WAAW;EAC/B,CAAC;;AAGJ,SAAgB,sBAAsB;CACpC,MAAM,MAAM,cAAc;CAC1B,MAAM,cAAc,gBAAgB;AAEpC,QAAO,YAAY;EACjB,aAAa,SAAyB,IAAI,WAAW,KAAK;EAC1D,iBAAiB;AACf,eAAY,kBAAkB,EAC5B,UAAU,mBAAmB,OAAO,EACrC,CAAC;;EAEL,CAAC;;AAGJ,SAAgB,sBAAsB;CACpC,MAAM,MAAM,cAAc;CAC1B,MAAM,cAAc,gBAAgB;AAEpC,QAAO,YAAY;EACjB,aAAa,EAAE,QAAQ,WACrB,IAAI,WAAW,QAAQ,KAAK;EAC9B,iBAAiB;AACf,eAAY,kBAAkB,EAC5B,UAAU,mBAAmB,OAAO,EACrC,CAAC;;EAEL,CAAC;;AAGJ,SAAgB,sBAAsB;CACpC,MAAM,MAAM,cAAc;CAC1B,MAAM,cAAc,gBAAgB;AAEpC,QAAO,YAAY;EACjB,aAAa,WAAmB,IAAI,WAAW,OAAO;EACtD,iBAAiB;AACf,eAAY,kBAAkB,EAC5B,UAAU,mBAAmB,OAAO,EACrC,CAAC;;EAEL,CAAC;;AAGJ,SAAgB,wBAAwB;CACtC,MAAM,MAAM,cAAc;CAC1B,MAAM,cAAc,gBAAgB;AAEpC,QAAO,YAAY;EACjB,aAAa,EACX,iBAII,IAAI,aAAa,WAAW;EAClC,UAAU,OAAO,EAAE,sBAAsB;AACvC,SAAM,YAAY,cAAc,EAC9B,UAAU,mBAAmB,OAAO,EACrC,CAAC;GACF,MAAM,eAAe,YAAY,aAC/B,mBAAmB,OAAO,CAC3B;AACD,eAAY,aAAa,mBAAmB,OAAO,EAAE,gBAAgB;AACrE,UAAO,EAAE,cAAc;;EAEzB,UAAU,MAAM,OAAO,YAAY;AACjC,OAAI,SAAS,aACX,aAAY,aACV,mBAAmB,OAAO,EAC1B,QAAQ,aACT;;EAGL,iBAAiB;AACf,eAAY,kBAAkB,EAC5B,UAAU,mBAAmB,OAAO,EACrC,CAAC;;EAEL,CAAC;;AAOJ,SAAgB,qBAAqB;CACnC,MAAM,MAAM,cAAc;AAC1B,QAAO,SAA2B;EAChC,UAAU,mBAAmB,WAAW;EACxC,eAAe,IAAI,eAAe;EACnC,CAAC;;AAiBJ,SAAgB,0BAA0B;CACxC,MAAM,MAAM,cAAc;CAC1B,MAAM,cAAc,gBAAgB;AAEpC,QAAO,YAAY;EACjB,aAAa,eAAuB,IAAI,eAAe,WAAW;EAClE,iBAAiB;AACf,eAAY,kBAAkB,EAC5B,UAAU,mBAAmB,WAAW,EACzC,CAAC;;EAEL,CAAC;;AAGJ,SAAgB,4BAA4B;CAC1C,MAAM,MAAM,cAAc;CAC1B,MAAM,cAAc,gBAAgB;AAEpC,QAAO,YAAY;EACjB,aAAa,EACX,iBAII,IAAI,iBAAiB,WAAW;EACtC,UAAU,OAAO,EAAE,sBAAsB;AACvC,SAAM,YAAY,cAAc,EAC9B,UAAU,mBAAmB,WAAW,EACzC,CAAC;GACF,MAAM,eAAe,YAAY,aAC/B,mBAAmB,WAAW,CAC/B;AACD,eAAY,aAAa,mBAAmB,WAAW,EAAE,gBAAgB;AACzE,UAAO,EAAE,cAAc;;EAEzB,UAAU,MAAM,OAAO,YAAY;AACjC,OAAI,SAAS,aACX,aAAY,aACV,mBAAmB,WAAW,EAC9B,QAAQ,aACT;;EAGL,iBAAiB;AACf,eAAY,kBAAkB,EAC5B,UAAU,mBAAmB,WAAW,EACzC,CAAC;;EAEL,CAAC;;AAOJ,SAAgB,wBAAwB;CACtC,MAAM,MAAM,cAAc;AAC1B,QAAO,SAAwB;EAC7B,UAAU,mBAAmB,QAAQ;EACrC,eAAe,IAAI,YAAY;EAChC,CAAC"}
@@ -1,5 +1,5 @@
1
1
  require("./chunk-9hOWP6kD.cjs");
2
- const require_mysite_api_context = require("./mysite-api-context-CilZcDS4.cjs");
2
+ const require_mysite_api_context = require("./mysite-api-context-DtEXblIV.cjs");
3
3
  let _tanstack_react_query = require("@tanstack/react-query");
4
4
  //#region ../../mysite/ui/src/portal/hooks/use-mysite-portal.ts
5
5
  const PORTAL_MYSITE_KEYS = {
@@ -199,4 +199,4 @@ Object.defineProperty(exports, "usePortalUpdateSettings", {
199
199
  }
200
200
  });
201
201
 
202
- //# sourceMappingURL=use-mysite-portal-CH9ZQROw.cjs.map
202
+ //# sourceMappingURL=use-mysite-portal-D3X51_Ax.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-mysite-portal-CH9ZQROw.cjs","names":["useMySiteApi"],"sources":["../../../mysite/ui/src/portal/hooks/use-mysite-portal.ts"],"sourcesContent":["\"use client\";\n\nimport { useQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { useMySiteApi } from \"@fluid-app/mysite-core/mysite-api-context\";\nimport type {\n MySiteProfile,\n MySiteLink,\n MySiteFavorite,\n MySiteTheme,\n CreateLinkBody,\n UpdateLinkBody,\n UpdateProfileBody,\n UpdateSettingsBody,\n AddFavoriteBody,\n} from \"@fluid-app/mysite-core/mysite-api-types\";\n\n// ---------------------------------------------------------------------------\n// Query keys — session-scoped, no userId/affiliateId needed\n// ---------------------------------------------------------------------------\n\nexport const PORTAL_MYSITE_KEYS = {\n profile: () => [\"portal-mysite\", \"profile\"] as const,\n links: () => [\"portal-mysite\", \"links\"] as const,\n favorites: () => [\"portal-mysite\", \"favorites\"] as const,\n themes: () => [\"portal-mysite\", \"themes\"] as const,\n};\n\n// ---------------------------------------------------------------------------\n// Profile hooks\n// ---------------------------------------------------------------------------\n\nexport function usePortalMySiteProfile() {\n const api = useMySiteApi();\n return useQuery<MySiteProfile>({\n queryKey: PORTAL_MYSITE_KEYS.profile(),\n queryFn: () => api.fetchProfile(),\n });\n}\n\nexport function usePortalUpdateProfile(options?: { onSuccess?: () => void }) {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (body: UpdateProfileBody) => api.updateProfile(body),\n onSuccess: (data) => {\n queryClient.setQueryData(PORTAL_MYSITE_KEYS.profile(), data);\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.profile(),\n });\n options?.onSuccess?.();\n },\n });\n}\n\nexport function usePortalUpdateSettings(options?: { onSuccess?: () => void }) {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (body: UpdateSettingsBody) => api.updateSettings(body),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.profile(),\n });\n options?.onSuccess?.();\n },\n });\n}\n\n// ---------------------------------------------------------------------------\n// Links hooks\n// ---------------------------------------------------------------------------\n\nexport function usePortalLinks() {\n const api = useMySiteApi();\n return useQuery<MySiteLink[]>({\n queryKey: PORTAL_MYSITE_KEYS.links(),\n queryFn: () => api.listLinks(),\n });\n}\n\nexport function usePortalCreateLink() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (body: CreateLinkBody) => api.createLink(body),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.links(),\n });\n },\n });\n}\n\nexport function usePortalUpdateLink() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: ({ linkId, body }: { linkId: number; body: UpdateLinkBody }) =>\n api.updateLink(linkId, body),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.links(),\n });\n },\n });\n}\n\nexport function usePortalDeleteLink() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (linkId: number) => api.deleteLink(linkId),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.links(),\n });\n },\n });\n}\n\nexport function usePortalReorderLinks() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: ({\n orderedIds,\n }: {\n orderedIds: number[];\n optimisticItems: MySiteLink[];\n }) => api.reorderLinks(orderedIds),\n onMutate: async ({ optimisticItems }) => {\n await queryClient.cancelQueries({\n queryKey: PORTAL_MYSITE_KEYS.links(),\n });\n const previousData = queryClient.getQueryData<MySiteLink[]>(\n PORTAL_MYSITE_KEYS.links(),\n );\n queryClient.setQueryData(PORTAL_MYSITE_KEYS.links(), optimisticItems);\n return { previousData };\n },\n onError: (_err, _vars, context) => {\n if (context?.previousData) {\n queryClient.setQueryData(\n PORTAL_MYSITE_KEYS.links(),\n context.previousData,\n );\n }\n },\n onSettled: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.links(),\n });\n },\n });\n}\n\n// ---------------------------------------------------------------------------\n// Favorites hooks\n// ---------------------------------------------------------------------------\n\nexport function usePortalFavorites() {\n const api = useMySiteApi();\n return useQuery<MySiteFavorite[]>({\n queryKey: PORTAL_MYSITE_KEYS.favorites(),\n queryFn: () => api.listFavorites(),\n });\n}\n\nexport function usePortalAddFavorite() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (body: AddFavoriteBody) => api.addFavorite(body),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.favorites(),\n });\n },\n });\n}\n\nexport function usePortalDeleteFavorite() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (favoriteId: number) => api.deleteFavorite(favoriteId),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.favorites(),\n });\n },\n });\n}\n\nexport function usePortalReorderFavorites() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: ({\n orderedIds,\n }: {\n orderedIds: number[];\n optimisticItems: MySiteFavorite[];\n }) => api.reorderFavorites(orderedIds),\n onMutate: async ({ optimisticItems }) => {\n await queryClient.cancelQueries({\n queryKey: PORTAL_MYSITE_KEYS.favorites(),\n });\n const previousData = queryClient.getQueryData<MySiteFavorite[]>(\n PORTAL_MYSITE_KEYS.favorites(),\n );\n queryClient.setQueryData(PORTAL_MYSITE_KEYS.favorites(), optimisticItems);\n return { previousData };\n },\n onError: (_err, _vars, context) => {\n if (context?.previousData) {\n queryClient.setQueryData(\n PORTAL_MYSITE_KEYS.favorites(),\n context.previousData,\n );\n }\n },\n onSettled: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.favorites(),\n });\n },\n });\n}\n\n// ---------------------------------------------------------------------------\n// Themes hook\n// ---------------------------------------------------------------------------\n\nexport function usePortalMySiteThemes() {\n const api = useMySiteApi();\n return useQuery<MySiteTheme[]>({\n queryKey: PORTAL_MYSITE_KEYS.themes(),\n queryFn: () => api.listThemes(),\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-mysite-portal-D3X51_Ax.cjs","names":["useMySiteApi"],"sources":["../../../mysite/ui/src/portal/hooks/use-mysite-portal.ts"],"sourcesContent":["\"use client\";\n\nimport { useQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { useMySiteApi } from \"@fluid-app/mysite-core/mysite-api-context\";\nimport type {\n MySiteProfile,\n MySiteLink,\n MySiteFavorite,\n MySiteTheme,\n CreateLinkBody,\n UpdateLinkBody,\n UpdateProfileBody,\n UpdateSettingsBody,\n AddFavoriteBody,\n} from \"@fluid-app/mysite-core/mysite-api-types\";\n\n// ---------------------------------------------------------------------------\n// Query keys — session-scoped, no userId/affiliateId needed\n// ---------------------------------------------------------------------------\n\nexport const PORTAL_MYSITE_KEYS = {\n profile: () => [\"portal-mysite\", \"profile\"] as const,\n links: () => [\"portal-mysite\", \"links\"] as const,\n favorites: () => [\"portal-mysite\", \"favorites\"] as const,\n themes: () => [\"portal-mysite\", \"themes\"] as const,\n};\n\n// ---------------------------------------------------------------------------\n// Profile hooks\n// ---------------------------------------------------------------------------\n\nexport function usePortalMySiteProfile() {\n const api = useMySiteApi();\n return useQuery<MySiteProfile>({\n queryKey: PORTAL_MYSITE_KEYS.profile(),\n queryFn: () => api.fetchProfile(),\n });\n}\n\nexport function usePortalUpdateProfile(options?: { onSuccess?: () => void }) {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (body: UpdateProfileBody) => api.updateProfile(body),\n onSuccess: (data) => {\n queryClient.setQueryData(PORTAL_MYSITE_KEYS.profile(), data);\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.profile(),\n });\n options?.onSuccess?.();\n },\n });\n}\n\nexport function usePortalUpdateSettings(options?: { onSuccess?: () => void }) {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (body: UpdateSettingsBody) => api.updateSettings(body),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.profile(),\n });\n options?.onSuccess?.();\n },\n });\n}\n\n// ---------------------------------------------------------------------------\n// Links hooks\n// ---------------------------------------------------------------------------\n\nexport function usePortalLinks() {\n const api = useMySiteApi();\n return useQuery<MySiteLink[]>({\n queryKey: PORTAL_MYSITE_KEYS.links(),\n queryFn: () => api.listLinks(),\n });\n}\n\nexport function usePortalCreateLink() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (body: CreateLinkBody) => api.createLink(body),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.links(),\n });\n },\n });\n}\n\nexport function usePortalUpdateLink() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: ({ linkId, body }: { linkId: number; body: UpdateLinkBody }) =>\n api.updateLink(linkId, body),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.links(),\n });\n },\n });\n}\n\nexport function usePortalDeleteLink() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (linkId: number) => api.deleteLink(linkId),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.links(),\n });\n },\n });\n}\n\nexport function usePortalReorderLinks() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: ({\n orderedIds,\n }: {\n orderedIds: number[];\n optimisticItems: MySiteLink[];\n }) => api.reorderLinks(orderedIds),\n onMutate: async ({ optimisticItems }) => {\n await queryClient.cancelQueries({\n queryKey: PORTAL_MYSITE_KEYS.links(),\n });\n const previousData = queryClient.getQueryData<MySiteLink[]>(\n PORTAL_MYSITE_KEYS.links(),\n );\n queryClient.setQueryData(PORTAL_MYSITE_KEYS.links(), optimisticItems);\n return { previousData };\n },\n onError: (_err, _vars, context) => {\n if (context?.previousData) {\n queryClient.setQueryData(\n PORTAL_MYSITE_KEYS.links(),\n context.previousData,\n );\n }\n },\n onSettled: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.links(),\n });\n },\n });\n}\n\n// ---------------------------------------------------------------------------\n// Favorites hooks\n// ---------------------------------------------------------------------------\n\nexport function usePortalFavorites() {\n const api = useMySiteApi();\n return useQuery<MySiteFavorite[]>({\n queryKey: PORTAL_MYSITE_KEYS.favorites(),\n queryFn: () => api.listFavorites(),\n });\n}\n\nexport function usePortalAddFavorite() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (body: AddFavoriteBody) => api.addFavorite(body),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.favorites(),\n });\n },\n });\n}\n\nexport function usePortalDeleteFavorite() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (favoriteId: number) => api.deleteFavorite(favoriteId),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.favorites(),\n });\n },\n });\n}\n\nexport function usePortalReorderFavorites() {\n const api = useMySiteApi();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: ({\n orderedIds,\n }: {\n orderedIds: number[];\n optimisticItems: MySiteFavorite[];\n }) => api.reorderFavorites(orderedIds),\n onMutate: async ({ optimisticItems }) => {\n await queryClient.cancelQueries({\n queryKey: PORTAL_MYSITE_KEYS.favorites(),\n });\n const previousData = queryClient.getQueryData<MySiteFavorite[]>(\n PORTAL_MYSITE_KEYS.favorites(),\n );\n queryClient.setQueryData(PORTAL_MYSITE_KEYS.favorites(), optimisticItems);\n return { previousData };\n },\n onError: (_err, _vars, context) => {\n if (context?.previousData) {\n queryClient.setQueryData(\n PORTAL_MYSITE_KEYS.favorites(),\n context.previousData,\n );\n }\n },\n onSettled: () => {\n queryClient.invalidateQueries({\n queryKey: PORTAL_MYSITE_KEYS.favorites(),\n });\n },\n });\n}\n\n// ---------------------------------------------------------------------------\n// Themes hook\n// ---------------------------------------------------------------------------\n\nexport function usePortalMySiteThemes() {\n const api = useMySiteApi();\n return useQuery<MySiteTheme[]>({\n queryKey: PORTAL_MYSITE_KEYS.themes(),\n queryFn: () => api.listThemes(),\n });\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { n as useAppNavigation } from "./AppNavigationContext-BCj6iFxr.mjs";
1
+ import { n as useAppNavigation } from "./AppNavigationContext-B-wToUBG.mjs";
2
2
  import { useMemo } from "react";
3
3
  //#region ../core/src/navigation/system-navigation-items.ts
4
4
  const SYSTEM_NAVIGATION_SLUGS = [
@@ -247,4 +247,4 @@ function useNavigationParent() {
247
247
  //#endregion
248
248
  export { matchSlugPrefix as a, getSystemNavigationBySection as c, normalizeSlug as d, isSlugInSection as i, isRepOnlySlug as l, collectNavSlugs as n, slugifyName as o, extractSlugFromPathname as r, SYSTEM_NAVIGATION_ITEMS as s, useNavigationParent as t, isSystemNavigationItem as u };
249
249
 
250
- //# sourceMappingURL=use-navigation-parent-DvHbbMB0.mjs.map
250
+ //# sourceMappingURL=use-navigation-parent-GjQMvU4i.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-navigation-parent-DvHbbMB0.mjs","names":[],"sources":["../../core/src/navigation/system-navigation-items.ts","../../core/src/navigation/slug-utils.ts","../src/shell/use-navigation-parent.ts"],"sourcesContent":["/**\n * System navigation items for portal apps\n * These are pre-built screens that cannot be edited or deleted in the builder.\n * They are handled by custom code (slugLibrary) rather than builder screens.\n */\n\nimport type { NavigationItem } from \"../types/navigation\";\n\nexport const SYSTEM_NAVIGATION_SLUGS = [\n \"app-download\",\n \"contacts\",\n \"account\",\n \"messages\",\n \"my-site\",\n \"orders\",\n \"profile\",\n \"subscriptions\",\n // \"share/enrollments\",\n \"share/media\",\n \"share/pages\",\n \"share/playlists\",\n \"share/products\",\n \"shop\",\n // \"upgrade\",\n] as const;\n\nexport type SystemNavigationSlug = (typeof SYSTEM_NAVIGATION_SLUGS)[number];\n\nexport type SystemNavigationItem = Omit<NavigationItem, \"children\">;\n\n/**\n * Slugs hidden from the quicklinks/shortcuts menu.\n * These screens still exist and are navigable, but don't appear in the grid.\n */\nexport const QUICKLINKS_HIDDEN_SLUGS: ReadonlySet<string> =\n new Set<SystemNavigationSlug>([\"app-download\"]);\n\n/**\n * Core navigation slugs that cannot be removed from a portal's navigation in the builder.\n * Subset of system items considered essential to every portal's UX.\n */\nexport const CORE_NAVIGATION_SLUGS: ReadonlySet<string> =\n new Set<SystemNavigationSlug>([\"profile\", \"orders\", \"subscriptions\"]);\n\n/**\n * Check if a slug refers to a core navigation item that cannot be deleted.\n * Handles a leading slash (e.g. \"/profile\").\n */\nexport function isCoreNavigationSlug(slug: string | undefined | null): boolean {\n if (!slug) return false;\n const normalized = slug.startsWith(\"/\") ? slug.slice(1) : slug;\n return CORE_NAVIGATION_SLUGS.has(normalized);\n}\n\n/**\n * Slugs restricted to rep (non-customer) users.\n * Customers will not see these in navigation and cannot access these routes.\n */\nexport const REP_ONLY_SLUGS: ReadonlySet<string> =\n new Set<SystemNavigationSlug>([\"messages\", \"my-site\"]);\n\n/**\n * Check if a slug is restricted to rep users only.\n * Handles both exact matches and prefix matches (e.g. \"contacts/123\").\n */\nexport function isRepOnlySlug(slug: string | undefined | null): boolean {\n if (!slug) return false;\n const normalized = slug.startsWith(\"/\") ? slug.slice(1) : slug;\n if (REP_ONLY_SLUGS.has(normalized)) return true;\n for (const repSlug of REP_ONLY_SLUGS) {\n if (normalized.startsWith(repSlug + \"/\")) return true;\n }\n return false;\n}\n\nexport const SYSTEM_NAVIGATION_ITEMS: Record<\n SystemNavigationSlug,\n SystemNavigationItem\n> = {\n \"app-download\": {\n slug: \"app-download\",\n label: \"App Download\",\n icon: \"mobile\",\n section: \"User\",\n },\n contacts: {\n slug: \"contacts\",\n label: \"Contacts\",\n icon: \"circle-user\",\n section: \"User\",\n },\n messages: {\n slug: \"messages\",\n label: \"Messaging\",\n icon: \"message\",\n section: \"User\",\n },\n \"my-site\": {\n slug: \"my-site\",\n label: \"My Site\",\n icon: \"globe\",\n section: \"Shareables\",\n },\n orders: {\n slug: \"orders\",\n label: \"Orders\",\n icon: \"package-open\",\n section: \"User\",\n },\n profile: {\n slug: \"profile\",\n label: \"Profile\",\n icon: \"user\",\n section: \"User\",\n },\n account: {\n slug: \"profile\",\n label: \"Account\",\n icon: \"user\",\n section: \"User\",\n },\n // \"share/enrollments\": {\n // slug: \"share/enrollments\",\n // label: \"Enrollments\",\n // icon: \"graduation-cap\",\n // section: \"Shareables\",\n // },\n \"share/media\": {\n slug: \"share/media\",\n label: \"Media\",\n icon: \"file-video\",\n section: \"Shareables\",\n },\n \"share/pages\": {\n slug: \"share/pages\",\n label: \"Pages\",\n icon: \"file-lines\",\n section: \"Shareables\",\n },\n \"share/playlists\": {\n slug: \"share/playlists\",\n label: \"Playlists\",\n icon: \"list-music\",\n section: \"Shareables\",\n },\n \"share/products\": {\n slug: \"share/products\",\n label: \"Products\",\n icon: \"box\",\n section: \"Shareables\",\n },\n shop: {\n slug: \"shop\",\n label: \"Shop\",\n icon: \"store\",\n section: \"User\",\n },\n subscriptions: {\n slug: \"subscriptions\",\n label: \"Subscriptions\",\n icon: \"arrows-repeat\",\n section: \"User\",\n },\n // upgrade: {\n // slug: \"upgrade\",\n // label: \"Upgrade\",\n // icon: \"arrow-up-from-bracket\",\n // section: \"User\",\n // },\n} as const;\n\n/**\n * Get the default system navigation items for a new portal app.\n * These items have no screen_id - they're handled by the slugLibrary.\n */\nexport function getDefaultSystemNavigationItems(): SystemNavigationItem[] {\n return Object.values(SYSTEM_NAVIGATION_ITEMS);\n}\n\n/**\n * Check if a navigation item is a system item that doesn't require a screen_id.\n * Used by RepApp to skip screen validation for these items.\n */\nexport function isSystemNavigationItem(\n slug?: string | null,\n): slug is SystemNavigationSlug {\n if (!slug) return false;\n return SYSTEM_NAVIGATION_SLUGS.includes(slug as SystemNavigationSlug);\n}\n\n/**\n * Get icon name for a navigation item by slug.\n */\nexport function getNavigationItemIcon(slug?: string): string | undefined {\n if (!slug) return undefined;\n const systemItem = SYSTEM_NAVIGATION_ITEMS[slug as SystemNavigationSlug];\n return systemItem?.icon;\n}\n\n/**\n * Get system navigation items grouped by section.\n */\nexport function getSystemNavigationBySection(): Record<\n string,\n SystemNavigationItem[]\n> {\n const sections: Record<string, SystemNavigationItem[]> = {};\n const seenSlugs = new Set<string>();\n for (const item of Object.values(SYSTEM_NAVIGATION_ITEMS)) {\n if (item.slug && seenSlugs.has(item.slug)) continue;\n if (item.slug) seenSlugs.add(item.slug);\n if (item.slug && QUICKLINKS_HIDDEN_SLUGS.has(item.slug)) continue;\n const section = item.section || \"Other\";\n if (!sections[section]) {\n sections[section] = [];\n }\n sections[section].push(item);\n }\n return sections;\n}\n\n/**\n * Normalize a navigation slug by stripping a leading slash.\n */\nexport function normalizeSlug(slug: string | undefined): string {\n if (!slug) return \"\";\n return slug.startsWith(\"/\") ? slug.slice(1) : slug;\n}\n","import type { NavigationItem } from \"../types/navigation\";\nimport { normalizeSlug } from \"./system-navigation-items\";\n\nexport interface SlugMatch {\n matchedSlug: string;\n rest: string;\n}\n\n/**\n * Extract all slugs from a navigation tree, sorted by segment count descending.\n * Longest slugs first enables greedy prefix matching (e.g. \"share/playlists\"\n * is checked before \"share\").\n */\nexport function collectNavSlugs(items: NavigationItem[]): string[] {\n const slugs: string[] = [];\n\n for (const item of items) {\n if (item.slug) slugs.push(normalizeSlug(item.slug));\n for (const child of item.children ?? []) {\n if (child.slug) slugs.push(normalizeSlug(child.slug));\n }\n }\n\n return slugs.toSorted((a, b) => b.split(\"/\").length - a.split(\"/\").length);\n}\n\n/**\n * Find the longest registered nav slug that is a prefix of `fullSlug`.\n * Uses segment-boundary checking to prevent \"shop\" from matching \"shopping\".\n */\nexport function matchSlugPrefix(\n fullSlug: string,\n navSlugs: string[],\n): SlugMatch | undefined {\n const normalized = normalizeSlug(fullSlug);\n if (!normalized) return undefined;\n\n for (const navSlug of navSlugs) {\n if (normalized === navSlug) {\n return { matchedSlug: navSlug, rest: \"\" };\n }\n if (normalized.startsWith(navSlug + \"/\")) {\n return {\n matchedSlug: navSlug,\n rest: normalized.slice(navSlug.length + 1),\n };\n }\n }\n\n return undefined;\n}\n\n/**\n * Extract the slug portion from a full pathname by stripping the basePath prefix.\n * Returns an empty string when the pathname equals the basePath exactly.\n *\n * Examples:\n * extractSlugFromPathname(\"/contacts/123\", \"/\") → \"contacts/123\"\n * extractSlugFromPathname(\"/portal/contacts\", \"/portal\") → \"contacts\"\n * extractSlugFromPathname(\"/portal\", \"/portal\") → \"\"\n * extractSlugFromPathname(\"/\", \"/\") → \"\"\n */\nexport function extractSlugFromPathname(\n pathname: string,\n basePath: string,\n): string {\n // For root basePath, just strip the leading slash\n if (basePath === \"/\") {\n return pathname.replace(/^\\//, \"\");\n }\n\n // Strip basePath prefix, then strip leading slash from remainder\n if (pathname.startsWith(basePath)) {\n return pathname.slice(basePath.length).replace(/^\\//, \"\");\n }\n\n // Fallback: return pathname without leading slash\n return pathname.replace(/^\\//, \"\");\n}\n\n/**\n * Generate a URL-safe slug from a human-readable name. Mirrors the admin\n * builder's `generateSlugFromName` so a navigation item authored as\n * `slugifyName(screen.name)` can be matched back to the screen at runtime\n * even when the screen's own `slug` is opaque (e.g. `screen-{uuid}`).\n */\nexport function slugifyName(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9\\s-]/g, \"\")\n .replace(/\\s+/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\nexport function resolveSlugLabel(\n slug: string,\n items: NavigationItem[],\n): string | null {\n const normalized = normalizeSlug(slug);\n if (!normalized) return null;\n\n const match = matchSlugPrefix(normalized, collectNavSlugs(items));\n const target = match?.matchedSlug ?? normalized;\n\n for (const item of items) {\n if (normalizeSlug(item.slug) === target) return item.label;\n for (const child of item.children ?? []) {\n if (normalizeSlug(child.slug) === target) return child.label;\n }\n }\n return null;\n}\n\nexport function isSlugInSection(\n item: NavigationItem,\n currentSlug: string,\n navSlugs: string[],\n): boolean {\n const match = matchSlugPrefix(currentSlug, navSlugs);\n const baseSlug = match?.matchedSlug ?? normalizeSlug(currentSlug);\n\n if (normalizeSlug(item.slug) === baseSlug) return true;\n return (\n item.children?.some((child) => normalizeSlug(child.slug) === baseSlug) ??\n false\n );\n}\n","import { useMemo } from \"react\";\nimport { resolveSlugLabel } from \"@fluid-app/portal-core/navigation/slug-utils\";\nimport { useAppNavigation } from \"./AppNavigationContext\";\n\nexport interface NavigationParent {\n slug: string;\n label: string;\n onClick: () => void;\n}\n\n/**\n * Resolve the previous in-session slug into a labeled back-target. Returns\n * `null` when there's no referrer or the referrer can't be matched to a\n * known nav item — callers should fall back to their structural parent.\n */\nexport function useNavigationParent(): NavigationParent | null {\n const { previousSlug, navItems, navigate } = useAppNavigation();\n\n return useMemo(() => {\n if (!previousSlug) return null;\n const label = resolveSlugLabel(previousSlug, navItems);\n if (!label) return null;\n return { slug: previousSlug, label, onClick: () => navigate(previousSlug) };\n }, [previousSlug, navItems, navigate]);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-navigation-parent-GjQMvU4i.mjs","names":[],"sources":["../../core/src/navigation/system-navigation-items.ts","../../core/src/navigation/slug-utils.ts","../src/shell/use-navigation-parent.ts"],"sourcesContent":["/**\n * System navigation items for portal apps\n * These are pre-built screens that cannot be edited or deleted in the builder.\n * They are handled by custom code (slugLibrary) rather than builder screens.\n */\n\nimport type { NavigationItem } from \"../types/navigation\";\n\nexport const SYSTEM_NAVIGATION_SLUGS = [\n \"app-download\",\n \"contacts\",\n \"account\",\n \"messages\",\n \"my-site\",\n \"orders\",\n \"profile\",\n \"subscriptions\",\n // \"share/enrollments\",\n \"share/media\",\n \"share/pages\",\n \"share/playlists\",\n \"share/products\",\n \"shop\",\n // \"upgrade\",\n] as const;\n\nexport type SystemNavigationSlug = (typeof SYSTEM_NAVIGATION_SLUGS)[number];\n\nexport type SystemNavigationItem = Omit<NavigationItem, \"children\">;\n\n/**\n * Slugs hidden from the quicklinks/shortcuts menu.\n * These screens still exist and are navigable, but don't appear in the grid.\n */\nexport const QUICKLINKS_HIDDEN_SLUGS: ReadonlySet<string> =\n new Set<SystemNavigationSlug>([\"app-download\"]);\n\n/**\n * Core navigation slugs that cannot be removed from a portal's navigation in the builder.\n * Subset of system items considered essential to every portal's UX.\n */\nexport const CORE_NAVIGATION_SLUGS: ReadonlySet<string> =\n new Set<SystemNavigationSlug>([\"profile\", \"orders\", \"subscriptions\"]);\n\n/**\n * Check if a slug refers to a core navigation item that cannot be deleted.\n * Handles a leading slash (e.g. \"/profile\").\n */\nexport function isCoreNavigationSlug(slug: string | undefined | null): boolean {\n if (!slug) return false;\n const normalized = slug.startsWith(\"/\") ? slug.slice(1) : slug;\n return CORE_NAVIGATION_SLUGS.has(normalized);\n}\n\n/**\n * Slugs restricted to rep (non-customer) users.\n * Customers will not see these in navigation and cannot access these routes.\n */\nexport const REP_ONLY_SLUGS: ReadonlySet<string> =\n new Set<SystemNavigationSlug>([\"messages\", \"my-site\"]);\n\n/**\n * Check if a slug is restricted to rep users only.\n * Handles both exact matches and prefix matches (e.g. \"contacts/123\").\n */\nexport function isRepOnlySlug(slug: string | undefined | null): boolean {\n if (!slug) return false;\n const normalized = slug.startsWith(\"/\") ? slug.slice(1) : slug;\n if (REP_ONLY_SLUGS.has(normalized)) return true;\n for (const repSlug of REP_ONLY_SLUGS) {\n if (normalized.startsWith(repSlug + \"/\")) return true;\n }\n return false;\n}\n\nexport const SYSTEM_NAVIGATION_ITEMS: Record<\n SystemNavigationSlug,\n SystemNavigationItem\n> = {\n \"app-download\": {\n slug: \"app-download\",\n label: \"App Download\",\n icon: \"mobile\",\n section: \"User\",\n },\n contacts: {\n slug: \"contacts\",\n label: \"Contacts\",\n icon: \"circle-user\",\n section: \"User\",\n },\n messages: {\n slug: \"messages\",\n label: \"Messaging\",\n icon: \"message\",\n section: \"User\",\n },\n \"my-site\": {\n slug: \"my-site\",\n label: \"My Site\",\n icon: \"globe\",\n section: \"Shareables\",\n },\n orders: {\n slug: \"orders\",\n label: \"Orders\",\n icon: \"package-open\",\n section: \"User\",\n },\n profile: {\n slug: \"profile\",\n label: \"Profile\",\n icon: \"user\",\n section: \"User\",\n },\n account: {\n slug: \"profile\",\n label: \"Account\",\n icon: \"user\",\n section: \"User\",\n },\n // \"share/enrollments\": {\n // slug: \"share/enrollments\",\n // label: \"Enrollments\",\n // icon: \"graduation-cap\",\n // section: \"Shareables\",\n // },\n \"share/media\": {\n slug: \"share/media\",\n label: \"Media\",\n icon: \"file-video\",\n section: \"Shareables\",\n },\n \"share/pages\": {\n slug: \"share/pages\",\n label: \"Pages\",\n icon: \"file-lines\",\n section: \"Shareables\",\n },\n \"share/playlists\": {\n slug: \"share/playlists\",\n label: \"Playlists\",\n icon: \"list-music\",\n section: \"Shareables\",\n },\n \"share/products\": {\n slug: \"share/products\",\n label: \"Products\",\n icon: \"box\",\n section: \"Shareables\",\n },\n shop: {\n slug: \"shop\",\n label: \"Shop\",\n icon: \"store\",\n section: \"User\",\n },\n subscriptions: {\n slug: \"subscriptions\",\n label: \"Subscriptions\",\n icon: \"arrows-repeat\",\n section: \"User\",\n },\n // upgrade: {\n // slug: \"upgrade\",\n // label: \"Upgrade\",\n // icon: \"arrow-up-from-bracket\",\n // section: \"User\",\n // },\n} as const;\n\n/**\n * Get the default system navigation items for a new portal app.\n * These items have no screen_id - they're handled by the slugLibrary.\n */\nexport function getDefaultSystemNavigationItems(): SystemNavigationItem[] {\n return Object.values(SYSTEM_NAVIGATION_ITEMS);\n}\n\n/**\n * Check if a navigation item is a system item that doesn't require a screen_id.\n * Used by RepApp to skip screen validation for these items.\n */\nexport function isSystemNavigationItem(\n slug?: string | null,\n): slug is SystemNavigationSlug {\n if (!slug) return false;\n return SYSTEM_NAVIGATION_SLUGS.includes(slug as SystemNavigationSlug);\n}\n\n/**\n * Get icon name for a navigation item by slug.\n */\nexport function getNavigationItemIcon(slug?: string): string | undefined {\n if (!slug) return undefined;\n const systemItem = SYSTEM_NAVIGATION_ITEMS[slug as SystemNavigationSlug];\n return systemItem?.icon;\n}\n\n/**\n * Get system navigation items grouped by section.\n */\nexport function getSystemNavigationBySection(): Record<\n string,\n SystemNavigationItem[]\n> {\n const sections: Record<string, SystemNavigationItem[]> = {};\n const seenSlugs = new Set<string>();\n for (const item of Object.values(SYSTEM_NAVIGATION_ITEMS)) {\n if (item.slug && seenSlugs.has(item.slug)) continue;\n if (item.slug) seenSlugs.add(item.slug);\n if (item.slug && QUICKLINKS_HIDDEN_SLUGS.has(item.slug)) continue;\n const section = item.section || \"Other\";\n if (!sections[section]) {\n sections[section] = [];\n }\n sections[section].push(item);\n }\n return sections;\n}\n\n/**\n * Normalize a navigation slug by stripping a leading slash.\n */\nexport function normalizeSlug(slug: string | undefined): string {\n if (!slug) return \"\";\n return slug.startsWith(\"/\") ? slug.slice(1) : slug;\n}\n","import type { NavigationItem } from \"../types/navigation\";\nimport { normalizeSlug } from \"./system-navigation-items\";\n\nexport interface SlugMatch {\n matchedSlug: string;\n rest: string;\n}\n\n/**\n * Extract all slugs from a navigation tree, sorted by segment count descending.\n * Longest slugs first enables greedy prefix matching (e.g. \"share/playlists\"\n * is checked before \"share\").\n */\nexport function collectNavSlugs(items: NavigationItem[]): string[] {\n const slugs: string[] = [];\n\n for (const item of items) {\n if (item.slug) slugs.push(normalizeSlug(item.slug));\n for (const child of item.children ?? []) {\n if (child.slug) slugs.push(normalizeSlug(child.slug));\n }\n }\n\n return slugs.toSorted((a, b) => b.split(\"/\").length - a.split(\"/\").length);\n}\n\n/**\n * Find the longest registered nav slug that is a prefix of `fullSlug`.\n * Uses segment-boundary checking to prevent \"shop\" from matching \"shopping\".\n */\nexport function matchSlugPrefix(\n fullSlug: string,\n navSlugs: string[],\n): SlugMatch | undefined {\n const normalized = normalizeSlug(fullSlug);\n if (!normalized) return undefined;\n\n for (const navSlug of navSlugs) {\n if (normalized === navSlug) {\n return { matchedSlug: navSlug, rest: \"\" };\n }\n if (normalized.startsWith(navSlug + \"/\")) {\n return {\n matchedSlug: navSlug,\n rest: normalized.slice(navSlug.length + 1),\n };\n }\n }\n\n return undefined;\n}\n\n/**\n * Extract the slug portion from a full pathname by stripping the basePath prefix.\n * Returns an empty string when the pathname equals the basePath exactly.\n *\n * Examples:\n * extractSlugFromPathname(\"/contacts/123\", \"/\") → \"contacts/123\"\n * extractSlugFromPathname(\"/portal/contacts\", \"/portal\") → \"contacts\"\n * extractSlugFromPathname(\"/portal\", \"/portal\") → \"\"\n * extractSlugFromPathname(\"/\", \"/\") → \"\"\n */\nexport function extractSlugFromPathname(\n pathname: string,\n basePath: string,\n): string {\n // For root basePath, just strip the leading slash\n if (basePath === \"/\") {\n return pathname.replace(/^\\//, \"\");\n }\n\n // Strip basePath prefix, then strip leading slash from remainder\n if (pathname.startsWith(basePath)) {\n return pathname.slice(basePath.length).replace(/^\\//, \"\");\n }\n\n // Fallback: return pathname without leading slash\n return pathname.replace(/^\\//, \"\");\n}\n\n/**\n * Generate a URL-safe slug from a human-readable name. Mirrors the admin\n * builder's `generateSlugFromName` so a navigation item authored as\n * `slugifyName(screen.name)` can be matched back to the screen at runtime\n * even when the screen's own `slug` is opaque (e.g. `screen-{uuid}`).\n */\nexport function slugifyName(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9\\s-]/g, \"\")\n .replace(/\\s+/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\nexport function resolveSlugLabel(\n slug: string,\n items: NavigationItem[],\n): string | null {\n const normalized = normalizeSlug(slug);\n if (!normalized) return null;\n\n const match = matchSlugPrefix(normalized, collectNavSlugs(items));\n const target = match?.matchedSlug ?? normalized;\n\n for (const item of items) {\n if (normalizeSlug(item.slug) === target) return item.label;\n for (const child of item.children ?? []) {\n if (normalizeSlug(child.slug) === target) return child.label;\n }\n }\n return null;\n}\n\nexport function isSlugInSection(\n item: NavigationItem,\n currentSlug: string,\n navSlugs: string[],\n): boolean {\n const match = matchSlugPrefix(currentSlug, navSlugs);\n const baseSlug = match?.matchedSlug ?? normalizeSlug(currentSlug);\n\n if (normalizeSlug(item.slug) === baseSlug) return true;\n return (\n item.children?.some((child) => normalizeSlug(child.slug) === baseSlug) ??\n false\n );\n}\n","import { useMemo } from \"react\";\nimport { resolveSlugLabel } from \"@fluid-app/portal-core/navigation/slug-utils\";\nimport { useAppNavigation } from \"./AppNavigationContext\";\n\nexport interface NavigationParent {\n slug: string;\n label: string;\n onClick: () => void;\n}\n\n/**\n * Resolve the previous in-session slug into a labeled back-target. Returns\n * `null` when there's no referrer or the referrer can't be matched to a\n * known nav item — callers should fall back to their structural parent.\n */\nexport function useNavigationParent(): NavigationParent | null {\n const { previousSlug, navItems, navigate } = useAppNavigation();\n\n return useMemo(() => {\n if (!previousSlug) return null;\n const label = resolveSlugLabel(previousSlug, navItems);\n if (!label) return null;\n return { slug: previousSlug, label, onClick: () => navigate(previousSlug) };\n }, [previousSlug, navItems, navigate]);\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  require("./chunk-9hOWP6kD.cjs");
2
- const require_AppNavigationContext = require("./AppNavigationContext-CoNtdUrr.cjs");
2
+ const require_AppNavigationContext = require("./AppNavigationContext-CLOwdlpx.cjs");
3
3
  let react = require("react");
4
4
  //#region ../core/src/navigation/system-navigation-items.ts
5
5
  const SYSTEM_NAVIGATION_SLUGS = [
@@ -313,4 +313,4 @@ Object.defineProperty(exports, "useNavigationParent", {
313
313
  }
314
314
  });
315
315
 
316
- //# sourceMappingURL=use-navigation-parent-DQ8CiN9L.cjs.map
316
+ //# sourceMappingURL=use-navigation-parent-q-la1wD2.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-navigation-parent-DQ8CiN9L.cjs","names":["useAppNavigation"],"sources":["../../core/src/navigation/system-navigation-items.ts","../../core/src/navigation/slug-utils.ts","../src/shell/use-navigation-parent.ts"],"sourcesContent":["/**\n * System navigation items for portal apps\n * These are pre-built screens that cannot be edited or deleted in the builder.\n * They are handled by custom code (slugLibrary) rather than builder screens.\n */\n\nimport type { NavigationItem } from \"../types/navigation\";\n\nexport const SYSTEM_NAVIGATION_SLUGS = [\n \"app-download\",\n \"contacts\",\n \"account\",\n \"messages\",\n \"my-site\",\n \"orders\",\n \"profile\",\n \"subscriptions\",\n // \"share/enrollments\",\n \"share/media\",\n \"share/pages\",\n \"share/playlists\",\n \"share/products\",\n \"shop\",\n // \"upgrade\",\n] as const;\n\nexport type SystemNavigationSlug = (typeof SYSTEM_NAVIGATION_SLUGS)[number];\n\nexport type SystemNavigationItem = Omit<NavigationItem, \"children\">;\n\n/**\n * Slugs hidden from the quicklinks/shortcuts menu.\n * These screens still exist and are navigable, but don't appear in the grid.\n */\nexport const QUICKLINKS_HIDDEN_SLUGS: ReadonlySet<string> =\n new Set<SystemNavigationSlug>([\"app-download\"]);\n\n/**\n * Core navigation slugs that cannot be removed from a portal's navigation in the builder.\n * Subset of system items considered essential to every portal's UX.\n */\nexport const CORE_NAVIGATION_SLUGS: ReadonlySet<string> =\n new Set<SystemNavigationSlug>([\"profile\", \"orders\", \"subscriptions\"]);\n\n/**\n * Check if a slug refers to a core navigation item that cannot be deleted.\n * Handles a leading slash (e.g. \"/profile\").\n */\nexport function isCoreNavigationSlug(slug: string | undefined | null): boolean {\n if (!slug) return false;\n const normalized = slug.startsWith(\"/\") ? slug.slice(1) : slug;\n return CORE_NAVIGATION_SLUGS.has(normalized);\n}\n\n/**\n * Slugs restricted to rep (non-customer) users.\n * Customers will not see these in navigation and cannot access these routes.\n */\nexport const REP_ONLY_SLUGS: ReadonlySet<string> =\n new Set<SystemNavigationSlug>([\"messages\", \"my-site\"]);\n\n/**\n * Check if a slug is restricted to rep users only.\n * Handles both exact matches and prefix matches (e.g. \"contacts/123\").\n */\nexport function isRepOnlySlug(slug: string | undefined | null): boolean {\n if (!slug) return false;\n const normalized = slug.startsWith(\"/\") ? slug.slice(1) : slug;\n if (REP_ONLY_SLUGS.has(normalized)) return true;\n for (const repSlug of REP_ONLY_SLUGS) {\n if (normalized.startsWith(repSlug + \"/\")) return true;\n }\n return false;\n}\n\nexport const SYSTEM_NAVIGATION_ITEMS: Record<\n SystemNavigationSlug,\n SystemNavigationItem\n> = {\n \"app-download\": {\n slug: \"app-download\",\n label: \"App Download\",\n icon: \"mobile\",\n section: \"User\",\n },\n contacts: {\n slug: \"contacts\",\n label: \"Contacts\",\n icon: \"circle-user\",\n section: \"User\",\n },\n messages: {\n slug: \"messages\",\n label: \"Messaging\",\n icon: \"message\",\n section: \"User\",\n },\n \"my-site\": {\n slug: \"my-site\",\n label: \"My Site\",\n icon: \"globe\",\n section: \"Shareables\",\n },\n orders: {\n slug: \"orders\",\n label: \"Orders\",\n icon: \"package-open\",\n section: \"User\",\n },\n profile: {\n slug: \"profile\",\n label: \"Profile\",\n icon: \"user\",\n section: \"User\",\n },\n account: {\n slug: \"profile\",\n label: \"Account\",\n icon: \"user\",\n section: \"User\",\n },\n // \"share/enrollments\": {\n // slug: \"share/enrollments\",\n // label: \"Enrollments\",\n // icon: \"graduation-cap\",\n // section: \"Shareables\",\n // },\n \"share/media\": {\n slug: \"share/media\",\n label: \"Media\",\n icon: \"file-video\",\n section: \"Shareables\",\n },\n \"share/pages\": {\n slug: \"share/pages\",\n label: \"Pages\",\n icon: \"file-lines\",\n section: \"Shareables\",\n },\n \"share/playlists\": {\n slug: \"share/playlists\",\n label: \"Playlists\",\n icon: \"list-music\",\n section: \"Shareables\",\n },\n \"share/products\": {\n slug: \"share/products\",\n label: \"Products\",\n icon: \"box\",\n section: \"Shareables\",\n },\n shop: {\n slug: \"shop\",\n label: \"Shop\",\n icon: \"store\",\n section: \"User\",\n },\n subscriptions: {\n slug: \"subscriptions\",\n label: \"Subscriptions\",\n icon: \"arrows-repeat\",\n section: \"User\",\n },\n // upgrade: {\n // slug: \"upgrade\",\n // label: \"Upgrade\",\n // icon: \"arrow-up-from-bracket\",\n // section: \"User\",\n // },\n} as const;\n\n/**\n * Get the default system navigation items for a new portal app.\n * These items have no screen_id - they're handled by the slugLibrary.\n */\nexport function getDefaultSystemNavigationItems(): SystemNavigationItem[] {\n return Object.values(SYSTEM_NAVIGATION_ITEMS);\n}\n\n/**\n * Check if a navigation item is a system item that doesn't require a screen_id.\n * Used by RepApp to skip screen validation for these items.\n */\nexport function isSystemNavigationItem(\n slug?: string | null,\n): slug is SystemNavigationSlug {\n if (!slug) return false;\n return SYSTEM_NAVIGATION_SLUGS.includes(slug as SystemNavigationSlug);\n}\n\n/**\n * Get icon name for a navigation item by slug.\n */\nexport function getNavigationItemIcon(slug?: string): string | undefined {\n if (!slug) return undefined;\n const systemItem = SYSTEM_NAVIGATION_ITEMS[slug as SystemNavigationSlug];\n return systemItem?.icon;\n}\n\n/**\n * Get system navigation items grouped by section.\n */\nexport function getSystemNavigationBySection(): Record<\n string,\n SystemNavigationItem[]\n> {\n const sections: Record<string, SystemNavigationItem[]> = {};\n const seenSlugs = new Set<string>();\n for (const item of Object.values(SYSTEM_NAVIGATION_ITEMS)) {\n if (item.slug && seenSlugs.has(item.slug)) continue;\n if (item.slug) seenSlugs.add(item.slug);\n if (item.slug && QUICKLINKS_HIDDEN_SLUGS.has(item.slug)) continue;\n const section = item.section || \"Other\";\n if (!sections[section]) {\n sections[section] = [];\n }\n sections[section].push(item);\n }\n return sections;\n}\n\n/**\n * Normalize a navigation slug by stripping a leading slash.\n */\nexport function normalizeSlug(slug: string | undefined): string {\n if (!slug) return \"\";\n return slug.startsWith(\"/\") ? slug.slice(1) : slug;\n}\n","import type { NavigationItem } from \"../types/navigation\";\nimport { normalizeSlug } from \"./system-navigation-items\";\n\nexport interface SlugMatch {\n matchedSlug: string;\n rest: string;\n}\n\n/**\n * Extract all slugs from a navigation tree, sorted by segment count descending.\n * Longest slugs first enables greedy prefix matching (e.g. \"share/playlists\"\n * is checked before \"share\").\n */\nexport function collectNavSlugs(items: NavigationItem[]): string[] {\n const slugs: string[] = [];\n\n for (const item of items) {\n if (item.slug) slugs.push(normalizeSlug(item.slug));\n for (const child of item.children ?? []) {\n if (child.slug) slugs.push(normalizeSlug(child.slug));\n }\n }\n\n return slugs.toSorted((a, b) => b.split(\"/\").length - a.split(\"/\").length);\n}\n\n/**\n * Find the longest registered nav slug that is a prefix of `fullSlug`.\n * Uses segment-boundary checking to prevent \"shop\" from matching \"shopping\".\n */\nexport function matchSlugPrefix(\n fullSlug: string,\n navSlugs: string[],\n): SlugMatch | undefined {\n const normalized = normalizeSlug(fullSlug);\n if (!normalized) return undefined;\n\n for (const navSlug of navSlugs) {\n if (normalized === navSlug) {\n return { matchedSlug: navSlug, rest: \"\" };\n }\n if (normalized.startsWith(navSlug + \"/\")) {\n return {\n matchedSlug: navSlug,\n rest: normalized.slice(navSlug.length + 1),\n };\n }\n }\n\n return undefined;\n}\n\n/**\n * Extract the slug portion from a full pathname by stripping the basePath prefix.\n * Returns an empty string when the pathname equals the basePath exactly.\n *\n * Examples:\n * extractSlugFromPathname(\"/contacts/123\", \"/\") → \"contacts/123\"\n * extractSlugFromPathname(\"/portal/contacts\", \"/portal\") → \"contacts\"\n * extractSlugFromPathname(\"/portal\", \"/portal\") → \"\"\n * extractSlugFromPathname(\"/\", \"/\") → \"\"\n */\nexport function extractSlugFromPathname(\n pathname: string,\n basePath: string,\n): string {\n // For root basePath, just strip the leading slash\n if (basePath === \"/\") {\n return pathname.replace(/^\\//, \"\");\n }\n\n // Strip basePath prefix, then strip leading slash from remainder\n if (pathname.startsWith(basePath)) {\n return pathname.slice(basePath.length).replace(/^\\//, \"\");\n }\n\n // Fallback: return pathname without leading slash\n return pathname.replace(/^\\//, \"\");\n}\n\n/**\n * Generate a URL-safe slug from a human-readable name. Mirrors the admin\n * builder's `generateSlugFromName` so a navigation item authored as\n * `slugifyName(screen.name)` can be matched back to the screen at runtime\n * even when the screen's own `slug` is opaque (e.g. `screen-{uuid}`).\n */\nexport function slugifyName(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9\\s-]/g, \"\")\n .replace(/\\s+/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\nexport function resolveSlugLabel(\n slug: string,\n items: NavigationItem[],\n): string | null {\n const normalized = normalizeSlug(slug);\n if (!normalized) return null;\n\n const match = matchSlugPrefix(normalized, collectNavSlugs(items));\n const target = match?.matchedSlug ?? normalized;\n\n for (const item of items) {\n if (normalizeSlug(item.slug) === target) return item.label;\n for (const child of item.children ?? []) {\n if (normalizeSlug(child.slug) === target) return child.label;\n }\n }\n return null;\n}\n\nexport function isSlugInSection(\n item: NavigationItem,\n currentSlug: string,\n navSlugs: string[],\n): boolean {\n const match = matchSlugPrefix(currentSlug, navSlugs);\n const baseSlug = match?.matchedSlug ?? normalizeSlug(currentSlug);\n\n if (normalizeSlug(item.slug) === baseSlug) return true;\n return (\n item.children?.some((child) => normalizeSlug(child.slug) === baseSlug) ??\n false\n );\n}\n","import { useMemo } from \"react\";\nimport { resolveSlugLabel } from \"@fluid-app/portal-core/navigation/slug-utils\";\nimport { useAppNavigation } from \"./AppNavigationContext\";\n\nexport interface NavigationParent {\n slug: string;\n label: string;\n onClick: () => void;\n}\n\n/**\n * Resolve the previous in-session slug into a labeled back-target. Returns\n * `null` when there's no referrer or the referrer can't be matched to a\n * known nav item — callers should fall back to their structural parent.\n */\nexport function useNavigationParent(): NavigationParent | null {\n const { previousSlug, navItems, navigate } = useAppNavigation();\n\n return useMemo(() => {\n if (!previousSlug) return null;\n const label = resolveSlugLabel(previousSlug, navItems);\n if (!label) return null;\n return { slug: previousSlug, label, onClick: () => navigate(previousSlug) };\n }, [previousSlug, navItems, navigate]);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-navigation-parent-q-la1wD2.cjs","names":["useAppNavigation"],"sources":["../../core/src/navigation/system-navigation-items.ts","../../core/src/navigation/slug-utils.ts","../src/shell/use-navigation-parent.ts"],"sourcesContent":["/**\n * System navigation items for portal apps\n * These are pre-built screens that cannot be edited or deleted in the builder.\n * They are handled by custom code (slugLibrary) rather than builder screens.\n */\n\nimport type { NavigationItem } from \"../types/navigation\";\n\nexport const SYSTEM_NAVIGATION_SLUGS = [\n \"app-download\",\n \"contacts\",\n \"account\",\n \"messages\",\n \"my-site\",\n \"orders\",\n \"profile\",\n \"subscriptions\",\n // \"share/enrollments\",\n \"share/media\",\n \"share/pages\",\n \"share/playlists\",\n \"share/products\",\n \"shop\",\n // \"upgrade\",\n] as const;\n\nexport type SystemNavigationSlug = (typeof SYSTEM_NAVIGATION_SLUGS)[number];\n\nexport type SystemNavigationItem = Omit<NavigationItem, \"children\">;\n\n/**\n * Slugs hidden from the quicklinks/shortcuts menu.\n * These screens still exist and are navigable, but don't appear in the grid.\n */\nexport const QUICKLINKS_HIDDEN_SLUGS: ReadonlySet<string> =\n new Set<SystemNavigationSlug>([\"app-download\"]);\n\n/**\n * Core navigation slugs that cannot be removed from a portal's navigation in the builder.\n * Subset of system items considered essential to every portal's UX.\n */\nexport const CORE_NAVIGATION_SLUGS: ReadonlySet<string> =\n new Set<SystemNavigationSlug>([\"profile\", \"orders\", \"subscriptions\"]);\n\n/**\n * Check if a slug refers to a core navigation item that cannot be deleted.\n * Handles a leading slash (e.g. \"/profile\").\n */\nexport function isCoreNavigationSlug(slug: string | undefined | null): boolean {\n if (!slug) return false;\n const normalized = slug.startsWith(\"/\") ? slug.slice(1) : slug;\n return CORE_NAVIGATION_SLUGS.has(normalized);\n}\n\n/**\n * Slugs restricted to rep (non-customer) users.\n * Customers will not see these in navigation and cannot access these routes.\n */\nexport const REP_ONLY_SLUGS: ReadonlySet<string> =\n new Set<SystemNavigationSlug>([\"messages\", \"my-site\"]);\n\n/**\n * Check if a slug is restricted to rep users only.\n * Handles both exact matches and prefix matches (e.g. \"contacts/123\").\n */\nexport function isRepOnlySlug(slug: string | undefined | null): boolean {\n if (!slug) return false;\n const normalized = slug.startsWith(\"/\") ? slug.slice(1) : slug;\n if (REP_ONLY_SLUGS.has(normalized)) return true;\n for (const repSlug of REP_ONLY_SLUGS) {\n if (normalized.startsWith(repSlug + \"/\")) return true;\n }\n return false;\n}\n\nexport const SYSTEM_NAVIGATION_ITEMS: Record<\n SystemNavigationSlug,\n SystemNavigationItem\n> = {\n \"app-download\": {\n slug: \"app-download\",\n label: \"App Download\",\n icon: \"mobile\",\n section: \"User\",\n },\n contacts: {\n slug: \"contacts\",\n label: \"Contacts\",\n icon: \"circle-user\",\n section: \"User\",\n },\n messages: {\n slug: \"messages\",\n label: \"Messaging\",\n icon: \"message\",\n section: \"User\",\n },\n \"my-site\": {\n slug: \"my-site\",\n label: \"My Site\",\n icon: \"globe\",\n section: \"Shareables\",\n },\n orders: {\n slug: \"orders\",\n label: \"Orders\",\n icon: \"package-open\",\n section: \"User\",\n },\n profile: {\n slug: \"profile\",\n label: \"Profile\",\n icon: \"user\",\n section: \"User\",\n },\n account: {\n slug: \"profile\",\n label: \"Account\",\n icon: \"user\",\n section: \"User\",\n },\n // \"share/enrollments\": {\n // slug: \"share/enrollments\",\n // label: \"Enrollments\",\n // icon: \"graduation-cap\",\n // section: \"Shareables\",\n // },\n \"share/media\": {\n slug: \"share/media\",\n label: \"Media\",\n icon: \"file-video\",\n section: \"Shareables\",\n },\n \"share/pages\": {\n slug: \"share/pages\",\n label: \"Pages\",\n icon: \"file-lines\",\n section: \"Shareables\",\n },\n \"share/playlists\": {\n slug: \"share/playlists\",\n label: \"Playlists\",\n icon: \"list-music\",\n section: \"Shareables\",\n },\n \"share/products\": {\n slug: \"share/products\",\n label: \"Products\",\n icon: \"box\",\n section: \"Shareables\",\n },\n shop: {\n slug: \"shop\",\n label: \"Shop\",\n icon: \"store\",\n section: \"User\",\n },\n subscriptions: {\n slug: \"subscriptions\",\n label: \"Subscriptions\",\n icon: \"arrows-repeat\",\n section: \"User\",\n },\n // upgrade: {\n // slug: \"upgrade\",\n // label: \"Upgrade\",\n // icon: \"arrow-up-from-bracket\",\n // section: \"User\",\n // },\n} as const;\n\n/**\n * Get the default system navigation items for a new portal app.\n * These items have no screen_id - they're handled by the slugLibrary.\n */\nexport function getDefaultSystemNavigationItems(): SystemNavigationItem[] {\n return Object.values(SYSTEM_NAVIGATION_ITEMS);\n}\n\n/**\n * Check if a navigation item is a system item that doesn't require a screen_id.\n * Used by RepApp to skip screen validation for these items.\n */\nexport function isSystemNavigationItem(\n slug?: string | null,\n): slug is SystemNavigationSlug {\n if (!slug) return false;\n return SYSTEM_NAVIGATION_SLUGS.includes(slug as SystemNavigationSlug);\n}\n\n/**\n * Get icon name for a navigation item by slug.\n */\nexport function getNavigationItemIcon(slug?: string): string | undefined {\n if (!slug) return undefined;\n const systemItem = SYSTEM_NAVIGATION_ITEMS[slug as SystemNavigationSlug];\n return systemItem?.icon;\n}\n\n/**\n * Get system navigation items grouped by section.\n */\nexport function getSystemNavigationBySection(): Record<\n string,\n SystemNavigationItem[]\n> {\n const sections: Record<string, SystemNavigationItem[]> = {};\n const seenSlugs = new Set<string>();\n for (const item of Object.values(SYSTEM_NAVIGATION_ITEMS)) {\n if (item.slug && seenSlugs.has(item.slug)) continue;\n if (item.slug) seenSlugs.add(item.slug);\n if (item.slug && QUICKLINKS_HIDDEN_SLUGS.has(item.slug)) continue;\n const section = item.section || \"Other\";\n if (!sections[section]) {\n sections[section] = [];\n }\n sections[section].push(item);\n }\n return sections;\n}\n\n/**\n * Normalize a navigation slug by stripping a leading slash.\n */\nexport function normalizeSlug(slug: string | undefined): string {\n if (!slug) return \"\";\n return slug.startsWith(\"/\") ? slug.slice(1) : slug;\n}\n","import type { NavigationItem } from \"../types/navigation\";\nimport { normalizeSlug } from \"./system-navigation-items\";\n\nexport interface SlugMatch {\n matchedSlug: string;\n rest: string;\n}\n\n/**\n * Extract all slugs from a navigation tree, sorted by segment count descending.\n * Longest slugs first enables greedy prefix matching (e.g. \"share/playlists\"\n * is checked before \"share\").\n */\nexport function collectNavSlugs(items: NavigationItem[]): string[] {\n const slugs: string[] = [];\n\n for (const item of items) {\n if (item.slug) slugs.push(normalizeSlug(item.slug));\n for (const child of item.children ?? []) {\n if (child.slug) slugs.push(normalizeSlug(child.slug));\n }\n }\n\n return slugs.toSorted((a, b) => b.split(\"/\").length - a.split(\"/\").length);\n}\n\n/**\n * Find the longest registered nav slug that is a prefix of `fullSlug`.\n * Uses segment-boundary checking to prevent \"shop\" from matching \"shopping\".\n */\nexport function matchSlugPrefix(\n fullSlug: string,\n navSlugs: string[],\n): SlugMatch | undefined {\n const normalized = normalizeSlug(fullSlug);\n if (!normalized) return undefined;\n\n for (const navSlug of navSlugs) {\n if (normalized === navSlug) {\n return { matchedSlug: navSlug, rest: \"\" };\n }\n if (normalized.startsWith(navSlug + \"/\")) {\n return {\n matchedSlug: navSlug,\n rest: normalized.slice(navSlug.length + 1),\n };\n }\n }\n\n return undefined;\n}\n\n/**\n * Extract the slug portion from a full pathname by stripping the basePath prefix.\n * Returns an empty string when the pathname equals the basePath exactly.\n *\n * Examples:\n * extractSlugFromPathname(\"/contacts/123\", \"/\") → \"contacts/123\"\n * extractSlugFromPathname(\"/portal/contacts\", \"/portal\") → \"contacts\"\n * extractSlugFromPathname(\"/portal\", \"/portal\") → \"\"\n * extractSlugFromPathname(\"/\", \"/\") → \"\"\n */\nexport function extractSlugFromPathname(\n pathname: string,\n basePath: string,\n): string {\n // For root basePath, just strip the leading slash\n if (basePath === \"/\") {\n return pathname.replace(/^\\//, \"\");\n }\n\n // Strip basePath prefix, then strip leading slash from remainder\n if (pathname.startsWith(basePath)) {\n return pathname.slice(basePath.length).replace(/^\\//, \"\");\n }\n\n // Fallback: return pathname without leading slash\n return pathname.replace(/^\\//, \"\");\n}\n\n/**\n * Generate a URL-safe slug from a human-readable name. Mirrors the admin\n * builder's `generateSlugFromName` so a navigation item authored as\n * `slugifyName(screen.name)` can be matched back to the screen at runtime\n * even when the screen's own `slug` is opaque (e.g. `screen-{uuid}`).\n */\nexport function slugifyName(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9\\s-]/g, \"\")\n .replace(/\\s+/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\nexport function resolveSlugLabel(\n slug: string,\n items: NavigationItem[],\n): string | null {\n const normalized = normalizeSlug(slug);\n if (!normalized) return null;\n\n const match = matchSlugPrefix(normalized, collectNavSlugs(items));\n const target = match?.matchedSlug ?? normalized;\n\n for (const item of items) {\n if (normalizeSlug(item.slug) === target) return item.label;\n for (const child of item.children ?? []) {\n if (normalizeSlug(child.slug) === target) return child.label;\n }\n }\n return null;\n}\n\nexport function isSlugInSection(\n item: NavigationItem,\n currentSlug: string,\n navSlugs: string[],\n): boolean {\n const match = matchSlugPrefix(currentSlug, navSlugs);\n const baseSlug = match?.matchedSlug ?? normalizeSlug(currentSlug);\n\n if (normalizeSlug(item.slug) === baseSlug) return true;\n return (\n item.children?.some((child) => normalizeSlug(child.slug) === baseSlug) ??\n false\n );\n}\n","import { useMemo } from \"react\";\nimport { resolveSlugLabel } from \"@fluid-app/portal-core/navigation/slug-utils\";\nimport { useAppNavigation } from \"./AppNavigationContext\";\n\nexport interface NavigationParent {\n slug: string;\n label: string;\n onClick: () => void;\n}\n\n/**\n * Resolve the previous in-session slug into a labeled back-target. Returns\n * `null` when there's no referrer or the referrer can't be matched to a\n * known nav item — callers should fall back to their structural parent.\n */\nexport function useNavigationParent(): NavigationParent | null {\n const { previousSlug, navItems, navigate } = useAppNavigation();\n\n return useMemo(() => {\n if (!previousSlug) return null;\n const label = resolveSlugLabel(previousSlug, navItems);\n if (!label) return null;\n return { slug: previousSlug, label, onClick: () => navigate(previousSlug) };\n }, [previousSlug, navItems, navigate]);\n}\n"],"mappings":"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"}